From ad5dfc983d8ad1fb673c644a930ab637b2c0aa4a Mon Sep 17 00:00:00 2001 From: GitGuru7 Date: Thu, 16 Apr 2026 17:10:09 +0530 Subject: [PATCH 1/5] feat: add configuration for institutional fixed rate vault --- .../vip-664/abi/AccessControlManager.json | 360 +++ .../vip-664/abi/InstitutionPositionToken.json | 267 +++ .../vip-664/abi/InstitutionalLoanVault.json | 2001 +++++++++++++++++ .../abi/InstitutionalVaultController.json | 505 +++++ simulations/vip-664/bsctestnet.ts | 191 ++ vips/vip-664/bsctestnet.ts | 167 ++ 6 files changed, 3491 insertions(+) create mode 100644 simulations/vip-664/abi/AccessControlManager.json create mode 100644 simulations/vip-664/abi/InstitutionPositionToken.json create mode 100644 simulations/vip-664/abi/InstitutionalLoanVault.json create mode 100644 simulations/vip-664/abi/InstitutionalVaultController.json create mode 100644 simulations/vip-664/bsctestnet.ts create mode 100644 vips/vip-664/bsctestnet.ts diff --git a/simulations/vip-664/abi/AccessControlManager.json b/simulations/vip-664/abi/AccessControlManager.json new file mode 100644 index 000000000..4a118fcc4 --- /dev/null +++ b/simulations/vip-664/abi/AccessControlManager.json @@ -0,0 +1,360 @@ +[ + { + "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-664/abi/InstitutionPositionToken.json b/simulations/vip-664/abi/InstitutionPositionToken.json new file mode 100644 index 000000000..8933a45ce --- /dev/null +++ b/simulations/vip-664/abi/InstitutionPositionToken.json @@ -0,0 +1,267 @@ +[ + { "inputs": [], "stateMutability": "nonpayable", "type": "constructor" }, + { "inputs": [], "name": "OwnershipCannotBeRenounced", "type": "error" }, + { + "inputs": [{ "internalType": "uint256", "name": "tokenId", "type": "uint256" }], + "name": "TransferNotApproved", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "owner", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "approved", "type": "address" }, + { "indexed": true, "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "owner", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "operator", "type": "address" }, + { "indexed": false, "internalType": "bool", "name": "approved", "type": "bool" } + ], + "name": "ApprovalForAll", + "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": "vault", "type": "address" }, + { "indexed": true, "internalType": "uint256", "name": "tokenId", "type": "uint256" }, + { "indexed": true, "internalType": "address", "name": "institution", "type": "address" } + ], + "name": "PositionTokenMinted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": true, "internalType": "uint256", "name": "tokenId", "type": "uint256" }], + "name": "PositionTransferApproved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": true, "internalType": "uint256", "name": "tokenId", "type": "uint256" }], + "name": "PositionTransferRevoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "from", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "to", "type": "address" }, + { "indexed": true, "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "Transfer", + "type": "event" + }, + { "inputs": [], "name": "acceptOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "tokenId", "type": "uint256" }], + "name": "approveTransfer", + "outputs": [], + "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": "uint256", "name": "tokenId", "type": "uint256" }], + "name": "getApproved", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "address", "name": "operator", "type": "address" } + ], + "name": "isApprovedForAll", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "address", "name": "vault", "type": "address" } + ], + "name": "mint", + "outputs": [{ "internalType": "uint256", "name": "tokenId", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "nextTokenId", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "tokenId", "type": "uint256" }], + "name": "ownerOf", + "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": "uint256", "name": "tokenId", "type": "uint256" }], + "name": "revokeTransferApproval", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "from", "type": "address" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "from", "type": "address" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, + { "internalType": "bytes", "name": "data", "type": "bytes" } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "operator", "type": "address" }, + { "internalType": "bool", "name": "approved", "type": "bool" } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "bytes4", "name": "interfaceId", "type": "bytes4" }], + "name": "supportsInterface", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "name": "tokenIdToVault", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "tokenId", "type": "uint256" }], + "name": "tokenURI", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "name": "transferApproved", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "from", "type": "address" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "newOwner", "type": "address" }], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "", "type": "address" }], + "name": "vaultToTokenId", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + } +] diff --git a/simulations/vip-664/abi/InstitutionalLoanVault.json b/simulations/vip-664/abi/InstitutionalLoanVault.json new file mode 100644 index 000000000..3ea5bbd50 --- /dev/null +++ b/simulations/vip-664/abi/InstitutionalLoanVault.json @@ -0,0 +1,2001 @@ +[ + { + "type": "constructor", + "inputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "BPS", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "MANTISSA_ONE", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "YEAR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "allowance", + "inputs": [ + { + "name": "owner", + "type": "address", + "internalType": "address" + }, + { + "name": "spender", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "approve", + "inputs": [ + { + "name": "spender", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "asset", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "balanceOf", + "inputs": [ + { + "name": "account", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "calculateSeizeAmount", + "inputs": [ + { + "name": "repayAmount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "liquidationType", + "type": "uint8", + "internalType": "enum LiquidationType" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "claimRaisedFunds", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "closeVault", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "completePause", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "config", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "tuple", + "internalType": "struct VaultConfig", + "components": [ + { + "name": "supplyAsset", + "type": "address", + "internalType": "contract IERC20" + }, + { + "name": "fixedAPY", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "reserveFactor", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "minBorrowCap", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "maxBorrowCap", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "minSupplierDeposit", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "openDuration", + "type": "uint40", + "internalType": "uint40" + }, + { + "name": "lockDuration", + "type": "uint40", + "internalType": "uint40" + }, + { + "name": "settlementWindow", + "type": "uint40", + "internalType": "uint40" + } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "convertToAssets", + "inputs": [ + { + "name": "shares", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "convertToShares", + "inputs": [ + { + "name": "assets", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "decimals", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint8", + "internalType": "uint8" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "decreaseAllowance", + "inputs": [ + { + "name": "spender", + "type": "address", + "internalType": "address" + }, + { + "name": "subtractedValue", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "deposit", + "inputs": [ + { + "name": "assets", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "receiver", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "shares", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "depositCollateral", + "inputs": [ + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "getCollateralValueUSD", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getDebtValueUSD", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getHypotheticalVaultLiquidity", + "inputs": [ + { + "name": "withdrawAmount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "additionalDebt", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "liquidity", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "shortfall", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getVaultLiquidity", + "inputs": [], + "outputs": [ + { + "name": "liquidity", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "shortfall", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "increaseAllowance", + "inputs": [ + { + "name": "spender", + "type": "address", + "internalType": "address" + }, + { + "name": "addedValue", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "initialize", + "inputs": [ + { + "name": "config_", + "type": "tuple", + "internalType": "struct VaultConfig", + "components": [ + { + "name": "supplyAsset", + "type": "address", + "internalType": "contract IERC20" + }, + { + "name": "fixedAPY", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "reserveFactor", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "minBorrowCap", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "maxBorrowCap", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "minSupplierDeposit", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "openDuration", + "type": "uint40", + "internalType": "uint40" + }, + { + "name": "lockDuration", + "type": "uint40", + "internalType": "uint40" + }, + { + "name": "settlementWindow", + "type": "uint40", + "internalType": "uint40" + } + ] + }, + { + "name": "instConfig_", + "type": "tuple", + "internalType": "struct InstitutionalConfig", + "components": [ + { + "name": "collateralAsset", + "type": "address", + "internalType": "contract IERC20" + }, + { + "name": "idealCollateralAmount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "marginRate", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "institutionOperator", + "type": "address", + "internalType": "address" + }, + { + "name": "positionTokenId", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "name": "riskConfig_", + "type": "tuple", + "internalType": "struct RiskConfig", + "components": [ + { + "name": "liquidationThreshold", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "liquidationIncentive", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "latePenaltyRate", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "name": "positionToken_", + "type": "address", + "internalType": "contract IInstitutionPositionToken" + }, + { + "name": "name_", + "type": "string", + "internalType": "string" + }, + { + "name": "symbol_", + "type": "string", + "internalType": "string" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "institutionalConfig", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "tuple", + "internalType": "struct InstitutionalConfig", + "components": [ + { + "name": "collateralAsset", + "type": "address", + "internalType": "contract IERC20" + }, + { + "name": "idealCollateralAmount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "marginRate", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "institutionOperator", + "type": "address", + "internalType": "address" + }, + { + "name": "positionTokenId", + "type": "uint256", + "internalType": "uint256" + } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "institutionalRuntime", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "tuple", + "internalType": "struct InstitutionalRuntime", + "components": [ + { + "name": "totalCollateralDeposited", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "minimumCollateralRequired", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "idealCollateralValuation", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "confiscatedMarginRemaining", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "institutionDefaulted", + "type": "bool", + "internalType": "bool" + } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "liquidate", + "inputs": [ + { + "name": "repayAmount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "actualRepay", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "liquidateOverdueVault", + "inputs": [ + { + "name": "repayAmount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "actualRepay", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "maxDeposit", + "inputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "maxMint", + "inputs": [ + { + "name": "receiver", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "maxRedeem", + "inputs": [ + { + "name": "owner", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "maxWithdraw", + "inputs": [ + { + "name": "owner", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "mint", + "inputs": [ + { + "name": "shares", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "receiver", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "assets", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "name", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "string", + "internalType": "string" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "openVault", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "outstandingDebt", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "partialPause", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "pauseLevel", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint8", + "internalType": "enum PauseLevel" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "positionToken", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "contract IInstitutionPositionToken" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "previewDeposit", + "inputs": [ + { + "name": "assets", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "previewMint", + "inputs": [ + { + "name": "shares", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "previewRedeem", + "inputs": [ + { + "name": "shares", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "previewWithdraw", + "inputs": [ + { + "name": "assets", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "redeem", + "inputs": [ + { + "name": "shares", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "receiver", + "type": "address", + "internalType": "address" + }, + { + "name": "owner", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "assets", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "repay", + "inputs": [ + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "repayBadDebt", + "inputs": [ + { + "name": "repayAmount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "riskConfig", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "tuple", + "internalType": "struct RiskConfig", + "components": [ + { + "name": "liquidationThreshold", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "liquidationIncentive", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "latePenaltyRate", + "type": "uint256", + "internalType": "uint256" + } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "runtime", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "tuple", + "internalType": "struct VaultRuntime", + "components": [ + { + "name": "state", + "type": "uint8", + "internalType": "enum VaultState" + }, + { + "name": "openStartTime", + "type": "uint40", + "internalType": "uint40" + }, + { + "name": "openEndTime", + "type": "uint40", + "internalType": "uint40" + }, + { + "name": "lockStartTime", + "type": "uint40", + "internalType": "uint40" + }, + { + "name": "lockEndTime", + "type": "uint40", + "internalType": "uint40" + }, + { + "name": "settlementDeadline", + "type": "uint40", + "internalType": "uint40" + }, + { + "name": "totalRaised", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "totalDebt", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "settlementAmount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "fundsWithdrawn", + "type": "bool", + "internalType": "bool" + }, + { + "name": "protocolShareSettled", + "type": "bool", + "internalType": "bool" + } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "setLatePenaltyRate", + "inputs": [ + { + "name": "newRate", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setLiquidationIncentive", + "inputs": [ + { + "name": "newLI", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setLiquidationThreshold", + "inputs": [ + { + "name": "newLT", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "state", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint8", + "internalType": "enum VaultState" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "sweep", + "inputs": [ + { + "name": "token", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "symbol", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "string", + "internalType": "string" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "totalAssets", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "totalSupply", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "transfer", + "inputs": [ + { + "name": "to", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "transferFrom", + "inputs": [ + { + "name": "from", + "type": "address", + "internalType": "address" + }, + { + "name": "to", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "unpause", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "updateVaultState", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "vaultController", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "withdraw", + "inputs": [ + { + "name": "assets", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "receiver", + "type": "address", + "internalType": "address" + }, + { + "name": "owner", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "shares", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "withdrawCollateral", + "inputs": [ + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "Approval", + "inputs": [ + { + "name": "owner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "spender", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "value", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "CollateralDeposited", + "inputs": [ + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "totalCollateral", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "CollateralWithdrawn", + "inputs": [ + { + "name": "positionHolder", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "remaining", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Deposit", + "inputs": [ + { + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "owner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "assets", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "shares", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Initialized", + "inputs": [ + { + "name": "version", + "type": "uint8", + "indexed": false, + "internalType": "uint8" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "LatePenaltyRateUpdated", + "inputs": [ + { + "name": "oldRate", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "newRate", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "LiquidationExecuted", + "inputs": [ + { + "name": "liquidator", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "repayAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "collateralSeized", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "LiquidationIncentiveUpdated", + "inputs": [ + { + "name": "oldLI", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "newLI", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "LiquidationThresholdUpdated", + "inputs": [ + { + "name": "oldLT", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "newLT", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "MarginCompensationClaimed", + "inputs": [ + { + "name": "receiver", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "MarginConfiscated", + "inputs": [ + { + "name": "marginAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OverdueLiquidationExecuted", + "inputs": [ + { + "name": "settler", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "repayAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "collateralSeized", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "PSRNotificationFailed", + "inputs": [ + { + "name": "psr", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "reason", + "type": "bytes", + "indexed": false, + "internalType": "bytes" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "PauseLevelSet", + "inputs": [ + { + "name": "oldLevel", + "type": "uint8", + "indexed": false, + "internalType": "enum PauseLevel" + }, + { + "name": "newLevel", + "type": "uint8", + "indexed": false, + "internalType": "enum PauseLevel" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "RaisedFundsClaimed", + "inputs": [ + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Repaid", + "inputs": [ + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "remainingDebt", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "SettlementConfirmed", + "inputs": [ + { + "name": "settlementAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "protocolFee", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "surplus", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ShortfallDetected", + "inputs": [ + { + "name": "totalOwed", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "available", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "StateTransition", + "inputs": [ + { + "name": "from", + "type": "uint8", + "indexed": true, + "internalType": "enum VaultState" + }, + { + "name": "to", + "type": "uint8", + "indexed": true, + "internalType": "enum VaultState" + }, + { + "name": "timestamp", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "TokensSwept", + "inputs": [ + { + "name": "token", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "recipient", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Transfer", + "inputs": [ + { + "name": "from", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "to", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "value", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "VaultClosed", + "inputs": [ + { + "name": "state", + "type": "uint8", + "indexed": false, + "internalType": "enum VaultState" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "VaultFailed", + "inputs": [ + { + "name": "totalRaised", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "minBorrowCap", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "VaultLiquidated", + "inputs": [ + { + "name": "available", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "VaultLocked", + "inputs": [ + { + "name": "totalRaised", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "lockEndTime", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "VaultOpened", + "inputs": [ + { + "name": "openEndTime", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Withdraw", + "inputs": [ + { + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "receiver", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "owner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "assets", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "shares", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "error", + "name": "AlreadyWithdrawn", + "inputs": [] + }, + { + "type": "error", + "name": "BelowMinimumDepositAmount", + "inputs": [] + }, + { + "type": "error", + "name": "ClaimWouldBreachLT", + "inputs": [] + }, + { + "type": "error", + "name": "CompletelyPaused", + "inputs": [] + }, + { + "type": "error", + "name": "ExceedsCloseFactor", + "inputs": [] + }, + { + "type": "error", + "name": "ExceedsMaxCap", + "inputs": [] + }, + { + "type": "error", + "name": "InsufficientCollateral", + "inputs": [] + }, + { + "type": "error", + "name": "InsufficientCollateralForSeize", + "inputs": [ + { + "name": "seizeAmount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "availableCollateral", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "type": "error", + "name": "InsufficientRepayment", + "inputs": [] + }, + { + "type": "error", + "name": "InvalidOraclePrice", + "inputs": [] + }, + { + "type": "error", + "name": "InvalidState", + "inputs": [] + }, + { + "type": "error", + "name": "InvalidStateForOverdueLiquidation", + "inputs": [] + }, + { + "type": "error", + "name": "NoOutstandingDebt", + "inputs": [] + }, + { + "type": "error", + "name": "NotBadDebt", + "inputs": [] + }, + { + "type": "error", + "name": "NotLiquidatable", + "inputs": [] + }, + { + "type": "error", + "name": "NotPositionHolder", + "inputs": [] + }, + { + "type": "error", + "name": "NothingToSweep", + "inputs": [] + }, + { + "type": "error", + "name": "PartiallyPaused", + "inputs": [] + }, + { + "type": "error", + "name": "PositionTokenIdNotSet", + "inputs": [] + }, + { + "type": "error", + "name": "SameStateTransition", + "inputs": [] + }, + { + "type": "error", + "name": "Unauthorized", + "inputs": [] + }, + { + "type": "error", + "name": "WithdrawExceedsCollateral", + "inputs": [] + }, + { + "type": "error", + "name": "WithdrawalWouldBreachLT", + "inputs": [] + }, + { + "type": "error", + "name": "ZeroAddress", + "inputs": [] + }, + { + "type": "error", + "name": "ZeroRepayAmount", + "inputs": [] + } +] \ No newline at end of file diff --git a/simulations/vip-664/abi/InstitutionalVaultController.json b/simulations/vip-664/abi/InstitutionalVaultController.json new file mode 100644 index 000000000..0bf203dbb --- /dev/null +++ b/simulations/vip-664/abi/InstitutionalVaultController.json @@ -0,0 +1,505 @@ +[ + { "inputs": [], "stateMutability": "nonpayable", "type": "constructor" }, + { "inputs": [], "name": "InvalidAddress", "type": "error" }, + { "inputs": [], "name": "InvalidConfig", "type": "error" }, + { "inputs": [], "name": "InvalidLatePenaltyRate", "type": "error" }, + { "inputs": [], "name": "InvalidLiquidationIncentive", "type": "error" }, + { "inputs": [], "name": "InvalidLiquidationThreshold", "type": "error" }, + { "inputs": [], "name": "OwnershipCannotBeRenounced", "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": "VaultNotRegistered", "type": "error" }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "oldComptroller", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "newComptroller", "type": "address" } + ], + "name": "ComptrollerUpdated", + "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": "vault", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "newRate", "type": "uint256" } + ], + "name": "LatePenaltyRateUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "oldAdapter", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "newAdapter", "type": "address" } + ], + "name": "LiquidationAdapterUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "vault", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "newLI", "type": "uint256" } + ], + "name": "LiquidationIncentiveUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "vault", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "newLT", "type": "uint256" } + ], + "name": "LiquidationThresholdUpdated", + "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": "oldOracle", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "newOracle", "type": "address" } + ], + "name": "OracleUpdated", + "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": "oldPSR", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "newPSR", "type": "address" } + ], + "name": "ProtocolShareReserveUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "oldTreasury", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "newTreasury", "type": "address" } + ], + "name": "TreasuryUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "vault", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "institution", "type": "address" } + ], + "name": "VaultCreated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "oldImpl", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "newImpl", "type": "address" } + ], + "name": "VaultImplementationUpdated", + "type": "event" + }, + { + "inputs": [], + "name": "MANTISSA_ONE", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MANTISSA_ONE_AND_HALF", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_APY_BPS", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { "inputs": [], "name": "acceptOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "acceptPositionTokenOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "accessControlManager", + "outputs": [{ "internalType": "contract IAccessControlManagerV8", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "name": "allVaults", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "allVaultsLength", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "vault", "type": "address" }], + "name": "approvePositionTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "vault", "type": "address" }], + "name": "closeVault", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "vault", "type": "address" }], + "name": "completePauseVault", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "comptroller", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "contract IERC20", "name": "supplyAsset", "type": "address" }, + { "internalType": "uint256", "name": "fixedAPY", "type": "uint256" }, + { "internalType": "uint256", "name": "reserveFactor", "type": "uint256" }, + { "internalType": "uint256", "name": "minBorrowCap", "type": "uint256" }, + { "internalType": "uint256", "name": "maxBorrowCap", "type": "uint256" }, + { "internalType": "uint256", "name": "minSupplierDeposit", "type": "uint256" }, + { "internalType": "uint40", "name": "openDuration", "type": "uint40" }, + { "internalType": "uint40", "name": "lockDuration", "type": "uint40" }, + { "internalType": "uint40", "name": "settlementWindow", "type": "uint40" } + ], + "internalType": "struct VaultConfig", + "name": "_vaultConfig", + "type": "tuple" + }, + { + "components": [ + { "internalType": "contract IERC20", "name": "collateralAsset", "type": "address" }, + { "internalType": "uint256", "name": "idealCollateralAmount", "type": "uint256" }, + { "internalType": "uint256", "name": "marginRate", "type": "uint256" }, + { "internalType": "address", "name": "institutionOperator", "type": "address" }, + { "internalType": "uint256", "name": "positionTokenId", "type": "uint256" } + ], + "internalType": "struct InstitutionalConfig", + "name": "_instConfig", + "type": "tuple" + }, + { + "components": [ + { "internalType": "uint256", "name": "liquidationThreshold", "type": "uint256" }, + { "internalType": "uint256", "name": "liquidationIncentive", "type": "uint256" }, + { "internalType": "uint256", "name": "latePenaltyRate", "type": "uint256" } + ], + "internalType": "struct RiskConfig", + "name": "_riskConfig", + "type": "tuple" + }, + { "internalType": "string", "name": "_name", "type": "string" }, + { "internalType": "string", "name": "_symbol", "type": "string" } + ], + "name": "createVault", + "outputs": [{ "internalType": "address", "name": "vault", "type": "address" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getAggregatedVaultStates", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "vault", "type": "address" }, + { "internalType": "enum VaultState", "name": "state", "type": "uint8" }, + { "internalType": "address", "name": "institutionOperator", "type": "address" }, + { "internalType": "uint256", "name": "totalRaised", "type": "uint256" }, + { "internalType": "uint256", "name": "outstandingDebt", "type": "uint256" } + ], + "internalType": "struct VaultStateInfo[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "vaultImplementation_", "type": "address" }, + { "internalType": "address", "name": "oracle_", "type": "address" }, + { "internalType": "address", "name": "protocolShareReserve_", "type": "address" }, + { "internalType": "address", "name": "comptroller_", "type": "address" }, + { "internalType": "address", "name": "treasury_", "type": "address" }, + { "internalType": "address", "name": "positionToken_", "type": "address" }, + { "internalType": "address", "name": "acm_", "type": "address" } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "", "type": "address" }], + "name": "institutionNonce", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "", "type": "address" }], + "name": "isRegistered", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "liquidationAdapter", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "vault", "type": "address" }], + "name": "openVault", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "oracle", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "vault", "type": "address" }], + "name": "partialPauseVault", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "positionToken", + "outputs": [{ "internalType": "contract IInstitutionPositionToken", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "institution", "type": "address" }], + "name": "predictVaultAddress", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "protocolShareReserve", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { "inputs": [], "name": "renounceOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [{ "internalType": "address", "name": "vault", "type": "address" }], + "name": "revokePositionTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "accessControlManager_", "type": "address" }], + "name": "setAccessControlManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "comptroller_", "type": "address" }], + "name": "setComptroller", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "vault", "type": "address" }, + { "internalType": "uint256", "name": "newRate", "type": "uint256" } + ], + "name": "setLatePenaltyRate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "adapter", "type": "address" }], + "name": "setLiquidationAdapter", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "vault", "type": "address" }, + { "internalType": "uint256", "name": "newLI", "type": "uint256" } + ], + "name": "setLiquidationIncentive", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "vault", "type": "address" }, + { "internalType": "uint256", "name": "newLT", "type": "uint256" } + ], + "name": "setLiquidationThreshold", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "oracle_", "type": "address" }], + "name": "setOracle", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "psr", "type": "address" }], + "name": "setProtocolShareReserve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "treasury_", "type": "address" }], + "name": "setTreasury", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "impl", "type": "address" }], + "name": "setVaultImplementation", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "vault", "type": "address" }, + { "internalType": "address", "name": "token", "type": "address" } + ], + "name": "sweep", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "newOwner", "type": "address" }], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "treasury", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "vault", "type": "address" }], + "name": "unpauseVault", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "vaultImplementation", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + } +] diff --git a/simulations/vip-664/bsctestnet.ts b/simulations/vip-664/bsctestnet.ts new file mode 100644 index 000000000..36c042541 --- /dev/null +++ b/simulations/vip-664/bsctestnet.ts @@ -0,0 +1,191 @@ +import { expect } from "chai"; +import { Contract } from "ethers"; +import { ethers } from "hardhat"; +import { NETWORK_ADDRESSES } from "src/networkAddresses"; +import { expectEvents } from "src/utils"; +import { forking, testVip } from "src/vip-framework"; + +import vip664, { + ADAPTER_FAST_TRACK_FUNCTIONS, + ADAPTER_FUNCTIONS, + CONTROLLER_CRITICAL_FUNCTIONS, + CONTROLLER_FAST_TRACK_FUNCTIONS, + CONTROLLER_FUNCTIONS, + EXPECTED_PERMISSION_GRANTED_EVENTS, + INSTITUTIONAL_VAULT_CONTROLLER, + INSTITUTION_POSITION_TOKEN, + LIQUIDATION_ADAPTER, +} from "../../vips/vip-664/bsctestnet"; +import ACCESS_CONTROL_MANAGER_ABI from "./abi/AccessControlManager.json"; +import INSTITUTION_POSITION_TOKEN_ABI from "./abi/InstitutionPositionToken.json"; +import INSTITUTIONAL_VAULT_CONTROLLER_ABI from "./abi/InstitutionalVaultController.json"; + +const { bsctestnet } = NETWORK_ADDRESSES; +const { NORMAL_TIMELOCK, FAST_TRACK_TIMELOCK, CRITICAL_TIMELOCK, GUARDIAN, ACCESS_CONTROL_MANAGER } = bsctestnet; + +const FORK_BLOCK = 101981562; + +forking(FORK_BLOCK, async () => { + let accessControlManager: Contract; + let controller: Contract; + let positionToken: Contract; + + before(async () => { + accessControlManager = new ethers.Contract(ACCESS_CONTROL_MANAGER, ACCESS_CONTROL_MANAGER_ABI, ethers.provider); + controller = new ethers.Contract( + INSTITUTIONAL_VAULT_CONTROLLER, + INSTITUTIONAL_VAULT_CONTROLLER_ABI, + ethers.provider, + ); + positionToken = new ethers.Contract(INSTITUTION_POSITION_TOKEN, INSTITUTION_POSITION_TOKEN_ABI, ethers.provider); + }); + + // ────────────────────────────────────────────────────────────────────── + // Pre-VIP: verify deployments + // ────────────────────────────────────────────────────────────────────── + // These checks confirm that all three contracts are deployed and that the + // ownership transfer initiation (transferOwnership in the deploy script) has + // been executed before the VIP runs. + describe("Pre-VIP: verify deployments", () => { + it("InstitutionalVaultController proxy should be deployed", async () => { + const code = await ethers.provider.getCode(INSTITUTIONAL_VAULT_CONTROLLER); + expect(code).to.not.equal("0x"); + }); + + it("LiquidationAdapter proxy should be deployed", async () => { + const code = await ethers.provider.getCode(LIQUIDATION_ADAPTER); + expect(code).to.not.equal("0x"); + }); + + it("InstitutionPositionToken should be deployed", async () => { + const code = await ethers.provider.getCode(INSTITUTION_POSITION_TOKEN); + expect(code).to.not.equal("0x"); + }); + + it("InstitutionPositionToken pending owner should be the controller proxy (transferOwnership was called in deploy script)", async () => { + const pendingOwner = await positionToken.pendingOwner(); + expect(pendingOwner).to.equal(INSTITUTIONAL_VAULT_CONTROLLER); + }); + + it("NORMAL_TIMELOCK should not yet have setLiquidationAdapter permission on controller", async () => { + const allowed = await accessControlManager.hasPermission( + NORMAL_TIMELOCK, + INSTITUTIONAL_VAULT_CONTROLLER, + "setLiquidationAdapter(address)", + ); + expect(allowed).to.be.false; + }); + + it("NORMAL_TIMELOCK should not yet have acceptPositionTokenOwnership permission on controller", async () => { + const allowed = await accessControlManager.hasPermission( + NORMAL_TIMELOCK, + INSTITUTIONAL_VAULT_CONTROLLER, + "acceptPositionTokenOwnership()", + ); + expect(allowed).to.be.false; + }); + + it("controller liquidationAdapter should be address(0) before VIP", async () => { + const adapter = await controller.liquidationAdapter(); + expect(adapter).to.equal(ethers.constants.AddressZero); + }); + }); + + // ────────────────────────────────────────────────────────────────────── + // VIP execution + // ────────────────────────────────────────────────────────────────────── + testVip("VIP-664 [BNB Chain Testnet] Configure Institutional Fixed Rate Vault System", await vip664(), { + callbackAfterExecution: async txResponse => { + await expectEvents( + txResponse, + [ACCESS_CONTROL_MANAGER_ABI], + ["PermissionGranted"], + [EXPECTED_PERMISSION_GRANTED_EVENTS], + ); + }, + }); + + // ────────────────────────────────────────────────────────────────────── + // Post-VIP: ACM permission checks + // ────────────────────────────────────────────────────────────────────── + + describe("Post-VIP: verify setup", () => { + describe("InstitutionalVaultController — NORMAL_TIMELOCK permissions", () => { + for (const funcSig of CONTROLLER_FUNCTIONS) { + it(`NORMAL_TIMELOCK should have permission: ${funcSig}`, async () => { + const allowed = await accessControlManager.hasPermission( + NORMAL_TIMELOCK, + INSTITUTIONAL_VAULT_CONTROLLER, + funcSig, + ); + expect(allowed).to.be.true; + }); + } + }); + + describe("InstitutionalVaultController — FAST_TRACK_TIMELOCK permissions", () => { + for (const funcSig of CONTROLLER_FAST_TRACK_FUNCTIONS) { + it(`FAST_TRACK_TIMELOCK should have permission: ${funcSig}`, async () => { + const allowed = await accessControlManager.hasPermission( + FAST_TRACK_TIMELOCK, + INSTITUTIONAL_VAULT_CONTROLLER, + funcSig, + ); + expect(allowed).to.be.true; + }); + } + }); + + describe("InstitutionalVaultController — CRITICAL_TIMELOCK permissions", () => { + for (const funcSig of CONTROLLER_CRITICAL_FUNCTIONS) { + it(`CRITICAL_TIMELOCK should have permission: ${funcSig}`, async () => { + const allowed = await accessControlManager.hasPermission( + CRITICAL_TIMELOCK, + INSTITUTIONAL_VAULT_CONTROLLER, + funcSig, + ); + expect(allowed).to.be.true; + }); + } + }); + + describe("InstitutionalVaultController — GUARDIAN permissions", () => { + for (const funcSig of CONTROLLER_CRITICAL_FUNCTIONS) { + it(`GUARDIAN should have permission: ${funcSig}`, async () => { + const allowed = await accessControlManager.hasPermission(GUARDIAN, INSTITUTIONAL_VAULT_CONTROLLER, funcSig); + expect(allowed).to.be.true; + }); + } + }); + + describe("LiquidationAdapter — NORMAL_TIMELOCK permissions", () => { + for (const funcSig of ADAPTER_FUNCTIONS) { + it(`NORMAL_TIMELOCK should have permission: ${funcSig}`, async () => { + const allowed = await accessControlManager.hasPermission(NORMAL_TIMELOCK, LIQUIDATION_ADAPTER, funcSig); + expect(allowed).to.be.true; + }); + } + }); + + describe("LiquidationAdapter — FAST_TRACK_TIMELOCK permissions", () => { + for (const funcSig of ADAPTER_FAST_TRACK_FUNCTIONS) { + it(`FAST_TRACK_TIMELOCK should have permission: ${funcSig}`, async () => { + const allowed = await accessControlManager.hasPermission(FAST_TRACK_TIMELOCK, LIQUIDATION_ADAPTER, funcSig); + expect(allowed).to.be.true; + }); + } + }); + + describe("System wiring", () => { + it("controller liquidationAdapter should be set to the adapter proxy", async () => { + const adapter = await controller.liquidationAdapter(); + expect(adapter).to.equal(LIQUIDATION_ADAPTER); + }); + + it("InstitutionPositionToken owner should be the controller proxy (ownership accepted)", async () => { + const owner = await positionToken.owner(); + expect(owner).to.equal(INSTITUTIONAL_VAULT_CONTROLLER); + }); + }); + }); +}); diff --git a/vips/vip-664/bsctestnet.ts b/vips/vip-664/bsctestnet.ts new file mode 100644 index 000000000..f7d31923d --- /dev/null +++ b/vips/vip-664/bsctestnet.ts @@ -0,0 +1,167 @@ +import { NETWORK_ADDRESSES } from "src/networkAddresses"; +import { ProposalType } from "src/types"; +import { makeProposal } from "src/utils"; + +const { bsctestnet } = NETWORK_ADDRESSES; +const { NORMAL_TIMELOCK, FAST_TRACK_TIMELOCK, CRITICAL_TIMELOCK, GUARDIAN, ACCESS_CONTROL_MANAGER } = bsctestnet; + +// ────────────────────────────────────────────────────────────────────── +// Deployed addresses +// ────────────────────────────────────────────────────────────────────── + +export const INSTITUTIONAL_VAULT_CONTROLLER = "0x36bA78812Ffff64B9ec060a1F07FcFa2012f6F89"; +export const LIQUIDATION_ADAPTER = "0x69d79D60abD5A7080C9f178a44c5f1bf1A461541"; +export const INSTITUTION_POSITION_TOKEN = "0x377180882397718D4061d815Df32CF7DF8492f4F"; + +// ────────────────────────────────────────────────────────────────────── +// ACM-gated function signatures — InstitutionalVaultController +// ────────────────────────────────────────────────────────────────────── + +export const CONTROLLER_FUNCTIONS = [ + "acceptPositionTokenOwnership()", + "createVault(VaultConfig,InstitutionalConfig,RiskConfig,string,string)", + "openVault(address)", + "partialPauseVault(address)", + "completePauseVault(address)", + "unpauseVault(address)", + "closeVault(address)", + "sweep(address,address)", + "approvePositionTransfer(address)", + "revokePositionTransfer(address)", + "setLiquidationThreshold(address,uint256)", + "setLiquidationIncentive(address,uint256)", + "setLatePenaltyRate(address,uint256)", + "setVaultImplementation(address)", + "setLiquidationAdapter(address)", + "setOracle(address)", + "setProtocolShareReserve(address)", + "setComptroller(address)", + "setTreasury(address)", +]; + +export const CONTROLLER_FAST_TRACK_FUNCTIONS = [ + "partialPauseVault(address)", + "completePauseVault(address)", + "unpauseVault(address)", + "setLiquidationThreshold(address,uint256)", + "setLiquidationIncentive(address,uint256)", + "setLatePenaltyRate(address,uint256)", +]; + +export const CONTROLLER_CRITICAL_FUNCTIONS = ["partialPauseVault(address)", "completePauseVault(address)"]; + +// ────────────────────────────────────────────────────────────────────── +// ACM-gated function signatures — LiquidationAdapter +// ────────────────────────────────────────────────────────────────────── + +export const ADAPTER_FUNCTIONS = [ + "setLiquidatorWhitelist(address,bool)", + "setSettlerWhitelist(address,bool)", + "setProtocolLiquidationShare(uint256)", + "setCloseFactor(uint256)", + "sweepProtocolShareToReserve(address)", +]; + +export const ADAPTER_FAST_TRACK_FUNCTIONS = ["setProtocolLiquidationShare(uint256)", "setCloseFactor(uint256)"]; + +// Total PermissionGranted events: 19 + 6 + 2 + 2 + 5 + 2 = 36 +export const EXPECTED_PERMISSION_GRANTED_EVENTS = 36; + +/** + * Returns a giveCallPermission command for the ACM. + * @param contract Target contract the permission applies to. + * @param funcSig Exact Solidity function signature string (must match _checkAccessAllowed). + * @param account Address being granted the permission. + */ +const permission = (contract: string, funcSig: string, account: string) => ({ + target: ACCESS_CONTROL_MANAGER, + signature: "giveCallPermission(address,string,address)", + params: [contract, funcSig, account], +}); + +export const vip664 = () => { + const meta = { + version: "v1", + title: "VIP-664 [BNB Chain Testnet] Configure Institutional Fixed Rate Vault System", + description: `#### Summary + +If passed, this VIP will configure the Institutional Fixed Rate Vault system on BNB Chain Testnet: + +1. Grant ACM permissions to governance timelocks for all access-controlled functions on \`InstitutionalVaultController\` and \`LiquidationAdapter\`. +2. Set the \`LiquidationAdapter\` on the controller via \`setLiquidationAdapter()\`. +3. Complete the two-step position token ownership transfer via \`acceptPositionTokenOwnership()\`. + +#### Deployed Contracts + +- **InstitutionalVaultController** (proxy): ${INSTITUTIONAL_VAULT_CONTROLLER} +- **LiquidationAdapter** (proxy): ${LIQUIDATION_ADAPTER} +- **InstitutionPositionToken**: ${INSTITUTION_POSITION_TOKEN} + +#### Permission Summary + +| Timelock | Contract | Permissions | +|---|---|---| +| Normal | InstitutionalVaultController | 19 functions (all operational + setter functions) | +| Fast-track | InstitutionalVaultController | 6 functions (pause + risk params) | +| Critical | InstitutionalVaultController | 2 functions (pause only) | +| Guardian | InstitutionalVaultController | 2 functions (pause only) | +| Normal | LiquidationAdapter | 5 functions (all setter functions) | +| Fast-track | LiquidationAdapter | 2 functions (risk params) |`, + 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( + [ + // ────────────────────────────────────────────────────────────────────── + // Phase 1 — InstitutionalVaultController permissions + // ────────────────────────────────────────────────────────────────────── + + // NORMAL_TIMELOCK — all 19 ACM-gated controller functions + ...CONTROLLER_FUNCTIONS.map(sig => permission(INSTITUTIONAL_VAULT_CONTROLLER, sig, NORMAL_TIMELOCK)), + + // FAST_TRACK_TIMELOCK — emergency pause + risk parameter functions + ...CONTROLLER_FAST_TRACK_FUNCTIONS.map(sig => + permission(INSTITUTIONAL_VAULT_CONTROLLER, sig, FAST_TRACK_TIMELOCK), + ), + + // CRITICAL_TIMELOCK — critical pause functions only + ...CONTROLLER_CRITICAL_FUNCTIONS.map(sig => permission(INSTITUTIONAL_VAULT_CONTROLLER, sig, CRITICAL_TIMELOCK)), + + // GUARDIAN — critical pause functions + ...CONTROLLER_CRITICAL_FUNCTIONS.map(sig => permission(INSTITUTIONAL_VAULT_CONTROLLER, sig, GUARDIAN)), + + // ────────────────────────────────────────────────────────────────────── + // Phase 2 — LiquidationAdapter permissions + // ────────────────────────────────────────────────────────────────────── + + // NORMAL_TIMELOCK — all 5 ACM-gated adapter functions + ...ADAPTER_FUNCTIONS.map(sig => permission(LIQUIDATION_ADAPTER, sig, NORMAL_TIMELOCK)), + + // FAST_TRACK_TIMELOCK — risk parameter functions + ...ADAPTER_FAST_TRACK_FUNCTIONS.map(sig => permission(LIQUIDATION_ADAPTER, sig, FAST_TRACK_TIMELOCK)), + + // ────────────────────────────────────────────────────────────────────── + // Phase 3 — System wiring + // ────────────────────────────────────────────────────────────────────── + // Note: NORMAL_TIMELOCK must have the ACM permission for both calls below, + // which is granted in Phase 1 above. Commands execute atomically in order. + + { + target: INSTITUTIONAL_VAULT_CONTROLLER, + signature: "setLiquidationAdapter(address)", + params: [LIQUIDATION_ADAPTER], + }, + { + target: INSTITUTIONAL_VAULT_CONTROLLER, + signature: "acceptPositionTokenOwnership()", + params: [], + }, + ], + meta, + ProposalType.REGULAR, + ); +}; + +export default vip664; From 31d2c7009f8d371def6fc3e061dd08c13894a7fd Mon Sep 17 00:00:00 2001 From: GitGuru7 Date: Thu, 16 Apr 2026 19:13:20 +0530 Subject: [PATCH 2/5] feat: add accept ownership for controller and liquidity adapter --- .../vip-664/abi/InstitutionalLoanVault.json | 2 +- .../vip-664/abi/LiquidationAdapter.json | 37 +++++++++++ simulations/vip-664/bsctestnet.ts | 65 ++++++++++++++++--- vips/vip-664/bsctestnet.ts | 63 +++++++++++------- 4 files changed, 136 insertions(+), 31 deletions(-) create mode 100644 simulations/vip-664/abi/LiquidationAdapter.json diff --git a/simulations/vip-664/abi/InstitutionalLoanVault.json b/simulations/vip-664/abi/InstitutionalLoanVault.json index 3ea5bbd50..efe35a44d 100644 --- a/simulations/vip-664/abi/InstitutionalLoanVault.json +++ b/simulations/vip-664/abi/InstitutionalLoanVault.json @@ -1998,4 +1998,4 @@ "name": "ZeroRepayAmount", "inputs": [] } -] \ No newline at end of file +] diff --git a/simulations/vip-664/abi/LiquidationAdapter.json b/simulations/vip-664/abi/LiquidationAdapter.json new file mode 100644 index 000000000..c90cf602b --- /dev/null +++ b/simulations/vip-664/abi/LiquidationAdapter.json @@ -0,0 +1,37 @@ +[ + { + "inputs": [], + "name": "acceptOwnership", + "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": "protocolLiquidationShare", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "closeFactor", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + } +] diff --git a/simulations/vip-664/bsctestnet.ts b/simulations/vip-664/bsctestnet.ts index 36c042541..e6b01f82f 100644 --- a/simulations/vip-664/bsctestnet.ts +++ b/simulations/vip-664/bsctestnet.ts @@ -1,5 +1,6 @@ import { expect } from "chai"; import { Contract } from "ethers"; +import { parseEther } from "ethers/lib/utils"; import { ethers } from "hardhat"; import { NETWORK_ADDRESSES } from "src/networkAddresses"; import { expectEvents } from "src/utils"; @@ -8,26 +9,28 @@ import { forking, testVip } from "src/vip-framework"; import vip664, { ADAPTER_FAST_TRACK_FUNCTIONS, ADAPTER_FUNCTIONS, - CONTROLLER_CRITICAL_FUNCTIONS, - CONTROLLER_FAST_TRACK_FUNCTIONS, + ADAPTER_GUARDIAN_FUNCTIONS, CONTROLLER_FUNCTIONS, + CONTROLLER_GUARDIAN_FUNCTIONS, EXPECTED_PERMISSION_GRANTED_EVENTS, INSTITUTIONAL_VAULT_CONTROLLER, INSTITUTION_POSITION_TOKEN, LIQUIDATION_ADAPTER, } from "../../vips/vip-664/bsctestnet"; import ACCESS_CONTROL_MANAGER_ABI from "./abi/AccessControlManager.json"; -import INSTITUTION_POSITION_TOKEN_ABI from "./abi/InstitutionPositionToken.json"; import INSTITUTIONAL_VAULT_CONTROLLER_ABI from "./abi/InstitutionalVaultController.json"; +import INSTITUTION_POSITION_TOKEN_ABI from "./abi/InstitutionPositionToken.json"; +import LIQUIDATION_ADAPTER_ABI from "./abi/LiquidationAdapter.json"; const { bsctestnet } = NETWORK_ADDRESSES; const { NORMAL_TIMELOCK, FAST_TRACK_TIMELOCK, CRITICAL_TIMELOCK, GUARDIAN, ACCESS_CONTROL_MANAGER } = bsctestnet; -const FORK_BLOCK = 101981562; +const FORK_BLOCK = 102008713; forking(FORK_BLOCK, async () => { let accessControlManager: Contract; let controller: Contract; + let liquidationAdapter: Contract; let positionToken: Contract; before(async () => { @@ -37,6 +40,7 @@ forking(FORK_BLOCK, async () => { INSTITUTIONAL_VAULT_CONTROLLER_ABI, ethers.provider, ); + liquidationAdapter = new ethers.Contract(LIQUIDATION_ADAPTER, LIQUIDATION_ADAPTER_ABI, ethers.provider); positionToken = new ethers.Contract(INSTITUTION_POSITION_TOKEN, INSTITUTION_POSITION_TOKEN_ABI, ethers.provider); }); @@ -85,6 +89,26 @@ forking(FORK_BLOCK, async () => { expect(allowed).to.be.false; }); + it("controller pendingOwner should be NORMAL_TIMELOCK (transferOwnership was called in deploy script)", async () => { + const pendingOwner = await controller.pendingOwner(); + expect(pendingOwner).to.equal(NORMAL_TIMELOCK); + }); + + it("liquidationAdapter pendingOwner should be NORMAL_TIMELOCK (transferOwnership was called in deploy script)", async () => { + const pendingOwner = await liquidationAdapter.pendingOwner(); + expect(pendingOwner).to.equal(NORMAL_TIMELOCK); + }); + + it("liquidationAdapter protocolLiquidationShare should be 0.5e18", async () => { + const share = await liquidationAdapter.protocolLiquidationShare(); + expect(share).to.equal(parseEther("0.5")); + }); + + it("liquidationAdapter closeFactor should be 0.5e18", async () => { + const factor = await liquidationAdapter.closeFactor(); + expect(factor).to.equal(parseEther("0.5")); + }); + it("controller liquidationAdapter should be address(0) before VIP", async () => { const adapter = await controller.liquidationAdapter(); expect(adapter).to.equal(ethers.constants.AddressZero); @@ -106,7 +130,7 @@ forking(FORK_BLOCK, async () => { }); // ────────────────────────────────────────────────────────────────────── - // Post-VIP: ACM permission checks + // Post-VIP: ACM permission and system wiring checks // ────────────────────────────────────────────────────────────────────── describe("Post-VIP: verify setup", () => { @@ -124,7 +148,7 @@ forking(FORK_BLOCK, async () => { }); describe("InstitutionalVaultController — FAST_TRACK_TIMELOCK permissions", () => { - for (const funcSig of CONTROLLER_FAST_TRACK_FUNCTIONS) { + for (const funcSig of CONTROLLER_FUNCTIONS) { it(`FAST_TRACK_TIMELOCK should have permission: ${funcSig}`, async () => { const allowed = await accessControlManager.hasPermission( FAST_TRACK_TIMELOCK, @@ -137,7 +161,7 @@ forking(FORK_BLOCK, async () => { }); describe("InstitutionalVaultController — CRITICAL_TIMELOCK permissions", () => { - for (const funcSig of CONTROLLER_CRITICAL_FUNCTIONS) { + for (const funcSig of CONTROLLER_FUNCTIONS) { it(`CRITICAL_TIMELOCK should have permission: ${funcSig}`, async () => { const allowed = await accessControlManager.hasPermission( CRITICAL_TIMELOCK, @@ -150,7 +174,7 @@ forking(FORK_BLOCK, async () => { }); describe("InstitutionalVaultController — GUARDIAN permissions", () => { - for (const funcSig of CONTROLLER_CRITICAL_FUNCTIONS) { + for (const funcSig of CONTROLLER_GUARDIAN_FUNCTIONS) { it(`GUARDIAN should have permission: ${funcSig}`, async () => { const allowed = await accessControlManager.hasPermission(GUARDIAN, INSTITUTIONAL_VAULT_CONTROLLER, funcSig); expect(allowed).to.be.true; @@ -176,6 +200,31 @@ forking(FORK_BLOCK, async () => { } }); + describe("LiquidationAdapter — GUARDIAN permissions", () => { + for (const funcSig of ADAPTER_GUARDIAN_FUNCTIONS) { + it(`GUARDIAN should have permission: ${funcSig}`, async () => { + const allowed = await accessControlManager.hasPermission( + GUARDIAN, + LIQUIDATION_ADAPTER, + funcSig, + ); + expect(allowed).to.be.true; + }); + } + }); + + describe("Ownership acceptance", () => { + it("controller owner should be NORMAL_TIMELOCK", async () => { + const owner = await controller.owner(); + expect(owner).to.equal(NORMAL_TIMELOCK); + }); + + it("liquidationAdapter owner should be NORMAL_TIMELOCK", async () => { + const owner = await liquidationAdapter.owner(); + expect(owner).to.equal(NORMAL_TIMELOCK); + }); + }); + describe("System wiring", () => { it("controller liquidationAdapter should be set to the adapter proxy", async () => { const adapter = await controller.liquidationAdapter(); diff --git a/vips/vip-664/bsctestnet.ts b/vips/vip-664/bsctestnet.ts index f7d31923d..9497fbbf1 100644 --- a/vips/vip-664/bsctestnet.ts +++ b/vips/vip-664/bsctestnet.ts @@ -39,17 +39,15 @@ export const CONTROLLER_FUNCTIONS = [ "setTreasury(address)", ]; -export const CONTROLLER_FAST_TRACK_FUNCTIONS = [ +export const CONTROLLER_GUARDIAN_FUNCTIONS = [ "partialPauseVault(address)", "completePauseVault(address)", "unpauseVault(address)", - "setLiquidationThreshold(address,uint256)", - "setLiquidationIncentive(address,uint256)", - "setLatePenaltyRate(address,uint256)", + "openVault(address)", + "closeVault(address)", + "sweep(address,address)", ]; -export const CONTROLLER_CRITICAL_FUNCTIONS = ["partialPauseVault(address)", "completePauseVault(address)"]; - // ────────────────────────────────────────────────────────────────────── // ACM-gated function signatures — LiquidationAdapter // ────────────────────────────────────────────────────────────────────── @@ -64,8 +62,10 @@ export const ADAPTER_FUNCTIONS = [ export const ADAPTER_FAST_TRACK_FUNCTIONS = ["setProtocolLiquidationShare(uint256)", "setCloseFactor(uint256)"]; -// Total PermissionGranted events: 19 + 6 + 2 + 2 + 5 + 2 = 36 -export const EXPECTED_PERMISSION_GRANTED_EVENTS = 36; +export const ADAPTER_GUARDIAN_FUNCTIONS = ["sweepProtocolShareToReserve(address)"]; + +// Total PermissionGranted events: 19*3 + 6 + 5 + 2 + 1 = 71 +export const EXPECTED_PERMISSION_GRANTED_EVENTS = 71; /** * Returns a giveCallPermission command for the ACM. @@ -88,8 +88,9 @@ export const vip664 = () => { If passed, this VIP will configure the Institutional Fixed Rate Vault system on BNB Chain Testnet: 1. Grant ACM permissions to governance timelocks for all access-controlled functions on \`InstitutionalVaultController\` and \`LiquidationAdapter\`. -2. Set the \`LiquidationAdapter\` on the controller via \`setLiquidationAdapter()\`. -3. Complete the two-step position token ownership transfer via \`acceptPositionTokenOwnership()\`. +2. Accept ownership of \`InstitutionalVaultController\` and \`LiquidationAdapter\` (two-step Ownable2Step transfer initiated in deploy script). +3. Set the \`LiquidationAdapter\` on the controller via \`setLiquidationAdapter()\`. +4. Complete the two-step position token ownership transfer via \`acceptPositionTokenOwnership()\`. #### Deployed Contracts @@ -102,9 +103,9 @@ If passed, this VIP will configure the Institutional Fixed Rate Vault system on | Timelock | Contract | Permissions | |---|---|---| | Normal | InstitutionalVaultController | 19 functions (all operational + setter functions) | -| Fast-track | InstitutionalVaultController | 6 functions (pause + risk params) | -| Critical | InstitutionalVaultController | 2 functions (pause only) | -| Guardian | InstitutionalVaultController | 2 functions (pause only) | +| Fast-track | InstitutionalVaultController | 19 functions (all operational + setter functions) | +| Critical | InstitutionalVaultController | 19 functions (all operational + setter functions) | +| Guardian | InstitutionalVaultController | 6 functions (pause + open/close/sweep) | | Normal | LiquidationAdapter | 5 functions (all setter functions) | | Fast-track | LiquidationAdapter | 2 functions (risk params) |`, forDescription: "I agree that Venus Protocol should proceed with this proposal", @@ -121,16 +122,14 @@ If passed, this VIP will configure the Institutional Fixed Rate Vault system on // NORMAL_TIMELOCK — all 19 ACM-gated controller functions ...CONTROLLER_FUNCTIONS.map(sig => permission(INSTITUTIONAL_VAULT_CONTROLLER, sig, NORMAL_TIMELOCK)), - // FAST_TRACK_TIMELOCK — emergency pause + risk parameter functions - ...CONTROLLER_FAST_TRACK_FUNCTIONS.map(sig => - permission(INSTITUTIONAL_VAULT_CONTROLLER, sig, FAST_TRACK_TIMELOCK), - ), + // FAST_TRACK_TIMELOCK — all 19 ACM-gated controller functions + ...CONTROLLER_FUNCTIONS.map(sig => permission(INSTITUTIONAL_VAULT_CONTROLLER, sig, FAST_TRACK_TIMELOCK)), - // CRITICAL_TIMELOCK — critical pause functions only - ...CONTROLLER_CRITICAL_FUNCTIONS.map(sig => permission(INSTITUTIONAL_VAULT_CONTROLLER, sig, CRITICAL_TIMELOCK)), + // CRITICAL_TIMELOCK — all 19 ACM-gated controller functions + ...CONTROLLER_FUNCTIONS.map(sig => permission(INSTITUTIONAL_VAULT_CONTROLLER, sig, CRITICAL_TIMELOCK)), - // GUARDIAN — critical pause functions - ...CONTROLLER_CRITICAL_FUNCTIONS.map(sig => permission(INSTITUTIONAL_VAULT_CONTROLLER, sig, GUARDIAN)), + // GUARDIAN — pause + operational functions + ...CONTROLLER_GUARDIAN_FUNCTIONS.map(sig => permission(INSTITUTIONAL_VAULT_CONTROLLER, sig, GUARDIAN)), // ────────────────────────────────────────────────────────────────────── // Phase 2 — LiquidationAdapter permissions @@ -142,8 +141,28 @@ If passed, this VIP will configure the Institutional Fixed Rate Vault system on // FAST_TRACK_TIMELOCK — risk parameter functions ...ADAPTER_FAST_TRACK_FUNCTIONS.map(sig => permission(LIQUIDATION_ADAPTER, sig, FAST_TRACK_TIMELOCK)), + // GUARDIAN — sweep protocol share to reserve + ...ADAPTER_GUARDIAN_FUNCTIONS.map(sig => permission(LIQUIDATION_ADAPTER, sig, GUARDIAN)), + + // ────────────────────────────────────────────────────────────────────── + // Phase 3 — Ownership acceptance + // ────────────────────────────────────────────────────────────────────── + // Deploy script called transferOwnership(NORMAL_TIMELOCK) on both contracts. + // acceptOwnership() completes the Ownable2Step transfer. + + { + target: INSTITUTIONAL_VAULT_CONTROLLER, + signature: "acceptOwnership()", + params: [], + }, + { + target: LIQUIDATION_ADAPTER, + signature: "acceptOwnership()", + params: [], + }, + // ────────────────────────────────────────────────────────────────────── - // Phase 3 — System wiring + // Phase 4 — System wiring // ────────────────────────────────────────────────────────────────────── // Note: NORMAL_TIMELOCK must have the ACM permission for both calls below, // which is granted in Phase 1 above. Commands execute atomically in order. From 34e14a52ec2eda38535735b44269e72d0bb6f283 Mon Sep 17 00:00:00 2001 From: GitGuru7 Date: Thu, 16 Apr 2026 19:20:25 +0530 Subject: [PATCH 3/5] feat: add accept ownership for controller and liquidity adapter --- simulations/vip-664/bsctestnet.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/simulations/vip-664/bsctestnet.ts b/simulations/vip-664/bsctestnet.ts index e6b01f82f..632293396 100644 --- a/simulations/vip-664/bsctestnet.ts +++ b/simulations/vip-664/bsctestnet.ts @@ -18,8 +18,8 @@ import vip664, { LIQUIDATION_ADAPTER, } from "../../vips/vip-664/bsctestnet"; import ACCESS_CONTROL_MANAGER_ABI from "./abi/AccessControlManager.json"; -import INSTITUTIONAL_VAULT_CONTROLLER_ABI from "./abi/InstitutionalVaultController.json"; import INSTITUTION_POSITION_TOKEN_ABI from "./abi/InstitutionPositionToken.json"; +import INSTITUTIONAL_VAULT_CONTROLLER_ABI from "./abi/InstitutionalVaultController.json"; import LIQUIDATION_ADAPTER_ABI from "./abi/LiquidationAdapter.json"; const { bsctestnet } = NETWORK_ADDRESSES; @@ -203,11 +203,7 @@ forking(FORK_BLOCK, async () => { describe("LiquidationAdapter — GUARDIAN permissions", () => { for (const funcSig of ADAPTER_GUARDIAN_FUNCTIONS) { it(`GUARDIAN should have permission: ${funcSig}`, async () => { - const allowed = await accessControlManager.hasPermission( - GUARDIAN, - LIQUIDATION_ADAPTER, - funcSig, - ); + const allowed = await accessControlManager.hasPermission(GUARDIAN, LIQUIDATION_ADAPTER, funcSig); expect(allowed).to.be.true; }); } From 6d3b08a7e0569e510c1a550266ad3f120fbca590 Mon Sep 17 00:00:00 2001 From: GitGuru7 Date: Thu, 16 Apr 2026 19:36:42 +0530 Subject: [PATCH 4/5] feat: extend permissions to all timelocks, add guardian whitelist and adapter permissions --- simulations/vip-664/bsctestnet.ts | 12 ++++++-- vips/vip-664/bsctestnet.ts | 50 +++++++++++++++++++++++-------- 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/simulations/vip-664/bsctestnet.ts b/simulations/vip-664/bsctestnet.ts index 632293396..d958dd5b7 100644 --- a/simulations/vip-664/bsctestnet.ts +++ b/simulations/vip-664/bsctestnet.ts @@ -7,7 +7,6 @@ import { expectEvents } from "src/utils"; import { forking, testVip } from "src/vip-framework"; import vip664, { - ADAPTER_FAST_TRACK_FUNCTIONS, ADAPTER_FUNCTIONS, ADAPTER_GUARDIAN_FUNCTIONS, CONTROLLER_FUNCTIONS, @@ -191,8 +190,17 @@ forking(FORK_BLOCK, async () => { } }); + describe("LiquidationAdapter — CRITICAL_TIMELOCK permissions", () => { + for (const funcSig of ADAPTER_FUNCTIONS) { + it(`CRITICAL_TIMELOCK should have permission: ${funcSig}`, async () => { + const allowed = await accessControlManager.hasPermission(CRITICAL_TIMELOCK, LIQUIDATION_ADAPTER, funcSig); + expect(allowed).to.be.true; + }); + } + }); + describe("LiquidationAdapter — FAST_TRACK_TIMELOCK permissions", () => { - for (const funcSig of ADAPTER_FAST_TRACK_FUNCTIONS) { + for (const funcSig of ADAPTER_FUNCTIONS) { it(`FAST_TRACK_TIMELOCK should have permission: ${funcSig}`, async () => { const allowed = await accessControlManager.hasPermission(FAST_TRACK_TIMELOCK, LIQUIDATION_ADAPTER, funcSig); expect(allowed).to.be.true; diff --git a/vips/vip-664/bsctestnet.ts b/vips/vip-664/bsctestnet.ts index 9497fbbf1..0aa4041fc 100644 --- a/vips/vip-664/bsctestnet.ts +++ b/vips/vip-664/bsctestnet.ts @@ -60,12 +60,14 @@ export const ADAPTER_FUNCTIONS = [ "sweepProtocolShareToReserve(address)", ]; -export const ADAPTER_FAST_TRACK_FUNCTIONS = ["setProtocolLiquidationShare(uint256)", "setCloseFactor(uint256)"]; - -export const ADAPTER_GUARDIAN_FUNCTIONS = ["sweepProtocolShareToReserve(address)"]; +export const ADAPTER_GUARDIAN_FUNCTIONS = [ + "sweepProtocolShareToReserve(address)", + "setSettlerWhitelist(address,bool)", + "setLiquidatorWhitelist(address,bool)", +]; -// Total PermissionGranted events: 19*3 + 6 + 5 + 2 + 1 = 71 -export const EXPECTED_PERMISSION_GRANTED_EVENTS = 71; +// Total PermissionGranted events: 19*3 + 6 + 5*3 + 3 = 81 +export const EXPECTED_PERMISSION_GRANTED_EVENTS = 81; /** * Returns a giveCallPermission command for the ACM. @@ -87,10 +89,12 @@ export const vip664 = () => { If passed, this VIP will configure the Institutional Fixed Rate Vault system on BNB Chain Testnet: -1. Grant ACM permissions to governance timelocks for all access-controlled functions on \`InstitutionalVaultController\` and \`LiquidationAdapter\`. -2. Accept ownership of \`InstitutionalVaultController\` and \`LiquidationAdapter\` (two-step Ownable2Step transfer initiated in deploy script). -3. Set the \`LiquidationAdapter\` on the controller via \`setLiquidationAdapter()\`. -4. Complete the two-step position token ownership transfer via \`acceptPositionTokenOwnership()\`. +1. Grant ACM permissions to all three governance timelocks (Normal, Fast-track, Critical) for all access-controlled functions on \`InstitutionalVaultController\` and \`LiquidationAdapter\`. +2. Grant ACM permissions to the Guardian for operational functions on \`InstitutionalVaultController\` (pause/unpause, open/close, sweep) and \`LiquidationAdapter\` (sweep, whitelist management). +3. Accept ownership of \`InstitutionalVaultController\` and \`LiquidationAdapter\` (two-step Ownable2Step transfer initiated in deploy script). +4. Set the \`LiquidationAdapter\` on the controller via \`setLiquidationAdapter()\`. +5. Complete the two-step position token ownership transfer via \`acceptPositionTokenOwnership()\`. +6. Whitelist the Guardian as a liquidator and settler on the \`LiquidationAdapter\`. #### Deployed Contracts @@ -105,9 +109,11 @@ If passed, this VIP will configure the Institutional Fixed Rate Vault system on | Normal | InstitutionalVaultController | 19 functions (all operational + setter functions) | | Fast-track | InstitutionalVaultController | 19 functions (all operational + setter functions) | | Critical | InstitutionalVaultController | 19 functions (all operational + setter functions) | -| Guardian | InstitutionalVaultController | 6 functions (pause + open/close/sweep) | +| Guardian | InstitutionalVaultController | 6 functions (pause/unpause + open/close/sweep) | | Normal | LiquidationAdapter | 5 functions (all setter functions) | -| Fast-track | LiquidationAdapter | 2 functions (risk params) |`, +| Fast-track | LiquidationAdapter | 5 functions (all setter functions) | +| Critical | LiquidationAdapter | 5 functions (all setter functions) | +| Guardian | LiquidationAdapter | 3 functions (sweep + whitelist management) |`, 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", @@ -138,8 +144,11 @@ If passed, this VIP will configure the Institutional Fixed Rate Vault system on // NORMAL_TIMELOCK — all 5 ACM-gated adapter functions ...ADAPTER_FUNCTIONS.map(sig => permission(LIQUIDATION_ADAPTER, sig, NORMAL_TIMELOCK)), - // FAST_TRACK_TIMELOCK — risk parameter functions - ...ADAPTER_FAST_TRACK_FUNCTIONS.map(sig => permission(LIQUIDATION_ADAPTER, sig, FAST_TRACK_TIMELOCK)), + // FAST_TRACK_TIMELOCK — all 5 ACM-gated adapter functions + ...ADAPTER_FUNCTIONS.map(sig => permission(LIQUIDATION_ADAPTER, sig, FAST_TRACK_TIMELOCK)), + + // CRITICAL_TIMELOCK — all 5 ACM-gated adapter functions + ...ADAPTER_FUNCTIONS.map(sig => permission(LIQUIDATION_ADAPTER, sig, CRITICAL_TIMELOCK)), // GUARDIAN — sweep protocol share to reserve ...ADAPTER_GUARDIAN_FUNCTIONS.map(sig => permission(LIQUIDATION_ADAPTER, sig, GUARDIAN)), @@ -177,6 +186,21 @@ If passed, this VIP will configure the Institutional Fixed Rate Vault system on signature: "acceptPositionTokenOwnership()", params: [], }, + + // ────────────────────────────────────────────────────────────────────── + // Phase 5 — Guardian whitelist configuration + // ────────────────────────────────────────────────────────────────────── + + { + target: LIQUIDATION_ADAPTER, + signature: "setLiquidatorWhitelist(address,bool)", + params: [GUARDIAN, true], + }, + { + target: LIQUIDATION_ADAPTER, + signature: "setSettlerWhitelist(address,bool)", + params: [GUARDIAN, true], + }, ], meta, ProposalType.REGULAR, From 02f84d8c57d18fc045eda432233b5e7e4fc3ed80 Mon Sep 17 00:00:00 2001 From: GitGuru7 Date: Fri, 17 Apr 2026 12:49:19 +0530 Subject: [PATCH 5/5] feat: use ACMCommandsAggregator for batched permission grants in VIP-664 --- simulations/vip-664/abi/ACMAggregator.json | 118 +++++++++++++++++++++ simulations/vip-664/bsctestnet.ts | 35 +++++- vips/vip-664/addGrantPermissions.ts | 56 ++++++++++ vips/vip-664/bsctestnet.ts | 109 ++++++++++--------- 4 files changed, 267 insertions(+), 51 deletions(-) create mode 100644 simulations/vip-664/abi/ACMAggregator.json create mode 100644 vips/vip-664/addGrantPermissions.ts diff --git a/simulations/vip-664/abi/ACMAggregator.json b/simulations/vip-664/abi/ACMAggregator.json new file mode 100644 index 000000000..0974d4ab3 --- /dev/null +++ b/simulations/vip-664/abi/ACMAggregator.json @@ -0,0 +1,118 @@ +[ + { + "inputs": [{ "internalType": "contract IAccessControlManagerV8", "name": "_acm", "type": "address" }], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { "inputs": [], "name": "EmptyPermissions", "type": "error" }, + { "inputs": [], "name": "ZeroAddressNotAllowed", "type": "error" }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "internalType": "uint256", "name": "index", "type": "uint256" }], + "name": "GrantPermissionsAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "internalType": "uint256", "name": "index", "type": "uint256" }], + "name": "GrantPermissionsExecuted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "internalType": "uint256", "name": "index", "type": "uint256" }], + "name": "RevokePermissionsAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "internalType": "uint256", "name": "index", "type": "uint256" }], + "name": "RevokePermissionsExecuted", + "type": "event" + }, + { + "inputs": [], + "name": "ACM", + "outputs": [{ "internalType": "contract IAccessControlManagerV8", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "contractAddress", "type": "address" }, + { "internalType": "string", "name": "functionSig", "type": "string" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "internalType": "struct ACMCommandsAggregator.Permission[]", + "name": "_permissions", + "type": "tuple[]" + } + ], + "name": "addGrantPermissions", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "contractAddress", "type": "address" }, + { "internalType": "string", "name": "functionSig", "type": "string" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "internalType": "struct ACMCommandsAggregator.Permission[]", + "name": "_permissions", + "type": "tuple[]" + } + ], + "name": "addRevokePermissions", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "index", "type": "uint256" }], + "name": "executeGrantPermissions", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "index", "type": "uint256" }], + "name": "executeRevokePermissions", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "", "type": "uint256" }, + { "internalType": "uint256", "name": "", "type": "uint256" } + ], + "name": "grantPermissions", + "outputs": [ + { "internalType": "address", "name": "contractAddress", "type": "address" }, + { "internalType": "string", "name": "functionSig", "type": "string" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "", "type": "uint256" }, + { "internalType": "uint256", "name": "", "type": "uint256" } + ], + "name": "revokePermissions", + "outputs": [ + { "internalType": "address", "name": "contractAddress", "type": "address" }, + { "internalType": "string", "name": "functionSig", "type": "string" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/simulations/vip-664/bsctestnet.ts b/simulations/vip-664/bsctestnet.ts index d958dd5b7..9eb5d5841 100644 --- a/simulations/vip-664/bsctestnet.ts +++ b/simulations/vip-664/bsctestnet.ts @@ -7,15 +7,20 @@ import { expectEvents } from "src/utils"; import { forking, testVip } from "src/vip-framework"; import vip664, { + ACM_AGGREGATOR, + ACM_AGGREGATOR_INDEX, ADAPTER_FUNCTIONS, ADAPTER_GUARDIAN_FUNCTIONS, CONTROLLER_FUNCTIONS, CONTROLLER_GUARDIAN_FUNCTIONS, + DEFAULT_ADMIN_ROLE, EXPECTED_PERMISSION_GRANTED_EVENTS, INSTITUTIONAL_VAULT_CONTROLLER, INSTITUTION_POSITION_TOKEN, LIQUIDATION_ADAPTER, + PERMISSIONS, } from "../../vips/vip-664/bsctestnet"; +import ACM_AGGREGATOR_ABI from "./abi/ACMAggregator.json"; import ACCESS_CONTROL_MANAGER_ABI from "./abi/AccessControlManager.json"; import INSTITUTION_POSITION_TOKEN_ABI from "./abi/InstitutionPositionToken.json"; import INSTITUTIONAL_VAULT_CONTROLLER_ABI from "./abi/InstitutionalVaultController.json"; @@ -24,13 +29,14 @@ import LIQUIDATION_ADAPTER_ABI from "./abi/LiquidationAdapter.json"; const { bsctestnet } = NETWORK_ADDRESSES; const { NORMAL_TIMELOCK, FAST_TRACK_TIMELOCK, CRITICAL_TIMELOCK, GUARDIAN, ACCESS_CONTROL_MANAGER } = bsctestnet; -const FORK_BLOCK = 102008713; +const FORK_BLOCK = 102151520; forking(FORK_BLOCK, async () => { let accessControlManager: Contract; let controller: Contract; let liquidationAdapter: Contract; let positionToken: Contract; + let acmAggregator: Contract; before(async () => { accessControlManager = new ethers.Contract(ACCESS_CONTROL_MANAGER, ACCESS_CONTROL_MANAGER_ABI, ethers.provider); @@ -41,6 +47,7 @@ forking(FORK_BLOCK, async () => { ); liquidationAdapter = new ethers.Contract(LIQUIDATION_ADAPTER, LIQUIDATION_ADAPTER_ABI, ethers.provider); positionToken = new ethers.Contract(INSTITUTION_POSITION_TOKEN, INSTITUTION_POSITION_TOKEN_ABI, ethers.provider); + acmAggregator = new ethers.Contract(ACM_AGGREGATOR, ACM_AGGREGATOR_ABI, ethers.provider); }); // ────────────────────────────────────────────────────────────────────── @@ -112,6 +119,21 @@ forking(FORK_BLOCK, async () => { const adapter = await controller.liquidationAdapter(); expect(adapter).to.equal(ethers.constants.AddressZero); }); + + it("ACM Aggregator stored permissions should match PERMISSIONS array", async () => { + for (let i = 0; i < PERMISSIONS.length; i++) { + const stored = await acmAggregator.grantPermissions(ACM_AGGREGATOR_INDEX, i); + expect(stored.contractAddress.toLowerCase()).to.equal( + PERMISSIONS[i][0].toLowerCase(), + `Permission ${i} contractAddress mismatch`, + ); + expect(stored.functionSig).to.equal(PERMISSIONS[i][1], `Permission ${i} functionSig mismatch`); + expect(stored.account.toLowerCase()).to.equal( + PERMISSIONS[i][2].toLowerCase(), + `Permission ${i} account mismatch`, + ); + } + }); }); // ────────────────────────────────────────────────────────────────────── @@ -122,9 +144,10 @@ forking(FORK_BLOCK, async () => { await expectEvents( txResponse, [ACCESS_CONTROL_MANAGER_ABI], - ["PermissionGranted"], - [EXPECTED_PERMISSION_GRANTED_EVENTS], + ["PermissionGranted", "RoleGranted", "RoleRevoked"], + [EXPECTED_PERMISSION_GRANTED_EVENTS, EXPECTED_PERMISSION_GRANTED_EVENTS + 1, 1], ); + await expectEvents(txResponse, [ACM_AGGREGATOR_ABI], ["GrantPermissionsExecuted"], [1]); }, }); @@ -229,6 +252,12 @@ forking(FORK_BLOCK, async () => { }); }); + describe("ACM Aggregator cleanup", () => { + it("DEFAULT_ADMIN_ROLE should be revoked from ACM Aggregator", async () => { + expect(await accessControlManager.hasRole(DEFAULT_ADMIN_ROLE, ACM_AGGREGATOR)).to.be.false; + }); + }); + describe("System wiring", () => { it("controller liquidationAdapter should be set to the adapter proxy", async () => { const adapter = await controller.liquidationAdapter(); diff --git a/vips/vip-664/addGrantPermissions.ts b/vips/vip-664/addGrantPermissions.ts new file mode 100644 index 000000000..d18cae838 --- /dev/null +++ b/vips/vip-664/addGrantPermissions.ts @@ -0,0 +1,56 @@ +import { ethers } from "hardhat"; + +import { ACM_AGGREGATOR, PERMISSIONS } from "./bsctestnet"; + +const ACM_AGGREGATOR_ABI = [ + { + inputs: [ + { + components: [ + { internalType: "address", name: "contractAddress", type: "address" }, + { internalType: "string", name: "functionSig", type: "string" }, + { internalType: "address", name: "account", type: "address" }, + ], + internalType: "struct ACMCommandsAggregator.Permission[]", + name: "_PERMISSIONS", + type: "tuple[]", + }, + ], + name: "addGrantPermissions", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + anonymous: false, + inputs: [{ indexed: false, internalType: "uint256", name: "index", type: "uint256" }], + name: "GrantPermissionsAdded", + type: "event", + }, +]; + +async function main() { + console.log(`Total PERMISSIONS to add: ${PERMISSIONS.length}`); + + const [signer] = await ethers.getSigners(); + const acmAggregator = new ethers.Contract(ACM_AGGREGATOR, ACM_AGGREGATOR_ABI, signer); + + const tx = await acmAggregator.addGrantPermissions(PERMISSIONS); + console.log(`Transaction hash: ${tx.hash}`); + + const receipt = await tx.wait(); + const event = receipt.events?.find((e: { event: string }) => e.event === "GrantPermissionsAdded"); + + if (event) { + console.log(`GrantPermissionsAdded index: ${event.args?.index.toString()}`); + } else { + console.log("GrantPermissionsAdded event not found in receipt"); + } +} + +main() + .then(() => process.exit(0)) + .catch(error => { + console.error(error); + process.exit(1); + }); diff --git a/vips/vip-664/bsctestnet.ts b/vips/vip-664/bsctestnet.ts index 0aa4041fc..8dc121e56 100644 --- a/vips/vip-664/bsctestnet.ts +++ b/vips/vip-664/bsctestnet.ts @@ -13,6 +13,14 @@ export const INSTITUTIONAL_VAULT_CONTROLLER = "0x36bA78812Ffff64B9ec060a1F07FcFa export const LIQUIDATION_ADAPTER = "0x69d79D60abD5A7080C9f178a44c5f1bf1A461541"; export const INSTITUTION_POSITION_TOKEN = "0x377180882397718D4061d815Df32CF7DF8492f4F"; +// ────────────────────────────────────────────────────────────────────── +// ACM Aggregator +// ────────────────────────────────────────────────────────────────────── + +export const ACM_AGGREGATOR = "0xB59523628D92f914ec6624Be4281397E8aFD71EF"; +export const DEFAULT_ADMIN_ROLE = "0x0000000000000000000000000000000000000000000000000000000000000000"; +export const ACM_AGGREGATOR_INDEX = 1; + // ────────────────────────────────────────────────────────────────────── // ACM-gated function signatures — InstitutionalVaultController // ────────────────────────────────────────────────────────────────────── @@ -69,17 +77,33 @@ export const ADAPTER_GUARDIAN_FUNCTIONS = [ // Total PermissionGranted events: 19*3 + 6 + 5*3 + 3 = 81 export const EXPECTED_PERMISSION_GRANTED_EVENTS = 81; -/** - * Returns a giveCallPermission command for the ACM. - * @param contract Target contract the permission applies to. - * @param funcSig Exact Solidity function signature string (must match _checkAccessAllowed). - * @param account Address being granted the permission. - */ -const permission = (contract: string, funcSig: string, account: string) => ({ - target: ACCESS_CONTROL_MANAGER, - signature: "giveCallPermission(address,string,address)", - params: [contract, funcSig, account], -}); +// ────────────────────────────────────────────────────────────────────── +// Full permissions array (pre-loaded into ACM Aggregator via script) +// ────────────────────────────────────────────────────────────────────── + +export const PERMISSIONS: [string, string, string][] = [ + // InstitutionalVaultController — all 19 functions × 3 timelocks + ...CONTROLLER_FUNCTIONS.flatMap(sig => [ + [INSTITUTIONAL_VAULT_CONTROLLER, sig, NORMAL_TIMELOCK] as [string, string, string], + [INSTITUTIONAL_VAULT_CONTROLLER, sig, FAST_TRACK_TIMELOCK] as [string, string, string], + [INSTITUTIONAL_VAULT_CONTROLLER, sig, CRITICAL_TIMELOCK] as [string, string, string], + ]), + + // InstitutionalVaultController — 6 guardian functions + ...CONTROLLER_GUARDIAN_FUNCTIONS.map( + sig => [INSTITUTIONAL_VAULT_CONTROLLER, sig, GUARDIAN] as [string, string, string], + ), + + // LiquidationAdapter — all 5 functions × 3 timelocks + ...ADAPTER_FUNCTIONS.flatMap(sig => [ + [LIQUIDATION_ADAPTER, sig, NORMAL_TIMELOCK] as [string, string, string], + [LIQUIDATION_ADAPTER, sig, FAST_TRACK_TIMELOCK] as [string, string, string], + [LIQUIDATION_ADAPTER, sig, CRITICAL_TIMELOCK] as [string, string, string], + ]), + + // LiquidationAdapter — 3 guardian functions + ...ADAPTER_GUARDIAN_FUNCTIONS.map(sig => [LIQUIDATION_ADAPTER, sig, GUARDIAN] as [string, string, string]), +]; export const vip664 = () => { const meta = { @@ -89,12 +113,11 @@ export const vip664 = () => { If passed, this VIP will configure the Institutional Fixed Rate Vault system on BNB Chain Testnet: -1. Grant ACM permissions to all three governance timelocks (Normal, Fast-track, Critical) for all access-controlled functions on \`InstitutionalVaultController\` and \`LiquidationAdapter\`. -2. Grant ACM permissions to the Guardian for operational functions on \`InstitutionalVaultController\` (pause/unpause, open/close, sweep) and \`LiquidationAdapter\` (sweep, whitelist management). -3. Accept ownership of \`InstitutionalVaultController\` and \`LiquidationAdapter\` (two-step Ownable2Step transfer initiated in deploy script). -4. Set the \`LiquidationAdapter\` on the controller via \`setLiquidationAdapter()\`. -5. Complete the two-step position token ownership transfer via \`acceptPositionTokenOwnership()\`. -6. Whitelist the Guardian as a liquidator and settler on the \`LiquidationAdapter\`. +1. Grant ACM permissions (81 total) via \`ACMCommandsAggregator\` to all three governance timelocks (Normal, Fast-track, Critical) for all access-controlled functions on \`InstitutionalVaultController\` and \`LiquidationAdapter\`, and to the Guardian for operational functions on both contracts. +2. Accept ownership of \`InstitutionalVaultController\` and \`LiquidationAdapter\` (two-step Ownable2Step transfer initiated in deploy script). +3. Set the \`LiquidationAdapter\` on the controller via \`setLiquidationAdapter()\`. +4. Complete the two-step position token ownership transfer via \`acceptPositionTokenOwnership()\`. +5. Whitelist the Guardian as a liquidator and settler on the \`LiquidationAdapter\`. #### Deployed Contracts @@ -122,39 +145,29 @@ If passed, this VIP will configure the Institutional Fixed Rate Vault system on return makeProposal( [ // ────────────────────────────────────────────────────────────────────── - // Phase 1 — InstitutionalVaultController permissions - // ────────────────────────────────────────────────────────────────────── - - // NORMAL_TIMELOCK — all 19 ACM-gated controller functions - ...CONTROLLER_FUNCTIONS.map(sig => permission(INSTITUTIONAL_VAULT_CONTROLLER, sig, NORMAL_TIMELOCK)), - - // FAST_TRACK_TIMELOCK — all 19 ACM-gated controller functions - ...CONTROLLER_FUNCTIONS.map(sig => permission(INSTITUTIONAL_VAULT_CONTROLLER, sig, FAST_TRACK_TIMELOCK)), - - // CRITICAL_TIMELOCK — all 19 ACM-gated controller functions - ...CONTROLLER_FUNCTIONS.map(sig => permission(INSTITUTIONAL_VAULT_CONTROLLER, sig, CRITICAL_TIMELOCK)), - - // GUARDIAN — pause + operational functions - ...CONTROLLER_GUARDIAN_FUNCTIONS.map(sig => permission(INSTITUTIONAL_VAULT_CONTROLLER, sig, GUARDIAN)), - - // ────────────────────────────────────────────────────────────────────── - // Phase 2 — LiquidationAdapter permissions + // Phase 1 — ACM permissions via Aggregator // ────────────────────────────────────────────────────────────────────── + // Permissions were pre-loaded into ACM Aggregator via addGrantPermissions.ts. + // Grant admin role, execute batched permissions, then revoke admin role. - // NORMAL_TIMELOCK — all 5 ACM-gated adapter functions - ...ADAPTER_FUNCTIONS.map(sig => permission(LIQUIDATION_ADAPTER, sig, NORMAL_TIMELOCK)), - - // FAST_TRACK_TIMELOCK — all 5 ACM-gated adapter functions - ...ADAPTER_FUNCTIONS.map(sig => permission(LIQUIDATION_ADAPTER, sig, FAST_TRACK_TIMELOCK)), - - // CRITICAL_TIMELOCK — all 5 ACM-gated adapter functions - ...ADAPTER_FUNCTIONS.map(sig => permission(LIQUIDATION_ADAPTER, sig, CRITICAL_TIMELOCK)), - - // GUARDIAN — sweep protocol share to reserve - ...ADAPTER_GUARDIAN_FUNCTIONS.map(sig => permission(LIQUIDATION_ADAPTER, sig, GUARDIAN)), + { + target: ACCESS_CONTROL_MANAGER, + signature: "grantRole(bytes32,address)", + params: [DEFAULT_ADMIN_ROLE, ACM_AGGREGATOR], + }, + { + target: ACM_AGGREGATOR, + signature: "executeGrantPermissions(uint256)", + params: [ACM_AGGREGATOR_INDEX], + }, + { + target: ACCESS_CONTROL_MANAGER, + signature: "revokeRole(bytes32,address)", + params: [DEFAULT_ADMIN_ROLE, ACM_AGGREGATOR], + }, // ────────────────────────────────────────────────────────────────────── - // Phase 3 — Ownership acceptance + // Phase 2 — Ownership acceptance // ────────────────────────────────────────────────────────────────────── // Deploy script called transferOwnership(NORMAL_TIMELOCK) on both contracts. // acceptOwnership() completes the Ownable2Step transfer. @@ -171,7 +184,7 @@ If passed, this VIP will configure the Institutional Fixed Rate Vault system on }, // ────────────────────────────────────────────────────────────────────── - // Phase 4 — System wiring + // Phase 3 — System wiring // ────────────────────────────────────────────────────────────────────── // Note: NORMAL_TIMELOCK must have the ACM permission for both calls below, // which is granted in Phase 1 above. Commands execute atomically in order. @@ -188,7 +201,7 @@ If passed, this VIP will configure the Institutional Fixed Rate Vault system on }, // ────────────────────────────────────────────────────────────────────── - // Phase 5 — Guardian whitelist configuration + // Phase 4 — Guardian whitelist configuration // ────────────────────────────────────────────────────────────────────── {