From 434cb91cbdd17e0e58265d1687286c3a0ea35886 Mon Sep 17 00:00:00 2001 From: GitGuru7 Date: Tue, 17 Feb 2026 17:24:59 +0530 Subject: [PATCH 01/13] feat: add vip to configure relative-position-manager on testnet --- .../vip-610/abi/AccessControlManager.json | 157 ++ simulations/vip-610/abi/FlashLoanFacet.json | 1218 ++++++++++++ .../abi/LeverageStrategiesManager.json | 722 +++++++ .../vip-610/abi/RelativePositionManager.json | 1692 +++++++++++++++++ simulations/vip-610/abi/Swaphelper.json | 444 +++++ simulations/vip-610/bsctestnet.ts | 111 ++ vips/vip-610/bsctestnet.ts | 74 + 7 files changed, 4418 insertions(+) create mode 100644 simulations/vip-610/abi/AccessControlManager.json create mode 100644 simulations/vip-610/abi/FlashLoanFacet.json create mode 100644 simulations/vip-610/abi/LeverageStrategiesManager.json create mode 100644 simulations/vip-610/abi/RelativePositionManager.json create mode 100644 simulations/vip-610/abi/Swaphelper.json create mode 100644 simulations/vip-610/bsctestnet.ts create mode 100644 vips/vip-610/bsctestnet.ts diff --git a/simulations/vip-610/abi/AccessControlManager.json b/simulations/vip-610/abi/AccessControlManager.json new file mode 100644 index 000000000..2ef119947 --- /dev/null +++ b/simulations/vip-610/abi/AccessControlManager.json @@ -0,0 +1,157 @@ +[ + { "inputs": [], "stateMutability": "nonpayable", "type": "constructor" }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "account", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "contractAddress", "type": "address" }, + { "indexed": false, "internalType": "string", "name": "functionSig", "type": "string" } + ], + "name": "PermissionGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "account", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "contractAddress", "type": "address" }, + { "indexed": false, "internalType": "string", "name": "functionSig", "type": "string" } + ], + "name": "PermissionRevoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "indexed": true, "internalType": "bytes32", "name": "previousAdminRole", "type": "bytes32" }, + { "indexed": true, "internalType": "bytes32", "name": "newAdminRole", "type": "bytes32" } + ], + "name": "RoleAdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "indexed": true, "internalType": "address", "name": "account", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "sender", "type": "address" } + ], + "name": "RoleGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "indexed": true, "internalType": "address", "name": "account", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "sender", "type": "address" } + ], + "name": "RoleRevoked", + "type": "event" + }, + { + "inputs": [], + "name": "DEFAULT_ADMIN_ROLE", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "bytes32", "name": "role", "type": "bytes32" }], + "name": "getRoleAdmin", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "contractAddress", "type": "address" }, + { "internalType": "string", "name": "functionSig", "type": "string" }, + { "internalType": "address", "name": "accountToPermit", "type": "address" } + ], + "name": "giveCallPermission", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "grantRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" }, + { "internalType": "address", "name": "contractAddress", "type": "address" }, + { "internalType": "string", "name": "functionSig", "type": "string" } + ], + "name": "hasPermission", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "hasRole", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" }, + { "internalType": "string", "name": "functionSig", "type": "string" } + ], + "name": "isAllowedToCall", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "renounceRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "contractAddress", "type": "address" }, + { "internalType": "string", "name": "functionSig", "type": "string" }, + { "internalType": "address", "name": "accountToRevoke", "type": "address" } + ], + "name": "revokeCallPermission", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "revokeRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "bytes4", "name": "interfaceId", "type": "bytes4" }], + "name": "supportsInterface", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + } +] diff --git a/simulations/vip-610/abi/FlashLoanFacet.json b/simulations/vip-610/abi/FlashLoanFacet.json new file mode 100644 index 000000000..2adc7e8fa --- /dev/null +++ b/simulations/vip-610/abi/FlashLoanFacet.json @@ -0,0 +1,1218 @@ +[ + { + "inputs": [], + "name": "AlreadyInSelectedPool", + "type": "error" + }, + { + "inputs": [], + "name": "ArrayLengthMismatch", + "type": "error" + }, + { + "inputs": [], + "name": "BorrowNotAllowedInPool", + "type": "error" + }, + { + "inputs": [], + "name": "EmptyPoolLabel", + "type": "error" + }, + { + "inputs": [], + "name": "ExecuteFlashLoanFailed", + "type": "error" + }, + { + "inputs": [], + "name": "FailedToCreateDebtPosition", + "type": "error" + }, + { + "inputs": [], + "name": "FlashLoanNotEnabled", + "type": "error" + }, + { + "inputs": [], + "name": "FlashLoanPausedSystemWide", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint96", + "name": "poolId", + "type": "uint96" + } + ], + "name": "InactivePool", + "type": "error" + }, + { + "inputs": [], + "name": "IncompatibleBorrowedAssets", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidAmount", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidFlashLoanParams", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidOperationForCorePool", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "enum WeightFunction", + "name": "strategy", + "type": "uint8" + } + ], + "name": "InvalidWeightingStrategy", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "errorCode", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortfall", + "type": "uint256" + } + ], + "name": "LiquidityCheckFailed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint96", + "name": "poolId", + "type": "uint96" + }, + { + "internalType": "address", + "name": "vToken", + "type": "address" + } + ], + "name": "MarketAlreadyListed", + "type": "error" + }, + { + "inputs": [], + "name": "MarketConfigNotFound", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + } + ], + "name": "MarketNotListed", + "type": "error" + }, + { + "inputs": [], + "name": "MarketNotListedInCorePool", + "type": "error" + }, + { + "inputs": [], + "name": "NoAssetsRequested", + "type": "error" + }, + { + "inputs": [], + "name": "NotAnApprovedDelegate", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "repaid", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "required", + "type": "uint256" + } + ], + "name": "NotEnoughRepayment", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint96", + "name": "poolId", + "type": "uint96" + } + ], + "name": "PoolDoesNotExist", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint96", + "name": "poolId", + "type": "uint96" + }, + { + "internalType": "address", + "name": "vToken", + "type": "address" + } + ], + "name": "PoolMarketNotFound", + "type": "error" + }, + { + "inputs": [], + "name": "ReentrancyGuardReentrantCall", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "SenderNotAuthorizedForFlashLoan", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "requested", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maximum", + "type": "uint256" + } + ], + "name": "TooManyAssetsRequested", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "DistributedVAIVaultVenus", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "error", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "info", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "detail", + "type": "uint256" + } + ], + "name": "Failure", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "indexed": false, + "internalType": "contract VToken[]", + "name": "assets", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "name": "FlashLoanExecuted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "onBehalf", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "repaidAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "remainingDebt", + "type": "uint256" + } + ], + "name": "FlashLoanRepaid", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "MarketEntered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "bool", + "name": "isWhitelisted", + "type": "bool" + } + ], + "name": "IsAccountFlashLoanWhitelisted", + "type": "event" + }, + { + "inputs": [], + "name": "MAX_FLASHLOAN_ASSETS", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "accountAssets", + "outputs": [ + { + "internalType": "contract VToken", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "market", + "type": "address" + }, + { + "internalType": "enum Action", + "name": "action", + "type": "uint8" + } + ], + "name": "actionPaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "admin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "allMarkets", + "outputs": [ + { + "internalType": "contract VToken", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "approvedDelegates", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "authorizedFlashLoan", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "borrowCapGuardian", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "borrowCaps", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "closeFactorMantissa", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "comptrollerImplementation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "comptrollerLens", + "outputs": [ + { + "internalType": "contract ComptrollerLensInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "corePoolId", + "outputs": [ + { + "internalType": "uint96", + "name": "", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address payable", + "name": "onBehalf", + "type": "address" + }, + { + "internalType": "address payable", + "name": "receiver", + "type": "address" + }, + { + "internalType": "contract VToken[]", + "name": "vTokens", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "underlyingAmounts", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "param", + "type": "bytes" + } + ], + "name": "executeFlashLoan", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "flashLoanPaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint96", + "name": "poolId", + "type": "uint96" + }, + { + "internalType": "address", + "name": "vToken", + "type": "address" + } + ], + "name": "getPoolMarketIndex", + "outputs": [ + { + "internalType": "PoolMarketId", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "getXVSAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isForcedLiquidationEnabled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "address", + "name": "market", + "type": "address" + } + ], + "name": "isForcedLiquidationEnabledForUser", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "lastPoolId", + "outputs": [ + { + "internalType": "uint96", + "name": "", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "liquidatorContract", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxAssets", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "minReleaseAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "mintVAIGuardianPaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "mintedVAIs", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "oracle", + "outputs": [ + { + "internalType": "contract ResilientOracleInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pauseGuardian", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingAdmin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingComptrollerImplementation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint96", + "name": "", + "type": "uint96" + } + ], + "name": "pools", + "outputs": [ + { + "internalType": "string", + "name": "label", + "type": "string" + }, + { + "internalType": "bool", + "name": "isActive", + "type": "bool" + }, + { + "internalType": "bool", + "name": "allowCorePoolFallback", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "prime", + "outputs": [ + { + "internalType": "contract IPrime", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "protocolPaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "releaseStartBlock", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "repayVAIGuardianPaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "supplyCaps", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "treasuryAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "treasuryGuardian", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "treasuryPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "userPoolId", + "outputs": [ + { + "internalType": "uint96", + "name": "", + "type": "uint96" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "vaiController", + "outputs": [ + { + "internalType": "contract VAIControllerInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "vaiMintRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "vaiVaultAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "venusAccrued", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "venusBorrowSpeeds", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "venusBorrowState", + "outputs": [ + { + "internalType": "uint224", + "name": "index", + "type": "uint224" + }, + { + "internalType": "uint32", + "name": "block", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "venusBorrowerIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "venusInitialIndex", + "outputs": [ + { + "internalType": "uint224", + "name": "", + "type": "uint224" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "venusSupplierIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "venusSupplySpeeds", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "venusSupplyState", + "outputs": [ + { + "internalType": "uint224", + "name": "index", + "type": "uint224" + }, + { + "internalType": "uint32", + "name": "block", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "venusVAIVaultRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/simulations/vip-610/abi/LeverageStrategiesManager.json b/simulations/vip-610/abi/LeverageStrategiesManager.json new file mode 100644 index 000000000..cb36e91f7 --- /dev/null +++ b/simulations/vip-610/abi/LeverageStrategiesManager.json @@ -0,0 +1,722 @@ +[ + { + "inputs": [ + { + "internalType": "contract IComptroller", + "name": "_comptroller", + "type": "address" + }, + { + "internalType": "contract SwapHelper", + "name": "_swapHelper", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "_vBNB", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "errorCode", + "type": "uint256" + } + ], + "name": "AccrueInterestFailed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "errorCode", + "type": "uint256" + } + ], + "name": "BorrowBehalfFailed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "err", + "type": "uint256" + } + ], + "name": "EnterMarketFailed", + "type": "error" + }, + { + "inputs": [], + "name": "FlashLoanAssetOrAmountMismatch", + "type": "error" + }, + { + "inputs": [], + "name": "IdenticalMarkets", + "type": "error" + }, + { + "inputs": [], + "name": "InitiatorMismatch", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientFundsToRepayFlashloan", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidExecuteOperation", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "market", + "type": "address" + } + ], + "name": "MarketNotListed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "errorCode", + "type": "uint256" + } + ], + "name": "MintBehalfFailed", + "type": "error" + }, + { + "inputs": [], + "name": "NotAnApprovedDelegate", + "type": "error" + }, + { + "inputs": [], + "name": "OnBehalfMismatch", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "errorCode", + "type": "uint256" + } + ], + "name": "OperationCausesLiquidation", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "errorCode", + "type": "uint256" + } + ], + "name": "RedeemBehalfFailed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "errorCode", + "type": "uint256" + } + ], + "name": "RepayBehalfFailed", + "type": "error" + }, + { + "inputs": [], + "name": "SlippageExceeded", + "type": "error" + }, + { + "inputs": [], + "name": "TokenSwapCallFailed", + "type": "error" + }, + { + "inputs": [], + "name": "UnauthorizedExecutor", + "type": "error" + }, + { + "inputs": [], + "name": "VBNBNotSupported", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroFlashLoanAmount", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "DustTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "contract IVToken", + "name": "collateralMarket", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "collateralAmountSeed", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "contract IVToken", + "name": "borrowedMarket", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "borrowedAmountToFlashLoan", + "type": "uint256" + } + ], + "name": "LeverageEntered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "contract IVToken", + "name": "collateralMarket", + "type": "address" + }, + { + "indexed": true, + "internalType": "contract IVToken", + "name": "borrowedMarket", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "borrowedAmountSeed", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "borrowedAmountToFlashLoan", + "type": "uint256" + } + ], + "name": "LeverageEnteredFromBorrow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "contract IVToken", + "name": "collateralMarket", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "collateralAmountToRedeemForSwap", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "contract IVToken", + "name": "borrowedMarket", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "borrowedAmountToFlashLoan", + "type": "uint256" + } + ], + "name": "LeverageExited", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "contract IVToken", + "name": "collateralMarket", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "collateralAmountSeed", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "collateralAmountToFlashLoan", + "type": "uint256" + } + ], + "name": "SingleAssetLeverageEntered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "contract IVToken", + "name": "collateralMarket", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "collateralAmountToFlashLoan", + "type": "uint256" + } + ], + "name": "SingleAssetLeverageExited", + "type": "event" + }, + { + "inputs": [], + "name": "COMPTROLLER", + "outputs": [ + { + "internalType": "contract IComptroller", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "_collateralMarket", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_collateralAmountSeed", + "type": "uint256" + }, + { + "internalType": "contract IVToken", + "name": "_borrowedMarket", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_borrowedAmountToFlashLoan", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_minAmountOutAfterSwap", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_swapData", + "type": "bytes" + } + ], + "name": "enterLeverage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "_collateralMarket", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "_borrowedMarket", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_borrowedAmountSeed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_borrowedAmountToFlashLoan", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_minAmountOutAfterSwap", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_swapData", + "type": "bytes" + } + ], + "name": "enterLeverageFromBorrow", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "_collateralMarket", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_collateralAmountSeed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_collateralAmountToFlashLoan", + "type": "uint256" + } + ], + "name": "enterSingleAssetLeverage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken[]", + "name": "vTokens", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "premiums", + "type": "uint256[]" + }, + { + "internalType": "address", + "name": "initiator", + "type": "address" + }, + { + "internalType": "address", + "name": "onBehalf", + "type": "address" + }, + { + "internalType": "bytes", + "name": "param", + "type": "bytes" + } + ], + "name": "executeOperation", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "internalType": "uint256[]", + "name": "repayAmounts", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "_collateralMarket", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_collateralAmountToRedeemForSwap", + "type": "uint256" + }, + { + "internalType": "contract IVToken", + "name": "_borrowedMarket", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_borrowedAmountToFlashLoan", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_minAmountOutAfterSwap", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_swapData", + "type": "bytes" + } + ], + "name": "exitLeverage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "_collateralMarket", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_collateralAmountToFlashLoan", + "type": "uint256" + } + ], + "name": "exitSingleAssetLeverage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "swapHelper", + "outputs": [ + { + "internalType": "contract SwapHelper", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "vBNB", + "outputs": [ + { + "internalType": "contract IVToken", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } + ] \ No newline at end of file diff --git a/simulations/vip-610/abi/RelativePositionManager.json b/simulations/vip-610/abi/RelativePositionManager.json new file mode 100644 index 000000000..192c7a216 --- /dev/null +++ b/simulations/vip-610/abi/RelativePositionManager.json @@ -0,0 +1,1692 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "address", + "name": "leverageManager", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "AssetNotListed", + "type": "error" + }, + { + "inputs": [], + "name": "BorrowAmountExceedsMaximum", + "type": "error" + }, + { + "inputs": [], + "name": "DSAInactive", + "type": "error" + }, + { + "inputs": [], + "name": "DSAVTokenAlreadyAdded", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "errorCode", + "type": "uint256" + } + ], + "name": "EnterMarketFailed", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientPrincipal", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientWithdrawableAmount", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidCloseFractionBps", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidCollateralFactor", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidDSA", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidLeverage", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidLongAmountToRedeem", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidOraclePrice", + "type": "error" + }, + { + "inputs": [], + "name": "MinAmountOutRepayBelowDebt", + "type": "error" + }, + { + "inputs": [], + "name": "MinAmountOutSecondBelowDebt", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "errorCode", + "type": "uint256" + } + ], + "name": "MintBehalfFailed", + "type": "error" + }, + { + "inputs": [], + "name": "PositionAccountImplementationNotSet", + "type": "error" + }, + { + "inputs": [], + "name": "PositionAlreadyExists", + "type": "error" + }, + { + "inputs": [], + "name": "PositionNotActive", + "type": "error" + }, + { + "inputs": [], + "name": "PositionNotFullyClosed", + "type": "error" + }, + { + "inputs": [], + "name": "ProportionalCloseAmountOutOfTolerance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "errorCode", + "type": "uint256" + } + ], + "name": "RedeemBehalfFailed", + "type": "error" + }, + { + "inputs": [], + "name": "SameDSAActiveStatus", + "type": "error" + }, + { + "inputs": [], + "name": "SameMarketNotAllowed", + "type": "error" + }, + { + "inputs": [], + "name": "SamePositionAccountImplementation", + "type": "error" + }, + { + "inputs": [], + "name": "SlippageExceeded", + "type": "error" + }, + { + "inputs": [], + "name": "TokenSwapCallFailed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "calledContract", + "type": "address" + }, + { + "internalType": "string", + "name": "methodSignature", + "type": "string" + } + ], + "name": "Unauthorized", + "type": "error" + }, + { + "inputs": [], + "name": "VBNBNotSupported", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroAmount", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroDebt", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroShortAmount", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "dsaVToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "index", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "bool", + "name": "active", + "type": "bool" + } + ], + "name": "DSAVTokenActiveUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "dsaVToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "index", + "type": "uint8" + } + ], + "name": "DSAVTokenAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "oldAccessControlManager", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAccessControlManager", + "type": "address" + } + ], + "name": "NewAccessControlManager", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "longAsset", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "shortAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "positionAccount", + "type": "address" + } + ], + "name": "PositionAccountDeployed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldImplementation", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newImplementation", + "type": "address" + } + ], + "name": "PositionAccountImplementationUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "longAsset", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "shortAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "dsaAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "cycleId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "initialPrincipal", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "effectiveLeverage", + "type": "uint256" + } + ], + "name": "PositionActivated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "cycleId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "closeFractionBps", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountRepaid", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountRedeemed", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountRedeemedDsa", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "longDustRedeemed", + "type": "uint256" + } + ], + "name": "PositionClosed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "cycleId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "dsaAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountWithdrawn", + "type": "uint256" + } + ], + "name": "PositionDeactivated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "cycleId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "longAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "shortAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "dsaAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "shortAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "additionalPrincipal", + "type": "uint256" + } + ], + "name": "PositionOpened", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "cycleId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "dsaAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newTotalPrincipal", + "type": "uint256" + } + ], + "name": "PrincipalSupplied", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "dsaAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "remainingPrincipal", + "type": "uint256" + } + ], + "name": "PrincipalWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountConvertedToProfit", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newTotalPrincipal", + "type": "uint256" + } + ], + "name": "ProfitConverted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "oldSuppliedPrincipal", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newSuppliedPrincipal", + "type": "uint256" + } + ], + "name": "RefreshedSuppliedPrincipal", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "UnderlyingTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "inputs": [], + "name": "COMPTROLLER", + "outputs": [ + { + "internalType": "contract IComptroller", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LEVERAGE_MANAGER", + "outputs": [ + { + "internalType": "contract LeverageStrategiesManager", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "POSITION_ACCOUNT_IMPLEMENTATION", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "accessControlManager", + "outputs": [ + { + "internalType": "contract IAccessControlManagerV8", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "address", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "uint8", + "name": "dsaIndex", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "initialPrincipal", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "effectiveLeverage", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minLongAmount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapData", + "type": "bytes" + } + ], + "name": "activateAndOpenPosition", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "address", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "uint8", + "name": "dsaIndex", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "initialPrincipal", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "effectiveLeverage", + "type": "uint256" + } + ], + "name": "activatePosition", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "dsaVToken", + "type": "address" + } + ], + "name": "addDSAVToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + } + ], + "name": "calculateMaxBorrow", + "outputs": [ + { + "internalType": "uint256", + "name": "maxBorrowAmount", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "closeFractionBps", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "longAmountToRedeemForFirstSwap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortAmountToRepayForFirstSwap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOutFirst", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapDataFirst", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "dsaAmountToRedeemForSecondSwap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOutSecond", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapDataSecond", + "type": "bytes" + } + ], + "name": "closeWithLoss", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "closeFractionBps", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "longAmountToRedeemForRepay", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOutRepay", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapDataRepay", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "longAmountToRedeemForProfit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOutProfit", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapDataProfit", + "type": "bytes" + } + ], + "name": "closeWithProfit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + } + ], + "name": "deactivatePosition", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "dsaVTokenIndexCounter", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "name": "dsaVTokens", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "internalType": "address[]", + "name": "targets", + "type": "address[]" + }, + { + "internalType": "bytes[]", + "name": "data", + "type": "bytes[]" + } + ], + "name": "executePositionAccountCall", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getDsaVTokens", + "outputs": [ + { + "internalType": "address[]", + "name": "dsaVTokensList", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + } + ], + "name": "getLongCollateralBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "longBalance", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + } + ], + "name": "getPosition", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "address", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "address", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "internalType": "bool", + "name": "isActive", + "type": "bool" + }, + { + "internalType": "uint8", + "name": "dsaIndex", + "type": "uint8" + }, + { + "internalType": "address", + "name": "dsaVToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "suppliedPrincipalVTokens", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "effectiveLeverage", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "cycleId", + "type": "uint256" + } + ], + "internalType": "struct IRelativePositionManager.Position", + "name": "position", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + } + ], + "name": "getPositionAccountAddress", + "outputs": [ + { + "internalType": "address", + "name": "predicted", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + } + ], + "name": "getSuppliedPrincipalBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + } + ], + "name": "getUtilizationInfo", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "actualCapitalUtilized", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nominalCapitalUtilized", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "finalCapitalUtilized", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "availableCapitalUSD", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "withdrawableAmount", + "type": "uint256" + } + ], + "internalType": "struct IRelativePositionManager.UtilizationInfo", + "name": "utilization", + "type": "tuple" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "accessControlManager_", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isDsaVTokenActive", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "additionalPrincipal", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minLongAmount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapData", + "type": "bytes" + } + ], + "name": "openPosition", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "positions", + "outputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "address", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "address", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "internalType": "bool", + "name": "isActive", + "type": "bool" + }, + { + "internalType": "uint8", + "name": "dsaIndex", + "type": "uint8" + }, + { + "internalType": "address", + "name": "dsaVToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "suppliedPrincipalVTokens", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "effectiveLeverage", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "cycleId", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "accessControlManager_", + "type": "address" + } + ], + "name": "setAccessControlManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "dsaIndex", + "type": "uint8" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + } + ], + "name": "setDSAVTokenActive", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "positionAccountImpl", + "type": "address" + } + ], + "name": "setPositionAccountImplementation", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "address", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "supplyPrincipal", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "withdrawPrincipal", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ] \ No newline at end of file diff --git a/simulations/vip-610/abi/Swaphelper.json b/simulations/vip-610/abi/Swaphelper.json new file mode 100644 index 000000000..fb92e0573 --- /dev/null +++ b/simulations/vip-610/abi/Swaphelper.json @@ -0,0 +1,444 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "backendSigner_", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "CallerNotAuthorized", + "type": "error" + }, + { + "inputs": [], + "name": "DeadlineReached", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidShortString", + "type": "error" + }, + { + "inputs": [], + "name": "MissingSignature", + "type": "error" + }, + { + "inputs": [], + "name": "NoCallsProvided", + "type": "error" + }, + { + "inputs": [], + "name": "SaltAlreadyUsed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "str", + "type": "string" + } + ], + "name": "StringTooLong", + "type": "error" + }, + { + "inputs": [], + "name": "Unauthorized", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroAddress", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "ApprovedMax", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldSigner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newSigner", + "type": "address" + } + ], + "name": "BackendSignerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "EIP712DomainChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "GenericCallExecuted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "callsCount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + } + ], + "name": "MulticallExecuted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Swept", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20Upgradeable", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "approveMax", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "backendSigner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "eip712Domain", + "outputs": [ + { + "internalType": "bytes1", + "name": "fields", + "type": "bytes1" + }, + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "version", + "type": "string" + }, + { + "internalType": "uint256", + "name": "chainId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "verifyingContract", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + }, + { + "internalType": "uint256[]", + "name": "extensions", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "genericCall", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes[]", + "name": "calls", + "type": "bytes[]" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "multicall", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newSigner", + "type": "address" + } + ], + "name": "setBackendSigner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20Upgradeable", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "sweep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "usedSalts", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/simulations/vip-610/bsctestnet.ts b/simulations/vip-610/bsctestnet.ts new file mode 100644 index 000000000..51b14d84d --- /dev/null +++ b/simulations/vip-610/bsctestnet.ts @@ -0,0 +1,111 @@ +import { expect } from "chai"; +import { Contract } from "ethers"; +import { ethers } from "hardhat"; +import { NETWORK_ADDRESSES } from "src/networkAddresses"; +import { forking, testVip } from "src/vip-framework"; + +import { + LEVERAGE_STRATEGIES_MANAGER, + POSITION_ACCOUNT, + RELATIVE_POSITION_MANAGER, + SWAP_HELPER, + TIMELOCKS_AND_GURDIAN, + vip589 as vip610Testnet, +} from "../../vips/vip-610/bsctestnet"; +import ACCESS_CONTROL_MANAGER_ABI from "./abi/AccessControlManager.json"; +import FLASHLOAN_FACET_ABI from "./abi/FlashLoanFacet.json"; +import LEVERAGE_MANAGER_ABI from "./abi/LeverageStrategiesManager.json"; +import RELATIVE_POSITION_MANAGER_ABI from "./abi/RelativePositionManager.json"; +import SWAP_HELPER_ABI from "./abi/SwapHelper.json"; + +const { bsctestnet } = NETWORK_ADDRESSES; + +const ACM_FUNCTION_SIGNATURES = [ + "pause()", + "unpause()", + "setPositionAccountImplementation(address)", + "addDSAVToken(address)", + "setDSAVTokenActive(uint8,bool)", + "executePositionAccountCall(address,address[],bytes[])", +] as const; + +forking(90876709, async () => { + let comptroller: Contract; + let accessControlManager: Contract; + let leverageStrategiesManager: Contract; + let swapHelper: Contract; + let relativePositionManager: Contract; + + before(async () => { + comptroller = await ethers.getContractAt(FLASHLOAN_FACET_ABI, bsctestnet.UNITROLLER); + accessControlManager = await ethers.getContractAt(ACCESS_CONTROL_MANAGER_ABI, bsctestnet.ACCESS_CONTROL_MANAGER); + leverageStrategiesManager = await ethers.getContractAt(LEVERAGE_MANAGER_ABI, LEVERAGE_STRATEGIES_MANAGER); + swapHelper = await ethers.getContractAt(SWAP_HELPER_ABI, SWAP_HELPER); + relativePositionManager = await ethers.getContractAt(RELATIVE_POSITION_MANAGER_ABI, RELATIVE_POSITION_MANAGER); + }); + + describe("Pre-VIP behavior", () => { + it("LeverageStrategiesManager should not be whitelisted for flash loans", async () => { + expect(await comptroller.authorizedFlashLoan(LEVERAGE_STRATEGIES_MANAGER)).to.equal(false); + }); + + it("SwapHelper should have NORMAL_TIMELOCK as owner", async () => { + expect(await swapHelper.owner()).not.equal(bsctestnet.NORMAL_TIMELOCK); + }); + + it("LeverageStrategiesManager should have NORMAL_TIMELOCK as pending owner", async () => { + expect(await leverageStrategiesManager.owner()).not.equal(bsctestnet.NORMAL_TIMELOCK); + }); + + it("RelativePositionManager should have NORMAL_TIMELOCK as pending owner", async () => { + expect(await relativePositionManager.owner()).not.equal(bsctestnet.NORMAL_TIMELOCK); + }); + + it("Timelocks/Guardian should not have ACM permissions on RelativePositionManager", async () => { + for (const timelockOrGuardian of TIMELOCKS_AND_GURDIAN) { + for (const fnSignature of ACM_FUNCTION_SIGNATURES) { + expect( + await accessControlManager.hasPermission(timelockOrGuardian, RELATIVE_POSITION_MANAGER, fnSignature), + ).to.equal(false); + } + } + }); + }); + + testVip("VIP-610 [BNB Chain] Testnet", await vip610Testnet()); + + describe("Post-VIP behavior", () => { + it("LeverageStrategiesManager should be whitelisted for flash loans", async () => { + expect(await comptroller.authorizedFlashLoan(LEVERAGE_STRATEGIES_MANAGER)).to.equal(true); + }); + + it("SwapHelper should have NORMAL_TIMELOCK as owner and no pending owner", async () => { + expect(await swapHelper.owner()).to.equal(bsctestnet.NORMAL_TIMELOCK); + expect(await swapHelper.pendingOwner()).to.equal(ethers.constants.AddressZero); + }); + + it("LeverageStrategiesManager should have NORMAL_TIMELOCK as owner and no pending owner", async () => { + expect(await leverageStrategiesManager.owner()).to.equal(bsctestnet.NORMAL_TIMELOCK); + expect(await leverageStrategiesManager.pendingOwner()).to.equal(ethers.constants.AddressZero); + }); + + it("RelativePositionManager should have NORMAL_TIMELOCK as owner and no pending owner", async () => { + expect(await relativePositionManager.owner()).to.equal(bsctestnet.NORMAL_TIMELOCK); + expect(await relativePositionManager.pendingOwner()).to.equal(ethers.constants.AddressZero); + }); + + it("Timelocks/Guardian should have ACM permissions on RelativePositionManager", async () => { + for (const timelockOrGuardian of TIMELOCKS_AND_GURDIAN) { + for (const fnSignature of ACM_FUNCTION_SIGNATURES) { + expect( + await accessControlManager.hasPermission(timelockOrGuardian, RELATIVE_POSITION_MANAGER, fnSignature), + ).to.equal(true); + } + } + }); + + it("RPM should have Position account implementation stored in the state", async () => { + expect(await relativePositionManager.POSITION_ACCOUNT_IMPLEMENTATION()).to.equals(POSITION_ACCOUNT); + }); + }); +}); diff --git a/vips/vip-610/bsctestnet.ts b/vips/vip-610/bsctestnet.ts new file mode 100644 index 000000000..cb6454b7a --- /dev/null +++ b/vips/vip-610/bsctestnet.ts @@ -0,0 +1,74 @@ +import { NETWORK_ADDRESSES } from "src/networkAddresses"; +import { ProposalType } from "src/types"; +import { makeProposal } from "src/utils"; + +const { bsctestnet } = NETWORK_ADDRESSES; +export const SWAP_HELPER = "0xf7Cfd0eDfAC7AA473813559b372297332EdEbB8B"; +export const LEVERAGE_STRATEGIES_MANAGER = "0xE852204A757A3Ee9Dfc5d608b7038f962f393706"; +export const RELATIVE_POSITION_MANAGER = "0xcB84425698B9426b5Edd9Ed25eA0116aA0c2Ce7F"; +export const POSITION_ACCOUNT = "0x599B79742AB82700Bc828cc44e0Ae22FBbB88e7c"; + +export const TIMELOCKS_AND_GURDIAN = [ + bsctestnet.NORMAL_TIMELOCK, + bsctestnet.FAST_TRACK_TIMELOCK, + bsctestnet.CRITICAL_TIMELOCK, + bsctestnet.GUARDIAN, +]; + +const giveAcmPermissions = (fnSignature: string, timelocks = TIMELOCKS_AND_GURDIAN) => + timelocks.map(timelock => ({ + target: bsctestnet.ACCESS_CONTROL_MANAGER, + signature: "giveCallPermission(address,string,address)", + params: [RELATIVE_POSITION_MANAGER, fnSignature, timelock], + })); + +export const vip589 = () => { + const meta = { + version: "v2", + title: "VIP-589 [BNB Chain] Add U market to the stablecoin Emode pool", + description: "VIP-589 [BNB Chain] Add U market to the stablecoin Emode pool", + forDescription: "I agree that Venus Protocol should proceed with this proposal", + againstDescription: "I do not think that Venus Protocol should proceed with this proposal", + abstainDescription: "I am indifferent to whether Venus Protocol proceeds or not", + }; + + return makeProposal( + [ + { + target: SWAP_HELPER, + signature: "acceptOwnership()", + params: [], + }, + { + target: LEVERAGE_STRATEGIES_MANAGER, + signature: "acceptOwnership()", + params: [], + }, + { + target: RELATIVE_POSITION_MANAGER, + signature: "acceptOwnership()", + params: [], + }, + { + target: bsctestnet.UNITROLLER, + signature: "setWhiteListFlashLoanAccount(address,bool)", + params: [LEVERAGE_STRATEGIES_MANAGER, true], + }, + // Already set in this contract + // { + // target: RELATIVE_POSITION_MANAGER, + // signature: "setPositionAccountImplementation(address)", + // params: [POSITION_ACCOUNT], + // }, + // ACM permissions + ...giveAcmPermissions("pause()"), + ...giveAcmPermissions("unpause()"), + ...giveAcmPermissions("setPositionAccountImplementation(address)"), + ...giveAcmPermissions("addDSAVToken(address)"), + ...giveAcmPermissions("setDSAVTokenActive(uint8,bool)"), + ...giveAcmPermissions("executePositionAccountCall(address,address[],bytes[])"), + ], + meta, + ProposalType.REGULAR, + ); +}; From 323d3ba015df194d4ce73b4d3e4e62baa7ba3323 Mon Sep 17 00:00:00 2001 From: GitGuru7 Date: Tue, 17 Feb 2026 17:32:19 +0530 Subject: [PATCH 02/13] feat: improve simulation pre checks --- .../abi/LeverageStrategiesManager.json | 1442 +++---- .../vip-610/abi/RelativePositionManager.json | 3382 ++++++++--------- simulations/vip-610/bsctestnet.ts | 9 +- 3 files changed, 2418 insertions(+), 2415 deletions(-) diff --git a/simulations/vip-610/abi/LeverageStrategiesManager.json b/simulations/vip-610/abi/LeverageStrategiesManager.json index cb36e91f7..72ef420b4 100644 --- a/simulations/vip-610/abi/LeverageStrategiesManager.json +++ b/simulations/vip-610/abi/LeverageStrategiesManager.json @@ -1,722 +1,722 @@ [ - { - "inputs": [ - { - "internalType": "contract IComptroller", - "name": "_comptroller", - "type": "address" - }, - { - "internalType": "contract SwapHelper", - "name": "_swapHelper", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "_vBNB", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "errorCode", - "type": "uint256" - } - ], - "name": "AccrueInterestFailed", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "errorCode", - "type": "uint256" - } - ], - "name": "BorrowBehalfFailed", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "err", - "type": "uint256" - } - ], - "name": "EnterMarketFailed", - "type": "error" - }, - { - "inputs": [], - "name": "FlashLoanAssetOrAmountMismatch", - "type": "error" - }, - { - "inputs": [], - "name": "IdenticalMarkets", - "type": "error" - }, - { - "inputs": [], - "name": "InitiatorMismatch", - "type": "error" - }, - { - "inputs": [], - "name": "InsufficientFundsToRepayFlashloan", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidExecuteOperation", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "market", - "type": "address" - } - ], - "name": "MarketNotListed", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "errorCode", - "type": "uint256" - } - ], - "name": "MintBehalfFailed", - "type": "error" - }, - { - "inputs": [], - "name": "NotAnApprovedDelegate", - "type": "error" - }, - { - "inputs": [], - "name": "OnBehalfMismatch", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "errorCode", - "type": "uint256" - } - ], - "name": "OperationCausesLiquidation", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "errorCode", - "type": "uint256" - } - ], - "name": "RedeemBehalfFailed", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "errorCode", - "type": "uint256" - } - ], - "name": "RepayBehalfFailed", - "type": "error" - }, - { - "inputs": [], - "name": "SlippageExceeded", - "type": "error" - }, - { - "inputs": [], - "name": "TokenSwapCallFailed", - "type": "error" - }, - { - "inputs": [], - "name": "UnauthorizedExecutor", - "type": "error" - }, - { - "inputs": [], - "name": "VBNBNotSupported", - "type": "error" - }, - { - "inputs": [], - "name": "ZeroAddress", - "type": "error" - }, - { - "inputs": [], - "name": "ZeroFlashLoanAmount", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "DustTransferred", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint8", - "name": "version", - "type": "uint8" - } - ], - "name": "Initialized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "contract IVToken", - "name": "collateralMarket", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "collateralAmountSeed", - "type": "uint256" - }, - { - "indexed": true, - "internalType": "contract IVToken", - "name": "borrowedMarket", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "borrowedAmountToFlashLoan", - "type": "uint256" - } - ], - "name": "LeverageEntered", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "contract IVToken", - "name": "collateralMarket", - "type": "address" - }, - { - "indexed": true, - "internalType": "contract IVToken", - "name": "borrowedMarket", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "borrowedAmountSeed", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "borrowedAmountToFlashLoan", - "type": "uint256" - } - ], - "name": "LeverageEnteredFromBorrow", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "contract IVToken", - "name": "collateralMarket", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "collateralAmountToRedeemForSwap", - "type": "uint256" - }, - { - "indexed": true, - "internalType": "contract IVToken", - "name": "borrowedMarket", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "borrowedAmountToFlashLoan", - "type": "uint256" - } - ], - "name": "LeverageExited", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferStarted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferred", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "contract IVToken", - "name": "collateralMarket", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "collateralAmountSeed", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "collateralAmountToFlashLoan", - "type": "uint256" - } - ], - "name": "SingleAssetLeverageEntered", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "contract IVToken", - "name": "collateralMarket", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "collateralAmountToFlashLoan", - "type": "uint256" - } - ], - "name": "SingleAssetLeverageExited", - "type": "event" - }, - { - "inputs": [], - "name": "COMPTROLLER", - "outputs": [ - { - "internalType": "contract IComptroller", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "acceptOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IVToken", - "name": "_collateralMarket", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_collateralAmountSeed", - "type": "uint256" - }, - { - "internalType": "contract IVToken", - "name": "_borrowedMarket", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_borrowedAmountToFlashLoan", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_minAmountOutAfterSwap", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "_swapData", - "type": "bytes" - } - ], - "name": "enterLeverage", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IVToken", - "name": "_collateralMarket", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "_borrowedMarket", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_borrowedAmountSeed", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_borrowedAmountToFlashLoan", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_minAmountOutAfterSwap", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "_swapData", - "type": "bytes" - } - ], - "name": "enterLeverageFromBorrow", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IVToken", - "name": "_collateralMarket", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_collateralAmountSeed", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_collateralAmountToFlashLoan", - "type": "uint256" - } - ], - "name": "enterSingleAssetLeverage", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IVToken[]", - "name": "vTokens", - "type": "address[]" - }, - { - "internalType": "uint256[]", - "name": "amounts", - "type": "uint256[]" - }, - { - "internalType": "uint256[]", - "name": "premiums", - "type": "uint256[]" - }, - { - "internalType": "address", - "name": "initiator", - "type": "address" - }, - { - "internalType": "address", - "name": "onBehalf", - "type": "address" - }, - { - "internalType": "bytes", - "name": "param", - "type": "bytes" - } - ], - "name": "executeOperation", - "outputs": [ - { - "internalType": "bool", - "name": "success", - "type": "bool" - }, - { - "internalType": "uint256[]", - "name": "repayAmounts", - "type": "uint256[]" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IVToken", - "name": "_collateralMarket", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_collateralAmountToRedeemForSwap", - "type": "uint256" - }, - { - "internalType": "contract IVToken", - "name": "_borrowedMarket", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_borrowedAmountToFlashLoan", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_minAmountOutAfterSwap", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "_swapData", - "type": "bytes" - } - ], - "name": "exitLeverage", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IVToken", - "name": "_collateralMarket", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_collateralAmountToFlashLoan", - "type": "uint256" - } - ], - "name": "exitSingleAssetLeverage", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "owner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "pendingOwner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "renounceOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "swapHelper", - "outputs": [ - { - "internalType": "contract SwapHelper", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "transferOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "vBNB", - "outputs": [ - { - "internalType": "contract IVToken", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - } - ] \ No newline at end of file + { + "inputs": [ + { + "internalType": "contract IComptroller", + "name": "_comptroller", + "type": "address" + }, + { + "internalType": "contract SwapHelper", + "name": "_swapHelper", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "_vBNB", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "errorCode", + "type": "uint256" + } + ], + "name": "AccrueInterestFailed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "errorCode", + "type": "uint256" + } + ], + "name": "BorrowBehalfFailed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "err", + "type": "uint256" + } + ], + "name": "EnterMarketFailed", + "type": "error" + }, + { + "inputs": [], + "name": "FlashLoanAssetOrAmountMismatch", + "type": "error" + }, + { + "inputs": [], + "name": "IdenticalMarkets", + "type": "error" + }, + { + "inputs": [], + "name": "InitiatorMismatch", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientFundsToRepayFlashloan", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidExecuteOperation", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "market", + "type": "address" + } + ], + "name": "MarketNotListed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "errorCode", + "type": "uint256" + } + ], + "name": "MintBehalfFailed", + "type": "error" + }, + { + "inputs": [], + "name": "NotAnApprovedDelegate", + "type": "error" + }, + { + "inputs": [], + "name": "OnBehalfMismatch", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "errorCode", + "type": "uint256" + } + ], + "name": "OperationCausesLiquidation", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "errorCode", + "type": "uint256" + } + ], + "name": "RedeemBehalfFailed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "errorCode", + "type": "uint256" + } + ], + "name": "RepayBehalfFailed", + "type": "error" + }, + { + "inputs": [], + "name": "SlippageExceeded", + "type": "error" + }, + { + "inputs": [], + "name": "TokenSwapCallFailed", + "type": "error" + }, + { + "inputs": [], + "name": "UnauthorizedExecutor", + "type": "error" + }, + { + "inputs": [], + "name": "VBNBNotSupported", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroFlashLoanAmount", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "DustTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "contract IVToken", + "name": "collateralMarket", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "collateralAmountSeed", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "contract IVToken", + "name": "borrowedMarket", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "borrowedAmountToFlashLoan", + "type": "uint256" + } + ], + "name": "LeverageEntered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "contract IVToken", + "name": "collateralMarket", + "type": "address" + }, + { + "indexed": true, + "internalType": "contract IVToken", + "name": "borrowedMarket", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "borrowedAmountSeed", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "borrowedAmountToFlashLoan", + "type": "uint256" + } + ], + "name": "LeverageEnteredFromBorrow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "contract IVToken", + "name": "collateralMarket", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "collateralAmountToRedeemForSwap", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "contract IVToken", + "name": "borrowedMarket", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "borrowedAmountToFlashLoan", + "type": "uint256" + } + ], + "name": "LeverageExited", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "contract IVToken", + "name": "collateralMarket", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "collateralAmountSeed", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "collateralAmountToFlashLoan", + "type": "uint256" + } + ], + "name": "SingleAssetLeverageEntered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "contract IVToken", + "name": "collateralMarket", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "collateralAmountToFlashLoan", + "type": "uint256" + } + ], + "name": "SingleAssetLeverageExited", + "type": "event" + }, + { + "inputs": [], + "name": "COMPTROLLER", + "outputs": [ + { + "internalType": "contract IComptroller", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "_collateralMarket", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_collateralAmountSeed", + "type": "uint256" + }, + { + "internalType": "contract IVToken", + "name": "_borrowedMarket", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_borrowedAmountToFlashLoan", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_minAmountOutAfterSwap", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_swapData", + "type": "bytes" + } + ], + "name": "enterLeverage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "_collateralMarket", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "_borrowedMarket", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_borrowedAmountSeed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_borrowedAmountToFlashLoan", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_minAmountOutAfterSwap", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_swapData", + "type": "bytes" + } + ], + "name": "enterLeverageFromBorrow", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "_collateralMarket", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_collateralAmountSeed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_collateralAmountToFlashLoan", + "type": "uint256" + } + ], + "name": "enterSingleAssetLeverage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken[]", + "name": "vTokens", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "premiums", + "type": "uint256[]" + }, + { + "internalType": "address", + "name": "initiator", + "type": "address" + }, + { + "internalType": "address", + "name": "onBehalf", + "type": "address" + }, + { + "internalType": "bytes", + "name": "param", + "type": "bytes" + } + ], + "name": "executeOperation", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "internalType": "uint256[]", + "name": "repayAmounts", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "_collateralMarket", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_collateralAmountToRedeemForSwap", + "type": "uint256" + }, + { + "internalType": "contract IVToken", + "name": "_borrowedMarket", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_borrowedAmountToFlashLoan", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_minAmountOutAfterSwap", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_swapData", + "type": "bytes" + } + ], + "name": "exitLeverage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "_collateralMarket", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_collateralAmountToFlashLoan", + "type": "uint256" + } + ], + "name": "exitSingleAssetLeverage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "swapHelper", + "outputs": [ + { + "internalType": "contract SwapHelper", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "vBNB", + "outputs": [ + { + "internalType": "contract IVToken", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/simulations/vip-610/abi/RelativePositionManager.json b/simulations/vip-610/abi/RelativePositionManager.json index 192c7a216..ed0ce022f 100644 --- a/simulations/vip-610/abi/RelativePositionManager.json +++ b/simulations/vip-610/abi/RelativePositionManager.json @@ -1,1692 +1,1692 @@ [ - { - "inputs": [ - { - "internalType": "address", - "name": "comptroller", - "type": "address" - }, - { - "internalType": "address", - "name": "leverageManager", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [], - "name": "AssetNotListed", - "type": "error" - }, - { - "inputs": [], - "name": "BorrowAmountExceedsMaximum", - "type": "error" - }, - { - "inputs": [], - "name": "DSAInactive", - "type": "error" - }, - { - "inputs": [], - "name": "DSAVTokenAlreadyAdded", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "errorCode", - "type": "uint256" - } - ], - "name": "EnterMarketFailed", - "type": "error" - }, - { - "inputs": [], - "name": "InsufficientPrincipal", - "type": "error" - }, - { - "inputs": [], - "name": "InsufficientWithdrawableAmount", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidCloseFractionBps", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidCollateralFactor", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidDSA", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidLeverage", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidLongAmountToRedeem", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidOraclePrice", - "type": "error" - }, - { - "inputs": [], - "name": "MinAmountOutRepayBelowDebt", - "type": "error" - }, - { - "inputs": [], - "name": "MinAmountOutSecondBelowDebt", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "errorCode", - "type": "uint256" - } - ], - "name": "MintBehalfFailed", - "type": "error" - }, - { - "inputs": [], - "name": "PositionAccountImplementationNotSet", - "type": "error" - }, - { - "inputs": [], - "name": "PositionAlreadyExists", - "type": "error" - }, - { - "inputs": [], - "name": "PositionNotActive", - "type": "error" - }, - { - "inputs": [], - "name": "PositionNotFullyClosed", - "type": "error" - }, - { - "inputs": [], - "name": "ProportionalCloseAmountOutOfTolerance", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "errorCode", - "type": "uint256" - } - ], - "name": "RedeemBehalfFailed", - "type": "error" - }, - { - "inputs": [], - "name": "SameDSAActiveStatus", - "type": "error" - }, - { - "inputs": [], - "name": "SameMarketNotAllowed", - "type": "error" - }, - { - "inputs": [], - "name": "SamePositionAccountImplementation", - "type": "error" - }, - { - "inputs": [], - "name": "SlippageExceeded", - "type": "error" - }, - { - "inputs": [], - "name": "TokenSwapCallFailed", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address", - "name": "calledContract", - "type": "address" - }, - { - "internalType": "string", - "name": "methodSignature", - "type": "string" - } - ], - "name": "Unauthorized", - "type": "error" - }, - { - "inputs": [], - "name": "VBNBNotSupported", - "type": "error" - }, - { - "inputs": [], - "name": "ZeroAddress", - "type": "error" - }, - { - "inputs": [], - "name": "ZeroAmount", - "type": "error" - }, - { - "inputs": [], - "name": "ZeroDebt", - "type": "error" - }, - { - "inputs": [], - "name": "ZeroShortAmount", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "dsaVToken", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint8", - "name": "index", - "type": "uint8" - }, - { - "indexed": false, - "internalType": "bool", - "name": "active", - "type": "bool" - } - ], - "name": "DSAVTokenActiveUpdated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "dsaVToken", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint8", - "name": "index", - "type": "uint8" - } - ], - "name": "DSAVTokenAdded", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint8", - "name": "version", - "type": "uint8" - } - ], - "name": "Initialized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "oldAccessControlManager", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "newAccessControlManager", - "type": "address" - } - ], - "name": "NewAccessControlManager", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferStarted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferred", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "Paused", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "longAsset", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "shortAsset", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "positionAccount", - "type": "address" - } - ], - "name": "PositionAccountDeployed", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "oldImplementation", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newImplementation", - "type": "address" - } - ], - "name": "PositionAccountImplementationUpdated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "longAsset", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "shortAsset", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "dsaAsset", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "cycleId", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "initialPrincipal", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "effectiveLeverage", - "type": "uint256" - } - ], - "name": "PositionActivated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "cycleId", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "closeFractionBps", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amountRepaid", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amountRedeemed", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amountRedeemedDsa", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "longDustRedeemed", - "type": "uint256" - } - ], - "name": "PositionClosed", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "cycleId", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "address", - "name": "dsaAsset", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amountWithdrawn", - "type": "uint256" - } - ], - "name": "PositionDeactivated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "cycleId", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "address", - "name": "longAsset", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "shortAsset", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "dsaAsset", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "shortAmount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "additionalPrincipal", - "type": "uint256" - } - ], - "name": "PositionOpened", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "cycleId", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "address", - "name": "dsaAsset", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "newTotalPrincipal", - "type": "uint256" - } - ], - "name": "PrincipalSupplied", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "dsaAsset", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "remainingPrincipal", - "type": "uint256" - } - ], - "name": "PrincipalWithdrawn", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amountConvertedToProfit", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "newTotalPrincipal", - "type": "uint256" - } - ], - "name": "ProfitConverted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "oldSuppliedPrincipal", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "newSuppliedPrincipal", - "type": "uint256" - } - ], - "name": "RefreshedSuppliedPrincipal", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "UnderlyingTransferred", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "Unpaused", - "type": "event" - }, - { - "inputs": [], - "name": "COMPTROLLER", - "outputs": [ - { - "internalType": "contract IComptroller", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "LEVERAGE_MANAGER", - "outputs": [ - { - "internalType": "contract LeverageStrategiesManager", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "POSITION_ACCOUNT_IMPLEMENTATION", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "acceptOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "accessControlManager", - "outputs": [ - { - "internalType": "contract IAccessControlManagerV8", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "address", - "name": "shortVToken", - "type": "address" - }, - { - "internalType": "uint8", - "name": "dsaIndex", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "initialPrincipal", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "effectiveLeverage", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "shortAmount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minLongAmount", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "swapData", - "type": "bytes" - } - ], - "name": "activateAndOpenPosition", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "address", - "name": "shortVToken", - "type": "address" - }, - { - "internalType": "uint8", - "name": "dsaIndex", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "initialPrincipal", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "effectiveLeverage", - "type": "uint256" - } - ], - "name": "activatePosition", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "dsaVToken", - "type": "address" - } - ], - "name": "addDSAVToken", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - } - ], - "name": "calculateMaxBorrow", - "outputs": [ - { - "internalType": "uint256", - "name": "maxBorrowAmount", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - }, - { - "internalType": "uint256", - "name": "closeFractionBps", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "longAmountToRedeemForFirstSwap", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "shortAmountToRepayForFirstSwap", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minAmountOutFirst", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "swapDataFirst", - "type": "bytes" - }, - { - "internalType": "uint256", - "name": "dsaAmountToRedeemForSecondSwap", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minAmountOutSecond", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "swapDataSecond", - "type": "bytes" - } - ], - "name": "closeWithLoss", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - }, - { - "internalType": "uint256", - "name": "closeFractionBps", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "longAmountToRedeemForRepay", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minAmountOutRepay", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "swapDataRepay", - "type": "bytes" - }, - { - "internalType": "uint256", - "name": "longAmountToRedeemForProfit", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minAmountOutProfit", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "swapDataProfit", - "type": "bytes" - } - ], - "name": "closeWithProfit", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - } - ], - "name": "deactivatePosition", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "dsaVTokenIndexCounter", - "outputs": [ - { - "internalType": "uint8", - "name": "", - "type": "uint8" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint8", - "name": "", - "type": "uint8" - } - ], - "name": "dsaVTokens", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "internalType": "address[]", - "name": "targets", - "type": "address[]" - }, - { - "internalType": "bytes[]", - "name": "data", - "type": "bytes[]" - } - ], - "name": "executePositionAccountCall", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "getDsaVTokens", - "outputs": [ - { - "internalType": "address[]", - "name": "dsaVTokensList", - "type": "address[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - } - ], - "name": "getLongCollateralBalance", - "outputs": [ - { - "internalType": "uint256", - "name": "longBalance", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - } - ], - "name": "getPosition", - "outputs": [ - { - "components": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "address", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "address", - "name": "shortVToken", - "type": "address" - }, - { - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "internalType": "bool", - "name": "isActive", - "type": "bool" - }, - { - "internalType": "uint8", - "name": "dsaIndex", - "type": "uint8" - }, - { - "internalType": "address", - "name": "dsaVToken", - "type": "address" - }, - { - "internalType": "uint256", - "name": "suppliedPrincipalVTokens", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "effectiveLeverage", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "cycleId", - "type": "uint256" - } - ], - "internalType": "struct IRelativePositionManager.Position", - "name": "position", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - } - ], - "name": "getPositionAccountAddress", - "outputs": [ - { - "internalType": "address", - "name": "predicted", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - } - ], - "name": "getSuppliedPrincipalBalance", - "outputs": [ - { - "internalType": "uint256", - "name": "balance", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - } - ], - "name": "getUtilizationInfo", - "outputs": [ - { - "components": [ - { - "internalType": "uint256", - "name": "actualCapitalUtilized", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "nominalCapitalUtilized", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "finalCapitalUtilized", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "availableCapitalUSD", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "withdrawableAmount", - "type": "uint256" - } - ], - "internalType": "struct IRelativePositionManager.UtilizationInfo", - "name": "utilization", - "type": "tuple" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "accessControlManager_", - "type": "address" - } - ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "isDsaVTokenActive", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - }, - { - "internalType": "uint256", - "name": "additionalPrincipal", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "shortAmount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minLongAmount", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "swapData", - "type": "bytes" - } - ], - "name": "openPosition", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "owner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "pause", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "paused", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "pendingOwner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - }, - { - "internalType": "address", - "name": "", - "type": "address" - }, - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "positions", - "outputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "address", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "address", - "name": "shortVToken", - "type": "address" - }, - { - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "internalType": "bool", - "name": "isActive", - "type": "bool" - }, - { - "internalType": "uint8", - "name": "dsaIndex", - "type": "uint8" - }, - { - "internalType": "address", - "name": "dsaVToken", - "type": "address" - }, - { - "internalType": "uint256", - "name": "suppliedPrincipalVTokens", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "effectiveLeverage", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "cycleId", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "renounceOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "accessControlManager_", - "type": "address" - } - ], - "name": "setAccessControlManager", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint8", - "name": "dsaIndex", - "type": "uint8" - }, - { - "internalType": "bool", - "name": "active", - "type": "bool" - } - ], - "name": "setDSAVTokenActive", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "positionAccountImpl", - "type": "address" - } - ], - "name": "setPositionAccountImplementation", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "address", - "name": "shortVToken", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "supplyPrincipal", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "transferOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "unpause", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "withdrawPrincipal", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } - ] \ No newline at end of file + { + "inputs": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "address", + "name": "leverageManager", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "AssetNotListed", + "type": "error" + }, + { + "inputs": [], + "name": "BorrowAmountExceedsMaximum", + "type": "error" + }, + { + "inputs": [], + "name": "DSAInactive", + "type": "error" + }, + { + "inputs": [], + "name": "DSAVTokenAlreadyAdded", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "errorCode", + "type": "uint256" + } + ], + "name": "EnterMarketFailed", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientPrincipal", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientWithdrawableAmount", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidCloseFractionBps", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidCollateralFactor", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidDSA", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidLeverage", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidLongAmountToRedeem", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidOraclePrice", + "type": "error" + }, + { + "inputs": [], + "name": "MinAmountOutRepayBelowDebt", + "type": "error" + }, + { + "inputs": [], + "name": "MinAmountOutSecondBelowDebt", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "errorCode", + "type": "uint256" + } + ], + "name": "MintBehalfFailed", + "type": "error" + }, + { + "inputs": [], + "name": "PositionAccountImplementationNotSet", + "type": "error" + }, + { + "inputs": [], + "name": "PositionAlreadyExists", + "type": "error" + }, + { + "inputs": [], + "name": "PositionNotActive", + "type": "error" + }, + { + "inputs": [], + "name": "PositionNotFullyClosed", + "type": "error" + }, + { + "inputs": [], + "name": "ProportionalCloseAmountOutOfTolerance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "errorCode", + "type": "uint256" + } + ], + "name": "RedeemBehalfFailed", + "type": "error" + }, + { + "inputs": [], + "name": "SameDSAActiveStatus", + "type": "error" + }, + { + "inputs": [], + "name": "SameMarketNotAllowed", + "type": "error" + }, + { + "inputs": [], + "name": "SamePositionAccountImplementation", + "type": "error" + }, + { + "inputs": [], + "name": "SlippageExceeded", + "type": "error" + }, + { + "inputs": [], + "name": "TokenSwapCallFailed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "calledContract", + "type": "address" + }, + { + "internalType": "string", + "name": "methodSignature", + "type": "string" + } + ], + "name": "Unauthorized", + "type": "error" + }, + { + "inputs": [], + "name": "VBNBNotSupported", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroAmount", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroDebt", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroShortAmount", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "dsaVToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "index", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "bool", + "name": "active", + "type": "bool" + } + ], + "name": "DSAVTokenActiveUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "dsaVToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "index", + "type": "uint8" + } + ], + "name": "DSAVTokenAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "oldAccessControlManager", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAccessControlManager", + "type": "address" + } + ], + "name": "NewAccessControlManager", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "longAsset", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "shortAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "positionAccount", + "type": "address" + } + ], + "name": "PositionAccountDeployed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldImplementation", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newImplementation", + "type": "address" + } + ], + "name": "PositionAccountImplementationUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "longAsset", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "shortAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "dsaAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "cycleId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "initialPrincipal", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "effectiveLeverage", + "type": "uint256" + } + ], + "name": "PositionActivated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "cycleId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "closeFractionBps", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountRepaid", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountRedeemed", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountRedeemedDsa", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "longDustRedeemed", + "type": "uint256" + } + ], + "name": "PositionClosed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "cycleId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "dsaAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountWithdrawn", + "type": "uint256" + } + ], + "name": "PositionDeactivated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "cycleId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "longAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "shortAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "dsaAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "shortAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "additionalPrincipal", + "type": "uint256" + } + ], + "name": "PositionOpened", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "cycleId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "dsaAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newTotalPrincipal", + "type": "uint256" + } + ], + "name": "PrincipalSupplied", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "dsaAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "remainingPrincipal", + "type": "uint256" + } + ], + "name": "PrincipalWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountConvertedToProfit", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newTotalPrincipal", + "type": "uint256" + } + ], + "name": "ProfitConverted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "oldSuppliedPrincipal", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newSuppliedPrincipal", + "type": "uint256" + } + ], + "name": "RefreshedSuppliedPrincipal", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "UnderlyingTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "inputs": [], + "name": "COMPTROLLER", + "outputs": [ + { + "internalType": "contract IComptroller", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LEVERAGE_MANAGER", + "outputs": [ + { + "internalType": "contract LeverageStrategiesManager", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "POSITION_ACCOUNT_IMPLEMENTATION", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "accessControlManager", + "outputs": [ + { + "internalType": "contract IAccessControlManagerV8", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "address", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "uint8", + "name": "dsaIndex", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "initialPrincipal", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "effectiveLeverage", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minLongAmount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapData", + "type": "bytes" + } + ], + "name": "activateAndOpenPosition", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "address", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "uint8", + "name": "dsaIndex", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "initialPrincipal", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "effectiveLeverage", + "type": "uint256" + } + ], + "name": "activatePosition", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "dsaVToken", + "type": "address" + } + ], + "name": "addDSAVToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + } + ], + "name": "calculateMaxBorrow", + "outputs": [ + { + "internalType": "uint256", + "name": "maxBorrowAmount", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "closeFractionBps", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "longAmountToRedeemForFirstSwap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortAmountToRepayForFirstSwap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOutFirst", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapDataFirst", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "dsaAmountToRedeemForSecondSwap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOutSecond", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapDataSecond", + "type": "bytes" + } + ], + "name": "closeWithLoss", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "closeFractionBps", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "longAmountToRedeemForRepay", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOutRepay", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapDataRepay", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "longAmountToRedeemForProfit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOutProfit", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapDataProfit", + "type": "bytes" + } + ], + "name": "closeWithProfit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + } + ], + "name": "deactivatePosition", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "dsaVTokenIndexCounter", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "name": "dsaVTokens", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "internalType": "address[]", + "name": "targets", + "type": "address[]" + }, + { + "internalType": "bytes[]", + "name": "data", + "type": "bytes[]" + } + ], + "name": "executePositionAccountCall", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getDsaVTokens", + "outputs": [ + { + "internalType": "address[]", + "name": "dsaVTokensList", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + } + ], + "name": "getLongCollateralBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "longBalance", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + } + ], + "name": "getPosition", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "address", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "address", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "internalType": "bool", + "name": "isActive", + "type": "bool" + }, + { + "internalType": "uint8", + "name": "dsaIndex", + "type": "uint8" + }, + { + "internalType": "address", + "name": "dsaVToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "suppliedPrincipalVTokens", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "effectiveLeverage", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "cycleId", + "type": "uint256" + } + ], + "internalType": "struct IRelativePositionManager.Position", + "name": "position", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + } + ], + "name": "getPositionAccountAddress", + "outputs": [ + { + "internalType": "address", + "name": "predicted", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + } + ], + "name": "getSuppliedPrincipalBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + } + ], + "name": "getUtilizationInfo", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "actualCapitalUtilized", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nominalCapitalUtilized", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "finalCapitalUtilized", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "availableCapitalUSD", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "withdrawableAmount", + "type": "uint256" + } + ], + "internalType": "struct IRelativePositionManager.UtilizationInfo", + "name": "utilization", + "type": "tuple" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "accessControlManager_", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isDsaVTokenActive", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "additionalPrincipal", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minLongAmount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapData", + "type": "bytes" + } + ], + "name": "openPosition", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "positions", + "outputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "address", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "address", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "internalType": "bool", + "name": "isActive", + "type": "bool" + }, + { + "internalType": "uint8", + "name": "dsaIndex", + "type": "uint8" + }, + { + "internalType": "address", + "name": "dsaVToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "suppliedPrincipalVTokens", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "effectiveLeverage", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "cycleId", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "accessControlManager_", + "type": "address" + } + ], + "name": "setAccessControlManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "dsaIndex", + "type": "uint8" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + } + ], + "name": "setDSAVTokenActive", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "positionAccountImpl", + "type": "address" + } + ], + "name": "setPositionAccountImplementation", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "address", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "supplyPrincipal", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "withdrawPrincipal", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/simulations/vip-610/bsctestnet.ts b/simulations/vip-610/bsctestnet.ts index 51b14d84d..4b7840c11 100644 --- a/simulations/vip-610/bsctestnet.ts +++ b/simulations/vip-610/bsctestnet.ts @@ -49,16 +49,19 @@ forking(90876709, async () => { expect(await comptroller.authorizedFlashLoan(LEVERAGE_STRATEGIES_MANAGER)).to.equal(false); }); - it("SwapHelper should have NORMAL_TIMELOCK as owner", async () => { + it("SwapHelper should have NORMAL_TIMELOCK as pending owner and not yet as owner", async () => { expect(await swapHelper.owner()).not.equal(bsctestnet.NORMAL_TIMELOCK); + expect(await swapHelper.pendingOwner()).to.equal(bsctestnet.NORMAL_TIMELOCK); }); - it("LeverageStrategiesManager should have NORMAL_TIMELOCK as pending owner", async () => { + it("LeverageStrategiesManager should have NORMAL_TIMELOCK as pending owner and not yet as owner", async () => { expect(await leverageStrategiesManager.owner()).not.equal(bsctestnet.NORMAL_TIMELOCK); + expect(await leverageStrategiesManager.pendingOwner()).to.equal(bsctestnet.NORMAL_TIMELOCK); }); - it("RelativePositionManager should have NORMAL_TIMELOCK as pending owner", async () => { + it("RelativePositionManager should have NORMAL_TIMELOCK as pending owner and not yet as owner", async () => { expect(await relativePositionManager.owner()).not.equal(bsctestnet.NORMAL_TIMELOCK); + expect(await relativePositionManager.pendingOwner()).to.equal(bsctestnet.NORMAL_TIMELOCK); }); it("Timelocks/Guardian should not have ACM permissions on RelativePositionManager", async () => { From a22e962b44a60d11e9f9f64198b721c105689eb6 Mon Sep 17 00:00:00 2001 From: GitGuru7 Date: Tue, 17 Feb 2026 17:42:26 +0530 Subject: [PATCH 03/13] fix: lint error --- simulations/vip-610/abi/{Swaphelper.json => SwapHelperAbi.json} | 0 simulations/vip-610/bsctestnet.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename simulations/vip-610/abi/{Swaphelper.json => SwapHelperAbi.json} (100%) diff --git a/simulations/vip-610/abi/Swaphelper.json b/simulations/vip-610/abi/SwapHelperAbi.json similarity index 100% rename from simulations/vip-610/abi/Swaphelper.json rename to simulations/vip-610/abi/SwapHelperAbi.json diff --git a/simulations/vip-610/bsctestnet.ts b/simulations/vip-610/bsctestnet.ts index 4b7840c11..d4ee145f0 100644 --- a/simulations/vip-610/bsctestnet.ts +++ b/simulations/vip-610/bsctestnet.ts @@ -16,7 +16,7 @@ import ACCESS_CONTROL_MANAGER_ABI from "./abi/AccessControlManager.json"; import FLASHLOAN_FACET_ABI from "./abi/FlashLoanFacet.json"; import LEVERAGE_MANAGER_ABI from "./abi/LeverageStrategiesManager.json"; import RELATIVE_POSITION_MANAGER_ABI from "./abi/RelativePositionManager.json"; -import SWAP_HELPER_ABI from "./abi/SwapHelper.json"; +import SWAP_HELPER_ABI from "./abi/SwapHelperAbi.json"; const { bsctestnet } = NETWORK_ADDRESSES; From 1304e97cfb65ddc49d7b7a0345a55ac20e68e5d8 Mon Sep 17 00:00:00 2001 From: GitGuru7 Date: Tue, 17 Mar 2026 22:26:55 +0530 Subject: [PATCH 04/13] feat: add vip-610 bscmainnet and improve testnet simulation --- .../vip-610/abi/RelativePositionManager.json | 1612 +++-------------- simulations/vip-610/bscmainnet.ts | 115 ++ simulations/vip-610/bsctestnet.ts | 54 +- vips/vip-610/bscmainnet.ts | 61 + vips/vip-610/bsctestnet.ts | 37 +- 5 files changed, 546 insertions(+), 1333 deletions(-) create mode 100644 simulations/vip-610/bscmainnet.ts create mode 100644 vips/vip-610/bscmainnet.ts diff --git a/simulations/vip-610/abi/RelativePositionManager.json b/simulations/vip-610/abi/RelativePositionManager.json index ed0ce022f..b4e6dbcfb 100644 --- a/simulations/vip-610/abi/RelativePositionManager.json +++ b/simulations/vip-610/abi/RelativePositionManager.json @@ -1,240 +1,83 @@ [ { "inputs": [ - { - "internalType": "address", - "name": "comptroller", - "type": "address" - }, - { - "internalType": "address", - "name": "leverageManager", - "type": "address" - } + { "internalType": "address", "name": "comptroller", "type": "address" }, + { "internalType": "address", "name": "leverageManager", "type": "address" } ], "stateMutability": "nonpayable", "type": "constructor" }, + { "inputs": [], "name": "AssetNotListed", "type": "error" }, + { "inputs": [], "name": "BorrowAmountExceedsMaximum", "type": "error" }, + { "inputs": [], "name": "CompletelyPaused", "type": "error" }, + { "inputs": [], "name": "DSAInactive", "type": "error" }, + { "inputs": [], "name": "DSAVTokenAlreadyAdded", "type": "error" }, { - "inputs": [], - "name": "AssetNotListed", - "type": "error" - }, - { - "inputs": [], - "name": "BorrowAmountExceedsMaximum", - "type": "error" - }, - { - "inputs": [], - "name": "DSAInactive", - "type": "error" - }, - { - "inputs": [], - "name": "DSAVTokenAlreadyAdded", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "errorCode", - "type": "uint256" - } - ], + "inputs": [{ "internalType": "uint256", "name": "errorCode", "type": "uint256" }], "name": "EnterMarketFailed", "type": "error" }, - { - "inputs": [], - "name": "InsufficientPrincipal", - "type": "error" - }, - { - "inputs": [], - "name": "InsufficientWithdrawableAmount", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidCloseFractionBps", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidCollateralFactor", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidDSA", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidLeverage", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidLongAmountToRedeem", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidOraclePrice", - "type": "error" - }, - { - "inputs": [], - "name": "MinAmountOutRepayBelowDebt", - "type": "error" - }, - { - "inputs": [], - "name": "MinAmountOutSecondBelowDebt", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "errorCode", - "type": "uint256" - } - ], + { "inputs": [], "name": "ExcessiveShortDust", "type": "error" }, + { "inputs": [], "name": "InsufficientPrincipal", "type": "error" }, + { "inputs": [], "name": "InsufficientWithdrawableAmount", "type": "error" }, + { "inputs": [], "name": "InvalidCloseFractionBps", "type": "error" }, + { "inputs": [], "name": "InvalidCollateralFactor", "type": "error" }, + { "inputs": [], "name": "InvalidDSA", "type": "error" }, + { "inputs": [], "name": "InvalidLeverage", "type": "error" }, + { "inputs": [], "name": "InvalidLongAmountToRedeem", "type": "error" }, + { "inputs": [], "name": "InvalidOraclePrice", "type": "error" }, + { "inputs": [], "name": "InvalidProportionalCloseTolerance", "type": "error" }, + { "inputs": [], "name": "MinAmountOutRepayBelowDebt", "type": "error" }, + { "inputs": [], "name": "MinAmountOutSecondBelowDebt", "type": "error" }, + { + "inputs": [{ "internalType": "uint256", "name": "errorCode", "type": "uint256" }], "name": "MintBehalfFailed", "type": "error" }, + { "inputs": [], "name": "PartiallyPaused", "type": "error" }, + { "inputs": [], "name": "PositionAccountImplementationLocked", "type": "error" }, + { "inputs": [], "name": "PositionAccountImplementationNotSet", "type": "error" }, + { "inputs": [], "name": "PositionAlreadyExists", "type": "error" }, + { "inputs": [], "name": "PositionNotActive", "type": "error" }, + { "inputs": [], "name": "PositionNotFullyClosed", "type": "error" }, + { "inputs": [], "name": "ProportionalCloseAmountOutOfTolerance", "type": "error" }, { - "inputs": [], - "name": "PositionAccountImplementationNotSet", - "type": "error" - }, - { - "inputs": [], - "name": "PositionAlreadyExists", - "type": "error" - }, - { - "inputs": [], - "name": "PositionNotActive", - "type": "error" - }, - { - "inputs": [], - "name": "PositionNotFullyClosed", - "type": "error" - }, - { - "inputs": [], - "name": "ProportionalCloseAmountOutOfTolerance", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "errorCode", - "type": "uint256" - } - ], + "inputs": [{ "internalType": "uint256", "name": "errorCode", "type": "uint256" }], "name": "RedeemBehalfFailed", "type": "error" }, - { - "inputs": [], - "name": "SameDSAActiveStatus", - "type": "error" - }, - { - "inputs": [], - "name": "SameMarketNotAllowed", - "type": "error" - }, - { - "inputs": [], - "name": "SamePositionAccountImplementation", - "type": "error" - }, - { - "inputs": [], - "name": "SlippageExceeded", - "type": "error" - }, - { - "inputs": [], - "name": "TokenSwapCallFailed", - "type": "error" - }, + { "inputs": [], "name": "SameDSAActiveStatus", "type": "error" }, + { "inputs": [], "name": "SameMarketNotAllowed", "type": "error" }, + { "inputs": [], "name": "SameProportionalCloseTolerance", "type": "error" }, + { "inputs": [], "name": "SlippageExceeded", "type": "error" }, + { "inputs": [], "name": "TokenSwapCallFailed", "type": "error" }, { "inputs": [ - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address", - "name": "calledContract", - "type": "address" - }, - { - "internalType": "string", - "name": "methodSignature", - "type": "string" - } + { "internalType": "address", "name": "sender", "type": "address" }, + { "internalType": "address", "name": "calledContract", "type": "address" }, + { "internalType": "string", "name": "methodSignature", "type": "string" } ], "name": "Unauthorized", "type": "error" }, + { "inputs": [], "name": "VBNBNotSupported", "type": "error" }, + { "inputs": [], "name": "ZeroAddress", "type": "error" }, + { "inputs": [], "name": "ZeroAmount", "type": "error" }, + { "inputs": [], "name": "ZeroDebt", "type": "error" }, + { "inputs": [], "name": "ZeroShortAmount", "type": "error" }, + { "inputs": [], "name": "ZeroVTokensMinted", "type": "error" }, { - "inputs": [], - "name": "VBNBNotSupported", - "type": "error" - }, - { - "inputs": [], - "name": "ZeroAddress", - "type": "error" - }, - { - "inputs": [], - "name": "ZeroAmount", - "type": "error" - }, - { - "inputs": [], - "name": "ZeroDebt", - "type": "error" - }, - { - "inputs": [], - "name": "ZeroShortAmount", - "type": "error" + "anonymous": false, + "inputs": [{ "indexed": false, "internalType": "bool", "name": "paused", "type": "bool" }], + "name": "CompletePauseToggled", + "type": "event" }, { "anonymous": false, "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "dsaVToken", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint8", - "name": "index", - "type": "uint8" - }, - { - "indexed": false, - "internalType": "bool", - "name": "active", - "type": "bool" - } + { "indexed": true, "internalType": "address", "name": "dsaVToken", "type": "address" }, + { "indexed": false, "internalType": "uint8", "name": "index", "type": "uint8" }, + { "indexed": false, "internalType": "bool", "name": "active", "type": "bool" } ], "name": "DSAVTokenActiveUpdated", "type": "event" @@ -242,50 +85,23 @@ { "anonymous": false, "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "dsaVToken", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint8", - "name": "index", - "type": "uint8" - } + { "indexed": true, "internalType": "address", "name": "dsaVToken", "type": "address" }, + { "indexed": false, "internalType": "uint8", "name": "index", "type": "uint8" } ], "name": "DSAVTokenAdded", "type": "event" }, { "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint8", - "name": "version", - "type": "uint8" - } - ], + "inputs": [{ "indexed": false, "internalType": "uint8", "name": "version", "type": "uint8" }], "name": "Initialized", "type": "event" }, { "anonymous": false, "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "oldAccessControlManager", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "newAccessControlManager", - "type": "address" - } + { "indexed": false, "internalType": "address", "name": "oldAccessControlManager", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "newAccessControlManager", "type": "address" } ], "name": "NewAccessControlManager", "type": "event" @@ -293,18 +109,8 @@ { "anonymous": false, "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } + { "indexed": true, "internalType": "address", "name": "previousOwner", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "newOwner", "type": "address" } ], "name": "OwnershipTransferStarted", "type": "event" @@ -312,136 +118,46 @@ { "anonymous": false, "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } + { "indexed": true, "internalType": "address", "name": "previousOwner", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "newOwner", "type": "address" } ], "name": "OwnershipTransferred", "type": "event" }, { "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "Paused", + "inputs": [{ "indexed": false, "internalType": "bool", "name": "paused", "type": "bool" }], + "name": "PartialPauseToggled", "type": "event" }, { "anonymous": false, "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "longAsset", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "shortAsset", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "positionAccount", - "type": "address" - } + { "indexed": true, "internalType": "address", "name": "user", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "longAsset", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "shortAsset", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "positionAccount", "type": "address" } ], "name": "PositionAccountDeployed", "type": "event" }, { "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "oldImplementation", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newImplementation", - "type": "address" - } - ], - "name": "PositionAccountImplementationUpdated", + "inputs": [{ "indexed": true, "internalType": "address", "name": "implementation", "type": "address" }], + "name": "PositionAccountImplementationSet", "type": "event" }, { "anonymous": false, "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "longAsset", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "shortAsset", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "dsaAsset", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "cycleId", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "initialPrincipal", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "effectiveLeverage", - "type": "uint256" - } + { "indexed": true, "internalType": "address", "name": "user", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "longAsset", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "shortAsset", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "dsaAsset", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "positionAccount", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "cycleId", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "initialPrincipal", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "effectiveLeverage", "type": "uint256" } ], "name": "PositionActivated", "type": "event" @@ -449,54 +165,14 @@ { "anonymous": false, "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "cycleId", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "closeFractionBps", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amountRepaid", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amountRedeemed", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amountRedeemedDsa", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "longDustRedeemed", - "type": "uint256" - } + { "indexed": true, "internalType": "address", "name": "user", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "positionAccount", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "cycleId", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "closeFractionBps", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "amountRepaid", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "amountRedeemed", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "amountRedeemedDsa", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "longDustRedeemed", "type": "uint256" } ], "name": "PositionClosed", "type": "event" @@ -504,36 +180,11 @@ { "anonymous": false, "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "cycleId", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "address", - "name": "dsaAsset", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amountWithdrawn", - "type": "uint256" - } + { "indexed": true, "internalType": "address", "name": "user", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "positionAccount", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "cycleId", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "longRedeemed", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "dsaRedeemed", "type": "uint256" } ], "name": "PositionDeactivated", "type": "event" @@ -541,54 +192,14 @@ { "anonymous": false, "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "cycleId", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "address", - "name": "longAsset", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "shortAsset", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "dsaAsset", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "shortAmount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "additionalPrincipal", - "type": "uint256" - } + { "indexed": true, "internalType": "address", "name": "user", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "positionAccount", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "cycleId", "type": "uint256" }, + { "indexed": false, "internalType": "address", "name": "longAsset", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "shortAsset", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "dsaAsset", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "shortAmount", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "initialPrincipal", "type": "uint256" } ], "name": "PositionOpened", "type": "event" @@ -596,42 +207,27 @@ { "anonymous": false, "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "cycleId", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "address", - "name": "dsaAsset", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "newTotalPrincipal", - "type": "uint256" - } + { "indexed": true, "internalType": "address", "name": "user", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "positionAccount", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "cycleId", "type": "uint256" }, + { "indexed": false, "internalType": "address", "name": "longAsset", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "shortAsset", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "dsaAsset", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "shortAmount", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "additionalPrincipal", "type": "uint256" } + ], + "name": "PositionScaled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "user", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "positionAccount", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "cycleId", "type": "uint256" }, + { "indexed": false, "internalType": "address", "name": "dsaAsset", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "newTotalPrincipal", "type": "uint256" } ], "name": "PrincipalSupplied", "type": "event" @@ -639,36 +235,12 @@ { "anonymous": false, "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "dsaAsset", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "remainingPrincipal", - "type": "uint256" - } + { "indexed": true, "internalType": "address", "name": "user", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "positionAccount", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "cycleId", "type": "uint256" }, + { "indexed": false, "internalType": "address", "name": "dsaAsset", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "remainingPrincipal", "type": "uint256" } ], "name": "PrincipalWithdrawn", "type": "event" @@ -676,30 +248,10 @@ { "anonymous": false, "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amountConvertedToProfit", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "newTotalPrincipal", - "type": "uint256" - } + { "indexed": true, "internalType": "address", "name": "user", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "positionAccount", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "amountConvertedToProfit", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "newTotalPrincipal", "type": "uint256" } ], "name": "ProfitConverted", "type": "event" @@ -707,179 +259,73 @@ { "anonymous": false, "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "oldSuppliedPrincipal", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "newSuppliedPrincipal", - "type": "uint256" - } + { "indexed": true, "internalType": "uint256", "name": "oldTolerance", "type": "uint256" }, + { "indexed": true, "internalType": "uint256", "name": "newTolerance", "type": "uint256" } ], - "name": "RefreshedSuppliedPrincipal", + "name": "ProportionalCloseToleranceUpdated", "type": "event" }, { "anonymous": false, "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } + { "indexed": true, "internalType": "address", "name": "user", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "positionAccount", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "oldSuppliedPrincipal", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "newSuppliedPrincipal", "type": "uint256" } ], - "name": "UnderlyingTransferred", + "name": "RefreshedSuppliedPrincipal", "type": "event" }, { "anonymous": false, "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "account", - "type": "address" - } + { "indexed": true, "internalType": "address", "name": "token", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "from", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "to", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" } ], - "name": "Unpaused", + "name": "UnderlyingTransferred", "type": "event" }, { "inputs": [], "name": "COMPTROLLER", - "outputs": [ - { - "internalType": "contract IComptroller", - "name": "", - "type": "address" - } - ], + "outputs": [{ "internalType": "contract IComptroller", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "LEVERAGE_MANAGER", - "outputs": [ - { - "internalType": "contract LeverageStrategiesManager", - "name": "", - "type": "address" - } - ], + "outputs": [{ "internalType": "contract LeverageStrategiesManager", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "POSITION_ACCOUNT_IMPLEMENTATION", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "acceptOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, + { "inputs": [], "name": "acceptOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "accessControlManager", - "outputs": [ - { - "internalType": "contract IAccessControlManagerV8", - "name": "", - "type": "address" - } - ], + "outputs": [{ "internalType": "contract IAccessControlManagerV8", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [ - { - "internalType": "address", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "address", - "name": "shortVToken", - "type": "address" - }, - { - "internalType": "uint8", - "name": "dsaIndex", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "initialPrincipal", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "effectiveLeverage", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "shortAmount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minLongAmount", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "swapData", - "type": "bytes" - } + { "internalType": "address", "name": "longVToken", "type": "address" }, + { "internalType": "address", "name": "shortVToken", "type": "address" }, + { "internalType": "uint8", "name": "dsaIndex", "type": "uint8" }, + { "internalType": "uint256", "name": "initialPrincipal", "type": "uint256" }, + { "internalType": "uint256", "name": "effectiveLeverage", "type": "uint256" }, + { "internalType": "uint256", "name": "shortAmount", "type": "uint256" }, + { "internalType": "uint256", "name": "minLongAmount", "type": "uint256" }, + { "internalType": "bytes", "name": "swapData", "type": "bytes" } ], "name": "activateAndOpenPosition", "outputs": [], @@ -887,46 +333,7 @@ "type": "function" }, { - "inputs": [ - { - "internalType": "address", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "address", - "name": "shortVToken", - "type": "address" - }, - { - "internalType": "uint8", - "name": "dsaIndex", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "initialPrincipal", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "effectiveLeverage", - "type": "uint256" - } - ], - "name": "activatePosition", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "dsaVToken", - "type": "address" - } - ], + "inputs": [{ "internalType": "address", "name": "dsaVToken", "type": "address" }], "name": "addDSAVToken", "outputs": [], "stateMutability": "nonpayable", @@ -934,85 +341,16 @@ }, { "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - } - ], - "name": "calculateMaxBorrow", - "outputs": [ - { - "internalType": "uint256", - "name": "maxBorrowAmount", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - }, - { - "internalType": "uint256", - "name": "closeFractionBps", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "longAmountToRedeemForFirstSwap", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "shortAmountToRepayForFirstSwap", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minAmountOutFirst", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "swapDataFirst", - "type": "bytes" - }, - { - "internalType": "uint256", - "name": "dsaAmountToRedeemForSecondSwap", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minAmountOutSecond", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "swapDataSecond", - "type": "bytes" - } + { "internalType": "contract IVToken", "name": "longVToken", "type": "address" }, + { "internalType": "contract IVToken", "name": "shortVToken", "type": "address" }, + { "internalType": "uint256", "name": "closeFractionBps", "type": "uint256" }, + { "internalType": "uint256", "name": "longAmountToRedeemForFirstSwap", "type": "uint256" }, + { "internalType": "uint256", "name": "shortAmountToRepayForFirstSwap", "type": "uint256" }, + { "internalType": "uint256", "name": "minAmountOutFirst", "type": "uint256" }, + { "internalType": "bytes", "name": "swapDataFirst", "type": "bytes" }, + { "internalType": "uint256", "name": "dsaAmountToRedeemForSecondSwap", "type": "uint256" }, + { "internalType": "uint256", "name": "minAmountOutSecond", "type": "uint256" }, + { "internalType": "bytes", "name": "swapDataSecond", "type": "bytes" } ], "name": "closeWithLoss", "outputs": [], @@ -1021,69 +359,27 @@ }, { "inputs": [ - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - }, - { - "internalType": "uint256", - "name": "closeFractionBps", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "longAmountToRedeemForRepay", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minAmountOutRepay", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "swapDataRepay", - "type": "bytes" - }, - { - "internalType": "uint256", - "name": "longAmountToRedeemForProfit", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minAmountOutProfit", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "swapDataProfit", - "type": "bytes" - } + { "internalType": "contract IVToken", "name": "longVToken", "type": "address" }, + { "internalType": "contract IVToken", "name": "shortVToken", "type": "address" }, + { "internalType": "uint256", "name": "closeFractionBps", "type": "uint256" }, + { "internalType": "uint256", "name": "longAmountToRedeemForRepay", "type": "uint256" }, + { "internalType": "uint256", "name": "minAmountOutRepay", "type": "uint256" }, + { "internalType": "bytes", "name": "swapDataRepay", "type": "bytes" }, + { "internalType": "uint256", "name": "longAmountToRedeemForProfit", "type": "uint256" }, + { "internalType": "uint256", "name": "minAmountOutProfit", "type": "uint256" }, + { "internalType": "bytes", "name": "swapDataProfit", "type": "bytes" } ], "name": "closeWithProfit", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { "inputs": [], "name": "completePause", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { "inputs": [], "name": "completeUnpause", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - } + { "internalType": "contract IVToken", "name": "longVToken", "type": "address" }, + { "internalType": "contract IVToken", "name": "shortVToken", "type": "address" } ], "name": "deactivatePosition", "outputs": [], @@ -1093,172 +389,87 @@ { "inputs": [], "name": "dsaVTokenIndexCounter", - "outputs": [ - { - "internalType": "uint8", - "name": "", - "type": "uint8" - } - ], + "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }], "stateMutability": "view", "type": "function" }, { - "inputs": [ - { - "internalType": "uint8", - "name": "", - "type": "uint8" - } - ], + "inputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }], "name": "dsaVTokens", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [ - { - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "internalType": "address[]", - "name": "targets", - "type": "address[]" - }, - { - "internalType": "bytes[]", - "name": "data", - "type": "bytes[]" - } + { "internalType": "address", "name": "positionAccount", "type": "address" }, + { "internalType": "address[]", "name": "targets", "type": "address[]" }, + { "internalType": "bytes[]", "name": "data", "type": "bytes[]" } ], "name": "executePositionAccountCall", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { "internalType": "address", "name": "user", "type": "address" }, + { "internalType": "contract IVToken", "name": "longVToken", "type": "address" }, + { "internalType": "contract IVToken", "name": "shortVToken", "type": "address" } + ], + "name": "getAvailableShortCapacity", + "outputs": [{ "internalType": "uint256", "name": "availableCapacity", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [], "name": "getDsaVTokens", - "outputs": [ - { - "internalType": "address[]", - "name": "dsaVTokensList", - "type": "address[]" - } - ], + "outputs": [{ "internalType": "address[]", "name": "dsaVTokensList", "type": "address[]" }], "stateMutability": "view", "type": "function" }, { "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - } + { "internalType": "address", "name": "user", "type": "address" }, + { "internalType": "contract IVToken", "name": "longVToken", "type": "address" }, + { "internalType": "contract IVToken", "name": "shortVToken", "type": "address" } ], "name": "getLongCollateralBalance", - "outputs": [ - { - "internalType": "uint256", - "name": "longBalance", - "type": "uint256" - } - ], + "outputs": [{ "internalType": "uint256", "name": "longBalance", "type": "uint256" }], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - } + { "internalType": "contract IVToken", "name": "dsaVToken", "type": "address" }, + { "internalType": "address", "name": "longVToken", "type": "address" } + ], + "name": "getMaxLeverageAllowed", + "outputs": [{ "internalType": "uint256", "name": "maxLeverage", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "user", "type": "address" }, + { "internalType": "contract IVToken", "name": "longVToken", "type": "address" }, + { "internalType": "contract IVToken", "name": "shortVToken", "type": "address" } ], "name": "getPosition", "outputs": [ { "components": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "address", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "address", - "name": "shortVToken", - "type": "address" - }, - { - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "internalType": "bool", - "name": "isActive", - "type": "bool" - }, - { - "internalType": "uint8", - "name": "dsaIndex", - "type": "uint8" - }, - { - "internalType": "address", - "name": "dsaVToken", - "type": "address" - }, - { - "internalType": "uint256", - "name": "suppliedPrincipalVTokens", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "effectiveLeverage", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "cycleId", - "type": "uint256" - } + { "internalType": "address", "name": "user", "type": "address" }, + { "internalType": "address", "name": "longVToken", "type": "address" }, + { "internalType": "address", "name": "shortVToken", "type": "address" }, + { "internalType": "address", "name": "positionAccount", "type": "address" }, + { "internalType": "bool", "name": "isActive", "type": "bool" }, + { "internalType": "uint8", "name": "dsaIndex", "type": "uint8" }, + { "internalType": "address", "name": "dsaVToken", "type": "address" }, + { "internalType": "uint256", "name": "suppliedPrincipalVTokens", "type": "uint256" }, + { "internalType": "uint256", "name": "effectiveLeverage", "type": "uint256" }, + { "internalType": "uint256", "name": "cycleId", "type": "uint256" } ], "internalType": "struct IRelativePositionManager.Position", "name": "position", @@ -1270,109 +481,42 @@ }, { "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - } + { "internalType": "address", "name": "user", "type": "address" }, + { "internalType": "contract IVToken", "name": "longVToken", "type": "address" }, + { "internalType": "contract IVToken", "name": "shortVToken", "type": "address" } ], "name": "getPositionAccountAddress", - "outputs": [ - { - "internalType": "address", - "name": "predicted", - "type": "address" - } - ], + "outputs": [{ "internalType": "address", "name": "predicted", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - } + { "internalType": "address", "name": "user", "type": "address" }, + { "internalType": "contract IVToken", "name": "longVToken", "type": "address" }, + { "internalType": "contract IVToken", "name": "shortVToken", "type": "address" } ], "name": "getSuppliedPrincipalBalance", - "outputs": [ - { - "internalType": "uint256", - "name": "balance", - "type": "uint256" - } - ], + "outputs": [{ "internalType": "uint256", "name": "balance", "type": "uint256" }], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - } + { "internalType": "address", "name": "user", "type": "address" }, + { "internalType": "contract IVToken", "name": "longVToken", "type": "address" }, + { "internalType": "contract IVToken", "name": "shortVToken", "type": "address" } ], "name": "getUtilizationInfo", "outputs": [ { "components": [ - { - "internalType": "uint256", - "name": "actualCapitalUtilized", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "nominalCapitalUtilized", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "finalCapitalUtilized", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "availableCapitalUSD", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "withdrawableAmount", - "type": "uint256" - } + { "internalType": "uint256", "name": "actualCapitalUtilized", "type": "uint256" }, + { "internalType": "uint256", "name": "nominalCapitalUtilized", "type": "uint256" }, + { "internalType": "uint256", "name": "finalCapitalUtilized", "type": "uint256" }, + { "internalType": "uint256", "name": "availableCapitalUSD", "type": "uint256" }, + { "internalType": "uint256", "name": "withdrawableAmount", "type": "uint256" }, + { "internalType": "uint256", "name": "clampedLeverage", "type": "uint256" } ], "internalType": "struct IRelativePositionManager.UtilizationInfo", "name": "utilization", @@ -1383,210 +527,102 @@ "type": "function" }, { - "inputs": [ - { - "internalType": "address", - "name": "accessControlManager_", - "type": "address" - } - ], + "inputs": [{ "internalType": "address", "name": "accessControlManager_", "type": "address" }], "name": "initialize", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "isDsaVTokenActive", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], + "inputs": [], + "name": "isCompletelyPaused", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], "stateMutability": "view", "type": "function" }, { - "inputs": [ - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - }, - { - "internalType": "uint256", - "name": "additionalPrincipal", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "shortAmount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minLongAmount", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "swapData", - "type": "bytes" - } - ], - "name": "openPosition", - "outputs": [], - "stateMutability": "nonpayable", + "inputs": [{ "internalType": "address", "name": "", "type": "address" }], + "name": "isDsaVTokenActive", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", "type": "function" }, { "inputs": [], - "name": "owner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], + "name": "isPartiallyPaused", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], "stateMutability": "view", "type": "function" }, { "inputs": [], - "name": "pause", - "outputs": [], - "stateMutability": "nonpayable", + "name": "isPositionAccountImplementationLocked", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", "type": "function" }, { "inputs": [], - "name": "paused", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], + "name": "owner", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "partialPause", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { "inputs": [], "name": "partialUnpause", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "pendingOwner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - }, - { - "internalType": "address", - "name": "", - "type": "address" - }, - { - "internalType": "address", - "name": "", - "type": "address" - } + { "internalType": "address", "name": "", "type": "address" }, + { "internalType": "address", "name": "", "type": "address" }, + { "internalType": "address", "name": "", "type": "address" } ], "name": "positions", "outputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "address", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "address", - "name": "shortVToken", - "type": "address" - }, - { - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "internalType": "bool", - "name": "isActive", - "type": "bool" - }, - { - "internalType": "uint8", - "name": "dsaIndex", - "type": "uint8" - }, - { - "internalType": "address", - "name": "dsaVToken", - "type": "address" - }, - { - "internalType": "uint256", - "name": "suppliedPrincipalVTokens", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "effectiveLeverage", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "cycleId", - "type": "uint256" - } + { "internalType": "address", "name": "user", "type": "address" }, + { "internalType": "address", "name": "longVToken", "type": "address" }, + { "internalType": "address", "name": "shortVToken", "type": "address" }, + { "internalType": "address", "name": "positionAccount", "type": "address" }, + { "internalType": "bool", "name": "isActive", "type": "bool" }, + { "internalType": "uint8", "name": "dsaIndex", "type": "uint8" }, + { "internalType": "address", "name": "dsaVToken", "type": "address" }, + { "internalType": "uint256", "name": "suppliedPrincipalVTokens", "type": "uint256" }, + { "internalType": "uint256", "name": "effectiveLeverage", "type": "uint256" }, + { "internalType": "uint256", "name": "cycleId", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], - "name": "renounceOwnership", - "outputs": [], - "stateMutability": "nonpayable", + "name": "proportionalCloseTolerance", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", "type": "function" }, + { "inputs": [], "name": "renounceOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ - { - "internalType": "address", - "name": "accessControlManager_", - "type": "address" - } + { "internalType": "contract IVToken", "name": "longVToken", "type": "address" }, + { "internalType": "contract IVToken", "name": "shortVToken", "type": "address" }, + { "internalType": "uint256", "name": "additionalPrincipal", "type": "uint256" }, + { "internalType": "uint256", "name": "shortAmount", "type": "uint256" }, + { "internalType": "uint256", "name": "minLongAmount", "type": "uint256" }, + { "internalType": "bytes", "name": "swapData", "type": "bytes" } ], + "name": "scalePosition", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "accessControlManager_", "type": "address" }], "name": "setAccessControlManager", "outputs": [], "stateMutability": "nonpayable", @@ -1594,16 +630,8 @@ }, { "inputs": [ - { - "internalType": "uint8", - "name": "dsaIndex", - "type": "uint8" - }, - { - "internalType": "bool", - "name": "active", - "type": "bool" - } + { "internalType": "uint8", "name": "dsaIndex", "type": "uint8" }, + { "internalType": "bool", "name": "active", "type": "bool" } ], "name": "setDSAVTokenActive", "outputs": [], @@ -1611,78 +639,42 @@ "type": "function" }, { - "inputs": [ - { - "internalType": "address", - "name": "positionAccountImpl", - "type": "address" - } - ], + "inputs": [{ "internalType": "address", "name": "positionAccountImpl", "type": "address" }], "name": "setPositionAccountImplementation", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { - "inputs": [ - { - "internalType": "address", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "address", - "name": "shortVToken", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "supplyPrincipal", + "inputs": [{ "internalType": "uint256", "name": "newTolerance", "type": "uint256" }], + "name": "setProportionalCloseTolerance", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ - { - "internalType": "address", - "name": "newOwner", - "type": "address" - } + { "internalType": "address", "name": "longVToken", "type": "address" }, + { "internalType": "address", "name": "shortVToken", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } ], - "name": "transferOwnership", + "name": "supplyPrincipal", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { - "inputs": [], - "name": "unpause", + "inputs": [{ "internalType": "address", "name": "newOwner", "type": "address" }], + "name": "transferOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } + { "internalType": "contract IVToken", "name": "longVToken", "type": "address" }, + { "internalType": "contract IVToken", "name": "shortVToken", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "name": "withdrawPrincipal", "outputs": [], diff --git a/simulations/vip-610/bscmainnet.ts b/simulations/vip-610/bscmainnet.ts new file mode 100644 index 000000000..000ee1178 --- /dev/null +++ b/simulations/vip-610/bscmainnet.ts @@ -0,0 +1,115 @@ +import { expect } from "chai"; +import { Contract } from "ethers"; +import { ethers } from "hardhat"; +import { NETWORK_ADDRESSES } from "src/networkAddresses"; +import { initMainnetUser } from "src/utils"; +import { forking, testVip } from "src/vip-framework"; + +import { + POSITION_ACCOUNT, + RELATIVE_POSITION_MANAGER, + TIMELOCKS_AND_GUARDIAN, + vip610, +} from "../../vips/vip-610/bscmainnet"; +import ACCESS_CONTROL_MANAGER_ABI from "./abi/AccessControlManager.json"; +import RELATIVE_POSITION_MANAGER_ABI from "./abi/RelativePositionManager.json"; + +const { bscmainnet } = NETWORK_ADDRESSES; + +const ACM_FUNCTION_SIGNATURES = [ + "partialPause()", + "partialUnpause()", + "completePause()", + "completeUnpause()", + "setPositionAccountImplementation(address)", + "setProportionalCloseTolerance(uint256)", + "addDSAVToken(address)", + "setDSAVTokenActive(uint8,bool)", + "executePositionAccountCall(address,address[],bytes[])", +] as const; + +forking(87147067, async () => { + let accessControlManager: Contract; + let relativePositionManager: Contract; + + before(async () => { + accessControlManager = await ethers.getContractAt(ACCESS_CONTROL_MANAGER_ABI, bscmainnet.ACCESS_CONTROL_MANAGER); + relativePositionManager = await ethers.getContractAt(RELATIVE_POSITION_MANAGER_ABI, RELATIVE_POSITION_MANAGER); + }); + + describe("Pre-VIP behavior", () => { + it("RelativePositionManager should have NORMAL_TIMELOCK as pending owner and not yet as owner", async () => { + expect(await relativePositionManager.owner()).not.equal(bscmainnet.NORMAL_TIMELOCK); + expect(await relativePositionManager.pendingOwner()).to.equal(bscmainnet.NORMAL_TIMELOCK); + }); + + it("RPM should not have Position Account implementation set", async () => { + expect(await relativePositionManager.POSITION_ACCOUNT_IMPLEMENTATION()).to.equal(ethers.constants.AddressZero); + }); + + it("Timelocks/Guardian should not have ACM permissions on RelativePositionManager", async () => { + for (const timelockOrGuardian of TIMELOCKS_AND_GUARDIAN) { + for (const fnSignature of ACM_FUNCTION_SIGNATURES) { + const role = ethers.utils.solidityPack(["address", "string"], [RELATIVE_POSITION_MANAGER, fnSignature]); + const roleHash = ethers.utils.keccak256(role); + expect(await accessControlManager.hasRole(roleHash, timelockOrGuardian)).to.equal(false); + } + } + }); + }); + + testVip("VIP-610 [BNB Chain] Configure Relative Position Manager", await vip610()); + + describe("Post-VIP behavior", () => { + it("RelativePositionManager should have NORMAL_TIMELOCK as owner and no pending owner", async () => { + expect(await relativePositionManager.owner()).to.equal(bscmainnet.NORMAL_TIMELOCK); + expect(await relativePositionManager.pendingOwner()).to.equal(ethers.constants.AddressZero); + }); + + it("Timelocks/Guardian should have ACM permissions on RelativePositionManager", async () => { + for (const timelockOrGuardian of TIMELOCKS_AND_GUARDIAN) { + for (const fnSignature of ACM_FUNCTION_SIGNATURES) { + const role = ethers.utils.solidityPack(["address", "string"], [RELATIVE_POSITION_MANAGER, fnSignature]); + const roleHash = ethers.utils.keccak256(role); + expect(await accessControlManager.hasRole(roleHash, timelockOrGuardian)).to.equal(true); + } + } + }); + + it("RPM should have Position Account implementation stored in the state", async () => { + expect(await relativePositionManager.POSITION_ACCOUNT_IMPLEMENTATION()).to.equals(POSITION_ACCOUNT); + }); + + for (const timelockOrGuardian of TIMELOCKS_AND_GUARDIAN) { + describe(`ACM-gated functions should be callable by ${timelockOrGuardian}`, () => { + let rpmAsCaller: Contract; + + before(async () => { + const signer = await initMainnetUser(timelockOrGuardian, ethers.utils.parseEther("1")); + rpmAsCaller = relativePositionManager.connect(signer); + }); + + it("partialPause and partialUnpause", async () => { + await rpmAsCaller.partialPause(); + expect(await relativePositionManager.isPartiallyPaused()).to.equal(true); + await rpmAsCaller.partialUnpause(); + expect(await relativePositionManager.isPartiallyPaused()).to.equal(false); + }); + + it("completePause and completeUnpause", async () => { + await rpmAsCaller.completePause(); + expect(await relativePositionManager.isCompletelyPaused()).to.equal(true); + await rpmAsCaller.completeUnpause(); + expect(await relativePositionManager.isCompletelyPaused()).to.equal(false); + }); + + it("setProportionalCloseTolerance", async () => { + const current = await relativePositionManager.proportionalCloseTolerance(); + const newValue = current.add(1); + await rpmAsCaller.setProportionalCloseTolerance(newValue); + expect(await relativePositionManager.proportionalCloseTolerance()).to.equal(newValue); + }); + }); + } + }); +}); diff --git a/simulations/vip-610/bsctestnet.ts b/simulations/vip-610/bsctestnet.ts index d4ee145f0..0353761c0 100644 --- a/simulations/vip-610/bsctestnet.ts +++ b/simulations/vip-610/bsctestnet.ts @@ -2,6 +2,7 @@ import { expect } from "chai"; import { Contract } from "ethers"; import { ethers } from "hardhat"; import { NETWORK_ADDRESSES } from "src/networkAddresses"; +import { initMainnetUser } from "src/utils"; import { forking, testVip } from "src/vip-framework"; import { @@ -9,8 +10,8 @@ import { POSITION_ACCOUNT, RELATIVE_POSITION_MANAGER, SWAP_HELPER, - TIMELOCKS_AND_GURDIAN, - vip589 as vip610Testnet, + TIMELOCKS_AND_GUARDIAN, + vip610 as vip610Testnet, } from "../../vips/vip-610/bsctestnet"; import ACCESS_CONTROL_MANAGER_ABI from "./abi/AccessControlManager.json"; import FLASHLOAN_FACET_ABI from "./abi/FlashLoanFacet.json"; @@ -21,15 +22,18 @@ import SWAP_HELPER_ABI from "./abi/SwapHelperAbi.json"; const { bsctestnet } = NETWORK_ADDRESSES; const ACM_FUNCTION_SIGNATURES = [ - "pause()", - "unpause()", + "partialPause()", + "partialUnpause()", + "completePause()", + "completeUnpause()", "setPositionAccountImplementation(address)", + "setProportionalCloseTolerance(uint256)", "addDSAVToken(address)", "setDSAVTokenActive(uint8,bool)", "executePositionAccountCall(address,address[],bytes[])", ] as const; -forking(90876709, async () => { +forking(96276916, async () => { let comptroller: Contract; let accessControlManager: Contract; let leverageStrategiesManager: Contract; @@ -64,8 +68,12 @@ forking(90876709, async () => { expect(await relativePositionManager.pendingOwner()).to.equal(bsctestnet.NORMAL_TIMELOCK); }); + it("RPM should not have Position Account implementation set", async () => { + expect(await relativePositionManager.POSITION_ACCOUNT_IMPLEMENTATION()).to.equal(ethers.constants.AddressZero); + }); + it("Timelocks/Guardian should not have ACM permissions on RelativePositionManager", async () => { - for (const timelockOrGuardian of TIMELOCKS_AND_GURDIAN) { + for (const timelockOrGuardian of TIMELOCKS_AND_GUARDIAN) { for (const fnSignature of ACM_FUNCTION_SIGNATURES) { expect( await accessControlManager.hasPermission(timelockOrGuardian, RELATIVE_POSITION_MANAGER, fnSignature), @@ -98,7 +106,7 @@ forking(90876709, async () => { }); it("Timelocks/Guardian should have ACM permissions on RelativePositionManager", async () => { - for (const timelockOrGuardian of TIMELOCKS_AND_GURDIAN) { + for (const timelockOrGuardian of TIMELOCKS_AND_GUARDIAN) { for (const fnSignature of ACM_FUNCTION_SIGNATURES) { expect( await accessControlManager.hasPermission(timelockOrGuardian, RELATIVE_POSITION_MANAGER, fnSignature), @@ -110,5 +118,37 @@ forking(90876709, async () => { it("RPM should have Position account implementation stored in the state", async () => { expect(await relativePositionManager.POSITION_ACCOUNT_IMPLEMENTATION()).to.equals(POSITION_ACCOUNT); }); + + for (const timelockOrGuardian of TIMELOCKS_AND_GUARDIAN) { + describe(`ACM-gated functions should be callable by ${timelockOrGuardian}`, () => { + let rpmAsCaller: Contract; + + before(async () => { + const signer = await initMainnetUser(timelockOrGuardian, ethers.utils.parseEther("1")); + rpmAsCaller = relativePositionManager.connect(signer); + }); + + it("partialPause and partialUnpause", async () => { + await rpmAsCaller.partialPause(); + expect(await relativePositionManager.isPartiallyPaused()).to.equal(true); + await rpmAsCaller.partialUnpause(); + expect(await relativePositionManager.isPartiallyPaused()).to.equal(false); + }); + + it("completePause and completeUnpause", async () => { + await rpmAsCaller.completePause(); + expect(await relativePositionManager.isCompletelyPaused()).to.equal(true); + await rpmAsCaller.completeUnpause(); + expect(await relativePositionManager.isCompletelyPaused()).to.equal(false); + }); + + it("setProportionalCloseTolerance", async () => { + const current = await relativePositionManager.proportionalCloseTolerance(); + const newValue = current.add(1); + await rpmAsCaller.setProportionalCloseTolerance(newValue); + expect(await relativePositionManager.proportionalCloseTolerance()).to.equal(newValue); + }); + }); + } }); }); diff --git a/vips/vip-610/bscmainnet.ts b/vips/vip-610/bscmainnet.ts new file mode 100644 index 000000000..03f01bf96 --- /dev/null +++ b/vips/vip-610/bscmainnet.ts @@ -0,0 +1,61 @@ +import { NETWORK_ADDRESSES } from "src/networkAddresses"; +import { ProposalType } from "src/types"; +import { makeProposal } from "src/utils"; + +const { bscmainnet } = NETWORK_ADDRESSES; +export const RELATIVE_POSITION_MANAGER = "0xedcD8725D08585A7B61eE77A22D9cf591C1171c1"; +export const POSITION_ACCOUNT = "0x18970e10B39BDf6981334b5DC0873d85CFdB9aa0"; + +export const TIMELOCKS_AND_GUARDIAN = [ + bscmainnet.NORMAL_TIMELOCK, + bscmainnet.FAST_TRACK_TIMELOCK, + bscmainnet.CRITICAL_TIMELOCK, + bscmainnet.GUARDIAN, +]; + +const giveAcmPermissions = (fnSignature: string, timelocks = TIMELOCKS_AND_GUARDIAN) => + timelocks.map(timelock => ({ + target: bscmainnet.ACCESS_CONTROL_MANAGER, + signature: "giveCallPermission(address,string,address)", + params: [RELATIVE_POSITION_MANAGER, fnSignature, timelock], + })); + +export const vip610 = () => { + const meta = { + version: "v2", + title: "VIP-610 [BNB Chain] Configure Relative Position Manager", + description: "VIP-610 [BNB Chain] Configure Relative Position Manager", + forDescription: "I agree that Venus Protocol should proceed with this proposal", + againstDescription: "I do not think that Venus Protocol should proceed with this proposal", + abstainDescription: "I am indifferent to whether Venus Protocol proceeds or not", + }; + + return makeProposal( + [ + { + target: RELATIVE_POSITION_MANAGER, + signature: "acceptOwnership()", + params: [], + }, + // ACM permissions + ...giveAcmPermissions("partialPause()"), + ...giveAcmPermissions("partialUnpause()"), + ...giveAcmPermissions("completePause()"), + ...giveAcmPermissions("completeUnpause()"), + ...giveAcmPermissions("setPositionAccountImplementation(address)"), + ...giveAcmPermissions("setProportionalCloseTolerance(uint256)"), + ...giveAcmPermissions("addDSAVToken(address)"), + ...giveAcmPermissions("setDSAVTokenActive(uint8,bool)"), + ...giveAcmPermissions("executePositionAccountCall(address,address[],bytes[])"), + { + target: RELATIVE_POSITION_MANAGER, + signature: "setPositionAccountImplementation(address)", + params: [POSITION_ACCOUNT], + }, + ], + meta, + ProposalType.REGULAR, + ); +}; + +export default vip610; diff --git a/vips/vip-610/bsctestnet.ts b/vips/vip-610/bsctestnet.ts index cb6454b7a..b4eff8400 100644 --- a/vips/vip-610/bsctestnet.ts +++ b/vips/vip-610/bsctestnet.ts @@ -3,30 +3,30 @@ import { ProposalType } from "src/types"; import { makeProposal } from "src/utils"; const { bsctestnet } = NETWORK_ADDRESSES; -export const SWAP_HELPER = "0xf7Cfd0eDfAC7AA473813559b372297332EdEbB8B"; -export const LEVERAGE_STRATEGIES_MANAGER = "0xE852204A757A3Ee9Dfc5d608b7038f962f393706"; -export const RELATIVE_POSITION_MANAGER = "0xcB84425698B9426b5Edd9Ed25eA0116aA0c2Ce7F"; +export const SWAP_HELPER = "0x3Bf0Eb0663BeCe17d95FE33736762bFD20f488b2"; +export const LEVERAGE_STRATEGIES_MANAGER = "0xfc8810B0f1144D5A1F6231aFDb8B51F31c0bc8A7"; +export const RELATIVE_POSITION_MANAGER = "0xF01CA5Ad6152d932Ed19FB28b285529399dA8166"; export const POSITION_ACCOUNT = "0x599B79742AB82700Bc828cc44e0Ae22FBbB88e7c"; -export const TIMELOCKS_AND_GURDIAN = [ +export const TIMELOCKS_AND_GUARDIAN = [ bsctestnet.NORMAL_TIMELOCK, bsctestnet.FAST_TRACK_TIMELOCK, bsctestnet.CRITICAL_TIMELOCK, bsctestnet.GUARDIAN, ]; -const giveAcmPermissions = (fnSignature: string, timelocks = TIMELOCKS_AND_GURDIAN) => +const giveAcmPermissions = (fnSignature: string, timelocks = TIMELOCKS_AND_GUARDIAN) => timelocks.map(timelock => ({ target: bsctestnet.ACCESS_CONTROL_MANAGER, signature: "giveCallPermission(address,string,address)", params: [RELATIVE_POSITION_MANAGER, fnSignature, timelock], })); -export const vip589 = () => { +export const vip610 = () => { const meta = { version: "v2", - title: "VIP-589 [BNB Chain] Add U market to the stablecoin Emode pool", - description: "VIP-589 [BNB Chain] Add U market to the stablecoin Emode pool", + title: "VIP-610 [BNB Chain] Configure Relative Position Manager", + description: "VIP-610 [BNB Chain] Configure Relative Position Manager", forDescription: "I agree that Venus Protocol should proceed with this proposal", againstDescription: "I do not think that Venus Protocol should proceed with this proposal", abstainDescription: "I am indifferent to whether Venus Protocol proceeds or not", @@ -34,6 +34,7 @@ export const vip589 = () => { return makeProposal( [ + // Extra testnet setup: accept ownership of SwapHelper, LeverageStrategiesManager and whitelist for flash loans { target: SWAP_HELPER, signature: "acceptOwnership()", @@ -54,21 +55,25 @@ export const vip589 = () => { signature: "setWhiteListFlashLoanAccount(address,bool)", params: [LEVERAGE_STRATEGIES_MANAGER, true], }, - // Already set in this contract - // { - // target: RELATIVE_POSITION_MANAGER, - // signature: "setPositionAccountImplementation(address)", - // params: [POSITION_ACCOUNT], - // }, // ACM permissions - ...giveAcmPermissions("pause()"), - ...giveAcmPermissions("unpause()"), + ...giveAcmPermissions("partialPause()"), + ...giveAcmPermissions("partialUnpause()"), + ...giveAcmPermissions("completePause()"), + ...giveAcmPermissions("completeUnpause()"), ...giveAcmPermissions("setPositionAccountImplementation(address)"), + ...giveAcmPermissions("setProportionalCloseTolerance(uint256)"), ...giveAcmPermissions("addDSAVToken(address)"), ...giveAcmPermissions("setDSAVTokenActive(uint8,bool)"), ...giveAcmPermissions("executePositionAccountCall(address,address[],bytes[])"), + { + target: RELATIVE_POSITION_MANAGER, + signature: "setPositionAccountImplementation(address)", + params: [POSITION_ACCOUNT], + }, ], meta, ProposalType.REGULAR, ); }; + +export default vip610; From 86608233b5eb9a1a580c1dcacec6156de3d19105 Mon Sep 17 00:00:00 2001 From: GitGuru7 Date: Wed, 18 Mar 2026 16:43:45 +0530 Subject: [PATCH 05/13] feat: add more coverage in the simulations and add e2e --- RPM_VIP_SIMULATION_GUIDE.md | 1071 +++++++++++++++++ simulations/vip-610/abi/BinanceOracle.json | 195 +++ simulations/vip-610/abi/ChainlinkOracle.json | 187 +++ .../vip-610/abi/RelativePositionManager.json | 5 + simulations/vip-610/abi/ResilientOracle.json | 320 +++++ simulations/vip-610/abi/VToken.json | 870 +++++++++++++ simulations/vip-610/bscmainnet.ts | 1070 +++++++++++++++- simulations/vip-610/bsctestnet.ts | 48 +- vips/vip-610/bsctestnet.ts | 2 +- 9 files changed, 3762 insertions(+), 6 deletions(-) create mode 100644 RPM_VIP_SIMULATION_GUIDE.md create mode 100644 simulations/vip-610/abi/BinanceOracle.json create mode 100644 simulations/vip-610/abi/ChainlinkOracle.json create mode 100644 simulations/vip-610/abi/ResilientOracle.json create mode 100644 simulations/vip-610/abi/VToken.json diff --git a/RPM_VIP_SIMULATION_GUIDE.md b/RPM_VIP_SIMULATION_GUIDE.md new file mode 100644 index 000000000..af3862179 --- /dev/null +++ b/RPM_VIP_SIMULATION_GUIDE.md @@ -0,0 +1,1071 @@ +# RPM (RelativePositionManager) VIP Simulation Test Guide + +## Purpose + +This guide explains how to write **VIP simulation tests** in the `vips` repo that exercise the **RelativePositionManager (RPM)** end-to-end on a mainnet fork. Unlike the fork tests in `venus-periphery` (which deploy RPM fresh), VIP simulations use **already-deployed contracts** on-chain. + +The goal is to write tests for six core scenarios: + +1. **Close with Profit** (favorable price movement) +2. **Close with Loss** (unfavorable price movement) +3. **Liquidation** (position becomes underwater) +4. **Scale Position** (increase leverage on existing position) +5. **Partial Close** (close a fraction of position, verify remainder) +6. **LONG = DSA Token** (special code path when long token equals DSA token) + +--- + +## Key Differences: venus-periphery Fork Tests vs VIP Simulations + +| Aspect | venus-periphery Fork Tests | VIP Simulation Tests | +| ------------------------- | -------------------------------------------- | ---------------------------------------------------------------------- | +| RPM contract | Deployed fresh via `upgrades.deployProxy()` | Already deployed on mainnet, use address directly | +| ACM permissions | Granted manually in setup | Already configured (or configured by VIP under test) | +| PositionAccount impl | Set in test setup | Already set (or set by VIP) | +| DSA VTokens | Added in test setup | Already configured (or configured by VIP) | +| SwapHelper | Uses mainnet deployed contract | Same - uses mainnet deployed contract | +| LeverageStrategiesManager | Uses mainnet deployed contract | Same - uses mainnet deployed contract | +| Test framework | `loadFixture` + `forking()` from local utils | `forking()` + `testVip()` from `src/vip-framework` | +| Running | `FORKED_NETWORK=bscmainnet npx hardhat test` | `npx hardhat test simulations/vip-XXX/bscmainnet.ts --fork bscmainnet` | + +--- + +## Deployed Contract Addresses (BSC Mainnet) + +```typescript +// RPM System (from venus-periphery deployments) +const RELATIVE_POSITION_MANAGER = "0xedcD8725D08585A7B61eE77A22D9cf591C1171c1"; +const POSITION_ACCOUNT_IMPL = "0x18970e10B39BDf6981334b5DC0873d85CFdB9aa0"; +const SWAP_HELPER = "0xD79be25aEe798Aa34A9Ba1230003d7499be29A24"; +const LEVERAGE_STRATEGIES_MANAGER = "0x03F079E809185a669Ca188676D0ADb09cbAd6dC1"; + +// Venus Core Pool (BSC Mainnet) +const COMPTROLLER = "0xfd36e2c2a6789db23113685031d7f16329158384"; +const ACM = "0x4788629abc6cfca10f9f969efdeaa1cf70c23555"; +const NORMAL_TIMELOCK = "0x939bD8d64c0A9583A7Dcea9933f7b21697ab6396"; + +// Tokens & Markets (example trio: USDC as DSA, WBNB as LONG, ETH as SHORT) +const DSA_ADDRESS = "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d"; // USDC +const vDSA_ADDRESS = "0xecA88125a5ADbe82614ffC12D0DB554E2e2867C8"; // vUSDC +const LONG_ADDRESS = "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c"; // WBNB +const vLONG_ADDRESS = "0x6bCa74586218dB34cdB402295796b79663d816e9"; // vWBNB +const SHORT_ADDRESS = "0x2170Ed0880ac9A755fd29B2688956BD959F933F8"; // ETH +const vSHORT_ADDRESS = "0xf508fCD89b8bd15579dc79A6827cB4686A3592c8"; // vETH + +// Whales for funding +const DSA_WHALE = DSA_ADDRESS; // Token contract itself holds supply +const SHORT_WHALE = "0x8894E0a0c962CB723c1976a4421c95949bE2D4E3"; // Binance 8 (for ETH) + +// Oracle +const CHAINLINK_ORACLE = "0x1B2103441A0A108daD8848D8F5d790e4D402921F"; +``` + +--- + +## File Structure + +``` +vips/ +├── vips/vip-XXX/ +│ └── bscmainnet.ts # VIP definition (if RPM config changes needed) +├── simulations/vip-XXX/ +│ ├── bscmainnet.ts # Main simulation test file +│ └── abi/ +│ ├── RelativePositionManager.json # RPM ABI +│ ├── LeverageStrategiesManager.json # LSM ABI +│ ├── SwapHelper.json # SwapHelper ABI +│ ├── Comptroller.json # Comptroller ABI +│ ├── VBep20.json # vToken ABI +│ ├── ChainlinkOracle.json # For price manipulation +│ └── ResilientOracle.json # For oracle config +``` + +### Getting ABIs + +Export ABIs from venus-periphery compiled artifacts: + +```bash +# From venus-periphery repo +cat artifacts/contracts/RelativePositionManager.sol/RelativePositionManager.json | jq '.abi' > RelativePositionManager.json +cat artifacts/contracts/LeverageStrategiesManager.sol/LeverageStrategiesManager.json | jq '.abi' > LeverageStrategiesManager.json +``` + +Or copy existing ABIs from `vips/simulations/vip-610/abi/`. + +--- + +## Core Helper Functions + +### 1. `setMaxStalePeriod` - Prevent Oracle Staleness Reverts + +On a forked network, oracle prices may be stale. Set max stale period to 1 year for all relevant tokens. + +```typescript +import { ethers } from "hardhat"; +import { initMainnetUser } from "src/utils"; + +const REDSTONE = "0x8455EFA4D7Ff63b8BFD96AdD889483Ea7d39B70a"; +const CHAINLINK = "0x1B2103441A0A108daD8848D8F5d790e4D402921F"; +const BINANCE = "0x594810b741d136f1960141C0d8Fb4a91bE78A820"; +const ONE_YEAR = "31536000"; + +async function setMaxStalePeriod(): Promise { + const timelock = await initMainnetUser(NORMAL_TIMELOCK, ethers.utils.parseEther("2")); + + const chainlinkOracle = new ethers.Contract(CHAINLINK, CHAINLINK_ORACLE_ABI, timelock); + const redStoneOracle = new ethers.Contract(REDSTONE, CHAINLINK_ORACLE_ABI, timelock); + const binanceOracle = new ethers.Contract(BINANCE, BINANCE_ORACLE_ABI, timelock); + + const tokens = [ + { + asset: DSA_ADDRESS, + chainlinkFeed: "0x51597f405303C4377E36123cBc172b13269EA163", + redstoneFeed: "0xeA2511205b959548459A01e358E0A30424dc0B70", + binanceSymbol: "USDC", + }, + { + asset: LONG_ADDRESS, + chainlinkFeed: "0x0567F2323251f0Aab15c8dFb1967E4e8A7D42aeE", + redstoneFeed: "0x8dd2D85C7c28F43F965AE4d9545189C7D022ED0e", + binanceSymbol: "WBNB", + }, + { + asset: SHORT_ADDRESS, + chainlinkFeed: "0xe48a5Fd74d4A5524D76960ef3B52204C0e11fCD1", + redstoneFeed: "0x9cF19D284862A66378c304ACAcB0E857EBc3F856", + binanceSymbol: "ETH", + }, + ]; + + for (const token of tokens) { + await chainlinkOracle.setTokenConfig({ asset: token.asset, feed: token.chainlinkFeed, maxStalePeriod: ONE_YEAR }); + await redStoneOracle.setTokenConfig({ asset: token.asset, feed: token.redstoneFeed, maxStalePeriod: ONE_YEAR }); + await binanceOracle.setMaxStalePeriod(token.binanceSymbol, ONE_YEAR); + } + await binanceOracle.setMaxStalePeriod("BNB", ONE_YEAR); +} +``` + +### 2. `getManipulatedSwapData` - Simulate Specific Swap Outcomes + +Instead of hitting the real Venus swap API (which may fail in test), manipulate swaps to produce exact output amounts. This lets you control profit/loss scenarios precisely. + +```typescript +import { BigNumber, Wallet } from "ethers"; + +let saltCounter = 0; + +async function getManipulatedSwapData( + tokenIn: string, + tokenOut: string, + amountIn: BigNumber, + amountOut: BigNumber, + recipient: string, + tokenOutWhaleOverride?: string, +): Promise { + // Use a known private key for signing (hardhat default account #0) + const swapSignerWallet = new Wallet( + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", + ethers.provider, + ); + + const swapHelperContract = new ethers.Contract(SWAP_HELPER, SWAP_HELPER_ABI, ethers.provider); + + // Set our wallet as backend signer + const swapHelperOwner = await swapHelperContract.owner(); + const impersonatedOwner = await initMainnetUser(swapHelperOwner, ethers.utils.parseEther("1")); + await swapHelperContract.connect(impersonatedOwner).setBackendSigner(swapSignerWallet.address); + + // Get EIP712 domain + const domain = await swapHelperContract.eip712Domain(); + const network = await ethers.provider.getNetwork(); + const eip712Domain = { + name: domain.name, + version: domain.version, + chainId: network.chainId, + verifyingContract: domain.verifyingContract, + }; + + const TEN_YEARS_SECS = 10 * 365 * 24 * 60 * 60; + const deadline = Math.floor(Date.now() / 1000) + TEN_YEARS_SECS; + + // Fund SwapHelper with tokenOut so it can "swap" + const tokenOutContract = new ethers.Contract(tokenOut, ERC20_ABI, ethers.provider); + const tokenOutWhale = tokenOutWhaleOverride ?? tokenOut; + const whaleSigner = await initMainnetUser(tokenOutWhale, ethers.utils.parseEther("1")); + await tokenOutContract.connect(whaleSigner).transfer(SWAP_HELPER, amountOut); + + // Encode a simple transfer call (fake swap: just transfer tokenOut to recipient) + const tokenOutIface = new ethers.utils.Interface(["function transfer(address to, uint256 amount) returns (bool)"]); + const transferCalldata = tokenOutIface.encodeFunctionData("transfer", [recipient, amountOut]); + + const swapHelperIface = swapHelperContract.interface; + const calls: string[] = []; + calls.push(swapHelperIface.encodeFunctionData("genericCall", [tokenOut, transferCalldata])); + + const salt = ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["uint256"], [++saltCounter])); + + const types = { + Multicall: [ + { name: "caller", type: "address" }, + { name: "calls", type: "bytes[]" }, + { name: "deadline", type: "uint256" }, + { name: "salt", type: "bytes32" }, + ], + }; + + const value = { caller: recipient, calls, deadline, salt }; + const signature = await swapSignerWallet._signTypedData(eip712Domain, types, value); + + return swapHelperIface.encodeFunctionData("multicall", [calls, deadline, salt, signature]); +} +``` + +### 3. `getSwapData` - Real Swap via Venus API (Optional) + +For tests that need real DEX routing (more realistic but slower and may fail if API is down): + +```typescript +async function getSwapData( + tokenIn: string, + tokenOut: string, + exactAmountInMantissa: string, + recipient: string, + slippagePercentage: string, +): Promise<{ swapData: string; minAmountOut: BigNumber }> { + // Same as venus-periphery fork test - calls https://api.venus.io/find-swap + // Signs multicall with EIP712 + // Returns encoded multicall data + minAmountOut + // See venus-periphery/tests/hardhat/Fork/RelativePositionManager.ts lines 85-176 +} +``` + +### 4. `setOraclePrice` - Manipulate Token Prices (For Liquidation) + +```typescript +async function setOraclePrice(comptroller: Contract, asset: string, price: BigNumber): Promise { + const timelock = await initMainnetUser(NORMAL_TIMELOCK, ethers.utils.parseEther("1")); + const resilientOracleAddr = await comptroller.oracle(); + + const resilientOracle = new ethers.Contract( + resilientOracleAddr, + [ + "function setTokenConfig((address asset, address[3] oracles, bool[3] enableFlagsForOracles, bool cachingEnabled))", + ], + timelock, + ); + + // Point oracle to only use Chainlink (so we can manipulate via setDirectPrice) + await resilientOracle.setTokenConfig({ + asset, + oracles: [CHAINLINK_ORACLE, ethers.constants.AddressZero, ethers.constants.AddressZero], + enableFlagsForOracles: [true, false, false], + cachingEnabled: false, + }); + + const chainlinkOracle = new ethers.Contract(CHAINLINK_ORACLE, CHAINLINK_ORACLE_ABI, timelock); + await chainlinkOracle.setDirectPrice(asset, price); +} +``` + +--- + +## Test Scenarios + +### Scenario 1: Close with Profit + +**Flow**: Open position -> Price moves favorably -> Close with profit -> Validate + +```typescript +it("open position and close with profit", async () => { + const rpm = new ethers.Contract(RELATIVE_POSITION_MANAGER, RPM_ABI, ethers.provider); + const leverageManager = new ethers.Contract(LEVERAGE_STRATEGIES_MANAGER, LSM_ABI, ethers.provider); + const [, alice] = await ethers.getSigners(); + + const INITIAL_PRINCIPAL = ethers.utils.parseEther("9000"); + const SHORT_AMOUNT = ethers.utils.parseEther("4"); + const leverage = ethers.utils.parseEther("1.5"); + const closeFractionBps = 10000; // Full close + + // --- STEP 1: Fund Alice with DSA (USDC) --- + const whaleSigner = await initMainnetUser(DSA_WHALE, ethers.utils.parseEther("1")); + const dsa = new ethers.Contract(DSA_ADDRESS, ERC20_ABI, whaleSigner); + await dsa.transfer(alice.address, INITIAL_PRINCIPAL); + await dsa.connect(alice).approve(RELATIVE_POSITION_MANAGER, INITIAL_PRINCIPAL); + + // --- STEP 2: Activate + Open Position --- + // Use manipulated swap: SHORT_AMOUNT ETH -> some WBNB amount + const longAmount = ethers.utils.parseEther("30"); // Expected WBNB from swap + const minLong = longAmount.mul(98).div(100); + + const openSwapData = await getManipulatedSwapData( + SHORT_ADDRESS, // tokenIn (ETH) + LONG_ADDRESS, // tokenOut (WBNB) + SHORT_AMOUNT, // amountIn + longAmount, // amountOut + leverageManager.address, + vLONG_ADDRESS, // whale override (vToken holds underlying) + ); + + await rpm.connect(alice).activateAndOpenPosition( + vLONG_ADDRESS, // longVToken + vSHORT_ADDRESS, // shortVToken + 0, // dsaIndex + INITIAL_PRINCIPAL, // initialPrincipal (must be > 0) + leverage, // effectiveLeverage + SHORT_AMOUNT, // shortAmount + minLong, // minLongAmount + openSwapData, // swapData + ); + + // --- STEP 3: Record post-open state --- + const position = await rpm.getPosition(alice.address, vLONG_ADDRESS, vSHORT_ADDRESS); + const positionAccount = position.positionAccount; + expect(position.isActive).to.eq(true); + + const shortVToken = new ethers.Contract(vSHORT_ADDRESS, VTOKEN_ABI, ethers.provider); + const shortDebtAfterOpen = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); + const longBalanceAfterOpen = await rpm.callStatic.getLongCollateralBalance( + alice.address, + vLONG_ADDRESS, + vSHORT_ADDRESS, + ); + + // --- STEP 4: Close with Profit --- + // Simulate favorable price: use 75% of long to repay all debt, 25% is profit + const longForRepay = longBalanceAfterOpen.mul(75).div(100); + const longForProfit = longBalanceAfterOpen.sub(longForRepay); + + // Buffer for full close (2.2% = 2% contract tolerance + 0.2% interest) + const shortRepayAmount = shortDebtAfterOpen.mul(1022).div(1000); + + const repaySwapData = await getManipulatedSwapData( + LONG_ADDRESS, + SHORT_ADDRESS, + longForRepay, + shortRepayAmount, + leverageManager.address, + ); + + // Profit: LONG -> DSA (goes to RPM, then supplied as principal) + const estimatedProfitDsa = longForProfit.mul(500); // ~500 USDC per WBNB rough estimate + const profitSwapData = await getManipulatedSwapData( + LONG_ADDRESS, + DSA_ADDRESS, + longForProfit, + estimatedProfitDsa, + rpm.address, // RPM receives profit, supplies as principal + ); + + await rpm.connect(alice).closeWithProfit( + vLONG_ADDRESS, + vSHORT_ADDRESS, + closeFractionBps, + longForRepay, // longAmountToRedeemForRepay + shortRepayAmount, // minAmountOutRepay + repaySwapData, + longForProfit, // longAmountToRedeemForProfit + estimatedProfitDsa.mul(98).div(100), // minAmountOutProfit + profitSwapData, + ); + + // --- VALIDATION --- + const shortDebtAfterClose = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); + const longBalanceAfterClose = await rpm.callStatic.getLongCollateralBalance( + alice.address, + vLONG_ADDRESS, + vSHORT_ADDRESS, + ); + + expect(shortDebtAfterClose).to.eq(0, "All debt repaid"); + expect(longBalanceAfterClose).to.eq(0, "All long redeemed"); +}); +``` + +### Scenario 2: Close with Loss + +**Flow**: Open position -> Price moves unfavorably -> Close with loss (use DSA to cover shortfall) -> Validate + +```typescript +it("open position and close with loss", async () => { + const rpm = new ethers.Contract(RELATIVE_POSITION_MANAGER, RPM_ABI, ethers.provider); + const leverageManager = new ethers.Contract(LEVERAGE_STRATEGIES_MANAGER, LSM_ABI, ethers.provider); + const [, alice] = await ethers.getSigners(); + + const INITIAL_PRINCIPAL = ethers.utils.parseEther("15000"); + const SHORT_AMOUNT = ethers.utils.parseEther("3"); + const leverage = ethers.utils.parseEther("3"); + const closeFractionBps = 10000; // Full close + + // --- STEP 1: Fund + Open (same pattern as profit test) --- + const whaleSigner = await initMainnetUser(DSA_WHALE, ethers.utils.parseEther("1")); + const dsa = new ethers.Contract(DSA_ADDRESS, ERC20_ABI, whaleSigner); + await dsa.transfer(alice.address, INITIAL_PRINCIPAL); + await dsa.connect(alice).approve(RELATIVE_POSITION_MANAGER, INITIAL_PRINCIPAL); + + const longAmount = ethers.utils.parseEther("25"); + const openSwapData = await getManipulatedSwapData( + SHORT_ADDRESS, + LONG_ADDRESS, + SHORT_AMOUNT, + longAmount, + leverageManager.address, + vLONG_ADDRESS, + ); + + await rpm + .connect(alice) + .activateAndOpenPosition( + vLONG_ADDRESS, + vSHORT_ADDRESS, + 0, + INITIAL_PRINCIPAL, + leverage, + SHORT_AMOUNT, + longAmount.mul(98).div(100), + openSwapData, + ); + + const position = await rpm.getPosition(alice.address, vLONG_ADDRESS, vSHORT_ADDRESS); + const positionAccount = position.positionAccount; + + const shortVToken = new ethers.Contract(vSHORT_ADDRESS, VTOKEN_ABI, ethers.provider); + const shortDebtAfterOpen = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); + const longBalanceAfterOpen = await rpm.callStatic.getLongCollateralBalance( + alice.address, + vLONG_ADDRESS, + vSHORT_ADDRESS, + ); + + // --- STEP 2: Close with Loss --- + // Simulate 20% loss: swapping all LONG only gets 80% of SHORT needed + const shortAmountFromLongSwap = shortDebtAfterOpen.mul(80).div(100); + const shortfall = shortDebtAfterOpen.sub(shortAmountFromLongSwap); + + // First swap: LONG -> SHORT (with 20% loss) + const firstSwapData = await getManipulatedSwapData( + LONG_ADDRESS, + SHORT_ADDRESS, + longBalanceAfterOpen, + shortAmountFromLongSwap, + leverageManager.address, + ); + + // Second swap: DSA -> SHORT (to cover shortfall from principal) + const dsaAmountToSwap = INITIAL_PRINCIPAL.mul(25).div(100); + // Buffer for full close: 2.2% + const shortFromDsaSwap = shortfall.mul(1022).div(1000); + + const secondSwapData = await getManipulatedSwapData( + DSA_ADDRESS, + SHORT_ADDRESS, + dsaAmountToSwap, + shortFromDsaSwap, + leverageManager.address, + ); + + await rpm.connect(alice).closeWithLoss( + vLONG_ADDRESS, + vSHORT_ADDRESS, + closeFractionBps, + longBalanceAfterOpen, // longAmountToRedeemForFirstSwap + shortAmountFromLongSwap, // shortAmountToRepayForFirstSwap + shortAmountFromLongSwap, // minAmountOutFirstSwap + firstSwapData, + dsaAmountToSwap, // dsaAmountToRedeemForSecondSwap + shortFromDsaSwap, // minAmountOutSecondSwap + secondSwapData, + ); + + // --- VALIDATION --- + const shortDebtAfterClose = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); + const longBalanceAfterClose = await rpm.callStatic.getLongCollateralBalance( + alice.address, + vLONG_ADDRESS, + vSHORT_ADDRESS, + ); + + expect(shortDebtAfterClose).to.eq(0, "All debt repaid"); + expect(longBalanceAfterClose).to.eq(0, "All long redeemed"); + + // Optional: deactivate and verify principal withdrawal + await rpm.connect(alice).deactivatePosition(vLONG_ADDRESS, vSHORT_ADDRESS); + const positionAfter = await rpm.getPosition(alice.address, vLONG_ADDRESS, vSHORT_ADDRESS); + expect(positionAfter.isActive).to.eq(false); +}); +``` + +### Scenario 3: Liquidation + +**Flow**: Open position -> Crash LONG price via oracle manipulation -> External liquidator calls `liquidateBorrow` -> Validate seized collateral + +```typescript +it("liquidate position after price crash", async () => { + const rpm = new ethers.Contract(RELATIVE_POSITION_MANAGER, RPM_ABI, ethers.provider); + const leverageManager = new ethers.Contract(LEVERAGE_STRATEGIES_MANAGER, LSM_ABI, ethers.provider); + const comptroller = new ethers.Contract(COMPTROLLER, COMPTROLLER_ABI, ethers.provider); + const [, alice, liquidator] = await ethers.getSigners(); + + // --- STEP 1: Open a highly leveraged position --- + const INITIAL_PRINCIPAL = ethers.utils.parseEther("1500"); + const SHORT_AMOUNT = ethers.utils.parseEther("1.5"); + const leverage = ethers.utils.parseEther("3"); + + const whaleSigner = await initMainnetUser(DSA_WHALE, ethers.utils.parseEther("1")); + const dsa = new ethers.Contract(DSA_ADDRESS, ERC20_ABI, whaleSigner); + await dsa.transfer(alice.address, INITIAL_PRINCIPAL); + await dsa.connect(alice).approve(RELATIVE_POSITION_MANAGER, INITIAL_PRINCIPAL); + + const longAmount = ethers.utils.parseEther("30"); + const openSwapData = await getManipulatedSwapData( + SHORT_ADDRESS, + LONG_ADDRESS, + SHORT_AMOUNT, + longAmount, + leverageManager.address, + vLONG_ADDRESS, + ); + + await rpm + .connect(alice) + .activateAndOpenPosition( + vLONG_ADDRESS, + vSHORT_ADDRESS, + 0, + INITIAL_PRINCIPAL, + leverage, + SHORT_AMOUNT, + longAmount.mul(98).div(100), + openSwapData, + ); + + const position = await rpm.getPosition(alice.address, vLONG_ADDRESS, vSHORT_ADDRESS); + const positionAccount = position.positionAccount; + + const shortVToken = new ethers.Contract(vSHORT_ADDRESS, VTOKEN_ABI, ethers.provider); + const shortDebtAfterOpen = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); + + // --- STEP 2: Set liquidator contract (required by Venus Core Pool) --- + const timelock = await initMainnetUser(NORMAL_TIMELOCK, ethers.utils.parseEther("1")); + await comptroller.connect(timelock)._setLiquidatorContract(liquidator.address); + + // --- STEP 3: Fund liquidator with SHORT tokens --- + const shortToken = new ethers.Contract(SHORT_ADDRESS, ERC20_ABI, ethers.provider); + const shortWhaleSigner = await initMainnetUser(SHORT_WHALE, ethers.utils.parseEther("1")); + await shortToken.connect(shortWhaleSigner).transfer(liquidator.address, ethers.utils.parseEther("10")); + + // --- STEP 4: Crash LONG (WBNB) price by 90% to make position liquidatable --- + const oracleAddr = await comptroller.oracle(); + const oracle = new ethers.Contract( + oracleAddr, + ["function getPrice(address) view returns (uint256)"], + ethers.provider, + ); + const wbnbPrice = await oracle.getPrice(LONG_ADDRESS); + await setOraclePrice(comptroller, LONG_ADDRESS, wbnbPrice.mul(10).div(100)); // 90% drop + + // Verify position is liquidatable + const [, , shortfall] = await comptroller.getAccountLiquidity(positionAccount); + expect(shortfall).to.be.gt(0, "Position should be liquidatable"); + + // --- STEP 5: Liquidate --- + const repayAmount = shortDebtAfterOpen.div(4); // Liquidate 25% of debt + await shortToken.connect(liquidator).approve(vSHORT_ADDRESS, repayAmount); + + const dsaVToken = new ethers.Contract(vDSA_ADDRESS, VTOKEN_ABI, ethers.provider); + const liquidatorVTokenBefore = await dsaVToken.balanceOf(liquidator.address); + + const shortVTokenAsLiquidator = new ethers.Contract(vSHORT_ADDRESS, VBEP20_ABI, liquidator); + await shortVTokenAsLiquidator.liquidateBorrow( + positionAccount, + repayAmount, + vDSA_ADDRESS, // Seize DSA collateral (or use vLONG_ADDRESS to seize LONG) + ); + + // --- VALIDATION --- + const shortDebtAfterLiq = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); + const liquidatorVTokenAfter = await dsaVToken.balanceOf(liquidator.address); + + expect(shortDebtAfterLiq).to.be.lt(shortDebtAfterOpen, "Debt should decrease"); + expect(liquidatorVTokenAfter).to.be.gt(liquidatorVTokenBefore, "Liquidator received seized vTokens"); +}); +``` + +### Scenario 4: Scale Position (Increase Leverage) + +**Flow**: Open position -> Scale up (add more leverage) -> Validate increased debt and long balance + +```typescript +it("open position and scale up", async () => { + const rpm = new ethers.Contract(RELATIVE_POSITION_MANAGER, RPM_ABI, ethers.provider); + const leverageManager = new ethers.Contract(LEVERAGE_STRATEGIES_MANAGER, LSM_ABI, ethers.provider); + const [, alice] = await ethers.getSigners(); + + const INITIAL_PRINCIPAL = ethers.utils.parseEther("10000"); + const SHORT_AMOUNT = ethers.utils.parseEther("3"); + const leverage = ethers.utils.parseEther("2"); + + // --- STEP 1: Fund + Open initial position --- + const whaleSigner = await initMainnetUser(DSA_WHALE, ethers.utils.parseEther("1")); + const dsa = new ethers.Contract(DSA_ADDRESS, ERC20_ABI, whaleSigner); + await dsa.transfer(alice.address, INITIAL_PRINCIPAL.mul(2)); // Extra for scaling + await dsa.connect(alice).approve(RELATIVE_POSITION_MANAGER, INITIAL_PRINCIPAL.mul(2)); + + const longAmount = ethers.utils.parseEther("25"); + const openSwapData = await getManipulatedSwapData( + SHORT_ADDRESS, + LONG_ADDRESS, + SHORT_AMOUNT, + longAmount, + leverageManager.address, + vLONG_ADDRESS, + ); + + await rpm + .connect(alice) + .activateAndOpenPosition( + vLONG_ADDRESS, + vSHORT_ADDRESS, + 0, + INITIAL_PRINCIPAL, + leverage, + SHORT_AMOUNT, + longAmount.mul(98).div(100), + openSwapData, + ); + + const position = await rpm.getPosition(alice.address, vLONG_ADDRESS, vSHORT_ADDRESS); + const positionAccount = position.positionAccount; + + const shortVToken = new ethers.Contract(vSHORT_ADDRESS, VTOKEN_ABI, ethers.provider); + const shortDebtAfterOpen = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); + const longBalanceAfterOpen = await rpm.callStatic.getLongCollateralBalance( + alice.address, + vLONG_ADDRESS, + vSHORT_ADDRESS, + ); + + // --- STEP 2: Scale position (borrow more SHORT, swap to more LONG) --- + const ADDITIONAL_PRINCIPAL = ethers.utils.parseEther("5000"); + const SCALE_SHORT_AMOUNT = ethers.utils.parseEther("2"); + const scaleLongAmount = ethers.utils.parseEther("18"); + + const scaleSwapData = await getManipulatedSwapData( + SHORT_ADDRESS, + LONG_ADDRESS, + SCALE_SHORT_AMOUNT, + scaleLongAmount, + leverageManager.address, + vLONG_ADDRESS, + ); + + await rpm.connect(alice).scalePosition( + vLONG_ADDRESS, // longVToken + vSHORT_ADDRESS, // shortVToken + ADDITIONAL_PRINCIPAL, // additionalPrincipal (can be 0 if not adding more) + SCALE_SHORT_AMOUNT, // shortAmount + scaleLongAmount.mul(98).div(100), // minLongAmount + scaleSwapData, // swapData + ); + + // --- VALIDATION --- + const shortDebtAfterScale = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); + const longBalanceAfterScale = await rpm.callStatic.getLongCollateralBalance( + alice.address, + vLONG_ADDRESS, + vSHORT_ADDRESS, + ); + + // Debt should increase by approximately SCALE_SHORT_AMOUNT + const expectedTotalDebt = shortDebtAfterOpen.add(SCALE_SHORT_AMOUNT); + const debtTolerance = expectedTotalDebt.mul(1).div(100); // 1% tolerance + expect(shortDebtAfterScale).to.be.closeTo(expectedTotalDebt, debtTolerance); + + // Long balance should increase by approximately scaleLongAmount + const expectedTotalLong = longBalanceAfterOpen.add(scaleLongAmount); + const longTolerance = expectedTotalLong.mul(2).div(100); // 2% tolerance + expect(longBalanceAfterScale).to.be.closeTo(expectedTotalLong, longTolerance); + + // Position should still be active + const positionAfterScale = await rpm.getPosition(alice.address, vLONG_ADDRESS, vSHORT_ADDRESS); + expect(positionAfterScale.isActive).to.eq(true); +}); +``` + +### Scenario 5: Partial Close (Profit or Loss) + +**Flow**: Open position -> Partial close (e.g., 30-50%) -> Validate remaining balances are proportional + +```typescript +it("open position and partial close with profit (50%)", async () => { + const rpm = new ethers.Contract(RELATIVE_POSITION_MANAGER, RPM_ABI, ethers.provider); + const leverageManager = new ethers.Contract(LEVERAGE_STRATEGIES_MANAGER, LSM_ABI, ethers.provider); + const [, alice] = await ethers.getSigners(); + + const INITIAL_PRINCIPAL = ethers.utils.parseEther("9000"); + const SHORT_AMOUNT = ethers.utils.parseEther("4"); + const leverage = ethers.utils.parseEther("1.5"); + const closeFractionBps = 5000; // 50% partial close + + // --- STEP 1: Fund + Open (same as full close) --- + const whaleSigner = await initMainnetUser(DSA_WHALE, ethers.utils.parseEther("1")); + const dsa = new ethers.Contract(DSA_ADDRESS, ERC20_ABI, whaleSigner); + await dsa.transfer(alice.address, INITIAL_PRINCIPAL); + await dsa.connect(alice).approve(RELATIVE_POSITION_MANAGER, INITIAL_PRINCIPAL); + + const longAmount = ethers.utils.parseEther("30"); + const openSwapData = await getManipulatedSwapData( + SHORT_ADDRESS, + LONG_ADDRESS, + SHORT_AMOUNT, + longAmount, + leverageManager.address, + vLONG_ADDRESS, + ); + + await rpm + .connect(alice) + .activateAndOpenPosition( + vLONG_ADDRESS, + vSHORT_ADDRESS, + 0, + INITIAL_PRINCIPAL, + leverage, + SHORT_AMOUNT, + longAmount.mul(98).div(100), + openSwapData, + ); + + const position = await rpm.getPosition(alice.address, vLONG_ADDRESS, vSHORT_ADDRESS); + const positionAccount = position.positionAccount; + + const shortVToken = new ethers.Contract(vSHORT_ADDRESS, VTOKEN_ABI, ethers.provider); + const shortDebtAfterOpen = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); + const longBalanceAfterOpen = await rpm.callStatic.getLongCollateralBalance( + alice.address, + vLONG_ADDRESS, + vSHORT_ADDRESS, + ); + + // --- STEP 2: Partial Close with Profit (50%) --- + // Proportional amounts based on close fraction + const expectedLongToRedeem = longBalanceAfterOpen.mul(closeFractionBps).div(10000); + const expectedShortToRepay = shortDebtAfterOpen.mul(closeFractionBps).div(10000); + + // Favorable price: 80% of redeemed long covers repay, 20% is profit + const longForRepay = expectedLongToRedeem.mul(80).div(100); + const longForProfit = expectedLongToRedeem.sub(longForRepay); + + // 0.2% buffer for partial close (no 2% contract tolerance like full close) + const shortRepayAmount = expectedShortToRepay.mul(1002).div(1000); + + const repaySwapData = await getManipulatedSwapData( + LONG_ADDRESS, + SHORT_ADDRESS, + longForRepay, + shortRepayAmount, + leverageManager.address, + ); + + const estimatedProfitDsa = longForProfit.mul(500); + const profitSwapData = await getManipulatedSwapData( + LONG_ADDRESS, + DSA_ADDRESS, + longForProfit, + estimatedProfitDsa, + rpm.address, + ); + + await rpm + .connect(alice) + .closeWithProfit( + vLONG_ADDRESS, + vSHORT_ADDRESS, + closeFractionBps, + longForRepay, + shortRepayAmount, + repaySwapData, + longForProfit, + estimatedProfitDsa.mul(98).div(100), + profitSwapData, + ); + + // --- VALIDATION --- + const shortDebtAfterClose = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); + const longBalanceAfterClose = await rpm.callStatic.getLongCollateralBalance( + alice.address, + vLONG_ADDRESS, + vSHORT_ADDRESS, + ); + + // Remaining balances should be ~50% of initial (within 2% tolerance) + const expectedRemainingDebt = shortDebtAfterOpen.mul(10000 - closeFractionBps).div(10000); + const expectedRemainingLong = longBalanceAfterOpen.mul(10000 - closeFractionBps).div(10000); + + const debtTolerance = expectedRemainingDebt.mul(2).div(100); + expect(shortDebtAfterClose).to.be.closeTo(expectedRemainingDebt, debtTolerance); + + const longTolerance = expectedRemainingLong.mul(2).div(100); + expect(longBalanceAfterClose).to.be.closeTo(expectedRemainingLong, longTolerance); + + // Position should STILL be active (partial close, not full) + expect(shortDebtAfterClose).to.be.gt(0, "Debt should remain after partial close"); + expect(longBalanceAfterClose).to.be.gt(0, "Long should remain after partial close"); + + const positionAfterClose = await rpm.getPosition(alice.address, vLONG_ADDRESS, vSHORT_ADDRESS); + expect(positionAfterClose.isActive).to.eq(true, "Position stays active after partial close"); +}); +``` + +### Scenario 6: LONG = DSA Token (Special Code Path) + +When the long token is the same as the DSA token (e.g., both USDC), the contract takes different paths: + +- **On close with profit**: no profit swap needed (`"0x"` swap data) - redeemed long IS DSA, supplied directly as principal +- **On close with loss**: second swap redeems from DSA principal (same market as long) +- **On open**: swap output token is DSA, deposited into DSA market (which is also long market) + +```typescript +it("close with profit when LONG = DSA (no profit swap needed)", async () => { + const rpm = new ethers.Contract(RELATIVE_POSITION_MANAGER, RPM_ABI, ethers.provider); + const leverageManager = new ethers.Contract(LEVERAGE_STRATEGIES_MANAGER, LSM_ABI, ethers.provider); + const [, alice] = await ethers.getSigners(); + + // LONG = DSA = USDC (vDSA market used for both) + const INITIAL_PRINCIPAL = ethers.utils.parseEther("10000"); + const SHORT_AMOUNT = ethers.utils.parseEther("1"); + const leverage = ethers.utils.parseEther("2"); + const closeFractionBps = 10000; // Full close + + // --- STEP 1: Fund + Open (LONG = DSA) --- + const whaleSigner = await initMainnetUser(DSA_WHALE, ethers.utils.parseEther("1")); + const dsa = new ethers.Contract(DSA_ADDRESS, ERC20_ABI, whaleSigner); + await dsa.transfer(alice.address, INITIAL_PRINCIPAL); + await dsa.connect(alice).approve(RELATIVE_POSITION_MANAGER, INITIAL_PRINCIPAL); + + // Swap: SHORT (ETH) -> LONG (USDC, which is DSA) + const longAmount = ethers.utils.parseEther("4000"); // ~4000 USDC from 1 ETH + const openSwapData = await getManipulatedSwapData( + SHORT_ADDRESS, + DSA_ADDRESS, + SHORT_AMOUNT, + longAmount, + leverageManager.address, + vDSA_ADDRESS, // whale: vToken holds underlying USDC + ); + + await rpm.connect(alice).activateAndOpenPosition( + vDSA_ADDRESS, // longVToken = dsaVToken (LONG = DSA) + vSHORT_ADDRESS, + 0, + INITIAL_PRINCIPAL, + leverage, + SHORT_AMOUNT, + longAmount.mul(98).div(100), + openSwapData, + ); + + const position = await rpm.getPosition(alice.address, vDSA_ADDRESS, vSHORT_ADDRESS); + const positionAccount = position.positionAccount; + + const shortVToken = new ethers.Contract(vSHORT_ADDRESS, VTOKEN_ABI, ethers.provider); + const dsaVToken = new ethers.Contract(vDSA_ADDRESS, VTOKEN_ABI, ethers.provider); + const shortDebtAfterOpen = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); + const longBalanceAfterOpen = await rpm.callStatic.getLongCollateralBalance( + alice.address, + vDSA_ADDRESS, + vSHORT_ADDRESS, + ); + + // Accrue interest for accurate balance reads + await dsaVToken.connect(alice).accrueInterest(); + await shortVToken.connect(alice).accrueInterest(); + + // --- STEP 2: Close with Profit (LONG = DSA) --- + // 80% of long for repay, 20% as profit (no swap needed - it's already DSA) + const longForRepay = longBalanceAfterOpen.mul(80).div(100); + const longForProfit = longBalanceAfterOpen.sub(longForRepay); + + // 2.2% buffer for full close + const shortRepayAmount = shortDebtAfterOpen.mul(1022).div(1000); + + // First swap: LONG (USDC) -> SHORT (ETH) for repay + const repaySwapData = await getManipulatedSwapData( + DSA_ADDRESS, + SHORT_ADDRESS, + longForRepay, + shortRepayAmount, + leverageManager.address, + vSHORT_ADDRESS, // whale for SHORT + ); + + // KEY DIFFERENCE: When LONG = DSA, profit swap is "0x" (no swap needed) + // The redeemed long IS DSA, so it's supplied directly as principal + const profitSwapData = "0x"; + const estimatedProfitDsa = longForProfit; // 1:1, no conversion needed + + const suppliedPrincipalBefore = await rpm.callStatic.getSuppliedPrincipalBalance( + alice.address, + vDSA_ADDRESS, + vSHORT_ADDRESS, + ); + + await rpm.connect(alice).closeWithProfit( + vDSA_ADDRESS, + vSHORT_ADDRESS, + closeFractionBps, + longForRepay, + shortRepayAmount, + repaySwapData, + longForProfit, + estimatedProfitDsa, // minAmountOutProfit (exact, no swap) + profitSwapData, // "0x" - no swap, long is already DSA + ); + + // --- VALIDATION --- + const shortDebtAfterClose = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); + const longBalanceAfterClose = await rpm.callStatic.getLongCollateralBalance( + alice.address, + vDSA_ADDRESS, + vSHORT_ADDRESS, + ); + const suppliedPrincipalAfter = await rpm.callStatic.getSuppliedPrincipalBalance( + alice.address, + vDSA_ADDRESS, + vSHORT_ADDRESS, + ); + + expect(shortDebtAfterClose).to.eq(0, "All debt repaid"); + expect(longBalanceAfterClose).to.eq(0, "All long redeemed"); + + // Profit (redeemed long) should be added to supplied principal + const principalIncrease = suppliedPrincipalAfter.sub(suppliedPrincipalBefore); + const profitTolerance = longForProfit.mul(1).div(10000); // 0.01% + expect(principalIncrease).to.be.closeTo( + longForProfit, + profitTolerance, + "Profit added to principal (no swap, long IS DSA)", + ); + + // Deactivate to withdraw remaining principal + await rpm.connect(alice).deactivatePosition(vDSA_ADDRESS, vSHORT_ADDRESS); + const positionAfter = await rpm.getPosition(alice.address, vDSA_ADDRESS, vSHORT_ADDRESS); + expect(positionAfter.isActive).to.eq(false); +}); +``` + +**Key differences when LONG = DSA:** + +- `longVToken` passed to RPM functions is `vDSA_ADDRESS` (same market for both long and DSA) +- Profit swap data is `"0x"` (redeemed long is already DSA, no conversion needed) +- `estimatedProfitDsa = longForProfit` (1:1, no swap slippage) +- On close with loss, second swap redeems DSA from the same market as long collateral +- `suppliedPrincipalBalance` tracks only the DSA principal portion (separate from long collateral in the same market) + +--- + +## Complete Simulation File Template + +```typescript +// simulations/vip-XXX/bscmainnet.ts +import { expect } from "chai"; +import { BigNumber, Contract, Wallet } from "ethers"; +import { ethers } from "hardhat"; +import { NETWORK_ADDRESSES } from "src/networkAddresses"; +import { initMainnetUser } from "src/utils"; +import { forking, testVip } from "src/vip-framework"; + +import { vipXXX } from "../../vips/vip-XXX/bscmainnet"; +import CHAINLINK_ORACLE_ABI from "./abi/ChainlinkOracle.json"; +import COMPTROLLER_ABI from "./abi/Comptroller.json"; +import ERC20_ABI from "./abi/ERC20.json"; +import LSM_ABI from "./abi/LeverageStrategiesManager.json"; +import RPM_ABI from "./abi/RelativePositionManager.json"; +import SWAP_HELPER_ABI from "./abi/SwapHelper.json"; +import VTOKEN_ABI from "./abi/VBep20.json"; + +const { bscmainnet } = NETWORK_ADDRESSES; + +// --- Constants (addresses above) --- +// ... paste deployed addresses here ... + +// --- Helpers --- +// ... paste helper functions (setMaxStalePeriod, getManipulatedSwapData, setOraclePrice) ... + +forking(BLOCK_NUMBER, async () => { + let rpm: Contract; + let leverageManager: Contract; + let comptroller: Contract; + + before(async () => { + await setMaxStalePeriod(); + rpm = new ethers.Contract(RELATIVE_POSITION_MANAGER, RPM_ABI, ethers.provider); + leverageManager = new ethers.Contract(LEVERAGE_STRATEGIES_MANAGER, LSM_ABI, ethers.provider); + comptroller = new ethers.Contract(COMPTROLLER, COMPTROLLER_ABI, ethers.provider); + }); + + // If there's a VIP to execute first (e.g., adding DSA, setting permissions): + testVip("VIP-XXX Configure RPM", await vipXXX()); + + describe("RPM Position Lifecycle Tests", function () { + this.timeout(720000); // 12 minutes - fork tests are slow + + it("close with profit", async () => { + /* ... */ + }); + it("close with loss", async () => { + /* ... */ + }); + it("liquidation", async () => { + /* ... */ + }); + }); +}); +``` + +--- + +## Important Notes + +### Swap Data Mechanics + +- **`getManipulatedSwapData`** is preferred for deterministic tests - it fakes swaps by directly transferring tokens +- The `recipient` for opening swaps is always `leverageManager.address` +- The `recipient` for profit swaps in `closeWithProfit` is `rpm.address` (RPM supplies profit as principal) +- Each swap call needs a unique `salt` (use incrementing counter) +- The SwapHelper backend signer must be set to your test wallet + +### Token Whale Overrides + +- When using `getManipulatedSwapData`, the function needs to fund SwapHelper with `tokenOut` +- For WBNB, use `vLONG_ADDRESS` (vToken holds underlying) as `tokenOutWhaleOverride` +- For ETH, use `SHORT_WHALE` (Binance hot wallet) +- For USDC, the token contract itself (`DSA_ADDRESS`) often holds supply + +### Buffer Amounts + +- **Partial close**: 0.2% buffer on repay amounts (for interest accrual during execution) +- **Full close (100%)**: 2.2% buffer (2% contract tolerance + 0.2% interest) +- Always use `.mul(X).div(Y)` for percentage calculations (no floating point) + +### Position Lifecycle + +1. `activateAndOpenPosition()` - Creates position account (proxy clone), supplies principal, borrows SHORT, swaps to LONG +2. `closeWithProfit()` - Redeems LONG, swaps to SHORT to repay debt, swaps remaining LONG to DSA as profit +3. `closeWithLoss()` - Redeems LONG, swaps to SHORT (insufficient), redeems DSA principal, swaps to SHORT to cover shortfall +4. `deactivatePosition()` - Withdraws remaining principal to user, marks position inactive +5. `liquidateBorrow()` - External call on vToken by liquidator when position is underwater + +### Key Gotchas + +- `initialPrincipal` MUST be > 0 in `activateAndOpenPosition` (reverts with `InsufficientPrincipal`) +- `_setLiquidatorContract` must be called on Comptroller before liquidation (Venus Core Pool requirement) +- Position stays `isActive = true` even after full close - must explicitly call `deactivatePosition()` +- Oracle manipulation requires impersonating `NORMAL_TIMELOCK` +- Fork block number must be recent enough that RPM contracts are deployed and configured + +### Running the Test + +```bash +npx hardhat test simulations/vip-XXX/bscmainnet.ts --fork bscmainnet +``` diff --git a/simulations/vip-610/abi/BinanceOracle.json b/simulations/vip-610/abi/BinanceOracle.json new file mode 100644 index 000000000..f740c68c8 --- /dev/null +++ b/simulations/vip-610/abi/BinanceOracle.json @@ -0,0 +1,195 @@ +[ + { "inputs": [], "stateMutability": "nonpayable", "type": "constructor" }, + { + "inputs": [ + { "internalType": "address", "name": "sender", "type": "address" }, + { "internalType": "address", "name": "calledContract", "type": "address" }, + { "internalType": "string", "name": "methodSignature", "type": "string" } + ], + "name": "Unauthorized", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "oldFeedRegistry", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "newFeedRegistry", "type": "address" } + ], + "name": "FeedRegistryUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "internalType": "uint8", "name": "version", "type": "uint8" }], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "string", "name": "asset", "type": "string" }, + { "indexed": false, "internalType": "uint256", "name": "maxStalePeriod", "type": "uint256" } + ], + "name": "MaxStalePeriodAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "oldAccessControlManager", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "newAccessControlManager", "type": "address" } + ], + "name": "NewAccessControlManager", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "previousOwner", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "newOwner", "type": "address" } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "previousOwner", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "newOwner", "type": "address" } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "string", "name": "symbol", "type": "string" }, + { "indexed": false, "internalType": "string", "name": "overriddenSymbol", "type": "string" } + ], + "name": "SymbolOverridden", + "type": "event" + }, + { + "inputs": [], + "name": "BNB_ADDR", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { "inputs": [], "name": "acceptOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "accessControlManager", + "outputs": [{ "internalType": "contract IAccessControlManagerV8", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "feedRegistryAddress", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getFeedRegistryAddress", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "asset", "type": "address" }], + "name": "getPrice", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_sidRegistryAddress", "type": "address" }, + { "internalType": "address", "name": "_acm", "type": "address" } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "string", "name": "", "type": "string" }], + "name": "maxStalePeriod", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { "inputs": [], "name": "renounceOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [{ "internalType": "address", "name": "accessControlManager_", "type": "address" }], + "name": "setAccessControlManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "newfeedRegistryAddress", "type": "address" }], + "name": "setFeedRegistryAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "string", "name": "symbol", "type": "string" }, + { "internalType": "uint256", "name": "_maxStalePeriod", "type": "uint256" } + ], + "name": "setMaxStalePeriod", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "string", "name": "symbol", "type": "string" }, + { "internalType": "string", "name": "overrideSymbol", "type": "string" } + ], + "name": "setSymbolOverride", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "sidRegistryAddress", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "string", "name": "", "type": "string" }], + "name": "symbols", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "newOwner", "type": "address" }], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/simulations/vip-610/abi/ChainlinkOracle.json b/simulations/vip-610/abi/ChainlinkOracle.json new file mode 100644 index 000000000..7dd5f6f5f --- /dev/null +++ b/simulations/vip-610/abi/ChainlinkOracle.json @@ -0,0 +1,187 @@ +[ + { "inputs": [], "stateMutability": "nonpayable", "type": "constructor" }, + { + "inputs": [ + { "internalType": "address", "name": "sender", "type": "address" }, + { "internalType": "address", "name": "calledContract", "type": "address" }, + { "internalType": "string", "name": "methodSignature", "type": "string" } + ], + "name": "Unauthorized", + "type": "error" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "internalType": "uint8", "name": "version", "type": "uint8" }], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "oldAccessControlManager", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "newAccessControlManager", "type": "address" } + ], + "name": "NewAccessControlManager", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "previousOwner", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "newOwner", "type": "address" } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "previousOwner", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "newOwner", "type": "address" } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "asset", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "previousPriceMantissa", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "newPriceMantissa", "type": "uint256" } + ], + "name": "PricePosted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "asset", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "feed", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "maxStalePeriod", "type": "uint256" } + ], + "name": "TokenConfigAdded", + "type": "event" + }, + { + "inputs": [], + "name": "NATIVE_TOKEN_ADDR", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { "inputs": [], "name": "acceptOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "accessControlManager", + "outputs": [{ "internalType": "contract IAccessControlManagerV8", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "asset", "type": "address" }], + "name": "getPrice", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "accessControlManager_", "type": "address" }], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "", "type": "address" }], + "name": "prices", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { "inputs": [], "name": "renounceOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [{ "internalType": "address", "name": "accessControlManager_", "type": "address" }], + "name": "setAccessControlManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "asset", "type": "address" }, + { "internalType": "uint256", "name": "price", "type": "uint256" } + ], + "name": "setDirectPrice", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "asset", "type": "address" }, + { "internalType": "address", "name": "feed", "type": "address" }, + { "internalType": "uint256", "name": "maxStalePeriod", "type": "uint256" } + ], + "internalType": "struct ChainlinkOracle.TokenConfig", + "name": "tokenConfig", + "type": "tuple" + } + ], + "name": "setTokenConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "asset", "type": "address" }, + { "internalType": "address", "name": "feed", "type": "address" }, + { "internalType": "uint256", "name": "maxStalePeriod", "type": "uint256" } + ], + "internalType": "struct ChainlinkOracle.TokenConfig[]", + "name": "tokenConfigs_", + "type": "tuple[]" + } + ], + "name": "setTokenConfigs", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "", "type": "address" }], + "name": "tokenConfigs", + "outputs": [ + { "internalType": "address", "name": "asset", "type": "address" }, + { "internalType": "address", "name": "feed", "type": "address" }, + { "internalType": "uint256", "name": "maxStalePeriod", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "newOwner", "type": "address" }], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/simulations/vip-610/abi/RelativePositionManager.json b/simulations/vip-610/abi/RelativePositionManager.json index b4e6dbcfb..7485c9f4c 100644 --- a/simulations/vip-610/abi/RelativePositionManager.json +++ b/simulations/vip-610/abi/RelativePositionManager.json @@ -680,5 +680,10 @@ "outputs": [], "stateMutability": "nonpayable", "type": "function" + }, + { + "inputs": [], + "name": "InvalidCallsLength", + "type": "error" } ] diff --git a/simulations/vip-610/abi/ResilientOracle.json b/simulations/vip-610/abi/ResilientOracle.json new file mode 100644 index 000000000..373eb14e9 --- /dev/null +++ b/simulations/vip-610/abi/ResilientOracle.json @@ -0,0 +1,320 @@ +[ + { + "inputs": [ + { "internalType": "address", "name": "nativeMarketAddress", "type": "address" }, + { "internalType": "address", "name": "vaiAddress", "type": "address" }, + { "internalType": "contract BoundValidatorInterface", "name": "_boundValidator", "type": "address" } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { "internalType": "address", "name": "sender", "type": "address" }, + { "internalType": "address", "name": "calledContract", "type": "address" }, + { "internalType": "string", "name": "methodSignature", "type": "string" } + ], + "name": "Unauthorized", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "asset", "type": "address" }, + { "indexed": true, "internalType": "bool", "name": "enabled", "type": "bool" } + ], + "name": "CachedEnabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "internalType": "uint8", "name": "version", "type": "uint8" }], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "oldAccessControlManager", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "newAccessControlManager", "type": "address" } + ], + "name": "NewAccessControlManager", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "asset", "type": "address" }, + { "indexed": true, "internalType": "uint256", "name": "role", "type": "uint256" }, + { "indexed": true, "internalType": "bool", "name": "enable", "type": "bool" } + ], + "name": "OracleEnabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "asset", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "oracle", "type": "address" }, + { "indexed": true, "internalType": "uint256", "name": "role", "type": "uint256" } + ], + "name": "OracleSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "previousOwner", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "newOwner", "type": "address" } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "previousOwner", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "newOwner", "type": "address" } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "internalType": "address", "name": "account", "type": "address" }], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "asset", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "mainOracle", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "pivotOracle", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "fallbackOracle", "type": "address" } + ], + "name": "TokenConfigAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "internalType": "address", "name": "account", "type": "address" }], + "name": "Unpaused", + "type": "event" + }, + { + "inputs": [], + "name": "CACHE_SLOT", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "INVALID_PRICE", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "NATIVE_TOKEN_ADDR", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { "inputs": [], "name": "acceptOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "accessControlManager", + "outputs": [{ "internalType": "contract IAccessControlManagerV8", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "boundValidator", + "outputs": [{ "internalType": "contract BoundValidatorInterface", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "asset", "type": "address" }, + { "internalType": "enum ResilientOracle.OracleRole", "name": "role", "type": "uint8" }, + { "internalType": "bool", "name": "enable", "type": "bool" } + ], + "name": "enableOracle", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "asset", "type": "address" }, + { "internalType": "enum ResilientOracle.OracleRole", "name": "role", "type": "uint8" } + ], + "name": "getOracle", + "outputs": [ + { "internalType": "address", "name": "oracle", "type": "address" }, + { "internalType": "bool", "name": "enabled", "type": "bool" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "asset", "type": "address" }], + "name": "getPrice", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "asset", "type": "address" }], + "name": "getTokenConfig", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "asset", "type": "address" }, + { "internalType": "address[3]", "name": "oracles", "type": "address[3]" }, + { "internalType": "bool[3]", "name": "enableFlagsForOracles", "type": "bool[3]" }, + { "internalType": "bool", "name": "cachingEnabled", "type": "bool" } + ], + "internalType": "struct ResilientOracle.TokenConfig", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "vToken", "type": "address" }], + "name": "getUnderlyingPrice", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "accessControlManager_", "type": "address" }], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "nativeMarket", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { "inputs": [], "name": "pause", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "paused", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { "inputs": [], "name": "renounceOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [{ "internalType": "address", "name": "accessControlManager_", "type": "address" }], + "name": "setAccessControlManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "asset", "type": "address" }, + { "internalType": "address", "name": "oracle", "type": "address" }, + { "internalType": "enum ResilientOracle.OracleRole", "name": "role", "type": "uint8" } + ], + "name": "setOracle", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "asset", "type": "address" }, + { "internalType": "address[3]", "name": "oracles", "type": "address[3]" }, + { "internalType": "bool[3]", "name": "enableFlagsForOracles", "type": "bool[3]" }, + { "internalType": "bool", "name": "cachingEnabled", "type": "bool" } + ], + "internalType": "struct ResilientOracle.TokenConfig", + "name": "tokenConfig", + "type": "tuple" + } + ], + "name": "setTokenConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "asset", "type": "address" }, + { "internalType": "address[3]", "name": "oracles", "type": "address[3]" }, + { "internalType": "bool[3]", "name": "enableFlagsForOracles", "type": "bool[3]" }, + { "internalType": "bool", "name": "cachingEnabled", "type": "bool" } + ], + "internalType": "struct ResilientOracle.TokenConfig[]", + "name": "tokenConfigs_", + "type": "tuple[]" + } + ], + "name": "setTokenConfigs", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "newOwner", "type": "address" }], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { "inputs": [], "name": "unpause", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [{ "internalType": "address", "name": "asset", "type": "address" }], + "name": "updateAssetPrice", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "vToken", "type": "address" }], + "name": "updatePrice", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "vai", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + } +] diff --git a/simulations/vip-610/abi/VToken.json b/simulations/vip-610/abi/VToken.json new file mode 100644 index 000000000..68f428c6c --- /dev/null +++ b/simulations/vip-610/abi/VToken.json @@ -0,0 +1,870 @@ +[ + { "inputs": [], "stateMutability": "nonpayable", "type": "constructor" }, + { "inputs": [], "name": "FlashLoanAlreadyActive", "type": "error" }, + { + "inputs": [ + { "internalType": "uint256", "name": "fee", "type": "uint256" }, + { "internalType": "uint256", "name": "maxFee", "type": "uint256" } + ], + "name": "FlashLoanFeeTooHigh", + "type": "error" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "fee", "type": "uint256" }, + { "internalType": "uint256", "name": "maxFee", "type": "uint256" } + ], + "name": "FlashLoanProtocolShareTooHigh", + "type": "error" + }, + { "inputs": [], "name": "InsufficientCash", "type": "error" }, + { + "inputs": [ + { "internalType": "uint256", "name": "actualAmount", "type": "uint256" }, + { "internalType": "uint256", "name": "requiredTotalFee", "type": "uint256" } + ], + "name": "InsufficientRepayment", + "type": "error" + }, + { "inputs": [], "name": "InvalidComptroller", "type": "error" }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "uint256", "name": "cashPrior", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "interestAccumulated", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "borrowIndex", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "totalBorrows", "type": "uint256" } + ], + "name": "AccrueInterest", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "owner", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "spender", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "borrower", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "borrowAmount", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "accountBorrows", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "totalBorrows", "type": "uint256" } + ], + "name": "Borrow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "uint256", "name": "error", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "info", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "detail", "type": "uint256" } + ], + "name": "Failure", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "uint256", "name": "oldFlashLoanFeeMantissa", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "newFlashLoanFeeMantissa", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "oldFlashLoanProtocolShare", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "newFlashLoanProtocolShare", "type": "uint256" } + ], + "name": "FlashLoanFeeUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "bool", "name": "previousStatus", "type": "bool" }, + { "indexed": false, "internalType": "bool", "name": "newStatus", "type": "bool" } + ], + "name": "FlashLoanStatusChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "liquidator", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "borrower", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "repayAmount", "type": "uint256" }, + { "indexed": false, "internalType": "address", "name": "vTokenCollateral", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "seizeTokens", "type": "uint256" } + ], + "name": "LiquidateBorrow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "minter", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "mintAmount", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "mintTokens", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "totalSupply", "type": "uint256" } + ], + "name": "Mint", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "payer", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "receiver", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "mintAmount", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "mintTokens", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "totalSupply", "type": "uint256" } + ], + "name": "MintBehalf", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "oldAccessControlAddress", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "newAccessControlAddress", "type": "address" } + ], + "name": "NewAccessControlManager", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "oldAdmin", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "newAdmin", "type": "address" } + ], + "name": "NewAdmin", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract ComptrollerInterface", + "name": "oldComptroller", + "type": "address" + }, + { "indexed": false, "internalType": "contract ComptrollerInterface", "name": "newComptroller", "type": "address" } + ], + "name": "NewComptroller", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract InterestRateModelV8", + "name": "oldInterestRateModel", + "type": "address" + }, + { + "indexed": false, + "internalType": "contract InterestRateModelV8", + "name": "newInterestRateModel", + "type": "address" + } + ], + "name": "NewMarketInterestRateModel", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "oldPendingAdmin", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "newPendingAdmin", "type": "address" } + ], + "name": "NewPendingAdmin", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "oldProtocolShareReserve", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "newProtocolShareReserve", "type": "address" } + ], + "name": "NewProtocolShareReserve", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "uint256", "name": "oldReduceReservesBlockDelta", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "newReduceReservesBlockDelta", "type": "uint256" } + ], + "name": "NewReduceReservesBlockDelta", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "uint256", "name": "oldReserveFactorMantissa", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "newReserveFactorMantissa", "type": "uint256" } + ], + "name": "NewReserveFactor", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "redeemer", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "redeemAmount", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "redeemTokens", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "totalSupply", "type": "uint256" } + ], + "name": "Redeem", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "redeemer", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "feeAmount", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "redeemTokens", "type": "uint256" } + ], + "name": "RedeemFee", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "payer", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "borrower", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "repayAmount", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "accountBorrows", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "totalBorrows", "type": "uint256" } + ], + "name": "RepayBorrow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "benefactor", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "addAmount", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "newTotalReserves", "type": "uint256" } + ], + "name": "ReservesAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "protocolShareReserve", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "reduceAmount", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "newTotalReserves", "type": "uint256" } + ], + "name": "ReservesReduced", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "from", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "to", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "asset", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "sender", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "totalFee", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "protocolFee", "type": "uint256" } + ], + "name": "TransferInUnderlyingFlashLoan", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "asset", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "receiver", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "TransferOutUnderlyingFlashLoan", + "type": "event" + }, + { + "inputs": [], + "name": "_acceptAdmin", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "addAmount", "type": "uint256" }], + "name": "_addReserves", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "bytes", "name": "data", "type": "bytes" }], + "name": "_becomeImplementation", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "reduceAmount_", "type": "uint256" }], + "name": "_reduceReserves", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { "inputs": [], "name": "_resignImplementation", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [{ "internalType": "contract ComptrollerInterface", "name": "newComptroller", "type": "address" }], + "name": "_setComptroller", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "contract InterestRateModelV8", "name": "newInterestRateModel_", "type": "address" }], + "name": "_setInterestRateModel", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address payable", "name": "newPendingAdmin", "type": "address" }], + "name": "_setPendingAdmin", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "newReserveFactorMantissa_", "type": "uint256" }], + "name": "_setReserveFactor", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "accessControlManager", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "accrualBlockNumber", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "accrueInterest", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "admin", + "outputs": [{ "internalType": "address payable", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "address", "name": "spender", "type": "address" } + ], + "name": "allowance", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "spender", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "approve", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "owner", "type": "address" }], + "name": "balanceOf", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "owner", "type": "address" }], + "name": "balanceOfUnderlying", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "borrowAmount", "type": "uint256" }], + "name": "borrow", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "account", "type": "address" }], + "name": "borrowBalanceCurrent", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "account", "type": "address" }], + "name": "borrowBalanceStored", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "borrower", "type": "address" }, + { "internalType": "uint256", "name": "borrowAmount", "type": "uint256" } + ], + "name": "borrowBehalf", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "borrowIndex", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "borrowRatePerBlock", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "amount", "type": "uint256" }], + "name": "calculateFlashLoanFee", + "outputs": [ + { "internalType": "uint256", "name": "", "type": "uint256" }, + { "internalType": "uint256", "name": "", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "comptroller", + "outputs": [{ "internalType": "contract ComptrollerInterface", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "exchangeRateCurrent", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "exchangeRateStored", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "flashLoanAmount", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "borrower", "type": "address" }, + { "internalType": "uint256", "name": "borrowAmount", "type": "uint256" } + ], + "name": "flashLoanDebtPosition", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "flashLoanFeeMantissa", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "flashLoanProtocolShareMantissa", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "account", "type": "address" }], + "name": "getAccountSnapshot", + "outputs": [ + { "internalType": "uint256", "name": "", "type": "uint256" }, + { "internalType": "uint256", "name": "", "type": "uint256" }, + { "internalType": "uint256", "name": "", "type": "uint256" }, + { "internalType": "uint256", "name": "", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getCash", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "implementation", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "underlying_", "type": "address" }, + { "internalType": "contract ComptrollerInterface", "name": "comptroller_", "type": "address" }, + { "internalType": "contract InterestRateModelV8", "name": "interestRateModel_", "type": "address" }, + { "internalType": "uint256", "name": "initialExchangeRateMantissa_", "type": "uint256" }, + { "internalType": "string", "name": "name_", "type": "string" }, + { "internalType": "string", "name": "symbol_", "type": "string" }, + { "internalType": "uint8", "name": "decimals_", "type": "uint8" } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "contract ComptrollerInterface", "name": "comptroller_", "type": "address" }, + { "internalType": "contract InterestRateModelV8", "name": "interestRateModel_", "type": "address" }, + { "internalType": "uint256", "name": "initialExchangeRateMantissa_", "type": "uint256" }, + { "internalType": "string", "name": "name_", "type": "string" }, + { "internalType": "string", "name": "symbol_", "type": "string" }, + { "internalType": "uint8", "name": "decimals_", "type": "uint8" } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "interestRateModel", + "outputs": [{ "internalType": "contract InterestRateModelV8", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isFlashLoanEnabled", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isVToken", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "borrower", "type": "address" }, + { "internalType": "uint256", "name": "repayAmount", "type": "uint256" }, + { "internalType": "contract VTokenInterface", "name": "vTokenCollateral", "type": "address" } + ], + "name": "liquidateBorrow", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "mintAmount", "type": "uint256" }], + "name": "mint", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "receiver", "type": "address" }, + { "internalType": "uint256", "name": "mintAmount", "type": "uint256" } + ], + "name": "mintBehalf", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingAdmin", + "outputs": [{ "internalType": "address payable", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "protocolShareReserve", + "outputs": [{ "internalType": "address payable", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "redeemTokens", "type": "uint256" }], + "name": "redeem", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "redeemer", "type": "address" }, + { "internalType": "uint256", "name": "redeemTokens", "type": "uint256" } + ], + "name": "redeemBehalf", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "redeemAmount", "type": "uint256" }], + "name": "redeemUnderlying", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "redeemer", "type": "address" }, + { "internalType": "uint256", "name": "redeemAmount", "type": "uint256" } + ], + "name": "redeemUnderlyingBehalf", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "reduceReservesBlockDelta", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "reduceReservesBlockNumber", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "repayAmount", "type": "uint256" }], + "name": "repayBorrow", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "borrower", "type": "address" }, + { "internalType": "uint256", "name": "repayAmount", "type": "uint256" } + ], + "name": "repayBorrowBehalf", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "reserveFactorMantissa", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "liquidator", "type": "address" }, + { "internalType": "address", "name": "borrower", "type": "address" }, + { "internalType": "uint256", "name": "seizeTokens", "type": "uint256" } + ], + "name": "seize", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "newAccessControlManagerAddress", "type": "address" }], + "name": "setAccessControlManager", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "bool", "name": "enabled", "type": "bool" }], + "name": "setFlashLoanEnabled", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "flashLoanFeeMantissa_", "type": "uint256" }, + { "internalType": "uint256", "name": "flashLoanProtocolShare_", "type": "uint256" } + ], + "name": "setFlashLoanFeeMantissa", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address payable", "name": "protcolShareReserve_", "type": "address" }], + "name": "setProtocolShareReserve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "newReduceReservesBlockDelta_", "type": "uint256" }], + "name": "setReduceReservesBlockDelta", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "supplyRatePerBlock", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalBorrows", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalBorrowsCurrent", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "totalReserves", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "dst", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transfer", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "src", "type": "address" }, + { "internalType": "address", "name": "dst", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transferFrom", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address payable", "name": "from", "type": "address" }, + { "internalType": "uint256", "name": "repaymentAmount", "type": "uint256" }, + { "internalType": "uint256", "name": "totalFee", "type": "uint256" }, + { "internalType": "uint256", "name": "protocolFee", "type": "uint256" } + ], + "name": "transferInUnderlyingFlashLoan", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address payable", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transferOutUnderlyingFlashLoan", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "underlying", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + } +] diff --git a/simulations/vip-610/bscmainnet.ts b/simulations/vip-610/bscmainnet.ts index 000ee1178..416ec1d64 100644 --- a/simulations/vip-610/bscmainnet.ts +++ b/simulations/vip-610/bscmainnet.ts @@ -1,8 +1,8 @@ import { expect } from "chai"; -import { Contract } from "ethers"; +import { BigNumber, Contract, Wallet } from "ethers"; import { ethers } from "hardhat"; import { NETWORK_ADDRESSES } from "src/networkAddresses"; -import { initMainnetUser } from "src/utils"; +import { expectEvents, initMainnetUser } from "src/utils"; import { forking, testVip } from "src/vip-framework"; import { @@ -12,10 +12,22 @@ import { vip610, } from "../../vips/vip-610/bscmainnet"; import ACCESS_CONTROL_MANAGER_ABI from "./abi/AccessControlManager.json"; +import BINANCE_ORACLE_ABI from "./abi/BinanceOracle.json"; +import CHAINLINK_ORACLE_ABI from "./abi/ChainlinkOracle.json"; +import FLASHLOAN_FACET_ABI from "./abi/FlashLoanFacet.json"; import RELATIVE_POSITION_MANAGER_ABI from "./abi/RelativePositionManager.json"; +import RESILIENT_ORACLE_ABI from "./abi/ResilientOracle.json"; +import SWAP_HELPER_ABI from "./abi/SwapHelperAbi.json"; const { bscmainnet } = NETWORK_ADDRESSES; +const DSA_VTOKENS = [ + "0xecA88125a5ADbe82614ffC12D0DB554E2e2867C8", // vUSDC + "0xfD5840Cd36d94D7229439859C0112a4185BC0255", // vUSDT + "0x95c78222B3D6e262426483D42CfA53685A67Ab9D", // vBUSD + "0x334b3eCB4DCa3593BCCC3c7EBD1A1C1d1780FBF1", // vDAI +]; + const ACM_FUNCTION_SIGNATURES = [ "partialPause()", "partialUnpause()", @@ -58,7 +70,18 @@ forking(87147067, async () => { }); }); - testVip("VIP-610 [BNB Chain] Configure Relative Position Manager", await vip610()); + testVip("VIP-610 [BNB Chain] Configure Relative Position Manager", await vip610(), { + callbackAfterExecution: async txResponse => { + await expectEvents(txResponse, [RELATIVE_POSITION_MANAGER_ABI], ["OwnershipTransferred"], [1]); + await expectEvents(txResponse, [ACCESS_CONTROL_MANAGER_ABI], ["RoleGranted"], [36]); + await expectEvents( + txResponse, + [RELATIVE_POSITION_MANAGER_ABI], + ["PositionAccountImplementationSet"], + [1], + ); + }, + }); describe("Post-VIP behavior", () => { it("RelativePositionManager should have NORMAL_TIMELOCK as owner and no pending owner", async () => { @@ -80,13 +103,23 @@ forking(87147067, async () => { expect(await relativePositionManager.POSITION_ACCOUNT_IMPLEMENTATION()).to.equals(POSITION_ACCOUNT); }); + it("Setting Position Account implementation again should revert", async () => { + const signer = await initMainnetUser(bscmainnet.NORMAL_TIMELOCK, ethers.utils.parseEther("1")); + await expect( + relativePositionManager.connect(signer).setPositionAccountImplementation(POSITION_ACCOUNT), + ).to.be.revertedWithCustomError(relativePositionManager, "PositionAccountImplementationLocked"); + }); + + let dsaVTokenIndex = 0; for (const timelockOrGuardian of TIMELOCKS_AND_GUARDIAN) { describe(`ACM-gated functions should be callable by ${timelockOrGuardian}`, () => { let rpmAsCaller: Contract; + let currentDsaIndex: number; before(async () => { const signer = await initMainnetUser(timelockOrGuardian, ethers.utils.parseEther("1")); rpmAsCaller = relativePositionManager.connect(signer); + currentDsaIndex = dsaVTokenIndex++; }); it("partialPause and partialUnpause", async () => { @@ -109,7 +142,1038 @@ forking(87147067, async () => { await rpmAsCaller.setProportionalCloseTolerance(newValue); expect(await relativePositionManager.proportionalCloseTolerance()).to.equal(newValue); }); + + it("addDSAVToken and setDSAVTokenActive", async () => { + await rpmAsCaller.addDSAVToken(DSA_VTOKENS[currentDsaIndex]); + expect(await relativePositionManager.dsaVTokens(currentDsaIndex)).to.equal(DSA_VTOKENS[currentDsaIndex]); + expect(await relativePositionManager.isDsaVTokenActive(DSA_VTOKENS[currentDsaIndex])).to.equal(true); + await rpmAsCaller.setDSAVTokenActive(currentDsaIndex, false); + expect(await relativePositionManager.isDsaVTokenActive(DSA_VTOKENS[currentDsaIndex])).to.equal(false); + }); + + it("executePositionAccountCall", async () => { + // Reverts with InvalidCallsLength (not Unauthorized) — proves ACM permission passed + await expect( + rpmAsCaller.executePositionAccountCall(POSITION_ACCOUNT, [], []), + ).to.be.revertedWithCustomError(relativePositionManager, "InvalidCallsLength"); + }); }); } }); + + describe("E2E: RelativePositionManager flows", function () { + this.timeout(720_000); // 12 minutes + + // --- E2E test constants --- + const SWAP_HELPER_ADDRESS = "0xD79be25aEe798Aa34A9Ba1230003d7499be29A24"; + const LEVERAGE_STRATEGIES_MANAGER_ADDRESS = "0x03F079E809185a669Ca188676D0ADb09cbAd6dC1"; + + // Close buffer multipliers (basis: 1000 = 1x) + // Full close: 2% contract proportional-close tolerance + 0.2% interest accrual buffer + const FULL_CLOSE_BUFFER_BPS = 1022; + // Partial close: 0.2% interest accrual buffer only (no contract tolerance applied) + const PARTIAL_CLOSE_BUFFER_BPS = 1002; + + // Core pool markets (BSC mainnet) + const DSA_ADDRESS = "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d"; // USDC + const vDSA_ADDRESS = "0xecA88125a5ADbe82614ffC12D0DB554E2e2867C8"; // vUSDC + const LONG_ADDRESS = "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c"; // WBNB + const vLONG_ADDRESS = "0x6bCa74586218dB34cdB402295796b79663d816e9"; // vWBNB + const SHORT_ADDRESS = "0x2170Ed0880ac9A755fd29B2688956BD959F933F8"; // ETH + const vSHORT_ADDRESS = "0xf508fCD89b8bd15579dc79A6827cB4686A3592c8"; // vETH + + const DSA_WHALE = DSA_ADDRESS; + const SHORT_WHALE = "0x8894E0a0c962CB723c1976a4421c95949bE2D4E3"; // Binance 8 (for ETH) + + // Oracles + const REDSTONE_ORACLE = "0x8455EFA4D7Ff63b8BFD96AdD889483Ea7d39B70a"; + const CHAINLINK_ORACLE_ADDRESS = "0x1B2103441A0A108daD8848D8F5d790e4D402921F"; + const BINANCE_ORACLE = "0x594810b741d136f1960141C0d8Fb4a91bE78A820"; + + // Minimal inline ABIs for E2E tests + const ERC20_ABI = [ + "function transfer(address to, uint256 amount) returns (bool)", + "function approve(address spender, uint256 amount) returns (bool)", + "function balanceOf(address account) view returns (uint256)", + ]; + + const VTOKEN_ABI = require("./abi/VToken.json"); + + let saltCounter = 0; + let comptroller: Contract; + let dsa: Contract; + let shortVToken: Contract; + let dsaVToken: Contract; + let alice: any; + let bob: any; + + /** + * Creates manipulated swap data for deterministic testing. + * Funds SwapHelper with output tokens and encodes a signed multicall + * that transfers them to the recipient (no real DEX swap). + */ + async function getManipulatedSwapData( + tokenIn: string, + tokenOut: string, + amountIn: BigNumber, + amountOut: BigNumber, + recipient: string, + tokenOutWhaleOverride?: string, + ): Promise { + const swapSignerWallet = new Wallet( + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", + ethers.provider, + ); + const swapHelperContract = new ethers.Contract(SWAP_HELPER_ADDRESS, SWAP_HELPER_ABI, ethers.provider); + + const swapHelperOwner = await swapHelperContract.owner(); + const impersonatedOwner = await initMainnetUser(swapHelperOwner, ethers.utils.parseEther("1")); + await swapHelperContract.connect(impersonatedOwner).setBackendSigner(swapSignerWallet.address); + + const domain = await swapHelperContract.eip712Domain(); + const network = await ethers.provider.getNetwork(); + const eip712Domain = { + name: domain.name, + version: domain.version, + chainId: network.chainId, + verifyingContract: domain.verifyingContract, + }; + + const TEN_YEARS_SECS = 10 * 365 * 24 * 60 * 60; + const deadline = Math.floor(Date.now() / 1000) + TEN_YEARS_SECS; + + // Fund SwapHelper with tokenOut + const tokenOutContract = new ethers.Contract(tokenOut, ERC20_ABI, ethers.provider); + const tokenOutWhale = tokenOutWhaleOverride ?? tokenOut; + const whaleSigner = await initMainnetUser(tokenOutWhale, ethers.utils.parseEther("1")); + await tokenOutContract.connect(whaleSigner).transfer(SWAP_HELPER_ADDRESS, amountOut); + + // Encode transfer: SwapHelper sends tokenOut to recipient + const tokenOutIface = new ethers.utils.Interface([ + "function transfer(address to, uint256 amount) returns (bool)", + ]); + const transferCalldata = tokenOutIface.encodeFunctionData("transfer", [recipient, amountOut]); + + const swapHelperIface = swapHelperContract.interface; + const calls: string[] = []; + calls.push(swapHelperIface.encodeFunctionData("genericCall", [tokenOut, transferCalldata])); + + const salt = ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["uint256"], [++saltCounter])); + + const types = { + Multicall: [ + { name: "caller", type: "address" }, + { name: "calls", type: "bytes[]" }, + { name: "deadline", type: "uint256" }, + { name: "salt", type: "bytes32" }, + ], + }; + + const value = { caller: recipient, calls, deadline, salt }; + const signature = await swapSignerWallet._signTypedData(eip712Domain, types, value); + + return swapHelperIface.encodeFunctionData("multicall", [calls, deadline, salt, signature]); + } + + /** + * Configures oracle stale periods so price feeds don't revert during fork tests. + */ + async function setMaxStalePeriod() { + const timelock = await initMainnetUser(bscmainnet.NORMAL_TIMELOCK, ethers.utils.parseEther("2")); + const chainlinkOracle = new ethers.Contract(CHAINLINK_ORACLE_ADDRESS, CHAINLINK_ORACLE_ABI, timelock); + const redStoneOracle = new ethers.Contract(REDSTONE_ORACLE, CHAINLINK_ORACLE_ABI, timelock); + const binanceOracle = new ethers.Contract(BINANCE_ORACLE, BINANCE_ORACLE_ABI, timelock); + + const ONE_YEAR = "31536000"; + const tokens = [ + { + asset: DSA_ADDRESS, + chainlinkFeed: "0x51597f405303C4377E36123cBc172b13269EA163", + redstoneFeed: "0xeA2511205b959548459A01e358E0A30424dc0B70", + binanceSymbol: "USDC", + }, + { + asset: LONG_ADDRESS, + chainlinkFeed: "0x0567F2323251f0Aab15c8dFb1967E4e8A7D42aeE", + redstoneFeed: "0x8dd2D85C7c28F43F965AE4d9545189C7D022ED0e", + binanceSymbol: "WBNB", + }, + { + asset: SHORT_ADDRESS, + chainlinkFeed: "0xe48a5Fd74d4A5524D76960ef3B52204C0e11fCD1", + redstoneFeed: "0x9cF19D284862A66378c304ACAcB0E857EBc3F856", + binanceSymbol: "ETH", + }, + ]; + + for (const token of tokens) { + await chainlinkOracle.setTokenConfig({ + asset: token.asset, + feed: token.chainlinkFeed, + maxStalePeriod: ONE_YEAR, + }); + await redStoneOracle.setTokenConfig({ + asset: token.asset, + feed: token.redstoneFeed, + maxStalePeriod: ONE_YEAR, + }); + await binanceOracle.setMaxStalePeriod(token.binanceSymbol, ONE_YEAR); + } + await binanceOracle.setMaxStalePeriod("BNB", ONE_YEAR); + } + + /** + * Manipulates the oracle price for an asset by configuring ResilientOracle to use + * only Chainlink as main oracle and calling setDirectPrice on the Chainlink oracle. + */ + async function setOraclePrice(asset: string, price: BigNumber): Promise { + const timelock = await initMainnetUser(bscmainnet.NORMAL_TIMELOCK, ethers.utils.parseEther("1")); + const resilientOracleAddr = await comptroller.oracle(); + + const resilientOracle = new ethers.Contract(resilientOracleAddr, RESILIENT_ORACLE_ABI, timelock); + + await resilientOracle.setTokenConfig({ + asset, + oracles: [CHAINLINK_ORACLE_ADDRESS, ethers.constants.AddressZero, ethers.constants.AddressZero], + enableFlagsForOracles: [true, false, false], + cachingEnabled: false, + }); + + const chainlinkOracle = new ethers.Contract(CHAINLINK_ORACLE_ADDRESS, CHAINLINK_ORACLE_ABI, timelock); + await chainlinkOracle.setDirectPrice(asset, price); + } + + before(async function () { + [, alice, bob] = await ethers.getSigners(); + + // Setup oracle stale periods + await setMaxStalePeriod(); + + const timelock = await initMainnetUser(bscmainnet.NORMAL_TIMELOCK, ethers.utils.parseEther("1")); + + // Activate DSA vToken (already added at index 0 by ACM-gated tests, left inactive) + await relativePositionManager.connect(timelock).setDSAVTokenActive(0, true); + + comptroller = await ethers.getContractAt(FLASHLOAN_FACET_ABI, bscmainnet.UNITROLLER); + + // Setup token contracts + dsa = new ethers.Contract(DSA_ADDRESS, ERC20_ABI, ethers.provider); + shortVToken = new ethers.Contract(vSHORT_ADDRESS, VTOKEN_ABI, ethers.provider); + dsaVToken = new ethers.Contract(vDSA_ADDRESS, VTOKEN_ABI, ethers.provider); + }); + + it("close with profit", async function () { + const INITIAL_PRINCIPAL = ethers.utils.parseEther("9000"); + const SHORT_AMOUNT = ethers.utils.parseEther("4"); // 4 ETH + const LONG_AMOUNT = ethers.utils.parseEther("30"); // 30 WBNB + const leverage = ethers.utils.parseEther("1.5"); + const closeFractionBps = 10000; // Full close + + // Fund Alice with USDC (DSA) + const whaleSigner = await initMainnetUser(DSA_WHALE, ethers.utils.parseEther("1")); + await dsa.connect(whaleSigner).transfer(alice.address, INITIAL_PRINCIPAL); + await dsa.connect(alice).approve(RELATIVE_POSITION_MANAGER, INITIAL_PRINCIPAL); + + // Open position with manipulated swap (SHORT -> LONG) + const minLong = LONG_AMOUNT.mul(98).div(100); + const openSwapData = await getManipulatedSwapData( + SHORT_ADDRESS, + LONG_ADDRESS, + SHORT_AMOUNT, + LONG_AMOUNT, + LEVERAGE_STRATEGIES_MANAGER_ADDRESS, + vLONG_ADDRESS, // vWBNB holds WBNB + ); + + await relativePositionManager + .connect(alice) + .activateAndOpenPosition( + vLONG_ADDRESS, + vSHORT_ADDRESS, + 0, + INITIAL_PRINCIPAL, + leverage, + SHORT_AMOUNT, + minLong, + openSwapData, + ); + + // Verify position is active + const position = await relativePositionManager.getPosition(alice.address, vLONG_ADDRESS, vSHORT_ADDRESS); + expect(position.isActive).to.eq(true); + const positionAccount = position.positionAccount; + + // Record state after open + const shortDebtAfterOpen = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); + const longBalanceAfterOpen = await relativePositionManager.callStatic.getLongCollateralBalance( + alice.address, + vLONG_ADDRESS, + vSHORT_ADDRESS, + ); + + expect(shortDebtAfterOpen).to.be.gt(0, "Should have short debt"); + expect(longBalanceAfterOpen).to.be.gt(0, "Should have long collateral"); + + // Close with profit: favorable price — use 75% of long to repay all debt, 25% is profit + const longForRepay = longBalanceAfterOpen.mul(75).div(100); + const longForProfit = longBalanceAfterOpen.sub(longForRepay); + + const shortRepayAmount = shortDebtAfterOpen.mul(FULL_CLOSE_BUFFER_BPS).div(1000); + + const repaySwapData = await getManipulatedSwapData( + LONG_ADDRESS, + SHORT_ADDRESS, + longForRepay, + shortRepayAmount, + LEVERAGE_STRATEGIES_MANAGER_ADDRESS, + ); + + // Profit: LONG -> DSA (goes to RPM, then supplied as principal) + const estimatedProfitDsa = longForProfit.mul(500); // ~7.5 WBNB * 500 USDC/WBNB ≈ 3750 USDC + const profitSwapData = await getManipulatedSwapData( + LONG_ADDRESS, + DSA_ADDRESS, + longForProfit, + estimatedProfitDsa, + RELATIVE_POSITION_MANAGER, // RPM receives profit, supplies as principal + ); + + const dsaUnderlyingBefore = await dsaVToken.callStatic.balanceOfUnderlying(positionAccount); + + await relativePositionManager.connect(alice).closeWithProfit( + vLONG_ADDRESS, + vSHORT_ADDRESS, + closeFractionBps, + longForRepay, // longAmountToRedeemForRepay + shortRepayAmount, // minAmountOutRepay + repaySwapData, + longForProfit, // longAmountToRedeemForProfit + estimatedProfitDsa.mul(98).div(100), // minAmountOutProfit + profitSwapData, + ); + + // Verify full close + const shortDebtAfterClose = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); + const longBalanceAfterClose = await relativePositionManager.callStatic.getLongCollateralBalance( + alice.address, + vLONG_ADDRESS, + vSHORT_ADDRESS, + ); + const dsaUnderlyingAfter = await dsaVToken.callStatic.balanceOfUnderlying(positionAccount); + + expect(shortDebtAfterClose).to.eq(0, "All debt repaid"); + expect(longBalanceAfterClose).to.eq(0, "All long redeemed"); + + const dsaUnderlyingIncrease = dsaUnderlyingAfter.sub(dsaUnderlyingBefore); + expect(dsaUnderlyingIncrease).to.be.closeTo( + estimatedProfitDsa, + ethers.utils.parseEther("1"), // small tolerance for interest accrual + "DSA increase ~= profit from swap", + ); + + const positionAfterClose = await relativePositionManager.getPosition( + alice.address, + vLONG_ADDRESS, + vSHORT_ADDRESS, + ); + expect(positionAfterClose.isActive).to.eq(true); + }); + + it("close with loss", async function () { + const INITIAL_PRINCIPAL = ethers.utils.parseEther("15000"); + const SHORT_AMOUNT = ethers.utils.parseEther("3"); // 3 ETH + const LONG_AMOUNT = ethers.utils.parseEther("25"); // 25 WBNB + const leverage = ethers.utils.parseEther("3"); + const closeFractionBps = 10000; // Full close + + // Fund Bob with USDC (DSA) + const whaleSigner = await initMainnetUser(DSA_WHALE, ethers.utils.parseEther("1")); + await dsa.connect(whaleSigner).transfer(bob.address, INITIAL_PRINCIPAL); + await dsa.connect(bob).approve(RELATIVE_POSITION_MANAGER, INITIAL_PRINCIPAL); + + // Open position + const minLong = LONG_AMOUNT.mul(98).div(100); + const openSwapData = await getManipulatedSwapData( + SHORT_ADDRESS, + LONG_ADDRESS, + SHORT_AMOUNT, + LONG_AMOUNT, + LEVERAGE_STRATEGIES_MANAGER_ADDRESS, + vLONG_ADDRESS, + ); + + await relativePositionManager + .connect(bob) + .activateAndOpenPosition( + vLONG_ADDRESS, + vSHORT_ADDRESS, + 0, + INITIAL_PRINCIPAL, + leverage, + SHORT_AMOUNT, + minLong, + openSwapData, + ); + + const position = await relativePositionManager.getPosition(bob.address, vLONG_ADDRESS, vSHORT_ADDRESS); + expect(position.isActive).to.eq(true); + const positionAccount = position.positionAccount; + + const shortDebtAfterOpen = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); + const longBalanceAfterOpen = await relativePositionManager.callStatic.getLongCollateralBalance( + bob.address, + vLONG_ADDRESS, + vSHORT_ADDRESS, + ); + + // Simulate 20% loss: swapping all LONG (~25 WBNB) only gets 80% of SHORT (ETH) needed + const shortAmountFromLongSwap = shortDebtAfterOpen.mul(80).div(100); // 80% of ETH debt + const shortfall = shortDebtAfterOpen.sub(shortAmountFromLongSwap); // 20% ETH shortfall + + // First swap: LONG -> SHORT (all WBNB -> ETH, but 20% short) + const firstSwapData = await getManipulatedSwapData( + LONG_ADDRESS, + SHORT_ADDRESS, + longBalanceAfterOpen, + shortAmountFromLongSwap, + LEVERAGE_STRATEGIES_MANAGER_ADDRESS, + ); + + // Second swap: DSA -> SHORT (25% of USDC principal -> ETH to cover shortfall) + const dsaAmountToSwap = INITIAL_PRINCIPAL.mul(25).div(100); // 3750 USDC + const shortFromDsaSwap = shortfall.mul(FULL_CLOSE_BUFFER_BPS).div(1000); + + const secondSwapData = await getManipulatedSwapData( + DSA_ADDRESS, + SHORT_ADDRESS, + dsaAmountToSwap, + shortFromDsaSwap, + LEVERAGE_STRATEGIES_MANAGER_ADDRESS, + ); + + const dsaUnderlyingBefore = await dsaVToken.callStatic.balanceOfUnderlying(positionAccount); + + await relativePositionManager.connect(bob).closeWithLoss( + vLONG_ADDRESS, + vSHORT_ADDRESS, + closeFractionBps, + longBalanceAfterOpen, // longAmountToRedeemForFirstSwap + shortAmountFromLongSwap, // shortAmountToRepayForFirstSwap + shortAmountFromLongSwap, // minAmountOutFirstSwap + firstSwapData, + dsaAmountToSwap, // dsaAmountToRedeemForSecondSwap + shortFromDsaSwap, // minAmountOutSecondSwap + secondSwapData, + ); + + // Verify full close + const shortDebtAfterClose = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); + const longBalanceAfterClose = await relativePositionManager.callStatic.getLongCollateralBalance( + bob.address, + vLONG_ADDRESS, + vSHORT_ADDRESS, + ); + + expect(shortDebtAfterClose).to.eq(0, "All debt repaid"); + expect(longBalanceAfterClose).to.eq(0, "All long redeemed"); + + // Verify DSA principal decreased by ~dsaAmountToSwap (~3750 USDC used to cover loss) + const dsaUnderlyingAfter = await dsaVToken.callStatic.balanceOfUnderlying(positionAccount); + const dsaUnderlyingDecrease = dsaUnderlyingBefore.sub(dsaUnderlyingAfter); + expect(dsaUnderlyingDecrease).to.be.closeTo( + dsaAmountToSwap, // ~3750 USDC redeemed from principal to cover ETH shortfall + ethers.utils.parseEther("1"), // small tolerance for interest accrual + "DSA principal decreased ~= USDC used to cover loss", + ); + + // Deactivate and verify principal withdrawal + await relativePositionManager.connect(bob).deactivatePosition(vLONG_ADDRESS, vSHORT_ADDRESS); + const positionAfter = await relativePositionManager.getPosition(bob.address, vLONG_ADDRESS, vSHORT_ADDRESS); + expect(positionAfter.isActive).to.eq(false); + }); + + it("scale position (increase leverage)", async function () { + const [, , , , , scaleUser] = await ethers.getSigners(); + const INITIAL_PRINCIPAL = ethers.utils.parseEther("10000"); + const SHORT_AMOUNT = ethers.utils.parseEther("4"); // 4 ETH + const LONG_AMOUNT = ethers.utils.parseEther("30"); // 30 WBNB + const leverage = ethers.utils.parseEther("1.5"); + + // Fund and open position + const whaleSigner = await initMainnetUser(DSA_WHALE, ethers.utils.parseEther("1")); + await dsa + .connect(whaleSigner) + .transfer(scaleUser.address, INITIAL_PRINCIPAL.add(ethers.utils.parseEther("5000"))); + await dsa + .connect(scaleUser) + .approve(RELATIVE_POSITION_MANAGER, INITIAL_PRINCIPAL.add(ethers.utils.parseEther("5000"))); + + const openSwapData = await getManipulatedSwapData( + SHORT_ADDRESS, + LONG_ADDRESS, + SHORT_AMOUNT, + LONG_AMOUNT, + LEVERAGE_STRATEGIES_MANAGER_ADDRESS, + vLONG_ADDRESS, + ); + + await relativePositionManager + .connect(scaleUser) + .activateAndOpenPosition( + vLONG_ADDRESS, + vSHORT_ADDRESS, + 0, + INITIAL_PRINCIPAL, + leverage, + SHORT_AMOUNT, + LONG_AMOUNT.mul(98).div(100), + openSwapData, + ); + + const position = await relativePositionManager.getPosition(scaleUser.address, vLONG_ADDRESS, vSHORT_ADDRESS); + const positionAccount = position.positionAccount; + + const shortDebtAfterOpen = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); + const longBalanceAfterOpen = await relativePositionManager.callStatic.getLongCollateralBalance( + scaleUser.address, + vLONG_ADDRESS, + vSHORT_ADDRESS, + ); + + // Scale: borrow more SHORT, swap to more LONG + const ADDITIONAL_PRINCIPAL = ethers.utils.parseEther("5000"); + const SCALE_SHORT_AMOUNT = ethers.utils.parseEther("2"); // 2 ETH + const scaleLongAmount = ethers.utils.parseEther("18"); // 18 WBNB + + const scaleSwapData = await getManipulatedSwapData( + SHORT_ADDRESS, + LONG_ADDRESS, + SCALE_SHORT_AMOUNT, + scaleLongAmount, + LEVERAGE_STRATEGIES_MANAGER_ADDRESS, + vLONG_ADDRESS, + ); + + await relativePositionManager + .connect(scaleUser) + .scalePosition( + vLONG_ADDRESS, + vSHORT_ADDRESS, + ADDITIONAL_PRINCIPAL, + SCALE_SHORT_AMOUNT, + scaleLongAmount.mul(98).div(100), + scaleSwapData, + ); + + // Validate increased debt and long balance + const shortDebtAfterScale = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); + const longBalanceAfterScale = await relativePositionManager.callStatic.getLongCollateralBalance( + scaleUser.address, + vLONG_ADDRESS, + vSHORT_ADDRESS, + ); + + // Total debt should be ~6 ETH (4 initial + 2 scaled) + const expectedTotalDebt = shortDebtAfterOpen.add(SCALE_SHORT_AMOUNT); + expect(shortDebtAfterScale).to.be.closeTo( + expectedTotalDebt, + ethers.utils.parseEther("0.1"), // small tolerance for interest accrual + "Debt ~= initial + scale ETH borrowed", + ); + + // Total long should be ~48 WBNB (30 initial + 18 scaled) + const expectedTotalLong = longBalanceAfterOpen.add(scaleLongAmount); + expect(longBalanceAfterScale).to.be.closeTo( + expectedTotalLong, + ethers.utils.parseEther("1"), // small tolerance for interest/rounding + "Long ~= initial + scale WBNB collateral", + ); + + const positionAfterScale = await relativePositionManager.getPosition( + scaleUser.address, + vLONG_ADDRESS, + vSHORT_ADDRESS, + ); + expect(positionAfterScale.isActive).to.eq(true); + }); + + it("partial close with profit (50%)", async function () { + const [, , , , , , partialUser] = await ethers.getSigners(); + const INITIAL_PRINCIPAL = ethers.utils.parseEther("9000"); + const SHORT_AMOUNT = ethers.utils.parseEther("4"); // 4 ETH + const LONG_AMOUNT = ethers.utils.parseEther("30"); // 30 WBNB + const leverage = ethers.utils.parseEther("1.5"); + const closeFractionBps = 5000; // 50% partial close + + // Fund and open position + const whaleSigner = await initMainnetUser(DSA_WHALE, ethers.utils.parseEther("1")); + await dsa.connect(whaleSigner).transfer(partialUser.address, INITIAL_PRINCIPAL); + await dsa.connect(partialUser).approve(RELATIVE_POSITION_MANAGER, INITIAL_PRINCIPAL); + + const openSwapData = await getManipulatedSwapData( + SHORT_ADDRESS, + LONG_ADDRESS, + SHORT_AMOUNT, + LONG_AMOUNT, + LEVERAGE_STRATEGIES_MANAGER_ADDRESS, + vLONG_ADDRESS, + ); + + await relativePositionManager + .connect(partialUser) + .activateAndOpenPosition( + vLONG_ADDRESS, + vSHORT_ADDRESS, + 0, + INITIAL_PRINCIPAL, + leverage, + SHORT_AMOUNT, + LONG_AMOUNT.mul(98).div(100), + openSwapData, + ); + + const position = await relativePositionManager.getPosition(partialUser.address, vLONG_ADDRESS, vSHORT_ADDRESS); + const positionAccount = position.positionAccount; + + const shortDebtAfterOpen = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); + const longBalanceAfterOpen = await relativePositionManager.callStatic.getLongCollateralBalance( + partialUser.address, + vLONG_ADDRESS, + vSHORT_ADDRESS, + ); + + // Proportional amounts based on close fraction + const expectedLongToRedeem = longBalanceAfterOpen.mul(closeFractionBps).div(10000); + const expectedShortToRepay = shortDebtAfterOpen.mul(closeFractionBps).div(10000); + + // Favorable price: 80% of redeemed WBNB covers ETH repay, 20% is profit + const longForRepay = expectedLongToRedeem.mul(80).div(100); + const longForProfit = expectedLongToRedeem.sub(longForRepay); // ~20% of 50% = ~3 WBNB + + const shortRepayAmount = expectedShortToRepay.mul(PARTIAL_CLOSE_BUFFER_BPS).div(1000); + + const repaySwapData = await getManipulatedSwapData( + LONG_ADDRESS, + SHORT_ADDRESS, + longForRepay, + shortRepayAmount, + LEVERAGE_STRATEGIES_MANAGER_ADDRESS, + ); + + // Profit: ~3 WBNB * 500 USDC/WBNB ≈ 1500 USDC + const estimatedProfitDsa = longForProfit.mul(500); + const profitSwapData = await getManipulatedSwapData( + LONG_ADDRESS, + DSA_ADDRESS, + longForProfit, + estimatedProfitDsa, + RELATIVE_POSITION_MANAGER, + ); + + const dsaUnderlyingBefore = await dsaVToken.callStatic.balanceOfUnderlying(positionAccount); + + await relativePositionManager + .connect(partialUser) + .closeWithProfit( + vLONG_ADDRESS, + vSHORT_ADDRESS, + closeFractionBps, + longForRepay, + shortRepayAmount, + repaySwapData, + longForProfit, + estimatedProfitDsa.mul(98).div(100), + profitSwapData, + ); + + const dsaUnderlyingAfter = await dsaVToken.callStatic.balanceOfUnderlying(positionAccount); + const dsaUnderlyingIncrease = dsaUnderlyingAfter.sub(dsaUnderlyingBefore); + expect(dsaUnderlyingIncrease).to.be.closeTo( + estimatedProfitDsa, + ethers.utils.parseEther("1"), // small tolerance for interest accrual + "DSA increase ~= profit from swap", + ); + + // Remaining balances should be ~50% of initial + const shortDebtAfterClose = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); + const longBalanceAfterClose = await relativePositionManager.callStatic.getLongCollateralBalance( + partialUser.address, + vLONG_ADDRESS, + vSHORT_ADDRESS, + ); + + // ~50% of ~4 ETH debt remaining ≈ 2 ETH + const expectedRemainingDebt = shortDebtAfterOpen.mul(10000 - closeFractionBps).div(10000); + expect(shortDebtAfterClose).to.be.closeTo( + expectedRemainingDebt, + ethers.utils.parseEther("0.1"), // small tolerance for interest accrual + "~50% ETH debt remaining", + ); + + // ~50% of ~30 WBNB remaining ≈ 15 WBNB + const expectedRemainingLong = longBalanceAfterOpen.mul(10000 - closeFractionBps).div(10000); + expect(longBalanceAfterClose).to.be.closeTo( + expectedRemainingLong, + ethers.utils.parseEther("1"), // small tolerance for interest/rounding + "~50% WBNB collateral remaining", + ); + + // Position should still be active after partial close + expect(shortDebtAfterClose).to.be.gt(0, "Debt should remain after partial close"); + expect(longBalanceAfterClose).to.be.gt(0, "Long should remain after partial close"); + + const positionAfterClose = await relativePositionManager.getPosition( + partialUser.address, + vLONG_ADDRESS, + vSHORT_ADDRESS, + ); + expect(positionAfterClose.isActive).to.eq(true, "Position stays active after partial close"); + }); + + it("close with profit when LONG = DSA", async function () { + const [, , , , carol] = await ethers.getSigners(); + const INITIAL_PRINCIPAL = ethers.utils.parseEther("10000"); + const SHORT_AMOUNT = ethers.utils.parseEther("1"); // 1 ETH + const LONG_AMOUNT = ethers.utils.parseEther("4000"); // 4000 USDC (LONG = DSA) + const leverage = ethers.utils.parseEther("2"); + const closeFractionBps = 10000; // Full close + + // Fund Carol with USDC (DSA) + const whaleSigner = await initMainnetUser(DSA_WHALE, ethers.utils.parseEther("1")); + await dsa.connect(whaleSigner).transfer(carol.address, INITIAL_PRINCIPAL); + await dsa.connect(carol).approve(RELATIVE_POSITION_MANAGER, INITIAL_PRINCIPAL); + + // Open position: LONG = DSA (vDSA_ADDRESS used as long vToken) + const minLong = LONG_AMOUNT.mul(98).div(100); + const openSwapData = await getManipulatedSwapData( + SHORT_ADDRESS, + DSA_ADDRESS, // LONG = DSA + SHORT_AMOUNT, + LONG_AMOUNT, + LEVERAGE_STRATEGIES_MANAGER_ADDRESS, + ); + + await relativePositionManager.connect(carol).activateAndOpenPosition( + vDSA_ADDRESS, // vLong = vDSA (LONG == DSA) + vSHORT_ADDRESS, + 0, + INITIAL_PRINCIPAL, + leverage, + SHORT_AMOUNT, + minLong, + openSwapData, + ); + + const position = await relativePositionManager.getPosition(carol.address, vDSA_ADDRESS, vSHORT_ADDRESS); + expect(position.isActive).to.eq(true); + const positionAccount = position.positionAccount; + + const shortDebtAfterOpen = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); + const longBalanceAfterOpen = await relativePositionManager.callStatic.getLongCollateralBalance( + carol.address, + vDSA_ADDRESS, + vSHORT_ADDRESS, + ); + + expect(shortDebtAfterOpen).to.be.gt(0, "Should have short debt"); + expect(longBalanceAfterOpen).to.be.gt(0, "Should have long collateral"); + + // Close with profit: 80% of USDC collateral for ETH repay, 20% (~800 USDC) as profit + const longForRepay = longBalanceAfterOpen.mul(80).div(100); + const longForProfit = longBalanceAfterOpen.sub(longForRepay); // ~800 USDC (20% of ~4000) + + const shortRepayAmount = shortDebtAfterOpen.mul(FULL_CLOSE_BUFFER_BPS).div(1000); + + const repaySwapData = await getManipulatedSwapData( + DSA_ADDRESS, // LONG = DSA, swap USDC -> ETH + SHORT_ADDRESS, + longForRepay, + shortRepayAmount, + LEVERAGE_STRATEGIES_MANAGER_ADDRESS, + SHORT_WHALE, + ); + + // Track principal balance before close (handles LONG=DSA internally) + const principalBefore = await relativePositionManager.callStatic.getSuppliedPrincipalBalance( + carol.address, + vDSA_ADDRESS, + vSHORT_ADDRESS, + ); + + // When LONG = DSA, profit swap is "0x" — no swap needed, redeemed long tokens ARE DSA tokens + await relativePositionManager.connect(carol).closeWithProfit( + vDSA_ADDRESS, // vLong = vDSA + vSHORT_ADDRESS, + closeFractionBps, + longForRepay, + shortRepayAmount, + repaySwapData, + longForProfit, + 0, // minAmountOutProfit = 0 (no swap) + "0x", // No swap needed — LONG is already DSA + ); + + // Verify full close + const shortDebtAfterClose = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); + const longBalanceAfterClose = await relativePositionManager.callStatic.getLongCollateralBalance( + carol.address, + vDSA_ADDRESS, + vSHORT_ADDRESS, + ); + + expect(shortDebtAfterClose).to.eq(0, "All debt repaid"); + expect(longBalanceAfterClose).to.eq(0, "All long redeemed"); + + // Principal should increase by ~longForProfit (~800 USDC, no swap needed — redeemed LONG IS DSA) + const principalAfter = await relativePositionManager.callStatic.getSuppliedPrincipalBalance( + carol.address, + vDSA_ADDRESS, + vSHORT_ADDRESS, + ); + const principalIncrease = principalAfter.sub(principalBefore); + expect(principalIncrease).to.be.closeTo( + longForProfit, // ~800 USDC (LONG = DSA, profit IS the redeemed USDC) + ethers.utils.parseEther("1"), // small tolerance for interest accrual + "Principal increase ~= profit (no swap needed)", + ); + }); + + it("deactivate position after full close withdraws principal", async function () { + const [, , , , , , , deactivateUser] = await ethers.getSigners(); + const INITIAL_PRINCIPAL = ethers.utils.parseEther("8000"); + const SHORT_AMOUNT = ethers.utils.parseEther("3"); // 3 ETH + const LONG_AMOUNT = ethers.utils.parseEther("25"); // 25 WBNB + const leverage = ethers.utils.parseEther("1.5"); + const closeFractionBps = 10000; // Full close + + // Fund and open position + const whaleSigner = await initMainnetUser(DSA_WHALE, ethers.utils.parseEther("1")); + await dsa.connect(whaleSigner).transfer(deactivateUser.address, INITIAL_PRINCIPAL); + await dsa.connect(deactivateUser).approve(RELATIVE_POSITION_MANAGER, INITIAL_PRINCIPAL); + + const openSwapData = await getManipulatedSwapData( + SHORT_ADDRESS, + LONG_ADDRESS, + SHORT_AMOUNT, + LONG_AMOUNT, + LEVERAGE_STRATEGIES_MANAGER_ADDRESS, + vLONG_ADDRESS, + ); + + await relativePositionManager + .connect(deactivateUser) + .activateAndOpenPosition( + vLONG_ADDRESS, + vSHORT_ADDRESS, + 0, + INITIAL_PRINCIPAL, + leverage, + SHORT_AMOUNT, + LONG_AMOUNT.mul(98).div(100), + openSwapData, + ); + + const position = await relativePositionManager.getPosition(deactivateUser.address, vLONG_ADDRESS, vSHORT_ADDRESS); + expect(position.isActive).to.eq(true); + const positionAccount = position.positionAccount; + + const shortDebtAfterOpen = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); + const longBalanceAfterOpen = await relativePositionManager.callStatic.getLongCollateralBalance( + deactivateUser.address, + vLONG_ADDRESS, + vSHORT_ADDRESS, + ); + + // Close with profit: 75% WBNB for ETH repay, 25% as profit + const longForRepay = longBalanceAfterOpen.mul(75).div(100); + const longForProfit = longBalanceAfterOpen.sub(longForRepay); // ~6.25 WBNB (25% of ~25) + const shortRepayAmount = shortDebtAfterOpen.mul(FULL_CLOSE_BUFFER_BPS).div(1000); + + const repaySwapData = await getManipulatedSwapData( + LONG_ADDRESS, + SHORT_ADDRESS, + longForRepay, + shortRepayAmount, + LEVERAGE_STRATEGIES_MANAGER_ADDRESS, + ); + + // Profit: ~6.25 WBNB * 500 USDC/WBNB ≈ 3125 USDC + const estimatedProfitDsa = longForProfit.mul(500); + const profitSwapData = await getManipulatedSwapData( + LONG_ADDRESS, + DSA_ADDRESS, + longForProfit, + estimatedProfitDsa, + RELATIVE_POSITION_MANAGER, + ); + + const dsaUnderlyingBeforeClose = await dsaVToken.callStatic.balanceOfUnderlying(positionAccount); + + await relativePositionManager + .connect(deactivateUser) + .closeWithProfit( + vLONG_ADDRESS, + vSHORT_ADDRESS, + closeFractionBps, + longForRepay, + shortRepayAmount, + repaySwapData, + longForProfit, + estimatedProfitDsa.mul(98).div(100), + profitSwapData, + ); + + // Verify full close — position still active + const shortDebtAfterClose = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); + const longBalanceAfterClose = await relativePositionManager.callStatic.getLongCollateralBalance( + deactivateUser.address, + vLONG_ADDRESS, + vSHORT_ADDRESS, + ); + expect(shortDebtAfterClose).to.eq(0, "All debt repaid"); + expect(longBalanceAfterClose).to.eq(0, "All long redeemed"); + + // Verify profit ~= estimated (~3125 USDC) + const dsaUnderlyingAfterClose = await dsaVToken.callStatic.balanceOfUnderlying(positionAccount); + const dsaUnderlyingIncrease = dsaUnderlyingAfterClose.sub(dsaUnderlyingBeforeClose); + expect(dsaUnderlyingIncrease).to.be.closeTo( + estimatedProfitDsa, + ethers.utils.parseEther("1"), // small tolerance for interest accrual + "DSA increase ~= profit from swap", + ); + + const positionBeforeDeactivate = await relativePositionManager.getPosition( + deactivateUser.address, + vLONG_ADDRESS, + vSHORT_ADDRESS, + ); + expect(positionBeforeDeactivate.isActive).to.eq(true, "Position still active after close"); + + // Record DSA balance before deactivation + const dsaBalanceBefore = await dsa.balanceOf(deactivateUser.address); + + // Deactivate: withdraws remaining DSA principal to user + await relativePositionManager.connect(deactivateUser).deactivatePosition(vLONG_ADDRESS, vSHORT_ADDRESS); + + // Verify position is inactive + const positionAfterDeactivate = await relativePositionManager.getPosition( + deactivateUser.address, + vLONG_ADDRESS, + vSHORT_ADDRESS, + ); + expect(positionAfterDeactivate.isActive).to.eq(false, "Position deactivated"); + + // Verify DSA principal withdrawn to user + const dsaBalanceAfter = await dsa.balanceOf(deactivateUser.address); + expect(dsaBalanceAfter).to.be.gt(dsaBalanceBefore, "User should receive DSA principal back"); + }); + + it("liquidate position after price crash", async function () { + const [, , , liquidator] = await ethers.getSigners(); + const INITIAL_PRINCIPAL = ethers.utils.parseEther("1500"); + const SHORT_AMOUNT = ethers.utils.parseEther("1.5"); // 1.5 ETH + const LONG_AMOUNT = ethers.utils.parseEther("30"); // 30 WBNB + const leverage = ethers.utils.parseEther("3"); + + // Fund and open a highly leveraged position + const whaleSigner = await initMainnetUser(DSA_WHALE, ethers.utils.parseEther("1")); + await dsa.connect(whaleSigner).transfer(liquidator.address, INITIAL_PRINCIPAL); + // Use liquidator as the position owner for this test (separate from alice/bob) + await dsa.connect(liquidator).approve(RELATIVE_POSITION_MANAGER, INITIAL_PRINCIPAL); + + const openSwapData = await getManipulatedSwapData( + SHORT_ADDRESS, + LONG_ADDRESS, + SHORT_AMOUNT, + LONG_AMOUNT, + LEVERAGE_STRATEGIES_MANAGER_ADDRESS, + vLONG_ADDRESS, + ); + + await relativePositionManager + .connect(liquidator) + .activateAndOpenPosition( + vLONG_ADDRESS, + vSHORT_ADDRESS, + 0, + INITIAL_PRINCIPAL, + leverage, + SHORT_AMOUNT, + LONG_AMOUNT.mul(98).div(100), + openSwapData, + ); + + const position = await relativePositionManager.getPosition(liquidator.address, vLONG_ADDRESS, vSHORT_ADDRESS); + const positionAccount = position.positionAccount; + const shortDebtAfterOpen = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); + expect(shortDebtAfterOpen).to.be.gt(0, "Should have short debt"); + + // Set liquidator contract (required by Venus Core Pool) + const timelock = await initMainnetUser(bscmainnet.NORMAL_TIMELOCK, ethers.utils.parseEther("1")); + const comptrollerAsTimelock = new ethers.Contract( + bscmainnet.UNITROLLER, + ["function _setLiquidatorContract(address)"], + timelock, + ); + await comptrollerAsTimelock._setLiquidatorContract(alice.address); // alice acts as liquidator + + // Fund alice (liquidator role) with SHORT tokens + const shortToken = new ethers.Contract(SHORT_ADDRESS, ERC20_ABI, ethers.provider); + const shortWhaleSigner = await initMainnetUser(SHORT_WHALE, ethers.utils.parseEther("1")); + await shortToken.connect(shortWhaleSigner).transfer(alice.address, ethers.utils.parseEther("10")); + + // Crash LONG (WBNB) price by 90% to make position liquidatable + const resilientOracleAddr = await comptroller.oracle(); + const oracle = new ethers.Contract( + resilientOracleAddr, + ["function getPrice(address) view returns (uint256)"], + ethers.provider, + ); + const wbnbPrice = await oracle.getPrice(LONG_ADDRESS); + await setOraclePrice(LONG_ADDRESS, wbnbPrice.mul(10).div(100)); // 90% drop + + // Verify position is liquidatable (shortfall > 0) + const comptrollerForLiquidity = new ethers.Contract( + bscmainnet.UNITROLLER, + ["function getAccountLiquidity(address) view returns (uint256, uint256, uint256)"], + ethers.provider, + ); + const [, , shortfall] = await comptrollerForLiquidity.getAccountLiquidity(positionAccount); + expect(shortfall).to.be.gt(0, "Position should be liquidatable"); + + // Liquidate: repay 25% of debt, seize DSA collateral + const repayAmount = shortDebtAfterOpen.div(4); + await shortToken.connect(alice).approve(vSHORT_ADDRESS, repayAmount); + + const liquidatorVTokenBefore = await dsaVToken.callStatic.balanceOf(alice.address); + const positionDsaVTokenBefore = await dsaVToken.callStatic.balanceOf(positionAccount); + + const shortVTokenWithLiquidator = new ethers.Contract(vSHORT_ADDRESS, VTOKEN_ABI, alice); + await shortVTokenWithLiquidator.liquidateBorrow(positionAccount, repayAmount, vDSA_ADDRESS); + + // Verify liquidation: debt reduced by ~repayAmount (25% of original ETH debt) + const shortDebtAfterLiq = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); + const liquidatorVTokenAfter = await dsaVToken.callStatic.balanceOf(alice.address); + const positionDsaVTokenAfter = await dsaVToken.callStatic.balanceOf(positionAccount); + + const expectedRemainingDebt = shortDebtAfterOpen.sub(repayAmount); + expect(shortDebtAfterLiq).to.be.closeTo( + expectedRemainingDebt, + ethers.utils.parseEther("0.01"), // small tolerance for interest accrual + "Debt should decrease by repayAmount (~25% of original)", + ); + + // Verify seized vDSA collateral transferred from position to liquidator + const seizedVTokens = liquidatorVTokenAfter.sub(liquidatorVTokenBefore); + expect(seizedVTokens).to.be.gt(0, "Liquidator received seized vDSA tokens"); + expect(positionDsaVTokenAfter).to.be.lt( + positionDsaVTokenBefore, + "Position DSA collateral decreased after seizure", + ); + + // Seized amount from position should match what liquidator received (minus protocol fee) + const positionVTokenDecrease = positionDsaVTokenBefore.sub(positionDsaVTokenAfter); + expect(positionVTokenDecrease).to.be.gte(seizedVTokens, "Position lost >= what liquidator received"); + }); + }); }); diff --git a/simulations/vip-610/bsctestnet.ts b/simulations/vip-610/bsctestnet.ts index 0353761c0..0ae4cf9eb 100644 --- a/simulations/vip-610/bsctestnet.ts +++ b/simulations/vip-610/bsctestnet.ts @@ -2,7 +2,7 @@ import { expect } from "chai"; import { Contract } from "ethers"; import { ethers } from "hardhat"; import { NETWORK_ADDRESSES } from "src/networkAddresses"; -import { initMainnetUser } from "src/utils"; +import { expectEvents, initMainnetUser } from "src/utils"; import { forking, testVip } from "src/vip-framework"; import { @@ -21,6 +21,13 @@ import SWAP_HELPER_ABI from "./abi/SwapHelperAbi.json"; const { bsctestnet } = NETWORK_ADDRESSES; +const DSA_VTOKENS = [ + "0xD5C4C2e2facBEB59D0216D0595d63FcDc6F9A1a7", // vUSDC + "0xb7526572FFE56AB9D7489838Bf2E18e3323b441A", // vUSDT + "0x08e0A5575De71037aE36AbfAfb516595fE68e5e4", // vBUSD + "0xd9E77847ec815E56ae2B9E69596C69b6972b0B1C", // vWBNB +]; + const ACM_FUNCTION_SIGNATURES = [ "partialPause()", "partialUnpause()", @@ -83,7 +90,19 @@ forking(96276916, async () => { }); }); - testVip("VIP-610 [BNB Chain] Testnet", await vip610Testnet()); + testVip("VIP-610 [BNB Chain] Testnet", await vip610Testnet(), { + callbackAfterExecution: async txResponse => { + await expectEvents(txResponse, [RELATIVE_POSITION_MANAGER_ABI], ["OwnershipTransferred"], [3]); + await expectEvents(txResponse, [FLASHLOAN_FACET_ABI], ["IsAccountFlashLoanWhitelisted"], [1]); + await expectEvents(txResponse, [ACCESS_CONTROL_MANAGER_ABI], ["PermissionGranted"], [36]); + await expectEvents( + txResponse, + [RELATIVE_POSITION_MANAGER_ABI], + ["PositionAccountImplementationSet"], + [1], + ); + }, + }); describe("Post-VIP behavior", () => { it("LeverageStrategiesManager should be whitelisted for flash loans", async () => { @@ -119,13 +138,23 @@ forking(96276916, async () => { expect(await relativePositionManager.POSITION_ACCOUNT_IMPLEMENTATION()).to.equals(POSITION_ACCOUNT); }); + it("Setting Position Account implementation again should revert", async () => { + const signer = await initMainnetUser(bsctestnet.NORMAL_TIMELOCK, ethers.utils.parseEther("1")); + await expect( + relativePositionManager.connect(signer).setPositionAccountImplementation(POSITION_ACCOUNT), + ).to.be.revertedWithCustomError(relativePositionManager, "PositionAccountImplementationLocked"); + }); + + let dsaVTokenIndex = 0; for (const timelockOrGuardian of TIMELOCKS_AND_GUARDIAN) { describe(`ACM-gated functions should be callable by ${timelockOrGuardian}`, () => { let rpmAsCaller: Contract; + let currentDsaIndex: number; before(async () => { const signer = await initMainnetUser(timelockOrGuardian, ethers.utils.parseEther("1")); rpmAsCaller = relativePositionManager.connect(signer); + currentDsaIndex = dsaVTokenIndex++; }); it("partialPause and partialUnpause", async () => { @@ -148,6 +177,21 @@ forking(96276916, async () => { await rpmAsCaller.setProportionalCloseTolerance(newValue); expect(await relativePositionManager.proportionalCloseTolerance()).to.equal(newValue); }); + + it("addDSAVToken and setDSAVTokenActive", async () => { + await rpmAsCaller.addDSAVToken(DSA_VTOKENS[currentDsaIndex]); + expect(await relativePositionManager.dsaVTokens(currentDsaIndex)).to.equal(DSA_VTOKENS[currentDsaIndex]); + expect(await relativePositionManager.isDsaVTokenActive(DSA_VTOKENS[currentDsaIndex])).to.equal(true); + await rpmAsCaller.setDSAVTokenActive(currentDsaIndex, false); + expect(await relativePositionManager.isDsaVTokenActive(DSA_VTOKENS[currentDsaIndex])).to.equal(false); + }); + + it("executePositionAccountCall", async () => { + // Reverts with InvalidCallsLength (not Unauthorized) — proves ACM permission passed + await expect( + rpmAsCaller.executePositionAccountCall(POSITION_ACCOUNT, [], []), + ).to.be.revertedWithCustomError(relativePositionManager, "InvalidCallsLength"); + }); }); } }); diff --git a/vips/vip-610/bsctestnet.ts b/vips/vip-610/bsctestnet.ts index b4eff8400..b3bf8876d 100644 --- a/vips/vip-610/bsctestnet.ts +++ b/vips/vip-610/bsctestnet.ts @@ -6,7 +6,7 @@ const { bsctestnet } = NETWORK_ADDRESSES; export const SWAP_HELPER = "0x3Bf0Eb0663BeCe17d95FE33736762bFD20f488b2"; export const LEVERAGE_STRATEGIES_MANAGER = "0xfc8810B0f1144D5A1F6231aFDb8B51F31c0bc8A7"; export const RELATIVE_POSITION_MANAGER = "0xF01CA5Ad6152d932Ed19FB28b285529399dA8166"; -export const POSITION_ACCOUNT = "0x599B79742AB82700Bc828cc44e0Ae22FBbB88e7c"; +export const POSITION_ACCOUNT = "0x03590ef916d538049Ed15f2690A01F70c2A02954"; export const TIMELOCKS_AND_GUARDIAN = [ bsctestnet.NORMAL_TIMELOCK, From 50779185b2b534152493d3c0d63cf8f5e5709358 Mon Sep 17 00:00:00 2001 From: GitGuru7 Date: Wed, 18 Mar 2026 20:17:29 +0530 Subject: [PATCH 06/13] feat: add addDSAVToken commands for USDC and USDT --- simulations/vip-610/bscmainnet.ts | 59 ++++++++++++++----------------- simulations/vip-610/bsctestnet.ts | 53 ++++++++++++++------------- vips/vip-610/bscmainnet.ts | 14 ++++++++ vips/vip-610/bsctestnet.ts | 14 ++++++++ 4 files changed, 80 insertions(+), 60 deletions(-) diff --git a/simulations/vip-610/bscmainnet.ts b/simulations/vip-610/bscmainnet.ts index 416ec1d64..b301fbe1f 100644 --- a/simulations/vip-610/bscmainnet.ts +++ b/simulations/vip-610/bscmainnet.ts @@ -9,6 +9,8 @@ import { POSITION_ACCOUNT, RELATIVE_POSITION_MANAGER, TIMELOCKS_AND_GUARDIAN, + vUSDC, + vUSDT, vip610, } from "../../vips/vip-610/bscmainnet"; import ACCESS_CONTROL_MANAGER_ABI from "./abi/AccessControlManager.json"; @@ -21,13 +23,6 @@ import SWAP_HELPER_ABI from "./abi/SwapHelperAbi.json"; const { bscmainnet } = NETWORK_ADDRESSES; -const DSA_VTOKENS = [ - "0xecA88125a5ADbe82614ffC12D0DB554E2e2867C8", // vUSDC - "0xfD5840Cd36d94D7229439859C0112a4185BC0255", // vUSDT - "0x95c78222B3D6e262426483D42CfA53685A67Ab9D", // vBUSD - "0x334b3eCB4DCa3593BCCC3c7EBD1A1C1d1780FBF1", // vDAI -]; - const ACM_FUNCTION_SIGNATURES = [ "partialPause()", "partialUnpause()", @@ -59,6 +54,14 @@ forking(87147067, async () => { expect(await relativePositionManager.POSITION_ACCOUNT_IMPLEMENTATION()).to.equal(ethers.constants.AddressZero); }); + it("vUSDC should not be an active DSA vToken", async () => { + expect(await relativePositionManager.isDsaVTokenActive(vUSDC)).to.equal(false); + }); + + it("vUSDT should not be an active DSA vToken", async () => { + expect(await relativePositionManager.isDsaVTokenActive(vUSDT)).to.equal(false); + }); + it("Timelocks/Guardian should not have ACM permissions on RelativePositionManager", async () => { for (const timelockOrGuardian of TIMELOCKS_AND_GUARDIAN) { for (const fnSignature of ACM_FUNCTION_SIGNATURES) { @@ -74,12 +77,8 @@ forking(87147067, async () => { callbackAfterExecution: async txResponse => { await expectEvents(txResponse, [RELATIVE_POSITION_MANAGER_ABI], ["OwnershipTransferred"], [1]); await expectEvents(txResponse, [ACCESS_CONTROL_MANAGER_ABI], ["RoleGranted"], [36]); - await expectEvents( - txResponse, - [RELATIVE_POSITION_MANAGER_ABI], - ["PositionAccountImplementationSet"], - [1], - ); + await expectEvents(txResponse, [RELATIVE_POSITION_MANAGER_ABI], ["PositionAccountImplementationSet"], [1]); + await expectEvents(txResponse, [RELATIVE_POSITION_MANAGER_ABI], ["DSAVTokenAdded"], [2]); }, }); @@ -103,6 +102,16 @@ forking(87147067, async () => { expect(await relativePositionManager.POSITION_ACCOUNT_IMPLEMENTATION()).to.equals(POSITION_ACCOUNT); }); + it("vUSDC should be an active DSA vToken at index 0", async () => { + expect(await relativePositionManager.dsaVTokens(0)).to.equal(vUSDC); + expect(await relativePositionManager.isDsaVTokenActive(vUSDC)).to.equal(true); + }); + + it("vUSDT should be an active DSA vToken at index 1", async () => { + expect(await relativePositionManager.dsaVTokens(1)).to.equal(vUSDT); + expect(await relativePositionManager.isDsaVTokenActive(vUSDT)).to.equal(true); + }); + it("Setting Position Account implementation again should revert", async () => { const signer = await initMainnetUser(bscmainnet.NORMAL_TIMELOCK, ethers.utils.parseEther("1")); await expect( @@ -110,16 +119,13 @@ forking(87147067, async () => { ).to.be.revertedWithCustomError(relativePositionManager, "PositionAccountImplementationLocked"); }); - let dsaVTokenIndex = 0; for (const timelockOrGuardian of TIMELOCKS_AND_GUARDIAN) { describe(`ACM-gated functions should be callable by ${timelockOrGuardian}`, () => { let rpmAsCaller: Contract; - let currentDsaIndex: number; before(async () => { const signer = await initMainnetUser(timelockOrGuardian, ethers.utils.parseEther("1")); rpmAsCaller = relativePositionManager.connect(signer); - currentDsaIndex = dsaVTokenIndex++; }); it("partialPause and partialUnpause", async () => { @@ -143,19 +149,12 @@ forking(87147067, async () => { expect(await relativePositionManager.proportionalCloseTolerance()).to.equal(newValue); }); - it("addDSAVToken and setDSAVTokenActive", async () => { - await rpmAsCaller.addDSAVToken(DSA_VTOKENS[currentDsaIndex]); - expect(await relativePositionManager.dsaVTokens(currentDsaIndex)).to.equal(DSA_VTOKENS[currentDsaIndex]); - expect(await relativePositionManager.isDsaVTokenActive(DSA_VTOKENS[currentDsaIndex])).to.equal(true); - await rpmAsCaller.setDSAVTokenActive(currentDsaIndex, false); - expect(await relativePositionManager.isDsaVTokenActive(DSA_VTOKENS[currentDsaIndex])).to.equal(false); - }); - it("executePositionAccountCall", async () => { // Reverts with InvalidCallsLength (not Unauthorized) — proves ACM permission passed - await expect( - rpmAsCaller.executePositionAccountCall(POSITION_ACCOUNT, [], []), - ).to.be.revertedWithCustomError(relativePositionManager, "InvalidCallsLength"); + await expect(rpmAsCaller.executePositionAccountCall(POSITION_ACCOUNT, [], [])).to.be.revertedWithCustomError( + relativePositionManager, + "InvalidCallsLength", + ); }); }); } @@ -348,12 +347,6 @@ forking(87147067, async () => { // Setup oracle stale periods await setMaxStalePeriod(); - - const timelock = await initMainnetUser(bscmainnet.NORMAL_TIMELOCK, ethers.utils.parseEther("1")); - - // Activate DSA vToken (already added at index 0 by ACM-gated tests, left inactive) - await relativePositionManager.connect(timelock).setDSAVTokenActive(0, true); - comptroller = await ethers.getContractAt(FLASHLOAN_FACET_ABI, bscmainnet.UNITROLLER); // Setup token contracts diff --git a/simulations/vip-610/bsctestnet.ts b/simulations/vip-610/bsctestnet.ts index 0ae4cf9eb..3d41e2e20 100644 --- a/simulations/vip-610/bsctestnet.ts +++ b/simulations/vip-610/bsctestnet.ts @@ -11,6 +11,8 @@ import { RELATIVE_POSITION_MANAGER, SWAP_HELPER, TIMELOCKS_AND_GUARDIAN, + vUSDC, + vUSDT, vip610 as vip610Testnet, } from "../../vips/vip-610/bsctestnet"; import ACCESS_CONTROL_MANAGER_ABI from "./abi/AccessControlManager.json"; @@ -21,13 +23,6 @@ import SWAP_HELPER_ABI from "./abi/SwapHelperAbi.json"; const { bsctestnet } = NETWORK_ADDRESSES; -const DSA_VTOKENS = [ - "0xD5C4C2e2facBEB59D0216D0595d63FcDc6F9A1a7", // vUSDC - "0xb7526572FFE56AB9D7489838Bf2E18e3323b441A", // vUSDT - "0x08e0A5575De71037aE36AbfAfb516595fE68e5e4", // vBUSD - "0xd9E77847ec815E56ae2B9E69596C69b6972b0B1C", // vWBNB -]; - const ACM_FUNCTION_SIGNATURES = [ "partialPause()", "partialUnpause()", @@ -79,6 +74,14 @@ forking(96276916, async () => { expect(await relativePositionManager.POSITION_ACCOUNT_IMPLEMENTATION()).to.equal(ethers.constants.AddressZero); }); + it("vUSDC should not be an active DSA vToken", async () => { + expect(await relativePositionManager.isDsaVTokenActive(vUSDC)).to.equal(false); + }); + + it("vUSDT should not be an active DSA vToken", async () => { + expect(await relativePositionManager.isDsaVTokenActive(vUSDT)).to.equal(false); + }); + it("Timelocks/Guardian should not have ACM permissions on RelativePositionManager", async () => { for (const timelockOrGuardian of TIMELOCKS_AND_GUARDIAN) { for (const fnSignature of ACM_FUNCTION_SIGNATURES) { @@ -95,12 +98,8 @@ forking(96276916, async () => { await expectEvents(txResponse, [RELATIVE_POSITION_MANAGER_ABI], ["OwnershipTransferred"], [3]); await expectEvents(txResponse, [FLASHLOAN_FACET_ABI], ["IsAccountFlashLoanWhitelisted"], [1]); await expectEvents(txResponse, [ACCESS_CONTROL_MANAGER_ABI], ["PermissionGranted"], [36]); - await expectEvents( - txResponse, - [RELATIVE_POSITION_MANAGER_ABI], - ["PositionAccountImplementationSet"], - [1], - ); + await expectEvents(txResponse, [RELATIVE_POSITION_MANAGER_ABI], ["PositionAccountImplementationSet"], [1]); + await expectEvents(txResponse, [RELATIVE_POSITION_MANAGER_ABI], ["DSAVTokenAdded"], [2]); }, }); @@ -138,6 +137,16 @@ forking(96276916, async () => { expect(await relativePositionManager.POSITION_ACCOUNT_IMPLEMENTATION()).to.equals(POSITION_ACCOUNT); }); + it("vUSDC should be an active DSA vToken at index 0", async () => { + expect(await relativePositionManager.dsaVTokens(0)).to.equal(vUSDC); + expect(await relativePositionManager.isDsaVTokenActive(vUSDC)).to.equal(true); + }); + + it("vUSDT should be an active DSA vToken at index 1", async () => { + expect(await relativePositionManager.dsaVTokens(1)).to.equal(vUSDT); + expect(await relativePositionManager.isDsaVTokenActive(vUSDT)).to.equal(true); + }); + it("Setting Position Account implementation again should revert", async () => { const signer = await initMainnetUser(bsctestnet.NORMAL_TIMELOCK, ethers.utils.parseEther("1")); await expect( @@ -145,16 +154,13 @@ forking(96276916, async () => { ).to.be.revertedWithCustomError(relativePositionManager, "PositionAccountImplementationLocked"); }); - let dsaVTokenIndex = 0; for (const timelockOrGuardian of TIMELOCKS_AND_GUARDIAN) { describe(`ACM-gated functions should be callable by ${timelockOrGuardian}`, () => { let rpmAsCaller: Contract; - let currentDsaIndex: number; before(async () => { const signer = await initMainnetUser(timelockOrGuardian, ethers.utils.parseEther("1")); rpmAsCaller = relativePositionManager.connect(signer); - currentDsaIndex = dsaVTokenIndex++; }); it("partialPause and partialUnpause", async () => { @@ -178,19 +184,12 @@ forking(96276916, async () => { expect(await relativePositionManager.proportionalCloseTolerance()).to.equal(newValue); }); - it("addDSAVToken and setDSAVTokenActive", async () => { - await rpmAsCaller.addDSAVToken(DSA_VTOKENS[currentDsaIndex]); - expect(await relativePositionManager.dsaVTokens(currentDsaIndex)).to.equal(DSA_VTOKENS[currentDsaIndex]); - expect(await relativePositionManager.isDsaVTokenActive(DSA_VTOKENS[currentDsaIndex])).to.equal(true); - await rpmAsCaller.setDSAVTokenActive(currentDsaIndex, false); - expect(await relativePositionManager.isDsaVTokenActive(DSA_VTOKENS[currentDsaIndex])).to.equal(false); - }); - it("executePositionAccountCall", async () => { // Reverts with InvalidCallsLength (not Unauthorized) — proves ACM permission passed - await expect( - rpmAsCaller.executePositionAccountCall(POSITION_ACCOUNT, [], []), - ).to.be.revertedWithCustomError(relativePositionManager, "InvalidCallsLength"); + await expect(rpmAsCaller.executePositionAccountCall(POSITION_ACCOUNT, [], [])).to.be.revertedWithCustomError( + relativePositionManager, + "InvalidCallsLength", + ); }); }); } diff --git a/vips/vip-610/bscmainnet.ts b/vips/vip-610/bscmainnet.ts index 03f01bf96..147374c7f 100644 --- a/vips/vip-610/bscmainnet.ts +++ b/vips/vip-610/bscmainnet.ts @@ -6,6 +6,9 @@ const { bscmainnet } = NETWORK_ADDRESSES; export const RELATIVE_POSITION_MANAGER = "0xedcD8725D08585A7B61eE77A22D9cf591C1171c1"; export const POSITION_ACCOUNT = "0x18970e10B39BDf6981334b5DC0873d85CFdB9aa0"; +export const vUSDC = "0xecA88125a5ADbe82614ffC12D0DB554E2e2867C8"; +export const vUSDT = "0xfD5840Cd36d94D7229439859C0112a4185BC0255"; + export const TIMELOCKS_AND_GUARDIAN = [ bscmainnet.NORMAL_TIMELOCK, bscmainnet.FAST_TRACK_TIMELOCK, @@ -52,6 +55,17 @@ export const vip610 = () => { signature: "setPositionAccountImplementation(address)", params: [POSITION_ACCOUNT], }, + // Add DSA vTokens (USDC, USDT) + { + target: RELATIVE_POSITION_MANAGER, + signature: "addDSAVToken(address)", + params: [vUSDC], + }, + { + target: RELATIVE_POSITION_MANAGER, + signature: "addDSAVToken(address)", + params: [vUSDT], + }, ], meta, ProposalType.REGULAR, diff --git a/vips/vip-610/bsctestnet.ts b/vips/vip-610/bsctestnet.ts index b3bf8876d..bf38ec524 100644 --- a/vips/vip-610/bsctestnet.ts +++ b/vips/vip-610/bsctestnet.ts @@ -8,6 +8,9 @@ export const LEVERAGE_STRATEGIES_MANAGER = "0xfc8810B0f1144D5A1F6231aFDb8B51F31c export const RELATIVE_POSITION_MANAGER = "0xF01CA5Ad6152d932Ed19FB28b285529399dA8166"; export const POSITION_ACCOUNT = "0x03590ef916d538049Ed15f2690A01F70c2A02954"; +export const vUSDC = "0xD5C4C2e2facBEB59D0216D0595d63FcDc6F9A1a7"; +export const vUSDT = "0xb7526572FFE56AB9D7489838Bf2E18e3323b441A"; + export const TIMELOCKS_AND_GUARDIAN = [ bsctestnet.NORMAL_TIMELOCK, bsctestnet.FAST_TRACK_TIMELOCK, @@ -70,6 +73,17 @@ export const vip610 = () => { signature: "setPositionAccountImplementation(address)", params: [POSITION_ACCOUNT], }, + // Add DSA vTokens (USDC, USDT) + { + target: RELATIVE_POSITION_MANAGER, + signature: "addDSAVToken(address)", + params: [vUSDC], + }, + { + target: RELATIVE_POSITION_MANAGER, + signature: "addDSAVToken(address)", + params: [vUSDT], + }, ], meta, ProposalType.REGULAR, From a3b458e5bfe84d2f2f7df142d8f2a3c8c649ded9 Mon Sep 17 00:00:00 2001 From: GitGuru7 Date: Wed, 18 Mar 2026 20:24:07 +0530 Subject: [PATCH 07/13] chore: remove unused files --- RPM_VIP_SIMULATION_GUIDE.md | 1071 ----------------------------- simulations/vip-610/bscmainnet.ts | 3 +- 2 files changed, 1 insertion(+), 1073 deletions(-) delete mode 100644 RPM_VIP_SIMULATION_GUIDE.md diff --git a/RPM_VIP_SIMULATION_GUIDE.md b/RPM_VIP_SIMULATION_GUIDE.md deleted file mode 100644 index af3862179..000000000 --- a/RPM_VIP_SIMULATION_GUIDE.md +++ /dev/null @@ -1,1071 +0,0 @@ -# RPM (RelativePositionManager) VIP Simulation Test Guide - -## Purpose - -This guide explains how to write **VIP simulation tests** in the `vips` repo that exercise the **RelativePositionManager (RPM)** end-to-end on a mainnet fork. Unlike the fork tests in `venus-periphery` (which deploy RPM fresh), VIP simulations use **already-deployed contracts** on-chain. - -The goal is to write tests for six core scenarios: - -1. **Close with Profit** (favorable price movement) -2. **Close with Loss** (unfavorable price movement) -3. **Liquidation** (position becomes underwater) -4. **Scale Position** (increase leverage on existing position) -5. **Partial Close** (close a fraction of position, verify remainder) -6. **LONG = DSA Token** (special code path when long token equals DSA token) - ---- - -## Key Differences: venus-periphery Fork Tests vs VIP Simulations - -| Aspect | venus-periphery Fork Tests | VIP Simulation Tests | -| ------------------------- | -------------------------------------------- | ---------------------------------------------------------------------- | -| RPM contract | Deployed fresh via `upgrades.deployProxy()` | Already deployed on mainnet, use address directly | -| ACM permissions | Granted manually in setup | Already configured (or configured by VIP under test) | -| PositionAccount impl | Set in test setup | Already set (or set by VIP) | -| DSA VTokens | Added in test setup | Already configured (or configured by VIP) | -| SwapHelper | Uses mainnet deployed contract | Same - uses mainnet deployed contract | -| LeverageStrategiesManager | Uses mainnet deployed contract | Same - uses mainnet deployed contract | -| Test framework | `loadFixture` + `forking()` from local utils | `forking()` + `testVip()` from `src/vip-framework` | -| Running | `FORKED_NETWORK=bscmainnet npx hardhat test` | `npx hardhat test simulations/vip-XXX/bscmainnet.ts --fork bscmainnet` | - ---- - -## Deployed Contract Addresses (BSC Mainnet) - -```typescript -// RPM System (from venus-periphery deployments) -const RELATIVE_POSITION_MANAGER = "0xedcD8725D08585A7B61eE77A22D9cf591C1171c1"; -const POSITION_ACCOUNT_IMPL = "0x18970e10B39BDf6981334b5DC0873d85CFdB9aa0"; -const SWAP_HELPER = "0xD79be25aEe798Aa34A9Ba1230003d7499be29A24"; -const LEVERAGE_STRATEGIES_MANAGER = "0x03F079E809185a669Ca188676D0ADb09cbAd6dC1"; - -// Venus Core Pool (BSC Mainnet) -const COMPTROLLER = "0xfd36e2c2a6789db23113685031d7f16329158384"; -const ACM = "0x4788629abc6cfca10f9f969efdeaa1cf70c23555"; -const NORMAL_TIMELOCK = "0x939bD8d64c0A9583A7Dcea9933f7b21697ab6396"; - -// Tokens & Markets (example trio: USDC as DSA, WBNB as LONG, ETH as SHORT) -const DSA_ADDRESS = "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d"; // USDC -const vDSA_ADDRESS = "0xecA88125a5ADbe82614ffC12D0DB554E2e2867C8"; // vUSDC -const LONG_ADDRESS = "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c"; // WBNB -const vLONG_ADDRESS = "0x6bCa74586218dB34cdB402295796b79663d816e9"; // vWBNB -const SHORT_ADDRESS = "0x2170Ed0880ac9A755fd29B2688956BD959F933F8"; // ETH -const vSHORT_ADDRESS = "0xf508fCD89b8bd15579dc79A6827cB4686A3592c8"; // vETH - -// Whales for funding -const DSA_WHALE = DSA_ADDRESS; // Token contract itself holds supply -const SHORT_WHALE = "0x8894E0a0c962CB723c1976a4421c95949bE2D4E3"; // Binance 8 (for ETH) - -// Oracle -const CHAINLINK_ORACLE = "0x1B2103441A0A108daD8848D8F5d790e4D402921F"; -``` - ---- - -## File Structure - -``` -vips/ -├── vips/vip-XXX/ -│ └── bscmainnet.ts # VIP definition (if RPM config changes needed) -├── simulations/vip-XXX/ -│ ├── bscmainnet.ts # Main simulation test file -│ └── abi/ -│ ├── RelativePositionManager.json # RPM ABI -│ ├── LeverageStrategiesManager.json # LSM ABI -│ ├── SwapHelper.json # SwapHelper ABI -│ ├── Comptroller.json # Comptroller ABI -│ ├── VBep20.json # vToken ABI -│ ├── ChainlinkOracle.json # For price manipulation -│ └── ResilientOracle.json # For oracle config -``` - -### Getting ABIs - -Export ABIs from venus-periphery compiled artifacts: - -```bash -# From venus-periphery repo -cat artifacts/contracts/RelativePositionManager.sol/RelativePositionManager.json | jq '.abi' > RelativePositionManager.json -cat artifacts/contracts/LeverageStrategiesManager.sol/LeverageStrategiesManager.json | jq '.abi' > LeverageStrategiesManager.json -``` - -Or copy existing ABIs from `vips/simulations/vip-610/abi/`. - ---- - -## Core Helper Functions - -### 1. `setMaxStalePeriod` - Prevent Oracle Staleness Reverts - -On a forked network, oracle prices may be stale. Set max stale period to 1 year for all relevant tokens. - -```typescript -import { ethers } from "hardhat"; -import { initMainnetUser } from "src/utils"; - -const REDSTONE = "0x8455EFA4D7Ff63b8BFD96AdD889483Ea7d39B70a"; -const CHAINLINK = "0x1B2103441A0A108daD8848D8F5d790e4D402921F"; -const BINANCE = "0x594810b741d136f1960141C0d8Fb4a91bE78A820"; -const ONE_YEAR = "31536000"; - -async function setMaxStalePeriod(): Promise { - const timelock = await initMainnetUser(NORMAL_TIMELOCK, ethers.utils.parseEther("2")); - - const chainlinkOracle = new ethers.Contract(CHAINLINK, CHAINLINK_ORACLE_ABI, timelock); - const redStoneOracle = new ethers.Contract(REDSTONE, CHAINLINK_ORACLE_ABI, timelock); - const binanceOracle = new ethers.Contract(BINANCE, BINANCE_ORACLE_ABI, timelock); - - const tokens = [ - { - asset: DSA_ADDRESS, - chainlinkFeed: "0x51597f405303C4377E36123cBc172b13269EA163", - redstoneFeed: "0xeA2511205b959548459A01e358E0A30424dc0B70", - binanceSymbol: "USDC", - }, - { - asset: LONG_ADDRESS, - chainlinkFeed: "0x0567F2323251f0Aab15c8dFb1967E4e8A7D42aeE", - redstoneFeed: "0x8dd2D85C7c28F43F965AE4d9545189C7D022ED0e", - binanceSymbol: "WBNB", - }, - { - asset: SHORT_ADDRESS, - chainlinkFeed: "0xe48a5Fd74d4A5524D76960ef3B52204C0e11fCD1", - redstoneFeed: "0x9cF19D284862A66378c304ACAcB0E857EBc3F856", - binanceSymbol: "ETH", - }, - ]; - - for (const token of tokens) { - await chainlinkOracle.setTokenConfig({ asset: token.asset, feed: token.chainlinkFeed, maxStalePeriod: ONE_YEAR }); - await redStoneOracle.setTokenConfig({ asset: token.asset, feed: token.redstoneFeed, maxStalePeriod: ONE_YEAR }); - await binanceOracle.setMaxStalePeriod(token.binanceSymbol, ONE_YEAR); - } - await binanceOracle.setMaxStalePeriod("BNB", ONE_YEAR); -} -``` - -### 2. `getManipulatedSwapData` - Simulate Specific Swap Outcomes - -Instead of hitting the real Venus swap API (which may fail in test), manipulate swaps to produce exact output amounts. This lets you control profit/loss scenarios precisely. - -```typescript -import { BigNumber, Wallet } from "ethers"; - -let saltCounter = 0; - -async function getManipulatedSwapData( - tokenIn: string, - tokenOut: string, - amountIn: BigNumber, - amountOut: BigNumber, - recipient: string, - tokenOutWhaleOverride?: string, -): Promise { - // Use a known private key for signing (hardhat default account #0) - const swapSignerWallet = new Wallet( - "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", - ethers.provider, - ); - - const swapHelperContract = new ethers.Contract(SWAP_HELPER, SWAP_HELPER_ABI, ethers.provider); - - // Set our wallet as backend signer - const swapHelperOwner = await swapHelperContract.owner(); - const impersonatedOwner = await initMainnetUser(swapHelperOwner, ethers.utils.parseEther("1")); - await swapHelperContract.connect(impersonatedOwner).setBackendSigner(swapSignerWallet.address); - - // Get EIP712 domain - const domain = await swapHelperContract.eip712Domain(); - const network = await ethers.provider.getNetwork(); - const eip712Domain = { - name: domain.name, - version: domain.version, - chainId: network.chainId, - verifyingContract: domain.verifyingContract, - }; - - const TEN_YEARS_SECS = 10 * 365 * 24 * 60 * 60; - const deadline = Math.floor(Date.now() / 1000) + TEN_YEARS_SECS; - - // Fund SwapHelper with tokenOut so it can "swap" - const tokenOutContract = new ethers.Contract(tokenOut, ERC20_ABI, ethers.provider); - const tokenOutWhale = tokenOutWhaleOverride ?? tokenOut; - const whaleSigner = await initMainnetUser(tokenOutWhale, ethers.utils.parseEther("1")); - await tokenOutContract.connect(whaleSigner).transfer(SWAP_HELPER, amountOut); - - // Encode a simple transfer call (fake swap: just transfer tokenOut to recipient) - const tokenOutIface = new ethers.utils.Interface(["function transfer(address to, uint256 amount) returns (bool)"]); - const transferCalldata = tokenOutIface.encodeFunctionData("transfer", [recipient, amountOut]); - - const swapHelperIface = swapHelperContract.interface; - const calls: string[] = []; - calls.push(swapHelperIface.encodeFunctionData("genericCall", [tokenOut, transferCalldata])); - - const salt = ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["uint256"], [++saltCounter])); - - const types = { - Multicall: [ - { name: "caller", type: "address" }, - { name: "calls", type: "bytes[]" }, - { name: "deadline", type: "uint256" }, - { name: "salt", type: "bytes32" }, - ], - }; - - const value = { caller: recipient, calls, deadline, salt }; - const signature = await swapSignerWallet._signTypedData(eip712Domain, types, value); - - return swapHelperIface.encodeFunctionData("multicall", [calls, deadline, salt, signature]); -} -``` - -### 3. `getSwapData` - Real Swap via Venus API (Optional) - -For tests that need real DEX routing (more realistic but slower and may fail if API is down): - -```typescript -async function getSwapData( - tokenIn: string, - tokenOut: string, - exactAmountInMantissa: string, - recipient: string, - slippagePercentage: string, -): Promise<{ swapData: string; minAmountOut: BigNumber }> { - // Same as venus-periphery fork test - calls https://api.venus.io/find-swap - // Signs multicall with EIP712 - // Returns encoded multicall data + minAmountOut - // See venus-periphery/tests/hardhat/Fork/RelativePositionManager.ts lines 85-176 -} -``` - -### 4. `setOraclePrice` - Manipulate Token Prices (For Liquidation) - -```typescript -async function setOraclePrice(comptroller: Contract, asset: string, price: BigNumber): Promise { - const timelock = await initMainnetUser(NORMAL_TIMELOCK, ethers.utils.parseEther("1")); - const resilientOracleAddr = await comptroller.oracle(); - - const resilientOracle = new ethers.Contract( - resilientOracleAddr, - [ - "function setTokenConfig((address asset, address[3] oracles, bool[3] enableFlagsForOracles, bool cachingEnabled))", - ], - timelock, - ); - - // Point oracle to only use Chainlink (so we can manipulate via setDirectPrice) - await resilientOracle.setTokenConfig({ - asset, - oracles: [CHAINLINK_ORACLE, ethers.constants.AddressZero, ethers.constants.AddressZero], - enableFlagsForOracles: [true, false, false], - cachingEnabled: false, - }); - - const chainlinkOracle = new ethers.Contract(CHAINLINK_ORACLE, CHAINLINK_ORACLE_ABI, timelock); - await chainlinkOracle.setDirectPrice(asset, price); -} -``` - ---- - -## Test Scenarios - -### Scenario 1: Close with Profit - -**Flow**: Open position -> Price moves favorably -> Close with profit -> Validate - -```typescript -it("open position and close with profit", async () => { - const rpm = new ethers.Contract(RELATIVE_POSITION_MANAGER, RPM_ABI, ethers.provider); - const leverageManager = new ethers.Contract(LEVERAGE_STRATEGIES_MANAGER, LSM_ABI, ethers.provider); - const [, alice] = await ethers.getSigners(); - - const INITIAL_PRINCIPAL = ethers.utils.parseEther("9000"); - const SHORT_AMOUNT = ethers.utils.parseEther("4"); - const leverage = ethers.utils.parseEther("1.5"); - const closeFractionBps = 10000; // Full close - - // --- STEP 1: Fund Alice with DSA (USDC) --- - const whaleSigner = await initMainnetUser(DSA_WHALE, ethers.utils.parseEther("1")); - const dsa = new ethers.Contract(DSA_ADDRESS, ERC20_ABI, whaleSigner); - await dsa.transfer(alice.address, INITIAL_PRINCIPAL); - await dsa.connect(alice).approve(RELATIVE_POSITION_MANAGER, INITIAL_PRINCIPAL); - - // --- STEP 2: Activate + Open Position --- - // Use manipulated swap: SHORT_AMOUNT ETH -> some WBNB amount - const longAmount = ethers.utils.parseEther("30"); // Expected WBNB from swap - const minLong = longAmount.mul(98).div(100); - - const openSwapData = await getManipulatedSwapData( - SHORT_ADDRESS, // tokenIn (ETH) - LONG_ADDRESS, // tokenOut (WBNB) - SHORT_AMOUNT, // amountIn - longAmount, // amountOut - leverageManager.address, - vLONG_ADDRESS, // whale override (vToken holds underlying) - ); - - await rpm.connect(alice).activateAndOpenPosition( - vLONG_ADDRESS, // longVToken - vSHORT_ADDRESS, // shortVToken - 0, // dsaIndex - INITIAL_PRINCIPAL, // initialPrincipal (must be > 0) - leverage, // effectiveLeverage - SHORT_AMOUNT, // shortAmount - minLong, // minLongAmount - openSwapData, // swapData - ); - - // --- STEP 3: Record post-open state --- - const position = await rpm.getPosition(alice.address, vLONG_ADDRESS, vSHORT_ADDRESS); - const positionAccount = position.positionAccount; - expect(position.isActive).to.eq(true); - - const shortVToken = new ethers.Contract(vSHORT_ADDRESS, VTOKEN_ABI, ethers.provider); - const shortDebtAfterOpen = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); - const longBalanceAfterOpen = await rpm.callStatic.getLongCollateralBalance( - alice.address, - vLONG_ADDRESS, - vSHORT_ADDRESS, - ); - - // --- STEP 4: Close with Profit --- - // Simulate favorable price: use 75% of long to repay all debt, 25% is profit - const longForRepay = longBalanceAfterOpen.mul(75).div(100); - const longForProfit = longBalanceAfterOpen.sub(longForRepay); - - // Buffer for full close (2.2% = 2% contract tolerance + 0.2% interest) - const shortRepayAmount = shortDebtAfterOpen.mul(1022).div(1000); - - const repaySwapData = await getManipulatedSwapData( - LONG_ADDRESS, - SHORT_ADDRESS, - longForRepay, - shortRepayAmount, - leverageManager.address, - ); - - // Profit: LONG -> DSA (goes to RPM, then supplied as principal) - const estimatedProfitDsa = longForProfit.mul(500); // ~500 USDC per WBNB rough estimate - const profitSwapData = await getManipulatedSwapData( - LONG_ADDRESS, - DSA_ADDRESS, - longForProfit, - estimatedProfitDsa, - rpm.address, // RPM receives profit, supplies as principal - ); - - await rpm.connect(alice).closeWithProfit( - vLONG_ADDRESS, - vSHORT_ADDRESS, - closeFractionBps, - longForRepay, // longAmountToRedeemForRepay - shortRepayAmount, // minAmountOutRepay - repaySwapData, - longForProfit, // longAmountToRedeemForProfit - estimatedProfitDsa.mul(98).div(100), // minAmountOutProfit - profitSwapData, - ); - - // --- VALIDATION --- - const shortDebtAfterClose = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); - const longBalanceAfterClose = await rpm.callStatic.getLongCollateralBalance( - alice.address, - vLONG_ADDRESS, - vSHORT_ADDRESS, - ); - - expect(shortDebtAfterClose).to.eq(0, "All debt repaid"); - expect(longBalanceAfterClose).to.eq(0, "All long redeemed"); -}); -``` - -### Scenario 2: Close with Loss - -**Flow**: Open position -> Price moves unfavorably -> Close with loss (use DSA to cover shortfall) -> Validate - -```typescript -it("open position and close with loss", async () => { - const rpm = new ethers.Contract(RELATIVE_POSITION_MANAGER, RPM_ABI, ethers.provider); - const leverageManager = new ethers.Contract(LEVERAGE_STRATEGIES_MANAGER, LSM_ABI, ethers.provider); - const [, alice] = await ethers.getSigners(); - - const INITIAL_PRINCIPAL = ethers.utils.parseEther("15000"); - const SHORT_AMOUNT = ethers.utils.parseEther("3"); - const leverage = ethers.utils.parseEther("3"); - const closeFractionBps = 10000; // Full close - - // --- STEP 1: Fund + Open (same pattern as profit test) --- - const whaleSigner = await initMainnetUser(DSA_WHALE, ethers.utils.parseEther("1")); - const dsa = new ethers.Contract(DSA_ADDRESS, ERC20_ABI, whaleSigner); - await dsa.transfer(alice.address, INITIAL_PRINCIPAL); - await dsa.connect(alice).approve(RELATIVE_POSITION_MANAGER, INITIAL_PRINCIPAL); - - const longAmount = ethers.utils.parseEther("25"); - const openSwapData = await getManipulatedSwapData( - SHORT_ADDRESS, - LONG_ADDRESS, - SHORT_AMOUNT, - longAmount, - leverageManager.address, - vLONG_ADDRESS, - ); - - await rpm - .connect(alice) - .activateAndOpenPosition( - vLONG_ADDRESS, - vSHORT_ADDRESS, - 0, - INITIAL_PRINCIPAL, - leverage, - SHORT_AMOUNT, - longAmount.mul(98).div(100), - openSwapData, - ); - - const position = await rpm.getPosition(alice.address, vLONG_ADDRESS, vSHORT_ADDRESS); - const positionAccount = position.positionAccount; - - const shortVToken = new ethers.Contract(vSHORT_ADDRESS, VTOKEN_ABI, ethers.provider); - const shortDebtAfterOpen = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); - const longBalanceAfterOpen = await rpm.callStatic.getLongCollateralBalance( - alice.address, - vLONG_ADDRESS, - vSHORT_ADDRESS, - ); - - // --- STEP 2: Close with Loss --- - // Simulate 20% loss: swapping all LONG only gets 80% of SHORT needed - const shortAmountFromLongSwap = shortDebtAfterOpen.mul(80).div(100); - const shortfall = shortDebtAfterOpen.sub(shortAmountFromLongSwap); - - // First swap: LONG -> SHORT (with 20% loss) - const firstSwapData = await getManipulatedSwapData( - LONG_ADDRESS, - SHORT_ADDRESS, - longBalanceAfterOpen, - shortAmountFromLongSwap, - leverageManager.address, - ); - - // Second swap: DSA -> SHORT (to cover shortfall from principal) - const dsaAmountToSwap = INITIAL_PRINCIPAL.mul(25).div(100); - // Buffer for full close: 2.2% - const shortFromDsaSwap = shortfall.mul(1022).div(1000); - - const secondSwapData = await getManipulatedSwapData( - DSA_ADDRESS, - SHORT_ADDRESS, - dsaAmountToSwap, - shortFromDsaSwap, - leverageManager.address, - ); - - await rpm.connect(alice).closeWithLoss( - vLONG_ADDRESS, - vSHORT_ADDRESS, - closeFractionBps, - longBalanceAfterOpen, // longAmountToRedeemForFirstSwap - shortAmountFromLongSwap, // shortAmountToRepayForFirstSwap - shortAmountFromLongSwap, // minAmountOutFirstSwap - firstSwapData, - dsaAmountToSwap, // dsaAmountToRedeemForSecondSwap - shortFromDsaSwap, // minAmountOutSecondSwap - secondSwapData, - ); - - // --- VALIDATION --- - const shortDebtAfterClose = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); - const longBalanceAfterClose = await rpm.callStatic.getLongCollateralBalance( - alice.address, - vLONG_ADDRESS, - vSHORT_ADDRESS, - ); - - expect(shortDebtAfterClose).to.eq(0, "All debt repaid"); - expect(longBalanceAfterClose).to.eq(0, "All long redeemed"); - - // Optional: deactivate and verify principal withdrawal - await rpm.connect(alice).deactivatePosition(vLONG_ADDRESS, vSHORT_ADDRESS); - const positionAfter = await rpm.getPosition(alice.address, vLONG_ADDRESS, vSHORT_ADDRESS); - expect(positionAfter.isActive).to.eq(false); -}); -``` - -### Scenario 3: Liquidation - -**Flow**: Open position -> Crash LONG price via oracle manipulation -> External liquidator calls `liquidateBorrow` -> Validate seized collateral - -```typescript -it("liquidate position after price crash", async () => { - const rpm = new ethers.Contract(RELATIVE_POSITION_MANAGER, RPM_ABI, ethers.provider); - const leverageManager = new ethers.Contract(LEVERAGE_STRATEGIES_MANAGER, LSM_ABI, ethers.provider); - const comptroller = new ethers.Contract(COMPTROLLER, COMPTROLLER_ABI, ethers.provider); - const [, alice, liquidator] = await ethers.getSigners(); - - // --- STEP 1: Open a highly leveraged position --- - const INITIAL_PRINCIPAL = ethers.utils.parseEther("1500"); - const SHORT_AMOUNT = ethers.utils.parseEther("1.5"); - const leverage = ethers.utils.parseEther("3"); - - const whaleSigner = await initMainnetUser(DSA_WHALE, ethers.utils.parseEther("1")); - const dsa = new ethers.Contract(DSA_ADDRESS, ERC20_ABI, whaleSigner); - await dsa.transfer(alice.address, INITIAL_PRINCIPAL); - await dsa.connect(alice).approve(RELATIVE_POSITION_MANAGER, INITIAL_PRINCIPAL); - - const longAmount = ethers.utils.parseEther("30"); - const openSwapData = await getManipulatedSwapData( - SHORT_ADDRESS, - LONG_ADDRESS, - SHORT_AMOUNT, - longAmount, - leverageManager.address, - vLONG_ADDRESS, - ); - - await rpm - .connect(alice) - .activateAndOpenPosition( - vLONG_ADDRESS, - vSHORT_ADDRESS, - 0, - INITIAL_PRINCIPAL, - leverage, - SHORT_AMOUNT, - longAmount.mul(98).div(100), - openSwapData, - ); - - const position = await rpm.getPosition(alice.address, vLONG_ADDRESS, vSHORT_ADDRESS); - const positionAccount = position.positionAccount; - - const shortVToken = new ethers.Contract(vSHORT_ADDRESS, VTOKEN_ABI, ethers.provider); - const shortDebtAfterOpen = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); - - // --- STEP 2: Set liquidator contract (required by Venus Core Pool) --- - const timelock = await initMainnetUser(NORMAL_TIMELOCK, ethers.utils.parseEther("1")); - await comptroller.connect(timelock)._setLiquidatorContract(liquidator.address); - - // --- STEP 3: Fund liquidator with SHORT tokens --- - const shortToken = new ethers.Contract(SHORT_ADDRESS, ERC20_ABI, ethers.provider); - const shortWhaleSigner = await initMainnetUser(SHORT_WHALE, ethers.utils.parseEther("1")); - await shortToken.connect(shortWhaleSigner).transfer(liquidator.address, ethers.utils.parseEther("10")); - - // --- STEP 4: Crash LONG (WBNB) price by 90% to make position liquidatable --- - const oracleAddr = await comptroller.oracle(); - const oracle = new ethers.Contract( - oracleAddr, - ["function getPrice(address) view returns (uint256)"], - ethers.provider, - ); - const wbnbPrice = await oracle.getPrice(LONG_ADDRESS); - await setOraclePrice(comptroller, LONG_ADDRESS, wbnbPrice.mul(10).div(100)); // 90% drop - - // Verify position is liquidatable - const [, , shortfall] = await comptroller.getAccountLiquidity(positionAccount); - expect(shortfall).to.be.gt(0, "Position should be liquidatable"); - - // --- STEP 5: Liquidate --- - const repayAmount = shortDebtAfterOpen.div(4); // Liquidate 25% of debt - await shortToken.connect(liquidator).approve(vSHORT_ADDRESS, repayAmount); - - const dsaVToken = new ethers.Contract(vDSA_ADDRESS, VTOKEN_ABI, ethers.provider); - const liquidatorVTokenBefore = await dsaVToken.balanceOf(liquidator.address); - - const shortVTokenAsLiquidator = new ethers.Contract(vSHORT_ADDRESS, VBEP20_ABI, liquidator); - await shortVTokenAsLiquidator.liquidateBorrow( - positionAccount, - repayAmount, - vDSA_ADDRESS, // Seize DSA collateral (or use vLONG_ADDRESS to seize LONG) - ); - - // --- VALIDATION --- - const shortDebtAfterLiq = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); - const liquidatorVTokenAfter = await dsaVToken.balanceOf(liquidator.address); - - expect(shortDebtAfterLiq).to.be.lt(shortDebtAfterOpen, "Debt should decrease"); - expect(liquidatorVTokenAfter).to.be.gt(liquidatorVTokenBefore, "Liquidator received seized vTokens"); -}); -``` - -### Scenario 4: Scale Position (Increase Leverage) - -**Flow**: Open position -> Scale up (add more leverage) -> Validate increased debt and long balance - -```typescript -it("open position and scale up", async () => { - const rpm = new ethers.Contract(RELATIVE_POSITION_MANAGER, RPM_ABI, ethers.provider); - const leverageManager = new ethers.Contract(LEVERAGE_STRATEGIES_MANAGER, LSM_ABI, ethers.provider); - const [, alice] = await ethers.getSigners(); - - const INITIAL_PRINCIPAL = ethers.utils.parseEther("10000"); - const SHORT_AMOUNT = ethers.utils.parseEther("3"); - const leverage = ethers.utils.parseEther("2"); - - // --- STEP 1: Fund + Open initial position --- - const whaleSigner = await initMainnetUser(DSA_WHALE, ethers.utils.parseEther("1")); - const dsa = new ethers.Contract(DSA_ADDRESS, ERC20_ABI, whaleSigner); - await dsa.transfer(alice.address, INITIAL_PRINCIPAL.mul(2)); // Extra for scaling - await dsa.connect(alice).approve(RELATIVE_POSITION_MANAGER, INITIAL_PRINCIPAL.mul(2)); - - const longAmount = ethers.utils.parseEther("25"); - const openSwapData = await getManipulatedSwapData( - SHORT_ADDRESS, - LONG_ADDRESS, - SHORT_AMOUNT, - longAmount, - leverageManager.address, - vLONG_ADDRESS, - ); - - await rpm - .connect(alice) - .activateAndOpenPosition( - vLONG_ADDRESS, - vSHORT_ADDRESS, - 0, - INITIAL_PRINCIPAL, - leverage, - SHORT_AMOUNT, - longAmount.mul(98).div(100), - openSwapData, - ); - - const position = await rpm.getPosition(alice.address, vLONG_ADDRESS, vSHORT_ADDRESS); - const positionAccount = position.positionAccount; - - const shortVToken = new ethers.Contract(vSHORT_ADDRESS, VTOKEN_ABI, ethers.provider); - const shortDebtAfterOpen = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); - const longBalanceAfterOpen = await rpm.callStatic.getLongCollateralBalance( - alice.address, - vLONG_ADDRESS, - vSHORT_ADDRESS, - ); - - // --- STEP 2: Scale position (borrow more SHORT, swap to more LONG) --- - const ADDITIONAL_PRINCIPAL = ethers.utils.parseEther("5000"); - const SCALE_SHORT_AMOUNT = ethers.utils.parseEther("2"); - const scaleLongAmount = ethers.utils.parseEther("18"); - - const scaleSwapData = await getManipulatedSwapData( - SHORT_ADDRESS, - LONG_ADDRESS, - SCALE_SHORT_AMOUNT, - scaleLongAmount, - leverageManager.address, - vLONG_ADDRESS, - ); - - await rpm.connect(alice).scalePosition( - vLONG_ADDRESS, // longVToken - vSHORT_ADDRESS, // shortVToken - ADDITIONAL_PRINCIPAL, // additionalPrincipal (can be 0 if not adding more) - SCALE_SHORT_AMOUNT, // shortAmount - scaleLongAmount.mul(98).div(100), // minLongAmount - scaleSwapData, // swapData - ); - - // --- VALIDATION --- - const shortDebtAfterScale = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); - const longBalanceAfterScale = await rpm.callStatic.getLongCollateralBalance( - alice.address, - vLONG_ADDRESS, - vSHORT_ADDRESS, - ); - - // Debt should increase by approximately SCALE_SHORT_AMOUNT - const expectedTotalDebt = shortDebtAfterOpen.add(SCALE_SHORT_AMOUNT); - const debtTolerance = expectedTotalDebt.mul(1).div(100); // 1% tolerance - expect(shortDebtAfterScale).to.be.closeTo(expectedTotalDebt, debtTolerance); - - // Long balance should increase by approximately scaleLongAmount - const expectedTotalLong = longBalanceAfterOpen.add(scaleLongAmount); - const longTolerance = expectedTotalLong.mul(2).div(100); // 2% tolerance - expect(longBalanceAfterScale).to.be.closeTo(expectedTotalLong, longTolerance); - - // Position should still be active - const positionAfterScale = await rpm.getPosition(alice.address, vLONG_ADDRESS, vSHORT_ADDRESS); - expect(positionAfterScale.isActive).to.eq(true); -}); -``` - -### Scenario 5: Partial Close (Profit or Loss) - -**Flow**: Open position -> Partial close (e.g., 30-50%) -> Validate remaining balances are proportional - -```typescript -it("open position and partial close with profit (50%)", async () => { - const rpm = new ethers.Contract(RELATIVE_POSITION_MANAGER, RPM_ABI, ethers.provider); - const leverageManager = new ethers.Contract(LEVERAGE_STRATEGIES_MANAGER, LSM_ABI, ethers.provider); - const [, alice] = await ethers.getSigners(); - - const INITIAL_PRINCIPAL = ethers.utils.parseEther("9000"); - const SHORT_AMOUNT = ethers.utils.parseEther("4"); - const leverage = ethers.utils.parseEther("1.5"); - const closeFractionBps = 5000; // 50% partial close - - // --- STEP 1: Fund + Open (same as full close) --- - const whaleSigner = await initMainnetUser(DSA_WHALE, ethers.utils.parseEther("1")); - const dsa = new ethers.Contract(DSA_ADDRESS, ERC20_ABI, whaleSigner); - await dsa.transfer(alice.address, INITIAL_PRINCIPAL); - await dsa.connect(alice).approve(RELATIVE_POSITION_MANAGER, INITIAL_PRINCIPAL); - - const longAmount = ethers.utils.parseEther("30"); - const openSwapData = await getManipulatedSwapData( - SHORT_ADDRESS, - LONG_ADDRESS, - SHORT_AMOUNT, - longAmount, - leverageManager.address, - vLONG_ADDRESS, - ); - - await rpm - .connect(alice) - .activateAndOpenPosition( - vLONG_ADDRESS, - vSHORT_ADDRESS, - 0, - INITIAL_PRINCIPAL, - leverage, - SHORT_AMOUNT, - longAmount.mul(98).div(100), - openSwapData, - ); - - const position = await rpm.getPosition(alice.address, vLONG_ADDRESS, vSHORT_ADDRESS); - const positionAccount = position.positionAccount; - - const shortVToken = new ethers.Contract(vSHORT_ADDRESS, VTOKEN_ABI, ethers.provider); - const shortDebtAfterOpen = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); - const longBalanceAfterOpen = await rpm.callStatic.getLongCollateralBalance( - alice.address, - vLONG_ADDRESS, - vSHORT_ADDRESS, - ); - - // --- STEP 2: Partial Close with Profit (50%) --- - // Proportional amounts based on close fraction - const expectedLongToRedeem = longBalanceAfterOpen.mul(closeFractionBps).div(10000); - const expectedShortToRepay = shortDebtAfterOpen.mul(closeFractionBps).div(10000); - - // Favorable price: 80% of redeemed long covers repay, 20% is profit - const longForRepay = expectedLongToRedeem.mul(80).div(100); - const longForProfit = expectedLongToRedeem.sub(longForRepay); - - // 0.2% buffer for partial close (no 2% contract tolerance like full close) - const shortRepayAmount = expectedShortToRepay.mul(1002).div(1000); - - const repaySwapData = await getManipulatedSwapData( - LONG_ADDRESS, - SHORT_ADDRESS, - longForRepay, - shortRepayAmount, - leverageManager.address, - ); - - const estimatedProfitDsa = longForProfit.mul(500); - const profitSwapData = await getManipulatedSwapData( - LONG_ADDRESS, - DSA_ADDRESS, - longForProfit, - estimatedProfitDsa, - rpm.address, - ); - - await rpm - .connect(alice) - .closeWithProfit( - vLONG_ADDRESS, - vSHORT_ADDRESS, - closeFractionBps, - longForRepay, - shortRepayAmount, - repaySwapData, - longForProfit, - estimatedProfitDsa.mul(98).div(100), - profitSwapData, - ); - - // --- VALIDATION --- - const shortDebtAfterClose = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); - const longBalanceAfterClose = await rpm.callStatic.getLongCollateralBalance( - alice.address, - vLONG_ADDRESS, - vSHORT_ADDRESS, - ); - - // Remaining balances should be ~50% of initial (within 2% tolerance) - const expectedRemainingDebt = shortDebtAfterOpen.mul(10000 - closeFractionBps).div(10000); - const expectedRemainingLong = longBalanceAfterOpen.mul(10000 - closeFractionBps).div(10000); - - const debtTolerance = expectedRemainingDebt.mul(2).div(100); - expect(shortDebtAfterClose).to.be.closeTo(expectedRemainingDebt, debtTolerance); - - const longTolerance = expectedRemainingLong.mul(2).div(100); - expect(longBalanceAfterClose).to.be.closeTo(expectedRemainingLong, longTolerance); - - // Position should STILL be active (partial close, not full) - expect(shortDebtAfterClose).to.be.gt(0, "Debt should remain after partial close"); - expect(longBalanceAfterClose).to.be.gt(0, "Long should remain after partial close"); - - const positionAfterClose = await rpm.getPosition(alice.address, vLONG_ADDRESS, vSHORT_ADDRESS); - expect(positionAfterClose.isActive).to.eq(true, "Position stays active after partial close"); -}); -``` - -### Scenario 6: LONG = DSA Token (Special Code Path) - -When the long token is the same as the DSA token (e.g., both USDC), the contract takes different paths: - -- **On close with profit**: no profit swap needed (`"0x"` swap data) - redeemed long IS DSA, supplied directly as principal -- **On close with loss**: second swap redeems from DSA principal (same market as long) -- **On open**: swap output token is DSA, deposited into DSA market (which is also long market) - -```typescript -it("close with profit when LONG = DSA (no profit swap needed)", async () => { - const rpm = new ethers.Contract(RELATIVE_POSITION_MANAGER, RPM_ABI, ethers.provider); - const leverageManager = new ethers.Contract(LEVERAGE_STRATEGIES_MANAGER, LSM_ABI, ethers.provider); - const [, alice] = await ethers.getSigners(); - - // LONG = DSA = USDC (vDSA market used for both) - const INITIAL_PRINCIPAL = ethers.utils.parseEther("10000"); - const SHORT_AMOUNT = ethers.utils.parseEther("1"); - const leverage = ethers.utils.parseEther("2"); - const closeFractionBps = 10000; // Full close - - // --- STEP 1: Fund + Open (LONG = DSA) --- - const whaleSigner = await initMainnetUser(DSA_WHALE, ethers.utils.parseEther("1")); - const dsa = new ethers.Contract(DSA_ADDRESS, ERC20_ABI, whaleSigner); - await dsa.transfer(alice.address, INITIAL_PRINCIPAL); - await dsa.connect(alice).approve(RELATIVE_POSITION_MANAGER, INITIAL_PRINCIPAL); - - // Swap: SHORT (ETH) -> LONG (USDC, which is DSA) - const longAmount = ethers.utils.parseEther("4000"); // ~4000 USDC from 1 ETH - const openSwapData = await getManipulatedSwapData( - SHORT_ADDRESS, - DSA_ADDRESS, - SHORT_AMOUNT, - longAmount, - leverageManager.address, - vDSA_ADDRESS, // whale: vToken holds underlying USDC - ); - - await rpm.connect(alice).activateAndOpenPosition( - vDSA_ADDRESS, // longVToken = dsaVToken (LONG = DSA) - vSHORT_ADDRESS, - 0, - INITIAL_PRINCIPAL, - leverage, - SHORT_AMOUNT, - longAmount.mul(98).div(100), - openSwapData, - ); - - const position = await rpm.getPosition(alice.address, vDSA_ADDRESS, vSHORT_ADDRESS); - const positionAccount = position.positionAccount; - - const shortVToken = new ethers.Contract(vSHORT_ADDRESS, VTOKEN_ABI, ethers.provider); - const dsaVToken = new ethers.Contract(vDSA_ADDRESS, VTOKEN_ABI, ethers.provider); - const shortDebtAfterOpen = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); - const longBalanceAfterOpen = await rpm.callStatic.getLongCollateralBalance( - alice.address, - vDSA_ADDRESS, - vSHORT_ADDRESS, - ); - - // Accrue interest for accurate balance reads - await dsaVToken.connect(alice).accrueInterest(); - await shortVToken.connect(alice).accrueInterest(); - - // --- STEP 2: Close with Profit (LONG = DSA) --- - // 80% of long for repay, 20% as profit (no swap needed - it's already DSA) - const longForRepay = longBalanceAfterOpen.mul(80).div(100); - const longForProfit = longBalanceAfterOpen.sub(longForRepay); - - // 2.2% buffer for full close - const shortRepayAmount = shortDebtAfterOpen.mul(1022).div(1000); - - // First swap: LONG (USDC) -> SHORT (ETH) for repay - const repaySwapData = await getManipulatedSwapData( - DSA_ADDRESS, - SHORT_ADDRESS, - longForRepay, - shortRepayAmount, - leverageManager.address, - vSHORT_ADDRESS, // whale for SHORT - ); - - // KEY DIFFERENCE: When LONG = DSA, profit swap is "0x" (no swap needed) - // The redeemed long IS DSA, so it's supplied directly as principal - const profitSwapData = "0x"; - const estimatedProfitDsa = longForProfit; // 1:1, no conversion needed - - const suppliedPrincipalBefore = await rpm.callStatic.getSuppliedPrincipalBalance( - alice.address, - vDSA_ADDRESS, - vSHORT_ADDRESS, - ); - - await rpm.connect(alice).closeWithProfit( - vDSA_ADDRESS, - vSHORT_ADDRESS, - closeFractionBps, - longForRepay, - shortRepayAmount, - repaySwapData, - longForProfit, - estimatedProfitDsa, // minAmountOutProfit (exact, no swap) - profitSwapData, // "0x" - no swap, long is already DSA - ); - - // --- VALIDATION --- - const shortDebtAfterClose = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); - const longBalanceAfterClose = await rpm.callStatic.getLongCollateralBalance( - alice.address, - vDSA_ADDRESS, - vSHORT_ADDRESS, - ); - const suppliedPrincipalAfter = await rpm.callStatic.getSuppliedPrincipalBalance( - alice.address, - vDSA_ADDRESS, - vSHORT_ADDRESS, - ); - - expect(shortDebtAfterClose).to.eq(0, "All debt repaid"); - expect(longBalanceAfterClose).to.eq(0, "All long redeemed"); - - // Profit (redeemed long) should be added to supplied principal - const principalIncrease = suppliedPrincipalAfter.sub(suppliedPrincipalBefore); - const profitTolerance = longForProfit.mul(1).div(10000); // 0.01% - expect(principalIncrease).to.be.closeTo( - longForProfit, - profitTolerance, - "Profit added to principal (no swap, long IS DSA)", - ); - - // Deactivate to withdraw remaining principal - await rpm.connect(alice).deactivatePosition(vDSA_ADDRESS, vSHORT_ADDRESS); - const positionAfter = await rpm.getPosition(alice.address, vDSA_ADDRESS, vSHORT_ADDRESS); - expect(positionAfter.isActive).to.eq(false); -}); -``` - -**Key differences when LONG = DSA:** - -- `longVToken` passed to RPM functions is `vDSA_ADDRESS` (same market for both long and DSA) -- Profit swap data is `"0x"` (redeemed long is already DSA, no conversion needed) -- `estimatedProfitDsa = longForProfit` (1:1, no swap slippage) -- On close with loss, second swap redeems DSA from the same market as long collateral -- `suppliedPrincipalBalance` tracks only the DSA principal portion (separate from long collateral in the same market) - ---- - -## Complete Simulation File Template - -```typescript -// simulations/vip-XXX/bscmainnet.ts -import { expect } from "chai"; -import { BigNumber, Contract, Wallet } from "ethers"; -import { ethers } from "hardhat"; -import { NETWORK_ADDRESSES } from "src/networkAddresses"; -import { initMainnetUser } from "src/utils"; -import { forking, testVip } from "src/vip-framework"; - -import { vipXXX } from "../../vips/vip-XXX/bscmainnet"; -import CHAINLINK_ORACLE_ABI from "./abi/ChainlinkOracle.json"; -import COMPTROLLER_ABI from "./abi/Comptroller.json"; -import ERC20_ABI from "./abi/ERC20.json"; -import LSM_ABI from "./abi/LeverageStrategiesManager.json"; -import RPM_ABI from "./abi/RelativePositionManager.json"; -import SWAP_HELPER_ABI from "./abi/SwapHelper.json"; -import VTOKEN_ABI from "./abi/VBep20.json"; - -const { bscmainnet } = NETWORK_ADDRESSES; - -// --- Constants (addresses above) --- -// ... paste deployed addresses here ... - -// --- Helpers --- -// ... paste helper functions (setMaxStalePeriod, getManipulatedSwapData, setOraclePrice) ... - -forking(BLOCK_NUMBER, async () => { - let rpm: Contract; - let leverageManager: Contract; - let comptroller: Contract; - - before(async () => { - await setMaxStalePeriod(); - rpm = new ethers.Contract(RELATIVE_POSITION_MANAGER, RPM_ABI, ethers.provider); - leverageManager = new ethers.Contract(LEVERAGE_STRATEGIES_MANAGER, LSM_ABI, ethers.provider); - comptroller = new ethers.Contract(COMPTROLLER, COMPTROLLER_ABI, ethers.provider); - }); - - // If there's a VIP to execute first (e.g., adding DSA, setting permissions): - testVip("VIP-XXX Configure RPM", await vipXXX()); - - describe("RPM Position Lifecycle Tests", function () { - this.timeout(720000); // 12 minutes - fork tests are slow - - it("close with profit", async () => { - /* ... */ - }); - it("close with loss", async () => { - /* ... */ - }); - it("liquidation", async () => { - /* ... */ - }); - }); -}); -``` - ---- - -## Important Notes - -### Swap Data Mechanics - -- **`getManipulatedSwapData`** is preferred for deterministic tests - it fakes swaps by directly transferring tokens -- The `recipient` for opening swaps is always `leverageManager.address` -- The `recipient` for profit swaps in `closeWithProfit` is `rpm.address` (RPM supplies profit as principal) -- Each swap call needs a unique `salt` (use incrementing counter) -- The SwapHelper backend signer must be set to your test wallet - -### Token Whale Overrides - -- When using `getManipulatedSwapData`, the function needs to fund SwapHelper with `tokenOut` -- For WBNB, use `vLONG_ADDRESS` (vToken holds underlying) as `tokenOutWhaleOverride` -- For ETH, use `SHORT_WHALE` (Binance hot wallet) -- For USDC, the token contract itself (`DSA_ADDRESS`) often holds supply - -### Buffer Amounts - -- **Partial close**: 0.2% buffer on repay amounts (for interest accrual during execution) -- **Full close (100%)**: 2.2% buffer (2% contract tolerance + 0.2% interest) -- Always use `.mul(X).div(Y)` for percentage calculations (no floating point) - -### Position Lifecycle - -1. `activateAndOpenPosition()` - Creates position account (proxy clone), supplies principal, borrows SHORT, swaps to LONG -2. `closeWithProfit()` - Redeems LONG, swaps to SHORT to repay debt, swaps remaining LONG to DSA as profit -3. `closeWithLoss()` - Redeems LONG, swaps to SHORT (insufficient), redeems DSA principal, swaps to SHORT to cover shortfall -4. `deactivatePosition()` - Withdraws remaining principal to user, marks position inactive -5. `liquidateBorrow()` - External call on vToken by liquidator when position is underwater - -### Key Gotchas - -- `initialPrincipal` MUST be > 0 in `activateAndOpenPosition` (reverts with `InsufficientPrincipal`) -- `_setLiquidatorContract` must be called on Comptroller before liquidation (Venus Core Pool requirement) -- Position stays `isActive = true` even after full close - must explicitly call `deactivatePosition()` -- Oracle manipulation requires impersonating `NORMAL_TIMELOCK` -- Fork block number must be recent enough that RPM contracts are deployed and configured - -### Running the Test - -```bash -npx hardhat test simulations/vip-XXX/bscmainnet.ts --fork bscmainnet -``` diff --git a/simulations/vip-610/bscmainnet.ts b/simulations/vip-610/bscmainnet.ts index b301fbe1f..c3b078fc1 100644 --- a/simulations/vip-610/bscmainnet.ts +++ b/simulations/vip-610/bscmainnet.ts @@ -20,6 +20,7 @@ import FLASHLOAN_FACET_ABI from "./abi/FlashLoanFacet.json"; import RELATIVE_POSITION_MANAGER_ABI from "./abi/RelativePositionManager.json"; import RESILIENT_ORACLE_ABI from "./abi/ResilientOracle.json"; import SWAP_HELPER_ABI from "./abi/SwapHelperAbi.json"; +import VTOKEN_ABI from "./abi/VToken.json"; const { bscmainnet } = NETWORK_ADDRESSES; @@ -196,8 +197,6 @@ forking(87147067, async () => { "function balanceOf(address account) view returns (uint256)", ]; - const VTOKEN_ABI = require("./abi/VToken.json"); - let saltCounter = 0; let comptroller: Contract; let dsa: Contract; From 1c8279e0a8952c22f1ca54a2c0c0bd953ee56076 Mon Sep 17 00:00:00 2001 From: GitGuru7 Date: Fri, 20 Mar 2026 10:42:56 +0530 Subject: [PATCH 08/13] fix: update testnet address after redeploy --- simulations/vip-610/bscmainnet.ts | 19 ++++++++++++++++--- simulations/vip-610/bsctestnet.ts | 19 ++++++++++++++++--- vips/vip-610/bscmainnet.ts | 2 +- vips/vip-610/bsctestnet.ts | 10 +++++----- 4 files changed, 38 insertions(+), 12 deletions(-) diff --git a/simulations/vip-610/bscmainnet.ts b/simulations/vip-610/bscmainnet.ts index c3b078fc1..47155a691 100644 --- a/simulations/vip-610/bscmainnet.ts +++ b/simulations/vip-610/bscmainnet.ts @@ -29,14 +29,13 @@ const ACM_FUNCTION_SIGNATURES = [ "partialUnpause()", "completePause()", "completeUnpause()", - "setPositionAccountImplementation(address)", "setProportionalCloseTolerance(uint256)", "addDSAVToken(address)", "setDSAVTokenActive(uint8,bool)", "executePositionAccountCall(address,address[],bytes[])", ] as const; -forking(87147067, async () => { +forking(87632849, async () => { let accessControlManager: Contract; let relativePositionManager: Contract; @@ -71,13 +70,20 @@ forking(87147067, async () => { expect(await accessControlManager.hasRole(roleHash, timelockOrGuardian)).to.equal(false); } } + const setImplRole = ethers.utils.solidityPack( + ["address", "string"], + [RELATIVE_POSITION_MANAGER, "setPositionAccountImplementation(address)"], + ); + expect( + await accessControlManager.hasRole(ethers.utils.keccak256(setImplRole), bscmainnet.NORMAL_TIMELOCK), + ).to.equal(false); }); }); testVip("VIP-610 [BNB Chain] Configure Relative Position Manager", await vip610(), { callbackAfterExecution: async txResponse => { await expectEvents(txResponse, [RELATIVE_POSITION_MANAGER_ABI], ["OwnershipTransferred"], [1]); - await expectEvents(txResponse, [ACCESS_CONTROL_MANAGER_ABI], ["RoleGranted"], [36]); + await expectEvents(txResponse, [ACCESS_CONTROL_MANAGER_ABI], ["RoleGranted"], [33]); await expectEvents(txResponse, [RELATIVE_POSITION_MANAGER_ABI], ["PositionAccountImplementationSet"], [1]); await expectEvents(txResponse, [RELATIVE_POSITION_MANAGER_ABI], ["DSAVTokenAdded"], [2]); }, @@ -97,6 +103,13 @@ forking(87147067, async () => { expect(await accessControlManager.hasRole(roleHash, timelockOrGuardian)).to.equal(true); } } + const setImplRole = ethers.utils.solidityPack( + ["address", "string"], + [RELATIVE_POSITION_MANAGER, "setPositionAccountImplementation(address)"], + ); + expect( + await accessControlManager.hasRole(ethers.utils.keccak256(setImplRole), bscmainnet.NORMAL_TIMELOCK), + ).to.equal(true); }); it("RPM should have Position Account implementation stored in the state", async () => { diff --git a/simulations/vip-610/bsctestnet.ts b/simulations/vip-610/bsctestnet.ts index 3d41e2e20..16c1f9e8e 100644 --- a/simulations/vip-610/bsctestnet.ts +++ b/simulations/vip-610/bsctestnet.ts @@ -28,14 +28,13 @@ const ACM_FUNCTION_SIGNATURES = [ "partialUnpause()", "completePause()", "completeUnpause()", - "setPositionAccountImplementation(address)", "setProportionalCloseTolerance(uint256)", "addDSAVToken(address)", "setDSAVTokenActive(uint8,bool)", "executePositionAccountCall(address,address[],bytes[])", ] as const; -forking(96276916, async () => { +forking(96763094, async () => { let comptroller: Contract; let accessControlManager: Contract; let leverageStrategiesManager: Contract; @@ -90,6 +89,13 @@ forking(96276916, async () => { ).to.equal(false); } } + expect( + await accessControlManager.hasPermission( + bsctestnet.NORMAL_TIMELOCK, + RELATIVE_POSITION_MANAGER, + "setPositionAccountImplementation(address)", + ), + ).to.equal(false); }); }); @@ -97,7 +103,7 @@ forking(96276916, async () => { callbackAfterExecution: async txResponse => { await expectEvents(txResponse, [RELATIVE_POSITION_MANAGER_ABI], ["OwnershipTransferred"], [3]); await expectEvents(txResponse, [FLASHLOAN_FACET_ABI], ["IsAccountFlashLoanWhitelisted"], [1]); - await expectEvents(txResponse, [ACCESS_CONTROL_MANAGER_ABI], ["PermissionGranted"], [36]); + await expectEvents(txResponse, [ACCESS_CONTROL_MANAGER_ABI], ["PermissionGranted"], [33]); await expectEvents(txResponse, [RELATIVE_POSITION_MANAGER_ABI], ["PositionAccountImplementationSet"], [1]); await expectEvents(txResponse, [RELATIVE_POSITION_MANAGER_ABI], ["DSAVTokenAdded"], [2]); }, @@ -131,6 +137,13 @@ forking(96276916, async () => { ).to.equal(true); } } + expect( + await accessControlManager.hasPermission( + bsctestnet.NORMAL_TIMELOCK, + RELATIVE_POSITION_MANAGER, + "setPositionAccountImplementation(address)", + ), + ).to.equal(true); }); it("RPM should have Position account implementation stored in the state", async () => { diff --git a/vips/vip-610/bscmainnet.ts b/vips/vip-610/bscmainnet.ts index 147374c7f..109782c4f 100644 --- a/vips/vip-610/bscmainnet.ts +++ b/vips/vip-610/bscmainnet.ts @@ -45,7 +45,7 @@ export const vip610 = () => { ...giveAcmPermissions("partialUnpause()"), ...giveAcmPermissions("completePause()"), ...giveAcmPermissions("completeUnpause()"), - ...giveAcmPermissions("setPositionAccountImplementation(address)"), + ...giveAcmPermissions("setPositionAccountImplementation(address)", [bscmainnet.NORMAL_TIMELOCK]), ...giveAcmPermissions("setProportionalCloseTolerance(uint256)"), ...giveAcmPermissions("addDSAVToken(address)"), ...giveAcmPermissions("setDSAVTokenActive(uint8,bool)"), diff --git a/vips/vip-610/bsctestnet.ts b/vips/vip-610/bsctestnet.ts index bf38ec524..4c51dad12 100644 --- a/vips/vip-610/bsctestnet.ts +++ b/vips/vip-610/bsctestnet.ts @@ -3,10 +3,10 @@ import { ProposalType } from "src/types"; import { makeProposal } from "src/utils"; const { bsctestnet } = NETWORK_ADDRESSES; -export const SWAP_HELPER = "0x3Bf0Eb0663BeCe17d95FE33736762bFD20f488b2"; -export const LEVERAGE_STRATEGIES_MANAGER = "0xfc8810B0f1144D5A1F6231aFDb8B51F31c0bc8A7"; -export const RELATIVE_POSITION_MANAGER = "0xF01CA5Ad6152d932Ed19FB28b285529399dA8166"; -export const POSITION_ACCOUNT = "0x03590ef916d538049Ed15f2690A01F70c2A02954"; +export const SWAP_HELPER = "0xf7Cfd0eDfAC7AA473813559b372297332EdEbB8B"; +export const LEVERAGE_STRATEGIES_MANAGER = "0x5187226337C95c4BE683D37Ffc66D41f5b6cE38f"; +export const RELATIVE_POSITION_MANAGER = "0x25dbA64B28F93cC40e9cAf9691266043fe1000a2"; +export const POSITION_ACCOUNT = "0xC9A5f1598e434E3E52CE25D7ff290E4CF167ee52"; export const vUSDC = "0xD5C4C2e2facBEB59D0216D0595d63FcDc6F9A1a7"; export const vUSDT = "0xb7526572FFE56AB9D7489838Bf2E18e3323b441A"; @@ -63,7 +63,7 @@ export const vip610 = () => { ...giveAcmPermissions("partialUnpause()"), ...giveAcmPermissions("completePause()"), ...giveAcmPermissions("completeUnpause()"), - ...giveAcmPermissions("setPositionAccountImplementation(address)"), + ...giveAcmPermissions("setPositionAccountImplementation(address)", [bsctestnet.NORMAL_TIMELOCK]), ...giveAcmPermissions("setProportionalCloseTolerance(uint256)"), ...giveAcmPermissions("addDSAVToken(address)"), ...giveAcmPermissions("setDSAVTokenActive(uint8,bool)"), From a00d59b5a5888a03a616b1e0697592c1682f57db Mon Sep 17 00:00:00 2001 From: GitGuru7 Date: Fri, 27 Mar 2026 14:48:38 +0530 Subject: [PATCH 09/13] fix: update mainnet address after redeploy --- .../vip-610/abi/RelativePositionManager.json | 2699 ++++++++++++----- simulations/vip-610/bscmainnet.ts | 245 +- vips/vip-610/bscmainnet.ts | 4 +- 3 files changed, 2257 insertions(+), 691 deletions(-) diff --git a/simulations/vip-610/abi/RelativePositionManager.json b/simulations/vip-610/abi/RelativePositionManager.json index 7485c9f4c..e995791c6 100644 --- a/simulations/vip-610/abi/RelativePositionManager.json +++ b/simulations/vip-610/abi/RelativePositionManager.json @@ -1,689 +1,2012 @@ [ - { - "inputs": [ - { "internalType": "address", "name": "comptroller", "type": "address" }, - { "internalType": "address", "name": "leverageManager", "type": "address" } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { "inputs": [], "name": "AssetNotListed", "type": "error" }, - { "inputs": [], "name": "BorrowAmountExceedsMaximum", "type": "error" }, - { "inputs": [], "name": "CompletelyPaused", "type": "error" }, - { "inputs": [], "name": "DSAInactive", "type": "error" }, - { "inputs": [], "name": "DSAVTokenAlreadyAdded", "type": "error" }, - { - "inputs": [{ "internalType": "uint256", "name": "errorCode", "type": "uint256" }], - "name": "EnterMarketFailed", - "type": "error" - }, - { "inputs": [], "name": "ExcessiveShortDust", "type": "error" }, - { "inputs": [], "name": "InsufficientPrincipal", "type": "error" }, - { "inputs": [], "name": "InsufficientWithdrawableAmount", "type": "error" }, - { "inputs": [], "name": "InvalidCloseFractionBps", "type": "error" }, - { "inputs": [], "name": "InvalidCollateralFactor", "type": "error" }, - { "inputs": [], "name": "InvalidDSA", "type": "error" }, - { "inputs": [], "name": "InvalidLeverage", "type": "error" }, - { "inputs": [], "name": "InvalidLongAmountToRedeem", "type": "error" }, - { "inputs": [], "name": "InvalidOraclePrice", "type": "error" }, - { "inputs": [], "name": "InvalidProportionalCloseTolerance", "type": "error" }, - { "inputs": [], "name": "MinAmountOutRepayBelowDebt", "type": "error" }, - { "inputs": [], "name": "MinAmountOutSecondBelowDebt", "type": "error" }, - { - "inputs": [{ "internalType": "uint256", "name": "errorCode", "type": "uint256" }], - "name": "MintBehalfFailed", - "type": "error" - }, - { "inputs": [], "name": "PartiallyPaused", "type": "error" }, - { "inputs": [], "name": "PositionAccountImplementationLocked", "type": "error" }, - { "inputs": [], "name": "PositionAccountImplementationNotSet", "type": "error" }, - { "inputs": [], "name": "PositionAlreadyExists", "type": "error" }, - { "inputs": [], "name": "PositionNotActive", "type": "error" }, - { "inputs": [], "name": "PositionNotFullyClosed", "type": "error" }, - { "inputs": [], "name": "ProportionalCloseAmountOutOfTolerance", "type": "error" }, - { - "inputs": [{ "internalType": "uint256", "name": "errorCode", "type": "uint256" }], - "name": "RedeemBehalfFailed", - "type": "error" - }, - { "inputs": [], "name": "SameDSAActiveStatus", "type": "error" }, - { "inputs": [], "name": "SameMarketNotAllowed", "type": "error" }, - { "inputs": [], "name": "SameProportionalCloseTolerance", "type": "error" }, - { "inputs": [], "name": "SlippageExceeded", "type": "error" }, - { "inputs": [], "name": "TokenSwapCallFailed", "type": "error" }, - { - "inputs": [ - { "internalType": "address", "name": "sender", "type": "address" }, - { "internalType": "address", "name": "calledContract", "type": "address" }, - { "internalType": "string", "name": "methodSignature", "type": "string" } - ], - "name": "Unauthorized", - "type": "error" - }, - { "inputs": [], "name": "VBNBNotSupported", "type": "error" }, - { "inputs": [], "name": "ZeroAddress", "type": "error" }, - { "inputs": [], "name": "ZeroAmount", "type": "error" }, - { "inputs": [], "name": "ZeroDebt", "type": "error" }, - { "inputs": [], "name": "ZeroShortAmount", "type": "error" }, - { "inputs": [], "name": "ZeroVTokensMinted", "type": "error" }, - { - "anonymous": false, - "inputs": [{ "indexed": false, "internalType": "bool", "name": "paused", "type": "bool" }], - "name": "CompletePauseToggled", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": true, "internalType": "address", "name": "dsaVToken", "type": "address" }, - { "indexed": false, "internalType": "uint8", "name": "index", "type": "uint8" }, - { "indexed": false, "internalType": "bool", "name": "active", "type": "bool" } - ], - "name": "DSAVTokenActiveUpdated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": true, "internalType": "address", "name": "dsaVToken", "type": "address" }, - { "indexed": false, "internalType": "uint8", "name": "index", "type": "uint8" } - ], - "name": "DSAVTokenAdded", - "type": "event" - }, - { - "anonymous": false, - "inputs": [{ "indexed": false, "internalType": "uint8", "name": "version", "type": "uint8" }], - "name": "Initialized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": false, "internalType": "address", "name": "oldAccessControlManager", "type": "address" }, - { "indexed": false, "internalType": "address", "name": "newAccessControlManager", "type": "address" } - ], - "name": "NewAccessControlManager", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": true, "internalType": "address", "name": "previousOwner", "type": "address" }, - { "indexed": true, "internalType": "address", "name": "newOwner", "type": "address" } - ], - "name": "OwnershipTransferStarted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": true, "internalType": "address", "name": "previousOwner", "type": "address" }, - { "indexed": true, "internalType": "address", "name": "newOwner", "type": "address" } - ], - "name": "OwnershipTransferred", - "type": "event" - }, - { - "anonymous": false, - "inputs": [{ "indexed": false, "internalType": "bool", "name": "paused", "type": "bool" }], - "name": "PartialPauseToggled", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": true, "internalType": "address", "name": "user", "type": "address" }, - { "indexed": true, "internalType": "address", "name": "longAsset", "type": "address" }, - { "indexed": true, "internalType": "address", "name": "shortAsset", "type": "address" }, - { "indexed": false, "internalType": "address", "name": "positionAccount", "type": "address" } - ], - "name": "PositionAccountDeployed", - "type": "event" - }, - { - "anonymous": false, - "inputs": [{ "indexed": true, "internalType": "address", "name": "implementation", "type": "address" }], - "name": "PositionAccountImplementationSet", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": true, "internalType": "address", "name": "user", "type": "address" }, - { "indexed": true, "internalType": "address", "name": "longAsset", "type": "address" }, - { "indexed": true, "internalType": "address", "name": "shortAsset", "type": "address" }, - { "indexed": false, "internalType": "address", "name": "dsaAsset", "type": "address" }, - { "indexed": false, "internalType": "address", "name": "positionAccount", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "cycleId", "type": "uint256" }, - { "indexed": false, "internalType": "uint256", "name": "initialPrincipal", "type": "uint256" }, - { "indexed": false, "internalType": "uint256", "name": "effectiveLeverage", "type": "uint256" } - ], - "name": "PositionActivated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": true, "internalType": "address", "name": "user", "type": "address" }, - { "indexed": true, "internalType": "address", "name": "positionAccount", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "cycleId", "type": "uint256" }, - { "indexed": false, "internalType": "uint256", "name": "closeFractionBps", "type": "uint256" }, - { "indexed": false, "internalType": "uint256", "name": "amountRepaid", "type": "uint256" }, - { "indexed": false, "internalType": "uint256", "name": "amountRedeemed", "type": "uint256" }, - { "indexed": false, "internalType": "uint256", "name": "amountRedeemedDsa", "type": "uint256" }, - { "indexed": false, "internalType": "uint256", "name": "longDustRedeemed", "type": "uint256" } - ], - "name": "PositionClosed", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": true, "internalType": "address", "name": "user", "type": "address" }, - { "indexed": true, "internalType": "address", "name": "positionAccount", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "cycleId", "type": "uint256" }, - { "indexed": false, "internalType": "uint256", "name": "longRedeemed", "type": "uint256" }, - { "indexed": false, "internalType": "uint256", "name": "dsaRedeemed", "type": "uint256" } - ], - "name": "PositionDeactivated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": true, "internalType": "address", "name": "user", "type": "address" }, - { "indexed": true, "internalType": "address", "name": "positionAccount", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "cycleId", "type": "uint256" }, - { "indexed": false, "internalType": "address", "name": "longAsset", "type": "address" }, - { "indexed": false, "internalType": "address", "name": "shortAsset", "type": "address" }, - { "indexed": false, "internalType": "address", "name": "dsaAsset", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "shortAmount", "type": "uint256" }, - { "indexed": false, "internalType": "uint256", "name": "initialPrincipal", "type": "uint256" } - ], - "name": "PositionOpened", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": true, "internalType": "address", "name": "user", "type": "address" }, - { "indexed": true, "internalType": "address", "name": "positionAccount", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "cycleId", "type": "uint256" }, - { "indexed": false, "internalType": "address", "name": "longAsset", "type": "address" }, - { "indexed": false, "internalType": "address", "name": "shortAsset", "type": "address" }, - { "indexed": false, "internalType": "address", "name": "dsaAsset", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "shortAmount", "type": "uint256" }, - { "indexed": false, "internalType": "uint256", "name": "additionalPrincipal", "type": "uint256" } - ], - "name": "PositionScaled", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": true, "internalType": "address", "name": "user", "type": "address" }, - { "indexed": true, "internalType": "address", "name": "positionAccount", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "cycleId", "type": "uint256" }, - { "indexed": false, "internalType": "address", "name": "dsaAsset", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }, - { "indexed": false, "internalType": "uint256", "name": "newTotalPrincipal", "type": "uint256" } - ], - "name": "PrincipalSupplied", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": true, "internalType": "address", "name": "user", "type": "address" }, - { "indexed": true, "internalType": "address", "name": "positionAccount", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "cycleId", "type": "uint256" }, - { "indexed": false, "internalType": "address", "name": "dsaAsset", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }, - { "indexed": false, "internalType": "uint256", "name": "remainingPrincipal", "type": "uint256" } - ], - "name": "PrincipalWithdrawn", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": true, "internalType": "address", "name": "user", "type": "address" }, - { "indexed": true, "internalType": "address", "name": "positionAccount", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "amountConvertedToProfit", "type": "uint256" }, - { "indexed": false, "internalType": "uint256", "name": "newTotalPrincipal", "type": "uint256" } - ], - "name": "ProfitConverted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": true, "internalType": "uint256", "name": "oldTolerance", "type": "uint256" }, - { "indexed": true, "internalType": "uint256", "name": "newTolerance", "type": "uint256" } - ], - "name": "ProportionalCloseToleranceUpdated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": true, "internalType": "address", "name": "user", "type": "address" }, - { "indexed": true, "internalType": "address", "name": "positionAccount", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "oldSuppliedPrincipal", "type": "uint256" }, - { "indexed": false, "internalType": "uint256", "name": "newSuppliedPrincipal", "type": "uint256" } - ], - "name": "RefreshedSuppliedPrincipal", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": true, "internalType": "address", "name": "token", "type": "address" }, - { "indexed": true, "internalType": "address", "name": "from", "type": "address" }, - { "indexed": true, "internalType": "address", "name": "to", "type": "address" }, - { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "UnderlyingTransferred", - "type": "event" - }, - { - "inputs": [], - "name": "COMPTROLLER", - "outputs": [{ "internalType": "contract IComptroller", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "LEVERAGE_MANAGER", - "outputs": [{ "internalType": "contract LeverageStrategiesManager", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "POSITION_ACCOUNT_IMPLEMENTATION", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { "inputs": [], "name": "acceptOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [], - "name": "accessControlManager", - "outputs": [{ "internalType": "contract IAccessControlManagerV8", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "longVToken", "type": "address" }, - { "internalType": "address", "name": "shortVToken", "type": "address" }, - { "internalType": "uint8", "name": "dsaIndex", "type": "uint8" }, - { "internalType": "uint256", "name": "initialPrincipal", "type": "uint256" }, - { "internalType": "uint256", "name": "effectiveLeverage", "type": "uint256" }, - { "internalType": "uint256", "name": "shortAmount", "type": "uint256" }, - { "internalType": "uint256", "name": "minLongAmount", "type": "uint256" }, - { "internalType": "bytes", "name": "swapData", "type": "bytes" } - ], - "name": "activateAndOpenPosition", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [{ "internalType": "address", "name": "dsaVToken", "type": "address" }], - "name": "addDSAVToken", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "contract IVToken", "name": "longVToken", "type": "address" }, - { "internalType": "contract IVToken", "name": "shortVToken", "type": "address" }, - { "internalType": "uint256", "name": "closeFractionBps", "type": "uint256" }, - { "internalType": "uint256", "name": "longAmountToRedeemForFirstSwap", "type": "uint256" }, - { "internalType": "uint256", "name": "shortAmountToRepayForFirstSwap", "type": "uint256" }, - { "internalType": "uint256", "name": "minAmountOutFirst", "type": "uint256" }, - { "internalType": "bytes", "name": "swapDataFirst", "type": "bytes" }, - { "internalType": "uint256", "name": "dsaAmountToRedeemForSecondSwap", "type": "uint256" }, - { "internalType": "uint256", "name": "minAmountOutSecond", "type": "uint256" }, - { "internalType": "bytes", "name": "swapDataSecond", "type": "bytes" } - ], - "name": "closeWithLoss", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "contract IVToken", "name": "longVToken", "type": "address" }, - { "internalType": "contract IVToken", "name": "shortVToken", "type": "address" }, - { "internalType": "uint256", "name": "closeFractionBps", "type": "uint256" }, - { "internalType": "uint256", "name": "longAmountToRedeemForRepay", "type": "uint256" }, - { "internalType": "uint256", "name": "minAmountOutRepay", "type": "uint256" }, - { "internalType": "bytes", "name": "swapDataRepay", "type": "bytes" }, - { "internalType": "uint256", "name": "longAmountToRedeemForProfit", "type": "uint256" }, - { "internalType": "uint256", "name": "minAmountOutProfit", "type": "uint256" }, - { "internalType": "bytes", "name": "swapDataProfit", "type": "bytes" } - ], - "name": "closeWithProfit", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { "inputs": [], "name": "completePause", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, - { "inputs": [], "name": "completeUnpause", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [ - { "internalType": "contract IVToken", "name": "longVToken", "type": "address" }, - { "internalType": "contract IVToken", "name": "shortVToken", "type": "address" } - ], - "name": "deactivatePosition", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "dsaVTokenIndexCounter", - "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }], - "name": "dsaVTokens", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "positionAccount", "type": "address" }, - { "internalType": "address[]", "name": "targets", "type": "address[]" }, - { "internalType": "bytes[]", "name": "data", "type": "bytes[]" } - ], - "name": "executePositionAccountCall", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "user", "type": "address" }, - { "internalType": "contract IVToken", "name": "longVToken", "type": "address" }, - { "internalType": "contract IVToken", "name": "shortVToken", "type": "address" } - ], - "name": "getAvailableShortCapacity", - "outputs": [{ "internalType": "uint256", "name": "availableCapacity", "type": "uint256" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "getDsaVTokens", - "outputs": [{ "internalType": "address[]", "name": "dsaVTokensList", "type": "address[]" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "user", "type": "address" }, - { "internalType": "contract IVToken", "name": "longVToken", "type": "address" }, - { "internalType": "contract IVToken", "name": "shortVToken", "type": "address" } - ], - "name": "getLongCollateralBalance", - "outputs": [{ "internalType": "uint256", "name": "longBalance", "type": "uint256" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "contract IVToken", "name": "dsaVToken", "type": "address" }, - { "internalType": "address", "name": "longVToken", "type": "address" } - ], - "name": "getMaxLeverageAllowed", - "outputs": [{ "internalType": "uint256", "name": "maxLeverage", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "user", "type": "address" }, - { "internalType": "contract IVToken", "name": "longVToken", "type": "address" }, - { "internalType": "contract IVToken", "name": "shortVToken", "type": "address" } - ], - "name": "getPosition", - "outputs": [ - { - "components": [ - { "internalType": "address", "name": "user", "type": "address" }, - { "internalType": "address", "name": "longVToken", "type": "address" }, - { "internalType": "address", "name": "shortVToken", "type": "address" }, - { "internalType": "address", "name": "positionAccount", "type": "address" }, - { "internalType": "bool", "name": "isActive", "type": "bool" }, - { "internalType": "uint8", "name": "dsaIndex", "type": "uint8" }, - { "internalType": "address", "name": "dsaVToken", "type": "address" }, - { "internalType": "uint256", "name": "suppliedPrincipalVTokens", "type": "uint256" }, - { "internalType": "uint256", "name": "effectiveLeverage", "type": "uint256" }, - { "internalType": "uint256", "name": "cycleId", "type": "uint256" } - ], - "internalType": "struct IRelativePositionManager.Position", - "name": "position", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "user", "type": "address" }, - { "internalType": "contract IVToken", "name": "longVToken", "type": "address" }, - { "internalType": "contract IVToken", "name": "shortVToken", "type": "address" } - ], - "name": "getPositionAccountAddress", - "outputs": [{ "internalType": "address", "name": "predicted", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "user", "type": "address" }, - { "internalType": "contract IVToken", "name": "longVToken", "type": "address" }, - { "internalType": "contract IVToken", "name": "shortVToken", "type": "address" } - ], - "name": "getSuppliedPrincipalBalance", - "outputs": [{ "internalType": "uint256", "name": "balance", "type": "uint256" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "user", "type": "address" }, - { "internalType": "contract IVToken", "name": "longVToken", "type": "address" }, - { "internalType": "contract IVToken", "name": "shortVToken", "type": "address" } - ], - "name": "getUtilizationInfo", - "outputs": [ - { - "components": [ - { "internalType": "uint256", "name": "actualCapitalUtilized", "type": "uint256" }, - { "internalType": "uint256", "name": "nominalCapitalUtilized", "type": "uint256" }, - { "internalType": "uint256", "name": "finalCapitalUtilized", "type": "uint256" }, - { "internalType": "uint256", "name": "availableCapitalUSD", "type": "uint256" }, - { "internalType": "uint256", "name": "withdrawableAmount", "type": "uint256" }, - { "internalType": "uint256", "name": "clampedLeverage", "type": "uint256" } - ], - "internalType": "struct IRelativePositionManager.UtilizationInfo", - "name": "utilization", - "type": "tuple" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [{ "internalType": "address", "name": "accessControlManager_", "type": "address" }], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "isCompletelyPaused", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [{ "internalType": "address", "name": "", "type": "address" }], - "name": "isDsaVTokenActive", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "isPartiallyPaused", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "isPositionAccountImplementationLocked", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "owner", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { "inputs": [], "name": "partialPause", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, - { "inputs": [], "name": "partialUnpause", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [], - "name": "pendingOwner", - "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "", "type": "address" }, - { "internalType": "address", "name": "", "type": "address" }, - { "internalType": "address", "name": "", "type": "address" } - ], - "name": "positions", - "outputs": [ - { "internalType": "address", "name": "user", "type": "address" }, - { "internalType": "address", "name": "longVToken", "type": "address" }, - { "internalType": "address", "name": "shortVToken", "type": "address" }, - { "internalType": "address", "name": "positionAccount", "type": "address" }, - { "internalType": "bool", "name": "isActive", "type": "bool" }, - { "internalType": "uint8", "name": "dsaIndex", "type": "uint8" }, - { "internalType": "address", "name": "dsaVToken", "type": "address" }, - { "internalType": "uint256", "name": "suppliedPrincipalVTokens", "type": "uint256" }, - { "internalType": "uint256", "name": "effectiveLeverage", "type": "uint256" }, - { "internalType": "uint256", "name": "cycleId", "type": "uint256" } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "proportionalCloseTolerance", - "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], - "stateMutability": "view", - "type": "function" - }, - { "inputs": [], "name": "renounceOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [ - { "internalType": "contract IVToken", "name": "longVToken", "type": "address" }, - { "internalType": "contract IVToken", "name": "shortVToken", "type": "address" }, - { "internalType": "uint256", "name": "additionalPrincipal", "type": "uint256" }, - { "internalType": "uint256", "name": "shortAmount", "type": "uint256" }, - { "internalType": "uint256", "name": "minLongAmount", "type": "uint256" }, - { "internalType": "bytes", "name": "swapData", "type": "bytes" } - ], - "name": "scalePosition", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [{ "internalType": "address", "name": "accessControlManager_", "type": "address" }], - "name": "setAccessControlManager", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "uint8", "name": "dsaIndex", "type": "uint8" }, - { "internalType": "bool", "name": "active", "type": "bool" } - ], - "name": "setDSAVTokenActive", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [{ "internalType": "address", "name": "positionAccountImpl", "type": "address" }], - "name": "setPositionAccountImplementation", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [{ "internalType": "uint256", "name": "newTolerance", "type": "uint256" }], - "name": "setProportionalCloseTolerance", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "longVToken", "type": "address" }, - { "internalType": "address", "name": "shortVToken", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "supplyPrincipal", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [{ "internalType": "address", "name": "newOwner", "type": "address" }], - "name": "transferOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "contract IVToken", "name": "longVToken", "type": "address" }, - { "internalType": "contract IVToken", "name": "shortVToken", "type": "address" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "withdrawPrincipal", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "InvalidCallsLength", - "type": "error" - } -] + { + "inputs": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "address", + "name": "leverageManager", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "AssetNotListed", + "type": "error" + }, + { + "inputs": [], + "name": "BorrowAmountExceedsMaximum", + "type": "error" + }, + { + "inputs": [], + "name": "CompletelyPaused", + "type": "error" + }, + { + "inputs": [], + "name": "DSAInactive", + "type": "error" + }, + { + "inputs": [], + "name": "DSAVTokenAlreadyAdded", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "errorCode", + "type": "uint256" + } + ], + "name": "EnterMarketFailed", + "type": "error" + }, + { + "inputs": [], + "name": "ExcessiveShortDust", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientPrincipal", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientWithdrawableAmount", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidCloseFractionBps", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidCollateralFactor", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidDSA", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidLeverage", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidLongAmountToRedeem", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidOraclePrice", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidProportionalCloseTolerance", + "type": "error" + }, + { + "inputs": [], + "name": "MinAmountOutRepayBelowDebt", + "type": "error" + }, + { + "inputs": [], + "name": "MinAmountOutSecondBelowDebt", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "errorCode", + "type": "uint256" + } + ], + "name": "MintBehalfFailed", + "type": "error" + }, + { + "inputs": [], + "name": "PartiallyPaused", + "type": "error" + }, + { + "inputs": [], + "name": "PositionAccountImplementationLocked", + "type": "error" + }, + { + "inputs": [], + "name": "PositionAccountImplementationNotSet", + "type": "error" + }, + { + "inputs": [], + "name": "PositionAlreadyExists", + "type": "error" + }, + { + "inputs": [], + "name": "PositionNotActive", + "type": "error" + }, + { + "inputs": [], + "name": "PositionNotFullyClosed", + "type": "error" + }, + { + "inputs": [], + "name": "ProportionalCloseAmountOutOfTolerance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "errorCode", + "type": "uint256" + } + ], + "name": "RedeemBehalfFailed", + "type": "error" + }, + { + "inputs": [], + "name": "SameDSAActiveStatus", + "type": "error" + }, + { + "inputs": [], + "name": "SameMarketNotAllowed", + "type": "error" + }, + { + "inputs": [], + "name": "SameProportionalCloseTolerance", + "type": "error" + }, + { + "inputs": [], + "name": "SlippageExceeded", + "type": "error" + }, + { + "inputs": [], + "name": "TokenSwapCallFailed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "calledContract", + "type": "address" + }, + { + "internalType": "string", + "name": "methodSignature", + "type": "string" + } + ], + "name": "Unauthorized", + "type": "error" + }, + { + "inputs": [], + "name": "VBNBNotSupported", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroAmount", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroDebt", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroShortAmount", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroVTokensMinted", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "paused", + "type": "bool" + } + ], + "name": "CompletePauseToggled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "dsaVToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "index", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "bool", + "name": "active", + "type": "bool" + } + ], + "name": "DSAVTokenActiveUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "dsaVToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "index", + "type": "uint8" + } + ], + "name": "DSAVTokenAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "oldAccessControlManager", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAccessControlManager", + "type": "address" + } + ], + "name": "NewAccessControlManager", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "paused", + "type": "bool" + } + ], + "name": "PartialPauseToggled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "longAsset", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "shortAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "positionAccount", + "type": "address" + } + ], + "name": "PositionAccountDeployed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "PositionAccountImplementationSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "longAsset", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "shortAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "dsaAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "cycleId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "initialPrincipal", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "effectiveLeverage", + "type": "uint256" + } + ], + "name": "PositionActivated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "cycleId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "closeFractionBps", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountRepaid", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountRedeemed", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountRedeemedDsa", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "longDustRedeemed", + "type": "uint256" + } + ], + "name": "PositionClosed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "cycleId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "longRedeemed", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "dsaRedeemed", + "type": "uint256" + } + ], + "name": "PositionDeactivated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "cycleId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "longAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "shortAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "dsaAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "shortAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "initialPrincipal", + "type": "uint256" + } + ], + "name": "PositionOpened", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "cycleId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "longAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "shortAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "dsaAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "shortAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "additionalPrincipal", + "type": "uint256" + } + ], + "name": "PositionScaled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "cycleId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "dsaAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newTotalPrincipal", + "type": "uint256" + } + ], + "name": "PrincipalSupplied", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "cycleId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "dsaAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "remainingPrincipal", + "type": "uint256" + } + ], + "name": "PrincipalWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountConvertedToProfit", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newTotalPrincipal", + "type": "uint256" + } + ], + "name": "ProfitConverted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "oldTolerance", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "newTolerance", + "type": "uint256" + } + ], + "name": "ProportionalCloseToleranceUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "oldSuppliedPrincipal", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newSuppliedPrincipal", + "type": "uint256" + } + ], + "name": "RefreshedSuppliedPrincipal", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "UnderlyingTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "COMPTROLLER", + "outputs": [ + { + "internalType": "contract IComptroller", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LEVERAGE_MANAGER", + "outputs": [ + { + "internalType": "contract LeverageStrategiesManager", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MIN_LEVERAGE", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "POSITION_ACCOUNT_IMPLEMENTATION", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PROPORTIONAL_CLOSE_MAX", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PROPORTIONAL_CLOSE_MIN", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "accessControlManager", + "outputs": [ + { + "internalType": "contract IAccessControlManagerV8", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "address", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "uint8", + "name": "dsaIndex", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "initialPrincipal", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "effectiveLeverage", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minLongAmount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapData", + "type": "bytes" + } + ], + "name": "activateAndOpenPosition", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "dsaVToken", + "type": "address" + } + ], + "name": "addDSAVToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "closeFractionBps", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "longAmountToRedeemForFirstSwap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortAmountToRepayForFirstSwap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOutFirst", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapDataFirst", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "dsaAmountToRedeemForSecondSwap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOutSecond", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapDataSecond", + "type": "bytes" + } + ], + "name": "closeWithLoss", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "longAmountToRedeemForFirstSwap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortAmountToRepayForFirstSwap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOutFirst", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapDataFirst", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "dsaAmountToRedeemForSecondSwap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOutSecond", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapDataSecond", + "type": "bytes" + } + ], + "internalType": "struct IRelativePositionManager.CloseWithLossParams", + "name": "lossSwapParams", + "type": "tuple" + } + ], + "name": "closeWithLossAndDeactivate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "closeFractionBps", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "longAmountToRedeemForRepay", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOutRepay", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapDataRepay", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "longAmountToRedeemForProfit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOutProfit", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapDataProfit", + "type": "bytes" + } + ], + "name": "closeWithProfit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "longAmountToRedeemForRepay", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOutRepay", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapDataRepay", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "longAmountToRedeemForProfit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOutProfit", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapDataProfit", + "type": "bytes" + } + ], + "internalType": "struct IRelativePositionManager.CloseWithProfitParams", + "name": "profitSwapParams", + "type": "tuple" + } + ], + "name": "closeWithProfitAndDeactivate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "completePause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "completeUnpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + } + ], + "name": "deactivatePosition", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "dsaVTokenIndexCounter", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "name": "dsaVTokens", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "internalType": "address[]", + "name": "targets", + "type": "address[]" + }, + { + "internalType": "bytes[]", + "name": "data", + "type": "bytes[]" + } + ], + "name": "executePositionAccountCall", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + } + ], + "name": "getAvailableShortCapacity", + "outputs": [ + { + "internalType": "uint256", + "name": "availableCapacity", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getDsaVTokens", + "outputs": [ + { + "internalType": "address[]", + "name": "dsaVTokensList", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + } + ], + "name": "getLongCollateralBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "longBalance", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "dsaVToken", + "type": "address" + }, + { + "internalType": "address", + "name": "longVToken", + "type": "address" + } + ], + "name": "getMaxLeverageAllowed", + "outputs": [ + { + "internalType": "uint256", + "name": "maxLeverage", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + } + ], + "name": "getPosition", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "address", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "address", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "internalType": "bool", + "name": "isActive", + "type": "bool" + }, + { + "internalType": "uint8", + "name": "dsaIndex", + "type": "uint8" + }, + { + "internalType": "address", + "name": "dsaVToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "suppliedPrincipalVTokens", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "effectiveLeverage", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "cycleId", + "type": "uint256" + } + ], + "internalType": "struct IRelativePositionManager.Position", + "name": "position", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + } + ], + "name": "getPositionAccountAddress", + "outputs": [ + { + "internalType": "address", + "name": "predicted", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + } + ], + "name": "getSuppliedPrincipalBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + } + ], + "name": "getUtilizationInfo", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "actualCapitalUtilized", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nominalCapitalUtilized", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "finalCapitalUtilized", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "availableCapitalUSD", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "withdrawableAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "clampedLeverage", + "type": "uint256" + } + ], + "internalType": "struct IRelativePositionManager.UtilizationInfo", + "name": "utilization", + "type": "tuple" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "accessControlManager_", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "isCompletelyPaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isDsaVTokenActive", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isPartiallyPaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isPositionAccountImplementationLocked", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "partialPause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "partialUnpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "positions", + "outputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "address", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "address", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "internalType": "bool", + "name": "isActive", + "type": "bool" + }, + { + "internalType": "uint8", + "name": "dsaIndex", + "type": "uint8" + }, + { + "internalType": "address", + "name": "dsaVToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "suppliedPrincipalVTokens", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "effectiveLeverage", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "cycleId", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proportionalCloseTolerance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "additionalPrincipal", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minLongAmount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapData", + "type": "bytes" + } + ], + "name": "scalePosition", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "accessControlManager_", + "type": "address" + } + ], + "name": "setAccessControlManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "dsaIndex", + "type": "uint8" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + } + ], + "name": "setDSAVTokenActive", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "positionAccountImpl", + "type": "address" + } + ], + "name": "setPositionAccountImplementation", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newTolerance", + "type": "uint256" + } + ], + "name": "setProportionalCloseTolerance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "address", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "supplyPrincipal", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "withdrawPrincipal", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ] \ No newline at end of file diff --git a/simulations/vip-610/bscmainnet.ts b/simulations/vip-610/bscmainnet.ts index 47155a691..1458f031c 100644 --- a/simulations/vip-610/bscmainnet.ts +++ b/simulations/vip-610/bscmainnet.ts @@ -35,7 +35,7 @@ const ACM_FUNCTION_SIGNATURES = [ "executePositionAccountCall(address,address[],bytes[])", ] as const; -forking(87632849, async () => { +forking(89004570, async () => { let accessControlManager: Contract; let relativePositionManager: Contract; @@ -597,6 +597,249 @@ forking(87632849, async () => { expect(positionAfter.isActive).to.eq(false); }); + it("closeWithProfitAndDeactivate", async function () { + const [, , , , , , , , profitDeactUser] = await ethers.getSigners(); + const INITIAL_PRINCIPAL = ethers.utils.parseEther("9000"); + const SHORT_AMOUNT = ethers.utils.parseEther("4"); // 4 ETH + const LONG_AMOUNT = ethers.utils.parseEther("30"); // 30 WBNB + const leverage = ethers.utils.parseEther("1.5"); + + // Fund user with USDC (DSA) + const whaleSigner = await initMainnetUser(DSA_WHALE, ethers.utils.parseEther("1")); + await dsa.connect(whaleSigner).transfer(profitDeactUser.address, INITIAL_PRINCIPAL); + await dsa.connect(profitDeactUser).approve(RELATIVE_POSITION_MANAGER, INITIAL_PRINCIPAL); + + // Open position with manipulated swap (SHORT -> LONG) + const minLong = LONG_AMOUNT.mul(98).div(100); + const openSwapData = await getManipulatedSwapData( + SHORT_ADDRESS, + LONG_ADDRESS, + SHORT_AMOUNT, + LONG_AMOUNT, + LEVERAGE_STRATEGIES_MANAGER_ADDRESS, + vLONG_ADDRESS, + ); + + await relativePositionManager + .connect(profitDeactUser) + .activateAndOpenPosition( + vLONG_ADDRESS, + vSHORT_ADDRESS, + 0, + INITIAL_PRINCIPAL, + leverage, + SHORT_AMOUNT, + minLong, + openSwapData, + ); + + // Verify position is active + const position = await relativePositionManager.getPosition( + profitDeactUser.address, + vLONG_ADDRESS, + vSHORT_ADDRESS, + ); + expect(position.isActive).to.eq(true); + const positionAccount = position.positionAccount; + + // Record state after open + const shortDebtAfterOpen = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); + const longBalanceAfterOpen = await relativePositionManager.callStatic.getLongCollateralBalance( + profitDeactUser.address, + vLONG_ADDRESS, + vSHORT_ADDRESS, + ); + + expect(shortDebtAfterOpen).to.be.gt(0, "Should have short debt"); + expect(longBalanceAfterOpen).to.be.gt(0, "Should have long collateral"); + + // Close with profit: favorable price — use 75% of long to repay all debt, 25% is profit + const longForRepay = longBalanceAfterOpen.mul(75).div(100); + const longForProfit = longBalanceAfterOpen.sub(longForRepay); + + const shortRepayAmount = shortDebtAfterOpen.mul(FULL_CLOSE_BUFFER_BPS).div(1000); + + const repaySwapData = await getManipulatedSwapData( + LONG_ADDRESS, + SHORT_ADDRESS, + longForRepay, + shortRepayAmount, + LEVERAGE_STRATEGIES_MANAGER_ADDRESS, + ); + + // Profit: LONG -> DSA (goes to RPM, then supplied as principal) + const estimatedProfitDsa = longForProfit.mul(500); // ~7.5 WBNB * 500 USDC/WBNB ≈ 3750 USDC + const profitSwapData = await getManipulatedSwapData( + LONG_ADDRESS, + DSA_ADDRESS, + longForProfit, + estimatedProfitDsa, + RELATIVE_POSITION_MANAGER, // RPM receives profit, supplies as principal + ); + + const dsaBalanceBefore = await dsa.balanceOf(profitDeactUser.address); + + // Execute atomic close + deactivate + await relativePositionManager.connect(profitDeactUser).closeWithProfitAndDeactivate( + vLONG_ADDRESS, + vSHORT_ADDRESS, + { + longAmountToRedeemForRepay: longForRepay, + minAmountOutRepay: shortRepayAmount, + swapDataRepay: repaySwapData, + longAmountToRedeemForProfit: longForProfit, + minAmountOutProfit: estimatedProfitDsa.mul(98).div(100), + swapDataProfit: profitSwapData, + }, + ); + + // Verify full close + const shortDebtAfterClose = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); + const longBalanceAfterClose = await relativePositionManager.callStatic.getLongCollateralBalance( + profitDeactUser.address, + vLONG_ADDRESS, + vSHORT_ADDRESS, + ); + + expect(shortDebtAfterClose).to.eq(0, "All debt repaid"); + expect(longBalanceAfterClose).to.eq(0, "All long redeemed"); + + // Position should be deactivated and principal returned to user + const positionAfterClose = await relativePositionManager.getPosition( + profitDeactUser.address, + vLONG_ADDRESS, + vSHORT_ADDRESS, + ); + expect(positionAfterClose.isActive).to.eq(false, "Position should be deactivated"); + + // All position account balances should be zeroed + expect(await dsaVToken.callStatic.balanceOfUnderlying(positionAccount)).to.eq(0, "DSA balance zeroed"); + + // User should have received principal + profit + const dsaBalanceAfter = await dsa.balanceOf(profitDeactUser.address); + expect(dsaBalanceAfter).to.be.gt(dsaBalanceBefore, "User received DSA tokens back"); + }); + + it("closeWithLossAndDeactivate", async function () { + const [, , , , , , , , , lossDeactUser] = await ethers.getSigners(); + const INITIAL_PRINCIPAL = ethers.utils.parseEther("15000"); + const SHORT_AMOUNT = ethers.utils.parseEther("3"); // 3 ETH + const LONG_AMOUNT = ethers.utils.parseEther("25"); // 25 WBNB + const leverage = ethers.utils.parseEther("3"); + + // Fund user with USDC (DSA) + const whaleSigner = await initMainnetUser(DSA_WHALE, ethers.utils.parseEther("1")); + await dsa.connect(whaleSigner).transfer(lossDeactUser.address, INITIAL_PRINCIPAL); + await dsa.connect(lossDeactUser).approve(RELATIVE_POSITION_MANAGER, INITIAL_PRINCIPAL); + + // Open position + const minLong = LONG_AMOUNT.mul(98).div(100); + const openSwapData = await getManipulatedSwapData( + SHORT_ADDRESS, + LONG_ADDRESS, + SHORT_AMOUNT, + LONG_AMOUNT, + LEVERAGE_STRATEGIES_MANAGER_ADDRESS, + vLONG_ADDRESS, + ); + + await relativePositionManager + .connect(lossDeactUser) + .activateAndOpenPosition( + vLONG_ADDRESS, + vSHORT_ADDRESS, + 0, + INITIAL_PRINCIPAL, + leverage, + SHORT_AMOUNT, + minLong, + openSwapData, + ); + + const position = await relativePositionManager.getPosition( + lossDeactUser.address, + vLONG_ADDRESS, + vSHORT_ADDRESS, + ); + expect(position.isActive).to.eq(true); + const positionAccount = position.positionAccount; + + const shortDebtAfterOpen = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); + const longBalanceAfterOpen = await relativePositionManager.callStatic.getLongCollateralBalance( + lossDeactUser.address, + vLONG_ADDRESS, + vSHORT_ADDRESS, + ); + + // Simulate 20% loss: swapping all LONG only gets 80% of SHORT needed + const shortAmountFromLongSwap = shortDebtAfterOpen.mul(80).div(100); + const shortfall = shortDebtAfterOpen.sub(shortAmountFromLongSwap); + + // First swap: LONG -> SHORT (all WBNB -> ETH, but 20% short) + const firstSwapData = await getManipulatedSwapData( + LONG_ADDRESS, + SHORT_ADDRESS, + longBalanceAfterOpen, + shortAmountFromLongSwap, + LEVERAGE_STRATEGIES_MANAGER_ADDRESS, + ); + + // Second swap: DSA -> SHORT (25% of USDC principal -> ETH to cover shortfall) + const dsaAmountToSwap = INITIAL_PRINCIPAL.mul(25).div(100); // 3750 USDC + const shortFromDsaSwap = shortfall.mul(FULL_CLOSE_BUFFER_BPS).div(1000); + + const secondSwapData = await getManipulatedSwapData( + DSA_ADDRESS, + SHORT_ADDRESS, + dsaAmountToSwap, + shortFromDsaSwap, + LEVERAGE_STRATEGIES_MANAGER_ADDRESS, + ); + + const dsaBalanceBefore = await dsa.balanceOf(lossDeactUser.address); + + // Execute atomic close + deactivate + await relativePositionManager.connect(lossDeactUser).closeWithLossAndDeactivate( + vLONG_ADDRESS, + vSHORT_ADDRESS, + { + longAmountToRedeemForFirstSwap: longBalanceAfterOpen, + shortAmountToRepayForFirstSwap: shortAmountFromLongSwap, + minAmountOutFirst: shortAmountFromLongSwap, + swapDataFirst: firstSwapData, + dsaAmountToRedeemForSecondSwap: dsaAmountToSwap, + minAmountOutSecond: shortFromDsaSwap, + swapDataSecond: secondSwapData, + }, + ); + + // Verify full close + const shortDebtAfterClose = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); + const longBalanceAfterClose = await relativePositionManager.callStatic.getLongCollateralBalance( + lossDeactUser.address, + vLONG_ADDRESS, + vSHORT_ADDRESS, + ); + + expect(shortDebtAfterClose).to.eq(0, "All debt repaid"); + expect(longBalanceAfterClose).to.eq(0, "All long redeemed"); + + // Position should be deactivated and remaining principal returned to user + const positionAfterClose = await relativePositionManager.getPosition( + lossDeactUser.address, + vLONG_ADDRESS, + vSHORT_ADDRESS, + ); + expect(positionAfterClose.isActive).to.eq(false, "Position should be deactivated"); + + // All position account balances should be zeroed + expect(await dsaVToken.callStatic.balanceOfUnderlying(positionAccount)).to.eq(0, "DSA balance zeroed"); + + // User should have received remaining principal (reduced by loss) + const dsaBalanceAfter = await dsa.balanceOf(lossDeactUser.address); + expect(dsaBalanceAfter).to.be.gt(dsaBalanceBefore, "User received remaining DSA tokens back"); + }); + it("scale position (increase leverage)", async function () { const [, , , , , scaleUser] = await ethers.getSigners(); const INITIAL_PRINCIPAL = ethers.utils.parseEther("10000"); diff --git a/vips/vip-610/bscmainnet.ts b/vips/vip-610/bscmainnet.ts index 109782c4f..3e4c9fc65 100644 --- a/vips/vip-610/bscmainnet.ts +++ b/vips/vip-610/bscmainnet.ts @@ -3,8 +3,8 @@ import { ProposalType } from "src/types"; import { makeProposal } from "src/utils"; const { bscmainnet } = NETWORK_ADDRESSES; -export const RELATIVE_POSITION_MANAGER = "0xedcD8725D08585A7B61eE77A22D9cf591C1171c1"; -export const POSITION_ACCOUNT = "0x18970e10B39BDf6981334b5DC0873d85CFdB9aa0"; +export const RELATIVE_POSITION_MANAGER = "0x1525D804DFff218DcC8B9359940F423209356C42"; +export const POSITION_ACCOUNT = "0xa75C5b438226bc73BDCc83408E7Aa41771b33E2C"; export const vUSDC = "0xecA88125a5ADbe82614ffC12D0DB554E2e2867C8"; export const vUSDT = "0xfD5840Cd36d94D7229439859C0112a4185BC0255"; From 8da12c354d19f0946cd8b9be0fdeda8dc3571ca2 Mon Sep 17 00:00:00 2001 From: GitGuru7 Date: Fri, 27 Mar 2026 15:14:07 +0530 Subject: [PATCH 10/13] chore: run prettier and use treasury for whale --- simulations/vip-610/abi/PositionAccount.json | 439 ++ .../vip-610/abi/RelativePositionManager.json | 4022 ++++++++--------- simulations/vip-610/bscmainnet.ts | 46 +- 3 files changed, 2469 insertions(+), 2038 deletions(-) create mode 100644 simulations/vip-610/abi/PositionAccount.json diff --git a/simulations/vip-610/abi/PositionAccount.json b/simulations/vip-610/abi/PositionAccount.json new file mode 100644 index 000000000..ef2b02033 --- /dev/null +++ b/simulations/vip-610/abi/PositionAccount.json @@ -0,0 +1,439 @@ +[ + { + "inputs": [ + { + "internalType": "contract IComptroller", + "name": "comptroller_", + "type": "address" + }, + { + "internalType": "address", + "name": "relativePositionManager_", + "type": "address" + }, + { + "internalType": "address", + "name": "leverageManager_", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "err", + "type": "uint256" + } + ], + "name": "ExitMarketFailed", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidCallsLength", + "type": "error" + }, + { + "inputs": [], + "name": "UnauthorizedCaller", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroAddress", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "DustTransferredToOwner", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "collateralMarket", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "borrowedMarket", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "collateralAmountSeed", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "borrowedAmount", + "type": "uint256" + } + ], + "name": "EnterLeverageForwarded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "collateralMarket", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "borrowedMarket", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "collateralAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "borrowedAmount", + "type": "uint256" + } + ], + "name": "ExitLeverageForwarded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "market", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ExitSingleAssetLeverageForwarded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "GenericCallExecuted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "vToken", + "type": "address" + } + ], + "name": "MarketExited", + "type": "event" + }, + { + "inputs": [], + "name": "COMPTROLLER", + "outputs": [ + { + "internalType": "contract IComptroller", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LEVERAGE_MANAGER", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "RELATIVE_POSITION_MANAGER", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "collateralMarket", + "type": "address" + }, + { + "internalType": "uint256", + "name": "collateralAmountSeed", + "type": "uint256" + }, + { + "internalType": "contract IVToken", + "name": "borrowedMarket", + "type": "address" + }, + { + "internalType": "uint256", + "name": "borrowedAmountToFlashLoan", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOutAfterSwap", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapData", + "type": "bytes" + } + ], + "name": "enterLeverage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "collateralMarket", + "type": "address" + }, + { + "internalType": "uint256", + "name": "collateralAmountToRedeemForSwap", + "type": "uint256" + }, + { + "internalType": "contract IVToken", + "name": "borrowedMarket", + "type": "address" + }, + { + "internalType": "uint256", + "name": "borrowedAmountToFlashLoan", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOutAfterSwap", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapData", + "type": "bytes" + } + ], + "name": "exitLeverage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vTokenToExit", + "type": "address" + } + ], + "name": "exitMarket", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "collateralMarket", + "type": "address" + }, + { + "internalType": "uint256", + "name": "collateralAmountToFlashLoan", + "type": "uint256" + } + ], + "name": "exitSingleAssetLeverage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "targets", + "type": "address[]" + }, + { + "internalType": "bytes[]", + "name": "data", + "type": "bytes[]" + } + ], + "name": "genericCalls", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner_", + "type": "address" + }, + { + "internalType": "address", + "name": "longVToken_", + "type": "address" + }, + { + "internalType": "address", + "name": "shortVToken_", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "longVToken", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "shortVToken", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "transferDustToOwner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/simulations/vip-610/abi/RelativePositionManager.json b/simulations/vip-610/abi/RelativePositionManager.json index e995791c6..f391e34d9 100644 --- a/simulations/vip-610/abi/RelativePositionManager.json +++ b/simulations/vip-610/abi/RelativePositionManager.json @@ -1,2012 +1,2012 @@ [ - { - "inputs": [ - { - "internalType": "address", - "name": "comptroller", - "type": "address" - }, - { - "internalType": "address", - "name": "leverageManager", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [], - "name": "AssetNotListed", - "type": "error" - }, - { - "inputs": [], - "name": "BorrowAmountExceedsMaximum", - "type": "error" - }, - { - "inputs": [], - "name": "CompletelyPaused", - "type": "error" - }, - { - "inputs": [], - "name": "DSAInactive", - "type": "error" - }, - { - "inputs": [], - "name": "DSAVTokenAlreadyAdded", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "errorCode", - "type": "uint256" - } - ], - "name": "EnterMarketFailed", - "type": "error" - }, - { - "inputs": [], - "name": "ExcessiveShortDust", - "type": "error" - }, - { - "inputs": [], - "name": "InsufficientPrincipal", - "type": "error" - }, - { - "inputs": [], - "name": "InsufficientWithdrawableAmount", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidCloseFractionBps", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidCollateralFactor", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidDSA", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidLeverage", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidLongAmountToRedeem", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidOraclePrice", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidProportionalCloseTolerance", - "type": "error" - }, - { - "inputs": [], - "name": "MinAmountOutRepayBelowDebt", - "type": "error" - }, - { - "inputs": [], - "name": "MinAmountOutSecondBelowDebt", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "errorCode", - "type": "uint256" - } - ], - "name": "MintBehalfFailed", - "type": "error" - }, - { - "inputs": [], - "name": "PartiallyPaused", - "type": "error" - }, - { - "inputs": [], - "name": "PositionAccountImplementationLocked", - "type": "error" - }, - { - "inputs": [], - "name": "PositionAccountImplementationNotSet", - "type": "error" - }, - { - "inputs": [], - "name": "PositionAlreadyExists", - "type": "error" - }, - { - "inputs": [], - "name": "PositionNotActive", - "type": "error" - }, - { - "inputs": [], - "name": "PositionNotFullyClosed", - "type": "error" - }, - { - "inputs": [], - "name": "ProportionalCloseAmountOutOfTolerance", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "errorCode", - "type": "uint256" - } - ], - "name": "RedeemBehalfFailed", - "type": "error" - }, - { - "inputs": [], - "name": "SameDSAActiveStatus", - "type": "error" - }, - { - "inputs": [], - "name": "SameMarketNotAllowed", - "type": "error" - }, - { - "inputs": [], - "name": "SameProportionalCloseTolerance", - "type": "error" - }, - { - "inputs": [], - "name": "SlippageExceeded", - "type": "error" - }, - { - "inputs": [], - "name": "TokenSwapCallFailed", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address", - "name": "calledContract", - "type": "address" - }, - { - "internalType": "string", - "name": "methodSignature", - "type": "string" - } - ], - "name": "Unauthorized", - "type": "error" - }, - { - "inputs": [], - "name": "VBNBNotSupported", - "type": "error" - }, - { - "inputs": [], - "name": "ZeroAddress", - "type": "error" - }, - { - "inputs": [], - "name": "ZeroAmount", - "type": "error" - }, - { - "inputs": [], - "name": "ZeroDebt", - "type": "error" - }, - { - "inputs": [], - "name": "ZeroShortAmount", - "type": "error" - }, - { - "inputs": [], - "name": "ZeroVTokensMinted", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "bool", - "name": "paused", - "type": "bool" - } - ], - "name": "CompletePauseToggled", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "dsaVToken", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint8", - "name": "index", - "type": "uint8" - }, - { - "indexed": false, - "internalType": "bool", - "name": "active", - "type": "bool" - } - ], - "name": "DSAVTokenActiveUpdated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "dsaVToken", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint8", - "name": "index", - "type": "uint8" - } - ], - "name": "DSAVTokenAdded", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint8", - "name": "version", - "type": "uint8" - } - ], - "name": "Initialized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "oldAccessControlManager", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "newAccessControlManager", - "type": "address" - } - ], - "name": "NewAccessControlManager", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferStarted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferred", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "bool", - "name": "paused", - "type": "bool" - } - ], - "name": "PartialPauseToggled", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "longAsset", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "shortAsset", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "positionAccount", - "type": "address" - } - ], - "name": "PositionAccountDeployed", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "implementation", - "type": "address" - } - ], - "name": "PositionAccountImplementationSet", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "longAsset", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "shortAsset", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "dsaAsset", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "cycleId", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "initialPrincipal", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "effectiveLeverage", - "type": "uint256" - } - ], - "name": "PositionActivated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "cycleId", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "closeFractionBps", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amountRepaid", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amountRedeemed", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amountRedeemedDsa", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "longDustRedeemed", - "type": "uint256" - } - ], - "name": "PositionClosed", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "cycleId", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "longRedeemed", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "dsaRedeemed", - "type": "uint256" - } - ], - "name": "PositionDeactivated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "cycleId", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "address", - "name": "longAsset", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "shortAsset", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "dsaAsset", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "shortAmount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "initialPrincipal", - "type": "uint256" - } - ], - "name": "PositionOpened", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "cycleId", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "address", - "name": "longAsset", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "shortAsset", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "dsaAsset", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "shortAmount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "additionalPrincipal", - "type": "uint256" - } - ], - "name": "PositionScaled", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "cycleId", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "address", - "name": "dsaAsset", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "newTotalPrincipal", - "type": "uint256" - } - ], - "name": "PrincipalSupplied", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "cycleId", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "address", - "name": "dsaAsset", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "remainingPrincipal", - "type": "uint256" - } - ], - "name": "PrincipalWithdrawn", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amountConvertedToProfit", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "newTotalPrincipal", - "type": "uint256" - } - ], - "name": "ProfitConverted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint256", - "name": "oldTolerance", - "type": "uint256" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "newTolerance", - "type": "uint256" - } - ], - "name": "ProportionalCloseToleranceUpdated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "oldSuppliedPrincipal", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "newSuppliedPrincipal", - "type": "uint256" - } - ], - "name": "RefreshedSuppliedPrincipal", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "UnderlyingTransferred", - "type": "event" - }, - { - "inputs": [], - "name": "COMPTROLLER", - "outputs": [ - { - "internalType": "contract IComptroller", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "LEVERAGE_MANAGER", - "outputs": [ - { - "internalType": "contract LeverageStrategiesManager", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "MIN_LEVERAGE", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "POSITION_ACCOUNT_IMPLEMENTATION", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "PROPORTIONAL_CLOSE_MAX", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "PROPORTIONAL_CLOSE_MIN", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "acceptOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "accessControlManager", - "outputs": [ - { - "internalType": "contract IAccessControlManagerV8", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "address", - "name": "shortVToken", - "type": "address" - }, - { - "internalType": "uint8", - "name": "dsaIndex", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "initialPrincipal", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "effectiveLeverage", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "shortAmount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minLongAmount", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "swapData", - "type": "bytes" - } - ], - "name": "activateAndOpenPosition", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "dsaVToken", - "type": "address" - } - ], - "name": "addDSAVToken", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - }, - { - "internalType": "uint256", - "name": "closeFractionBps", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "longAmountToRedeemForFirstSwap", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "shortAmountToRepayForFirstSwap", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minAmountOutFirst", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "swapDataFirst", - "type": "bytes" - }, - { - "internalType": "uint256", - "name": "dsaAmountToRedeemForSecondSwap", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minAmountOutSecond", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "swapDataSecond", - "type": "bytes" - } - ], - "name": "closeWithLoss", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "longAmountToRedeemForFirstSwap", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "shortAmountToRepayForFirstSwap", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minAmountOutFirst", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "swapDataFirst", - "type": "bytes" - }, - { - "internalType": "uint256", - "name": "dsaAmountToRedeemForSecondSwap", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minAmountOutSecond", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "swapDataSecond", - "type": "bytes" - } - ], - "internalType": "struct IRelativePositionManager.CloseWithLossParams", - "name": "lossSwapParams", - "type": "tuple" - } - ], - "name": "closeWithLossAndDeactivate", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - }, - { - "internalType": "uint256", - "name": "closeFractionBps", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "longAmountToRedeemForRepay", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minAmountOutRepay", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "swapDataRepay", - "type": "bytes" - }, - { - "internalType": "uint256", - "name": "longAmountToRedeemForProfit", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minAmountOutProfit", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "swapDataProfit", - "type": "bytes" - } - ], - "name": "closeWithProfit", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "longAmountToRedeemForRepay", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minAmountOutRepay", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "swapDataRepay", - "type": "bytes" - }, - { - "internalType": "uint256", - "name": "longAmountToRedeemForProfit", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minAmountOutProfit", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "swapDataProfit", - "type": "bytes" - } - ], - "internalType": "struct IRelativePositionManager.CloseWithProfitParams", - "name": "profitSwapParams", - "type": "tuple" - } - ], - "name": "closeWithProfitAndDeactivate", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "completePause", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "completeUnpause", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - } - ], - "name": "deactivatePosition", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "dsaVTokenIndexCounter", - "outputs": [ - { - "internalType": "uint8", - "name": "", - "type": "uint8" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint8", - "name": "", - "type": "uint8" - } - ], - "name": "dsaVTokens", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "internalType": "address[]", - "name": "targets", - "type": "address[]" - }, - { - "internalType": "bytes[]", - "name": "data", - "type": "bytes[]" - } - ], - "name": "executePositionAccountCall", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - } - ], - "name": "getAvailableShortCapacity", - "outputs": [ - { - "internalType": "uint256", - "name": "availableCapacity", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "getDsaVTokens", - "outputs": [ - { - "internalType": "address[]", - "name": "dsaVTokensList", - "type": "address[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - } - ], - "name": "getLongCollateralBalance", - "outputs": [ - { - "internalType": "uint256", - "name": "longBalance", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IVToken", - "name": "dsaVToken", - "type": "address" - }, - { - "internalType": "address", - "name": "longVToken", - "type": "address" - } - ], - "name": "getMaxLeverageAllowed", - "outputs": [ - { - "internalType": "uint256", - "name": "maxLeverage", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - } - ], - "name": "getPosition", - "outputs": [ - { - "components": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "address", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "address", - "name": "shortVToken", - "type": "address" - }, - { - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "internalType": "bool", - "name": "isActive", - "type": "bool" - }, - { - "internalType": "uint8", - "name": "dsaIndex", - "type": "uint8" - }, - { - "internalType": "address", - "name": "dsaVToken", - "type": "address" - }, - { - "internalType": "uint256", - "name": "suppliedPrincipalVTokens", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "effectiveLeverage", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "cycleId", - "type": "uint256" - } - ], - "internalType": "struct IRelativePositionManager.Position", - "name": "position", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - } - ], - "name": "getPositionAccountAddress", - "outputs": [ - { - "internalType": "address", - "name": "predicted", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - } - ], - "name": "getSuppliedPrincipalBalance", - "outputs": [ - { - "internalType": "uint256", - "name": "balance", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - } - ], - "name": "getUtilizationInfo", - "outputs": [ - { - "components": [ - { - "internalType": "uint256", - "name": "actualCapitalUtilized", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "nominalCapitalUtilized", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "finalCapitalUtilized", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "availableCapitalUSD", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "withdrawableAmount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "clampedLeverage", - "type": "uint256" - } - ], - "internalType": "struct IRelativePositionManager.UtilizationInfo", - "name": "utilization", - "type": "tuple" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "accessControlManager_", - "type": "address" - } - ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "isCompletelyPaused", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "isDsaVTokenActive", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "isPartiallyPaused", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "isPositionAccountImplementationLocked", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "owner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "partialPause", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "partialUnpause", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "pendingOwner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - }, - { - "internalType": "address", - "name": "", - "type": "address" - }, - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "positions", - "outputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "address", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "address", - "name": "shortVToken", - "type": "address" - }, - { - "internalType": "address", - "name": "positionAccount", - "type": "address" - }, - { - "internalType": "bool", - "name": "isActive", - "type": "bool" - }, - { - "internalType": "uint8", - "name": "dsaIndex", - "type": "uint8" - }, - { - "internalType": "address", - "name": "dsaVToken", - "type": "address" - }, - { - "internalType": "uint256", - "name": "suppliedPrincipalVTokens", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "effectiveLeverage", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "cycleId", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "proportionalCloseTolerance", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "renounceOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - }, - { - "internalType": "uint256", - "name": "additionalPrincipal", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "shortAmount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minLongAmount", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "swapData", - "type": "bytes" - } - ], - "name": "scalePosition", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "accessControlManager_", - "type": "address" - } - ], - "name": "setAccessControlManager", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint8", - "name": "dsaIndex", - "type": "uint8" - }, - { - "internalType": "bool", - "name": "active", - "type": "bool" - } - ], - "name": "setDSAVTokenActive", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "positionAccountImpl", - "type": "address" - } - ], - "name": "setPositionAccountImplementation", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "newTolerance", - "type": "uint256" - } - ], - "name": "setProportionalCloseTolerance", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "address", - "name": "shortVToken", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "supplyPrincipal", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "transferOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IVToken", - "name": "longVToken", - "type": "address" - }, - { - "internalType": "contract IVToken", - "name": "shortVToken", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "withdrawPrincipal", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } - ] \ No newline at end of file + { + "inputs": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "address", + "name": "leverageManager", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "AssetNotListed", + "type": "error" + }, + { + "inputs": [], + "name": "BorrowAmountExceedsMaximum", + "type": "error" + }, + { + "inputs": [], + "name": "CompletelyPaused", + "type": "error" + }, + { + "inputs": [], + "name": "DSAInactive", + "type": "error" + }, + { + "inputs": [], + "name": "DSAVTokenAlreadyAdded", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "errorCode", + "type": "uint256" + } + ], + "name": "EnterMarketFailed", + "type": "error" + }, + { + "inputs": [], + "name": "ExcessiveShortDust", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientPrincipal", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientWithdrawableAmount", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidCloseFractionBps", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidCollateralFactor", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidDSA", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidLeverage", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidLongAmountToRedeem", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidOraclePrice", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidProportionalCloseTolerance", + "type": "error" + }, + { + "inputs": [], + "name": "MinAmountOutRepayBelowDebt", + "type": "error" + }, + { + "inputs": [], + "name": "MinAmountOutSecondBelowDebt", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "errorCode", + "type": "uint256" + } + ], + "name": "MintBehalfFailed", + "type": "error" + }, + { + "inputs": [], + "name": "PartiallyPaused", + "type": "error" + }, + { + "inputs": [], + "name": "PositionAccountImplementationLocked", + "type": "error" + }, + { + "inputs": [], + "name": "PositionAccountImplementationNotSet", + "type": "error" + }, + { + "inputs": [], + "name": "PositionAlreadyExists", + "type": "error" + }, + { + "inputs": [], + "name": "PositionNotActive", + "type": "error" + }, + { + "inputs": [], + "name": "PositionNotFullyClosed", + "type": "error" + }, + { + "inputs": [], + "name": "ProportionalCloseAmountOutOfTolerance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "errorCode", + "type": "uint256" + } + ], + "name": "RedeemBehalfFailed", + "type": "error" + }, + { + "inputs": [], + "name": "SameDSAActiveStatus", + "type": "error" + }, + { + "inputs": [], + "name": "SameMarketNotAllowed", + "type": "error" + }, + { + "inputs": [], + "name": "SameProportionalCloseTolerance", + "type": "error" + }, + { + "inputs": [], + "name": "SlippageExceeded", + "type": "error" + }, + { + "inputs": [], + "name": "TokenSwapCallFailed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "calledContract", + "type": "address" + }, + { + "internalType": "string", + "name": "methodSignature", + "type": "string" + } + ], + "name": "Unauthorized", + "type": "error" + }, + { + "inputs": [], + "name": "VBNBNotSupported", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroAmount", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroDebt", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroShortAmount", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroVTokensMinted", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "paused", + "type": "bool" + } + ], + "name": "CompletePauseToggled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "dsaVToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "index", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "bool", + "name": "active", + "type": "bool" + } + ], + "name": "DSAVTokenActiveUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "dsaVToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "index", + "type": "uint8" + } + ], + "name": "DSAVTokenAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "oldAccessControlManager", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAccessControlManager", + "type": "address" + } + ], + "name": "NewAccessControlManager", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "paused", + "type": "bool" + } + ], + "name": "PartialPauseToggled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "longAsset", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "shortAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "positionAccount", + "type": "address" + } + ], + "name": "PositionAccountDeployed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "PositionAccountImplementationSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "longAsset", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "shortAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "dsaAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "cycleId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "initialPrincipal", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "effectiveLeverage", + "type": "uint256" + } + ], + "name": "PositionActivated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "cycleId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "closeFractionBps", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountRepaid", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountRedeemed", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountRedeemedDsa", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "longDustRedeemed", + "type": "uint256" + } + ], + "name": "PositionClosed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "cycleId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "longRedeemed", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "dsaRedeemed", + "type": "uint256" + } + ], + "name": "PositionDeactivated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "cycleId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "longAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "shortAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "dsaAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "shortAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "initialPrincipal", + "type": "uint256" + } + ], + "name": "PositionOpened", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "cycleId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "longAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "shortAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "dsaAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "shortAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "additionalPrincipal", + "type": "uint256" + } + ], + "name": "PositionScaled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "cycleId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "dsaAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newTotalPrincipal", + "type": "uint256" + } + ], + "name": "PrincipalSupplied", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "cycleId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "dsaAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "remainingPrincipal", + "type": "uint256" + } + ], + "name": "PrincipalWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountConvertedToProfit", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newTotalPrincipal", + "type": "uint256" + } + ], + "name": "ProfitConverted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "oldTolerance", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "newTolerance", + "type": "uint256" + } + ], + "name": "ProportionalCloseToleranceUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "oldSuppliedPrincipal", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newSuppliedPrincipal", + "type": "uint256" + } + ], + "name": "RefreshedSuppliedPrincipal", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "UnderlyingTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "COMPTROLLER", + "outputs": [ + { + "internalType": "contract IComptroller", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LEVERAGE_MANAGER", + "outputs": [ + { + "internalType": "contract LeverageStrategiesManager", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MIN_LEVERAGE", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "POSITION_ACCOUNT_IMPLEMENTATION", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PROPORTIONAL_CLOSE_MAX", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PROPORTIONAL_CLOSE_MIN", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "accessControlManager", + "outputs": [ + { + "internalType": "contract IAccessControlManagerV8", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "address", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "uint8", + "name": "dsaIndex", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "initialPrincipal", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "effectiveLeverage", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minLongAmount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapData", + "type": "bytes" + } + ], + "name": "activateAndOpenPosition", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "dsaVToken", + "type": "address" + } + ], + "name": "addDSAVToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "closeFractionBps", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "longAmountToRedeemForFirstSwap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortAmountToRepayForFirstSwap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOutFirst", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapDataFirst", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "dsaAmountToRedeemForSecondSwap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOutSecond", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapDataSecond", + "type": "bytes" + } + ], + "name": "closeWithLoss", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "longAmountToRedeemForFirstSwap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortAmountToRepayForFirstSwap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOutFirst", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapDataFirst", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "dsaAmountToRedeemForSecondSwap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOutSecond", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapDataSecond", + "type": "bytes" + } + ], + "internalType": "struct IRelativePositionManager.CloseWithLossParams", + "name": "lossSwapParams", + "type": "tuple" + } + ], + "name": "closeWithLossAndDeactivate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "closeFractionBps", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "longAmountToRedeemForRepay", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOutRepay", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapDataRepay", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "longAmountToRedeemForProfit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOutProfit", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapDataProfit", + "type": "bytes" + } + ], + "name": "closeWithProfit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "longAmountToRedeemForRepay", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOutRepay", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapDataRepay", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "longAmountToRedeemForProfit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minAmountOutProfit", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapDataProfit", + "type": "bytes" + } + ], + "internalType": "struct IRelativePositionManager.CloseWithProfitParams", + "name": "profitSwapParams", + "type": "tuple" + } + ], + "name": "closeWithProfitAndDeactivate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "completePause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "completeUnpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + } + ], + "name": "deactivatePosition", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "dsaVTokenIndexCounter", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "name": "dsaVTokens", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "internalType": "address[]", + "name": "targets", + "type": "address[]" + }, + { + "internalType": "bytes[]", + "name": "data", + "type": "bytes[]" + } + ], + "name": "executePositionAccountCall", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + } + ], + "name": "getAvailableShortCapacity", + "outputs": [ + { + "internalType": "uint256", + "name": "availableCapacity", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getDsaVTokens", + "outputs": [ + { + "internalType": "address[]", + "name": "dsaVTokensList", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + } + ], + "name": "getLongCollateralBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "longBalance", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "dsaVToken", + "type": "address" + }, + { + "internalType": "address", + "name": "longVToken", + "type": "address" + } + ], + "name": "getMaxLeverageAllowed", + "outputs": [ + { + "internalType": "uint256", + "name": "maxLeverage", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + } + ], + "name": "getPosition", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "address", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "address", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "internalType": "bool", + "name": "isActive", + "type": "bool" + }, + { + "internalType": "uint8", + "name": "dsaIndex", + "type": "uint8" + }, + { + "internalType": "address", + "name": "dsaVToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "suppliedPrincipalVTokens", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "effectiveLeverage", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "cycleId", + "type": "uint256" + } + ], + "internalType": "struct IRelativePositionManager.Position", + "name": "position", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + } + ], + "name": "getPositionAccountAddress", + "outputs": [ + { + "internalType": "address", + "name": "predicted", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + } + ], + "name": "getSuppliedPrincipalBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + } + ], + "name": "getUtilizationInfo", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "actualCapitalUtilized", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nominalCapitalUtilized", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "finalCapitalUtilized", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "availableCapitalUSD", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "withdrawableAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "clampedLeverage", + "type": "uint256" + } + ], + "internalType": "struct IRelativePositionManager.UtilizationInfo", + "name": "utilization", + "type": "tuple" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "accessControlManager_", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "isCompletelyPaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isDsaVTokenActive", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isPartiallyPaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isPositionAccountImplementationLocked", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "partialPause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "partialUnpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "positions", + "outputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "address", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "address", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "address", + "name": "positionAccount", + "type": "address" + }, + { + "internalType": "bool", + "name": "isActive", + "type": "bool" + }, + { + "internalType": "uint8", + "name": "dsaIndex", + "type": "uint8" + }, + { + "internalType": "address", + "name": "dsaVToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "suppliedPrincipalVTokens", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "effectiveLeverage", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "cycleId", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proportionalCloseTolerance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "additionalPrincipal", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minLongAmount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "swapData", + "type": "bytes" + } + ], + "name": "scalePosition", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "accessControlManager_", + "type": "address" + } + ], + "name": "setAccessControlManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "dsaIndex", + "type": "uint8" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + } + ], + "name": "setDSAVTokenActive", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "positionAccountImpl", + "type": "address" + } + ], + "name": "setPositionAccountImplementation", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newTolerance", + "type": "uint256" + } + ], + "name": "setProportionalCloseTolerance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "address", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "supplyPrincipal", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IVToken", + "name": "longVToken", + "type": "address" + }, + { + "internalType": "contract IVToken", + "name": "shortVToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "withdrawPrincipal", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/simulations/vip-610/bscmainnet.ts b/simulations/vip-610/bscmainnet.ts index 1458f031c..e1704829e 100644 --- a/simulations/vip-610/bscmainnet.ts +++ b/simulations/vip-610/bscmainnet.ts @@ -17,6 +17,7 @@ import ACCESS_CONTROL_MANAGER_ABI from "./abi/AccessControlManager.json"; import BINANCE_ORACLE_ABI from "./abi/BinanceOracle.json"; import CHAINLINK_ORACLE_ABI from "./abi/ChainlinkOracle.json"; import FLASHLOAN_FACET_ABI from "./abi/FlashLoanFacet.json"; +import POSITION_ACCOUNT_ABI from "./abi/PositionAccount.json"; import RELATIVE_POSITION_MANAGER_ABI from "./abi/RelativePositionManager.json"; import RESILIENT_ORACLE_ABI from "./abi/ResilientOracle.json"; import SWAP_HELPER_ABI from "./abi/SwapHelperAbi.json"; @@ -165,8 +166,9 @@ forking(89004570, async () => { it("executePositionAccountCall", async () => { // Reverts with InvalidCallsLength (not Unauthorized) — proves ACM permission passed + const positionAccountContract = await ethers.getContractAt(POSITION_ACCOUNT_ABI, POSITION_ACCOUNT); await expect(rpmAsCaller.executePositionAccountCall(POSITION_ACCOUNT, [], [])).to.be.revertedWithCustomError( - relativePositionManager, + positionAccountContract, "InvalidCallsLength", ); }); @@ -195,8 +197,8 @@ forking(89004570, async () => { const SHORT_ADDRESS = "0x2170Ed0880ac9A755fd29B2688956BD959F933F8"; // ETH const vSHORT_ADDRESS = "0xf508fCD89b8bd15579dc79A6827cB4686A3592c8"; // vETH - const DSA_WHALE = DSA_ADDRESS; - const SHORT_WHALE = "0x8894E0a0c962CB723c1976a4421c95949bE2D4E3"; // Binance 8 (for ETH) + const DSA_WHALE = "0xf322942f644a996a617bd29c16bd7d231d9f35e9"; // Venus Treasury + const SHORT_WHALE = "0xf322942f644a996a617bd29c16bd7d231d9f35e9"; // Venus Treasury (for ETH) // Oracles const REDSTONE_ORACLE = "0x8455EFA4D7Ff63b8BFD96AdD889483Ea7d39B70a"; @@ -680,18 +682,16 @@ forking(89004570, async () => { const dsaBalanceBefore = await dsa.balanceOf(profitDeactUser.address); // Execute atomic close + deactivate - await relativePositionManager.connect(profitDeactUser).closeWithProfitAndDeactivate( - vLONG_ADDRESS, - vSHORT_ADDRESS, - { + await relativePositionManager + .connect(profitDeactUser) + .closeWithProfitAndDeactivate(vLONG_ADDRESS, vSHORT_ADDRESS, { longAmountToRedeemForRepay: longForRepay, minAmountOutRepay: shortRepayAmount, swapDataRepay: repaySwapData, longAmountToRedeemForProfit: longForProfit, minAmountOutProfit: estimatedProfitDsa.mul(98).div(100), swapDataProfit: profitSwapData, - }, - ); + }); // Verify full close const shortDebtAfterClose = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); @@ -756,11 +756,7 @@ forking(89004570, async () => { openSwapData, ); - const position = await relativePositionManager.getPosition( - lossDeactUser.address, - vLONG_ADDRESS, - vSHORT_ADDRESS, - ); + const position = await relativePositionManager.getPosition(lossDeactUser.address, vLONG_ADDRESS, vSHORT_ADDRESS); expect(position.isActive).to.eq(true); const positionAccount = position.positionAccount; @@ -799,19 +795,15 @@ forking(89004570, async () => { const dsaBalanceBefore = await dsa.balanceOf(lossDeactUser.address); // Execute atomic close + deactivate - await relativePositionManager.connect(lossDeactUser).closeWithLossAndDeactivate( - vLONG_ADDRESS, - vSHORT_ADDRESS, - { - longAmountToRedeemForFirstSwap: longBalanceAfterOpen, - shortAmountToRepayForFirstSwap: shortAmountFromLongSwap, - minAmountOutFirst: shortAmountFromLongSwap, - swapDataFirst: firstSwapData, - dsaAmountToRedeemForSecondSwap: dsaAmountToSwap, - minAmountOutSecond: shortFromDsaSwap, - swapDataSecond: secondSwapData, - }, - ); + await relativePositionManager.connect(lossDeactUser).closeWithLossAndDeactivate(vLONG_ADDRESS, vSHORT_ADDRESS, { + longAmountToRedeemForFirstSwap: longBalanceAfterOpen, + shortAmountToRepayForFirstSwap: shortAmountFromLongSwap, + minAmountOutFirst: shortAmountFromLongSwap, + swapDataFirst: firstSwapData, + dsaAmountToRedeemForSecondSwap: dsaAmountToSwap, + minAmountOutSecond: shortFromDsaSwap, + swapDataSecond: secondSwapData, + }); // Verify full close const shortDebtAfterClose = await shortVToken.callStatic.borrowBalanceCurrent(positionAccount); From 489a290336108b79839fd040ac1691efb197a7c2 Mon Sep 17 00:00:00 2001 From: Fred Date: Fri, 10 Apr 2026 04:48:01 +0000 Subject: [PATCH 11/13] chore: prepare VIP-608 for proposal --- scripts/data/markets.json | 1 + scripts/data/safePauseTxBuilder.json | 28 ++++ scripts/data/safePauseTxMetadata.json | 9 ++ .../abi/AccessControlManager.json | 0 .../abi/BinanceOracle.json | 0 .../abi/ChainlinkOracle.json | 0 .../abi/FlashLoanFacet.json | 0 .../abi/LeverageStrategiesManager.json | 0 .../abi/PositionAccount.json | 0 .../abi/RelativePositionManager.json | 0 .../abi/ResilientOracle.json | 0 .../abi/SwapHelperAbi.json | 0 .../{vip-610 => vip-608}/abi/VToken.json | 0 .../{vip-610 => vip-608}/bscmainnet.ts | 6 +- .../{vip-610 => vip-608}/bsctestnet.ts | 0 vips/vip-608/bscmainnet.ts | 141 ++++++++++++++++++ vips/{vip-610 => vip-608}/bsctestnet.ts | 0 vips/vip-610/bscmainnet.ts | 75 ---------- 18 files changed, 182 insertions(+), 78 deletions(-) create mode 100644 scripts/data/markets.json create mode 100644 scripts/data/safePauseTxBuilder.json create mode 100644 scripts/data/safePauseTxMetadata.json rename simulations/{vip-610 => vip-608}/abi/AccessControlManager.json (100%) rename simulations/{vip-610 => vip-608}/abi/BinanceOracle.json (100%) rename simulations/{vip-610 => vip-608}/abi/ChainlinkOracle.json (100%) rename simulations/{vip-610 => vip-608}/abi/FlashLoanFacet.json (100%) rename simulations/{vip-610 => vip-608}/abi/LeverageStrategiesManager.json (100%) rename simulations/{vip-610 => vip-608}/abi/PositionAccount.json (100%) rename simulations/{vip-610 => vip-608}/abi/RelativePositionManager.json (100%) rename simulations/{vip-610 => vip-608}/abi/ResilientOracle.json (100%) rename simulations/{vip-610 => vip-608}/abi/SwapHelperAbi.json (100%) rename simulations/{vip-610 => vip-608}/abi/VToken.json (100%) rename simulations/{vip-610 => vip-608}/bscmainnet.ts (99%) rename simulations/{vip-610 => vip-608}/bsctestnet.ts (100%) create mode 100644 vips/vip-608/bscmainnet.ts rename vips/{vip-610 => vip-608}/bsctestnet.ts (100%) delete mode 100644 vips/vip-610/bscmainnet.ts diff --git a/scripts/data/markets.json b/scripts/data/markets.json new file mode 100644 index 000000000..a60de35e7 --- /dev/null +++ b/scripts/data/markets.json @@ -0,0 +1 @@ +["0x86e06EAfa6A1eA631Eab51DE500E3D474933739f"] diff --git a/scripts/data/safePauseTxBuilder.json b/scripts/data/safePauseTxBuilder.json new file mode 100644 index 000000000..cfe86daef --- /dev/null +++ b/scripts/data/safePauseTxBuilder.json @@ -0,0 +1,28 @@ +{ + "version": "1.0", + "chainId": "56", + "createdAt": 1774362755458, + "meta": { + "name": "Transactions Batch", + "description": "Created from @morpho-labs/gnosis-tx-builder", + "txBuilderVersion": "1.10.0", + "createdFromSafeAddress": "0x1C2CAc6ec528c20800B2fe734820D87b581eAA6B", + "createdFromOwnerAddress": "", + "checksum": "0x7fd7634b25c13b9f37d416767afc8768119bc1d7faf337f9caa747f713978a7a" + }, + "transactions": [ + { + "to": "0xfD36E2c2a6789Db23113685031d7F16329158384", + "data": "0x24aaa220000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000086e06eafa6a1ea631eab51de500e3d474933739f00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000", + "value": "0", + "operation": 0 + }, + { + "to": "0xfD36E2c2a6789Db23113685031d7F16329158384", + "data": "0x24aaa220000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000086e06eafa6a1ea631eab51de500e3d474933739f00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", + "value": "0", + "operation": 0 + } + ], + "blockNumber": 88475583 +} \ No newline at end of file diff --git a/scripts/data/safePauseTxMetadata.json b/scripts/data/safePauseTxMetadata.json new file mode 100644 index 000000000..ad2260706 --- /dev/null +++ b/scripts/data/safePauseTxMetadata.json @@ -0,0 +1,9 @@ +{ + "comptroller": "0xfD36E2c2a6789Db23113685031d7F16329158384", + "network": "bscmainnet", + "blockNumber": 88475583, + "createdAt": "2026-03-24T14:32:35.437Z", + "symbols": { + "0x86e06EAfa6A1eA631Eab51DE500E3D474933739f": "vTHE" + } +} \ No newline at end of file diff --git a/simulations/vip-610/abi/AccessControlManager.json b/simulations/vip-608/abi/AccessControlManager.json similarity index 100% rename from simulations/vip-610/abi/AccessControlManager.json rename to simulations/vip-608/abi/AccessControlManager.json diff --git a/simulations/vip-610/abi/BinanceOracle.json b/simulations/vip-608/abi/BinanceOracle.json similarity index 100% rename from simulations/vip-610/abi/BinanceOracle.json rename to simulations/vip-608/abi/BinanceOracle.json diff --git a/simulations/vip-610/abi/ChainlinkOracle.json b/simulations/vip-608/abi/ChainlinkOracle.json similarity index 100% rename from simulations/vip-610/abi/ChainlinkOracle.json rename to simulations/vip-608/abi/ChainlinkOracle.json diff --git a/simulations/vip-610/abi/FlashLoanFacet.json b/simulations/vip-608/abi/FlashLoanFacet.json similarity index 100% rename from simulations/vip-610/abi/FlashLoanFacet.json rename to simulations/vip-608/abi/FlashLoanFacet.json diff --git a/simulations/vip-610/abi/LeverageStrategiesManager.json b/simulations/vip-608/abi/LeverageStrategiesManager.json similarity index 100% rename from simulations/vip-610/abi/LeverageStrategiesManager.json rename to simulations/vip-608/abi/LeverageStrategiesManager.json diff --git a/simulations/vip-610/abi/PositionAccount.json b/simulations/vip-608/abi/PositionAccount.json similarity index 100% rename from simulations/vip-610/abi/PositionAccount.json rename to simulations/vip-608/abi/PositionAccount.json diff --git a/simulations/vip-610/abi/RelativePositionManager.json b/simulations/vip-608/abi/RelativePositionManager.json similarity index 100% rename from simulations/vip-610/abi/RelativePositionManager.json rename to simulations/vip-608/abi/RelativePositionManager.json diff --git a/simulations/vip-610/abi/ResilientOracle.json b/simulations/vip-608/abi/ResilientOracle.json similarity index 100% rename from simulations/vip-610/abi/ResilientOracle.json rename to simulations/vip-608/abi/ResilientOracle.json diff --git a/simulations/vip-610/abi/SwapHelperAbi.json b/simulations/vip-608/abi/SwapHelperAbi.json similarity index 100% rename from simulations/vip-610/abi/SwapHelperAbi.json rename to simulations/vip-608/abi/SwapHelperAbi.json diff --git a/simulations/vip-610/abi/VToken.json b/simulations/vip-608/abi/VToken.json similarity index 100% rename from simulations/vip-610/abi/VToken.json rename to simulations/vip-608/abi/VToken.json diff --git a/simulations/vip-610/bscmainnet.ts b/simulations/vip-608/bscmainnet.ts similarity index 99% rename from simulations/vip-610/bscmainnet.ts rename to simulations/vip-608/bscmainnet.ts index e1704829e..72472e521 100644 --- a/simulations/vip-610/bscmainnet.ts +++ b/simulations/vip-608/bscmainnet.ts @@ -11,8 +11,8 @@ import { TIMELOCKS_AND_GUARDIAN, vUSDC, vUSDT, - vip610, -} from "../../vips/vip-610/bscmainnet"; + vip608, +} from "../../vips/vip-608/bscmainnet"; import ACCESS_CONTROL_MANAGER_ABI from "./abi/AccessControlManager.json"; import BINANCE_ORACLE_ABI from "./abi/BinanceOracle.json"; import CHAINLINK_ORACLE_ABI from "./abi/ChainlinkOracle.json"; @@ -81,7 +81,7 @@ forking(89004570, async () => { }); }); - testVip("VIP-610 [BNB Chain] Configure Relative Position Manager", await vip610(), { + testVip("VIP-608 [BNB Chain] Configure Relative Position Manager", await vip608(), { callbackAfterExecution: async txResponse => { await expectEvents(txResponse, [RELATIVE_POSITION_MANAGER_ABI], ["OwnershipTransferred"], [1]); await expectEvents(txResponse, [ACCESS_CONTROL_MANAGER_ABI], ["RoleGranted"], [33]); diff --git a/simulations/vip-610/bsctestnet.ts b/simulations/vip-608/bsctestnet.ts similarity index 100% rename from simulations/vip-610/bsctestnet.ts rename to simulations/vip-608/bsctestnet.ts diff --git a/vips/vip-608/bscmainnet.ts b/vips/vip-608/bscmainnet.ts new file mode 100644 index 000000000..4cb354b65 --- /dev/null +++ b/vips/vip-608/bscmainnet.ts @@ -0,0 +1,141 @@ +import { NETWORK_ADDRESSES } from "src/networkAddresses"; +import { ProposalType } from "src/types"; +import { makeProposal } from "src/utils"; + +const { bscmainnet } = NETWORK_ADDRESSES; +export const RELATIVE_POSITION_MANAGER = "0x1525D804DFff218DcC8B9359940F423209356C42"; +export const POSITION_ACCOUNT = "0xa75C5b438226bc73BDCc83408E7Aa41771b33E2C"; + +export const vUSDC = "0xecA88125a5ADbe82614ffC12D0DB554E2e2867C8"; +export const vUSDT = "0xfD5840Cd36d94D7229439859C0112a4185BC0255"; + +export const TIMELOCKS_AND_GUARDIAN = [ + bscmainnet.NORMAL_TIMELOCK, + bscmainnet.FAST_TRACK_TIMELOCK, + bscmainnet.CRITICAL_TIMELOCK, + bscmainnet.GUARDIAN, +]; + +const giveAcmPermissions = (fnSignature: string, timelocks = TIMELOCKS_AND_GUARDIAN) => + timelocks.map(timelock => ({ + target: bscmainnet.ACCESS_CONTROL_MANAGER, + signature: "giveCallPermission(address,string,address)", + params: [RELATIVE_POSITION_MANAGER, fnSignature, timelock], + })); + +export const vip608 = () => { + const meta = { + version: "v2", + title: "VIP-608 [BNB Chain] Configure Relative Position Manager", + description: `This VIP activates the RelativePositionManager contract on BNB Chain mainnet, enabling the **Venus Yield+** product — a relative performance trading feature built on top of Venus Protocol's existing lending and borrowing infrastructure. Yield+ allows users to express a view that one asset will outperform another, packaged into a single, easy-to-manage leveraged position, without manually managing separate supply and borrow operations. + +The RelativePositionManager is the core orchestration contract for Yield+. It coordinates position account deployment, collateral management, and integration with Venus Core markets. This VIP performs four categories of on-chain configuration: transferring ownership of the contract to the Normal Timelock (consistent with Venus governance standards), granting Access Control Manager (ACM) permissions required for protocol operations across all relevant timelocks and the guardian, setting the PositionAccount implementation template, and registering the initial DSA (Default Settlement Asset) vTokens. + +This is the initial activation VIP for the Yield+ product. Subsequent VIPs may expand supported trading pairs and DSA options as the product matures. + +--- + +#### Changes + +#### 1. Accept Ownership of RelativePositionManager + +- **Contract:** RelativePositionManager (0x1525D804DFff218DcC8B9359940F423209356C42) +- **Function:** acceptOwnership() +- **Parameters:** none +- **Effect:** Transfers ownership of the RelativePositionManager contract from its pending owner to the Normal Timelock, completing the two-step ownership handoff and placing the contract under Venus governance control. + +#### 2. Grant ACM Permissions to Timelocks and Guardian + +- **Contract:** AccessControlManager (BSC Mainnet ACM) +- **Function:** giveCallPermission(address, string, address) — called multiple times +- **Permissions granted:** + - partialPause() / partialUnpause() — Normal Timelock, Fast Track Timelock, Critical Timelock, Guardian + - completePause() / completeUnpause() — Normal Timelock, Fast Track Timelock, Critical Timelock, Guardian + - setPositionAccountImplementation(address) — Normal Timelock only + - setProportionalCloseTolerance(uint256) — Normal Timelock, Fast Track Timelock, Critical Timelock, Guardian + - addDSAVToken(address) — Normal Timelock, Fast Track Timelock, Critical Timelock, Guardian + - setDSAVTokenActive(uint8, bool) — Normal Timelock, Fast Track Timelock, Critical Timelock, Guardian + - executePositionAccountCall(address, address[], bytes[]) — Normal Timelock, Fast Track Timelock, Critical Timelock, Guardian +- **Effect:** Authorizes all three timelocks and the Guardian to manage the Yield+ system — including circuit-breaker (pause/unpause), DSA configuration, and position account operations. The setPositionAccountImplementation permission is restricted to Normal Timelock only, as implementation upgrades require full governance deliberation. + +#### 3. Set PositionAccount Implementation + +- **Contract:** RelativePositionManager (0x1525D804DFff218DcC8B9359940F423209356C42) +- **Function:** setPositionAccountImplementation(address) +- **Parameters:** + - implementation: 0xa75C5b438226bc73BDCc83408E7Aa41771b33E2C (PositionAccount) +- **Effect:** Registers the PositionAccount smart contract template. When a user opens their first Yield+ position on a trading pair, a minimal proxy clone of this implementation is automatically deployed as their dedicated position account. + +#### 4. Register DSA vTokens (USDC and USDT) + +- **Contract:** RelativePositionManager (0x1525D804DFff218DcC8B9359940F423209356C42) +- **Function:** addDSAVToken(address) — called twice +- **Parameters:** + - vToken 1: 0xecA88125a5ADbe82614ffC12D0DB554E2e2867C8 (vUSDC) + - vToken 2: 0xfD5840Cd36d94D7229439859C0112a4185BC0255 (vUSDT) +- **Effect:** Registers USDC and USDT (via their Venus vToken wrappers) as supported Default Settlement Assets. Users can choose either stablecoin as their collateral currency and PnL settlement denomination when opening Yield+ positions. + +--- + +#### Summary + +If approved, this VIP will: + +- Transfer ownership of the RelativePositionManager contract (0x1525D804DFff218DcC8B9359940F423209356C42) to Venus's Normal Timelock +- Grant operational permissions (pause/unpause, DSA management, position account operations) to all timelocks and Guardian via the ACM +- Set the PositionAccount implementation (0xa75C5b438226bc73BDCc83408E7Aa41771b33E2C) for automatic per-user position account deployment +- Register vUSDC and vUSDT as supported DSA collateral assets +- Enable the Venus Yield+ relative performance trading feature on BSC mainnet + +--- + +#### References + +- [GitHub PR: VenusProtocol/vips#671](https://github.com/VenusProtocol/vips/pull/671) +- Venus Yield+ Community Post +- [Venus Protocol Documentation](https://docs.venus.io)`, + forDescription: "I agree that Venus Protocol should proceed with this proposal", + againstDescription: "I do not think that Venus Protocol should proceed with this proposal", + abstainDescription: "I am indifferent to whether Venus Protocol proceeds or not", + }; + + return makeProposal( + [ + { + target: RELATIVE_POSITION_MANAGER, + signature: "acceptOwnership()", + params: [], + }, + // ACM permissions + ...giveAcmPermissions("partialPause()"), + ...giveAcmPermissions("partialUnpause()"), + ...giveAcmPermissions("completePause()"), + ...giveAcmPermissions("completeUnpause()"), + ...giveAcmPermissions("setPositionAccountImplementation(address)", [bscmainnet.NORMAL_TIMELOCK]), + ...giveAcmPermissions("setProportionalCloseTolerance(uint256)"), + ...giveAcmPermissions("addDSAVToken(address)"), + ...giveAcmPermissions("setDSAVTokenActive(uint8,bool)"), + ...giveAcmPermissions("executePositionAccountCall(address,address[],bytes[])"), + { + target: RELATIVE_POSITION_MANAGER, + signature: "setPositionAccountImplementation(address)", + params: [POSITION_ACCOUNT], + }, + // Add DSA vTokens (USDC, USDT) + { + target: RELATIVE_POSITION_MANAGER, + signature: "addDSAVToken(address)", + params: [vUSDC], + }, + { + target: RELATIVE_POSITION_MANAGER, + signature: "addDSAVToken(address)", + params: [vUSDT], + }, + ], + meta, + ProposalType.REGULAR, + ); +}; + +export default vip608; diff --git a/vips/vip-610/bsctestnet.ts b/vips/vip-608/bsctestnet.ts similarity index 100% rename from vips/vip-610/bsctestnet.ts rename to vips/vip-608/bsctestnet.ts diff --git a/vips/vip-610/bscmainnet.ts b/vips/vip-610/bscmainnet.ts deleted file mode 100644 index 3e4c9fc65..000000000 --- a/vips/vip-610/bscmainnet.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { NETWORK_ADDRESSES } from "src/networkAddresses"; -import { ProposalType } from "src/types"; -import { makeProposal } from "src/utils"; - -const { bscmainnet } = NETWORK_ADDRESSES; -export const RELATIVE_POSITION_MANAGER = "0x1525D804DFff218DcC8B9359940F423209356C42"; -export const POSITION_ACCOUNT = "0xa75C5b438226bc73BDCc83408E7Aa41771b33E2C"; - -export const vUSDC = "0xecA88125a5ADbe82614ffC12D0DB554E2e2867C8"; -export const vUSDT = "0xfD5840Cd36d94D7229439859C0112a4185BC0255"; - -export const TIMELOCKS_AND_GUARDIAN = [ - bscmainnet.NORMAL_TIMELOCK, - bscmainnet.FAST_TRACK_TIMELOCK, - bscmainnet.CRITICAL_TIMELOCK, - bscmainnet.GUARDIAN, -]; - -const giveAcmPermissions = (fnSignature: string, timelocks = TIMELOCKS_AND_GUARDIAN) => - timelocks.map(timelock => ({ - target: bscmainnet.ACCESS_CONTROL_MANAGER, - signature: "giveCallPermission(address,string,address)", - params: [RELATIVE_POSITION_MANAGER, fnSignature, timelock], - })); - -export const vip610 = () => { - const meta = { - version: "v2", - title: "VIP-610 [BNB Chain] Configure Relative Position Manager", - description: "VIP-610 [BNB Chain] Configure Relative Position Manager", - forDescription: "I agree that Venus Protocol should proceed with this proposal", - againstDescription: "I do not think that Venus Protocol should proceed with this proposal", - abstainDescription: "I am indifferent to whether Venus Protocol proceeds or not", - }; - - return makeProposal( - [ - { - target: RELATIVE_POSITION_MANAGER, - signature: "acceptOwnership()", - params: [], - }, - // ACM permissions - ...giveAcmPermissions("partialPause()"), - ...giveAcmPermissions("partialUnpause()"), - ...giveAcmPermissions("completePause()"), - ...giveAcmPermissions("completeUnpause()"), - ...giveAcmPermissions("setPositionAccountImplementation(address)", [bscmainnet.NORMAL_TIMELOCK]), - ...giveAcmPermissions("setProportionalCloseTolerance(uint256)"), - ...giveAcmPermissions("addDSAVToken(address)"), - ...giveAcmPermissions("setDSAVTokenActive(uint8,bool)"), - ...giveAcmPermissions("executePositionAccountCall(address,address[],bytes[])"), - { - target: RELATIVE_POSITION_MANAGER, - signature: "setPositionAccountImplementation(address)", - params: [POSITION_ACCOUNT], - }, - // Add DSA vTokens (USDC, USDT) - { - target: RELATIVE_POSITION_MANAGER, - signature: "addDSAVToken(address)", - params: [vUSDC], - }, - { - target: RELATIVE_POSITION_MANAGER, - signature: "addDSAVToken(address)", - params: [vUSDT], - }, - ], - meta, - ProposalType.REGULAR, - ); -}; - -export default vip610; From 799f850268692a0f30d9bf7013b9689fc02e2f8b Mon Sep 17 00:00:00 2001 From: Fred Date: Fri, 10 Apr 2026 04:53:27 +0000 Subject: [PATCH 12/13] chore: remove unrelated scripts/data files --- scripts/data/markets.json | 1 - scripts/data/safePauseTxBuilder.json | 28 --------------------------- scripts/data/safePauseTxMetadata.json | 9 --------- 3 files changed, 38 deletions(-) delete mode 100644 scripts/data/markets.json delete mode 100644 scripts/data/safePauseTxBuilder.json delete mode 100644 scripts/data/safePauseTxMetadata.json diff --git a/scripts/data/markets.json b/scripts/data/markets.json deleted file mode 100644 index a60de35e7..000000000 --- a/scripts/data/markets.json +++ /dev/null @@ -1 +0,0 @@ -["0x86e06EAfa6A1eA631Eab51DE500E3D474933739f"] diff --git a/scripts/data/safePauseTxBuilder.json b/scripts/data/safePauseTxBuilder.json deleted file mode 100644 index cfe86daef..000000000 --- a/scripts/data/safePauseTxBuilder.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "version": "1.0", - "chainId": "56", - "createdAt": 1774362755458, - "meta": { - "name": "Transactions Batch", - "description": "Created from @morpho-labs/gnosis-tx-builder", - "txBuilderVersion": "1.10.0", - "createdFromSafeAddress": "0x1C2CAc6ec528c20800B2fe734820D87b581eAA6B", - "createdFromOwnerAddress": "", - "checksum": "0x7fd7634b25c13b9f37d416767afc8768119bc1d7faf337f9caa747f713978a7a" - }, - "transactions": [ - { - "to": "0xfD36E2c2a6789Db23113685031d7F16329158384", - "data": "0x24aaa220000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000086e06eafa6a1ea631eab51de500e3d474933739f00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000", - "value": "0", - "operation": 0 - }, - { - "to": "0xfD36E2c2a6789Db23113685031d7F16329158384", - "data": "0x24aaa220000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000086e06eafa6a1ea631eab51de500e3d474933739f00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", - "value": "0", - "operation": 0 - } - ], - "blockNumber": 88475583 -} \ No newline at end of file diff --git a/scripts/data/safePauseTxMetadata.json b/scripts/data/safePauseTxMetadata.json deleted file mode 100644 index ad2260706..000000000 --- a/scripts/data/safePauseTxMetadata.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "comptroller": "0xfD36E2c2a6789Db23113685031d7F16329158384", - "network": "bscmainnet", - "blockNumber": 88475583, - "createdAt": "2026-03-24T14:32:35.437Z", - "symbols": { - "0x86e06EAfa6A1eA631Eab51DE500E3D474933739f": "vTHE" - } -} \ No newline at end of file From a56763ed280022fb37984112e3ec026462989500 Mon Sep 17 00:00:00 2001 From: Fred Date: Fri, 10 Apr 2026 05:03:33 +0000 Subject: [PATCH 13/13] fix: update testnet simulation import path to vip-608 --- simulations/vip-608/bsctestnet.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simulations/vip-608/bsctestnet.ts b/simulations/vip-608/bsctestnet.ts index 16c1f9e8e..375db3999 100644 --- a/simulations/vip-608/bsctestnet.ts +++ b/simulations/vip-608/bsctestnet.ts @@ -14,7 +14,7 @@ import { vUSDC, vUSDT, vip610 as vip610Testnet, -} from "../../vips/vip-610/bsctestnet"; +} from "../../vips/vip-608/bsctestnet"; import ACCESS_CONTROL_MANAGER_ABI from "./abi/AccessControlManager.json"; import FLASHLOAN_FACET_ABI from "./abi/FlashLoanFacet.json"; import LEVERAGE_MANAGER_ABI from "./abi/LeverageStrategiesManager.json";