diff --git a/simulations/vip-999/abi/AccessControlManager.json b/simulations/vip-999/abi/AccessControlManager.json new file mode 100644 index 000000000..2ef119947 --- /dev/null +++ b/simulations/vip-999/abi/AccessControlManager.json @@ -0,0 +1,157 @@ +[ + { "inputs": [], "stateMutability": "nonpayable", "type": "constructor" }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "account", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "contractAddress", "type": "address" }, + { "indexed": false, "internalType": "string", "name": "functionSig", "type": "string" } + ], + "name": "PermissionGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "account", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "contractAddress", "type": "address" }, + { "indexed": false, "internalType": "string", "name": "functionSig", "type": "string" } + ], + "name": "PermissionRevoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "indexed": true, "internalType": "bytes32", "name": "previousAdminRole", "type": "bytes32" }, + { "indexed": true, "internalType": "bytes32", "name": "newAdminRole", "type": "bytes32" } + ], + "name": "RoleAdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "indexed": true, "internalType": "address", "name": "account", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "sender", "type": "address" } + ], + "name": "RoleGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "indexed": true, "internalType": "address", "name": "account", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "sender", "type": "address" } + ], + "name": "RoleRevoked", + "type": "event" + }, + { + "inputs": [], + "name": "DEFAULT_ADMIN_ROLE", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "bytes32", "name": "role", "type": "bytes32" }], + "name": "getRoleAdmin", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "contractAddress", "type": "address" }, + { "internalType": "string", "name": "functionSig", "type": "string" }, + { "internalType": "address", "name": "accountToPermit", "type": "address" } + ], + "name": "giveCallPermission", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "grantRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" }, + { "internalType": "address", "name": "contractAddress", "type": "address" }, + { "internalType": "string", "name": "functionSig", "type": "string" } + ], + "name": "hasPermission", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "hasRole", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" }, + { "internalType": "string", "name": "functionSig", "type": "string" } + ], + "name": "isAllowedToCall", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "renounceRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "contractAddress", "type": "address" }, + { "internalType": "string", "name": "functionSig", "type": "string" }, + { "internalType": "address", "name": "accountToRevoke", "type": "address" } + ], + "name": "revokeCallPermission", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "revokeRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "bytes4", "name": "interfaceId", "type": "bytes4" }], + "name": "supportsInterface", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + } +] diff --git a/simulations/vip-999/abi/BoundValidator.json b/simulations/vip-999/abi/BoundValidator.json new file mode 100644 index 000000000..6fa133e31 --- /dev/null +++ b/simulations/vip-999/abi/BoundValidator.json @@ -0,0 +1,333 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "calledContract", + "type": "address" + }, + { + "internalType": "string", + "name": "methodSignature", + "type": "string" + } + ], + "name": "Unauthorized", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "oldAccessControlManager", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAccessControlManager", + "type": "address" + } + ], + "name": "NewAccessControlManager", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "upperBound", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "lowerBound", + "type": "uint256" + } + ], + "name": "ValidateConfigAdded", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "accessControlManager", + "outputs": [ + { + "internalType": "contract IAccessControlManagerV8", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "accessControlManager_", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "accessControlManager_", + "type": "address" + } + ], + "name": "setAccessControlManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "internalType": "uint256", + "name": "upperBoundRatio", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lowerBoundRatio", + "type": "uint256" + } + ], + "internalType": "struct BoundValidator.ValidateConfig", + "name": "config", + "type": "tuple" + } + ], + "name": "setValidateConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "internalType": "uint256", + "name": "upperBoundRatio", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lowerBoundRatio", + "type": "uint256" + } + ], + "internalType": "struct BoundValidator.ValidateConfig[]", + "name": "configs", + "type": "tuple[]" + } + ], + "name": "setValidateConfigs", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "validateConfigs", + "outputs": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "internalType": "uint256", + "name": "upperBoundRatio", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lowerBoundRatio", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "internalType": "uint256", + "name": "reportedPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "anchorPrice", + "type": "uint256" + } + ], + "name": "validatePriceWithAnchorPrice", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/simulations/vip-999/abi/ChainlinkOracle.json b/simulations/vip-999/abi/ChainlinkOracle.json new file mode 100644 index 000000000..a30575f77 --- /dev/null +++ b/simulations/vip-999/abi/ChainlinkOracle.json @@ -0,0 +1,187 @@ +[ + { "inputs": [], "stateMutability": "nonpayable", "type": "constructor" }, + { + "inputs": [ + { "internalType": "address", "name": "sender", "type": "address" }, + { "internalType": "address", "name": "calledContract", "type": "address" }, + { "internalType": "string", "name": "methodSignature", "type": "string" } + ], + "name": "Unauthorized", + "type": "error" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "internalType": "uint8", "name": "version", "type": "uint8" }], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "oldAccessControlManager", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "newAccessControlManager", "type": "address" } + ], + "name": "NewAccessControlManager", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "previousOwner", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "newOwner", "type": "address" } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "previousOwner", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "newOwner", "type": "address" } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "asset", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "previousPriceMantissa", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "newPriceMantissa", "type": "uint256" } + ], + "name": "PricePosted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "asset", "type": "address" }, + { "indexed": false, "internalType": "address", "name": "feed", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "maxStalePeriod", "type": "uint256" } + ], + "name": "TokenConfigAdded", + "type": "event" + }, + { + "inputs": [], + "name": "BNB_ADDR", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { "inputs": [], "name": "acceptOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "accessControlManager", + "outputs": [{ "internalType": "contract IAccessControlManagerV8", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "asset", "type": "address" }], + "name": "getPrice", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "accessControlManager_", "type": "address" }], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "", "type": "address" }], + "name": "prices", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { "inputs": [], "name": "renounceOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [{ "internalType": "address", "name": "accessControlManager_", "type": "address" }], + "name": "setAccessControlManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "asset", "type": "address" }, + { "internalType": "uint256", "name": "price", "type": "uint256" } + ], + "name": "setDirectPrice", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "asset", "type": "address" }, + { "internalType": "address", "name": "feed", "type": "address" }, + { "internalType": "uint256", "name": "maxStalePeriod", "type": "uint256" } + ], + "internalType": "struct ChainlinkOracle.TokenConfig", + "name": "tokenConfig", + "type": "tuple" + } + ], + "name": "setTokenConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "asset", "type": "address" }, + { "internalType": "address", "name": "feed", "type": "address" }, + { "internalType": "uint256", "name": "maxStalePeriod", "type": "uint256" } + ], + "internalType": "struct ChainlinkOracle.TokenConfig[]", + "name": "tokenConfigs_", + "type": "tuple[]" + } + ], + "name": "setTokenConfigs", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "", "type": "address" }], + "name": "tokenConfigs", + "outputs": [ + { "internalType": "address", "name": "asset", "type": "address" }, + { "internalType": "address", "name": "feed", "type": "address" }, + { "internalType": "uint256", "name": "maxStalePeriod", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "newOwner", "type": "address" }], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/simulations/vip-999/abi/InstitutionalLoanVault.json b/simulations/vip-999/abi/InstitutionalLoanVault.json new file mode 100644 index 000000000..5a0cac9a0 --- /dev/null +++ b/simulations/vip-999/abi/InstitutionalLoanVault.json @@ -0,0 +1,2062 @@ +[ + { + "type": "constructor", + "inputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "BPS", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "MANTISSA_ONE", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "YEAR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "allowance", + "inputs": [ + { + "name": "owner", + "type": "address", + "internalType": "address" + }, + { + "name": "spender", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "approve", + "inputs": [ + { + "name": "spender", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "asset", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "balanceOf", + "inputs": [ + { + "name": "account", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "calculateSeizeAmount", + "inputs": [ + { + "name": "repayAmount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "liquidationType", + "type": "uint8", + "internalType": "enum LiquidationType" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "cancelVault", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "claimRaisedFunds", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "closeVault", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "completePause", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "config", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "tuple", + "internalType": "struct VaultConfig", + "components": [ + { + "name": "supplyAsset", + "type": "address", + "internalType": "contract IERC20" + }, + { + "name": "fixedAPY", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "reserveFactor", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "minBorrowCap", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "maxBorrowCap", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "minSupplierDeposit", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "openDuration", + "type": "uint40", + "internalType": "uint40" + }, + { + "name": "lockDuration", + "type": "uint40", + "internalType": "uint40" + }, + { + "name": "settlementWindow", + "type": "uint40", + "internalType": "uint40" + } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "convertToAssets", + "inputs": [ + { + "name": "shares", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "convertToShares", + "inputs": [ + { + "name": "assets", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "decimals", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint8", + "internalType": "uint8" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "decreaseAllowance", + "inputs": [ + { + "name": "spender", + "type": "address", + "internalType": "address" + }, + { + "name": "subtractedValue", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "deposit", + "inputs": [ + { + "name": "assets", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "receiver", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "shares", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "depositCollateral", + "inputs": [ + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "getCollateralValueUSD", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getDebtValueUSD", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getHypotheticalVaultLiquidity", + "inputs": [ + { + "name": "withdrawAmount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "additionalDebt", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "liquidity", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "shortfall", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getVaultLiquidity", + "inputs": [], + "outputs": [ + { + "name": "liquidity", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "shortfall", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "increaseAllowance", + "inputs": [ + { + "name": "spender", + "type": "address", + "internalType": "address" + }, + { + "name": "addedValue", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "initialize", + "inputs": [ + { + "name": "config_", + "type": "tuple", + "internalType": "struct VaultConfig", + "components": [ + { + "name": "supplyAsset", + "type": "address", + "internalType": "contract IERC20" + }, + { + "name": "fixedAPY", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "reserveFactor", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "minBorrowCap", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "maxBorrowCap", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "minSupplierDeposit", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "openDuration", + "type": "uint40", + "internalType": "uint40" + }, + { + "name": "lockDuration", + "type": "uint40", + "internalType": "uint40" + }, + { + "name": "settlementWindow", + "type": "uint40", + "internalType": "uint40" + } + ] + }, + { + "name": "instConfig_", + "type": "tuple", + "internalType": "struct InstitutionalConfig", + "components": [ + { + "name": "collateralAsset", + "type": "address", + "internalType": "contract IERC20" + }, + { + "name": "idealCollateralAmount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "marginRate", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "institutionOperator", + "type": "address", + "internalType": "address" + }, + { + "name": "positionTokenId", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "name": "riskConfig_", + "type": "tuple", + "internalType": "struct RiskConfig", + "components": [ + { + "name": "liquidationThreshold", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "liquidationIncentive", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "latePenaltyRate", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "name": "positionToken_", + "type": "address", + "internalType": "contract IInstitutionPositionToken" + }, + { + "name": "name_", + "type": "string", + "internalType": "string" + }, + { + "name": "symbol_", + "type": "string", + "internalType": "string" + }, + { + "name": "institutionName_", + "type": "string", + "internalType": "string" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "institutionName", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "string", + "internalType": "string" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "institutionalConfig", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "tuple", + "internalType": "struct InstitutionalConfig", + "components": [ + { + "name": "collateralAsset", + "type": "address", + "internalType": "contract IERC20" + }, + { + "name": "idealCollateralAmount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "marginRate", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "institutionOperator", + "type": "address", + "internalType": "address" + }, + { + "name": "positionTokenId", + "type": "uint256", + "internalType": "uint256" + } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "institutionalRuntime", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "tuple", + "internalType": "struct InstitutionalRuntime", + "components": [ + { + "name": "totalCollateralDeposited", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "minimumCollateralRequired", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "confiscatedMarginRemaining", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "institutionDefaulted", + "type": "bool", + "internalType": "bool" + } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "liquidate", + "inputs": [ + { + "name": "repayAmount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "actualRepay", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "liquidateOverdueVault", + "inputs": [ + { + "name": "repayAmount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "actualRepay", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "maxDeposit", + "inputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "maxMint", + "inputs": [ + { + "name": "receiver", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "maxRedeem", + "inputs": [ + { + "name": "owner", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "maxWithdraw", + "inputs": [ + { + "name": "owner", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "mint", + "inputs": [ + { + "name": "shares", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "receiver", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "assets", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "name", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "string", + "internalType": "string" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "openVault", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "outstandingDebt", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "partialPause", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "pauseLevel", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint8", + "internalType": "enum PauseLevel" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "positionToken", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "contract IInstitutionPositionToken" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "previewDeposit", + "inputs": [ + { + "name": "assets", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "previewMint", + "inputs": [ + { + "name": "shares", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "previewRedeem", + "inputs": [ + { + "name": "shares", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "previewWithdraw", + "inputs": [ + { + "name": "assets", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "redeem", + "inputs": [ + { + "name": "shares", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "receiver", + "type": "address", + "internalType": "address" + }, + { + "name": "owner", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "assets", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "repay", + "inputs": [ + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "repayBadDebt", + "inputs": [ + { + "name": "repayAmount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "riskConfig", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "tuple", + "internalType": "struct RiskConfig", + "components": [ + { + "name": "liquidationThreshold", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "liquidationIncentive", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "latePenaltyRate", + "type": "uint256", + "internalType": "uint256" + } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "runtime", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "tuple", + "internalType": "struct VaultRuntime", + "components": [ + { + "name": "state", + "type": "uint8", + "internalType": "enum VaultState" + }, + { + "name": "openStartTime", + "type": "uint40", + "internalType": "uint40" + }, + { + "name": "openEndTime", + "type": "uint40", + "internalType": "uint40" + }, + { + "name": "lockStartTime", + "type": "uint40", + "internalType": "uint40" + }, + { + "name": "lockEndTime", + "type": "uint40", + "internalType": "uint40" + }, + { + "name": "settlementDeadline", + "type": "uint40", + "internalType": "uint40" + }, + { + "name": "totalRaised", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "totalDebt", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "settlementAmount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "fundsWithdrawn", + "type": "bool", + "internalType": "bool" + }, + { + "name": "protocolShareSettled", + "type": "bool", + "internalType": "bool" + } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "setInstitutionName", + "inputs": [ + { + "name": "newName", + "type": "string", + "internalType": "string" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setLatePenaltyRate", + "inputs": [ + { + "name": "newRate", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setLiquidationIncentive", + "inputs": [ + { + "name": "newLI", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setLiquidationThreshold", + "inputs": [ + { + "name": "newLT", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "state", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint8", + "internalType": "enum VaultState" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "sweep", + "inputs": [ + { + "name": "token", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "symbol", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "string", + "internalType": "string" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "totalAssets", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "totalSupply", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "transfer", + "inputs": [ + { + "name": "to", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "transferFrom", + "inputs": [ + { + "name": "from", + "type": "address", + "internalType": "address" + }, + { + "name": "to", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "unpause", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "updateVaultState", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "vaultController", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "withdraw", + "inputs": [ + { + "name": "assets", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "receiver", + "type": "address", + "internalType": "address" + }, + { + "name": "owner", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "shares", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "withdrawCollateral", + "inputs": [ + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "Approval", + "inputs": [ + { + "name": "owner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "spender", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "value", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "CollateralDeposited", + "inputs": [ + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "totalCollateral", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "CollateralWithdrawn", + "inputs": [ + { + "name": "positionHolder", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "remaining", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Deposit", + "inputs": [ + { + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "owner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "assets", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "shares", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Initialized", + "inputs": [ + { + "name": "version", + "type": "uint8", + "indexed": false, + "internalType": "uint8" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "InstitutionNameUpdated", + "inputs": [ + { + "name": "oldName", + "type": "string", + "indexed": false, + "internalType": "string" + }, + { + "name": "newName", + "type": "string", + "indexed": false, + "internalType": "string" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "LatePenaltyRateUpdated", + "inputs": [ + { + "name": "oldRate", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "newRate", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "LiquidationExecuted", + "inputs": [ + { + "name": "liquidator", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "repayAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "collateralSeized", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "LiquidationIncentiveUpdated", + "inputs": [ + { + "name": "oldLI", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "newLI", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "LiquidationThresholdUpdated", + "inputs": [ + { + "name": "oldLT", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "newLT", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "MarginCompensationClaimed", + "inputs": [ + { + "name": "receiver", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "MarginConfiscated", + "inputs": [ + { + "name": "marginAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OverdueLiquidationExecuted", + "inputs": [ + { + "name": "settler", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "repayAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "collateralSeized", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "PSRNotificationFailed", + "inputs": [ + { + "name": "psr", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "reason", + "type": "bytes", + "indexed": false, + "internalType": "bytes" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "PauseLevelSet", + "inputs": [ + { + "name": "oldLevel", + "type": "uint8", + "indexed": false, + "internalType": "enum PauseLevel" + }, + { + "name": "newLevel", + "type": "uint8", + "indexed": false, + "internalType": "enum PauseLevel" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "RaisedFundsClaimed", + "inputs": [ + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Repaid", + "inputs": [ + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "remainingDebt", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "SettlementConfirmed", + "inputs": [ + { + "name": "settlementAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "protocolFee", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "surplus", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ShortfallDetected", + "inputs": [ + { + "name": "totalOwed", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "available", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "StateTransition", + "inputs": [ + { + "name": "from", + "type": "uint8", + "indexed": true, + "internalType": "enum VaultState" + }, + { + "name": "to", + "type": "uint8", + "indexed": true, + "internalType": "enum VaultState" + }, + { + "name": "timestamp", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "TokensSwept", + "inputs": [ + { + "name": "token", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "recipient", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Transfer", + "inputs": [ + { + "name": "from", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "to", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "value", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "VaultCancelled", + "inputs": [ + { + "name": "recipient", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "collateralAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "VaultClosed", + "inputs": [ + { + "name": "state", + "type": "uint8", + "indexed": false, + "internalType": "enum VaultState" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "VaultFailed", + "inputs": [ + { + "name": "totalRaised", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "minBorrowCap", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "VaultLiquidated", + "inputs": [ + { + "name": "available", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "VaultLocked", + "inputs": [ + { + "name": "totalRaised", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "lockEndTime", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "VaultOpened", + "inputs": [ + { + "name": "openEndTime", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Withdraw", + "inputs": [ + { + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "receiver", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "owner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "assets", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "shares", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "error", + "name": "AlreadyWithdrawn", + "inputs": [] + }, + { + "type": "error", + "name": "BelowMinimumDepositAmount", + "inputs": [] + }, + { + "type": "error", + "name": "ClaimWouldBreachLT", + "inputs": [] + }, + { + "type": "error", + "name": "CompletelyPaused", + "inputs": [] + }, + { + "type": "error", + "name": "ExceedsCloseFactor", + "inputs": [] + }, + { + "type": "error", + "name": "ExceedsMaxCap", + "inputs": [] + }, + { + "type": "error", + "name": "InsufficientCollateral", + "inputs": [] + }, + { + "type": "error", + "name": "InsufficientCollateralForSeize", + "inputs": [ + { + "name": "seizeAmount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "availableCollateral", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "type": "error", + "name": "InsufficientRepayment", + "inputs": [] + }, + { + "type": "error", + "name": "InvalidOraclePrice", + "inputs": [] + }, + { + "type": "error", + "name": "InvalidState", + "inputs": [] + }, + { + "type": "error", + "name": "InvalidStateForOverdueLiquidation", + "inputs": [] + }, + { + "type": "error", + "name": "NoOutstandingDebt", + "inputs": [] + }, + { + "type": "error", + "name": "NotBadDebt", + "inputs": [] + }, + { + "type": "error", + "name": "NotLiquidatable", + "inputs": [] + }, + { + "type": "error", + "name": "NotPositionHolder", + "inputs": [] + }, + { + "type": "error", + "name": "NothingToSweep", + "inputs": [] + }, + { + "type": "error", + "name": "PartiallyPaused", + "inputs": [] + }, + { + "type": "error", + "name": "SameStateTransition", + "inputs": [] + }, + { + "type": "error", + "name": "Unauthorized", + "inputs": [] + }, + { + "type": "error", + "name": "WithdrawExceedsCollateral", + "inputs": [] + }, + { + "type": "error", + "name": "WithdrawalWouldBreachLT", + "inputs": [] + }, + { + "type": "error", + "name": "ZeroRepayAmount", + "inputs": [] + } +] diff --git a/simulations/vip-999/abi/InstitutionalVaultController.json b/simulations/vip-999/abi/InstitutionalVaultController.json new file mode 100644 index 000000000..408194b97 --- /dev/null +++ b/simulations/vip-999/abi/InstitutionalVaultController.json @@ -0,0 +1,1105 @@ +[ + { + "type": "constructor", + "inputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "MANTISSA_ONE", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "MANTISSA_ONE_AND_HALF", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "MAX_APY_BPS", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "acceptOwnership", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "acceptPositionTokenOwnership", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "accessControlManager", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "contract IAccessControlManagerV8" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "allVaults", + "inputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "allVaultsLength", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "approvePositionTransfer", + "inputs": [ + { + "name": "vault", + "type": "address", + "internalType": "address" + }, + { + "name": "recipient", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "cancelVault", + "inputs": [ + { + "name": "vault", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "closeVault", + "inputs": [ + { + "name": "vault", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "completePauseVault", + "inputs": [ + { + "name": "vault", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "comptroller", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "createVault", + "inputs": [ + { + "name": "_vaultConfig", + "type": "tuple", + "internalType": "struct VaultConfig", + "components": [ + { + "name": "supplyAsset", + "type": "address", + "internalType": "contract IERC20" + }, + { + "name": "fixedAPY", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "reserveFactor", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "minBorrowCap", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "maxBorrowCap", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "minSupplierDeposit", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "openDuration", + "type": "uint40", + "internalType": "uint40" + }, + { + "name": "lockDuration", + "type": "uint40", + "internalType": "uint40" + }, + { + "name": "settlementWindow", + "type": "uint40", + "internalType": "uint40" + } + ] + }, + { + "name": "_instConfig", + "type": "tuple", + "internalType": "struct InstitutionalConfig", + "components": [ + { + "name": "collateralAsset", + "type": "address", + "internalType": "contract IERC20" + }, + { + "name": "idealCollateralAmount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "marginRate", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "institutionOperator", + "type": "address", + "internalType": "address" + }, + { + "name": "positionTokenId", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "name": "_riskConfig", + "type": "tuple", + "internalType": "struct RiskConfig", + "components": [ + { + "name": "liquidationThreshold", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "liquidationIncentive", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "latePenaltyRate", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "name": "_name", + "type": "string", + "internalType": "string" + }, + { + "name": "_symbol", + "type": "string", + "internalType": "string" + }, + { + "name": "_institutionName", + "type": "string", + "internalType": "string" + } + ], + "outputs": [ + { + "name": "vault", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "getAggregatedVaultStates", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "tuple[]", + "internalType": "struct VaultStateInfo[]", + "components": [ + { + "name": "vault", + "type": "address", + "internalType": "address" + }, + { + "name": "state", + "type": "uint8", + "internalType": "enum VaultState" + }, + { + "name": "institutionOperator", + "type": "address", + "internalType": "address" + }, + { + "name": "totalRaised", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "outstandingDebt", + "type": "uint256", + "internalType": "uint256" + } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "initialize", + "inputs": [ + { + "name": "vaultImplementation_", + "type": "address", + "internalType": "address" + }, + { + "name": "oracle_", + "type": "address", + "internalType": "address" + }, + { + "name": "protocolShareReserve_", + "type": "address", + "internalType": "address" + }, + { + "name": "comptroller_", + "type": "address", + "internalType": "address" + }, + { + "name": "treasury_", + "type": "address", + "internalType": "address" + }, + { + "name": "positionToken_", + "type": "address", + "internalType": "address" + }, + { + "name": "acm_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "institutionNonce", + "inputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "isRegistered", + "inputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "liquidationAdapter", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "openVault", + "inputs": [ + { + "name": "vault", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "oracle", + "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": "partialPauseVault", + "inputs": [ + { + "name": "vault", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "pendingOwner", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "positionToken", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "contract IInstitutionPositionToken" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "predictVaultAddress", + "inputs": [ + { + "name": "institution", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "protocolShareReserve", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "renounceOwnership", + "inputs": [], + "outputs": [], + "stateMutability": "pure" + }, + { + "type": "function", + "name": "revokePositionTransfer", + "inputs": [ + { + "name": "vault", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setAccessControlManager", + "inputs": [ + { + "name": "accessControlManager_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setComptroller", + "inputs": [ + { + "name": "comptroller_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setLatePenaltyRate", + "inputs": [ + { + "name": "vault", + "type": "address", + "internalType": "address" + }, + { + "name": "newRate", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setLiquidationAdapter", + "inputs": [ + { + "name": "adapter", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setLiquidationIncentive", + "inputs": [ + { + "name": "vault", + "type": "address", + "internalType": "address" + }, + { + "name": "newLI", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setLiquidationThreshold", + "inputs": [ + { + "name": "vault", + "type": "address", + "internalType": "address" + }, + { + "name": "newLT", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setOracle", + "inputs": [ + { + "name": "oracle_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setProtocolShareReserve", + "inputs": [ + { + "name": "psr", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setTreasury", + "inputs": [ + { + "name": "treasury_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setVaultImplementation", + "inputs": [ + { + "name": "impl", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "sweep", + "inputs": [ + { + "name": "vault", + "type": "address", + "internalType": "address" + }, + { + "name": "token", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "transferOwnership", + "inputs": [ + { + "name": "newOwner", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "treasury", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "unpauseVault", + "inputs": [ + { + "name": "vault", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "vaultImplementation", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "event", + "name": "ComptrollerUpdated", + "inputs": [ + { + "name": "oldComptroller", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newComptroller", + "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": "LatePenaltyRateUpdated", + "inputs": [ + { + "name": "vault", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newRate", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "LiquidationAdapterUpdated", + "inputs": [ + { + "name": "oldAdapter", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newAdapter", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "LiquidationIncentiveUpdated", + "inputs": [ + { + "name": "vault", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newLI", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "LiquidationThresholdUpdated", + "inputs": [ + { + "name": "vault", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newLT", + "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": "OracleUpdated", + "inputs": [ + { + "name": "oldOracle", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newOracle", + "type": "address", + "indexed": true, + "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": "ProtocolShareReserveUpdated", + "inputs": [ + { + "name": "oldPSR", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newPSR", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "TreasuryUpdated", + "inputs": [ + { + "name": "oldTreasury", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newTreasury", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "VaultCreated", + "inputs": [ + { + "name": "vault", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "institution", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "VaultImplementationUpdated", + "inputs": [ + { + "name": "oldImpl", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newImpl", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "error", + "name": "InvalidAddress", + "inputs": [] + }, + { + "type": "error", + "name": "InvalidConfig", + "inputs": [] + }, + { + "type": "error", + "name": "InvalidLatePenaltyRate", + "inputs": [] + }, + { + "type": "error", + "name": "InvalidLiquidationIncentive", + "inputs": [] + }, + { + "type": "error", + "name": "InvalidLiquidationThreshold", + "inputs": [] + }, + { + "type": "error", + "name": "OwnershipCannotBeRenounced", + "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": "VaultNotRegistered", + "inputs": [] + } +] diff --git a/simulations/vip-999/abi/ResilientOracle.json b/simulations/vip-999/abi/ResilientOracle.json new file mode 100644 index 000000000..927d27526 --- /dev/null +++ b/simulations/vip-999/abi/ResilientOracle.json @@ -0,0 +1,655 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "nativeMarketAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "vaiAddress", + "type": "address" + }, + { + "internalType": "contract BoundValidatorInterface", + "name": "_boundValidator", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "calledContract", + "type": "address" + }, + { + "internalType": "string", + "name": "methodSignature", + "type": "string" + } + ], + "name": "Unauthorized", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "oldAccessControlManager", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAccessControlManager", + "type": "address" + } + ], + "name": "NewAccessControlManager", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "role", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bool", + "name": "enable", + "type": "bool" + } + ], + "name": "OracleEnabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "oracle", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "role", + "type": "uint256" + } + ], + "name": "OracleSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "mainOracle", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "pivotOracle", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "fallbackOracle", + "type": "address" + } + ], + "name": "TokenConfigAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "inputs": [], + "name": "INVALID_PRICE", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "NATIVE_TOKEN_ADDR", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "accessControlManager", + "outputs": [ + { + "internalType": "contract IAccessControlManagerV8", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "boundValidator", + "outputs": [ + { + "internalType": "contract BoundValidatorInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "internalType": "enum ResilientOracle.OracleRole", + "name": "role", + "type": "uint8" + }, + { + "internalType": "bool", + "name": "enable", + "type": "bool" + } + ], + "name": "enableOracle", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "internalType": "enum ResilientOracle.OracleRole", + "name": "role", + "type": "uint8" + } + ], + "name": "getOracle", + "outputs": [ + { + "internalType": "address", + "name": "oracle", + "type": "address" + }, + { + "internalType": "bool", + "name": "enabled", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + } + ], + "name": "getPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + } + ], + "name": "getTokenConfig", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "internalType": "address[3]", + "name": "oracles", + "type": "address[3]" + }, + { + "internalType": "bool[3]", + "name": "enableFlagsForOracles", + "type": "bool[3]" + }, + { + "internalType": "bool", + "name": "cachingEnabled", + "type": "bool" + } + ], + "internalType": "struct ResilientOracle.TokenConfig", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + } + ], + "name": "getUnderlyingPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "accessControlManager_", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "nativeMarket", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "accessControlManager_", + "type": "address" + } + ], + "name": "setAccessControlManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "internalType": "address", + "name": "oracle", + "type": "address" + }, + { + "internalType": "enum ResilientOracle.OracleRole", + "name": "role", + "type": "uint8" + } + ], + "name": "setOracle", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "internalType": "address[3]", + "name": "oracles", + "type": "address[3]" + }, + { + "internalType": "bool[3]", + "name": "enableFlagsForOracles", + "type": "bool[3]" + }, + { + "internalType": "bool", + "name": "cachingEnabled", + "type": "bool" + } + ], + "internalType": "struct ResilientOracle.TokenConfig", + "name": "tokenConfig", + "type": "tuple" + } + ], + "name": "setTokenConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "internalType": "address[3]", + "name": "oracles", + "type": "address[3]" + }, + { + "internalType": "bool[3]", + "name": "enableFlagsForOracles", + "type": "bool[3]" + }, + { + "internalType": "bool", + "name": "cachingEnabled", + "type": "bool" + } + ], + "internalType": "struct ResilientOracle.TokenConfig[]", + "name": "tokenConfigs_", + "type": "tuple[]" + } + ], + "name": "setTokenConfigs", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + } + ], + "name": "updateAssetPrice", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + } + ], + "name": "updatePrice", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "vai", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/simulations/vip-999/abi/VenusERC20.json b/simulations/vip-999/abi/VenusERC20.json new file mode 100644 index 000000000..174f57a0f --- /dev/null +++ b/simulations/vip-999/abi/VenusERC20.json @@ -0,0 +1,480 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "name_", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol_", + "type": "string" + }, + { + "internalType": "uint8", + "name": "decimals_", + "type": "uint8" + }, + { + "internalType": "address", + "name": "accessControlManager_", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "Unauthorized", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroAddressNotAllowed", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldAccessControlManager", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newAccessControlManager", + "type": "address" + } + ], + "name": "NewAccessControlManager", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "accessControlManager", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account_", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount_", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account_", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount_", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newAccessControlManager_", + "type": "address" + } + ], + "name": "setAccessControlManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/simulations/vip-999/bscmainnet.ts b/simulations/vip-999/bscmainnet.ts new file mode 100644 index 000000000..7e24fe516 --- /dev/null +++ b/simulations/vip-999/bscmainnet.ts @@ -0,0 +1,426 @@ +import { time } from "@nomicfoundation/hardhat-network-helpers"; +import { expect } from "chai"; +import { BigNumber, Contract } from "ethers"; +import { parseUnits } from "ethers/lib/utils"; +import { ethers } from "hardhat"; +import { NETWORK_ADDRESSES } from "src/networkAddresses"; +import { initMainnetUser, setMaxStalePeriodForAllAssets } from "src/utils"; +import { forking, pretendExecutingVip, testVip } from "src/vip-framework"; + +import vip640 from "../../vips/vip-640/bscmainnet"; +import vip999, { + ATLAS_ORACLE, + BOUND_VALIDATOR, + BTCB, + BTCB_LOWER_BOUND, + BTCB_ORACLE_CONFIGS, + BTCB_UPPER_BOUND, + CHAINLINK_ORACLE, + FIXED_RATE_VAULT_CONTROLLER, + INITIAL_SUPPLY_RECIPIENT, + INSTITUTION_NAME, + INSTITUTION_OPERATOR, + REDSTONE_ORACLE, + SUPPLY_ASSET, + VAULT_SHARE_NAME, + VAULT_SHARE_SYMBOL, + VCEBTC, + VCEBTC_INITIAL_SUPPLY, + instConfig, + riskConfig, + vaultConfig, +} from "../../vips/vip-999/bscmainnet"; +import ACM_ABI from "./abi/AccessControlManager.json"; +import BOUND_VALIDATOR_ABI from "./abi/BoundValidator.json"; +import CHAINLINK_ORACLE_ABI from "./abi/ChainlinkOracle.json"; +import VAULT_ABI from "./abi/InstitutionalLoanVault.json"; +import CONTROLLER_ABI from "./abi/InstitutionalVaultController.json"; +import ORACLE_ABI from "./abi/ResilientOracle.json"; +import ERC20_ABI from "./abi/VenusERC20.json"; + +const { bscmainnet } = NETWORK_ADDRESSES; + +const FORK_BLOCK = 108402150; + +const USDT_WHALE = "0xF977814e90dA44bFA03b6295A0616a897441aceC"; // Binance Hot Wallet +const LENDER = "0x2222222222222222222222222222222222222222"; // dummy test lender + +// Minimal price-oracle stub that always returns 1e18 — swapped in so getPrice-probing calls +// succeed against feeds that are stale on the fork or not configured yet. +// Source (compiled with solc 0.8.25, optimizer off): +// contract StubOracle { +// function getPrice(address) external pure returns (uint256) { return 1e18; } +// } +const STUB_ORACLE_BYTECODE = + "0x6080604052348015600e575f80fd5b5061015e8061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610029575f3560e01c806341976e091461002d575b5f80fd5b610047600480360381019061004291906100cc565b61005d565b604051610054919061010f565b60405180910390f35b5f670de0b6b3a76400009050919050565b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61009b82610072565b9050919050565b6100ab81610091565b81146100b5575f80fd5b50565b5f813590506100c6816100a2565b92915050565b5f602082840312156100e1576100e061006e565b5b5f6100ee848285016100b8565b91505092915050565b5f819050919050565b610109816100f7565b82525050565b5f6020820190506101225f830184610100565b9291505056fea26469706673582212209282f7f2d85233912d0088d6dc45ce2459097d2866597e41f0a286059758c12c64736f6c63430008190033"; + +const deployStubOracle = async (): Promise => { + const [deployer] = await ethers.getSigners(); + const factory = new ethers.ContractFactory( + ["function getPrice(address) external pure returns (uint256)"], + STUB_ORACLE_BYTECODE, + deployer, + ); + const stubOracle = await factory.deploy(); + await stubOracle.deployed(); + return stubOracle; +}; + +// IVaultTypes.VaultState +const VaultState = { + WaitingForMargin: 0, + MarginDeposited: 1, + Fundraising: 2, + Lock: 4, + PendingSettlement: 5, + Matured: 7, +}; + +forking(FORK_BLOCK, async () => { + let oracle: Contract; + let acm: Contract; + let controller: Contract; + let vceBTC: Contract; + let btcb: Contract; + let usdt: Contract; + let timelock: any; + let vaultsBefore: any; + let btcbPriceAtFork: any; + let originalControllerOracle: string; + const vceBtcConfigsAfterVip: Record = {}; + + before(async () => { + oracle = await ethers.getContractAt(ORACLE_ABI, bscmainnet.RESILIENT_ORACLE); + acm = await ethers.getContractAt(ACM_ABI, bscmainnet.ACCESS_CONTROL_MANAGER); + controller = await ethers.getContractAt(CONTROLLER_ABI, FIXED_RATE_VAULT_CONTROLLER); + btcb = await ethers.getContractAt(ERC20_ABI, BTCB); + usdt = await ethers.getContractAt(ERC20_ABI, SUPPLY_ASSET); + timelock = await initMainnetUser(bscmainnet.NORMAL_TIMELOCK, parseUnits("40")); + vaultsBefore = await controller.allVaultsLength(); + + // Capture the BTCB price while the fork-block feed data is still fresh + btcbPriceAtFork = await oracle.getPrice(BTCB); + + // Reproduce the prerequisite controller/vault-upgrade VIP (VIP-640), which upgrades the + // controller/vault implementations and re-grants the 6-arg createVault permission. + await pretendExecutingVip(await vip640(), bscmainnet.NORMAL_TIMELOCK); + + vceBTC = await ethers.getContractAt(ERC20_ABI, VCEBTC); + }); + + describe("Pre-VIP behavior", () => { + it("vceBTC is deployed with correct ACM, name, symbol, and decimals", async () => { + expect(await vceBTC.accessControlManager()).to.equal(bscmainnet.ACCESS_CONTROL_MANAGER); + expect(await vceBTC.name()).to.equal("Ceffu Custody BTC for Venus"); + expect(await vceBTC.symbol()).to.equal("vceBTC"); + expect(await vceBTC.decimals()).to.equal(18); + expect(await vceBTC.totalSupply()).to.equal(0); + }); + + it("ownership has been offered to the Normal Timelock but not yet accepted", async () => { + expect(await vceBTC.pendingOwner()).to.equal(bscmainnet.NORMAL_TIMELOCK); + expect(await vceBTC.owner()).to.not.equal(bscmainnet.NORMAL_TIMELOCK); + }); + + it("vceBTC has no oracle config yet (getPrice reverts)", async () => { + await expect(oracle.getPrice(VCEBTC)).to.be.reverted; + }); + + it("Timelock and Guardian cannot yet mint/burn vceBTC", async () => { + const vceBtcAsCaller = await initMainnetUser(VCEBTC, parseUnits("1")); + expect( + await acm.connect(vceBtcAsCaller).isAllowedToCall(bscmainnet.NORMAL_TIMELOCK, "mint(address,uint256)"), + ).to.equal(false); + expect( + await acm.connect(vceBtcAsCaller).isAllowedToCall(bscmainnet.CRITICAL_GUARDIAN, "mint(address,uint256)"), + ).to.equal(false); + expect( + await acm.connect(vceBtcAsCaller).isAllowedToCall(bscmainnet.NORMAL_TIMELOCK, "burn(address,uint256)"), + ).to.equal(false); + expect( + await acm.connect(vceBtcAsCaller).isAllowedToCall(bscmainnet.CRITICAL_GUARDIAN, "burn(address,uint256)"), + ).to.equal(false); + }); + + it("BTCB's live sub-oracle configs match the feeds and stale periods the VIP clones", async () => { + for (const { name, address, feed, maxStalePeriod } of BTCB_ORACLE_CONFIGS) { + const subOracle = await ethers.getContractAt(CHAINLINK_ORACLE_ABI, address); + const config = await subOracle.tokenConfigs(BTCB); + expect(config.feed, name).to.equal(feed); + expect(config.maxStalePeriod, name).to.equal(maxStalePeriod); + } + }); + + // createVault probes getPrice() for both vault assets, but on the fork the feeds are stale by + // execution time (testVip advances days) and vceBTC's config doesn't exist until the VIP runs. + // Swap the controller's oracle for an always-1e18 stub; callbackAfterExecution restores it. + it("[Test-Only] swaps the controller's oracle for a stub so createVault's price probe passes during execution", async () => { + originalControllerOracle = await controller.oracle(); + expect(originalControllerOracle).to.equal(bscmainnet.RESILIENT_ORACLE); + const stubOracle = await deployStubOracle(); + await controller.connect(timelock).setOracle(stubOracle.address); + expect(await controller.oracle()).to.equal(stubOracle.address); + }); + }); + + testVip("VIP-999 Create Ceffu Custody BTC Fixed Rate Vault", await vip999(), { + callbackAfterExecution: async () => { + // createVault executed against the stub oracle set in the last pre-VIP step; restore the + // original oracle so everything below runs against production wiring. + await controller.connect(timelock).setOracle(originalControllerOracle); + // Snapshot the VIP-set vceBTC sub-oracle configs before the stale-period bump rewrites them. + for (const { name, address } of BTCB_ORACLE_CONFIGS) { + const subOracle = await ethers.getContractAt(CHAINLINK_ORACLE_ABI, address); + vceBtcConfigsAfterVip[name] = await subOracle.tokenConfigs(VCEBTC); + } + // Fork feed data is frozen at the fork block while testVip advanced simulated time past the + // real stale periods: bump them for every priced asset (vceBTC's config only exists now). + await setMaxStalePeriodForAllAssets(oracle, [btcb, usdt, vceBTC]); + }, + }); + + describe("Post-VIP behavior", () => { + it("vceBTC is priced close to BTCB", async () => { + const vceBtcPrice = await oracle.getPrice(VCEBTC); + const tolerance = btcbPriceAtFork.div(100); // 1% tolerance + expect(vceBtcPrice).to.be.closeTo(btcbPriceAtFork, tolerance); + }); + + it("the Normal Timelock accepted ownership of vceBTC", async () => { + expect(await vceBTC.owner()).to.equal(bscmainnet.NORMAL_TIMELOCK); + expect(await vceBTC.pendingOwner()).to.equal(ethers.constants.AddressZero); + }); + + it("Timelock and Guardian can mint/burn vceBTC", async () => { + const vceBtcAsCaller = await initMainnetUser(VCEBTC, parseUnits("1")); + expect( + await acm.connect(vceBtcAsCaller).isAllowedToCall(bscmainnet.NORMAL_TIMELOCK, "mint(address,uint256)"), + ).to.equal(true); + expect( + await acm.connect(vceBtcAsCaller).isAllowedToCall(bscmainnet.CRITICAL_GUARDIAN, "mint(address,uint256)"), + ).to.equal(true); + expect( + await acm.connect(vceBtcAsCaller).isAllowedToCall(bscmainnet.NORMAL_TIMELOCK, "burn(address,uint256)"), + ).to.equal(true); + expect( + await acm.connect(vceBtcAsCaller).isAllowedToCall(bscmainnet.CRITICAL_GUARDIAN, "burn(address,uint256)"), + ).to.equal(true); + }); + + it("the Normal Timelock can mint and burn vceBTC", async () => { + const recipient = "0x000000000000000000000000000000000000dEaD"; + const amount = parseUnits("1", 18); + + const supplyBefore = await vceBTC.totalSupply(); + const balanceBefore = await vceBTC.balanceOf(recipient); + + await vceBTC.connect(timelock).mint(recipient, amount); + expect(await vceBTC.balanceOf(recipient)).to.equal(balanceBefore.add(amount)); + expect(await vceBTC.totalSupply()).to.equal(supplyBefore.add(amount)); + + await vceBTC.connect(timelock).burn(recipient, amount); + expect(await vceBTC.balanceOf(recipient)).to.equal(balanceBefore); + expect(await vceBTC.totalSupply()).to.equal(supplyBefore); + }); + + it("initial vceBTC collateral was minted to the Ceffu multisig", async () => { + expect(await vceBTC.totalSupply()).to.equal(VCEBTC_INITIAL_SUPPLY); + expect(await vceBTC.balanceOf(INITIAL_SUPPLY_RECIPIENT)).to.equal(VCEBTC_INITIAL_SUPPLY); + }); + + it("a Fixed Rate Vault backed by vceBTC was created with the configured parameters and names", async () => { + expect(await controller.allVaultsLength()).to.equal(vaultsBefore.add(1)); + const vaultAddress = await controller.allVaults(vaultsBefore); + expect(await controller.isRegistered(vaultAddress)).to.equal(true); + + const vault = await ethers.getContractAt(VAULT_ABI, vaultAddress); + + // VaultConfig + const config = await vault.config(); + expect(config.supplyAsset).to.equal(vaultConfig[0]); + expect(config.fixedAPY).to.equal(vaultConfig[1]); + expect(config.reserveFactor).to.equal(vaultConfig[2]); + expect(config.minBorrowCap).to.equal(vaultConfig[3]); + expect(config.maxBorrowCap).to.equal(vaultConfig[4]); + expect(config.minSupplierDeposit).to.equal(vaultConfig[5]); + expect(config.openDuration).to.equal(vaultConfig[6]); + expect(config.lockDuration).to.equal(vaultConfig[7]); + expect(config.settlementWindow).to.equal(vaultConfig[8]); + + // InstitutionalConfig (positionTokenId is assigned by the controller) + const inst = await vault.institutionalConfig(); + expect(inst.collateralAsset).to.equal(instConfig[0]); + expect(inst.idealCollateralAmount).to.equal(instConfig[1]); + expect(inst.marginRate).to.equal(instConfig[2]); + expect(inst.institutionOperator).to.equal(instConfig[3]); + expect(inst.positionTokenId).to.be.gt(0); + + // RiskConfig + const risk = await vault.riskConfig(); + expect(risk.liquidationThreshold).to.equal(riskConfig[0]); + expect(risk.liquidationIncentive).to.equal(riskConfig[1]); + expect(risk.latePenaltyRate).to.equal(riskConfig[2]); + + // Share token names + institution name + expect(await vault.name()).to.equal(VAULT_SHARE_NAME); + expect(await vault.symbol()).to.equal(VAULT_SHARE_SYMBOL); + expect(await vault.institutionName()).to.equal(INSTITUTION_NAME); + }); + }); + + describe("Oracle configuration for BTCB and vceBTC", () => { + it("BTCB price is available from ResilientOracle", async () => { + const btcbPrice = await oracle.getPrice(BTCB); + expect(btcbPrice).to.be.gt(0); + }); + + it("vceBTC price is available from ResilientOracle", async () => { + const vceBtcPrice = await oracle.getPrice(VCEBTC); + expect(vceBtcPrice).to.be.gt(0); + }); + + it("vceBTC price matches BTCB price configuration", async () => { + const btcbPrice = await oracle.getPrice(BTCB); + const vceBtcPrice = await oracle.getPrice(VCEBTC); + expect(vceBtcPrice).to.equal(btcbPrice); + }); + + // Together with the pre-VIP check that BTCB's live configs match BTCB_ORACLE_CONFIGS, this proves + // vceBTC's configs are exact clones of BTCB's. + for (const { name, feed, maxStalePeriod } of BTCB_ORACLE_CONFIGS) { + it(`vceBTC ${name} config clones BTCB's feed and stale period`, async () => { + const vceBtcConfig = vceBtcConfigsAfterVip[name]; + expect(vceBtcConfig, `${name} snapshot missing — callbackAfterExecution did not run`).to.not.be.undefined; + expect(vceBtcConfig.asset).to.equal(VCEBTC); + expect(vceBtcConfig.feed).to.equal(feed); + expect(vceBtcConfig.maxStalePeriod).to.equal(maxStalePeriod); + }); + } + + it("vceBTC ResilientOracle config mirrors BTCB's (same sub-oracles, all enabled)", async () => { + const btcbConfig = await oracle.getTokenConfig(BTCB); + const vceBtcConfig = await oracle.getTokenConfig(VCEBTC); + expect(vceBtcConfig.asset).to.equal(VCEBTC); + expect(vceBtcConfig.oracles).to.deep.equal([CHAINLINK_ORACLE, REDSTONE_ORACLE, ATLAS_ORACLE]); + expect(vceBtcConfig.oracles).to.deep.equal(btcbConfig.oracles); + expect(vceBtcConfig.enableFlagsForOracles).to.deep.equal(btcbConfig.enableFlagsForOracles); + expect(vceBtcConfig.cachingEnabled).to.equal(btcbConfig.cachingEnabled); + expect(vceBtcConfig.cachingEnabled).to.equal(false); + }); + + it("BTCB bounds are configured correctly in BoundValidator", async () => { + const boundValidator = await ethers.getContractAt(BOUND_VALIDATOR_ABI, BOUND_VALIDATOR); + const btcbBounds = await boundValidator.validateConfigs(BTCB); + expect(btcbBounds.asset).to.equal(BTCB); + expect(btcbBounds.upperBoundRatio).to.equal(BTCB_UPPER_BOUND); + expect(btcbBounds.lowerBoundRatio).to.equal(BTCB_LOWER_BOUND); + }); + + it("vceBTC bounds are configured correctly in BoundValidator (cloned from BTCB)", async () => { + const boundValidator = await ethers.getContractAt(BOUND_VALIDATOR_ABI, BOUND_VALIDATOR); + const vceBtcBounds = await boundValidator.validateConfigs(VCEBTC); + expect(vceBtcBounds.asset).to.equal(VCEBTC); + expect(vceBtcBounds.upperBoundRatio).to.equal(BTCB_UPPER_BOUND); + expect(vceBtcBounds.lowerBoundRatio).to.equal(BTCB_LOWER_BOUND); + }); + }); + + describe("Vault lifecycle", () => { + let vault: Contract; + let institution: any; + let lender: any; + + const idealCollateralAmount = BigNumber.from(instConfig[1]); + const marginRate = BigNumber.from(instConfig[2]); + const minBorrowCap = BigNumber.from(vaultConfig[3]); + const maxBorrowCap = BigNumber.from(vaultConfig[4]); + const openDuration = Number(vaultConfig[6]); + const lockDuration = Number(vaultConfig[7]); + const marginAmount = idealCollateralAmount.mul(marginRate).div(parseUnits("1", 18)); + const lenderDepositAmount = minBorrowCap.add(maxBorrowCap).div(2); + + before(async () => { + const vaultAddress = await controller.allVaults(vaultsBefore); + vault = await ethers.getContractAt(VAULT_ABI, vaultAddress); + + institution = await initMainnetUser(INSTITUTION_OPERATOR, parseUnits("1")); + lender = await initMainnetUser(LENDER, parseUnits("1")); + + // Fund the institution with vceBTC collateral and both parties with the supply asset. + await vceBTC.connect(timelock).mint(INSTITUTION_OPERATOR, idealCollateralAmount); + const whale = await initMainnetUser(USDT_WHALE, parseUnits("1")); + await usdt.connect(whale).transfer(LENDER, lenderDepositAmount); + await usdt.connect(whale).transfer(INSTITUTION_OPERATOR, lenderDepositAmount.div(5)); // to repay principal + interest + }); + + it("institution deposits margin collateral (WaitingForMargin -> MarginDeposited)", async () => { + expect(await vault.state()).to.equal(VaultState.WaitingForMargin); + await vceBTC.connect(institution).approve(vault.address, idealCollateralAmount); + await vault.connect(institution).depositCollateral(marginAmount); + expect(await vault.state()).to.equal(VaultState.MarginDeposited); + }); + + it("controller opens the vault (MarginDeposited -> Fundraising)", async () => { + await controller.connect(timelock).openVault(vault.address); + expect(await vault.state()).to.equal(VaultState.Fundraising); + }); + + it("institution tops up collateral to the full ideal amount", async () => { + await vault.connect(institution).depositCollateral(idealCollateralAmount.sub(marginAmount)); + expect(await vceBTC.balanceOf(vault.address)).to.equal(idealCollateralAmount); + }); + + it("a lender deposits the supply asset", async () => { + await usdt.connect(lender).approve(vault.address, lenderDepositAmount); + await vault.connect(lender).deposit(lenderDepositAmount, LENDER); + expect(await vault.balanceOf(LENDER)).to.be.gt(0); + }); + + it("the open window elapses and the vault locks (Fundraising -> Lock)", async () => { + await time.increase(openDuration + 1); + await vault.updateVaultState(); + expect(await vault.state()).to.equal(VaultState.Lock); + }); + + it("the institution claims the raised funds", async () => { + const usdtBefore = await usdt.balanceOf(INSTITUTION_OPERATOR); + await vault.connect(institution).claimRaisedFunds(); + expect(await usdt.balanceOf(INSTITUTION_OPERATOR)).to.equal(usdtBefore.add(lenderDepositAmount)); + expect(await vault.outstandingDebt()).to.be.gt(lenderDepositAmount); // principal + accrued interest + }); + + it("the institution repays in full after the lock period (Lock -> PendingSettlement -> Matured)", async () => { + await time.increase(lockDuration + 1); + await vault.updateVaultState(); + expect(await vault.state()).to.equal(VaultState.PendingSettlement); + + const owed = await vault.outstandingDebt(); + await usdt.connect(institution).approve(vault.address, owed); + await vault.connect(institution).repay(owed); + await vault.updateVaultState(); + expect(await vault.state()).to.equal(VaultState.Matured); + expect(await vault.outstandingDebt()).to.equal(0); + }); + + it("the lender redeems principal plus interest", async () => { + const shares = await vault.balanceOf(LENDER); + const expectedAssets = await vault.previewRedeem(shares); + expect(expectedAssets).to.be.gt(lenderDepositAmount); // net of the reserve-factor cut + + const usdtBefore = await usdt.balanceOf(LENDER); + await vault.connect(lender).redeem(shares, LENDER, LENDER); + expect(await usdt.balanceOf(LENDER)).to.equal(usdtBefore.add(expectedAssets)); + expect(await vault.balanceOf(LENDER)).to.equal(0); + }); + + it("the institution withdraws its collateral", async () => { + const collateral = await vceBTC.balanceOf(vault.address); + expect(collateral).to.equal(idealCollateralAmount); + + const before = await vceBTC.balanceOf(INSTITUTION_OPERATOR); + await vault.connect(institution).withdrawCollateral(collateral); + expect(await vceBTC.balanceOf(INSTITUTION_OPERATOR)).to.equal(before.add(collateral)); + expect(await vceBTC.balanceOf(vault.address)).to.equal(0); + }); + }); +}); diff --git a/simulations/vip-999/bsctestnet.ts b/simulations/vip-999/bsctestnet.ts new file mode 100644 index 000000000..8131700d6 --- /dev/null +++ b/simulations/vip-999/bsctestnet.ts @@ -0,0 +1,292 @@ +import { time } from "@nomicfoundation/hardhat-network-helpers"; +import { expect } from "chai"; +import { BigNumber, Contract } from "ethers"; +import { parseUnits } from "ethers/lib/utils"; +import { ethers } from "hardhat"; +import { NETWORK_ADDRESSES } from "src/networkAddresses"; +import { initMainnetUser } from "src/utils"; +import { forking, testVip } from "src/vip-framework"; + +import vip999, { + CHAINLINK_ORACLE, + FIXED_RATE_VAULT_CONTROLLER, + INSTITUTION_NAME, + INSTITUTION_OPERATOR, + SUPPLY_ASSET, + VAULT_SHARE_NAME, + VAULT_SHARE_SYMBOL, + VCEBTC, + VCEBTC_DIRECT_PRICE, + VCEBTC_INITIAL_SUPPLY, + instConfig, + riskConfig, + vaultConfig, +} from "../../vips/vip-999/bsctestnet"; +import ACM_ABI from "./abi/AccessControlManager.json"; +import VAULT_ABI from "./abi/InstitutionalLoanVault.json"; +import CONTROLLER_ABI from "./abi/InstitutionalVaultController.json"; +import ORACLE_ABI from "./abi/ResilientOracle.json"; +import ERC20_ABI from "./abi/VenusERC20.json"; + +const { bsctestnet } = NETWORK_ADDRESSES; + +const FORK_BLOCK = 117573000; + +const USDT_FAUCET_ABI = ["function allocateTo(address to, uint256 amount) external"]; +const CHAINLINK_ORACLE_GETPRICE_ABI = ["function getPrice(address) external view returns (uint256)"]; + +// IVaultTypes.VaultState +const VaultState = { + WaitingForMargin: 0, + MarginDeposited: 1, + Fundraising: 2, + Lock: 4, + PendingSettlement: 5, + Matured: 7, +}; + +forking(FORK_BLOCK, async () => { + let oracle: Contract; + let acm: Contract; + let controller: Contract; + let vceBTC: Contract; + let usdt: Contract; + let timelock: any; + let vaultsBefore: any; + + before(async () => { + oracle = await ethers.getContractAt(ORACLE_ABI, bsctestnet.RESILIENT_ORACLE); + acm = await ethers.getContractAt(ACM_ABI, bsctestnet.ACCESS_CONTROL_MANAGER); + controller = await ethers.getContractAt(CONTROLLER_ABI, FIXED_RATE_VAULT_CONTROLLER); + usdt = await ethers.getContractAt(ERC20_ABI, SUPPLY_ASSET); + timelock = await initMainnetUser(bsctestnet.NORMAL_TIMELOCK, parseUnits("40")); + vaultsBefore = await controller.allVaultsLength(); + vceBTC = await ethers.getContractAt(ERC20_ABI, VCEBTC); + }); + + describe("Pre-VIP behavior", () => { + it("vceBTC is deployed with correct ACM, name, symbol, and decimals", async () => { + expect(await vceBTC.accessControlManager()).to.equal(bsctestnet.ACCESS_CONTROL_MANAGER); + expect(await vceBTC.name()).to.equal("Ceffu Custody BTC for Venus"); + expect(await vceBTC.symbol()).to.equal("vceBTC"); + expect(await vceBTC.decimals()).to.equal(18); + expect(await vceBTC.totalSupply()).to.equal(0); + }); + + it("ownership has been offered to the Normal Timelock but not yet accepted", async () => { + expect(await vceBTC.pendingOwner()).to.equal(bsctestnet.NORMAL_TIMELOCK); + expect(await vceBTC.owner()).to.not.equal(bsctestnet.NORMAL_TIMELOCK); + }); + + it("vceBTC has no oracle config yet (getPrice reverts)", async () => { + await expect(oracle.getPrice(VCEBTC)).to.be.reverted; + }); + + it("Timelock and Guardian cannot yet mint/burn vceBTC", async () => { + const vceBtcAsCaller = await initMainnetUser(VCEBTC, parseUnits("1")); + expect( + await acm.connect(vceBtcAsCaller).isAllowedToCall(bsctestnet.NORMAL_TIMELOCK, "mint(address,uint256)"), + ).to.equal(false); + expect(await acm.connect(vceBtcAsCaller).isAllowedToCall(bsctestnet.GUARDIAN, "mint(address,uint256)")).to.equal( + false, + ); + }); + }); + + testVip("VIP-999 Create Ceffu Custody BTC Fixed Rate Vault (Testnet)", await vip999()); + + describe("Post-VIP behavior", () => { + it("vceBTC is priced at the fixed direct price set on the Chainlink sub-oracle", async () => { + const chainlink = await ethers.getContractAt(CHAINLINK_ORACLE_GETPRICE_ABI, CHAINLINK_ORACLE); + const vceBtcPrice = await oracle.getPrice(VCEBTC); + expect(vceBtcPrice).to.equal(VCEBTC_DIRECT_PRICE); + // ResilientOracle proxies to the Chainlink main sub-oracle, which returns the direct price. + expect(vceBtcPrice).to.equal(await chainlink.getPrice(VCEBTC)); + }); + + it("the Normal Timelock accepted ownership of vceBTC", async () => { + expect(await vceBTC.owner()).to.equal(bsctestnet.NORMAL_TIMELOCK); + expect(await vceBTC.pendingOwner()).to.equal(ethers.constants.AddressZero); + }); + + it("Timelock and Guardian can mint/burn vceBTC", async () => { + const vceBtcAsCaller = await initMainnetUser(VCEBTC, parseUnits("1")); + expect( + await acm.connect(vceBtcAsCaller).isAllowedToCall(bsctestnet.NORMAL_TIMELOCK, "mint(address,uint256)"), + ).to.equal(true); + expect(await acm.connect(vceBtcAsCaller).isAllowedToCall(bsctestnet.GUARDIAN, "mint(address,uint256)")).to.equal( + true, + ); + expect( + await acm.connect(vceBtcAsCaller).isAllowedToCall(bsctestnet.NORMAL_TIMELOCK, "burn(address,uint256)"), + ).to.equal(true); + expect(await acm.connect(vceBtcAsCaller).isAllowedToCall(bsctestnet.GUARDIAN, "burn(address,uint256)")).to.equal( + true, + ); + }); + + it("the Normal Timelock can mint and burn vceBTC", async () => { + const recipient = "0x000000000000000000000000000000000000dEaD"; + const amount = parseUnits("1", 18); + + const supplyBefore = await vceBTC.totalSupply(); + const balanceBefore = await vceBTC.balanceOf(recipient); + + await vceBTC.connect(timelock).mint(recipient, amount); + expect(await vceBTC.balanceOf(recipient)).to.equal(balanceBefore.add(amount)); + expect(await vceBTC.totalSupply()).to.equal(supplyBefore.add(amount)); + + await vceBTC.connect(timelock).burn(recipient, amount); + expect(await vceBTC.balanceOf(recipient)).to.equal(balanceBefore); + expect(await vceBTC.totalSupply()).to.equal(supplyBefore); + }); + + it("initial vceBTC collateral was minted to the Guardian", async () => { + expect(await vceBTC.totalSupply()).to.equal(VCEBTC_INITIAL_SUPPLY); + expect(await vceBTC.balanceOf(bsctestnet.GUARDIAN)).to.equal(VCEBTC_INITIAL_SUPPLY); + }); + + it("a Fixed Rate Vault backed by vceBTC was created with the configured parameters and names", async () => { + expect(await controller.allVaultsLength()).to.equal(vaultsBefore.add(1)); + const vaultAddress = await controller.allVaults(vaultsBefore); + expect(await controller.isRegistered(vaultAddress)).to.equal(true); + + const vault = await ethers.getContractAt(VAULT_ABI, vaultAddress); + + // VaultConfig + const config = await vault.config(); + expect(config.supplyAsset).to.equal(vaultConfig[0]); + expect(config.fixedAPY).to.equal(vaultConfig[1]); + expect(config.reserveFactor).to.equal(vaultConfig[2]); + expect(config.minBorrowCap).to.equal(vaultConfig[3]); + expect(config.maxBorrowCap).to.equal(vaultConfig[4]); + expect(config.minSupplierDeposit).to.equal(vaultConfig[5]); + expect(config.openDuration).to.equal(vaultConfig[6]); + expect(config.lockDuration).to.equal(vaultConfig[7]); + expect(config.settlementWindow).to.equal(vaultConfig[8]); + + // InstitutionalConfig (positionTokenId is assigned by the controller) + const inst = await vault.institutionalConfig(); + expect(inst.collateralAsset).to.equal(instConfig[0]); + expect(inst.idealCollateralAmount).to.equal(instConfig[1]); + expect(inst.marginRate).to.equal(instConfig[2]); + expect(inst.institutionOperator).to.equal(instConfig[3]); + expect(inst.positionTokenId).to.be.gt(0); + + // RiskConfig + const risk = await vault.riskConfig(); + expect(risk.liquidationThreshold).to.equal(riskConfig[0]); + expect(risk.liquidationIncentive).to.equal(riskConfig[1]); + expect(risk.latePenaltyRate).to.equal(riskConfig[2]); + + // Share token names + institution name + expect(await vault.name()).to.equal(VAULT_SHARE_NAME); + expect(await vault.symbol()).to.equal(VAULT_SHARE_SYMBOL); + expect(await vault.institutionName()).to.equal(INSTITUTION_NAME); + }); + }); + + describe("Vault lifecycle", () => { + let vault: Contract; + let institution: any; + let lender: any; + let LENDER: string; + + const idealCollateralAmount = BigNumber.from(instConfig[1]); + const marginRate = BigNumber.from(instConfig[2]); + const minBorrowCap = BigNumber.from(vaultConfig[3]); + const maxBorrowCap = BigNumber.from(vaultConfig[4]); + const openDuration = Number(vaultConfig[6]); + const lockDuration = Number(vaultConfig[7]); + const marginAmount = idealCollateralAmount.mul(marginRate).div(parseUnits("1", 18)); + const lenderDepositAmount = minBorrowCap.add(maxBorrowCap).div(2); + + before(async () => { + const vaultAddress = await controller.allVaults(vaultsBefore); + vault = await ethers.getContractAt(VAULT_ABI, vaultAddress); + + institution = await initMainnetUser(INSTITUTION_OPERATOR, parseUnits("1")); + + // Lender is a pre-funded Hardhat signer (account #1), so no impersonation is needed. + const [deployer, lenderSigner] = await ethers.getSigners(); + lender = lenderSigner; + LENDER = await lenderSigner.getAddress(); + + // Fund the institution with vceBTC collateral and both parties with the supply asset (USDT). + await vceBTC.connect(timelock).mint(INSTITUTION_OPERATOR, idealCollateralAmount); + const usdtFaucet = await ethers.getContractAt(USDT_FAUCET_ABI, SUPPLY_ASSET); + await usdtFaucet.connect(deployer).allocateTo(LENDER, lenderDepositAmount); + await usdtFaucet.connect(deployer).allocateTo(INSTITUTION_OPERATOR, lenderDepositAmount.div(5)); // buffer to cover interest on repay (principal comes from claimed funds) + }); + + it("institution deposits margin collateral (WaitingForMargin -> MarginDeposited)", async () => { + expect(await vault.state()).to.equal(VaultState.WaitingForMargin); + await vceBTC.connect(institution).approve(vault.address, idealCollateralAmount); + await vault.connect(institution).depositCollateral(marginAmount); + expect(await vault.state()).to.equal(VaultState.MarginDeposited); + }); + + it("controller opens the vault (MarginDeposited -> Fundraising)", async () => { + await controller.connect(timelock).openVault(vault.address); + expect(await vault.state()).to.equal(VaultState.Fundraising); + }); + + it("institution tops up collateral to the full ideal amount", async () => { + await vault.connect(institution).depositCollateral(idealCollateralAmount.sub(marginAmount)); + expect(await vceBTC.balanceOf(vault.address)).to.equal(idealCollateralAmount); + }); + + it("a lender deposits the supply asset", async () => { + await usdt.connect(lender).approve(vault.address, lenderDepositAmount); + await vault.connect(lender).deposit(lenderDepositAmount, LENDER); + expect(await vault.balanceOf(LENDER)).to.be.gt(0); + }); + + it("the open window elapses and the vault locks (Fundraising -> Lock)", async () => { + await time.increase(openDuration + 1); + await vault.updateVaultState(); + expect(await vault.state()).to.equal(VaultState.Lock); + }); + + it("the institution claims the raised funds", async () => { + const usdtBefore = await usdt.balanceOf(INSTITUTION_OPERATOR); + await vault.connect(institution).claimRaisedFunds(); + expect(await usdt.balanceOf(INSTITUTION_OPERATOR)).to.equal(usdtBefore.add(lenderDepositAmount)); + expect(await vault.outstandingDebt()).to.be.gt(lenderDepositAmount); // principal + accrued interest + }); + + it("the institution repays in full after the lock period (Lock -> PendingSettlement -> Matured)", async () => { + await time.increase(lockDuration + 1); + await vault.updateVaultState(); + expect(await vault.state()).to.equal(VaultState.PendingSettlement); + + const owed = await vault.outstandingDebt(); + await usdt.connect(institution).approve(vault.address, owed); + await vault.connect(institution).repay(owed); + await vault.updateVaultState(); + expect(await vault.state()).to.equal(VaultState.Matured); + expect(await vault.outstandingDebt()).to.equal(0); + }); + + it("the lender redeems principal plus interest", async () => { + const shares = await vault.balanceOf(LENDER); + const expectedAssets = await vault.previewRedeem(shares); + expect(expectedAssets).to.be.gt(lenderDepositAmount); // net of the reserve-factor cut + + const usdtBefore = await usdt.balanceOf(LENDER); + await vault.connect(lender).redeem(shares, LENDER, LENDER); + expect(await usdt.balanceOf(LENDER)).to.equal(usdtBefore.add(expectedAssets)); + expect(await vault.balanceOf(LENDER)).to.equal(0); + }); + + it("the institution withdraws its collateral", async () => { + const collateral = await vceBTC.balanceOf(vault.address); + expect(collateral).to.equal(idealCollateralAmount); + + const before = await vceBTC.balanceOf(INSTITUTION_OPERATOR); + await vault.connect(institution).withdrawCollateral(collateral); + expect(await vceBTC.balanceOf(INSTITUTION_OPERATOR)).to.equal(before.add(collateral)); + expect(await vceBTC.balanceOf(vault.address)).to.equal(0); + }); + }); +}); diff --git a/src/networkAddresses.ts b/src/networkAddresses.ts index c985fd67f..002bf48a2 100644 --- a/src/networkAddresses.ts +++ b/src/networkAddresses.ts @@ -50,6 +50,7 @@ export const NETWORK_ADDRESSES = { BINANCE_ORACLE: oracleBscmainnetContracts.addresses.BinanceOracle, RESILIENT_ORACLE: oracleBscmainnetContracts.addresses.ResilientOracle, REDSTONE_ORACLE: oracleBscmainnetContracts.addresses.RedStoneOracle, + ATLAS_ORACLE: "0x9E6928Ec418948ceb9f1cd9872fD312b13D841D0", }, bsctestnet: { DEFAULT_PROPOSER_ADDRESS: "0x2Ce1d0ffD7E869D9DF33e28552b12DdDed326706", @@ -76,6 +77,7 @@ export const NETWORK_ADDRESSES = { BINANCE_ORACLE: oracleBsctestnetContracts.addresses.BinanceOracle, RESILIENT_ORACLE: oracleBsctestnetContracts.addresses.ResilientOracle, REDSTONE_ORACLE: oracleBsctestnetContracts.addresses.RedStoneOracle, + ATLAS_ORACLE: "0x7F00af2f30a55e79311392C98fBBfA629D19b3A5", }, ethereum: { NORMAL_TIMELOCK: "0xd969E79406c35E80750aAae061D402Aab9325714", diff --git a/src/utils.ts b/src/utils.ts index 84da07f85..3306f0fbe 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -478,6 +478,8 @@ export const setMaxStalePeriod = async ( binance: tryGetForkedNetworkAddress("BINANCE_ORACLE"), chainlink: tryGetForkedNetworkAddress("CHAINLINK_ORACLE"), redstone: tryGetForkedNetworkAddress("REDSTONE_ORACLE"), + // same interface as the Chainlink oracle, different address + atlas: tryGetForkedNetworkAddress("ATLAS_ORACLE"), }; const adminKey = NORMAL_TIMELOCK_NETWORKS.includes(FORKED_NETWORK ?? "") ? "NORMAL_TIMELOCK" : "GUARDIAN"; @@ -495,7 +497,7 @@ export const setMaxStalePeriod = async ( const symbol = underlyingAsset.address.toLowerCase() === ORACLE_BNB.toLowerCase() ? "BNB" : await underlyingAsset.symbol(); await setMaxStalePeriodInBinanceOracle(oracle, symbol, maxStalePeriodInSeconds); - } else if (oracle === knownOracles.chainlink || oracle === knownOracles.redstone) { + } else if (oracle === knownOracles.chainlink || oracle === knownOracles.redstone || oracle === knownOracles.atlas) { await setMaxStalePeriodInChainlinkOracle( oracle, underlyingAsset.address, diff --git a/vips/vip-999/bscmainnet.ts b/vips/vip-999/bscmainnet.ts new file mode 100644 index 000000000..502ffaa2c --- /dev/null +++ b/vips/vip-999/bscmainnet.ts @@ -0,0 +1,194 @@ +import { parseUnits } from "ethers/lib/utils"; +import { NETWORK_ADDRESSES } from "src/networkAddresses"; +import { ProposalType } from "src/types"; +import { makeProposal } from "src/utils"; + +const { bscmainnet } = NETWORK_ADDRESSES; + +// TODO before proposing: +// 1. Execute the controller/vault upgrade VIP (vip-640) first, then remove the +// pretendExecutingVip(vip640) prerequisite from the simulation. +// 2. Set the real Ceffu institution operator address (INSTITUTION_OPERATOR below). +// 3. Redeploy vceBTC from fixed-rate-vaults CustodyReceiptToken (current deployment is the older +// VenusERC20 bytecode without pause/unpause), transfer ownership to the Normal Timelock, +// update VCEBTC, add pause()/unpause() ACM grants, and update the simulations. + +// Deployed vceBTC +export const VCEBTC = "0xba63642A893b0F15aDE730943972824c9e2147a7"; + +export const FIXED_RATE_VAULT_CONTROLLER = "0x6D9e91cB766259af42619c14c994E694E57e6E85"; +export const BTCB = "0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c"; + +export const CHAINLINK_ORACLE = bscmainnet.CHAINLINK_ORACLE; +export const REDSTONE_ORACLE = bscmainnet.REDSTONE_ORACLE; +export const ATLAS_ORACLE = bscmainnet.ATLAS_ORACLE; + +// BTCB's oracle config per sub-oracle — the VIP clones these for vceBTC: (oracle, feed, max stale period) +export const BTCB_ORACLE_CONFIGS = [ + { + name: "Chainlink", + address: CHAINLINK_ORACLE, + feed: "0x8ECF7dE377F788A813F5215668E282556b35f300", + maxStalePeriod: 100, + }, + { + name: "RedStone", + address: REDSTONE_ORACLE, + feed: "0xa51738d1937FFc553d5070f43300B385AA2D9F55", + maxStalePeriod: 100, + }, + { + name: "Atlas", + address: ATLAS_ORACLE, + feed: "0x4f6c53fb9CdD46269d24bCa4E68bB680879132fc", + maxStalePeriod: 120, + }, +]; + +export const BOUND_VALIDATOR = "0x6E332fF0bB52475304494E4AE5063c1051c7d735"; +export const BTCB_UPPER_BOUND = parseUnits("1.01", 18); +export const BTCB_LOWER_BOUND = parseUnits("0.99", 18); + +// USDT +export const SUPPLY_ASSET = "0x55d398326f99059fF775485246999027B3197955"; + +// TODO(2): placeholder — replace with the real Ceffu operator address +export const INSTITUTION_OPERATOR = "0x1111111111111111111111111111111111111111"; + +// Initial vceBTC collateral supply +export const VCEBTC_INITIAL_SUPPLY = parseUnits("21.92", 18); + +// Recipient of the initial vceBTC collateral mint (multisig) +export const INITIAL_SUPPLY_RECIPIENT = "0x5D1507d5Cfb3d031C3209e9FB8e2644e4094Ea01"; + +// Fixed Rate Vault configuration. +// VaultConfig: [supplyAsset, fixedAPY(bps), reserveFactor(1e18), minBorrowCap, +// maxBorrowCap, minSupplierDeposit, openDuration, lockDuration, settlementWindow] +export const vaultConfig = [ + SUPPLY_ASSET, + 600, // fixedAPY = 6% + parseUnits("0.3", 18), // reserveFactor = 30% + parseUnits("10", 18), // minBorrowCap = 10 USDT (must be > 0; createVault reverts if 0) + parseUnits("1000000", 18), // maxBorrowCap = 1M + 0, // minSupplierDeposit + 7 * 24 * 60 * 60, // openDuration = 7 days + 30 * 24 * 60 * 60, // lockDuration = 1 month + 3 * 24 * 60 * 60, // settlementWindow = 3 days +]; + +// InstitutionalConfig: [collateralAsset, idealCollateralAmount, marginRate(1e18), +// institutionOperator, positionTokenId] +export const instConfig = [ + VCEBTC, // collateral = vceBTC + parseUnits("21.92", 18), // idealCollateralAmount = 21.92 BTCB + parseUnits("0.005", 18), // marginRate = 0.5% (must be > 0; createVault reverts InvalidConfig if 0) + INSTITUTION_OPERATOR, + 0, // positionTokenId assigned by the controller +]; + +// RiskConfig: [liquidationThreshold(1e18), liquidationIncentive(1e18), latePenaltyRate(1e18)] +export const riskConfig = [parseUnits("0.9", 18), parseUnits("1.1", 18), parseUnits("1.1", 18)]; + +export const VAULT_SHARE_NAME = "FRV Solv BTCB 24JUL2026 30"; +export const VAULT_SHARE_SYMBOL = "FRV-sv-24JUL2026-30"; +export const INSTITUTION_NAME = "Ceffu"; + +export const vip999 = () => { + const meta = { + version: "v2", + title: "VIP-999 [BNB Chain] Create Ceffu Custody BTC Fixed Rate Vault (DRAFT)", + description: `#### Summary + +This proposal onboards a new custody-mirror collateral token, **vceBTC ("Ceffu Custody BTC for Venus")**, and creates a new Venus **Fixed Rate Vault** (Institutional Loan Vault) that uses vceBTC as collateral. vceBTC is an accounting mirror for BTC held in custody by Ceffu; its supply is controlled by Venus governance and priced identically to BTCB. + +#### Actions + +1. **Oracle** — price vceBTC identically to BTCB by cloning BTCB's full oracle configuration: the Chainlink / RedStone / Atlas sub-oracle feeds and stale periods, the BoundValidator bounds, and the ResilientOracle token config. +2. **Ownership** — accept ownership of vceBTC (already transferred to the Normal Timelock by the deployer). +3. **Access control** — grant \`mint(address,uint256)\` and \`burn(address,uint256)\` on vceBTC to the Normal Timelock and the Guardian. (The \`createVault(...)\` permission on the InstitutionalVaultController is granted by the separate controller/vault-upgrade VIP.) +4. **Initial supply** — mint the initial vceBTC collateral to the Ceffu multisig. +5. **Vault creation** — create the Fixed Rate Vault with vceBTC as collateral.`, + forDescription: "I agree that Venus Protocol should proceed with this proposal", + againstDescription: "I do not think that Venus Protocol should proceed with this proposal", + abstainDescription: "I am indifferent to whether Venus Protocol proceeds or not", + }; + + return makeProposal( + [ + // ────────────────────────────────────────────────────────────────────── + // 1. Oracle configuration identically to BTCB + // ────────────────────────────────────────────────────────────────────── + ...BTCB_ORACLE_CONFIGS.map(({ address, feed, maxStalePeriod }) => ({ + target: address, + signature: "setTokenConfig((address,address,uint256))", + params: [[VCEBTC, feed, maxStalePeriod]], + })), + { + target: BOUND_VALIDATOR, + signature: "setValidateConfig((address,uint256,uint256))", + params: [[VCEBTC, BTCB_UPPER_BOUND, BTCB_LOWER_BOUND]], + }, + { + target: bscmainnet.RESILIENT_ORACLE, + signature: "setTokenConfig((address,address[3],bool[3],bool))", + params: [[VCEBTC, [CHAINLINK_ORACLE, REDSTONE_ORACLE, ATLAS_ORACLE], [true, true, true], false]], + }, + + // ────────────────────────────────────────────────────────────────────── + // 2. Accept ownership of vceBTC + // ────────────────────────────────────────────────────────────────────── + { + target: VCEBTC, + signature: "acceptOwnership()", + params: [], + }, + + // ────────────────────────────────────────────────────────────────────── + // 3. Access control — vceBTC mint/burn + // ────────────────────────────────────────────────────────────────────── + { + target: bscmainnet.ACCESS_CONTROL_MANAGER, + signature: "giveCallPermission(address,string,address)", + params: [VCEBTC, "mint(address,uint256)", bscmainnet.NORMAL_TIMELOCK], + }, + { + target: bscmainnet.ACCESS_CONTROL_MANAGER, + signature: "giveCallPermission(address,string,address)", + params: [VCEBTC, "mint(address,uint256)", bscmainnet.CRITICAL_GUARDIAN], + }, + { + target: bscmainnet.ACCESS_CONTROL_MANAGER, + signature: "giveCallPermission(address,string,address)", + params: [VCEBTC, "burn(address,uint256)", bscmainnet.NORMAL_TIMELOCK], + }, + { + target: bscmainnet.ACCESS_CONTROL_MANAGER, + signature: "giveCallPermission(address,string,address)", + params: [VCEBTC, "burn(address,uint256)", bscmainnet.CRITICAL_GUARDIAN], + }, + + // ────────────────────────────────────────────────────────────────────── + // 4. Mint initial vceBTC collateral + // ────────────────────────────────────────────────────────────────────── + { + target: VCEBTC, + signature: "mint(address,uint256)", + params: [INITIAL_SUPPLY_RECIPIENT, VCEBTC_INITIAL_SUPPLY], + }, + + // ────────────────────────────────────────────────────────────────────── + // 5. Create the Fixed Rate Vault (vceBTC as collateral) + // ────────────────────────────────────────────────────────────────────── + { + target: FIXED_RATE_VAULT_CONTROLLER, + signature: + "createVault((address,uint256,uint256,uint256,uint256,uint256,uint40,uint40,uint40),(address,uint256,uint256,address,uint256),(uint256,uint256,uint256),string,string,string)", + params: [vaultConfig, instConfig, riskConfig, VAULT_SHARE_NAME, VAULT_SHARE_SYMBOL, INSTITUTION_NAME], + }, + ], + meta, + ProposalType.REGULAR, + ); +}; + +export default vip999; diff --git a/vips/vip-999/bsctestnet.ts b/vips/vip-999/bsctestnet.ts new file mode 100644 index 000000000..911917dbf --- /dev/null +++ b/vips/vip-999/bsctestnet.ts @@ -0,0 +1,163 @@ +import { ethers } from "ethers"; +import { parseUnits } from "ethers/lib/utils"; +import { NETWORK_ADDRESSES } from "src/networkAddresses"; +import { ProposalType } from "src/types"; +import { makeProposal } from "src/utils"; + +const { bsctestnet } = NETWORK_ADDRESSES; + +// TODO before proposing: +// 1. Redeploy vceBTC from fixed-rate-vaults CustodyReceiptToken (current deployment is the older +// VenusERC20 bytecode without pause/unpause), transfer ownership to the Normal Timelock, +// update VCEBTC, add pause()/unpause() ACM grants, and update the simulations. + +export const VCEBTC = "0x3C5Fc884BF6d1Ec8957A75EF6436b3B5750A57da"; +export const FIXED_RATE_VAULT_CONTROLLER = "0xf77dED2A00F94e33C392126238360D4642c16Ba2"; +export const BTCB = "0xA808e341e8e723DC6BA0Bb5204Bafc2330d7B8e4"; +export const CHAINLINK_ORACLE = bsctestnet.CHAINLINK_ORACLE; + +// Fixed direct price for vceBTC set on the Chainlink sub-oracle (18-decimal scale). +export const VCEBTC_DIRECT_PRICE = "2100000000000000000000000"; + +// Loan market supply asset — testnet USDT (6 decimals) +export const SUPPLY_ASSET = "0xA11c8D9DC9b66E209Ef60F0C8D969D3CD988782c"; + +// Test institution operator +export const INSTITUTION_OPERATOR = "0x4cD6300F5cb8D6BbA5E646131c3522664C10dF11"; + +// Initial vceBTC collateral supply +export const VCEBTC_INITIAL_SUPPLY = parseUnits("21.92", 18); + +// VaultConfig: [supplyAsset, fixedAPY(bps), reserveFactor(1e18), minBorrowCap, +// maxBorrowCap, minSupplierDeposit, openDuration, lockDuration, settlementWindow] +// Caps / minSupplierDeposit are in the supply asset (USDT, 6 decimals). +export const vaultConfig = [ + SUPPLY_ASSET, + 600, // fixedAPY = 6% + parseUnits("0.3", 18), // reserveFactor = 30% + parseUnits("10", 6), // minBorrowCap = 10 USDT (must be > 0; createVault reverts if 0) + parseUnits("1000000", 6), // maxBorrowCap = 1M USDT + 0, // minSupplierDeposit + 7 * 24 * 60 * 60, // openDuration = 7 days + 30 * 24 * 60 * 60, // lockDuration = 1 month + 3 * 24 * 60 * 60, // settlementWindow = 3 days +]; + +// InstitutionalConfig: [collateralAsset, idealCollateralAmount, marginRate(1e18), +// institutionOperator, positionTokenId] +export const instConfig = [ + VCEBTC, // collateral = vceBTC (18 decimals) + parseUnits("21.92", 18), // idealCollateralAmount = 21.92 BTCB + parseUnits("0.005", 18), // marginRate = 0.5% (must be > 0; createVault reverts InvalidConfig if 0) + INSTITUTION_OPERATOR, + 0, // positionTokenId assigned by the controller +]; + +// RiskConfig: [liquidationThreshold(1e18), liquidationIncentive(1e18), latePenaltyRate(1e18)] +export const riskConfig = [parseUnits("0.9", 18), parseUnits("1.1", 18), parseUnits("1.1", 18)]; + +export const VAULT_SHARE_NAME = "FRV Solv BTCB 24JUL2026 30"; +export const VAULT_SHARE_SYMBOL = "FRV-sv-24JUL2026-30"; +export const INSTITUTION_NAME = "Ceffu"; + +export const vip999 = () => { + const meta = { + version: "v2", + title: "VIP-999 [BNB Chain Testnet] Create Ceffu Custody BTC Fixed Rate Vault (DRAFT)", + description: `#### Summary + +This proposal onboards a new custody-mirror collateral token, **vceBTC ("Ceffu Custody BTC for Venus")**, and creates a new Venus **Fixed Rate Vault** (Institutional Loan Vault) that uses vceBTC as collateral. vceBTC is an accounting mirror for BTC held in custody by Ceffu; its supply is controlled by Venus governance and priced identically to BTCB. + +#### Actions + +1. **Oracle** — price vceBTC identically to BTCB by mirroring BTCB's oracle setup: set a fixed direct price on the Chainlink main sub-oracle (equal to BTCB's price) and point the ResilientOracle token config at that sub-oracle (main only; pivot / fallback disabled, no bound validation — matching BTCB on testnet). +2. **Ownership** — accept ownership of vceBTC (already transferred to the Normal Timelock by the deployer). +3. **Access control** — grant \`mint(address,uint256)\` and \`burn(address,uint256)\` on vceBTC to the Normal Timelock and the Guardian. (The \`createVault(...)\` permission on the InstitutionalVaultController is granted by the separate controller/vault-upgrade VIP.) +4. **Initial supply** — mint the initial vceBTC collateral to the Guardian. +5. **Vault creation** — create the Fixed Rate Vault with vceBTC as collateral.`, + forDescription: "I agree that Venus Protocol should proceed with this proposal", + againstDescription: "I do not think that Venus Protocol should proceed with this proposal", + abstainDescription: "I am indifferent to whether Venus Protocol proceeds or not", + }; + + return makeProposal( + [ + // ────────────────────────────────────────────────────────────────────── + // 1. Oracle configuration — fixed direct price on the Chainlink sub-oracle + // ────────────────────────────────────────────────────────────────────── + { + target: CHAINLINK_ORACLE, + signature: "setDirectPrice(address,uint256)", + params: [VCEBTC, VCEBTC_DIRECT_PRICE], + }, + { + target: bsctestnet.RESILIENT_ORACLE, + signature: "setTokenConfig((address,address[3],bool[3],bool))", + params: [ + [ + VCEBTC, + [CHAINLINK_ORACLE, ethers.constants.AddressZero, ethers.constants.AddressZero], + [true, false, false], + false, + ], + ], + }, + + // ────────────────────────────────────────────────────────────────────── + // 2. Accept ownership of vceBTC + // ────────────────────────────────────────────────────────────────────── + { + target: VCEBTC, + signature: "acceptOwnership()", + params: [], + }, + + // ────────────────────────────────────────────────────────────────────── + // 3. Access control — vceBTC mint/burn + // ────────────────────────────────────────────────────────────────────── + { + target: bsctestnet.ACCESS_CONTROL_MANAGER, + signature: "giveCallPermission(address,string,address)", + params: [VCEBTC, "mint(address,uint256)", bsctestnet.NORMAL_TIMELOCK], + }, + { + target: bsctestnet.ACCESS_CONTROL_MANAGER, + signature: "giveCallPermission(address,string,address)", + params: [VCEBTC, "mint(address,uint256)", bsctestnet.GUARDIAN], + }, + { + target: bsctestnet.ACCESS_CONTROL_MANAGER, + signature: "giveCallPermission(address,string,address)", + params: [VCEBTC, "burn(address,uint256)", bsctestnet.NORMAL_TIMELOCK], + }, + { + target: bsctestnet.ACCESS_CONTROL_MANAGER, + signature: "giveCallPermission(address,string,address)", + params: [VCEBTC, "burn(address,uint256)", bsctestnet.GUARDIAN], + }, + + // ────────────────────────────────────────────────────────────────────── + // 4. Mint initial vceBTC collateral + // ────────────────────────────────────────────────────────────────────── + { + target: VCEBTC, + signature: "mint(address,uint256)", + params: [bsctestnet.GUARDIAN, VCEBTC_INITIAL_SUPPLY], + }, + + // ────────────────────────────────────────────────────────────────────── + // 5. Create the Fixed Rate Vault (vceBTC as collateral) + // ────────────────────────────────────────────────────────────────────── + { + target: FIXED_RATE_VAULT_CONTROLLER, + signature: + "createVault((address,uint256,uint256,uint256,uint256,uint256,uint40,uint40,uint40),(address,uint256,uint256,address,uint256),(uint256,uint256,uint256),string,string,string)", + params: [vaultConfig, instConfig, riskConfig, VAULT_SHARE_NAME, VAULT_SHARE_SYMBOL, INSTITUTION_NAME], + }, + ], + meta, + ProposalType.REGULAR, + ); +}; + +export default vip999;