diff --git a/simulations/vip-608/abi/AccessControlManager.json b/simulations/vip-608/abi/AccessControlManager.json new file mode 100644 index 000000000..2ef119947 --- /dev/null +++ b/simulations/vip-608/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-608/abi/BinanceOracle.json b/simulations/vip-608/abi/BinanceOracle.json new file mode 100644 index 000000000..f740c68c8 --- /dev/null +++ b/simulations/vip-608/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-608/abi/ChainlinkOracle.json b/simulations/vip-608/abi/ChainlinkOracle.json new file mode 100644 index 000000000..7dd5f6f5f --- /dev/null +++ b/simulations/vip-608/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-608/abi/FlashLoanFacet.json b/simulations/vip-608/abi/FlashLoanFacet.json new file mode 100644 index 000000000..2adc7e8fa --- /dev/null +++ b/simulations/vip-608/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-608/abi/LeverageStrategiesManager.json b/simulations/vip-608/abi/LeverageStrategiesManager.json new file mode 100644 index 000000000..72ef420b4 --- /dev/null +++ b/simulations/vip-608/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" + } +] diff --git a/simulations/vip-608/abi/PositionAccount.json b/simulations/vip-608/abi/PositionAccount.json new file mode 100644 index 000000000..ef2b02033 --- /dev/null +++ b/simulations/vip-608/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-608/abi/RelativePositionManager.json b/simulations/vip-608/abi/RelativePositionManager.json new file mode 100644 index 000000000..f391e34d9 --- /dev/null +++ b/simulations/vip-608/abi/RelativePositionManager.json @@ -0,0 +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" + } +] diff --git a/simulations/vip-608/abi/ResilientOracle.json b/simulations/vip-608/abi/ResilientOracle.json new file mode 100644 index 000000000..373eb14e9 --- /dev/null +++ b/simulations/vip-608/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-608/abi/SwapHelperAbi.json b/simulations/vip-608/abi/SwapHelperAbi.json new file mode 100644 index 000000000..fb92e0573 --- /dev/null +++ b/simulations/vip-608/abi/SwapHelperAbi.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-608/abi/VToken.json b/simulations/vip-608/abi/VToken.json new file mode 100644 index 000000000..68f428c6c --- /dev/null +++ b/simulations/vip-608/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-608/bscmainnet.ts b/simulations/vip-608/bscmainnet.ts new file mode 100644 index 000000000..72472e521 --- /dev/null +++ b/simulations/vip-608/bscmainnet.ts @@ -0,0 +1,1419 @@ +import { expect } from "chai"; +import { BigNumber, Contract, Wallet } from "ethers"; +import { ethers } from "hardhat"; +import { NETWORK_ADDRESSES } from "src/networkAddresses"; +import { expectEvents, initMainnetUser } from "src/utils"; +import { forking, testVip } from "src/vip-framework"; + +import { + POSITION_ACCOUNT, + RELATIVE_POSITION_MANAGER, + TIMELOCKS_AND_GUARDIAN, + vUSDC, + vUSDT, + 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"; +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"; +import VTOKEN_ABI from "./abi/VToken.json"; + +const { bscmainnet } = NETWORK_ADDRESSES; + +const ACM_FUNCTION_SIGNATURES = [ + "partialPause()", + "partialUnpause()", + "completePause()", + "completeUnpause()", + "setProportionalCloseTolerance(uint256)", + "addDSAVToken(address)", + "setDSAVTokenActive(uint8,bool)", + "executePositionAccountCall(address,address[],bytes[])", +] as const; + +forking(89004570, 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("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) { + 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); + } + } + 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-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]); + await expectEvents(txResponse, [RELATIVE_POSITION_MANAGER_ABI], ["PositionAccountImplementationSet"], [1]); + await expectEvents(txResponse, [RELATIVE_POSITION_MANAGER_ABI], ["DSAVTokenAdded"], [2]); + }, + }); + + 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); + } + } + 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 () => { + 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( + relativePositionManager.connect(signer).setPositionAccountImplementation(POSITION_ACCOUNT), + ).to.be.revertedWithCustomError(relativePositionManager, "PositionAccountImplementationLocked"); + }); + + 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); + }); + + 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( + positionAccountContract, + "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 = "0xf322942f644a996a617bd29c16bd7d231d9f35e9"; // Venus Treasury + const SHORT_WHALE = "0xf322942f644a996a617bd29c16bd7d231d9f35e9"; // Venus Treasury (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)", + ]; + + 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(); + 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("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"); + 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-608/bsctestnet.ts b/simulations/vip-608/bsctestnet.ts new file mode 100644 index 000000000..375db3999 --- /dev/null +++ b/simulations/vip-608/bsctestnet.ts @@ -0,0 +1,210 @@ +import { expect } from "chai"; +import { Contract } from "ethers"; +import { ethers } from "hardhat"; +import { NETWORK_ADDRESSES } from "src/networkAddresses"; +import { expectEvents, initMainnetUser } from "src/utils"; +import { forking, testVip } from "src/vip-framework"; + +import { + LEVERAGE_STRATEGIES_MANAGER, + POSITION_ACCOUNT, + RELATIVE_POSITION_MANAGER, + SWAP_HELPER, + TIMELOCKS_AND_GUARDIAN, + vUSDC, + vUSDT, + vip610 as vip610Testnet, +} 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"; +import RELATIVE_POSITION_MANAGER_ABI from "./abi/RelativePositionManager.json"; +import SWAP_HELPER_ABI from "./abi/SwapHelperAbi.json"; + +const { bsctestnet } = NETWORK_ADDRESSES; + +const ACM_FUNCTION_SIGNATURES = [ + "partialPause()", + "partialUnpause()", + "completePause()", + "completeUnpause()", + "setProportionalCloseTolerance(uint256)", + "addDSAVToken(address)", + "setDSAVTokenActive(uint8,bool)", + "executePositionAccountCall(address,address[],bytes[])", +] as const; + +forking(96763094, 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 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 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 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("RPM should not have Position Account implementation set", 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) { + expect( + await accessControlManager.hasPermission(timelockOrGuardian, RELATIVE_POSITION_MANAGER, fnSignature), + ).to.equal(false); + } + } + expect( + await accessControlManager.hasPermission( + bsctestnet.NORMAL_TIMELOCK, + RELATIVE_POSITION_MANAGER, + "setPositionAccountImplementation(address)", + ), + ).to.equal(false); + }); + }); + + 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"], [33]); + await expectEvents(txResponse, [RELATIVE_POSITION_MANAGER_ABI], ["PositionAccountImplementationSet"], [1]); + await expectEvents(txResponse, [RELATIVE_POSITION_MANAGER_ABI], ["DSAVTokenAdded"], [2]); + }, + }); + + 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_GUARDIAN) { + for (const fnSignature of ACM_FUNCTION_SIGNATURES) { + expect( + await accessControlManager.hasPermission(timelockOrGuardian, RELATIVE_POSITION_MANAGER, fnSignature), + ).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 () => { + 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( + relativePositionManager.connect(signer).setPositionAccountImplementation(POSITION_ACCOUNT), + ).to.be.revertedWithCustomError(relativePositionManager, "PositionAccountImplementationLocked"); + }); + + 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); + }); + + 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-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-608/bsctestnet.ts b/vips/vip-608/bsctestnet.ts new file mode 100644 index 000000000..4c51dad12 --- /dev/null +++ b/vips/vip-608/bsctestnet.ts @@ -0,0 +1,93 @@ +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 = "0x5187226337C95c4BE683D37Ffc66D41f5b6cE38f"; +export const RELATIVE_POSITION_MANAGER = "0x25dbA64B28F93cC40e9cAf9691266043fe1000a2"; +export const POSITION_ACCOUNT = "0xC9A5f1598e434E3E52CE25D7ff290E4CF167ee52"; + +export const vUSDC = "0xD5C4C2e2facBEB59D0216D0595d63FcDc6F9A1a7"; +export const vUSDT = "0xb7526572FFE56AB9D7489838Bf2E18e3323b441A"; + +export const TIMELOCKS_AND_GUARDIAN = [ + bsctestnet.NORMAL_TIMELOCK, + bsctestnet.FAST_TRACK_TIMELOCK, + bsctestnet.CRITICAL_TIMELOCK, + bsctestnet.GUARDIAN, +]; + +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 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( + [ + // Extra testnet setup: accept ownership of SwapHelper, LeverageStrategiesManager and whitelist for flash loans + { + 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], + }, + // ACM permissions + ...giveAcmPermissions("partialPause()"), + ...giveAcmPermissions("partialUnpause()"), + ...giveAcmPermissions("completePause()"), + ...giveAcmPermissions("completeUnpause()"), + ...giveAcmPermissions("setPositionAccountImplementation(address)", [bsctestnet.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;