diff --git a/simulations/vip-680/abi/AccessControlManager.json b/simulations/vip-680/abi/AccessControlManager.json new file mode 100644 index 000000000..4a118fcc4 --- /dev/null +++ b/simulations/vip-680/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-680/abi/Hub.json b/simulations/vip-680/abi/Hub.json new file mode 100644 index 000000000..bd16f91fb --- /dev/null +++ b/simulations/vip-680/abi/Hub.json @@ -0,0 +1,2210 @@ +[ + { + "type": "constructor", + "inputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "BPS_DENOMINATOR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint16", + "internalType": "uint16" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "EXP_SCALE", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "MAX_DECIMALS_OFFSET", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint8", + "internalType": "uint8" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "MAX_FEE_BPS", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint16", + "internalType": "uint16" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "MAX_MGMT_FEE_PER_ACCRUAL_BPS", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint16", + "internalType": "uint16" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "MAX_REDEEM_FEE_BPS", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint16", + "internalType": "uint16" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "SECONDS_PER_YEAR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "acceptOwnership", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "accessControlManager", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "contract IAccessControlManagerV8" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "accrueFees", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "addYieldGroup", + "inputs": [ + { + "name": "yieldGroup", + "type": "address", + "internalType": "address" + }, + { + "name": "absoluteCap", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "percentageCapBps", + "type": "uint16", + "internalType": "uint16" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "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": "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": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "depositWithConsent", + "inputs": [ + { + "name": "assets", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "receiver", + "type": "address", + "internalType": "address" + }, + { + "name": "consentHash", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "outputs": [ + { + "name": "shares", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "emergencyReallocate", + "inputs": [ + { + "name": "withdraws", + "type": "tuple[]", + "internalType": "struct IHub.ReallocateLeg[]", + "components": [ + { + "name": "yieldGroup", + "type": "address", + "internalType": "address" + }, + { + "name": "resource", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "name": "deposits", + "type": "tuple[]", + "internalType": "struct IHub.ReallocateLeg[]", + "components": [ + { + "name": "yieldGroup", + "type": "address", + "internalType": "address" + }, + { + "name": "resource", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ] + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "feeBps", + "inputs": [], + "outputs": [ + { + "name": "managementBps", + "type": "uint16", + "internalType": "uint16" + }, + { + "name": "performanceBps", + "type": "uint16", + "internalType": "uint16" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "feeRecipient", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "highWaterMarkPerShare", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "hubPaused", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "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": "asset_", + "type": "address", + "internalType": "contract IERC20Upgradeable" + }, + { + "name": "name_", + "type": "string", + "internalType": "string" + }, + { + "name": "symbol_", + "type": "string", + "internalType": "string" + }, + { + "name": "acm_", + "type": "address", + "internalType": "address" + }, + { + "name": "decimalsOffset_", + "type": "uint8", + "internalType": "uint8" + }, + { + "name": "initialMaxWithdrawalSize", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "feeRecipient_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "lowerMaxWithdrawalSize", + "inputs": [ + { + "name": "newSize", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "lowerYieldGroupCap", + "inputs": [ + { + "name": "yieldGroup", + "type": "address", + "internalType": "address" + }, + { + "name": "absoluteCap", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "percentageCapBps", + "type": "uint16", + "internalType": "uint16" + } + ], + "outputs": [], + "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": "maxWithdrawalSize", + "inputs": [], + "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": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "mintWithConsent", + "inputs": [ + { + "name": "shares", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "receiver", + "type": "address", + "internalType": "address" + }, + { + "name": "consentHash", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "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": "outerDepositQueue", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address[]", + "internalType": "address[]" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "outerWithdrawQueue", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address[]", + "internalType": "address[]" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "owner", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "pauseHub", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "pauseYieldGroup", + "inputs": [ + { + "name": "yieldGroup", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "pendingOwner", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "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": "raiseMaxWithdrawalSize", + "inputs": [ + { + "name": "newSize", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "raiseYieldGroupCap", + "inputs": [ + { + "name": "yieldGroup", + "type": "address", + "internalType": "address" + }, + { + "name": "absoluteCap", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "percentageCapBps", + "type": "uint16", + "internalType": "uint16" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "reallocate", + "inputs": [ + { + "name": "withdraws", + "type": "tuple[]", + "internalType": "struct IHub.ReallocateLeg[]", + "components": [ + { + "name": "yieldGroup", + "type": "address", + "internalType": "address" + }, + { + "name": "resource", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "name": "deposits", + "type": "tuple[]", + "internalType": "struct IHub.ReallocateLeg[]", + "components": [ + { + "name": "yieldGroup", + "type": "address", + "internalType": "address" + }, + { + "name": "resource", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ] + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "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": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "redeemFeeBps", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint16", + "internalType": "uint16" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "registeredYieldGroups", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address[]", + "internalType": "address[]" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "removeYieldGroup", + "inputs": [ + { + "name": "yieldGroup", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "renounceOwnership", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setAccessControlManager", + "inputs": [ + { + "name": "accessControlManager_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setFeeRecipient", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setManagementFeeBps", + "inputs": [ + { + "name": "bps", + "type": "uint16", + "internalType": "uint16" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setOuterDepositQueue", + "inputs": [ + { + "name": "queue", + "type": "address[]", + "internalType": "address[]" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setOuterWithdrawQueue", + "inputs": [ + { + "name": "queue", + "type": "address[]", + "internalType": "address[]" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setPerformanceFeeBps", + "inputs": [ + { + "name": "bps", + "type": "uint16", + "internalType": "uint16" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setRedeemFeeBps", + "inputs": [ + { + "name": "bps", + "type": "uint16", + "internalType": "uint16" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "sweep", + "inputs": [ + { + "name": "token", + "type": "address", + "internalType": "address" + }, + { + "name": "to", + "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": "total", + "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": "transferOwnership", + "inputs": [ + { + "name": "newOwner", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "unpauseHub", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "unpauseYieldGroup", + "inputs": [ + { + "name": "yieldGroup", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "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": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "yieldGroupConfig", + "inputs": [ + { + "name": "yieldGroup", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "tuple", + "internalType": "struct IHub.YieldGroupConfig", + "components": [ + { + "name": "absoluteCap", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "percentageCapBps", + "type": "uint16", + "internalType": "uint16" + }, + { + "name": "paused", + "type": "bool", + "internalType": "bool" + }, + { + "name": "registered", + "type": "bool", + "internalType": "bool" + } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "yieldGroupEffectiveCap", + "inputs": [ + { + "name": "yieldGroup", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "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": "ConsentRecorded", + "inputs": [ + { + "name": "supplier", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "receiver", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "consentHash", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + } + ], + "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": "DepositRouted", + "inputs": [ + { + "name": "yieldGroup", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "FeeRecipientSet", + "inputs": [ + { + "name": "oldRecipient", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "newRecipient", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "FeesAccrued", + "inputs": [ + { + "name": "managementShares", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "performanceShares", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "totalShares", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "HighWaterMarkUpdated", + "inputs": [ + { + "name": "oldHwm", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "newHwm", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "HubPauseToggled", + "inputs": [ + { + "name": "paused", + "type": "bool", + "indexed": false, + "internalType": "bool" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Initialized", + "inputs": [ + { + "name": "version", + "type": "uint8", + "indexed": false, + "internalType": "uint8" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ManagementFeeBpsSet", + "inputs": [ + { + "name": "oldBps", + "type": "uint16", + "indexed": false, + "internalType": "uint16" + }, + { + "name": "newBps", + "type": "uint16", + "indexed": false, + "internalType": "uint16" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "MaxWithdrawalSizeLowered", + "inputs": [ + { + "name": "oldSize", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "newSize", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "MaxWithdrawalSizeRaised", + "inputs": [ + { + "name": "oldSize", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "newSize", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "NewAccessControlManager", + "inputs": [ + { + "name": "oldAccessControlManager", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "newAccessControlManager", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OuterDepositQueueSet", + "inputs": [ + { + "name": "queue", + "type": "address[]", + "indexed": false, + "internalType": "address[]" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OuterWithdrawQueueSet", + "inputs": [ + { + "name": "queue", + "type": "address[]", + "indexed": false, + "internalType": "address[]" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OwnershipTransferStarted", + "inputs": [ + { + "name": "previousOwner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newOwner", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OwnershipTransferred", + "inputs": [ + { + "name": "previousOwner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newOwner", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "PerformanceFeeBpsSet", + "inputs": [ + { + "name": "oldBps", + "type": "uint16", + "indexed": false, + "internalType": "uint16" + }, + { + "name": "newBps", + "type": "uint16", + "indexed": false, + "internalType": "uint16" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "RedeemFeeBpsSet", + "inputs": [ + { + "name": "oldBps", + "type": "uint16", + "indexed": false, + "internalType": "uint16" + }, + { + "name": "newBps", + "type": "uint16", + "indexed": false, + "internalType": "uint16" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Swept", + "inputs": [ + { + "name": "token", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "to", + "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": "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": "event", + "name": "WithdrawRouted", + "inputs": [ + { + "name": "yieldGroup", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "YieldGroupAdded", + "inputs": [ + { + "name": "yieldGroup", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "absoluteCap", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "percentageCapBps", + "type": "uint16", + "indexed": false, + "internalType": "uint16" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "YieldGroupCapLowered", + "inputs": [ + { + "name": "yieldGroup", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "absoluteCap", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "percentageCapBps", + "type": "uint16", + "indexed": false, + "internalType": "uint16" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "YieldGroupCapRaised", + "inputs": [ + { + "name": "yieldGroup", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "absoluteCap", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "percentageCapBps", + "type": "uint16", + "indexed": false, + "internalType": "uint16" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "YieldGroupPauseToggled", + "inputs": [ + { + "name": "yieldGroup", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "paused", + "type": "bool", + "indexed": false, + "internalType": "bool" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "YieldGroupRemoved", + "inputs": [ + { + "name": "yieldGroup", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "YieldGroupSkipped", + "inputs": [ + { + "name": "yieldGroup", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "isDeposit", + "type": "bool", + "indexed": false, + "internalType": "bool" + } + ], + "anonymous": false + }, + { + "type": "error", + "name": "HubCapacityExceeded", + "inputs": [ + { + "name": "requested", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "maxAvailable", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "type": "error", + "name": "HubInsufficientLiquidity", + "inputs": [ + { + "name": "requested", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "available", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "type": "error", + "name": "HubPaused", + "inputs": [] + }, + { + "type": "error", + "name": "HubWithdrawCapExceeded", + "inputs": [ + { + "name": "requested", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "perRequestCap", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "type": "error", + "name": "InvalidCap", + "inputs": [ + { + "name": "absoluteCap", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "percentageCapBps", + "type": "uint16", + "internalType": "uint16" + } + ] + }, + { + "type": "error", + "name": "InvalidDecimalsOffset", + "inputs": [] + }, + { + "type": "error", + "name": "InvalidFeeBps", + "inputs": [ + { + "name": "bps", + "type": "uint16", + "internalType": "uint16" + } + ] + }, + { + "type": "error", + "name": "InvalidQueue", + "inputs": [] + }, + { + "type": "error", + "name": "NotDecreasing", + "inputs": [] + }, + { + "type": "error", + "name": "NotIncreasing", + "inputs": [] + }, + { + "type": "error", + "name": "ReallocateImbalanced", + "inputs": [ + { + "name": "withdrawSum", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "depositSum", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "type": "error", + "name": "SweepProtectedAsset", + "inputs": [] + }, + { + "type": "error", + "name": "Unauthorized", + "inputs": [ + { + "name": "sender", + "type": "address", + "internalType": "address" + }, + { + "name": "calledContract", + "type": "address", + "internalType": "address" + }, + { + "name": "methodSignature", + "type": "string", + "internalType": "string" + } + ] + }, + { + "type": "error", + "name": "WithdrawQueueOmitsFundedYieldGroup", + "inputs": [ + { + "name": "yieldGroup", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "YieldGroupAlreadyRegistered", + "inputs": [ + { + "name": "yieldGroup", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "YieldGroupAssetMismatch", + "inputs": [ + { + "name": "yieldGroup", + "type": "address", + "internalType": "address" + }, + { + "name": "yieldGroupAsset", + "type": "address", + "internalType": "address" + }, + { + "name": "hubAsset", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "YieldGroupHasBalance", + "inputs": [ + { + "name": "yieldGroup", + "type": "address", + "internalType": "address" + }, + { + "name": "balance", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "type": "error", + "name": "YieldGroupNotRegistered", + "inputs": [ + { + "name": "yieldGroup", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "YieldGroupPaused", + "inputs": [ + { + "name": "yieldGroup", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "YieldGroupUnderfilled", + "inputs": [ + { + "name": "yieldGroup", + "type": "address", + "internalType": "address" + }, + { + "name": "requested", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "actual", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "type": "error", + "name": "ZeroAddress", + "inputs": [] + }, + { + "type": "error", + "name": "ZeroAmount", + "inputs": [] + } +] diff --git a/simulations/vip-680/abi/HubRegistry.json b/simulations/vip-680/abi/HubRegistry.json new file mode 100644 index 000000000..bd1afe17c --- /dev/null +++ b/simulations/vip-680/abi/HubRegistry.json @@ -0,0 +1,361 @@ +[ + { + "type": "constructor", + "inputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "acceptOwnership", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "accessControlManager", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "contract IAccessControlManagerV8" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "addHub", + "inputs": [ + { + "name": "hub", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "getHubs", + "inputs": [], + "outputs": [ + { + "name": "hubs", + "type": "address[]", + "internalType": "address[]" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getHubsCount", + "inputs": [], + "outputs": [ + { + "name": "count", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "hubForAsset", + "inputs": [ + { + "name": "asset", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "hub", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "initialize", + "inputs": [ + { + "name": "accessControlManager_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "isHub", + "inputs": [ + { + "name": "hub", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "registered", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "owner", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "pendingOwner", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "removeHub", + "inputs": [ + { + "name": "hub", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "renounceOwnership", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setAccessControlManager", + "inputs": [ + { + "name": "accessControlManager_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "transferOwnership", + "inputs": [ + { + "name": "newOwner", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "HubAdded", + "inputs": [ + { + "name": "asset", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "hub", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "HubRemoved", + "inputs": [ + { + "name": "asset", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "hub", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Initialized", + "inputs": [ + { + "name": "version", + "type": "uint8", + "indexed": false, + "internalType": "uint8" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "NewAccessControlManager", + "inputs": [ + { + "name": "oldAccessControlManager", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "newAccessControlManager", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OwnershipTransferStarted", + "inputs": [ + { + "name": "previousOwner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newOwner", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OwnershipTransferred", + "inputs": [ + { + "name": "previousOwner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newOwner", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "error", + "name": "AssetAlreadyHasHub", + "inputs": [ + { + "name": "asset", + "type": "address", + "internalType": "address" + }, + { + "name": "existingHub", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "HubAlreadyRegistered", + "inputs": [ + { + "name": "hub", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "HubNotRegistered", + "inputs": [ + { + "name": "hub", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "Unauthorized", + "inputs": [ + { + "name": "sender", + "type": "address", + "internalType": "address" + }, + { + "name": "calledContract", + "type": "address", + "internalType": "address" + }, + { + "name": "methodSignature", + "type": "string", + "internalType": "string" + } + ] + }, + { + "type": "error", + "name": "ZeroAddress", + "inputs": [] + } +] diff --git a/simulations/vip-680/abi/YieldGroup.json b/simulations/vip-680/abi/YieldGroup.json new file mode 100644 index 000000000..be5b44c73 --- /dev/null +++ b/simulations/vip-680/abi/YieldGroup.json @@ -0,0 +1,1077 @@ +[ + { + "type": "function", + "name": "accessControlManager", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "accrue", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "addResource", + "inputs": [ + { + "name": "resource", + "type": "address", + "internalType": "address" + }, + { + "name": "adapter", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "asset", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "blocksPerYear", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "deposit", + "inputs": [ + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "deposited", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "depositResource", + "inputs": [ + { + "name": "resource", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "deposited", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "dispatchDepositExternal", + "inputs": [ + { + "name": "adapter", + "type": "address", + "internalType": "address" + }, + { + "name": "resource", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "placed", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "dispatchWithdrawExternal", + "inputs": [ + { + "name": "adapter", + "type": "address", + "internalType": "address" + }, + { + "name": "resource", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "to", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "received", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "hub", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "initialize", + "inputs": [ + { + "name": "hub_", + "type": "address", + "internalType": "address" + }, + { + "name": "asset_", + "type": "address", + "internalType": "address" + }, + { + "name": "blocksPerYear_", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "acm_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "innerDepositQueue", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address[]", + "internalType": "address[]" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "innerWithdrawQueue", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address[]", + "internalType": "address[]" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "lowerResourceCap", + "inputs": [ + { + "name": "resource", + "type": "address", + "internalType": "address" + }, + { + "name": "newCap", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "maxDeposit", + "inputs": [], + "outputs": [ + { + "name": "total", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "maxWithdraw", + "inputs": [], + "outputs": [ + { + "name": "total", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "pauseResource", + "inputs": [ + { + "name": "resource", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "raiseResourceCap", + "inputs": [ + { + "name": "resource", + "type": "address", + "internalType": "address" + }, + { + "name": "newCap", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "removeResource", + "inputs": [ + { + "name": "resource", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "resourceCap", + "inputs": [ + { + "name": "resource", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "resourceConfig", + "inputs": [ + { + "name": "resource", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "registered", + "type": "bool", + "internalType": "bool" + }, + { + "name": "paused", + "type": "bool", + "internalType": "bool" + }, + { + "name": "adapter", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "resources", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address[]", + "internalType": "address[]" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "setBlocksPerYear", + "inputs": [ + { + "name": "newBlocksPerYear", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setInnerDepositQueue", + "inputs": [ + { + "name": "queue", + "type": "address[]", + "internalType": "address[]" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setInnerWithdrawQueue", + "inputs": [ + { + "name": "queue", + "type": "address[]", + "internalType": "address[]" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "spotAPYBps", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint64", + "internalType": "uint64" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "sweep", + "inputs": [ + { + "name": "token", + "type": "address", + "internalType": "address" + }, + { + "name": "to", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "totalAssets", + "inputs": [], + "outputs": [ + { + "name": "total", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "unpauseResource", + "inputs": [ + { + "name": "resource", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "updateResourceAdapter", + "inputs": [ + { + "name": "resource", + "type": "address", + "internalType": "address" + }, + { + "name": "newAdapter", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "withdraw", + "inputs": [ + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "to", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "withdrawResource", + "inputs": [ + { + "name": "resource", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "to", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "BlocksPerYearSet", + "inputs": [ + { + "name": "oldBlocksPerYear", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "newBlocksPerYear", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "DepositRouted", + "inputs": [ + { + "name": "resource", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "amount", + "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": "InnerDepositQueueSet", + "inputs": [ + { + "name": "queue", + "type": "address[]", + "indexed": false, + "internalType": "address[]" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "InnerWithdrawQueueSet", + "inputs": [ + { + "name": "queue", + "type": "address[]", + "indexed": false, + "internalType": "address[]" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ResourceAccrualFailed", + "inputs": [ + { + "name": "resource", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "adapter", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ResourceAdapterUpdated", + "inputs": [ + { + "name": "resource", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "oldAdapter", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newAdapter", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ResourceAdded", + "inputs": [ + { + "name": "resource", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "adapter", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ResourceCapLowered", + "inputs": [ + { + "name": "resource", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newCap", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ResourceCapRaised", + "inputs": [ + { + "name": "resource", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newCap", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ResourcePauseToggled", + "inputs": [ + { + "name": "resource", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "paused", + "type": "bool", + "indexed": false, + "internalType": "bool" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ResourceRemoved", + "inputs": [ + { + "name": "resource", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ResourceSkipped", + "inputs": [ + { + "name": "resource", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "isDeposit", + "type": "bool", + "indexed": false, + "internalType": "bool" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Swept", + "inputs": [ + { + "name": "token", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "to", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "WithdrawRouted", + "inputs": [ + { + "name": "resource", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "error", + "name": "AdapterNotContract", + "inputs": [ + { + "name": "adapter", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "AdapterUnderfilled", + "inputs": [ + { + "name": "resource", + "type": "address", + "internalType": "address" + }, + { + "name": "requested", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "actual", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "type": "error", + "name": "HubAssetMismatch", + "inputs": [ + { + "name": "declared", + "type": "address", + "internalType": "address" + }, + { + "name": "hubAsset", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "InvalidQueue", + "inputs": [] + }, + { + "type": "error", + "name": "NotDecreasing", + "inputs": [] + }, + { + "type": "error", + "name": "NotHub", + "inputs": [ + { + "name": "caller", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "NotIncreasing", + "inputs": [] + }, + { + "type": "error", + "name": "OnlySelf", + "inputs": [] + }, + { + "type": "error", + "name": "ResourceAlreadyRegistered", + "inputs": [ + { + "name": "resource", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "ResourceAssetMismatch", + "inputs": [ + { + "name": "resource", + "type": "address", + "internalType": "address" + }, + { + "name": "resourceAsset", + "type": "address", + "internalType": "address" + }, + { + "name": "expected", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "ResourceBelowMinimumDeposit", + "inputs": [ + { + "name": "resource", + "type": "address", + "internalType": "address" + }, + { + "name": "requested", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "minimum", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "type": "error", + "name": "ResourceCapacityExceeded", + "inputs": [ + { + "name": "requested", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "maxAvailable", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "type": "error", + "name": "ResourceHasBalance", + "inputs": [ + { + "name": "resource", + "type": "address", + "internalType": "address" + }, + { + "name": "balance", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "type": "error", + "name": "ResourceIsPaused", + "inputs": [ + { + "name": "resource", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "ResourceLiquidityInsufficient", + "inputs": [ + { + "name": "requested", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "available", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "type": "error", + "name": "ResourceNotContract", + "inputs": [ + { + "name": "resource", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "ResourceNotRegistered", + "inputs": [ + { + "name": "resource", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "SweepProtectedAsset", + "inputs": [] + }, + { + "type": "error", + "name": "SweepProtectedResource", + "inputs": [ + { + "name": "resource", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "Unauthorized", + "inputs": [ + { + "name": "caller", + "type": "address", + "internalType": "address" + }, + { + "name": "signature", + "type": "string", + "internalType": "string" + } + ] + }, + { + "type": "error", + "name": "WithdrawQueueOmitsFundedResource", + "inputs": [ + { + "name": "resource", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "ZeroAddress", + "inputs": [] + } +] diff --git a/simulations/vip-680/bsctestnet-critical.ts b/simulations/vip-680/bsctestnet-critical.ts new file mode 100644 index 000000000..516400517 --- /dev/null +++ b/simulations/vip-680/bsctestnet-critical.ts @@ -0,0 +1,74 @@ +import { expect } from "chai"; +import { Contract } from "ethers"; +import { ethers } from "hardhat"; +import { expectEvents } from "src/utils"; +import { forking, testVip } from "src/vip-framework"; + +import { + ACM, + CORE_SOURCE_USDT, + CRITICAL_TIMELOCK, + FLUX_SOURCE_USDT, + FRV_SOURCE_USDT, + HUB_REGISTRY, + HUB_USDT, +} from "../../vips/vip-680/addresses"; +import vip680Critical from "../../vips/vip-680/bsctestnet-critical"; +import { + CORE_FLUX_GOVERNANCE, + FRV_GOVERNANCE, + HUB_GOVERNANCE, + HUB_REGISTRY_GOVERNANCE, + REALLOCATE, +} from "../../vips/vip-680/permissions"; +import ACCESS_CONTROL_MANAGER_ABI from "./abi/AccessControlManager.json"; + +// bsctestnet block after the Liquidity Hub (USDT) redeployment and the FRV vault creation. +const BLOCK_NUMBER = 119474506; + +const roleId = (contract: string, sig: string) => + ethers.utils.solidityKeccak256(["address", "string"], [contract, sig]); + +// The 51 (contract, sig) pairs granted to the Critical timelock: 18 Hub + 11 Core + 9 FRV + 11 Flux + +// 2 Registry. +const GOV_GRANTS: [string, string][] = [ + ...HUB_GOVERNANCE.map((s): [string, string] => [HUB_USDT, s]), + ...CORE_FLUX_GOVERNANCE.map((s): [string, string] => [CORE_SOURCE_USDT, s]), + ...FRV_GOVERNANCE.map((s): [string, string] => [FRV_SOURCE_USDT, s]), + ...CORE_FLUX_GOVERNANCE.map((s): [string, string] => [FLUX_SOURCE_USDT, s]), + ...HUB_REGISTRY_GOVERNANCE.map((s): [string, string] => [HUB_REGISTRY, s]), +]; + +forking(BLOCK_NUMBER, async () => { + let acm: Contract; + + before(async () => { + acm = await ethers.getContractAt(ACCESS_CONTROL_MANAGER_ABI, ACM); + }); + + describe("Pre-VIP state", () => { + it("Critical timelock holds none of the governance roles this proposal grants", async () => { + for (const [c, s] of GOV_GRANTS) { + expect(await acm.hasRole(roleId(c, s), CRITICAL_TIMELOCK)).to.equal(false, `pre ${c} ${s}`); + } + }); + }); + + testVip("VIP-680 [BNB Testnet] Liquidity Hub (USDT) Critical timelock permissions", await vip680Critical(), { + callbackAfterExecution: async txResponse => { + await expectEvents(txResponse, [ACCESS_CONTROL_MANAGER_ABI], ["RoleGranted"], [51]); + }, + }); + + describe("Post-VIP state", () => { + it("Critical timelock holds the full governance set across the stack", async () => { + for (const [c, s] of GOV_GRANTS) { + expect(await acm.hasRole(roleId(c, s), CRITICAL_TIMELOCK)).to.equal(true, `post ${c} ${s}`); + } + }); + + it("Critical timelock does NOT hold the operator-only reallocate role", async () => { + expect(await acm.hasRole(roleId(HUB_USDT, REALLOCATE), CRITICAL_TIMELOCK)).to.equal(false); + }); + }); +}); diff --git a/simulations/vip-680/bsctestnet-fast-track.ts b/simulations/vip-680/bsctestnet-fast-track.ts new file mode 100644 index 000000000..6fe14e16d --- /dev/null +++ b/simulations/vip-680/bsctestnet-fast-track.ts @@ -0,0 +1,74 @@ +import { expect } from "chai"; +import { Contract } from "ethers"; +import { ethers } from "hardhat"; +import { expectEvents } from "src/utils"; +import { forking, testVip } from "src/vip-framework"; + +import { + ACM, + CORE_SOURCE_USDT, + FAST_TRACK_TIMELOCK, + FLUX_SOURCE_USDT, + FRV_SOURCE_USDT, + HUB_REGISTRY, + HUB_USDT, +} from "../../vips/vip-680/addresses"; +import vip680FastTrack from "../../vips/vip-680/bsctestnet-fast-track"; +import { + CORE_FLUX_GOVERNANCE, + FRV_GOVERNANCE, + HUB_GOVERNANCE, + HUB_REGISTRY_GOVERNANCE, + REALLOCATE, +} from "../../vips/vip-680/permissions"; +import ACCESS_CONTROL_MANAGER_ABI from "./abi/AccessControlManager.json"; + +// bsctestnet block after the Liquidity Hub (USDT) redeployment and the FRV vault creation. +const BLOCK_NUMBER = 119474506; + +const roleId = (contract: string, sig: string) => + ethers.utils.solidityKeccak256(["address", "string"], [contract, sig]); + +// The 51 (contract, sig) pairs granted to the Fast-Track timelock: 18 Hub + 11 Core + 9 FRV + 11 Flux +// + 2 Registry. +const GOV_GRANTS: [string, string][] = [ + ...HUB_GOVERNANCE.map((s): [string, string] => [HUB_USDT, s]), + ...CORE_FLUX_GOVERNANCE.map((s): [string, string] => [CORE_SOURCE_USDT, s]), + ...FRV_GOVERNANCE.map((s): [string, string] => [FRV_SOURCE_USDT, s]), + ...CORE_FLUX_GOVERNANCE.map((s): [string, string] => [FLUX_SOURCE_USDT, s]), + ...HUB_REGISTRY_GOVERNANCE.map((s): [string, string] => [HUB_REGISTRY, s]), +]; + +forking(BLOCK_NUMBER, async () => { + let acm: Contract; + + before(async () => { + acm = await ethers.getContractAt(ACCESS_CONTROL_MANAGER_ABI, ACM); + }); + + describe("Pre-VIP state", () => { + it("Fast-Track timelock holds none of the governance roles this proposal grants", async () => { + for (const [c, s] of GOV_GRANTS) { + expect(await acm.hasRole(roleId(c, s), FAST_TRACK_TIMELOCK)).to.equal(false, `pre ${c} ${s}`); + } + }); + }); + + testVip("VIP-680 [BNB Testnet] Liquidity Hub (USDT) Fast-Track timelock permissions", await vip680FastTrack(), { + callbackAfterExecution: async txResponse => { + await expectEvents(txResponse, [ACCESS_CONTROL_MANAGER_ABI], ["RoleGranted"], [51]); + }, + }); + + describe("Post-VIP state", () => { + it("Fast-Track timelock holds the full governance set across the stack", async () => { + for (const [c, s] of GOV_GRANTS) { + expect(await acm.hasRole(roleId(c, s), FAST_TRACK_TIMELOCK)).to.equal(true, `post ${c} ${s}`); + } + }); + + it("Fast-Track timelock does NOT hold the operator-only reallocate role", async () => { + expect(await acm.hasRole(roleId(HUB_USDT, REALLOCATE), FAST_TRACK_TIMELOCK)).to.equal(false); + }); + }); +}); diff --git a/simulations/vip-680/bsctestnet-guardian.ts b/simulations/vip-680/bsctestnet-guardian.ts new file mode 100644 index 000000000..1564a53aa --- /dev/null +++ b/simulations/vip-680/bsctestnet-guardian.ts @@ -0,0 +1,89 @@ +import { expect } from "chai"; +import { Contract } from "ethers"; +import { ethers } from "hardhat"; +import { expectEvents } from "src/utils"; +import { forking, testVip } from "src/vip-framework"; + +import { + ACM, + CORE_SOURCE_USDT, + FLUX_SOURCE_USDT, + FRV_SOURCE_USDT, + GUARDIAN, + HUB_REGISTRY, + HUB_USDT, +} from "../../vips/vip-680/addresses"; +import vip680Guardian from "../../vips/vip-680/bsctestnet-guardian"; +import { + CORE_FLUX_GOVERNANCE, + FRV_GOVERNANCE, + HUB_FULL, + HUB_REGISTRY_GOVERNANCE, + REALLOCATE, +} from "../../vips/vip-680/permissions"; +import ACCESS_CONTROL_MANAGER_ABI from "./abi/AccessControlManager.json"; + +// bsctestnet block after the Liquidity Hub (USDT) redeployment and the FRV vault creation. +const BLOCK_NUMBER = 119474506; + +const roleId = (contract: string, sig: string) => + ethers.utils.solidityKeccak256(["address", "string"], [contract, sig]); + +// Every (contract, sig) pair this testnet-only proposal grants to the Guardian: 19 Hub (18 governance + +// operator-only reallocate) + 11 Core + 9 FRV + 11 Flux + 2 Registry = 52. +const GUARDIAN_GRANTS: [string, string][] = [ + ...HUB_FULL.map((s): [string, string] => [HUB_USDT, s]), + ...CORE_FLUX_GOVERNANCE.map((s): [string, string] => [CORE_SOURCE_USDT, s]), + ...FRV_GOVERNANCE.map((s): [string, string] => [FRV_SOURCE_USDT, s]), + ...CORE_FLUX_GOVERNANCE.map((s): [string, string] => [FLUX_SOURCE_USDT, s]), + ...HUB_REGISTRY_GOVERNANCE.map((s): [string, string] => [HUB_REGISTRY, s]), +]; + +forking(BLOCK_NUMBER, async () => { + let acm: Contract; + + before(async () => { + acm = await ethers.getContractAt(ACCESS_CONTROL_MANAGER_ABI, ACM); + }); + + describe("Pre-VIP state", () => { + it("this proposal grants exactly 52 (contract, sig) pairs to the Guardian", () => { + expect(GUARDIAN_GRANTS.length).to.equal(52); + }); + + it("Guardian holds none of the roles this proposal grants", async () => { + for (const [c, s] of GUARDIAN_GRANTS) { + expect(await acm.hasRole(roleId(c, s), GUARDIAN)).to.equal(false, `pre ${c} ${s}`); + } + }); + }); + + testVip("VIP-680 [BNB Testnet] Liquidity Hub (USDT) Guardian full permissions", await vip680Guardian(), { + callbackAfterExecution: async txResponse => { + await expectEvents(txResponse, [ACCESS_CONTROL_MANAGER_ABI], ["RoleGranted"], [52]); + }, + }); + + describe("Post-VIP state", () => { + it("Guardian now holds every role this proposal grants", async () => { + for (const [c, s] of GUARDIAN_GRANTS) { + expect(await acm.hasRole(roleId(c, s), GUARDIAN)).to.equal(true, `post ${c} ${s}`); + } + }); + + it("Guardian holds the operator-only reallocate role on the Hub (full set, testnet)", async () => { + expect(await acm.hasRole(roleId(HUB_USDT, REALLOCATE), GUARDIAN)).to.equal(true); + }); + + it("Guardian can loosen on every target (the point of this testnet proposal)", async () => { + expect(await acm.hasRole(roleId(HUB_USDT, "addYieldGroup(address,uint256,uint16)"), GUARDIAN)).to.equal(true); + for (const source of [CORE_SOURCE_USDT, FRV_SOURCE_USDT, FLUX_SOURCE_USDT]) { + expect(await acm.hasRole(roleId(source, "addResource(address,address)"), GUARDIAN)).to.equal( + true, + `addResource @ ${source}`, + ); + } + expect(await acm.hasRole(roleId(HUB_REGISTRY, "addHub(address)"), GUARDIAN)).to.equal(true); + }); + }); +}); diff --git a/simulations/vip-680/bsctestnet-wiring.ts b/simulations/vip-680/bsctestnet-wiring.ts new file mode 100644 index 000000000..f41a6a749 --- /dev/null +++ b/simulations/vip-680/bsctestnet-wiring.ts @@ -0,0 +1,160 @@ +import { expect } from "chai"; +import { Contract } from "ethers"; +import { ethers } from "hardhat"; +import { expectEvents } from "src/utils"; +import { forking, pretendExecutingVip, testVip } from "src/vip-framework"; + +import { + ABSOLUTE_CAP_UNBOUNDED, + ACM, + ADAPTER_CORE_V1, + ADAPTER_FLUX, + ADAPTER_FRV, + CORE_SOURCE_USDT, + FLUX_SOURCE_USDT, + FRV_SOURCE_USDT, + FRV_VAULT_USDT, + FUSDT_FLUX, + HUB_REGISTRY, + HUB_USDT, + NORMAL_TIMELOCK, + PERCENTAGE_CAP_DISABLED, + USDT, + VUSDT_CORE, +} from "../../vips/vip-680/addresses"; +import vip680 from "../../vips/vip-680/bsctestnet"; +import vip680Wiring from "../../vips/vip-680/bsctestnet-wiring"; +import HUB_ABI from "./abi/Hub.json"; +import HUB_REGISTRY_ABI from "./abi/HubRegistry.json"; +import YIELD_GROUP_ABI from "./abi/YieldGroup.json"; + +// bsctestnet block after the Liquidity Hub (USDT) redeployment and the FRV vault creation. +const BLOCK_NUMBER = 119474506; + +const addr = (a: string) => ethers.utils.getAddress(a); + +const OUTER_QUEUE = [FRV_SOURCE_USDT, FLUX_SOURCE_USDT, CORE_SOURCE_USDT]; + +forking(BLOCK_NUMBER, async () => { + let hub: Contract; + let registry: Contract; + let core: Contract; + let frv: Contract; + let flux: Contract; + + before(async () => { + // This wiring proposal needs the roles + ownership from the main VIP-680 proposal. Replay it as the + // Normal Timelock so the fixture snapshot reflects the on-chain state after it executes. + await pretendExecutingVip(await vip680(), NORMAL_TIMELOCK); + + hub = await ethers.getContractAt(HUB_ABI, HUB_USDT); + registry = await ethers.getContractAt(HUB_REGISTRY_ABI, HUB_REGISTRY); + core = await ethers.getContractAt(YIELD_GROUP_ABI, CORE_SOURCE_USDT); + frv = await ethers.getContractAt(YIELD_GROUP_ABI, FRV_SOURCE_USDT); + flux = await ethers.getContractAt(YIELD_GROUP_ABI, FLUX_SOURCE_USDT); + }); + + describe("Pre-wiring state (after main VIP-680)", () => { + it("Normal Timelock owns the Hub and registry (main VIP-680 accepted ownership)", async () => { + expect(await hub.owner()).to.equal(NORMAL_TIMELOCK); + expect(await registry.owner()).to.equal(NORMAL_TIMELOCK); + }); + + it("Hub unregistered, no yield groups, sources empty", async () => { + expect(await registry.isHub(HUB_USDT)).to.equal(false); + expect(await registry.getHubs()).to.deep.equal([]); + expect(await hub.registeredYieldGroups()).to.deep.equal([]); + for (const src of [core, frv, flux]) { + expect(await src.resources()).to.deep.equal([]); + } + }); + + it("Hub advertises zero deposit capacity (no routes wired)", async () => { + expect(await hub.maxDeposit(NORMAL_TIMELOCK)).to.equal(0); + }); + }); + + testVip("VIP-680 [BNB Testnet] Liquidity Hub (USDT) wiring", await vip680Wiring(), { + callbackAfterExecution: async txResponse => { + await expectEvents(txResponse, [HUB_REGISTRY_ABI], ["HubAdded"], [1]); + await expectEvents( + txResponse, + [HUB_ABI], + ["YieldGroupAdded", "OuterDepositQueueSet", "OuterWithdrawQueueSet"], + [3, 1, 1], + ); + await expectEvents( + txResponse, + [YIELD_GROUP_ABI], + ["ResourceAdded", "InnerDepositQueueSet", "InnerWithdrawQueueSet"], + [3, 3, 3], + ); + + // README invariant: HubAdded lands at a lower log index than every YieldGroupAdded, so indexers + // seed each Hub's yield-group set correctly. + const receipt = await txResponse.wait(); + const topicHubAdded = new ethers.utils.Interface(HUB_REGISTRY_ABI).getEventTopic("HubAdded"); + const topicYieldGroupAdded = new ethers.utils.Interface(HUB_ABI).getEventTopic("YieldGroupAdded"); + const hubAddedIdx = receipt.logs.filter(l => l.topics[0] === topicHubAdded).map(l => l.logIndex); + const yieldGroupAddedIdx = receipt.logs.filter(l => l.topics[0] === topicYieldGroupAdded).map(l => l.logIndex); + expect(hubAddedIdx.length).to.equal(1); + expect(yieldGroupAddedIdx.length).to.equal(3); + expect(hubAddedIdx[0]).to.be.lessThan(Math.min(...yieldGroupAddedIdx)); + }, + }); + + describe("Post-wiring state", () => { + it("Hub is registered in the HubRegistry", async () => { + expect(await registry.isHub(HUB_USDT)).to.equal(true); + expect((await registry.getHubsCount()).toNumber()).to.equal(1); + expect(addr(await registry.hubForAsset(USDT))).to.equal(addr(HUB_USDT)); + }); + + it("three yield groups registered on the Hub, all uncapped", async () => { + expect((await hub.registeredYieldGroups()).map(addr)).to.have.members( + [CORE_SOURCE_USDT, FRV_SOURCE_USDT, FLUX_SOURCE_USDT].map(addr), + ); + for (const src of [CORE_SOURCE_USDT, FRV_SOURCE_USDT, FLUX_SOURCE_USDT]) { + const cfg = await hub.yieldGroupConfig(src); + expect(cfg.registered).to.equal(true); + expect(cfg.paused).to.equal(false); + expect(cfg.absoluteCap.toString()).to.equal(ABSOLUTE_CAP_UNBOUNDED); + expect(cfg.percentageCapBps).to.equal(PERCENTAGE_CAP_DISABLED); + } + }); + + it("Hub outer queues route [FRV, Flux, Core] for deposits and withdrawals", async () => { + const want = OUTER_QUEUE.map(addr); + expect((await hub.outerDepositQueue()).map(addr)).to.deep.equal(want); + expect((await hub.outerWithdrawQueue()).map(addr)).to.deep.equal(want); + }); + + it("each source holds its resource behind the right adapter, with matching inner queues", async () => { + const cases: [Contract, string, string][] = [ + [core, VUSDT_CORE, ADAPTER_CORE_V1], + [frv, FRV_VAULT_USDT, ADAPTER_FRV], + [flux, FUSDT_FLUX, ADAPTER_FLUX], + ]; + for (const [src, resource, adapter] of cases) { + expect((await src.resources()).map(addr)).to.deep.equal([addr(resource)]); + const [registered, paused, boundAdapter] = await src.resourceConfig(resource); + expect(registered).to.equal(true); + expect(paused).to.equal(false); + expect(addr(boundAdapter)).to.equal(addr(adapter)); + expect((await src.innerDepositQueue()).map(addr)).to.deep.equal([addr(resource)]); + expect((await src.innerWithdrawQueue()).map(addr)).to.deep.equal([addr(resource)]); + } + }); + + it("accessControlManager is the canonical ACM on all five contracts", async () => { + for (const c of [hub, registry, core, frv, flux]) { + expect(addr(await c.accessControlManager())).to.equal(addr(ACM)); + } + }); + + it("Hub now advertises deposit capacity through the wired route", async () => { + // maxDeposit walks the whole live route: Hub outer queue -> source inner queue -> adapter.maxDeposit. + expect(await hub.maxDeposit(NORMAL_TIMELOCK)).to.be.gt(0); + }); + }); +}); diff --git a/simulations/vip-680/bsctestnet.ts b/simulations/vip-680/bsctestnet.ts new file mode 100644 index 000000000..007ad5c78 --- /dev/null +++ b/simulations/vip-680/bsctestnet.ts @@ -0,0 +1,116 @@ +import { expect } from "chai"; +import { Contract } from "ethers"; +import { ethers } from "hardhat"; +import { expectEvents } from "src/utils"; +import { forking, testVip } from "src/vip-framework"; + +import { + ACM, + CORE_SOURCE_USDT, + CRITICAL_TIMELOCK, + FAST_TRACK_TIMELOCK, + FLUX_SOURCE_USDT, + FRV_SOURCE_USDT, + GUARDIAN, + HUB_REGISTRY, + HUB_USDT, + NORMAL_TIMELOCK, +} from "../../vips/vip-680/addresses"; +import vip680 from "../../vips/vip-680/bsctestnet"; +import { + CORE_FLUX_GOVERNANCE, + FRV_GOVERNANCE, + HUB_GOVERNANCE, + HUB_REGISTRY_GOVERNANCE, + REALLOCATE, +} from "../../vips/vip-680/permissions"; +import ACCESS_CONTROL_MANAGER_ABI from "./abi/AccessControlManager.json"; +import HUB_ABI from "./abi/Hub.json"; +import HUB_REGISTRY_ABI from "./abi/HubRegistry.json"; + +// bsctestnet block after the Liquidity Hub (USDT) redeployment and the FRV vault creation. +const BLOCK_NUMBER = 119474506; + +// The exact ACM role id giveCallPermission(contract, sig, account) grants: +// keccak256(abi.encodePacked(contract, functionSig)). Asserting hasRole on this specific role is immune +// to the isAllowedToCall wildcard fallback, so pre-VIP "not granted" checks cannot be masked. +const roleId = (contract: string, sig: string) => + ethers.utils.solidityKeccak256(["address", "string"], [contract, sig]); + +// Every (contract, sig) pair this proposal grants to the Normal Timelock: 18 Hub + 11 Core + 9 FRV + +// 11 Flux + 2 Registry = 51. +const GOV_GRANTS: [string, string][] = [ + ...HUB_GOVERNANCE.map((s): [string, string] => [HUB_USDT, s]), + ...CORE_FLUX_GOVERNANCE.map((s): [string, string] => [CORE_SOURCE_USDT, s]), + ...FRV_GOVERNANCE.map((s): [string, string] => [FRV_SOURCE_USDT, s]), + ...CORE_FLUX_GOVERNANCE.map((s): [string, string] => [FLUX_SOURCE_USDT, s]), + ...HUB_REGISTRY_GOVERNANCE.map((s): [string, string] => [HUB_REGISTRY, s]), +]; + +forking(BLOCK_NUMBER, async () => { + let acm: Contract; + let hub: Contract; + let registry: Contract; + + before(async () => { + acm = await ethers.getContractAt(ACCESS_CONTROL_MANAGER_ABI, ACM); + hub = await ethers.getContractAt(HUB_ABI, HUB_USDT); + registry = await ethers.getContractAt(HUB_REGISTRY_ABI, HUB_REGISTRY); + }); + + describe("Pre-VIP state", () => { + it("GOV_GRANTS is the expected 51 (contract, sig) pairs", () => { + expect(GOV_GRANTS.length).to.equal(51); + }); + + it("Hub and HubRegistry are pending-owned by the Normal Timelock (not yet accepted)", async () => { + expect(await hub.pendingOwner()).to.equal(NORMAL_TIMELOCK); + expect(await registry.pendingOwner()).to.equal(NORMAL_TIMELOCK); + expect(await hub.owner()).to.not.equal(NORMAL_TIMELOCK); + expect(await registry.owner()).to.not.equal(NORMAL_TIMELOCK); + }); + + it("Normal Timelock holds none of the governance roles this proposal grants", async () => { + for (const [c, s] of GOV_GRANTS) { + expect(await acm.hasRole(roleId(c, s), NORMAL_TIMELOCK)).to.equal(false, `pre ${c} ${s}`); + } + }); + }); + + testVip("VIP-680 [BNB Testnet] Liquidity Hub (USDT) Normal Timelock permissions", await vip680(), { + callbackAfterExecution: async txResponse => { + // 51 governance grants + 2 acceptOwnership (Hub + registry each emit OwnershipTransferred). + await expectEvents(txResponse, [ACCESS_CONTROL_MANAGER_ABI], ["RoleGranted"], [51]); + await expectEvents(txResponse, [HUB_ABI], ["OwnershipTransferred"], [2]); + }, + }); + + describe("Post-VIP state", () => { + it("Hub and HubRegistry are now owned by the Normal Timelock", async () => { + expect(await hub.owner()).to.equal(NORMAL_TIMELOCK); + expect(await registry.owner()).to.equal(NORMAL_TIMELOCK); + }); + + it("Normal Timelock holds the full governance set across the stack", async () => { + for (const [c, s] of GOV_GRANTS) { + expect(await acm.hasRole(roleId(c, s), NORMAL_TIMELOCK)).to.equal(true, `post ${c} ${s}`); + } + }); + + it("Normal Timelock does NOT hold the operator-only reallocate role", async () => { + expect(await acm.hasRole(roleId(HUB_USDT, REALLOCATE), NORMAL_TIMELOCK)).to.equal(false); + }); + + it("Fast-Track, Critical and Guardian hold no governance role yet", async () => { + for (const account of [FAST_TRACK_TIMELOCK, CRITICAL_TIMELOCK, GUARDIAN]) { + for (const [c, s] of GOV_GRANTS) { + expect(await acm.hasRole(roleId(c, s), account)).to.equal(false, `${account} ${c} ${s}`); + } + } + }); + + it("Hub still has no registered yield groups (wiring deferred to the wiring proposal)", async () => { + expect(await hub.registeredYieldGroups()).to.deep.equal([]); + }); + }); +}); diff --git a/vips/vip-680/addresses.ts b/vips/vip-680/addresses.ts new file mode 100644 index 000000000..21960b716 --- /dev/null +++ b/vips/vip-680/addresses.ts @@ -0,0 +1,75 @@ +import { NETWORK_ADDRESSES } from "src/networkAddresses"; + +// =================================================================================================== +// VIP-680 [BNB Chain Testnet] — Liquidity Hub (USDT) address book. +// +// One reference for every address the VIP touches. The whole Hub stack (HubRegistry + per-asset Hub + +// Core/FRV/Flux sources + adapters) was REDEPLOYED at ~block 119,459,xxx; the values below are the +// current committed addresses from venus-liquidity-hub/deployments/bsctestnet/*.json. That repo is not +// published as an @venusprotocol/*-deployments npm package, so its addresses are inlined here (source +// file noted per section). Governance / ACM come from @venusprotocol/governance-contracts via +// NETWORK_ADDRESSES — the canonical source — so they are imported, never hardcoded. +// +// Verified on a bsctestnet fork/RPC (2026-07-16): +// - Hub_USDT.asset() == vUSDT.underlying() == fUSDT.asset() == FRV_VAULT.asset() == USDT +// (0xA11c…782c) -> addResource passes its ResourceAssetMismatch guard on every source. +// - Hub_USDT.pendingOwner() == HubRegistry.pendingOwner() == NORMAL_TIMELOCK +// -> both acceptOwnership() calls succeed from the Normal-timelock proposal. +// - config.acm == ACM (0x45f8…); config.governance == NORMAL_TIMELOCK (0xce10…). +// =================================================================================================== + +const { ACCESS_CONTROL_MANAGER, NORMAL_TIMELOCK, FAST_TRACK_TIMELOCK, CRITICAL_TIMELOCK, GUARDIAN } = + NETWORK_ADDRESSES.bsctestnet; + +// --------------------------------------------------------------------------------------------------- +// Governance / access control (source: @venusprotocol/governance-contracts, via NETWORK_ADDRESSES). +// --------------------------------------------------------------------------------------------------- +export const ACM = ACCESS_CONTROL_MANAGER; // 0x45f8a08F534f34A97187626E05d4b6648Eeaa9AA +export { NORMAL_TIMELOCK, FAST_TRACK_TIMELOCK, CRITICAL_TIMELOCK, GUARDIAN }; +// The Guardian multisig plays the Operator role, and (testnet only) is topped up to full Governance. +export const OPERATOR = GUARDIAN; // 0x2Ce1d0ffD7E869D9DF33e28552b12DdDed326706 + +// --------------------------------------------------------------------------------------------------- +// Hub stack — CALLED by the VIP (source: venus-liquidity-hub/deployments/bsctestnet/*.json). +// --------------------------------------------------------------------------------------------------- +export const HUB_REGISTRY = "0x5346f648029d1D1d1034e09e8AD7a115f5D7A159"; // acceptOwnership + addHub target +export const HUB_USDT = "0x7cE6ADF754D0eC81A6CF8ACd9C7454F45077dc61"; // acceptOwnership + roles + wiring target +export const CORE_SOURCE_USDT = "0x11e39DC7b8b16BBDA8D9C2903dF741Ae9341Ec88"; +export const FRV_SOURCE_USDT = "0xA0Fb0fFeBdcB7F45A3Ec841cCE7F78B7CeBD0f82"; +export const FLUX_SOURCE_USDT = "0x044E572144bc08ed2D90E081EeEd7b5b6Cb01016"; +export const ADAPTER_CORE_V1 = "0xDf669957448eCB23309eEFda4de230c62d22AE33"; +export const ADAPTER_FRV = "0xeF0E85ab9A23F50EB4595CF7e2F5461feF7E7fc5"; +export const ADAPTER_FLUX = "0x15Dca35ae0b16BeceabAEC9Dea49630e8C601730"; + +// Ownable2Step contracts whose ownership was transferred to governance at deploy and must be accepted +// (both are AccessControlledV8 -> Ownable2Step). The beacons and the ProxyAdmin are single-step Ownable +// (already owned by governance, no accept); the source proxies are ACM-only (not Ownable, no owner). +export const OWNERSHIP_ACCEPT_TARGETS = [HUB_USDT, HUB_REGISTRY]; + +// --------------------------------------------------------------------------------------------------- +// Resources registered inside each source — CALLED by the VIP (addResource + inner queues). +// --------------------------------------------------------------------------------------------------- +export const VUSDT_CORE = "0xb7526572FFE56AB9D7489838Bf2E18e3323b441A"; // @venusprotocol/venus-protocol core vUSDT +export const FRV_VAULT_USDT = "0x9F6Edab0123188C852854D2D9601115168f52F7a"; // new FRV vault (asset()==USDT, verified) +export const FUSDT_FLUX = "0x52217232e12A1c906aB8DEf58532a3618970D025"; // Fluid fUSDT fToken (ERC-4626) + +// Underlying asset shared by the Hub and every resource above. +export const USDT = "0xA11c8D9DC9b66E209Ef60F0C8D969D3CD988782c"; + +// --------------------------------------------------------------------------------------------------- +// Hub stack — REFERENCE ONLY (not called by the VIP; owned by governance at deploy). +// --------------------------------------------------------------------------------------------------- +export const HUB_BEACON = "0x7cbaC6991aC33DaFDD347e84CFbE2F372b936d92"; +export const CORE_BEACON = "0xbBEe25aE7d2Db035Afc327fb0096fC88FDfF3170"; +export const FRV_BEACON = "0x6196Ec22133610132563B03b6Fad5aa766A9C037"; +export const FLUX_BEACON = "0x6b9CA74F82848668EA04D56E0A8396A816ba5330"; +export const HUB_REGISTRY_PROXY_ADMIN = "0x9f8413eEE33D434F6D4f40C83181f32A831c9ef7"; +export const MIGRATOR = "0x343D518d8C89f9B5D770000F1ed80f45bF1419f5"; // immutable, permissionless — no wiring + +// --------------------------------------------------------------------------------------------------- +// Cap constants for Hub.addYieldGroup(source, absoluteCap, percentageCapBps). +// --------------------------------------------------------------------------------------------------- +// The Hub rejects type(uint256).max as InvalidCap; type(uint128).max is the canonical "no ceiling". +export const ABSOLUTE_CAP_UNBOUNDED = "340282366920938463463374607431768211455"; // type(uint128).max +// 10_000 bps disables the percentage-of-TVL cap dimension, leaving only the absolute cap binding. +export const PERCENTAGE_CAP_DISABLED = 10_000; diff --git a/vips/vip-680/bsctestnet-critical.ts b/vips/vip-680/bsctestnet-critical.ts new file mode 100644 index 000000000..6ce604974 --- /dev/null +++ b/vips/vip-680/bsctestnet-critical.ts @@ -0,0 +1,71 @@ +import { ProposalType } from "src/types"; +import { makeProposal } from "src/utils"; + +import { + CORE_SOURCE_USDT, + CRITICAL_TIMELOCK, + FLUX_SOURCE_USDT, + FRV_SOURCE_USDT, + HUB_REGISTRY, + HUB_USDT, +} from "./addresses"; +import { + CORE_FLUX_GOVERNANCE, + FRV_GOVERNANCE, + HUB_GOVERNANCE, + HUB_REGISTRY_GOVERNANCE, + giveCallPermission, +} from "./permissions"; + +// --------------------------------------------------------------------------------------------------- +// VIP-680 (Critical addendum) — BNB Chain Testnet. +// +// Grants the same Governance role set the main proposal gives the Normal Timelock to the Critical +// timelock, across the whole stack (Hub + Core + FRV + Flux + HubRegistry), so critical proposals can +// operate the Hub too. Separate proposal for the same block-gas reason as the Fast-Track addendum. +// +// NOTE: full-Governance parity with the Normal Timelock is a testnet choice; on mainnet the fast lanes +// would instead receive only a tightening subset. Do not mirror this full-set grant to mainnet. +// --------------------------------------------------------------------------------------------------- + +export const vip680Critical = () => { + const meta = { + version: "v2", + title: "VIP-680 [BNB Chain Testnet] Liquidity Hub (USDT) — Critical timelock permissions (addendum)", + description: `#### Summary + +Fourth of five proposals onboarding the redeployed **Liquidity Hub (USDT)** on BNB Chain Testnet. Grants +the **Governance** role set to the **Critical timelock** on **Hub_USDT**, the **Core**, **FRV** and +**Flux** yield sources, and the **HubRegistry**, so critical proposals can operate the Hub alongside the +Normal Timelock configured in the main proposal. + +#### Actions + +Grant the full Governance role set (registry, caps, queues, pause/unpause, fees, sweep, adapter updates, +\`emergencyReallocate\`, and registry \`addHub\`/\`removeHub\`) to the Critical timelock across the stack. + +#### Notes + +- The Critical timelock does not receive the operator-only \`reallocate\` role. +- Full-Governance parity with the Normal Timelock is a testnet choice; on mainnet the fast lanes would + receive only a tightening subset. Do not mirror this grant to mainnet. +- Testnet-only. The Hub is not yet deployed on any mainnet.`, + forDescription: "Execute this proposal", + againstDescription: "Do not execute this proposal", + abstainDescription: "Indifferent to execution", + }; + + return makeProposal( + [ + ...HUB_GOVERNANCE.map(sig => giveCallPermission(HUB_USDT, sig, CRITICAL_TIMELOCK)), + ...CORE_FLUX_GOVERNANCE.map(sig => giveCallPermission(CORE_SOURCE_USDT, sig, CRITICAL_TIMELOCK)), + ...FRV_GOVERNANCE.map(sig => giveCallPermission(FRV_SOURCE_USDT, sig, CRITICAL_TIMELOCK)), + ...CORE_FLUX_GOVERNANCE.map(sig => giveCallPermission(FLUX_SOURCE_USDT, sig, CRITICAL_TIMELOCK)), + ...HUB_REGISTRY_GOVERNANCE.map(sig => giveCallPermission(HUB_REGISTRY, sig, CRITICAL_TIMELOCK)), + ], + meta, + ProposalType.REGULAR, + ); +}; + +export default vip680Critical; diff --git a/vips/vip-680/bsctestnet-fast-track.ts b/vips/vip-680/bsctestnet-fast-track.ts new file mode 100644 index 000000000..9810efcb3 --- /dev/null +++ b/vips/vip-680/bsctestnet-fast-track.ts @@ -0,0 +1,74 @@ +import { ProposalType } from "src/types"; +import { makeProposal } from "src/utils"; + +import { + CORE_SOURCE_USDT, + FAST_TRACK_TIMELOCK, + FLUX_SOURCE_USDT, + FRV_SOURCE_USDT, + HUB_REGISTRY, + HUB_USDT, +} from "./addresses"; +import { + CORE_FLUX_GOVERNANCE, + FRV_GOVERNANCE, + HUB_GOVERNANCE, + HUB_REGISTRY_GOVERNANCE, + giveCallPermission, +} from "./permissions"; + +// --------------------------------------------------------------------------------------------------- +// VIP-680 (Fast-Track addendum) — BNB Chain Testnet. +// +// Grants the same Governance role set the main proposal gives the Normal Timelock to the Fast-Track +// timelock, across the whole stack (Hub + Core + FRV + Flux + HubRegistry), so urgent proposals can +// operate the Hub too. Split into its own proposal because all three timelocks' grants plus the wiring +// exceed a single proposal's block-gas budget on BSC. +// +// NOTE: this grants the Fast-Track timelock the FULL Governance set (fees, addYieldGroup/removeYieldGroup, +// registry management, ...) for testnet parity with the Normal Timelock. On mainnet the fast lanes would +// instead receive only a tightening subset (lower caps, pause, reorder queues); do not mirror this +// full-set grant to mainnet. +// --------------------------------------------------------------------------------------------------- + +export const vip680FastTrack = () => { + const meta = { + version: "v2", + title: "VIP-680 [BNB Chain Testnet] Liquidity Hub (USDT) — Fast-Track timelock permissions (addendum)", + description: `#### Summary + +Third of five proposals onboarding the redeployed **Liquidity Hub (USDT)** on BNB Chain Testnet. Grants +the **Governance** role set to the **Fast-Track timelock** on **Hub_USDT**, the **Core**, **FRV** and +**Flux** yield sources, and the **HubRegistry**, so fast-track proposals can operate the Hub alongside +the Normal Timelock configured in the main proposal. + +#### Actions + +Grant the full Governance role set (registry, caps, queues, pause/unpause, fees, sweep, adapter updates, +\`emergencyReallocate\`, and registry \`addHub\`/\`removeHub\`) to the Fast-Track timelock across the stack. + +#### Notes + +- The Fast-Track timelock does not receive the operator-only \`reallocate\` role. +- Full-Governance parity with the Normal Timelock is a testnet choice; on mainnet the fast lanes would + receive only a tightening subset. Do not mirror this grant to mainnet. +- Testnet-only. The Hub is not yet deployed on any mainnet.`, + forDescription: "Execute this proposal", + againstDescription: "Do not execute this proposal", + abstainDescription: "Indifferent to execution", + }; + + return makeProposal( + [ + ...HUB_GOVERNANCE.map(sig => giveCallPermission(HUB_USDT, sig, FAST_TRACK_TIMELOCK)), + ...CORE_FLUX_GOVERNANCE.map(sig => giveCallPermission(CORE_SOURCE_USDT, sig, FAST_TRACK_TIMELOCK)), + ...FRV_GOVERNANCE.map(sig => giveCallPermission(FRV_SOURCE_USDT, sig, FAST_TRACK_TIMELOCK)), + ...CORE_FLUX_GOVERNANCE.map(sig => giveCallPermission(FLUX_SOURCE_USDT, sig, FAST_TRACK_TIMELOCK)), + ...HUB_REGISTRY_GOVERNANCE.map(sig => giveCallPermission(HUB_REGISTRY, sig, FAST_TRACK_TIMELOCK)), + ], + meta, + ProposalType.REGULAR, + ); +}; + +export default vip680FastTrack; diff --git a/vips/vip-680/bsctestnet-guardian.ts b/vips/vip-680/bsctestnet-guardian.ts new file mode 100644 index 000000000..febba28e5 --- /dev/null +++ b/vips/vip-680/bsctestnet-guardian.ts @@ -0,0 +1,85 @@ +import { ProposalType } from "src/types"; +import { makeProposal } from "src/utils"; + +import { CORE_SOURCE_USDT, FLUX_SOURCE_USDT, FRV_SOURCE_USDT, GUARDIAN, HUB_REGISTRY, HUB_USDT } from "./addresses"; +import { + CORE_FLUX_GOVERNANCE, + FRV_GOVERNANCE, + HUB_FULL, + HUB_REGISTRY_GOVERNANCE, + giveCallPermission, +} from "./permissions"; + +// --------------------------------------------------------------------------------------------------- +// VIP-680 (Guardian permissions) — BNB Chain Testnet, TESTNET ONLY. +// +// Grants the Guardian multisig the FULL permission set across the whole Hub stack (Hub_USDT + Core + +// FRV + Flux sources + HubRegistry) so the backend can add, remove and reconfigure resources — list new +// FRV vaults / Flux markets, change caps, pause/unpause, swap adapters, reorder queues, register/remove +// Hubs — directly through the multisig during testing, without a governance proposal per change. +// +// This is a DELIBERATE testnet-only deviation from the mainnet asymmetric model, where the Operator +// (Guardian) is tighten-only and only Governance (the timelocks) may loosen. DO NOT mirror this proposal +// to mainnet: the mainnet Guardian must receive only the Operator sets (see permissions.ts). +// +// The Hub grant uses HUB_FULL = Governance ∪ {reallocate} (the operator-only sig), so the Guardian holds +// every gated Hub function. On the sources the Operator sets are subsumed by Governance, so the full +// Governance set is granted directly; FRV uses its own set (YieldGroupFRV: no cap setters, no +// setBlocksPerYear, plus forceRemoveResource). +// --------------------------------------------------------------------------------------------------- + +export const vip680Guardian = () => { + const meta = { + version: "v2", + title: "VIP-680 [BNB Chain Testnet] Liquidity Hub (USDT) — Guardian full permissions (testnet only)", + description: `#### Summary + +**Testnet-only.** Last of five proposals onboarding the redeployed **Liquidity Hub (USDT)** on BNB Chain +Testnet. Grants the **Guardian** multisig the **full permission set** across the entire stack: +**Hub_USDT**, the **Core**, **FRV** and **Flux** yield sources, and the **HubRegistry**. This lets the +backend add, remove and reconfigure resources (list new FRV vaults / Flux markets, change caps, +pause/unpause, swap adapters, reorder queues, register/remove Hubs) directly through the Guardian +multisig during testing, without a governance proposal per change. + +#### Access-control note + +On mainnet the Hub uses an asymmetric model: only Governance (the timelocks) may loosen, while the +Operator (Guardian) is tighten-only. Granting the Guardian the full set here is a **deliberate deviation +made purely to speed up testnet iteration, and must not be mirrored to mainnet.** + +#### Actions + +Grant to the **Guardian** multisig: + +1. Every gated function on **Hub_USDT** — the full Governance set plus the operator-only \`reallocate\`. +2. The full Governance role set on the **Core**, **FRV** and **Flux** yield sources. +3. \`addHub\` / \`removeHub\` on the **HubRegistry**. + +#### Notes + +- Permissions only — this proposal performs no wiring. +- Testnet-only. The Hub is not yet deployed on any mainnet.`, + forDescription: "Execute this proposal", + againstDescription: "Do not execute this proposal", + abstainDescription: "Indifferent to execution", + }; + + return makeProposal( + [ + // 1. Hub_USDT — every gated function (Governance ∪ reallocate) -> Guardian. + ...HUB_FULL.map(sig => giveCallPermission(HUB_USDT, sig, GUARDIAN)), + + // 2. Core / FRV / Flux sources — full Governance set -> Guardian. + ...CORE_FLUX_GOVERNANCE.map(sig => giveCallPermission(CORE_SOURCE_USDT, sig, GUARDIAN)), + ...FRV_GOVERNANCE.map(sig => giveCallPermission(FRV_SOURCE_USDT, sig, GUARDIAN)), + ...CORE_FLUX_GOVERNANCE.map(sig => giveCallPermission(FLUX_SOURCE_USDT, sig, GUARDIAN)), + + // 3. HubRegistry — addHub / removeHub -> Guardian. + ...HUB_REGISTRY_GOVERNANCE.map(sig => giveCallPermission(HUB_REGISTRY, sig, GUARDIAN)), + ], + meta, + ProposalType.REGULAR, + ); +}; + +export default vip680Guardian; diff --git a/vips/vip-680/bsctestnet-wiring.ts b/vips/vip-680/bsctestnet-wiring.ts new file mode 100644 index 000000000..c730c3b19 --- /dev/null +++ b/vips/vip-680/bsctestnet-wiring.ts @@ -0,0 +1,121 @@ +import { ProposalType } from "src/types"; +import { makeProposal } from "src/utils"; + +import { + ABSOLUTE_CAP_UNBOUNDED, + ADAPTER_CORE_V1, + ADAPTER_FLUX, + ADAPTER_FRV, + CORE_SOURCE_USDT, + FLUX_SOURCE_USDT, + FRV_SOURCE_USDT, + FRV_VAULT_USDT, + FUSDT_FLUX, + HUB_REGISTRY, + HUB_USDT, + PERCENTAGE_CAP_DISABLED, + VUSDT_CORE, +} from "./addresses"; + +// --------------------------------------------------------------------------------------------------- +// VIP-680 (wiring) — BNB Chain Testnet. +// +// Registers the Hub in the HubRegistry and wires all three yield sources (Core, FRV, Flux) +// end-to-end, in one atomic proposal. Depends on the main VIP-680 proposal, which grants the Normal +// Timelock the roles used here and accepts the Hub/registry ownership; this proposal must execute +// after it. +// +// Ordering is load-bearing and must not be reshuffled: +// - addHub comes FIRST, before every addYieldGroup, so HubAdded lands at a lower log index than any +// YieldGroupAdded (indexers rely on this to seed each Hub's yield-group set correctly). +// - each source's addResource precedes its inner-queue setters (the setters reject unregistered +// resources). +// - each addYieldGroup precedes the Hub's outer-queue setters (they reject unregistered groups). +// +// Outer queue order [FRV, Flux, Core] (deposit and withdraw): FRV first so deposits reach the vault +// under test; Core last because it is registered uncapped and would otherwise swallow every deposit +// before the others receive any. All three groups are registered uncapped (type(uint128).max absolute, +// 10_000 bps disabling the percentage dimension), matching the testnet policy. +// --------------------------------------------------------------------------------------------------- + +const CORE_ONLY = [VUSDT_CORE]; +const FRV_ONLY = [FRV_VAULT_USDT]; +const FLUX_ONLY = [FUSDT_FLUX]; + +// Outer queues: FRV -> Flux -> Core, same order for deposits and withdrawals. Every registered group +// is listed so the withdraw-queue funded-coverage guard is satisfied now and after any deposit. +const OUTER_QUEUE = [FRV_SOURCE_USDT, FLUX_SOURCE_USDT, CORE_SOURCE_USDT]; + +const addYieldGroup = (source: string) => ({ + target: HUB_USDT, + signature: "addYieldGroup(address,uint256,uint16)", + params: [source, ABSOLUTE_CAP_UNBOUNDED, PERCENTAGE_CAP_DISABLED], +}); + +export const vip680Wiring = () => { + const meta = { + version: "v2", + title: "VIP-680 [BNB Chain Testnet] Liquidity Hub (USDT) — register Hub and wire Core, FRV and Flux", + description: `#### Summary + +Second of five proposals onboarding the redeployed **Liquidity Hub (USDT)** on BNB Chain Testnet. +Using the Governance roles granted to the Normal Timelock in the main VIP-680 proposal, this proposal +registers the Hub in the **HubRegistry** and wires all three yield sources end-to-end so the Hub routes +deposits and withdrawals through **FRV**, **Flux** and **Core**. + +#### Actions (one atomic transaction, in order) + +1. Register **Hub_USDT** in the **HubRegistry** (\`addHub\`) — ordered before any \`addYieldGroup\` so the + \`HubAdded\` event precedes every \`YieldGroupAdded\`. +2. On the **Core** source, register **vUSDT** behind **AdapterCoreV1** and set its inner deposit/withdraw + queues. +3. On the **FRV** source, register the **FRV vault** behind **AdapterFRV** and set its inner queues. +4. On the **Flux** source, register the **Fluid fUSDT** fToken behind **AdapterFlux** and set its inner + queues. +5. Register all three sources on **Hub_USDT** with an effectively-unbounded cap, then point the Hub's + outer deposit and withdraw queues at **[FRV, Flux, Core]**. + +#### Notes + +- Queue order routes deposits to FRV first, then Flux, with Core last as the uncapped backstop. +- All three groups are registered uncapped (testnet policy); caps can be tightened later by governance + or the operator. +- Testnet-only. The Hub is not yet deployed on any mainnet.`, + forDescription: "Execute this proposal", + againstDescription: "Do not execute this proposal", + abstainDescription: "Indifferent to execution", + }; + + return makeProposal( + [ + // 1. Register the Hub in the registry (must precede every addYieldGroup below). + { target: HUB_REGISTRY, signature: "addHub(address)", params: [HUB_USDT] }, + + // 2. Core source: register vUSDT behind AdapterCoreV1, then set inner queues. + { target: CORE_SOURCE_USDT, signature: "addResource(address,address)", params: [VUSDT_CORE, ADAPTER_CORE_V1] }, + { target: CORE_SOURCE_USDT, signature: "setInnerDepositQueue(address[])", params: [CORE_ONLY] }, + { target: CORE_SOURCE_USDT, signature: "setInnerWithdrawQueue(address[])", params: [CORE_ONLY] }, + + // 3. FRV source: register the FRV vault behind AdapterFRV, then set inner queues. + { target: FRV_SOURCE_USDT, signature: "addResource(address,address)", params: [FRV_VAULT_USDT, ADAPTER_FRV] }, + { target: FRV_SOURCE_USDT, signature: "setInnerDepositQueue(address[])", params: [FRV_ONLY] }, + { target: FRV_SOURCE_USDT, signature: "setInnerWithdrawQueue(address[])", params: [FRV_ONLY] }, + + // 4. Flux source: register the Fluid fUSDT fToken behind AdapterFlux, then set inner queues. + { target: FLUX_SOURCE_USDT, signature: "addResource(address,address)", params: [FUSDT_FLUX, ADAPTER_FLUX] }, + { target: FLUX_SOURCE_USDT, signature: "setInnerDepositQueue(address[])", params: [FLUX_ONLY] }, + { target: FLUX_SOURCE_USDT, signature: "setInnerWithdrawQueue(address[])", params: [FLUX_ONLY] }, + + // 5. Register the three sources on the Hub (in queue order), then point the outer queues at them. + addYieldGroup(FRV_SOURCE_USDT), + addYieldGroup(FLUX_SOURCE_USDT), + addYieldGroup(CORE_SOURCE_USDT), + { target: HUB_USDT, signature: "setOuterDepositQueue(address[])", params: [OUTER_QUEUE] }, + { target: HUB_USDT, signature: "setOuterWithdrawQueue(address[])", params: [OUTER_QUEUE] }, + ], + meta, + ProposalType.REGULAR, + ); +}; + +export default vip680Wiring; diff --git a/vips/vip-680/bsctestnet.ts b/vips/vip-680/bsctestnet.ts new file mode 100644 index 000000000..fa1f7cd53 --- /dev/null +++ b/vips/vip-680/bsctestnet.ts @@ -0,0 +1,96 @@ +import { ProposalType } from "src/types"; +import { makeProposal } from "src/utils"; + +import { + CORE_SOURCE_USDT, + FLUX_SOURCE_USDT, + FRV_SOURCE_USDT, + HUB_REGISTRY, + HUB_USDT, + NORMAL_TIMELOCK, +} from "./addresses"; +import { + CORE_FLUX_GOVERNANCE, + FRV_GOVERNANCE, + HUB_GOVERNANCE, + HUB_REGISTRY_GOVERNANCE, + giveCallPermission, +} from "./permissions"; + +// --------------------------------------------------------------------------------------------------- +// VIP-680 (main) — BNB Chain Testnet. +// +// First of five proposals that onboard the freshly redeployed Liquidity Hub (USDT) stack. This one +// gives the Normal Timelock full control of the stack: it grants the Governance role set on the Hub, +// the three yield sources (Core / FRV / Flux) and the HubRegistry, and it accepts the Hub's and the +// registry's pending Ownable2Step ownership (both were transferred to the Normal Timelock at deploy). +// +// Ownership is accepted here, early, on purpose: it retires the deployer's owner key immediately, so +// the deployer can no longer repoint `setAccessControlManager` in the deploy -> onboarding window. It +// has no effect on indexers, whose only ordering requirement (HubAdded before every YieldGroupAdded) +// lives entirely in the wiring proposal. +// +// Companion proposals: a wiring proposal registers the Hub and wires the three sources end-to-end (it +// needs the roles granted here, so it executes after this one); two addendum proposals grant the same +// Governance set to the Fast-Track and Critical timelocks; and a testnet-only proposal tops the +// Guardian up to full permissions. +// --------------------------------------------------------------------------------------------------- + +export const vip680 = () => { + const meta = { + version: "v2", + title: "VIP-680 [BNB Chain Testnet] Liquidity Hub (USDT) — Normal Timelock permissions and ownership", + description: `#### Summary + +First of five proposals onboarding the newly redeployed **Liquidity Hub (USDT)** on BNB Chain Testnet. +The Hub, its three yield sources (Core, FRV, Flux) and the HubRegistry were deployed with no ACM +permissions and their ownership left pending to governance. This proposal gives the **Normal Timelock** +full control: the Governance role set across the whole stack, plus acceptance of the Hub's and +registry's pending ownership. + +#### Access-control model + +The Hub uses an asymmetric permission model: **Governance** can both loosen and tighten, while the +**Operator** can only tighten (lower caps, pause, reorder queues) plus \`reallocate\`. This proposal +provisions the Governance set for the Normal Timelock, which then performs the wiring in the companion +wiring proposal. + +#### Actions + +1. Grant the **Governance** role set to the Normal Timelock on **Hub_USDT** (registry, caps, queues, + pause/unpause, fees, sweep, adapter updates, \`emergencyReallocate\`), the **Core**, **FRV** and + **Flux** yield sources, and the **HubRegistry** (\`addHub\` / \`removeHub\`). +2. Accept the pending ownership of the **HubRegistry** and **Hub_USDT** (both transferred to the Normal + Timelock at deploy), so the deployer no longer holds the owner key that gates + \`setAccessControlManager\`. + +#### Notes + +- The Normal Timelock intentionally does **not** receive the operator-only \`reallocate\` role. +- No routing is wired here; the companion wiring proposal registers the Hub and the Core/FRV/Flux + resources once this proposal has executed. +- Testnet-only. The Hub is not yet deployed on any mainnet.`, + forDescription: "Execute this proposal", + againstDescription: "Do not execute this proposal", + abstainDescription: "Indifferent to execution", + }; + + return makeProposal( + [ + // 1. Governance role set -> Normal Timelock, across the whole stack. + ...HUB_GOVERNANCE.map(sig => giveCallPermission(HUB_USDT, sig, NORMAL_TIMELOCK)), + ...CORE_FLUX_GOVERNANCE.map(sig => giveCallPermission(CORE_SOURCE_USDT, sig, NORMAL_TIMELOCK)), + ...FRV_GOVERNANCE.map(sig => giveCallPermission(FRV_SOURCE_USDT, sig, NORMAL_TIMELOCK)), + ...CORE_FLUX_GOVERNANCE.map(sig => giveCallPermission(FLUX_SOURCE_USDT, sig, NORMAL_TIMELOCK)), + ...HUB_REGISTRY_GOVERNANCE.map(sig => giveCallPermission(HUB_REGISTRY, sig, NORMAL_TIMELOCK)), + + // 2. Accept pending ownership (Ownable2Step) — registry first, then the Hub. + { target: HUB_REGISTRY, signature: "acceptOwnership()", params: [] }, + { target: HUB_USDT, signature: "acceptOwnership()", params: [] }, + ], + meta, + ProposalType.REGULAR, + ); +}; + +export default vip680; diff --git a/vips/vip-680/permissions.ts b/vips/vip-680/permissions.ts new file mode 100644 index 000000000..9fcd77b0d --- /dev/null +++ b/vips/vip-680/permissions.ts @@ -0,0 +1,124 @@ +import { ACM } from "./addresses"; + +// =================================================================================================== +// VIP-680 — Liquidity Hub (USDT) ACM role-string sets. +// +// Every string is the literal function signature passed to `_checkAccessAllowed(...)` in the deployed +// contracts (the ACM role is `keccak256(targetContract, roleString)`), copied verbatim from +// venus-liquidity-hub/contracts. The asymmetric model (README "Permissions"): Governance may loosen +// AND tighten; the Operator may only tighten (lower caps, pause, reorder queues) plus `reallocate`. +// +// Source of truth per contract: +// Hub/Hub.sol -> HUB_* (19 gated fns: 18 governance + operator-only reallocate) +// YieldGroup/base/YieldGroupBase -> YIELD_GROUP_BASE (8 shared gated fns) +// YieldGroup/YieldGroup.sol -> CORE_FLUX_* (base + resource caps + setBlocksPerYear) +// YieldGroup/YieldGroupFRV.sol -> FRV_* (base + forceRemoveResource; NO caps, NO blocksPerYear) +// registry/HubRegistry.sol -> HUB_REGISTRY (addHub / removeHub) +// =================================================================================================== + +// Operator-only on the Hub: net-zero rebalance, callable only while unpaused. NOT in the Governance +// set (governance holds the pause-bypassing `emergencyReallocate` instead). +export const REALLOCATE = "reallocate((address,address,uint256)[],(address,address,uint256)[])"; + +// --------------------------------------------------------------------------------------------------- +// Hub_USDT. +// --------------------------------------------------------------------------------------------------- + +// Governance role set: every gated Hub function EXCEPT `reallocate` (operator-only). +export const HUB_GOVERNANCE = [ + "addYieldGroup(address,uint256,uint16)", + "removeYieldGroup(address)", + "raiseYieldGroupCap(address,uint256,uint16)", + "lowerYieldGroupCap(address,uint256,uint16)", + "setOuterDepositQueue(address[])", + "setOuterWithdrawQueue(address[])", + "emergencyReallocate((address,address,uint256)[],(address,address,uint256)[])", + "pauseHub()", + "unpauseHub()", + "pauseYieldGroup(address)", + "unpauseYieldGroup(address)", + "raiseMaxWithdrawalSize(uint256)", + "lowerMaxWithdrawalSize(uint256)", + "setManagementFeeBps(uint16)", + "setPerformanceFeeBps(uint16)", + "setRedeemFeeBps(uint16)", + "setFeeRecipient(address)", + "sweep(address,address)", +]; + +// Operator role set: tighten-only + `reallocate` + emergency pause. +export const HUB_OPERATOR = [ + "lowerYieldGroupCap(address,uint256,uint16)", + "setOuterDepositQueue(address[])", + "setOuterWithdrawQueue(address[])", + REALLOCATE, + "pauseHub()", + "pauseYieldGroup(address)", + "lowerMaxWithdrawalSize(uint256)", +]; + +// Full control on the Hub = Governance ∪ {reallocate}, deduped (reallocate is the only operator sig +// not already in HUB_GOVERNANCE). Used only by the testnet Guardian proposal. +export const HUB_FULL = [...HUB_GOVERNANCE, REALLOCATE]; + +// --------------------------------------------------------------------------------------------------- +// Yield sources (Core / FRV / Flux). All three share the YieldGroupBase surface; each subclass adds +// a few functions. +// --------------------------------------------------------------------------------------------------- + +// Shared by all three sources. +export const YIELD_GROUP_BASE = [ + "addResource(address,address)", + "removeResource(address)", + "updateResourceAdapter(address,address)", + "setInnerDepositQueue(address[])", + "setInnerWithdrawQueue(address[])", + "pauseResource(address)", + "unpauseResource(address)", + "sweep(address,address)", +]; + +// Core & Flux use `YieldGroup`: base + per-resource caps + blocksPerYear. +export const CORE_FLUX_GOVERNANCE = [ + ...YIELD_GROUP_BASE, + "raiseResourceCap(address,uint256)", + "lowerResourceCap(address,uint256)", + "setBlocksPerYear(uint256)", +]; + +// FRV uses `YieldGroupFRV`: base + forceRemoveResource. It has NO cap setters and NO setBlocksPerYear, +// so it must not reuse CORE_FLUX_GOVERNANCE. +export const FRV_GOVERNANCE = [...YIELD_GROUP_BASE, "forceRemoveResource(address)"]; + +// Operator (tighten-only) sets. Core/Flux can lower per-resource caps; FRV has no cap to lower. +export const CORE_FLUX_OPERATOR = [ + "setInnerDepositQueue(address[])", + "setInnerWithdrawQueue(address[])", + "pauseResource(address)", + "lowerResourceCap(address,uint256)", +]; +export const FRV_OPERATOR = [ + "setInnerDepositQueue(address[])", + "setInnerWithdrawQueue(address[])", + "pauseResource(address)", +]; + +// --------------------------------------------------------------------------------------------------- +// HubRegistry — the only two gated functions. +// --------------------------------------------------------------------------------------------------- +export const HUB_REGISTRY_GOVERNANCE = ["addHub(address)", "removeHub(address)"]; + +// --------------------------------------------------------------------------------------------------- +// Command builder: ACM.giveCallPermission(contract, roleString, account). ACM is the single ACM the +// Hub stack checks against (see addresses.ts). +// --------------------------------------------------------------------------------------------------- +export const giveCallPermission = (contract: string, sig: string, account: string) => ({ + target: ACM, + signature: "giveCallPermission(address,string,address)", + params: [contract, sig, account], +}); + +// NOTE for a future mainnet VIP: the mainnet Guardian must receive ONLY the Operator sets +// (HUB_OPERATOR on the Hub, CORE_FLUX_OPERATOR on Core/Flux, FRV_OPERATOR on FRV — no registry, no +// Governance). HUB_FULL / the full-Governance Guardian grant in bsctestnet-guardian.ts is a +// testnet-only deviation and must not be mirrored to mainnet.