From da824c3f7296594b69895ae64ba6384bfd2ec8fb Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Sat, 2 Mar 2024 13:57:47 +0200 Subject: [PATCH 1/3] Implement migration to V3 --- abi/Oracles.json | 91 --- abi/Pool.json | 439 +---------- abi/PoolValidators.json | 597 --------------- abi/RewardToken.json | 117 +-- abi/StakedToken.json | 59 +- contracts/Oracles.sol | 104 +-- contracts/interfaces/IGnoGenesisVault.sol | 19 + contracts/interfaces/IMGNOWrapper.sol | 9 + contracts/interfaces/IOracles.sol | 34 - contracts/interfaces/IPool.sol | 178 +---- contracts/interfaces/IRewardToken.sol | 47 +- contracts/interfaces/IStakedToken.sol | 22 +- contracts/mocks/MulticallMock.sol | 68 +- contracts/mocks/RewardTokenMock.sol | 34 + contracts/mocks/StakedTokenMock.sol | 63 ++ contracts/mocks/VaultMock.sol | 27 + contracts/pool/Pool.sol | 342 +-------- contracts/pool/PoolValidators.sol | 147 ---- contracts/tokens/RewardToken.sol | 118 ++- contracts/tokens/StakedToken.sol | 42 +- deployments/index.js | 70 +- hardhat.config.js | 5 +- test/MerkleDistributor.test.js | 148 +--- test/Roles.test.js | 4 +- test/oracles/Oracles.test.js | 371 +--------- test/pool/Pool.test.js | 57 ++ test/pool/PoolEscrow.test.js | 28 +- test/pool/PoolValidators.test.js | 491 ------------- test/pool/settings.test.js | 202 ----- test/pool/stake.test.js | 853 ---------------------- test/tokens/RewardToken.test.js | 378 +++++++--- test/tokens/StakedToken.test.js | 146 ++-- test/tokens/toggleRewards.test.js | 50 +- test/utils.js | 325 ++------- 34 files changed, 1039 insertions(+), 4646 deletions(-) delete mode 100644 abi/PoolValidators.json create mode 100644 contracts/interfaces/IGnoGenesisVault.sol create mode 100644 contracts/mocks/RewardTokenMock.sol create mode 100644 contracts/mocks/StakedTokenMock.sol create mode 100644 contracts/mocks/VaultMock.sol delete mode 100644 contracts/pool/PoolValidators.sol create mode 100644 test/pool/Pool.test.js delete mode 100644 test/pool/PoolValidators.test.js delete mode 100644 test/pool/settings.test.js delete mode 100644 test/pool/stake.test.js diff --git a/abi/Oracles.json b/abi/Oracles.json index 6ebf6adc..a20055e5 100644 --- a/abi/Oracles.json +++ b/abi/Oracles.json @@ -316,19 +316,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "currentValidatorsNonce", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [ { @@ -523,61 +510,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [ - { - "components": [ - { - "internalType": "address", - "name": "operator", - "type": "address" - }, - { - "internalType": "bytes32", - "name": "withdrawalCredentials", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "depositDataRoot", - "type": "bytes32" - }, - { - "internalType": "bytes", - "name": "publicKey", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "signature", - "type": "bytes" - } - ], - "internalType": "struct IPoolValidators.DepositData[]", - "name": "depositData", - "type": "tuple[]" - }, - { - "internalType": "bytes32[][]", - "name": "merkleProofs", - "type": "bytes32[][]" - }, - { - "internalType": "bytes32", - "name": "validatorsDepositRoot", - "type": "bytes32" - }, - { - "internalType": "bytes[]", - "name": "signatures", - "type": "bytes[]" - } - ], - "name": "registerValidators", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [ { @@ -676,29 +608,6 @@ "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "totalRewards", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "activatedValidators", - "type": "uint256" - }, - { - "internalType": "bytes[]", - "name": "signatures", - "type": "bytes[]" - } - ], - "name": "submitRewards", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [], "name": "unpause", diff --git a/abi/Pool.json b/abi/Pool.json index 38420592..3478a2cf 100644 --- a/abi/Pool.json +++ b/abi/Pool.json @@ -1,4 +1,15 @@ [ + { + "inputs": [ + { + "internalType": "address", + "name": "_poolEscrow", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, { "anonymous": false, "inputs": [ @@ -354,92 +365,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "VALIDATOR_TOTAL_DEPOSIT", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - }, - { - "internalType": "uint256", - "name": "validatorIndex", - "type": "uint256" - } - ], - "name": "activate", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - }, - { - "internalType": "uint256[]", - "name": "validatorIndexes", - "type": "uint256[]" - } - ], - "name": "activateMultiple", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "activatedValidators", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - }, - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "name": "activations", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [ { @@ -466,44 +391,6 @@ "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "mgnoIn", - "type": "uint256" - } - ], - "name": "calculateGNO", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "validatorIndex", - "type": "uint256" - } - ], - "name": "canActivate", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [ { @@ -608,54 +495,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [ - { - "internalType": "address", - "name": "admin", - "type": "address" - }, - { - "internalType": "bytes32", - "name": "_withdrawalCredentials", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "_validatorRegistration", - "type": "address" - }, - { - "internalType": "address", - "name": "_stakedToken", - "type": "address" - }, - { - "internalType": "address", - "name": "_validators", - "type": "address" - }, - { - "internalType": "address", - "name": "_oracles", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_minActivatingDeposit", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_pendingValidatorsLimit", - "type": "uint256" - } - ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [ { @@ -694,19 +533,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "minActivatingDeposit", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [], "name": "pause", @@ -729,83 +555,17 @@ }, { "inputs": [], - "name": "pendingValidators", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "pendingValidatorsLimit", + "name": "poolEscrow", "outputs": [ { - "internalType": "uint256", + "internalType": "address", "name": "", - "type": "uint256" + "type": "address" } ], "stateMutability": "view", "type": "function" }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "refund", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "address", - "name": "operator", - "type": "address" - }, - { - "internalType": "bytes32", - "name": "withdrawalCredentials", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "depositDataRoot", - "type": "bytes32" - }, - { - "internalType": "bytes", - "name": "publicKey", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "signature", - "type": "bytes" - } - ], - "internalType": "struct IPoolValidators.DepositData", - "name": "depositData", - "type": "tuple" - } - ], - "name": "registerValidator", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [ { @@ -869,149 +629,8 @@ "type": "function" }, { - "inputs": [ - { - "internalType": "uint256", - "name": "newActivatedValidators", - "type": "uint256" - } - ], - "name": "setActivatedValidators", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "newMinActivatingDeposit", - "type": "uint256" - } - ], - "name": "setMinActivatingDeposit", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "newPendingValidatorsLimit", - "type": "uint256" - } - ], - "name": "setPendingValidatorsLimit", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "address", - "name": "referredBy", - "type": "address" - }, - { - "internalType": "bool", - "name": "hasRevenueShare", - "type": "bool" - } - ], - "name": "stakeGNO", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "address", - "name": "referredBy", - "type": "address" - }, - { - "internalType": "bool", - "name": "hasRevenueShare", - "type": "bool" - }, - { - "internalType": "uint256", - "name": "nonce", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "expiry", - "type": "uint256" - }, - { - "internalType": "uint8", - "name": "v", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "r", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "s", - "type": "bytes32" - } - ], - "name": "stakeGNOWithPermit", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "address", - "name": "referredBy", - "type": "address" - }, - { - "internalType": "bool", - "name": "hasRevenueShare", - "type": "bool" - } - ], - "name": "stakeMGNO", + "inputs": [], + "name": "transferToPoolEscrow", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -1022,31 +641,5 @@ "outputs": [], "stateMutability": "nonpayable", "type": "function" - }, - { - "inputs": [], - "name": "validatorRegistration", - "outputs": [ - { - "internalType": "contract IDepositContract", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "withdrawalCredentials", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" } ] diff --git a/abi/PoolValidators.json b/abi/PoolValidators.json deleted file mode 100644 index 92d3b9f7..00000000 --- a/abi/PoolValidators.json +++ /dev/null @@ -1,597 +0,0 @@ -[ - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "operator", - "type": "address" - }, - { - "indexed": true, - "internalType": "bytes32", - "name": "depositDataMerkleRoot", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "string", - "name": "depositDataMerkleProofs", - "type": "string" - } - ], - "name": "OperatorAdded", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "operator", - "type": "address" - } - ], - "name": "OperatorCommitted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "operator", - "type": "address" - } - ], - "name": "OperatorRemoved", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "Paused", - "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" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "Unpaused", - "type": "event" - }, - { - "inputs": [], - "name": "DEFAULT_ADMIN_ROLE", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "PAUSER_ROLE", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_account", - "type": "address" - } - ], - "name": "addAdmin", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_operator", - "type": "address" - }, - { - "internalType": "bytes32", - "name": "depositDataMerkleRoot", - "type": "bytes32" - }, - { - "internalType": "string", - "name": "depositDataMerkleProofs", - "type": "string" - } - ], - "name": "addOperator", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_account", - "type": "address" - } - ], - "name": "addPauser", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "commitOperator", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_operator", - "type": "address" - } - ], - "name": "getOperator", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - }, - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "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": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "index", - "type": "uint256" - } - ], - "name": "getRoleMember", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - } - ], - "name": "getRoleMemberCount", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "grantRole", - "outputs": [], - "stateMutability": "nonpayable", - "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": "_admin", - "type": "address" - }, - { - "internalType": "address", - "name": "_pool", - "type": "address" - }, - { - "internalType": "address", - "name": "_oracles", - "type": "address" - } - ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_account", - "type": "address" - } - ], - "name": "isAdmin", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_account", - "type": "address" - } - ], - "name": "isPauser", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "name": "isValidatorRegistered", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "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": [ - { - "components": [ - { - "internalType": "address", - "name": "operator", - "type": "address" - }, - { - "internalType": "bytes32", - "name": "withdrawalCredentials", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "depositDataRoot", - "type": "bytes32" - }, - { - "internalType": "bytes", - "name": "publicKey", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "signature", - "type": "bytes" - } - ], - "internalType": "struct IPoolValidators.DepositData", - "name": "depositData", - "type": "tuple" - }, - { - "internalType": "bytes32[]", - "name": "merkleProof", - "type": "bytes32[]" - } - ], - "name": "registerValidator", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_account", - "type": "address" - } - ], - "name": "removeAdmin", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_operator", - "type": "address" - } - ], - "name": "removeOperator", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_account", - "type": "address" - } - ], - "name": "removePauser", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "renounceRole", - "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": [], - "name": "unpause", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } -] diff --git a/abi/RewardToken.json b/abi/RewardToken.json index 933e4491..011bfc3c 100644 --- a/abi/RewardToken.json +++ b/abi/RewardToken.json @@ -1,4 +1,15 @@ [ + { + "inputs": [ + { + "internalType": "address", + "name": "_vault", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, { "anonymous": false, "inputs": [ @@ -571,44 +582,6 @@ "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [ - { - "internalType": "address", - "name": "admin", - "type": "address" - }, - { - "internalType": "address", - "name": "_stakedToken", - "type": "address" - }, - { - "internalType": "address", - "name": "_oracles", - "type": "address" - }, - { - "internalType": "address", - "name": "_protocolFeeRecipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_protocolFee", - "type": "uint256" - }, - { - "internalType": "address", - "name": "_merkleDistributor", - "type": "address" - } - ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [ { @@ -673,6 +646,29 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "principal", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reward", + "type": "uint256" + } + ], + "name": "migrate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [], "name": "name", @@ -945,6 +941,32 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "totalAssets", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalPenalty", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "totalRewards", @@ -1082,14 +1104,27 @@ { "inputs": [ { - "internalType": "uint256", - "name": "newTotalRewards", - "type": "uint256" + "internalType": "int256", + "name": "rewardsDelta", + "type": "int256" } ], "name": "updateTotalRewards", "outputs": [], "stateMutability": "nonpayable", "type": "function" + }, + { + "inputs": [], + "name": "vault", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" } ] diff --git a/abi/StakedToken.json b/abi/StakedToken.json index e11fdd09..8d48d530 100644 --- a/abi/StakedToken.json +++ b/abi/StakedToken.json @@ -282,6 +282,24 @@ "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", @@ -460,29 +478,6 @@ "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [ - { - "internalType": "address", - "name": "admin", - "type": "address" - }, - { - "internalType": "address", - "name": "_pool", - "type": "address" - }, - { - "internalType": "address", - "name": "_rewardToken", - "type": "address" - } - ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [ { @@ -521,24 +516,6 @@ "stateMutability": "view", "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", diff --git a/contracts/Oracles.sol b/contracts/Oracles.sol index f3c797d6..da050381 100644 --- a/contracts/Oracles.sol +++ b/contracts/Oracles.sol @@ -38,7 +38,7 @@ contract Oracles is IOracles, OwnablePausableUpgradeable { // @dev Address of the Pool contract. IPool private pool; - // @dev Address of the Pool contract. + // @dev Address of the PoolValidators contract. IPoolValidators private poolValidators; // @dev Address of the MerkleDistributor contract. @@ -59,13 +59,6 @@ contract Oracles is IOracles, OwnablePausableUpgradeable { return rewardsNonce.current(); } - /** - * @dev See {IOracles-currentValidatorsNonce}. - */ - function currentValidatorsNonce() external override view returns (uint256) { - return validatorsNonce.current(); - } - /** * @dev See {IOracles-isOracle}. */ @@ -107,50 +100,6 @@ contract Oracles is IOracles, OwnablePausableUpgradeable { return totalOracles >= signaturesCount && signaturesCount.mul(3) > totalOracles.mul(2); } - /** - * @dev See {IOracles-submitRewards}. - */ - function submitRewards( - uint256 totalRewards, - uint256 activatedValidators, - bytes[] calldata signatures - ) - external override onlyOracle whenNotPaused - { - require(isEnoughSignatures(signatures.length), "Oracles: invalid number of signatures"); - - // calculate candidate ID hash - uint256 nonce = rewardsNonce.current(); - bytes32 candidateId = ECDSAUpgradeable.toEthSignedMessageHash( - keccak256(abi.encode(nonce, activatedValidators, totalRewards)) - ); - - // check signatures and calculate number of submitted oracle votes - address[] memory signedOracles = new address[](signatures.length); - for (uint256 i = 0; i < signatures.length; i++) { - bytes memory signature = signatures[i]; - address signer = ECDSAUpgradeable.recover(candidateId, signature); - require(hasRole(ORACLE_ROLE, signer), "Oracles: invalid signer"); - - for (uint256 j = 0; j < i; j++) { - require(signedOracles[j] != signer, "Oracles: repeated signature"); - } - signedOracles[i] = signer; - emit RewardsVoteSubmitted(msg.sender, signer, nonce, totalRewards, activatedValidators); - } - - // increment nonce for future signatures - rewardsNonce.increment(); - - // update total rewards - rewardToken.updateTotalRewards(totalRewards); - - // update activated validators - if (activatedValidators != pool.activatedValidators()) { - pool.setActivatedValidators(activatedValidators); - } - } - /** * @dev See {IOracles-submitMerkleRoot}. */ @@ -190,55 +139,4 @@ contract Oracles is IOracles, OwnablePausableUpgradeable { // update merkle root merkleDistributor.setMerkleRoot(merkleRoot, merkleProofs); } - - /** - * @dev See {IOracles-registerValidators}. - */ - function registerValidators( - IPoolValidators.DepositData[] calldata depositData, - bytes32[][] calldata merkleProofs, - bytes32 validatorsDepositRoot, - bytes[] calldata signatures - ) - external override onlyOracle whenNotPaused - { - require( - pool.validatorRegistration().get_deposit_root() == validatorsDepositRoot, - "Oracles: invalid validators deposit root" - ); - require(isEnoughSignatures(signatures.length), "Oracles: invalid number of signatures"); - - // calculate candidate ID hash - uint256 nonce = validatorsNonce.current(); - bytes32 candidateId = ECDSAUpgradeable.toEthSignedMessageHash( - keccak256(abi.encode(nonce, depositData, validatorsDepositRoot)) - ); - - // check signatures are correct - address[] memory signedOracles = new address[](signatures.length); - for (uint256 i = 0; i < signatures.length; i++) { - bytes memory signature = signatures[i]; - address signer = ECDSAUpgradeable.recover(candidateId, signature); - require(hasRole(ORACLE_ROLE, signer), "Oracles: invalid signer"); - - for (uint256 j = 0; j < i; j++) { - require(signedOracles[j] != signer, "Oracles: repeated signature"); - } - signedOracles[i] = signer; - } - - uint256 depositDataLength = depositData.length; - require(merkleProofs.length == depositDataLength, "Oracles: invalid merkle proofs length"); - - // submit deposit data - for (uint256 i = 0; i < depositDataLength; i++) { - // register validator - poolValidators.registerValidator(depositData[i], merkleProofs[i]); - } - - emit RegisterValidatorsVoteSubmitted(msg.sender, signedOracles, nonce); - - // increment nonce for future registrations - validatorsNonce.increment(); - } } diff --git a/contracts/interfaces/IGnoGenesisVault.sol b/contracts/interfaces/IGnoGenesisVault.sol new file mode 100644 index 00000000..62b51b41 --- /dev/null +++ b/contracts/interfaces/IGnoGenesisVault.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +pragma solidity 0.7.5; + + +/** + * @title IGnoGenesisVault + * @author StakeWise + * @notice Defines the interface for the GnoGenesisVault contract + */ +interface IGnoGenesisVault { + /** + * @notice Function for migrating from StakeWise v2. Can be called only by RewardToken contract. + * @param receiver The address of the receiver + * @param assets The amount of assets migrated + * @return shares The amount of shares minted + */ + function migrate(address receiver, uint256 assets) external returns (uint256 shares); +} diff --git a/contracts/interfaces/IMGNOWrapper.sol b/contracts/interfaces/IMGNOWrapper.sol index 6c205f6e..bf9b9e58 100644 --- a/contracts/interfaces/IMGNOWrapper.sol +++ b/contracts/interfaces/IMGNOWrapper.sol @@ -11,4 +11,13 @@ interface IMGNOWrapper { * @param token - address of the token. */ function tokenRate(address token) external view returns (uint256); + + /** + * @dev Swaps some of the wrapped tokens to the whitelisted token. + * Wrapped tokens will be burned. + * @param _token Address of the whitelisted token contract. + * @param _amount Amount of tokens to swap. + * @return Amount of returned tokens. + */ + function unwrap(address _token, uint256 _amount) external returns (uint256); } diff --git a/contracts/interfaces/IOracles.sol b/contracts/interfaces/IOracles.sol index eed704f4..98fb9bf8 100644 --- a/contracts/interfaces/IOracles.sol +++ b/contracts/interfaces/IOracles.sol @@ -2,7 +2,6 @@ pragma solidity 0.7.5; -import "./IPoolValidators.sol"; pragma abicoder v2; /** @@ -81,11 +80,6 @@ interface IOracles { */ function currentRewardsNonce() external view returns (uint256); - /** - * @dev Function for retrieving current validators nonce. - */ - function currentValidatorsNonce() external view returns (uint256); - /** * @dev Function for adding an oracle role to the account. * Can only be called by an account with an admin role. @@ -100,19 +94,6 @@ interface IOracles { */ function removeOracle(address account) external; - /** - * @dev Function for submitting oracle vote for total rewards. - * The quorum of signatures over the same data is required to submit the new value. - * @param totalRewards - voted total rewards. - * @param activatedValidators - voted amount of activated validators. - * @param signatures - oracles' signatures. - */ - function submitRewards( - uint256 totalRewards, - uint256 activatedValidators, - bytes[] calldata signatures - ) external; - /** * @dev Function for submitting new merkle root. * The quorum of signatures over the same data is required to submit the new value. @@ -125,19 +106,4 @@ interface IOracles { string calldata merkleProofs, bytes[] calldata signatures ) external; - - /** - * @dev Function for submitting registrations of the new validators. - * The quorum of signatures over the same data is required to register. - * @param depositData - an array of deposit data to register. - * @param merkleProofs - an array of hashes to verify whether the every deposit data is part of the merkle root. - * @param validatorsDepositRoot - validators deposit root to protect from malicious operators. - * @param signatures - oracles' signatures. - */ - function registerValidators( - IPoolValidators.DepositData[] calldata depositData, - bytes32[][] calldata merkleProofs, - bytes32 validatorsDepositRoot, - bytes[] calldata signatures - ) external; } diff --git a/contracts/interfaces/IPool.sol b/contracts/interfaces/IPool.sol index f73eeba9..1bdeb9e6 100644 --- a/contracts/interfaces/IPool.sol +++ b/contracts/interfaces/IPool.sol @@ -1,10 +1,6 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.7.5; -pragma abicoder v2; - -import "./IDepositContract.sol"; -import "./IPoolValidators.sol"; /** * @dev Interface of the Pool contract. @@ -78,32 +74,9 @@ interface IPool { event StakedWithReferrer(address indexed referrer, uint256 amount); /** - * @dev Function for initializing the Pool contract. - * @param admin - address of the contract admin. - * @param _withdrawalCredentials - withdrawal credentials for the pool validators. - * @param _validatorRegistration - address of the ValidatorRegistration contract. - * @param _stakedToken - address of the StakedToken contract. - * @param _validators - address of the Validators contract. - * @param _oracles - address of the Oracles contract. - * @param _minActivatingDeposit - minimal deposit amount considered for the activation. - * @param _pendingValidatorsLimit - pending validators limit. When it's exceeded, the deposits will be set for the activation. - */ - function initialize( - address admin, - bytes32 _withdrawalCredentials, - address _validatorRegistration, - address _stakedToken, - address _validators, - address _oracles, - uint256 _minActivatingDeposit, - uint256 _pendingValidatorsLimit - ) external; - - /** - * @dev Function for getting the total validator deposit. + * @dev Returns PoolEscrow contract address. */ - // solhint-disable-next-line func-name-mixedcase - function VALIDATOR_TOTAL_DEPOSIT() external view returns (uint256); + function poolEscrow() external view returns (address); /** * @dev Function for getting the address of mGNO <-> GNO wrapper. @@ -124,150 +97,7 @@ interface IPool { function MGNO_TOKEN() external view returns (address); /** - * @dev Function for retrieving the total amount of pending validators. - */ - function pendingValidators() external view returns (uint256); - - /** - * @dev Function for retrieving the total amount of activated validators. - */ - function activatedValidators() external view returns (uint256); - - /** - * @dev Function for retrieving the withdrawal credentials used to - * initiate pool validators withdrawal from the beacon chain. - */ - function withdrawalCredentials() external view returns (bytes32); - - /** - * @dev Function for getting the minimal deposit amount considered for the activation. - */ - function minActivatingDeposit() external view returns (uint256); - - /** - * @dev Function for getting the pending validators percent limit. - * When it's exceeded, the deposits will be set for the activation. - */ - function pendingValidatorsLimit() external view returns (uint256); - - /** - * @dev Function for getting the amount of activating deposits. - * @param account - address of the account to get the amount for. - * @param validatorIndex - index of the activated validator. - */ - function activations(address account, uint256 validatorIndex) external view returns (uint256); - - /** - * @dev Function for setting minimal deposit amount considered for the activation period. - * @param newMinActivatingDeposit - new minimal deposit amount considered for the activation. - */ - function setMinActivatingDeposit(uint256 newMinActivatingDeposit) external; - - /** - * @dev Function for changing the total amount of activated validators. - * @param newActivatedValidators - new total amount of activated validators. - */ - function setActivatedValidators(uint256 newActivatedValidators) external; - - /** - * @dev Function for changing pending validators limit. - * @param newPendingValidatorsLimit - new pending validators limit. When it's exceeded, the deposits will be set for the activation. - */ - function setPendingValidatorsLimit(uint256 newPendingValidatorsLimit) external; - - /** - * @dev Function for calculating GNO amount for the mGNO input. - * @param mgnoIn - mGNO tokens amount. - */ - function calculateGNO(uint256 mgnoIn) external view returns (uint256); - - /** - * @dev Function for checking whether validator index can be activated. - * @param validatorIndex - index of the validator to check. - */ - function canActivate(uint256 validatorIndex) external view returns (bool); - - /** - * @dev Function for retrieving the validator registration contract address. - */ - function validatorRegistration() external view returns (IDepositContract); - - /** - * @dev Function for staking GNO tokens to the pool. The tokens will be converted to mGNO and then staked. - * @param amount - the amount of tokens to stake. - * @param recipient - address of the staked mGNO tokens recipient. Can be zero if should be the sender. - * @param referredBy - address of the referrer. Can be zero if not referred by anyone. - * @param hasRevenueShare - defines whether referrer participates in revenue sharing. - */ - function stakeGNO( - uint256 amount, - address recipient, - address referredBy, - bool hasRevenueShare - ) external; - - /** - * @dev Function for staking GNO tokens to the pool with permit call. The tokens will be converted to mGNO and then staked. - * @param amount - the amount of tokens to stake. - * @param recipient - address of the staked mGNO tokens recipient. Can be zero if should be the sender. - * @param referredBy - address of the referrer. Can be zero if not referred by anyone. - * @param hasRevenueShare - defines whether referrer participates in revenue sharing. - * @param nonce - The nonce taken from `nonces(_holder)` public getter. - * @param expiry - The allowance expiration date (unix timestamp in UTC). Can be zero for no expiration. - * @param v - A final byte of signature (ECDSA component). - * @param r - The first 32 bytes of signature (ECDSA component). - * @param s - The second 32 bytes of signature (ECDSA component). - */ - function stakeGNOWithPermit( - uint256 amount, - address recipient, - address referredBy, - bool hasRevenueShare, - uint256 nonce, - uint256 expiry, - uint8 v, - bytes32 r, - bytes32 s - ) external; - - /** - * @dev Function for staking mGNO tokens to the pool. - * @param amount - the amount of tokens to stake. - * @param recipient - address of the staked mGNO tokens recipient. Can be zero if should be the sender. - * @param referredBy - address of the referrer. Can be zero if not referred by anyone. - * @param hasRevenueShare - defines whether referrer participates in revenue sharing. - */ - function stakeMGNO( - uint256 amount, - address recipient, - address referredBy, - bool hasRevenueShare - ) external; - - /** - * @dev Function for minting account's tokens for the specific validator index. - * @param account - account address to activate the tokens for. - * @param validatorIndex - index of the activated validator. - */ - function activate(address account, uint256 validatorIndex) external; - - /** - * @dev Function for minting account's tokens for the specific validator indexes. - * @param account - account address to activate the tokens for. - * @param validatorIndexes - list of activated validator indexes. - */ - function activateMultiple(address account, uint256[] calldata validatorIndexes) external; - - /** - * @dev Function for registering new pool validator registration. - * @param depositData - the deposit data to submit for the validator. - */ - function registerValidator(IPoolValidators.DepositData calldata depositData) external; - - /** - * @dev Function for refunding to the pool. - * Can only be executed by the account with admin role. - * @param amount - the amount of mGNO tokens to refund. + * @dev Function for transferring all GNO accumulated in Pool contract to PoolEscrow contract. */ - function refund(uint256 amount) external; + function transferToPoolEscrow() external; } diff --git a/contracts/interfaces/IRewardToken.sol b/contracts/interfaces/IRewardToken.sol index 5ca9200f..c5cce410 100644 --- a/contracts/interfaces/IRewardToken.sol +++ b/contracts/interfaces/IRewardToken.sol @@ -53,24 +53,6 @@ interface IRewardToken is IERC20Upgradeable { uint256 protocolReward ); - /** - * @dev Function for initializing the RewardToken contract. - * @param admin - address of the contract admin. - * @param _stakedToken - address of the StakedToken contract. - * @param _oracles - address of the Oracles contract. - * @param _protocolFeeRecipient - address of the protocol fee recipient. - * @param _protocolFee - protocol fee. - * @param _merkleDistributor - address of the MerkleDistributor contract. - */ - function initialize( - address admin, - address _stakedToken, - address _oracles, - address _protocolFeeRecipient, - uint256 _protocolFee, - address _merkleDistributor - ) external; - /** * @dev Function for getting the address of the merkle distributor. */ @@ -81,6 +63,21 @@ interface IRewardToken is IERC20Upgradeable { */ function protocolFeeRecipient() external view returns (address); + /** + * @dev Function for getting the address of the vault. + */ + function vault() external view returns (address); + + /** + * @dev Function for getting the total assets. + */ + function totalAssets() external view returns (uint256); + + /** + * @dev Function for getting the total penalty. + */ + function totalPenalty() external view returns (uint256); + /** * @dev Function for changing the protocol fee recipient's address. * @param recipient - new protocol fee recipient's address. @@ -148,10 +145,18 @@ interface IRewardToken is IERC20Upgradeable { /** * @dev Function for updating validators total rewards. - * Can only be called by Oracles contract. - * @param newTotalRewards - new total rewards. + * Can only be called by Vault contract. + * @param rewardsDelta - the total rewards earned or penalties received. + */ + function updateTotalRewards(int256 rewardsDelta) external; + + /** + * @dev Function for migrating to the StakeWise V3 Vault. + * @param receiver - address of the account the tokens will be assigned to. + * @param principal - amount of staked tokens to migrate. + * @param reward - amount of reward tokens to migrate. */ - function updateTotalRewards(uint256 newTotalRewards) external; + function migrate(address receiver, uint256 principal, uint256 reward) external; /** * @dev Function for claiming reward tokens from the merkle distribution. diff --git a/contracts/interfaces/IStakedToken.sol b/contracts/interfaces/IStakedToken.sol index a1cf78dd..c8061722 100644 --- a/contracts/interfaces/IStakedToken.sol +++ b/contracts/interfaces/IStakedToken.sol @@ -9,18 +9,6 @@ import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; */ interface IStakedToken is IERC20Upgradeable { /** -* @dev Function for initializing the StakedToken contract. - * @param admin - address of the contract admin. - * @param _pool - address of the Pool contract. - * @param _rewardToken - address of the RewardToken contract. - */ - function initialize( - address admin, - address _pool, - address _rewardToken - ) external; - - /** * @dev Function for retrieving the total deposits amount. */ function totalDeposits() external view returns (uint256); @@ -38,10 +26,10 @@ interface IStakedToken is IERC20Upgradeable { function toggleRewards(address account, bool isDisabled) external; /** - * @dev Function for creating `amount` tokens and assigning them to `account`. - * Can only be called by Pool contract. - * @param account - address of the account to assign tokens to. - * @param amount - amount of tokens to assign. + * @dev Function for burning `amount` tokens from `account`. + * Can only be called by RewardToken contract. + * @param account - address of the account to burn tokens from. + * @param amount - amount of tokens to burn. */ - function mint(address account, uint256 amount) external; + function burn(address account, uint256 amount) external; } diff --git a/contracts/mocks/MulticallMock.sol b/contracts/mocks/MulticallMock.sol index 34f4f4b5..93989f58 100644 --- a/contracts/mocks/MulticallMock.sol +++ b/contracts/mocks/MulticallMock.sol @@ -7,14 +7,9 @@ pragma abicoder v2; import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import "../interfaces/IOracles.sol"; import "../interfaces/IMerkleDistributor.sol"; +import "../interfaces/IRewardToken.sol"; contract MulticallMock { - struct OracleRewards { - uint256 totalRewards; - uint256 activatedValidators; - bytes[] signatures; - } - struct MerkleRoot { bytes32 merkleRoot; string merkleProofs; @@ -23,42 +18,47 @@ contract MulticallMock { IOracles private oracles; IERC20Upgradeable private stakedToken; - IERC20Upgradeable private rewardToken; + IRewardToken private rewardToken; IMerkleDistributor private merkleDistributor; constructor(address _oracles, address _stakedToken, address _rewardToken, address _merkleDistributor) { oracles = IOracles(_oracles); stakedToken = IERC20Upgradeable(_stakedToken); - rewardToken = IERC20Upgradeable(_rewardToken); + rewardToken = IRewardToken(_rewardToken); merkleDistributor = IMerkleDistributor(_merkleDistributor); } function transferRewardsAndUpdateTotalRewards( - uint256 totalRewards, - uint256 activatedValidators, - address payee, - bytes[] calldata signatures + int256 rewardsDelta, + address payee ) external { rewardToken.transferFrom(msg.sender, payee, rewardToken.balanceOf(msg.sender)); - oracles.submitRewards(totalRewards, activatedValidators, signatures); + rewardToken.updateTotalRewards(rewardsDelta); } function updateTotalRewardsAndTransferRewards( - uint256 totalRewards, - uint256 activatedValidators, - address payee, - bytes[] calldata signatures + int256 rewardsDelta, + address payee ) external { - oracles.submitRewards(totalRewards, activatedValidators, signatures); + rewardToken.updateTotalRewards(rewardsDelta); rewardToken.transferFrom(msg.sender, payee, rewardToken.balanceOf(msg.sender)); } + function updateTotalRewardsAndMigrate(int256 rewardsDelta) external { + rewardToken.updateTotalRewards(rewardsDelta); + rewardToken.migrate( + msg.sender, + stakedToken.balanceOf(address(this)), + rewardToken.balanceOf(address(this)) + ); + } + function updateTotalRewardsAndClaim( - OracleRewards memory oracleRewards, + int256 rewardsDelta, uint256 index, address account, address[] calldata tokens, @@ -67,12 +67,12 @@ contract MulticallMock { ) external { - oracles.submitRewards(oracleRewards.totalRewards, oracleRewards.activatedValidators, oracleRewards.signatures); + rewardToken.updateTotalRewards(rewardsDelta); merkleDistributor.claim(index, account, tokens, amounts, merkleProof); } function claimAndUpdateTotalRewards( - OracleRewards memory oracleRewards, + int256 rewardsDelta, uint256 index, address account, address[] calldata tokens, @@ -82,7 +82,7 @@ contract MulticallMock { external { merkleDistributor.claim(index, account, tokens, amounts, merkleProof); - oracles.submitRewards(oracleRewards.totalRewards, oracleRewards.activatedValidators, oracleRewards.signatures); + rewardToken.updateTotalRewards(rewardsDelta); } function claimAndUpdateMerkleRoot( @@ -113,37 +113,33 @@ contract MulticallMock { merkleDistributor.claim(index, account, tokens, amounts, merkleProof); } - function updateTotalRewardsAndTransferStakedTokens( - uint256 totalRewards, - uint256 activatedValidators, - address payee, - bytes[] calldata signatures + function updateTotalRewardsAndTransferStakedEth( + int256 rewardsDelta, + address payee ) external { - oracles.submitRewards(totalRewards, activatedValidators, signatures); + rewardToken.updateTotalRewards(rewardsDelta); stakedToken.transferFrom(msg.sender, payee, stakedToken.balanceOf(msg.sender)); } - function transferStakedTokensAndUpdateTotalRewards( - uint256 totalRewards, - uint256 activatedValidators, - address payee, - bytes[] calldata signatures + function transferStakedEthAndUpdateTotalRewards( + int256 rewardsDelta, + address payee ) external { stakedToken.transferFrom(msg.sender, payee, stakedToken.balanceOf(msg.sender)); - oracles.submitRewards(totalRewards, activatedValidators, signatures); + rewardToken.updateTotalRewards(rewardsDelta); } function updateTotalRewardsAndMerkleRoot( - OracleRewards memory oracleRewards, + int256 rewardsDelta, MerkleRoot memory merkleRoot ) external { - oracles.submitRewards(oracleRewards.totalRewards, oracleRewards.activatedValidators, oracleRewards.signatures); + rewardToken.updateTotalRewards(rewardsDelta); oracles.submitMerkleRoot(merkleRoot.merkleRoot, merkleRoot.merkleProofs, merkleRoot.signatures); } } diff --git a/contracts/mocks/RewardTokenMock.sol b/contracts/mocks/RewardTokenMock.sol new file mode 100644 index 00000000..f4bf88d5 --- /dev/null +++ b/contracts/mocks/RewardTokenMock.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +pragma solidity 0.7.5; + +import "../tokens/RewardToken.sol"; + +/** + * @title RewardTokenMock + * + * @dev RewardTokenMock contract is used for testing the RewardToken contract. + */ +contract RewardTokenMock is RewardToken { + constructor(address _vault) RewardToken(_vault) {} + + /** + * @dev Initializes the contract. + * + * @param _name Name of the token. + * @param _symbol Symbol of the token. + * @param _admin Address of the admin. + * @param _merkleDistributor Address of the MerkleDistributor contract. + */ + function initialize( + string memory _name, + string memory _symbol, + address _admin, + address _merkleDistributor + ) external initializer { + __ERC20_init(_name, _symbol); + __ERC20Permit_init(_name); + __OwnablePausableUpgradeable_init(_admin); + merkleDistributor = _merkleDistributor; + } +} diff --git a/contracts/mocks/StakedTokenMock.sol b/contracts/mocks/StakedTokenMock.sol new file mode 100644 index 00000000..bda5eda1 --- /dev/null +++ b/contracts/mocks/StakedTokenMock.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +pragma solidity 0.7.5; + +import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/token/ERC20/SafeERC20Upgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol"; +import "../interfaces/IRewardToken.sol"; +import "../tokens/StakedToken.sol"; + +/** + * @title StakedTokenMock + * + * @dev StakedTokenMock contract is used for testing the StakedToken contract. + */ +contract StakedTokenMock is StakedToken { + using SafeMathUpgradeable for uint256; + using SafeERC20Upgradeable for IERC20Upgradeable; + + address public immutable poolEscrow; + address public immutable gnoToken; + + constructor(address _poolEscrow, address _gnoToken) { + poolEscrow = _poolEscrow; + gnoToken = _gnoToken; + } + + /** + * @dev Initializes the contract. + * + * @param _name Name of the token. + * @param _symbol Symbol of the token. + * @param _admin Address of the admin. + * @param _rewardToken Address of the RewardToken contract. + */ + function initialize( + string memory _name, + string memory _symbol, + address _admin, + address _rewardToken + ) external initializer { + __ERC20_init(_name, _symbol); + __ERC20Permit_init(_name); + __OwnablePausableUpgradeable_init(_admin); + rewardToken = IRewardToken(_rewardToken); + } + + function mint(uint256 amount) external { + IERC20Upgradeable(gnoToken).safeTransferFrom(msg.sender, poolEscrow, amount); + + // start calculating account rewards with updated deposit amount + bool rewardsDisabled = rewardToken.updateRewardCheckpoint(msg.sender); + if (rewardsDisabled) { + // update merkle distributor principal if account has disabled rewards + distributorPrincipal = distributorPrincipal.add(amount); + } + + totalDeposits = totalDeposits.add(amount); + deposits[msg.sender] = deposits[msg.sender].add(amount); + + emit Transfer(address(0), msg.sender, amount); + } +} diff --git a/contracts/mocks/VaultMock.sol b/contracts/mocks/VaultMock.sol new file mode 100644 index 00000000..002119de --- /dev/null +++ b/contracts/mocks/VaultMock.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +pragma solidity 0.7.5; + +import {IRewardToken} from "../interfaces/IRewardToken.sol"; + + +contract VaultMock { + event Migrated(address receiver, uint256 assets, uint256 shares); + + IRewardToken private rewardToken; + uint256 public migratedAssets; + + constructor(address _rewardToken) { + rewardToken = IRewardToken(_rewardToken); + } + + function updateTotalRewards(int256 rewardsDelta) external { + rewardToken.updateTotalRewards(rewardsDelta); + } + + function migrate(address receiver, uint256 assets) external returns (uint256 shares) { + shares = assets; + migratedAssets = assets; + emit Migrated(receiver, assets, shares); + } +} diff --git a/contracts/pool/Pool.sol b/contracts/pool/Pool.sol index ba6b2350..d9dc1ed3 100644 --- a/contracts/pool/Pool.sol +++ b/contracts/pool/Pool.sol @@ -5,15 +5,11 @@ pragma abicoder v2; import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/SafeERC20Upgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol"; import "../presets/OwnablePausableUpgradeable.sol"; import "../interfaces/IStakedToken.sol"; import "../interfaces/IDepositContract.sol"; -import "../interfaces/IPoolValidators.sol"; import "../interfaces/IPool.sol"; -import "../interfaces/IStakedToken.sol"; import "../interfaces/IPoolValidators.sol"; -import "../interfaces/IGNOToken.sol"; import "../interfaces/IMGNOWrapper.sol"; /** @@ -23,10 +19,6 @@ import "../interfaces/IMGNOWrapper.sol"; */ contract Pool is IPool, OwnablePausableUpgradeable { using SafeERC20Upgradeable for IERC20Upgradeable; - using SafeMathUpgradeable for uint256; - - // @dev Validator deposit amount. - uint256 public constant override VALIDATOR_TOTAL_DEPOSIT = 32 ether; // @dev Address of the GNO <-> mGNO wrapper. address public constant override MGNO_WRAPPER = 0x647507A70Ff598F386CB96ae5046486389368C66; @@ -37,17 +29,17 @@ contract Pool is IPool, OwnablePausableUpgradeable { // @dev Address of the mGNO token. address public constant override MGNO_TOKEN = 0x722fc4DAABFEaff81b97894fC623f91814a1BF68; - // @dev base unit. - uint256 internal constant WAD = 1e18; + // @dev Address of the PoolEscrow contract. + address public immutable override poolEscrow; // @dev Total activated validators. - uint256 public override activatedValidators; + uint256 private activatedValidators; // @dev Pool validator withdrawal credentials. - bytes32 public override withdrawalCredentials; + bytes32 private withdrawalCredentials; // @dev Address of the GBC Deposit Contract. - IDepositContract public override validatorRegistration; + IDepositContract private validatorRegistration; // @dev Address of the StakedToken contract. IStakedToken private stakedToken; @@ -59,317 +51,41 @@ contract Pool is IPool, OwnablePausableUpgradeable { address private oracles; // @dev Maps senders to the validator index that it will be activated in. - mapping(address => mapping(uint256 => uint256)) public override activations; + mapping(address => mapping(uint256 => uint256)) private activations; // @dev Total pending validators. - uint256 public override pendingValidators; + uint256 private pendingValidators; // @dev Amount of deposited mGNO that is not considered for the activation period. - uint256 public override minActivatingDeposit; + uint256 private minActivatingDeposit; // @dev Pending validators percent limit. If it's not exceeded tokens can be minted immediately. - uint256 public override pendingValidatorsLimit; - - /** - * @dev See {IPool-initialize}. - */ - function initialize( - address admin, - bytes32 _withdrawalCredentials, - address _validatorRegistration, - address _stakedToken, - address _validators, - address _oracles, - uint256 _minActivatingDeposit, - uint256 _pendingValidatorsLimit - ) - external override initializer - { - require(admin != address(0), "Pool: invalid admin address"); - require(_withdrawalCredentials != "", "Pool: invalid withdrawal credentials"); - require(_validatorRegistration != address(0), "Pool: invalid ValidatorRegistration address"); - require(_stakedToken != address(0), "Pool: invalid StakedToken address"); - require(_validators != address(0), "Pool: invalid Validators address"); - require(_oracles != address(0), "Pool: invalid Oracles address"); - require(_pendingValidatorsLimit < 1e4, "Pool: invalid limit"); - - // initialize admin user - __OwnablePausableUpgradeable_init(admin); - - withdrawalCredentials = _withdrawalCredentials; - validatorRegistration = IDepositContract(_validatorRegistration); - stakedToken = IStakedToken(_stakedToken); - validators = IPoolValidators(_validators); - oracles = _oracles; - - minActivatingDeposit = _minActivatingDeposit; - emit MinActivatingDepositUpdated(_minActivatingDeposit, msg.sender); - - pendingValidatorsLimit = _pendingValidatorsLimit; - emit PendingValidatorsLimitUpdated(_pendingValidatorsLimit, msg.sender); - - // approve transfers to the validator registration contract - IERC20Upgradeable(MGNO_TOKEN).safeApprove(_validatorRegistration, type(uint256).max); - } - - /** - * @dev See {IPool-setMinActivatingDeposit}. - */ - function setMinActivatingDeposit(uint256 newMinActivatingDeposit) external override onlyAdmin { - minActivatingDeposit = newMinActivatingDeposit; - emit MinActivatingDepositUpdated(newMinActivatingDeposit, msg.sender); - } - - /** - * @dev See {IPool-setPendingValidatorsLimit}. - */ - function setPendingValidatorsLimit(uint256 newPendingValidatorsLimit) external override onlyAdmin { - require(newPendingValidatorsLimit < 1e4, "Pool: invalid limit"); - pendingValidatorsLimit = newPendingValidatorsLimit; - emit PendingValidatorsLimitUpdated(newPendingValidatorsLimit, msg.sender); - } - - /** - * @dev See {IPool-setActivatedValidators}. - */ - function setActivatedValidators(uint256 newActivatedValidators) external override { - require(msg.sender == oracles || hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Pool: access denied"); - - // subtract activated validators from pending validators - pendingValidators = pendingValidators.sub(newActivatedValidators.sub(activatedValidators)); - activatedValidators = newActivatedValidators; - emit ActivatedValidatorsUpdated(newActivatedValidators, msg.sender); - } - - /** - * @dev See {IPool-calculateGNO}. - */ - function calculateGNO(uint256 mgnoIn) public view override returns (uint256) { - // fetch MGNO <-> GNO conversion rate - uint256 rate = IMGNOWrapper(MGNO_WRAPPER).tokenRate(GNO_TOKEN); - return mgnoIn.mul(WAD).div(rate); - } - - /** - * @dev See {IPool-stakeGNO}. - */ - function stakeGNO( - uint256 amount, - address recipient, - address referredBy, - bool hasRevenueShare - ) - external override - { - // mint staked tokens - if (recipient != address(0)) { - _stake(recipient, amount); - } else { - _stake(msg.sender, amount); - } - - // withdraw GNO tokens from the user - IERC20Upgradeable(GNO_TOKEN).safeTransferFrom(msg.sender, address(this), amount); - - // convert GNO tokens to mGNO - bool success = IGNOToken(GNO_TOKEN).transferAndCall(MGNO_WRAPPER, amount, ""); - require(success, "Pool: failed to convert tokens"); - - // emit events for tracking referrers or partners - if (referredBy != address(0)) { - if (hasRevenueShare) { - emit StakedWithPartner(referredBy, amount); - } else { - emit StakedWithReferrer(referredBy, amount); - } - } - } - - /** - * @dev See {IPool-stakeGNOWithPermit}. - */ - function stakeGNOWithPermit( - uint256 amount, - address recipient, - address referredBy, - bool hasRevenueShare, - uint256 nonce, - uint256 expiry, - uint8 v, - bytes32 r, - bytes32 s - ) - external override - { - // mint staked tokens - if (recipient != address(0)) { - _stake(recipient, amount); - } else { - _stake(msg.sender, amount); - } - - // approve transfers - IGNOToken(GNO_TOKEN).permit( - msg.sender, - address(this), - nonce, - expiry, - true, - v, - r, - s - ); - - // withdraw GNO tokens from the user - IERC20Upgradeable(GNO_TOKEN).safeTransferFrom(msg.sender, address(this), amount); - - // convert GNO tokens to mGNO - bool success = IGNOToken(GNO_TOKEN).transferAndCall(MGNO_WRAPPER, amount, ""); - require(success, "Pool: failed to convert tokens"); - - // emit events for tracking referrers or partners - if (referredBy != address(0)) { - if (hasRevenueShare) { - emit StakedWithPartner(referredBy, amount); - } else { - emit StakedWithReferrer(referredBy, amount); - } - } + uint256 private pendingValidatorsLimit; + + /** + * @dev Constructor + * @dev Since the immutable variable value is stored in the bytecode, + * its value would be shared among all proxies pointing to a given contract instead of each proxy’s storage. + * @param _poolEscrow Address of the PoolEscrow contract. + */ + constructor(address _poolEscrow) { + poolEscrow = _poolEscrow; } /** - * @dev See {IPool-stakeMGNO}. + * @dev See {IPool-transferToPoolEscrow}. */ - function stakeMGNO( - uint256 amount, - address recipient, - address referredBy, - bool hasRevenueShare - ) - external override - { - // convert mGNO amount to GNO - uint256 convertedAmount = calculateGNO(amount); - - // mint staked tokens - if (recipient != address(0)) { - _stake(recipient, convertedAmount); - } else { - _stake(msg.sender, convertedAmount); + function transferToPoolEscrow() external override { + // fetch balances + uint256 mGnoBalance = IERC20Upgradeable(MGNO_TOKEN).balanceOf(address(this)); + if (mGnoBalance > 0) { + // convert mGNO to GNO + IMGNOWrapper(MGNO_WRAPPER).unwrap(GNO_TOKEN, mGnoBalance); } - - // transfer mGNO tokens from the user - IERC20Upgradeable(MGNO_TOKEN).safeTransferFrom(msg.sender, address(this), amount); - - // emit events for tracking referrers or partners - if (referredBy != address(0)) { - if (hasRevenueShare) { - emit StakedWithPartner(referredBy, convertedAmount); - } else { - emit StakedWithReferrer(referredBy, convertedAmount); - } - } - } - - function _stake(address recipient, uint256 value) internal whenNotPaused { - require(recipient != address(0), "Pool: invalid recipient"); - require(value > 0, "Pool: invalid deposit amount"); - - // mint tokens for small deposits immediately - if (value <= minActivatingDeposit) { - stakedToken.mint(recipient, value); - return; - } - - // mint tokens if current pending validators limit is not exceed - uint256 poolBalance = IERC20Upgradeable(MGNO_TOKEN).balanceOf(address(this)).add(value); - uint256 _pendingValidators = pendingValidators.add((poolBalance).div(VALIDATOR_TOTAL_DEPOSIT)); - uint256 _activatedValidators = activatedValidators; // gas savings - uint256 validatorIndex = _activatedValidators.add(_pendingValidators); - if (validatorIndex.mul(1e4) <= _activatedValidators.mul(pendingValidatorsLimit.add(1e4))) { - stakedToken.mint(recipient, value); - } else { - // lock deposit amount until validator activated - activations[recipient][validatorIndex] = activations[recipient][validatorIndex].add(value); - emit ActivationScheduled(recipient, validatorIndex, value); - } - } - - /** - * @dev See {IPool-canActivate}. - */ - function canActivate(uint256 validatorIndex) external view override returns (bool) { - return validatorIndex.mul(1e4) <= activatedValidators.mul(pendingValidatorsLimit.add(1e4)); - } - - /** - * @dev See {IPool-activate}. - */ - function activate(address account, uint256 validatorIndex) external override whenNotPaused { - uint256 activatedAmount = _activateAmount( - account, - validatorIndex, - activatedValidators.mul(pendingValidatorsLimit.add(1e4)) - ); - - stakedToken.mint(account, activatedAmount); - } - - /** - * @dev See {IPool-activateMultiple}. - */ - function activateMultiple(address account, uint256[] calldata validatorIndexes) external override whenNotPaused { - uint256 toMint; - uint256 maxValidatorIndex = activatedValidators.mul(pendingValidatorsLimit.add(1e4)); - for (uint256 i = 0; i < validatorIndexes.length; i++) { - uint256 activatedAmount = _activateAmount(account, validatorIndexes[i], maxValidatorIndex); - toMint = toMint.add(activatedAmount); + uint256 gnoBalance = IERC20Upgradeable(GNO_TOKEN).balanceOf(address(this)); + if (gnoBalance > 0) { + // transfer GNO to the PoolEscrow + IERC20Upgradeable(GNO_TOKEN).safeTransfer(poolEscrow, gnoBalance); } - stakedToken.mint(account, toMint); - } - - function _activateAmount( - address account, - uint256 validatorIndex, - uint256 maxValidatorIndex - ) - internal returns (uint256 amount) - { - require(validatorIndex.mul(1e4) <= maxValidatorIndex, "Pool: validator is not active yet"); - - amount = activations[account][validatorIndex]; - require(amount > 0, "Pool: invalid validator index"); - - delete activations[account][validatorIndex]; - emit Activated(account, validatorIndex, amount, msg.sender); - } - - /** - * @dev See {IPool-registerValidator}. - */ - function registerValidator(IPoolValidators.DepositData calldata depositData) external override whenNotPaused { - require(msg.sender == address(validators), "Pool: access denied"); - require(depositData.withdrawalCredentials == withdrawalCredentials, "Pool: invalid withdrawal credentials"); - - // update number of pending validators - pendingValidators = pendingValidators.add(1); - emit ValidatorRegistered(depositData.publicKey, depositData.operator); - - // register validator - validatorRegistration.deposit( - depositData.publicKey, - abi.encodePacked(depositData.withdrawalCredentials), - depositData.signature, - depositData.depositDataRoot, - VALIDATOR_TOTAL_DEPOSIT - ); - } - - /** - * @dev See {IPool-refund}. - */ - function refund(uint256 amount) external override onlyAdmin { - IERC20Upgradeable(MGNO_TOKEN).safeTransferFrom(msg.sender, address(this), amount); - emit Refunded(msg.sender, amount); } } diff --git a/contracts/pool/PoolValidators.sol b/contracts/pool/PoolValidators.sol deleted file mode 100644 index 5ba4dd5b..00000000 --- a/contracts/pool/PoolValidators.sol +++ /dev/null @@ -1,147 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only - -pragma solidity 0.7.5; -pragma abicoder v2; - -import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/cryptography/MerkleProofUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; -import "../presets/OwnablePausableUpgradeable.sol"; -import "../interfaces/IPoolValidators.sol"; -import "../interfaces/IPool.sol"; - -/** - * @title PoolValidators - * - * @dev PoolValidators contract keeps track of the pool validators' deposit data and onboards new operators. - */ -contract PoolValidators is IPoolValidators, OwnablePausableUpgradeable, ReentrancyGuardUpgradeable { - using AddressUpgradeable for address payable; - using SafeMathUpgradeable for uint256; - - // Maps hash of the validator public key to whether it is registered. - mapping(bytes32 => bool) public override isValidatorRegistered; - - // Maps operator address to its data. - mapping(address => Operator) private operators; - - // @dev Address of the Pool contract. - IPool private pool; - - // @dev Address of the Oracles contract. - address private oracles; - - /** - * @dev See {IPoolValidators-initialize}. - */ - function initialize(address _admin, address _pool, address _oracles) external override initializer { - require(_admin != address(0), "Pool: invalid admin address"); - require(_pool != address(0), "Pool: invalid Pool address"); - require(_oracles != address(0), "Pool: invalid Oracles address"); - - __OwnablePausableUpgradeable_init(_admin); - pool = IPool(_pool); - oracles = _oracles; - } - - /** - * @dev See {IPoolValidators-getOperator}. - */ - function getOperator(address _operator) external view override returns (bytes32, bool) { - Operator storage operator = operators[_operator]; - return ( - operator.depositDataMerkleRoot, - operator.committed - ); - } - - /** - * @dev See {IPoolValidators-addOperator}. - */ - function addOperator( - address _operator, - bytes32 depositDataMerkleRoot, - string calldata depositDataMerkleProofs - ) - external override onlyAdmin whenNotPaused - { - require(_operator != address(0), "PoolValidators: invalid operator"); - // merkle root and proofs must be validated off chain prior submitting the transaction - require(depositDataMerkleRoot != "", "PoolValidators: invalid merkle root"); - require(bytes(depositDataMerkleProofs).length != 0, "PoolValidators: invalid merkle proofs"); - - // load operator - Operator storage operator = operators[_operator]; - require(operator.depositDataMerkleRoot != depositDataMerkleRoot, "PoolValidators: same merkle root"); - - // update operator - operator.depositDataMerkleRoot = depositDataMerkleRoot; - operator.committed = false; - - emit OperatorAdded( - _operator, - depositDataMerkleRoot, - depositDataMerkleProofs - ); - } - - /** - * @dev See {IPoolValidators-commitOperator}. - */ - function commitOperator() external override whenNotPaused { - // mark operator as committed - Operator storage operator = operators[msg.sender]; - require(operator.depositDataMerkleRoot != "" && !operator.committed, "PoolValidators: invalid operator"); - operator.committed = true; - - emit OperatorCommitted(msg.sender); - } - - /** - * @dev See {IPoolValidators-removeOperator}. - */ - function removeOperator(address _operator) external override whenNotPaused { - require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender) || msg.sender == _operator, "PoolValidators: access denied"); - - Operator storage operator = operators[_operator]; - require(operator.depositDataMerkleRoot != "", "PoolValidators: invalid operator"); - - // clean up operator - delete operators[_operator]; - - emit OperatorRemoved(msg.sender, _operator); - } - - /** - * @dev See {IPoolValidators-registerValidator}. - */ - function registerValidator(DepositData calldata depositData, bytes32[] calldata merkleProof) external override { - require(msg.sender == oracles, "PoolValidators: access denied"); - - // mark validator as registered -> prevents from registering the same validator twice - bytes32 validatorId = keccak256(abi.encode(depositData.publicKey)); - require(!isValidatorRegistered[validatorId], "PoolValidators: validator already registered"); - isValidatorRegistered[validatorId] = true; - - // fetch deposit data merkle root - Operator storage operator = operators[depositData.operator]; - bytes32 depositDataMerkleRoot = operator.depositDataMerkleRoot; - require(depositDataMerkleRoot != "" && operator.committed, "PoolValidators: invalid operator"); - - // check whether provided deposit data was previously approved - bytes32 node = keccak256(abi.encode( - depositData.publicKey, - depositData.withdrawalCredentials, - depositData.signature, - depositData.depositDataRoot - )); - require( - MerkleProofUpgradeable.verify(merkleProof, depositDataMerkleRoot, node), - "PoolValidators: invalid merkle proof" - ); - - // register validator - pool.registerValidator(depositData); - } -} diff --git a/contracts/tokens/RewardToken.sol b/contracts/tokens/RewardToken.sol index 8722a995..6ed426cd 100644 --- a/contracts/tokens/RewardToken.sol +++ b/contracts/tokens/RewardToken.sol @@ -8,7 +8,7 @@ import "../presets/OwnablePausableUpgradeable.sol"; import "../interfaces/IStakedToken.sol"; import "../interfaces/IRewardToken.sol"; import "../interfaces/IMerkleDistributor.sol"; -import "../interfaces/IOracles.sol"; +import "../interfaces/IGnoGenesisVault.sol"; import "./ERC20PermitUpgradeable.sol"; /** @@ -21,6 +21,9 @@ contract RewardToken is IRewardToken, OwnablePausableUpgradeable, ERC20PermitUpg using SafeMathUpgradeable for uint256; using SafeCastUpgradeable for uint256; + // @dev Address of the Vault contract. + address public immutable override vault; + // @dev Address of the StakedToken contract. IStakedToken private stakedToken; @@ -51,38 +54,17 @@ contract RewardToken is IRewardToken, OwnablePausableUpgradeable, ERC20PermitUpg // @dev Maps account address to whether rewards are distributed through the merkle distributor. mapping(address => bool) public override rewardsDisabled; - /** - * @dev See {IRewardToken-initialize}. - */ - function initialize( - address admin, - address _stakedToken, - address _oracles, - address _protocolFeeRecipient, - uint256 _protocolFee, - address _merkleDistributor - ) - external override initializer - { - require(admin != address(0), "RewardToken: invalid admin address"); - require(_stakedToken != address(0), "RewardToken: invalid StakedToken address"); - require(_oracles != address(0), "RewardToken: invalid Oracles address"); - require(_protocolFee < 1e4, "RewardToken: invalid protocol fee"); - require(_merkleDistributor != address(0), "RewardToken: invalid MerkleDistributor address"); - - __OwnablePausableUpgradeable_init(admin); - __ERC20_init("StakeWise Reward GNO", "rGNO"); - __ERC20Permit_init("StakeWise Reward GNO"); - - stakedToken = IStakedToken(_stakedToken); - oracles = _oracles; - merkleDistributor = _merkleDistributor; - - protocolFeeRecipient = _protocolFeeRecipient; - emit ProtocolFeeRecipientUpdated(_protocolFeeRecipient); - - protocolFee = _protocolFee; - emit ProtocolFeeUpdated(_protocolFee); + // @dev Total amount of penalties received. + uint256 public override totalPenalty; + + /** + * @dev Constructor + * @dev Since the immutable variable value is stored in the bytecode, + * its value would be shared among all proxies pointing to a given contract instead of each proxy’s storage. + * @param _vault Address of the StakeWise V3 vault. + */ + constructor(address _vault) { + vault = _vault; } /** @@ -127,6 +109,13 @@ contract RewardToken is IRewardToken, OwnablePausableUpgradeable, ERC20PermitUpg return totalRewards; } + /** + * @dev See {IRewardToken-totalAssets}. + */ + function totalAssets() public view override returns (uint256) { + return uint256(totalRewards).add(stakedToken.totalSupply()); + } + /** * @dev See {IERC20-balanceOf}. */ @@ -233,17 +222,35 @@ contract RewardToken is IRewardToken, OwnablePausableUpgradeable, ERC20PermitUpg /** * @dev See {IRewardToken-updateTotalRewards}. */ - function updateTotalRewards(uint256 newTotalRewards) external override { - require(msg.sender == oracles, "RewardToken: access denied"); + function updateTotalRewards(int256 rewardsDelta) external override { + require(msg.sender == address(vault), "RewardToken: access denied"); + + uint256 periodRewards; + if (rewardsDelta > 0) { + periodRewards = uint256(rewardsDelta); + uint256 _totalPenalty = totalPenalty; // gas savings + if (periodRewards <= _totalPenalty) { + totalPenalty = _totalPenalty.sub(periodRewards); + periodRewards = 0; + } else if (_totalPenalty > 0) { + periodRewards = periodRewards.sub(_totalPenalty); + totalPenalty = 0; + } + } else if (rewardsDelta < 0) { + uint256 _totalPenalty = totalPenalty; // gas savings + _totalPenalty = _totalPenalty.add(uint256(-rewardsDelta)); + require(_totalPenalty <= totalAssets(), "RewardToken: invalid penalty amount"); + totalPenalty = _totalPenalty; + } - uint256 periodRewards = newTotalRewards.sub(totalRewards); if (periodRewards == 0) { lastUpdateBlockNumber = block.number; - emit RewardsUpdated(0, newTotalRewards, rewardPerToken, 0, 0); + emit RewardsUpdated(0, totalRewards, rewardPerToken, 0, 0); return; } // calculate protocol reward and new reward per token amount + uint256 newTotalRewards = uint256(totalRewards).add(periodRewards); uint256 protocolReward = periodRewards.mul(protocolFee).div(1e4); uint256 prevRewardPerToken = rewardPerToken; uint256 newRewardPerToken = prevRewardPerToken.add(periodRewards.sub(protocolReward).mul(1e18).div(stakedToken.totalDeposits())); @@ -286,6 +293,43 @@ contract RewardToken is IRewardToken, OwnablePausableUpgradeable, ERC20PermitUpg ); } + function _burn(uint256 amount) private { + uint128 _rewardPerToken = rewardPerToken; // gas savings + checkpoints[msg.sender] = Checkpoint({ + reward: _balanceOf(msg.sender, _rewardPerToken).sub(amount).toUint128(), + rewardPerToken: _rewardPerToken + }); + totalRewards = uint256(totalRewards).sub(amount).toUint128(); + emit Transfer(msg.sender, address(0), amount); + } + + /** + * @dev See {IRewardToken-migrate}. + */ + function migrate(address receiver, uint256 principal, uint256 reward) external override { + require(receiver != address(0), "RewardToken: invalid receiver"); + require(block.number > lastUpdateBlockNumber, "RewardToken: cannot migrate during rewards update"); + + // calculate amount of assets to migrate + uint256 assets = principal.add(reward); + + uint256 _totalPenalty = totalPenalty; // gas savings + if (_totalPenalty > 0) { + uint256 _totalAssets = totalAssets(); // gas savings + // apply penalty to assets + uint256 assetsAfterPenalty = assets.mul(_totalAssets.sub(_totalPenalty)).div(_totalAssets); + totalPenalty = _totalPenalty.add(assetsAfterPenalty).sub(assets); + assets = assetsAfterPenalty; + } + require(assets > 0, "RewardToken: zero assets"); + + // burn rewards and principal + if (reward > 0) _burn(reward); + if (principal > 0) stakedToken.burn(msg.sender, principal); + + IGnoGenesisVault(vault).migrate(receiver, assets); + } + /** * @dev See {IRewardToken-claim}. */ diff --git a/contracts/tokens/StakedToken.sol b/contracts/tokens/StakedToken.sol index 0475d391..7e390ff8 100644 --- a/contracts/tokens/StakedToken.sol +++ b/contracts/tokens/StakedToken.sol @@ -20,39 +20,17 @@ contract StakedToken is IStakedToken, OwnablePausableUpgradeable, ERC20PermitUpg uint256 public override totalDeposits; // @dev Maps account address to its deposit amount. - mapping(address => uint256) private deposits; + mapping(address => uint256) internal deposits; // @dev Address of the Pool contract. address private pool; // @dev Address of the RewardToken contract. - IRewardToken private rewardToken; + IRewardToken internal rewardToken; // @dev The principal amount of the distributor. uint256 public override distributorPrincipal; - /** - * @dev See {IStakedToken-initialize}. - */ - function initialize( - address admin, - address _pool, - address _rewardToken - ) - external override initializer - { - require(admin != address(0), "StakedToken: invalid admin address"); - require(_pool != address(0), "StakedToken: invalid Pool address"); - require(_rewardToken != address(0), "StakedToken: invalid RewardToken address"); - - __OwnablePausableUpgradeable_init(admin); - __ERC20_init("StakeWise Staked GNO", "sGNO"); - __ERC20Permit_init("StakeWise Staked GNO"); - - pool = _pool; - rewardToken = IRewardToken(_rewardToken); - } - /** * @dev See {IERC20-totalSupply}. */ @@ -113,21 +91,23 @@ contract StakedToken is IStakedToken, OwnablePausableUpgradeable, ERC20PermitUpg } /** - * @dev See {IStakedToken-mint}. + * @dev See {IStakedToken-burn}. */ - function mint(address account, uint256 amount) external override { - require(msg.sender == pool, "StakedToken: access denied"); + function burn(address account, uint256 amount) external override { + IRewardToken _rewardToken = rewardToken; // gas savings + require(msg.sender == address(_rewardToken), "StakedToken: access denied"); + require(account != address(0), "StakedToken: invalid account"); // start calculating account rewards with updated deposit amount bool rewardsDisabled = rewardToken.updateRewardCheckpoint(account); if (rewardsDisabled) { // update merkle distributor principal if account has disabled rewards - distributorPrincipal = distributorPrincipal.add(amount); + distributorPrincipal = distributorPrincipal.sub(amount); } - totalDeposits = totalDeposits.add(amount); - deposits[account] = deposits[account].add(amount); + totalDeposits = totalDeposits.sub(amount); + deposits[account] = deposits[account].sub(amount); - emit Transfer(address(0), account, amount); + emit Transfer(account, address(0), amount); } } diff --git a/deployments/index.js b/deployments/index.js index 83339ee5..9c152ee4 100644 --- a/deployments/index.js +++ b/deployments/index.js @@ -1,14 +1,80 @@ -const { contracts } = require('./settings'); +const { white } = require('chalk'); +const { ethers, upgrades, config } = require('hardhat'); +const { contracts, contractSettings } = require('./settings'); +const { + silenceWarnings, +} = require('@openzeppelin/upgrades-core/dist/utils/log'); + +function log(message) { + if (config != null && config.suppressLogs !== true) { + console.log(message); + } +} + +async function upgradePool(signer) { + const Pool = await ethers.getContractFactory('Pool', signer); + + // upgrade Pool to new implementation + const proxy = await upgrades.upgradeProxy(contracts.pool, Pool, { + unsafeAllow: ['state-variable-immutable', 'constructor'], + constructorArgs: [contracts.poolEscrow], + }); + await proxy.deployed(); +} + +async function upgradeOracles(signer) { + const Oracles = await ethers.getContractFactory('Oracles', signer); + + // upgrade Pool to new implementation + const proxy = await upgrades.upgradeProxy(contracts.oracles, Oracles); + await proxy.deployed(); +} + +async function upgradeRewardToken(signer, vault) { + const RewardToken = await ethers.getContractFactory('RewardToken', signer); + // upgrade RewardToken to new implementation + const proxy = await upgrades.upgradeProxy( + contracts.rewardToken, + RewardToken, + { + unsafeAllow: ['state-variable-immutable', 'constructor'], + constructorArgs: [vault], + } + ); + await proxy.deployed(); +} + +async function upgradeStakedToken(signer) { + const StakedToken = await ethers.getContractFactory('StakedToken', signer); + // upgrade RewardToken to new implementation + const proxy = await upgrades.upgradeProxy(contracts.stakedToken, StakedToken); + await proxy.deployed(); +} async function deployContracts() { return contracts; } -async function upgradeContracts() { +async function upgradeContracts(vault = contracts.vault) { + const signer = await ethers.provider.getSigner(contractSettings.admin); + silenceWarnings(); + await upgradeOracles(signer); + log(white('Upgraded Oracles contract')); + + await upgradePool(signer); + log(white('Upgraded Pool contract')); + + await upgradeRewardToken(signer, vault); + log(white('Upgraded RewardToken contract')); + + await upgradeStakedToken(signer); + log(white('Upgraded StakedToken contract')); + return contracts; } module.exports = { deployContracts, upgradeContracts, + upgradeRewardToken, }; diff --git a/hardhat.config.js b/hardhat.config.js index 3b19608b..2bd9cc2a 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -109,7 +109,7 @@ module.exports = { networks: { hardhat: { forking: { - url: 'https://dai.poa.network/', + url: 'https://rpc.gnosischain.com', chainId: 100, }, accounts: { @@ -117,7 +117,7 @@ module.exports = { }, }, gnosis: { - url: 'https://dai.poa.network/', + url: 'https://rpc.gnosischain.com', }, local: { url: 'http://localhost:8545', @@ -140,7 +140,6 @@ module.exports = { 'IERC20Upgradeable', 'Pool', 'PoolEscrow', - 'PoolValidators', 'RewardToken', 'StakedToken', 'MerkleDistributor', diff --git a/test/MerkleDistributor.test.js b/test/MerkleDistributor.test.js index 4bf259f9..7e2b8b98 100644 --- a/test/MerkleDistributor.test.js +++ b/test/MerkleDistributor.test.js @@ -9,7 +9,7 @@ const { } = require('@openzeppelin/test-helpers'); const { MerkleTree } = require('merkletreejs'); const keccak256 = require('keccak256'); -const { upgradeContracts } = require('../deployments'); +const { upgradeContracts, upgradeRewardToken } = require('../deployments'); const { contractSettings, contracts } = require('../deployments/settings'); const { stopImpersonatingAccount, @@ -18,7 +18,7 @@ const { setupOracleAccounts, setTotalRewards, setMerkleRoot, - stakeGNO, + addStakedToken, mintTokens, } = require('./utils'); @@ -28,7 +28,6 @@ const RewardToken = artifacts.require('RewardToken'); const StakedToken = artifacts.require('StakedToken'); const MulticallMock = artifacts.require('MulticallMock'); const Oracles = artifacts.require('Oracles'); -const Pool = artifacts.require('Pool'); const account1 = '0x7981973BF488Ea610AF41dDB9DFeF1f8095ECF56'; const account2 = '0xB3a69b7cdd7510D51c4CCc2c0fC105021A92Fc5D'; @@ -37,7 +36,7 @@ const account3 = '0xd1b91Ac5eb55f30D742751f4Ae4437F738eB8F6b'; const distributorGNOReward = ether('25.1777'); const distributorTokenReward = ether('14.86062535'); -contract('Merkle Distributor', ([beneficiary, anyone, ...otherAccounts]) => { +contract('Merkle Distributor', ([beneficiary, anyone, vault, ...accounts]) => { const admin = contractSettings.admin; let merkleDistributor, amount, @@ -48,7 +47,6 @@ contract('Merkle Distributor', ([beneficiary, anyone, ...otherAccounts]) => { oracles, oracleAccounts, prevDistributorBalance, - pool, merkleRoot, merkleProofs; @@ -58,13 +56,10 @@ contract('Merkle Distributor', ([beneficiary, anyone, ...otherAccounts]) => { await impersonateAccount(admin); await send.ether(anyone, admin, ether('5')); - let upgradedContracts = await upgradeContracts(); + let upgradedContracts = await upgradeContracts(vault); amount = ether('10'); durationInBlocks = new BN(1000); token = await IGCToken.at(contracts.stakeWiseToken); - merkleDistributor = await MerkleDistributor.at( - upgradedContracts.merkleDistributor - ); rewardToken = await RewardToken.at(upgradedContracts.rewardToken); stakedToken = await StakedToken.at(upgradedContracts.stakedToken); @@ -75,9 +70,8 @@ contract('Merkle Distributor', ([beneficiary, anyone, ...otherAccounts]) => { oracleAccounts = await setupOracleAccounts({ admin, oracles, - accounts: otherAccounts, + accounts, }); - pool = await Pool.at(upgradedContracts.pool); prevDistributorBalance = await token.balanceOf(merkleDistributor.address); merkleProofs = { [account1]: { @@ -115,7 +109,7 @@ contract('Merkle Distributor', ([beneficiary, anyone, ...otherAccounts]) => { merkleProofs[account].proof = tree.getHexProof(keccak256(encoded)); } - await stakeGNO({ account: anyone, pool, amount: ether('1') }); + await addStakedToken(stakedToken, anyone, ether('1')); await send.ether(anyone, admin, ether('3000')); await mintTokens(token, admin, amount); }); @@ -396,13 +390,11 @@ contract('Merkle Distributor', ([beneficiary, anyone, ...otherAccounts]) => { let totalRewards = (await rewardToken.totalRewards()).add(ether('10')); await mintTokens(token, admin, amount); - await stakeGNO({ account: anyone, pool, amount: ether('1') }); + await addStakedToken(stakedToken, anyone, ether('1')); await setTotalRewards({ rewardToken, - oracles, - oracleAccounts, - pool, totalRewards, + vault, }); }); @@ -453,10 +445,7 @@ contract('Merkle Distributor', ([beneficiary, anyone, ...otherAccounts]) => { }); it('cannot claim twice', async () => { - await pool.setMinActivatingDeposit(constants.MAX_UINT256, { - from: admin, - }); - await stakeGNO({ account: anyone, pool, amount: ether('1000') }); + await addStakedToken(stakedToken, anyone, ether('100')); await stakedToken.toggleRewards(anyone, true, { from: admin, }); @@ -473,10 +462,8 @@ contract('Merkle Distributor', ([beneficiary, anyone, ...otherAccounts]) => { await setTotalRewards({ rewardToken, - oracles, - oracleAccounts, - pool, totalRewards, + vault, }); await token.transfer(merkleDistributor.address, distributorTokenReward, { @@ -504,18 +491,13 @@ contract('Merkle Distributor', ([beneficiary, anyone, ...otherAccounts]) => { }); it('can claim reward tokens', async () => { - await pool.setMinActivatingDeposit(constants.MAX_UINT256, { - from: admin, - }); - await stakeGNO({ account: anyone, pool, amount: ether('1000') }); + await addStakedToken(stakedToken, anyone, ether('1000')); await stakedToken.toggleRewards(anyone, true, { from: admin, }); await setTotalRewards({ rewardToken, - oracles, - oracleAccounts, - pool, + vault, totalRewards: ether('100000'), }); @@ -534,7 +516,7 @@ contract('Merkle Distributor', ([beneficiary, anyone, ...otherAccounts]) => { ); expect(distributorGNORewards).to.bignumber.greaterThan(new BN(0)); - let totalTransferredEthReward = new BN(0); + let totalTransferredGnoReward = new BN(0); for (const [account, { index, proof, tokens, amounts }] of Object.entries( merkleProofs )) { @@ -565,13 +547,13 @@ contract('Merkle Distributor', ([beneficiary, anyone, ...otherAccounts]) => { expect(await rewardToken.balanceOf(account)).to.be.bignumber.equal( balance1.add(new BN(amounts[0])) ); - totalTransferredEthReward = totalTransferredEthReward.add( + totalTransferredGnoReward = totalTransferredGnoReward.add( new BN(amounts[0]) ); expect( await rewardToken.balanceOf(constants.ZERO_ADDRESS) ).to.bignumber.equal( - distributorGNORewards.sub(totalTransferredEthReward) + distributorGNORewards.sub(totalTransferredGnoReward) ); expect(await token.balanceOf(account)).to.be.bignumber.equal( balance2.add(new BN(amounts[1])) @@ -579,13 +561,9 @@ contract('Merkle Distributor', ([beneficiary, anyone, ...otherAccounts]) => { } }); - // TODO: re-enable once on forked network - describe.skip('claiming within the same block', () => { - let multicallMock, - totalRewards, - activatedValidators, - rewardsSignatures, - merkleRootSignatures; + describe('claiming within the same block', () => { + const rewardsDelta = ether('10'); + let multicallMock, totalRewards, merkleRootSignatures; beforeEach(async () => { await setMerkleRoot({ @@ -607,7 +585,7 @@ contract('Merkle Distributor', ([beneficiary, anyone, ...otherAccounts]) => { from: admin, }); - await stakeGNO({ account: anyone, pool, amount: ether('1000') }); + await addStakedToken(stakedToken, anyone, ether('1000')); await stakedToken.toggleRewards(anyone, true, { from: admin, }); @@ -619,32 +597,15 @@ contract('Merkle Distributor', ([beneficiary, anyone, ...otherAccounts]) => { totalRewards = totalRewards.add( totalRewards.add(protocolFee.div(new BN(10000))) ); - activatedValidators = await pool.activatedValidators(); - // create rewards signature + // create merkle root signature let currentNonce = await oracles.currentRewardsNonce(); let encoded = defaultAbiCoder.encode( - ['uint256', 'uint256', 'uint256'], - [ - currentNonce.toString(), - activatedValidators.toString(), - totalRewards.toString(), - ] - ); - let candidateId = hexlify(keccak256(encoded)); - rewardsSignatures = []; - for (const oracleAccount of oracleAccounts) { - rewardsSignatures.push( - await web3.eth.sign(candidateId, oracleAccount) - ); - } - - // create merkle root signature - encoded = defaultAbiCoder.encode( ['uint256', 'string', 'bytes32'], - [currentNonce.add(new BN(1)).toString(), merkleProofs, merkleRoot] + [currentNonce.toString(), merkleProofs, merkleRoot] ); - candidateId = hexlify(keccak256(encoded)); + + let candidateId = hexlify(keccak256(encoded)); merkleRootSignatures = []; for (const oracleAccount of oracleAccounts) { merkleRootSignatures.push( @@ -655,13 +616,11 @@ contract('Merkle Distributor', ([beneficiary, anyone, ...otherAccounts]) => { it('cannot claim after total rewards update in the same block', async () => { const { index, amounts, tokens, proof } = merkleProofs[account1]; + const signer = await ethers.provider.getSigner(contractSettings.admin); + await upgradeRewardToken(signer, multicallMock.address); await expectRevert( multicallMock.updateTotalRewardsAndClaim( - { - totalRewards: totalRewards.toString(), - activatedValidators: activatedValidators.toString(), - signatures: rewardsSignatures, - }, + rewardsDelta, index, account1, tokens, @@ -675,35 +634,13 @@ contract('Merkle Distributor', ([beneficiary, anyone, ...otherAccounts]) => { ); }); - it('can claim before total rewards update in the same block', async () => { - const { index, amounts, tokens, proof } = merkleProofs[account1]; - await multicallMock.claimAndUpdateTotalRewards( - { - totalRewards: totalRewards.toString(), - activatedValidators: activatedValidators.toString(), - signatures: rewardsSignatures, - }, - index, - account1, - tokens, - amounts, - proof, - { - from: anyone, - } - ); - }); - it('cannot claim before merkle root update in the same block', async () => { const { index, amounts, tokens, proof } = merkleProofs[account1]; - await oracles.submitRewards( - totalRewards, - activatedValidators, - rewardsSignatures, - { - from: oracleAccounts[0], - } - ); + const signer = await ethers.provider.getSigner(contractSettings.admin); + await upgradeRewardToken(signer, vault); + await rewardToken.updateTotalRewards(rewardsDelta, { + from: vault, + }); await expectRevert( multicallMock.claimAndUpdateMerkleRoot( { merkleRoot, merkleProofs, signatures: merkleRootSignatures }, @@ -719,29 +656,6 @@ contract('Merkle Distributor', ([beneficiary, anyone, ...otherAccounts]) => { 'MerkleDistributor: merkle root updating' ); }); - - it('can claim after merkle root update in the same block', async () => { - await oracles.submitRewards( - totalRewards, - activatedValidators, - rewardsSignatures, - { - from: oracleAccounts[0], - } - ); - const { index, amounts, tokens, proof } = merkleProofs[account1]; - await multicallMock.updateMerkleRootAndClaim( - { merkleRoot, merkleProofs, signatures: merkleRootSignatures }, - index, - account1, - tokens, - amounts, - proof, - { - from: anyone, - } - ); - }); }); }); }); diff --git a/test/Roles.test.js b/test/Roles.test.js index 414e216c..5c5b4e74 100644 --- a/test/Roles.test.js +++ b/test/Roles.test.js @@ -16,7 +16,7 @@ const { const Roles = artifacts.require('Roles'); -contract('Roles', ([anyone, operator, partner]) => { +contract('Roles', ([anyone, operator, partner, vault]) => { const admin = contractSettings.admin; let revenueShare = new BN(3000); let roles; @@ -27,7 +27,7 @@ contract('Roles', ([anyone, operator, partner]) => { await impersonateAccount(admin); await send.ether(anyone, admin, ether('5')); - let upgradedContracts = await upgradeContracts(); + let upgradedContracts = await upgradeContracts(vault); roles = await Roles.at(upgradedContracts.roles); }); diff --git a/test/oracles/Oracles.test.js b/test/oracles/Oracles.test.js index cacb2a7c..57eadace 100644 --- a/test/oracles/Oracles.test.js +++ b/test/oracles/Oracles.test.js @@ -4,7 +4,6 @@ const { expectEvent, expectRevert, ether, - BN, send, } = require('@openzeppelin/test-helpers'); const { @@ -13,27 +12,20 @@ const { resetFork, setupOracleAccounts, setTotalRewards, - stakeGNO, } = require('../utils'); const { contractSettings } = require('../../deployments/settings'); -const { upgradeContracts } = require('../../deployments'); -const { - depositDataMerkleRoot, - depositData, -} = require('../pool/depositDataMerkleRoot'); +const { upgradeContracts, upgradeRewardToken } = require('../../deployments'); +const { ethers } = require('hardhat'); const RewardToken = artifacts.require('RewardToken'); const Oracles = artifacts.require('Oracles'); -const Pool = artifacts.require('Pool'); const MulticallMock = artifacts.require('MulticallMock'); const MerkleDistributor = artifacts.require('MerkleDistributor'); -const PoolValidators = artifacts.require('PoolValidators'); -const iDepositContract = artifacts.require('IDepositContract'); -contract('Oracles', ([_, anyone, operator, ...accounts]) => { +contract('Oracles', ([_, anyone, ...accounts]) => { let admin = contractSettings.admin; - let oracles, rewardToken, pool, merkleDistributor, poolValidators, contracts; - let [oracle, anotherOracle] = accounts; + let oracles, rewardToken, merkleDistributor, contracts; + let [oracle, anotherOracle, vault] = accounts; after(async () => stopImpersonatingAccount(admin)); @@ -41,15 +33,10 @@ contract('Oracles', ([_, anyone, operator, ...accounts]) => { await impersonateAccount(admin); await send.ether(anyone, admin, ether('5')); - contracts = await upgradeContracts(); - + contracts = await upgradeContracts(vault); oracles = await Oracles.at(contracts.oracles); - pool = await Pool.at(contracts.pool); rewardToken = await RewardToken.at(contracts.rewardToken); merkleDistributor = await MerkleDistributor.at(contracts.merkleDistributor); - poolValidators = await PoolValidators.at(contracts.poolValidators); - - await stakeGNO({ account: anyone, amount: ether('1'), pool }); }); afterEach(async () => resetFork()); @@ -136,151 +123,6 @@ contract('Oracles', ([_, anyone, operator, ...accounts]) => { }); }); - describe('rewards voting', () => { - let prevTotalRewards, - newTotalRewards, - currentNonce, - newActivatedValidators, - oracleAccounts, - candidateId, - signatures; - - beforeEach(async () => { - oracleAccounts = await setupOracleAccounts({ oracles, accounts, admin }); - prevTotalRewards = await rewardToken.totalRewards(); - newTotalRewards = prevTotalRewards.add(ether('10')); - currentNonce = await oracles.currentRewardsNonce(); - newActivatedValidators = (await pool.activatedValidators()).add( - await pool.pendingValidators() - ); - - let encoded = defaultAbiCoder.encode( - ['uint256', 'uint256', 'uint256'], - [ - currentNonce.toString(), - newActivatedValidators.toString(), - newTotalRewards.toString(), - ] - ); - candidateId = keccak256(encoded); - - signatures = []; - for (const oracleAccount of oracleAccounts) { - signatures.push(await web3.eth.sign(candidateId, oracleAccount)); - } - }); - - it('fails to submit when contract is paused', async () => { - await oracles.pause({ from: admin }); - expect(await oracles.paused()).equal(true); - - await expectRevert( - oracles.submitRewards( - newTotalRewards, - newActivatedValidators, - signatures, - { - from: oracleAccounts[0], - } - ), - 'Pausable: paused' - ); - }); - - it('fails to submit with not enough signatures', async () => { - await expectRevert( - oracles.submitRewards( - newTotalRewards, - newActivatedValidators, - signatures.slice(signatures.length - 1), - { - from: oracleAccounts[0], - } - ), - 'Oracles: invalid number of signatures' - ); - }); - - it('fails to submit with invalid signature', async () => { - signatures[0] = await web3.eth.sign(candidateId, anyone); - await expectRevert( - oracles.submitRewards( - newTotalRewards, - newActivatedValidators, - signatures, - { - from: oracleAccounts[0], - } - ), - 'Oracles: invalid signer' - ); - }); - - it('fails to submit with repeated signature', async () => { - let signature = signatures[0]; - await expectRevert( - oracles.submitRewards( - newTotalRewards, - newActivatedValidators, - Array(oracleAccounts.length).fill(signature), - { - from: oracleAccounts[0], - } - ), - 'Oracles: repeated signature' - ); - }); - - it('fails to submit without oracle role assigned', async () => { - await expectRevert( - oracles.submitRewards( - newTotalRewards, - newActivatedValidators, - signatures, - { - from: anyone, - } - ), - 'Oracles: access denied' - ); - }); - - it('submits data with enough signatures', async () => { - let receipt = await oracles.submitRewards( - newTotalRewards, - newActivatedValidators, - signatures, - { - from: oracleAccounts[0], - } - ); - - // check signatures - for (const oracleAccount of oracleAccounts) { - expectEvent(receipt, 'RewardsVoteSubmitted', { - oracle: oracleAccount, - totalRewards: newTotalRewards, - activatedValidators: newActivatedValidators, - nonce: currentNonce, - }); - } - - // check values updates - expect(await rewardToken.totalRewards()).to.bignumber.equal( - newTotalRewards - ); - - // update submitted - expect(await rewardToken.totalRewards()).to.bignumber.equal( - newTotalRewards - ); - expect(await pool.activatedValidators()).to.bignumber.equal( - newActivatedValidators - ); - expect(await pool.pendingValidators()).to.bignumber.equal(new BN(0)); - }); - }); - describe('merkle root voting', () => { const merkleRoot = '0xa3e724fce28a564a7908e40994bd8f48ed4470ffcab4c135fe661bcf5b15afe6'; @@ -292,10 +134,8 @@ contract('Oracles', ([_, anyone, operator, ...accounts]) => { let totalRewards = (await rewardToken.totalRewards()).add(ether('10')); oracleAccounts = await setupOracleAccounts({ oracles, accounts, admin }); await setTotalRewards({ + vault, rewardToken, - oracles, - oracleAccounts, - pool, totalRewards, }); @@ -416,38 +256,22 @@ contract('Oracles', ([_, anyone, operator, ...accounts]) => { it('fails to vote for total rewards and merkle root in same block', async () => { // deploy mocked oracle - let mockedOracle = await MulticallMock.new( + let multicallMock = await MulticallMock.new( oracles.address, contracts.stakedToken, contracts.rewardToken, merkleDistributor.address ); - await oracles.addOracle(mockedOracle.address, { + await oracles.addOracle(multicallMock.address, { from: admin, }); - - let totalRewards = (await rewardToken.totalRewards()).add(ether('10')); - let activatedValidators = await pool.activatedValidators(); - - // create rewards signatures - let currentNonce = await oracles.currentRewardsNonce(); - let encoded = defaultAbiCoder.encode( - ['uint256', 'uint256', 'uint256'], - [ - currentNonce.toString(), - activatedValidators.toString(), - totalRewards.toString(), - ] - ); - candidateId = keccak256(encoded); - let rewardSignatures = []; - for (const oracleAccount of oracleAccounts) { - rewardSignatures.push(await web3.eth.sign(candidateId, oracleAccount)); - } + const signer = await ethers.provider.getSigner(contractSettings.admin); + await upgradeRewardToken(signer, multicallMock.address); + const rewardsDelta = ether('10'); // create merkle root signatures currentNonce = await oracles.currentRewardsNonce(); - encoded = defaultAbiCoder.encode( + let encoded = defaultAbiCoder.encode( ['uint256', 'string', 'bytes32'], [currentNonce.toString(), merkleProofs, merkleRoot] ); @@ -460,12 +284,8 @@ contract('Oracles', ([_, anyone, operator, ...accounts]) => { } await expectRevert( - mockedOracle.updateTotalRewardsAndMerkleRoot( - { - totalRewards: totalRewards.toString(), - activatedValidators: activatedValidators.toString(), - signatures: rewardSignatures, - }, + multicallMock.updateTotalRewardsAndMerkleRoot( + rewardsDelta, { merkleRoot, merkleProofs, @@ -479,165 +299,4 @@ contract('Oracles', ([_, anyone, operator, ...accounts]) => { ); }); }); - - describe('validator voting', () => { - const depositDataMerkleProofs = - 'ipfs://QmehR8yCaKdHqHSxZMSJA5q2SWc8jTVCSKuVgbtqDEdXCH'; - let currentNonce, - oracleAccounts, - candidateId, - signatures, - validatorsDepositRoot; - let validatorsDepositData = [ - { - operator, - withdrawalCredentials: depositData[0].withdrawalCredentials, - depositDataRoot: depositData[0].depositDataRoot, - publicKey: depositData[0].publicKey, - signature: depositData[0].signature, - }, - { - operator, - withdrawalCredentials: depositData[1].withdrawalCredentials, - depositDataRoot: depositData[1].depositDataRoot, - publicKey: depositData[1].publicKey, - signature: depositData[1].signature, - }, - ]; - let merkleProofs = [depositData[0].merkleProof, depositData[1].merkleProof]; - - beforeEach(async () => { - await poolValidators.addOperator( - operator, - depositDataMerkleRoot, - depositDataMerkleProofs, - { - from: admin, - } - ); - await poolValidators.commitOperator({ - from: operator, - }); - oracleAccounts = await setupOracleAccounts({ oracles, accounts, admin }); - currentNonce = await oracles.currentValidatorsNonce(); - - let depositContract = await iDepositContract.at( - await pool.validatorRegistration() - ); - validatorsDepositRoot = await depositContract.get_deposit_root(); - - let encoded = defaultAbiCoder.encode( - [ - 'uint256', - 'tuple(address operator,bytes32 withdrawalCredentials,bytes32 depositDataRoot,bytes publicKey,bytes signature)[]', - 'bytes32', - ], - [currentNonce.toString(), validatorsDepositData, validatorsDepositRoot] - ); - candidateId = keccak256(encoded); - - signatures = []; - for (const oracleAccount of oracleAccounts) { - signatures.push(await web3.eth.sign(candidateId, oracleAccount)); - } - }); - - it('fails to submit when contract is paused', async () => { - await oracles.pause({ from: admin }); - expect(await oracles.paused()).equal(true); - - await expectRevert( - oracles.registerValidators( - validatorsDepositData, - merkleProofs, - validatorsDepositRoot, - signatures, - { - from: oracleAccounts[0], - } - ), - 'Pausable: paused' - ); - }); - - it('fails to submit with not enough signatures', async () => { - await expectRevert( - oracles.registerValidators( - validatorsDepositData, - merkleProofs, - validatorsDepositRoot, - signatures.slice(signatures.length - 1), - { - from: oracleAccounts[0], - } - ), - 'Oracles: invalid number of signatures' - ); - }); - - it('fails to submit with invalid signature', async () => { - signatures[0] = await web3.eth.sign(candidateId, anyone); - await expectRevert( - oracles.registerValidators( - validatorsDepositData, - merkleProofs, - validatorsDepositRoot, - signatures, - { - from: oracleAccounts[0], - } - ), - 'Oracles: invalid signer' - ); - }); - - it('fails to submit with repeated signature', async () => { - let signature = signatures[0]; - await expectRevert( - oracles.registerValidators( - validatorsDepositData, - merkleProofs, - validatorsDepositRoot, - Array(oracleAccounts.length).fill(signature), - { - from: oracleAccounts[0], - } - ), - 'Oracles: repeated signature' - ); - }); - - it('fails to submit without oracle role assigned', async () => { - await expectRevert( - oracles.registerValidators( - validatorsDepositData, - merkleProofs, - validatorsDepositRoot, - signatures, - { - from: anyone, - } - ), - 'Oracles: access denied' - ); - }); - - it('can vote for multiple validators', async () => { - await stakeGNO({ account: anyone, pool, amount: ether('2') }); - let receipt = await oracles.registerValidators( - validatorsDepositData, - merkleProofs, - validatorsDepositRoot, - signatures, - { - from: oracleAccounts[0], - } - ); - await expectEvent(receipt, 'RegisterValidatorsVoteSubmitted', { - sender: oracleAccounts[0], - oracles: oracleAccounts, - nonce: currentNonce, - }); - }); - }); }); diff --git a/test/pool/Pool.test.js b/test/pool/Pool.test.js new file mode 100644 index 00000000..f8ae3a20 --- /dev/null +++ b/test/pool/Pool.test.js @@ -0,0 +1,57 @@ +const { expect } = require('chai'); +const { ether, BN } = require('@openzeppelin/test-helpers'); +const { + stopImpersonatingAccount, + impersonateAccount, + resetFork, +} = require('../utils'); +const { upgradeContracts } = require('../../deployments'); +const { contractSettings, contracts } = require('../../deployments/settings'); + +const Pool = artifacts.require('Pool'); +const IGCToken = artifacts.require('IGCToken'); + +contract('Pool', (accounts) => { + const admin = contractSettings.admin; + let [vault] = accounts; + let pool, mgnoToken, gnoToken; + + after(async () => stopImpersonatingAccount(admin)); + + beforeEach(async () => { + await impersonateAccount(admin); + let upgradedContracts = await upgradeContracts(vault); + + mgnoToken = await IGCToken.at(contracts.MGNOToken); + gnoToken = await IGCToken.at(contracts.GNOToken); + pool = await Pool.at(upgradedContracts.pool); + }); + + afterEach(async () => resetFork()); + + it('transfers all GNO to pool escrow', async () => { + const gnoBalance = await gnoToken.balanceOf(pool.address); + const mgnoBalance = await mgnoToken.balanceOf(pool.address); + const poolEscrowGnoBalance = await gnoToken.balanceOf(contracts.poolEscrow); + expect( + await mgnoToken.balanceOf(contracts.poolEscrow) + ).to.be.bignumber.equal(new BN(0)); + await pool.transferToPoolEscrow(); + expect(await gnoToken.balanceOf(pool.address)).to.be.bignumber.equal( + new BN(0) + ); + expect(await mgnoToken.balanceOf(pool.address)).to.be.bignumber.equal( + new BN(0) + ); + expect( + await gnoToken.balanceOf(contracts.poolEscrow) + ).to.be.bignumber.equal( + gnoBalance + .add(poolEscrowGnoBalance) + .add(mgnoBalance.mul(ether('1')).div(ether('32'))) + ); + expect( + await mgnoToken.balanceOf(contracts.poolEscrow) + ).to.be.bignumber.equal(new BN(0)); + }); +}); diff --git a/test/pool/PoolEscrow.test.js b/test/pool/PoolEscrow.test.js index 1a5044a8..50f3e1cc 100644 --- a/test/pool/PoolEscrow.test.js +++ b/test/pool/PoolEscrow.test.js @@ -14,14 +14,14 @@ const { stopImpersonatingAccount, impersonateAccount, resetFork, - mintTokens, + mintGNOTokens, mintMGNOTokens, } = require('../utils'); const PoolEscrow = artifacts.require('PoolEscrow'); const IGCToken = artifacts.require('IGCToken'); -contract('PoolEscrow', ([anyone, newOwner, payee]) => { +contract('PoolEscrow', ([anyone, newOwner, payee, vault]) => { const owner = contractSettings.admin; let poolEscrow, mgnoToken, gnoToken; @@ -31,7 +31,7 @@ contract('PoolEscrow', ([anyone, newOwner, payee]) => { await impersonateAccount(owner); await send.ether(anyone, owner, ether('5')); - let upgradedContracts = await upgradeContracts(); + let upgradedContracts = await upgradeContracts(vault); poolEscrow = await PoolEscrow.at(upgradedContracts.poolEscrow); mgnoToken = await IGCToken.at(contracts.MGNOToken); gnoToken = await IGCToken.at(contracts.GNOToken); @@ -51,21 +51,13 @@ contract('PoolEscrow', ([anyone, newOwner, payee]) => { ); }); - it('can receive mGNO transfers', async () => { - let amount = ether('5'); - await mintMGNOTokens(anyone, amount); - await mgnoToken.transfer(poolEscrow.address, amount, { from: anyone }); - expect(await mgnoToken.balanceOf(poolEscrow.address)).to.bignumber.equal( - amount - ); - }); - it('can receive GNO transfers', async () => { + const balanceBefore = await gnoToken.balanceOf(poolEscrow.address); let amount = ether('5'); - await mintTokens(gnoToken, anyone, amount); + await mintGNOTokens(gnoToken, anyone, amount); await gnoToken.transfer(poolEscrow.address, amount, { from: anyone }); expect(await gnoToken.balanceOf(poolEscrow.address)).to.bignumber.equal( - amount + balanceBefore.add(amount) ); }); @@ -213,7 +205,7 @@ contract('PoolEscrow', ([anyone, newOwner, payee]) => { describe('withdraw tokens', () => { it('owner can withdraw tokens from the escrow', async () => { let amount = ether('5'); - await mintMGNOTokens(anyone, amount); + await mintMGNOTokens(mgnoToken, anyone, amount); await mgnoToken.transfer(poolEscrow.address, amount); let payeeBalance = await mgnoToken.balanceOf(payee); @@ -240,7 +232,7 @@ contract('PoolEscrow', ([anyone, newOwner, payee]) => { it('fails to withdraw tokens without admin role', async () => { let amount = ether('5'); - await mintMGNOTokens(anyone, amount); + await mintMGNOTokens(mgnoToken, anyone, amount); await mgnoToken.transfer(poolEscrow.address, amount); await expectRevert( @@ -253,7 +245,7 @@ contract('PoolEscrow', ([anyone, newOwner, payee]) => { it('fails to withdraw tokens with invalid payee address', async () => { let amount = ether('5'); - await mintMGNOTokens(anyone, amount); + await mintMGNOTokens(mgnoToken, anyone, amount); await mgnoToken.transfer(poolEscrow.address, amount); await expectRevert( @@ -271,7 +263,7 @@ contract('PoolEscrow', ([anyone, newOwner, payee]) => { it('fails to withdraw with invalid token address', async () => { let amount = ether('5'); - await mintMGNOTokens(anyone, amount); + await mintMGNOTokens(mgnoToken, anyone, amount); await mgnoToken.transfer(poolEscrow.address, amount); await expectRevert( diff --git a/test/pool/PoolValidators.test.js b/test/pool/PoolValidators.test.js deleted file mode 100644 index 1392bab3..00000000 --- a/test/pool/PoolValidators.test.js +++ /dev/null @@ -1,491 +0,0 @@ -const { - expectRevert, - expectEvent, - ether, - send, - constants, - BN, -} = require('@openzeppelin/test-helpers'); -const { keccak256, defaultAbiCoder } = require('ethers/lib/utils'); -const { upgradeContracts } = require('../../deployments'); -const { contractSettings, contracts } = require('../../deployments/settings'); -const { - registerValidators, - setupOracleAccounts, - stopImpersonatingAccount, - impersonateAccount, - resetFork, - checkValidatorRegistered, - stakeGNO, -} = require('../utils'); -const { - depositData, - depositDataMerkleRoot, -} = require('./depositDataMerkleRoot'); - -const Pool = artifacts.require('Pool'); -const PoolValidators = artifacts.require('PoolValidators'); -const Oracles = artifacts.require('Oracles'); -const IDepositContract = artifacts.require('IDepositContract'); -const IGCToken = artifacts.require('IGCToken'); - -contract('Pool Validators', (accounts) => { - const admin = contractSettings.admin; - const validatorDeposit = ether('32'); - const depositDataMerkleProofs = - 'ipfs://QmSTP443zR6oKnYVRE23RARyuuzwhhaidUiSXyRTsw3pDs'; - let pool, - validators, - validatorDepositAmount, - oracleAccounts, - oracles, - depositContract, - validatorsDepositRoot, - mgnoToken; - let [operator, anyone, ...otherAccounts] = accounts; - - after(async () => stopImpersonatingAccount(admin)); - - beforeEach(async () => { - await impersonateAccount(admin); - await send.ether(anyone, admin, ether('5')); - - let upgradedContracts = await upgradeContracts(); - pool = await Pool.at(upgradedContracts.pool); - depositContract = await IDepositContract.at( - await pool.validatorRegistration() - ); - validatorDepositAmount = await pool.VALIDATOR_TOTAL_DEPOSIT(); - validatorsDepositRoot = await depositContract.get_deposit_root(); - - validators = await PoolValidators.at(upgradedContracts.poolValidators); - mgnoToken = await IGCToken.at(contracts.MGNOToken); - - // collect validator deposit - let poolBalance = await mgnoToken.balanceOf(pool.address); - let depositAmount = validatorDeposit.sub(poolBalance.mod(validatorDeposit)); - await stakeGNO({ account: anyone, amount: depositAmount, pool }); - - oracles = await Oracles.at(upgradedContracts.oracles); - oracleAccounts = await setupOracleAccounts({ - admin, - oracles, - accounts: otherAccounts, - }); - }); - - afterEach(async () => resetFork()); - - describe('add operator', () => { - it('fails to add with not admin privilege', async () => { - await expectRevert( - validators.addOperator( - operator, - depositDataMerkleRoot, - depositDataMerkleProofs, - { - from: anyone, - } - ), - 'OwnablePausable: access denied' - ); - }); - - it('fails to add with zero operator address', async () => { - await expectRevert( - validators.addOperator( - constants.ZERO_ADDRESS, - depositDataMerkleRoot, - depositDataMerkleProofs, - { - from: admin, - } - ), - 'PoolValidators: invalid operator' - ); - }); - - it('fails to add with invalid merkle root', async () => { - await expectRevert( - validators.addOperator( - operator, - constants.ZERO_BYTES32, - depositDataMerkleProofs, - { - from: admin, - } - ), - 'PoolValidators: invalid merkle root' - ); - }); - - it('fails to add with invalid merkle proofs', async () => { - await expectRevert( - validators.addOperator(operator, depositDataMerkleRoot, '', { - from: admin, - }), - 'PoolValidators: invalid merkle proofs' - ); - }); - - it('can update existing operator', async () => { - await validators.addOperator( - operator, - depositDataMerkleRoot, - depositDataMerkleProofs, - { - from: admin, - } - ); - - let depositDataMerkleRoot2 = - '0x2a6d4eed3ba81bd99efdfd31333e244bb84989cfadbf9ddbf8fabd7296099bc0'; - - let receipt = await validators.addOperator( - operator, - depositDataMerkleRoot2, - depositDataMerkleProofs, - { - from: admin, - } - ); - - await expectEvent(receipt, 'OperatorAdded', { - operator, - depositDataMerkleRoot: depositDataMerkleRoot2, - depositDataMerkleProofs, - }); - - let _operator = await validators.getOperator(operator); - expect(_operator[0]).to.equal(depositDataMerkleRoot2); - expect(_operator[1]).to.equal(false); - }); - - it('can add new operator', async () => { - let receipt = await validators.addOperator( - operator, - depositDataMerkleRoot, - depositDataMerkleProofs, - { - from: admin, - } - ); - - await expectEvent(receipt, 'OperatorAdded', { - operator, - depositDataMerkleRoot, - depositDataMerkleProofs, - }); - - let _operator = await validators.getOperator(operator); - expect(_operator[0]).to.equal(depositDataMerkleRoot); - expect(_operator[1]).to.equal(false); - }); - }); - - describe('remove operator', () => { - beforeEach(async () => { - await validators.addOperator( - operator, - depositDataMerkleRoot, - depositDataMerkleProofs, - { - from: admin, - } - ); - }); - - it('fails to remove by user other than admin and operator', async () => { - await expectRevert( - validators.removeOperator(operator, { - from: anyone, - }), - 'PoolValidators: access denied' - ); - }); - - it('fails to remove not existing operator', async () => { - await expectRevert( - validators.removeOperator(anyone, { - from: admin, - }), - 'PoolValidators: invalid operator' - ); - }); - - it('operator or admin can remove operator', async () => { - let receipt = await validators.removeOperator(operator, { - from: admin, - }); - - await expectEvent(receipt, 'OperatorRemoved', { - sender: admin, - operator, - }); - - let _operator = await validators.getOperator(operator); - expect(_operator[0]).to.equal(constants.ZERO_BYTES32); - expect(_operator[1]).to.equal(false); - }); - }); - - describe('commit operator', () => { - beforeEach(async () => { - await validators.addOperator( - operator, - depositDataMerkleRoot, - depositDataMerkleProofs, - { - from: admin, - } - ); - }); - - it('fails to commit invalid operator', async () => { - await expectRevert( - validators.commitOperator({ - from: anyone, - }), - 'PoolValidators: invalid operator' - ); - }); - - it('fails to commit operator twice', async () => { - await validators.commitOperator({ - from: operator, - }); - await expectRevert( - validators.commitOperator({ - from: operator, - }), - 'PoolValidators: invalid operator' - ); - }); - - it('can commit operator', async () => { - let receipt = await validators.commitOperator({ - from: operator, - }); - - await expectEvent(receipt, 'OperatorCommitted', { - operator, - }); - }); - }); - - describe('register validators', () => { - let validatorDepositData = { - operator, - withdrawalCredentials: depositData[0].withdrawalCredentials, - depositDataRoot: depositData[0].depositDataRoot, - publicKey: depositData[0].publicKey, - signature: depositData[0].signature, - }; - let merkleProof = depositData[0].merkleProof; - - beforeEach(async () => { - await validators.addOperator( - operator, - depositDataMerkleRoot, - depositDataMerkleProofs, - { - from: admin, - } - ); - }); - - it('fails to register validator by not oracles', async () => { - await expectRevert( - validators.registerValidator(validatorDepositData, merkleProof, { - from: anyone, - }), - 'PoolValidators: access denied' - ); - }); - - it('fails to register validator for not committed operator', async () => { - await expectRevert( - registerValidators({ - depositData: [validatorDepositData], - merkleProofs: [merkleProof], - oracles, - oracleAccounts, - validatorsDepositRoot, - }), - 'PoolValidators: invalid operator' - ); - }); - - it('fails to register validator twice', async () => { - await validators.commitOperator({ - from: operator, - }); - - await expectRevert( - registerValidators({ - depositData: [validatorDepositData, validatorDepositData], - merkleProofs: [merkleProof, merkleProof], - oracles, - oracleAccounts, - validatorsDepositRoot, - }), - 'PoolValidators: validator already registered' - ); - }); - - it('fails to register for invalid operator', async () => { - await validators.commitOperator({ - from: operator, - }); - - await expectRevert( - registerValidators({ - depositData: [{ ...validatorDepositData, operator: anyone }], - merkleProofs: [merkleProof], - oracles, - oracleAccounts, - validatorsDepositRoot, - }), - 'PoolValidators: invalid operator' - ); - }); - - it('fails to register for invalid deposit data', async () => { - await validators.commitOperator({ - from: operator, - }); - await expectRevert( - registerValidators({ - depositData: [ - { - ...validatorDepositData, - depositDataRoot: constants.ZERO_BYTES32, - }, - ], - merkleProofs: [merkleProof], - oracles, - oracleAccounts, - validatorsDepositRoot, - }), - 'PoolValidators: invalid merkle proof' - ); - }); - - it('fails to register with invalid validators deposit root', async () => { - await validators.commitOperator({ - from: operator, - }); - await expectRevert( - registerValidators({ - depositData: [validatorDepositData], - merkleProofs: [merkleProof], - oracles, - oracleAccounts, - validatorsDepositRoot: keccak256('0x6be4000000000000'), - }), - 'Oracles: invalid validators deposit root' - ); - }); - - it('oracles can register one validator', async () => { - await validators.commitOperator({ - from: operator, - }); - - let poolBalance = await mgnoToken.balanceOf(pool.address); - let receipt = await registerValidators({ - depositData: [validatorDepositData], - merkleProofs: [merkleProof], - oracles, - oracleAccounts, - validatorsDepositRoot, - }); - - await expectEvent.inTransaction(receipt.tx, Pool, 'ValidatorRegistered', { - operator, - publicKey: validatorDepositData.publicKey, - }); - expect( - await validators.isValidatorRegistered( - keccak256( - defaultAbiCoder.encode(['bytes'], [validatorDepositData.publicKey]) - ) - ) - ).to.equal(true); - let _operator = await validators.getOperator(operator); - expect(_operator[1]).to.equal(true); - expect(await mgnoToken.balanceOf(pool.address)).to.bignumber.equal( - poolBalance.sub(validatorDepositAmount) - ); - await checkValidatorRegistered({ - transaction: receipt.tx, - pubKey: validatorDepositData.publicKey, - withdrawalCredentials: validatorDepositData.withdrawalCredentials, - signature: validatorDepositData.signature, - validatorDepositAmount, - }); - }); - - it('oracles can register multiple validators', async () => { - await validators.commitOperator({ - from: operator, - }); - - let poolBalance = await mgnoToken.balanceOf(pool.address); - let validatorsDepositData = []; - let merkleProofs = []; - for (let i = 0; i < depositData.length; i++) { - validatorsDepositData.push({ - operator, - withdrawalCredentials: depositData[i].withdrawalCredentials, - depositDataRoot: depositData[i].depositDataRoot, - publicKey: depositData[i].publicKey, - signature: depositData[i].signature, - }); - merkleProofs.push(depositData[i].merkleProof); - } - let receipt = await registerValidators({ - depositData: validatorsDepositData, - merkleProofs, - oracles, - oracleAccounts, - validatorsDepositRoot, - }); - - for (let i = 0; i < depositData.length; i++) { - await expectEvent.inTransaction( - receipt.tx, - Pool, - 'ValidatorRegistered', - { - operator, - publicKey: validatorsDepositData[i].publicKey, - } - ); - expect( - await validators.isValidatorRegistered( - keccak256( - defaultAbiCoder.encode( - ['bytes'], - [validatorsDepositData[i].publicKey] - ) - ) - ) - ).to.equal(true); - await checkValidatorRegistered({ - transaction: receipt.tx, - pubKey: validatorsDepositData[i].publicKey, - withdrawalCredentials: validatorsDepositData[i].withdrawalCredentials, - signature: validatorsDepositData[i].signature, - validatorDepositAmount, - }); - } - - let _operator = await validators.getOperator(operator); - expect(_operator[1]).to.equal(true); - expect(await mgnoToken.balanceOf(pool.address)).to.bignumber.equal( - poolBalance.sub(validatorDepositAmount.mul(new BN(depositData.length))) - ); - }); - }); -}); diff --git a/test/pool/settings.test.js b/test/pool/settings.test.js deleted file mode 100644 index d1f9ca48..00000000 --- a/test/pool/settings.test.js +++ /dev/null @@ -1,202 +0,0 @@ -const { expect } = require('chai'); -const { - send, - ether, - expectRevert, - expectEvent, -} = require('@openzeppelin/test-helpers'); -const { - stopImpersonatingAccount, - impersonateAccount, - resetFork, - setActivatedValidators, - setupOracleAccounts, - registerValidators, - stakeGNO, -} = require('../utils'); -const { upgradeContracts } = require('../../deployments'); -const { contractSettings } = require('../../deployments/settings'); -const { - depositData, - depositDataMerkleRoot, -} = require('./depositDataMerkleRoot'); - -const Pool = artifacts.require('Pool'); -const Oracles = artifacts.require('Oracles'); -const PoolValidators = artifacts.require('PoolValidators'); -const RewardToken = artifacts.require('RewardToken'); -const IDepositContract = artifacts.require('IDepositContract'); - -contract('Pool (settings)', ([operator, anyone, ...otherAccounts]) => { - const admin = contractSettings.admin; - let pool, oracles, oracleAccounts, rewardToken, validatorsDepositRoot; - - after(async () => stopImpersonatingAccount(admin)); - - beforeEach(async () => { - await impersonateAccount(admin); - await send.ether(anyone, admin, ether('5')); - - let upgradedContracts = await upgradeContracts(); - let validators = await PoolValidators.at(upgradedContracts.poolValidators); - await validators.addOperator( - operator, - depositDataMerkleRoot, - 'ipfs://QmSTP443zR6oKnYVRE23RARyuuzwhhaidUiSXyRTsw3pDs', - { - from: admin, - } - ); - await validators.commitOperator({ - from: operator, - }); - pool = await Pool.at(upgradedContracts.pool); - let depositContract = await IDepositContract.at( - await pool.validatorRegistration() - ); - validatorsDepositRoot = await depositContract.get_deposit_root(); - oracles = await Oracles.at(upgradedContracts.oracles); - rewardToken = await RewardToken.at(upgradedContracts.rewardToken); - oracleAccounts = await setupOracleAccounts({ - admin, - oracles, - accounts: otherAccounts, - }); - }); - - afterEach(async () => resetFork()); - - describe('min activating deposit', () => { - it('not admin fails to set min activating deposit', async () => { - await expectRevert( - pool.setMinActivatingDeposit(ether('10'), { - from: anyone, - }), - 'OwnablePausable: access denied' - ); - }); - - it('admin can set min activating deposit', async () => { - let minActivatingDeposit = ether('10'); - let receipt = await pool.setMinActivatingDeposit(minActivatingDeposit, { - from: admin, - }); - await expectEvent(receipt, 'MinActivatingDepositUpdated', { - minActivatingDeposit, - sender: admin, - }); - expect(await pool.minActivatingDeposit()).to.bignumber.equal( - minActivatingDeposit - ); - }); - }); - - describe('pending validators limit', () => { - it('not admin fails to set pending validators limit', async () => { - await expectRevert( - pool.setPendingValidatorsLimit('1000', { - from: anyone, - }), - 'OwnablePausable: access denied' - ); - }); - - it('admin can set pending validators limit', async () => { - let pendingValidatorsLimit = '1000'; - let receipt = await pool.setPendingValidatorsLimit( - pendingValidatorsLimit, - { - from: admin, - } - ); - await expectEvent(receipt, 'PendingValidatorsLimitUpdated', { - pendingValidatorsLimit, - sender: admin, - }); - expect(await pool.pendingValidatorsLimit()).to.bignumber.equal( - pendingValidatorsLimit - ); - }); - - it('fails to set invalid pending validators limit', async () => { - await expectRevert( - pool.setPendingValidatorsLimit(10000, { - from: admin, - }), - 'Pool: invalid limit' - ); - }); - }); - - describe('activated validators', () => { - it('not oracles contract or admin fails to set activated validators', async () => { - await expectRevert( - pool.setActivatedValidators('10', { - from: anyone, - }), - 'Pool: access denied' - ); - }); - - it('admin can override activated validators', async () => { - let activatedValidators = await pool.activatedValidators(); - activatedValidators = activatedValidators.add( - await pool.pendingValidators() - ); - - let receipt = await pool.setActivatedValidators(activatedValidators, { - from: admin, - }); - expectEvent(receipt, 'ActivatedValidatorsUpdated', { - activatedValidators, - }); - expect(await pool.activatedValidators()).to.bignumber.equal( - activatedValidators - ); - }); - - it('oracles contract can set activated validators', async () => { - await stakeGNO({ account: anyone, amount: ether('32'), pool }); - await registerValidators({ - depositData: [ - { - operator, - withdrawalCredentials: depositData[0].withdrawalCredentials, - depositDataRoot: depositData[0].depositDataRoot, - publicKey: depositData[0].publicKey, - signature: depositData[0].signature, - }, - ], - merkleProofs: [depositData[0].merkleProof], - oracles, - oracleAccounts, - validatorsDepositRoot, - }); - - let activatedValidators = await pool.activatedValidators(); - activatedValidators = activatedValidators.add( - await pool.pendingValidators() - ); - - let receipt = await setActivatedValidators({ - pool, - rewardToken, - activatedValidators, - oracleAccounts, - oracles, - }); - await expectEvent.inTransaction( - receipt.tx, - Pool, - 'ActivatedValidatorsUpdated', - { - activatedValidators, - sender: oracles.address, - } - ); - expect(await pool.activatedValidators()).to.bignumber.equal( - activatedValidators - ); - }); - }); -}); diff --git a/test/pool/stake.test.js b/test/pool/stake.test.js deleted file mode 100644 index 43df8ef6..00000000 --- a/test/pool/stake.test.js +++ /dev/null @@ -1,853 +0,0 @@ -const { expect } = require('chai'); -const { - ether, - send, - expectRevert, - expectEvent, - constants, - BN, -} = require('@openzeppelin/test-helpers'); -const { - stopImpersonatingAccount, - impersonateAccount, - resetFork, - getDepositAmount, - registerValidator, - setupOracleAccounts, -} = require('../utils'); -const { upgradeContracts } = require('../../deployments'); -const { contractSettings, contracts } = require('../../deployments/settings'); -const { - checkStakedToken, - mintMGNOTokens, - stakeGNO, - stakeMGNO, - stakeGNOWithPermit, -} = require('../utils'); -const { - depositData, - depositDataMerkleRoot, -} = require('./depositDataMerkleRoot'); - -const Pool = artifacts.require('Pool'); -const StakedToken = artifacts.require('StakedToken'); -const PoolValidators = artifacts.require('PoolValidators'); -const Oracles = artifacts.require('Oracles'); -const IDepositContract = artifacts.require('IDepositContract'); -const IGCToken = artifacts.require('IGCToken'); - -contract('Pool (stake)', (accounts) => { - const admin = contractSettings.admin; - let [sender1, sender2, sender3, operator, ...otherAccounts] = accounts; - let pool, - stakedToken, - mgnoToken, - validators, - oracles, - oracleAccounts, - totalSupply, - poolBalance, - activatedValidators, - pendingValidators, - depositContract, - validatorsDepositRoot; - - after(async () => stopImpersonatingAccount(admin)); - - beforeEach(async () => { - await impersonateAccount(admin); - await send.ether(sender3, admin, ether('5')); - let upgradedContracts = await upgradeContracts(); - - mgnoToken = await IGCToken.at(contracts.MGNOToken); - pool = await Pool.at(upgradedContracts.pool); - stakedToken = await StakedToken.at(upgradedContracts.stakedToken); - validators = await PoolValidators.at(upgradedContracts.poolValidators); - oracles = await Oracles.at(upgradedContracts.oracles); - oracleAccounts = await setupOracleAccounts({ - admin, - oracles, - accounts: otherAccounts, - }); - depositContract = await IDepositContract.at( - await pool.validatorRegistration() - ); - validatorsDepositRoot = await depositContract.get_deposit_root(); - await validators.addOperator( - operator, - depositDataMerkleRoot, - 'ipfs://QmSTP443zR6oKnYVRE23RARyuuzwhhaidUiSXyRTsw3pDs', - { - from: admin, - } - ); - await validators.commitOperator({ - from: operator, - }); - - totalSupply = await stakedToken.totalSupply(); - poolBalance = await mgnoToken.balanceOf(pool.address); - activatedValidators = await pool.activatedValidators(); - pendingValidators = await pool.pendingValidators(); - }); - - afterEach(async () => resetFork()); - - describe('stake mGNO', async () => { - it('fails to stake with zero amount', async () => { - await expectRevert( - pool.stakeMGNO( - ether('0'), - constants.ZERO_ADDRESS, - constants.ZERO_ADDRESS, - false, - { - from: sender1, - } - ), - 'Pool: invalid deposit amount' - ); - }); - - it('fails to stake with no allowance', async () => { - await expectRevert( - stakeMGNO({ - account: sender1, - amount: ether('1'), - pool, - noAllowance: true, - }), - 'ERC20: transfer amount exceeds allowance' - ); - }); - - it('mints tokens for users with deposit less than min activating', async () => { - // User 1 creates a deposit - let maxAmount = ether('0.01'); - await pool.setMinActivatingDeposit(maxAmount, { from: admin }); - let depositAmount1 = getDepositAmount({ - max: maxAmount, - }); - let gnoAmount1 = await pool.calculateGNO(depositAmount1); - totalSupply = totalSupply.add(gnoAmount1); - poolBalance = poolBalance.add(depositAmount1); - - await stakeMGNO({ - account: sender1, - amount: depositAmount1, - pool, - }); - await checkStakedToken({ - stakedToken, - totalSupply, - account: sender1, - balance: gnoAmount1, - }); - - // User 2 creates a deposit - let depositAmount2 = getDepositAmount({ - max: maxAmount, - }); - let gnoAmount2 = await pool.calculateGNO(depositAmount2); - totalSupply = totalSupply.add(gnoAmount2); - poolBalance = poolBalance.add(depositAmount2); - - await stakeMGNO({ - account: sender2, - amount: depositAmount2, - pool, - }); - await checkStakedToken({ - stakedToken, - totalSupply, - account: sender2, - balance: gnoAmount2, - }); - - // check contract balance - expect(await mgnoToken.balanceOf(pool.address)).to.be.bignumber.equal( - poolBalance - ); - }); - - // TODO: re-enable once activations enabled - it.skip('places deposit of user to the activation queue with exceeded pending validators limit', async () => { - await pool.setPendingValidatorsLimit('1', { from: admin }); // 0.01 % - await pool.setMinActivatingDeposit(ether('0.01'), { from: admin }); - - // deposit more than 0.01 % - let depositAmount = ether('32').mul(new BN(2)); - let gnoAmount = await pool.calculateGNO(depositAmount); - - poolBalance = poolBalance.add(depositAmount); - let validatorIndex = activatedValidators - .add(pendingValidators) - .add(poolBalance.div(ether('32'))); - - // check deposit amount placed in activation queue - let receipt = await stakeMGNO({ - account: sender1, - amount: depositAmount, - pool, - }); - await expectEvent(receipt, 'ActivationScheduled', { - sender: sender1, - validatorIndex, - value: gnoAmount, - }); - expect( - await pool.activations(sender1, validatorIndex) - ).to.bignumber.equal(gnoAmount); - - // check contract balance - expect(await mgnoToken.balanceOf(pool.address)).to.be.bignumber.equal( - poolBalance - ); - expect(await stakedToken.totalSupply()).to.bignumber.equal(totalSupply); - }); - - // TODO: re-enable once activations enabled - it.skip('activates deposit of user immediately with not exceeded pending validators limit', async () => { - await pool.setPendingValidatorsLimit('1000', { from: admin }); // 10 % - await pool.setMinActivatingDeposit(ether('0.01'), { from: admin }); - - // deposit less than 10 % - let depositAmount = ether('32'); - poolBalance = poolBalance.add(depositAmount); - let validatorIndex = activatedValidators - .add(pendingValidators) - .add(new BN(1)); - totalSupply = totalSupply.add(depositAmount); - - // check deposit amount added immediately - await stakeMGNO({ - account: sender1, - amount: depositAmount, - pool, - }); - await checkStakedToken({ - stakedToken, - totalSupply, - account: sender1, - balance: depositAmount, - }); - expect( - await pool.activations(sender1, validatorIndex) - ).to.bignumber.equal(new BN(0)); - - // check contract balance - expect(await mgnoToken.balanceOf(pool.address)).to.be.bignumber.equal( - poolBalance - ); - }); - - it('can stake to different recipient address', async () => { - let amount = ether('1'); - let gnoAmount = await pool.calculateGNO(amount); - - totalSupply = totalSupply.add(gnoAmount); - - let receipt = await stakeMGNO({ - account: sender1, - amount: amount, - recipient: sender2, - pool, - }); - await expectEvent.inTransaction(receipt.tx, StakedToken, 'Transfer', { - from: constants.ZERO_ADDRESS, - to: sender2, - value: gnoAmount, - }); - await checkStakedToken({ - stakedToken, - totalSupply, - account: sender2, - balance: gnoAmount, - }); - await checkStakedToken({ - stakedToken, - totalSupply, - account: sender1, - balance: new BN(0), - }); - }); - - it('can stake with partner', async () => { - let amount = ether('1'); - let gnoAmount = await pool.calculateGNO(amount); - - const partner = otherAccounts[0]; - totalSupply = totalSupply.add(gnoAmount); - - let receipt = await stakeMGNO({ - referrer: partner, - account: sender1, - amount: amount, - hasRevenueShare: true, - pool, - }); - await expectEvent(receipt, 'StakedWithPartner', { - partner, - amount: gnoAmount, - }); - await checkStakedToken({ - stakedToken, - totalSupply, - account: sender1, - balance: gnoAmount, - }); - }); - - it('can stake with referrer', async () => { - const referrer = otherAccounts[0]; - let amount = ether('1'); - let gnoAmount = await pool.calculateGNO(amount); - - totalSupply = totalSupply.add(gnoAmount); - - let receipt = await stakeMGNO({ - referrer: referrer, - account: sender1, - amount: amount, - pool, - }); - await expectEvent(receipt, 'StakedWithReferrer', { - referrer, - amount: gnoAmount, - }); - await checkStakedToken({ - stakedToken, - totalSupply, - account: sender1, - balance: gnoAmount, - }); - }); - }); - - describe('stake GNO', async () => { - it('fails to stake with zero amount', async () => { - await expectRevert( - pool.stakeGNO( - ether('0'), - constants.ZERO_ADDRESS, - constants.ZERO_ADDRESS, - false, - { - from: sender1, - } - ), - 'Pool: invalid deposit amount' - ); - }); - - it('fails to stake with no allowance', async () => { - await expectRevert( - stakeGNO({ - account: sender1, - amount: ether('1'), - pool, - noAllowance: true, - }), - 'SafeERC20: low-level call failed' - ); - }); - - it('can stake to different recipient address', async () => { - let amount = ether('1'); - let totalSupply = await stakedToken.totalSupply(); - let receipt = await stakeGNO({ - account: sender1, - amount: amount, - recipient: sender2, - pool, - }); - await expectEvent.inTransaction(receipt.tx, StakedToken, 'Transfer', { - from: constants.ZERO_ADDRESS, - to: sender2, - value: amount, - }); - await checkStakedToken({ - stakedToken, - totalSupply: totalSupply.add(amount), - account: sender2, - balance: amount, - }); - await checkStakedToken({ - stakedToken, - totalSupply: totalSupply.add(amount), - account: sender1, - balance: new BN(0), - }); - }); - - it('can stake with partner', async () => { - let amount = ether('1'); - const partner = otherAccounts[0]; - totalSupply = totalSupply.add(amount); - - let receipt = await stakeGNO({ - referrer: partner, - account: sender1, - amount: amount, - hasRevenueShare: true, - pool, - }); - await expectEvent(receipt, 'StakedWithPartner', { - partner, - amount, - }); - await checkStakedToken({ - stakedToken, - totalSupply, - account: sender1, - balance: amount, - }); - }); - - it('can stake with referrer', async () => { - const referrer = otherAccounts[0]; - let amount = ether('1'); - totalSupply = totalSupply.add(amount); - - let receipt = await stakeGNO({ - referrer: referrer, - account: sender1, - amount, - pool, - }); - await expectEvent(receipt, 'StakedWithReferrer', { - referrer, - amount, - }); - await checkStakedToken({ - stakedToken, - totalSupply, - account: sender1, - balance: amount, - }); - }); - }); - - describe('stake GNO with Permit', async () => { - let account; - - beforeEach(async () => { - account = web3.eth.accounts.create(); - await send.ether(sender1, account.address, ether('5')); - }); - - it('fails to stake with zero amount', async () => { - await expectRevert( - stakeGNOWithPermit({ - account, - amount: ether('0'), - minter: sender1, - recipient: sender2, - pool, - }), - 'Pool: invalid deposit amount' - ); - }); - - it('can stake to different recipient address', async () => { - let amount = ether('1'); - totalSupply = totalSupply.add(amount); - - let receipt = await stakeGNOWithPermit({ - account, - amount, - minter: sender1, - recipient: sender2, - pool, - }); - await expectEvent.inTransaction( - receipt.transactionHash, - StakedToken, - 'Transfer', - { - from: constants.ZERO_ADDRESS, - to: sender2, - value: amount, - } - ); - await checkStakedToken({ - stakedToken, - totalSupply, - account: sender2, - balance: amount, - }); - await checkStakedToken({ - stakedToken, - totalSupply, - account: account.address, - balance: new BN(0), - }); - }); - - it('can stake with partner', async () => { - let amount = ether('1'); - const partner = otherAccounts[0]; - totalSupply = totalSupply.add(amount); - - let receipt = await stakeGNOWithPermit({ - account, - minter: sender1, - referrer: partner, - amount, - hasRevenueShare: true, - pool, - }); - await expectEvent.inTransaction( - receipt.transactionHash, - Pool, - 'StakedWithPartner', - { - partner, - amount, - } - ); - await checkStakedToken({ - stakedToken, - totalSupply, - account: account.address, - balance: amount, - }); - }); - - it('can stake with referrer', async () => { - const referrer = otherAccounts[0]; - let amount = ether('1'); - totalSupply = totalSupply.add(amount); - - let receipt = await stakeGNOWithPermit({ - account, - minter: sender1, - referrer, - amount, - pool, - }); - await expectEvent.inTransaction( - receipt.transactionHash, - Pool, - 'StakedWithReferrer', - { - referrer, - amount, - } - ); - await checkStakedToken({ - stakedToken, - totalSupply, - account: account.address, - balance: amount, - }); - }); - - it('fails to stake with invalid permit', async () => { - let amount = ether('1'); - await expectRevert.unspecified( - stakeGNOWithPermit({ - account, - minter: sender1, - amount: amount, - pool, - invalidHolder: true, - }) - ); - }); - }); - - // TODO: re-enable once activations enabled - describe.skip('activating', () => { - let validatorIndex, depositAmount; - - beforeEach(async () => { - await pool.setPendingValidatorsLimit('1', { from: admin }); // 0.01 % - await pool.setMinActivatingDeposit(ether('0.01'), { from: admin }); - - depositAmount = ether('1000'); - await stakeGNO({ - account: sender1, - amount: depositAmount, - pool, - }); - poolBalance = poolBalance.add(depositAmount); - validatorIndex = activatedValidators - .add(pendingValidators) - .add(poolBalance.div(ether('32'))); - - for (let i = 0; i < validatorIndex.sub(activatedValidators); i++) { - validatorsDepositRoot = await depositContract.get_deposit_root(); - await registerValidator({ - admin, - validators, - oracles, - oracleAccounts, - operator, - validatorsDepositRoot, - merkleProof: depositData[i].merkleProof, - signature: depositData[i].signature, - publicKey: depositData[i].publicKey, - withdrawalCredentials: depositData[i].withdrawalCredentials, - depositDataRoot: depositData[i].depositDataRoot, - }); - } - }); - - it('fails to activate with invalid validator index', async () => { - await expectRevert( - pool.activate(sender1, validatorIndex, { - from: sender1, - }), - 'Pool: validator is not active yet' - ); - }); - - it('fails to activate in paused pool', async () => { - await pool.pause({ from: admin }); - expect(await pool.paused()).equal(true); - - await expectRevert( - pool.activate(sender1, validatorIndex, { - from: sender1, - }), - 'Pausable: paused' - ); - }); - - it('fails to activate not existing deposit', async () => { - await pool.setActivatedValidators(validatorIndex, { - from: admin, - }); - await expectRevert( - pool.activate(sender2, validatorIndex, { - from: sender1, - }), - 'Pool: invalid validator index' - ); - }); - - it('fails to activate deposit amount twice', async () => { - await pool.setActivatedValidators(validatorIndex, { - from: admin, - }); - await pool.activate(sender1, validatorIndex, { - from: sender1, - }); - - await expectRevert( - pool.activate(sender1, validatorIndex, { - from: sender1, - }), - 'Pool: invalid validator index' - ); - }); - - it('activates deposit amount', async () => { - await pool.setActivatedValidators(validatorIndex, { - from: admin, - }); - expect(await pool.canActivate(validatorIndex)).to.equal(true); - let receipt = await pool.activate(sender1, validatorIndex, { - from: sender1, - }); - await expectEvent(receipt, 'Activated', { - account: sender1, - validatorIndex, - value: depositAmount, - sender: sender1, - }); - totalSupply = totalSupply.add(depositAmount); - - await checkStakedToken({ - stakedToken, - totalSupply, - account: sender1, - balance: depositAmount, - }); - }); - }); - - // TODO: re-enable once activations enabled - describe.skip('activating multiple', () => { - let validatorIndex1, validatorIndex2, depositAmount; - - beforeEach(async () => { - await pool.setPendingValidatorsLimit('1', { from: admin }); // 0.01 % - await pool.setMinActivatingDeposit(ether('0.01'), { from: admin }); - - depositAmount = ether('32'); - await stakeMGNO({ - account: sender3, - amount: depositAmount, - pool, - }); - poolBalance = poolBalance.add(depositAmount); - validatorIndex1 = activatedValidators - .add(pendingValidators) - .add(poolBalance.div(ether('32'))); - - await stakeMGNO({ - account: sender3, - amount: depositAmount, - pool, - }); - poolBalance = poolBalance.add(depositAmount); - validatorIndex2 = activatedValidators - .add(pendingValidators) - .add(poolBalance.div(ether('32'))); - - for (let i = 0; i < validatorIndex2.sub(activatedValidators); i++) { - validatorsDepositRoot = await depositContract.get_deposit_root(); - await registerValidator({ - admin, - validators, - oracles, - oracleAccounts, - operator, - validatorsDepositRoot, - merkleProof: depositData[i].merkleProof, - signature: depositData[i].signature, - publicKey: depositData[i].publicKey, - withdrawalCredentials: depositData[i].withdrawalCredentials, - depositDataRoot: depositData[i].depositDataRoot, - }); - } - }); - - it('fails to activate with invalid validator indexes', async () => { - await expectRevert( - pool.activateMultiple( - sender3, - [validatorIndex1.add(new BN(2)), validatorIndex2.add(new BN(3))], - { - from: sender3, - } - ), - 'Pool: validator is not active yet' - ); - }); - - it('fails to activate in paused pool', async () => { - await pool.pause({ from: admin }); - expect(await pool.paused()).equal(true); - - await expectRevert( - pool.activateMultiple(sender3, [validatorIndex1, validatorIndex2], { - from: sender3, - }), - 'Pausable: paused' - ); - }); - - it('fails to activate not existing deposit', async () => { - await pool.setActivatedValidators(validatorIndex2, { - from: admin, - }); - await expectRevert( - pool.activateMultiple(sender2, [validatorIndex1, validatorIndex2], { - from: sender3, - }), - 'Pool: invalid validator index' - ); - }); - - it('fails to activate multiple deposit amounts twice', async () => { - await pool.setActivatedValidators(validatorIndex2, { - from: admin, - }); - await pool.activateMultiple(sender3, [validatorIndex1, validatorIndex2], { - from: sender3, - }); - - await expectRevert( - pool.activateMultiple(sender3, [validatorIndex1, validatorIndex2], { - from: sender3, - }), - 'Pool: invalid validator index' - ); - }); - - it('activates multiple deposit amounts', async () => { - await pool.setActivatedValidators(validatorIndex2, { - from: admin, - }); - expect(await pool.canActivate(validatorIndex1)).to.equal(true); - expect(await pool.canActivate(validatorIndex2)).to.equal(true); - let receipt = await pool.activateMultiple( - sender3, - [validatorIndex1, validatorIndex2], - { - from: sender3, - } - ); - totalSupply = totalSupply.add(depositAmount.mul(new BN(2))); - - await checkStakedToken({ - stakedToken, - totalSupply, - account: sender3, - balance: depositAmount.mul(new BN(2)), - }); - - await expectEvent.inTransaction(receipt.tx, Pool, 'Activated', { - account: sender3, - validatorIndex: validatorIndex1, - value: depositAmount, - sender: sender3, - }); - await expectEvent.inTransaction(receipt.tx, Pool, 'Activated', { - account: sender3, - validatorIndex: validatorIndex2, - value: depositAmount, - sender: sender3, - }); - }); - }); - - it('only PoolValidators contract can register new validators', async () => { - const { publicKey, signature, withdrawalCredentials, depositDataRoot } = - depositData[0]; - await expectRevert( - pool.registerValidator( - { - operator, - withdrawalCredentials, - depositDataRoot, - publicKey, - signature, - }, - { - from: sender1, - } - ), - 'Pool: access denied' - ); - }); - - it('not admin cannot refund', async () => { - let amount = ether('10'); - await mintMGNOTokens(sender1, amount); - await expectRevert( - pool.refund(amount, { - from: sender1, - }), - 'OwnablePausable: access denied' - ); - }); - - it('admin can refund', async () => { - let amount = ether('10'); - await send.ether(otherAccounts[0], admin, ether('3000')); - await mintMGNOTokens(admin, amount); - await mgnoToken.approve(pool.address, amount, { from: admin }); - - let receipt = await pool.refund(amount, { - from: admin, - }); - await expectEvent(receipt, 'Refunded', { - sender: admin, - amount, - }); - }); -}); diff --git a/test/tokens/RewardToken.test.js b/test/tokens/RewardToken.test.js index 01a9581d..3d6b3f02 100644 --- a/test/tokens/RewardToken.test.js +++ b/test/tokens/RewardToken.test.js @@ -1,4 +1,3 @@ -const { hexlify, keccak256, defaultAbiCoder } = require('ethers/lib/utils'); const { expect } = require('chai'); const { expectRevert, @@ -8,28 +7,30 @@ const { constants, send, } = require('@openzeppelin/test-helpers'); -const { upgradeContracts } = require('../../deployments'); -const { contractSettings } = require('../../deployments/settings'); +const { upgradeContracts, upgradeRewardToken } = require('../../deployments'); +const { contractSettings, contracts } = require('../../deployments/settings'); const { stopImpersonatingAccount, impersonateAccount, resetFork, checkRewardToken, setTotalRewards, - setupOracleAccounts, - stakeGNO, + addStakedToken, + addRewardToken, + checkStakedToken, } = require('../utils'); +const { ethers } = require('hardhat'); const StakedToken = artifacts.require('StakedToken'); const RewardToken = artifacts.require('RewardToken'); -const Pool = artifacts.require('Pool'); const Oracles = artifacts.require('Oracles'); const MulticallMock = artifacts.require('MulticallMock'); +const VaultMock = artifacts.require('VaultMock'); const protocolFee = new BN(1000); -contract('RewardToken', ([sender, merkleDistributor, ...accounts]) => { +contract('RewardToken', ([sender, merkleDistributor, vault, ...others]) => { const admin = contractSettings.admin; - let stakedToken, rewardToken, totalSupply, pool, oracles, oracleAccounts; + let stakedToken, rewardToken, totalSupply, oracles; after(async () => stopImpersonatingAccount(admin)); @@ -37,18 +38,14 @@ contract('RewardToken', ([sender, merkleDistributor, ...accounts]) => { await impersonateAccount(admin); await send.ether(sender, admin, ether('5')); - let contracts = await upgradeContracts(); + let contracts = await upgradeContracts(vault); stakedToken = await StakedToken.at(contracts.stakedToken); rewardToken = await RewardToken.at(contracts.rewardToken); - pool = await Pool.at(contracts.pool); oracles = await Oracles.at(contracts.oracles); - oracleAccounts = await setupOracleAccounts({ oracles, admin, accounts }); totalSupply = await rewardToken.totalSupply(); await rewardToken.setProtocolFee(protocolFee, { from: admin }); - - await stakeGNO({ account: sender, amount: ether('1'), pool }); }); afterEach(async () => resetFork()); @@ -140,15 +137,13 @@ contract('RewardToken', ([sender, merkleDistributor, ...accounts]) => { }); }); - it('oracles can update rewards', async () => { + it('vault can update rewards', async () => { let prevTotalRewards = await rewardToken.totalRewards(); let newTotalRewards = prevTotalRewards.add(ether('10')); let receipt = await setTotalRewards({ rewardToken, - oracles, - pool, + vault, totalRewards: newTotalRewards, - oracleAccounts, }); await expectEvent.inTransaction( receipt.tx, @@ -161,96 +156,127 @@ contract('RewardToken', ([sender, merkleDistributor, ...accounts]) => { ); }); - it('anyone cannot update rewards', async () => { - await expectRevert( - rewardToken.updateTotalRewards(ether('10'), { - from: sender, - }), - 'RewardToken: access denied' - ); - await checkRewardToken({ - rewardToken, - totalSupply, - account: sender, - balance: new BN(0), - }); - }); - - it('oracles can update rewards', async () => { - await rewardToken.setProtocolFeeRecipient(admin, { + it('assigns protocol fee to distributor', async () => { + await rewardToken.setProtocolFeeRecipient(constants.ZERO_ADDRESS, { from: admin, }); + + let periodReward = ether('10'); let prevTotalRewards = await rewardToken.totalRewards(); - let newTotalRewards = prevTotalRewards.add(ether('10')); + let newTotalRewards = prevTotalRewards.add(periodReward); let receipt = await setTotalRewards({ rewardToken, - oracles, - pool, + vault, totalRewards: newTotalRewards, - oracleAccounts, }); await expectEvent.inTransaction( receipt.tx, RewardToken, 'RewardsUpdated', { - periodRewards: newTotalRewards.sub(prevTotalRewards), + periodRewards: periodReward, totalRewards: newTotalRewards, - protocolReward: new BN(0), + protocolReward: periodReward + .mul(await rewardToken.protocolFee()) + .div(new BN(10000)), } ); }); - it('assigns protocol fee to distributor', async () => { - await rewardToken.setProtocolFeeRecipient(constants.ZERO_ADDRESS, { - from: admin, - }); + it('accumulates penalty', async () => { + let penalty = ether('10'); + let totalRewards = await rewardToken.totalRewards(); + let totalPenalty = await rewardToken.totalPenalty(); - let periodReward = ether('10'); - let prevTotalRewards = await rewardToken.totalRewards(); - let newTotalRewards = prevTotalRewards.add(periodReward); let receipt = await setTotalRewards({ rewardToken, - oracles, - pool, - totalRewards: newTotalRewards, - oracleAccounts, + vault, + totalRewards: totalRewards.sub(penalty), }); await expectEvent.inTransaction( receipt.tx, RewardToken, 'RewardsUpdated', { - periodRewards: newTotalRewards.sub(prevTotalRewards), - totalRewards: newTotalRewards, + periodRewards: '0', + totalRewards: totalRewards, + protocolReward: '0', + } + ); + totalPenalty = totalPenalty.add(ether('10')); + expect(await rewardToken.totalPenalty()).to.bignumber.equal(totalPenalty); + + // reduces penalty partially + let periodReward = ether('5'); + totalPenalty = totalPenalty.sub(periodReward); + receipt = await setTotalRewards({ + rewardToken, + vault, + totalRewards: totalRewards.add(periodReward), + }); + await expectEvent.inTransaction( + receipt.tx, + RewardToken, + 'RewardsUpdated', + { + periodRewards: '0', + totalRewards: totalRewards, + protocolReward: '0', + } + ); + expect(await rewardToken.totalPenalty()).to.bignumber.equal(totalPenalty); + + // reduces penalty completely + periodReward = ether('1'); + receipt = await setTotalRewards({ + rewardToken, + vault, + totalRewards: totalRewards.add(periodReward).add(totalPenalty), + }); + totalPenalty = new BN(0); + totalRewards = totalRewards.add(periodReward); + await expectEvent.inTransaction( + receipt.tx, + RewardToken, + 'RewardsUpdated', + { + periodRewards: periodReward, + totalRewards: totalRewards, protocolReward: periodReward .mul(await rewardToken.protocolFee()) .div(new BN(10000)), } ); + expect(await rewardToken.totalPenalty()).to.bignumber.equal(totalPenalty); + }); + + it('penalty cannot exceed total assets', async () => { + let totalAssets = await rewardToken.totalAssets(); + + await expectRevert( + rewardToken.updateTotalRewards(totalAssets.add(new BN(1)).neg(), { + from: vault, + }), + 'RewardToken: invalid penalty amount' + ); }); }); describe('transfer', () => { const stakedAmount1 = ether('4'); const stakedAmount2 = ether('5'); - const [sender1, sender2] = accounts; + const [sender1, sender2] = others; let rewardAmount1, rewardAmount2; beforeEach(async () => { - await pool.setMinActivatingDeposit(stakedAmount2.add(ether('1')), { - from: admin, - }); - await stakeGNO({ account: sender1, amount: stakedAmount1, pool }); - await stakeGNO({ account: sender2, amount: stakedAmount2, pool }); + await addStakedToken(stakedToken, sender1, stakedAmount1); + await addStakedToken(stakedToken, sender2, stakedAmount2); totalSupply = (await rewardToken.totalSupply()).add(ether('10')); await setTotalRewards({ totalRewards: totalSupply, rewardToken, - pool, - oracles, - oracleAccounts, + vault, }); rewardAmount1 = await rewardToken.balanceOf(sender1); @@ -384,37 +410,19 @@ contract('RewardToken', ([sender, merkleDistributor, ...accounts]) => { rewardToken.address, merkleDistributor ); - await oracles.addOracle(multicallMock.address, { - from: admin, - }); + + const signer = await ethers.provider.getSigner(contractSettings.admin); + await upgradeRewardToken(signer, multicallMock.address); await rewardToken.approve(multicallMock.address, rewardAmount1, { from: sender1, }); - let currentNonce = await oracles.currentRewardsNonce(); - let totalRewards = (await rewardToken.totalRewards()).add(ether('10')); - let activatedValidators = await pool.activatedValidators(); - let signatures = []; - let encoded = defaultAbiCoder.encode( - ['uint256', 'uint256', 'uint256'], - [ - currentNonce.toString(), - activatedValidators.toString(), - totalRewards.toString(), - ] - ); - let candidateId = hexlify(keccak256(encoded)); - for (const oracleAccount of oracleAccounts) { - signatures.push(await web3.eth.sign(candidateId, oracleAccount)); - } - + const rewardsDelta = ether('10'); await expectRevert( multicallMock.updateTotalRewardsAndTransferRewards( - totalRewards, - activatedValidators, + rewardsDelta, sender2, - signatures, { from: sender1, } @@ -431,36 +439,18 @@ contract('RewardToken', ([sender, merkleDistributor, ...accounts]) => { rewardToken.address, merkleDistributor ); - await oracles.addOracle(multicallMock.address, { - from: admin, - }); + + const signer = await ethers.provider.getSigner(contractSettings.admin); + await upgradeRewardToken(signer, multicallMock.address); await rewardToken.approve(multicallMock.address, rewardAmount1, { from: sender1, }); - let currentNonce = await oracles.currentRewardsNonce(); - let totalRewards = (await rewardToken.totalRewards()).add(ether('10')); - let activatedValidators = await pool.activatedValidators(); - let signatures = []; - let encoded = defaultAbiCoder.encode( - ['uint256', 'uint256', 'uint256'], - [ - currentNonce.toString(), - activatedValidators.toString(), - totalRewards.toString(), - ] - ); - let candidateId = hexlify(keccak256(encoded)); - for (const oracleAccount of oracleAccounts) { - signatures.push(await web3.eth.sign(candidateId, oracleAccount)); - } - + const rewardsDelta = ether('10'); let receipt = await multicallMock.transferRewardsAndUpdateTotalRewards( - totalRewards, - activatedValidators, + rewardsDelta, sender2, - signatures, { from: sender1, } @@ -473,4 +463,176 @@ contract('RewardToken', ([sender, merkleDistributor, ...accounts]) => { }); }); }); + + describe('migrate', () => { + const stakedAmount = ether('1'); + const rewardAmount = ether('1'); + let vaultMock; + + beforeEach(async () => { + vaultMock = await VaultMock.new(rewardToken.address); + const signer = await ethers.provider.getSigner(contractSettings.admin); + await upgradeRewardToken(signer, vaultMock.address); + await addStakedToken(stakedToken, sender, stakedAmount); + await addRewardToken(rewardToken, sender, rewardAmount); + }); + + it('cannot migrate to zero address receiver', async () => { + await expectRevert( + rewardToken.migrate( + constants.ZERO_ADDRESS, + stakedAmount, + rewardAmount, + { + from: sender, + } + ), + 'RewardToken: invalid receiver' + ); + }); + + it('cannot migrate after total rewards update in the same block', async () => { + const multicallMock = await MulticallMock.new( + oracles.address, + contracts.stakedToken, + contracts.rewardToken, + merkleDistributor + ); + await rewardToken.transfer(multicallMock.address, ether('1'), { + from: sender, + }); + await stakedToken.transfer(multicallMock.address, ether('1'), { + from: sender, + }); + const signer = await ethers.provider.getSigner(contractSettings.admin); + await upgradeRewardToken(signer, multicallMock.address); + + const rewardsDelta = ether('10'); + await expectRevert( + multicallMock.updateTotalRewardsAndMigrate(rewardsDelta, { + from: sender, + }), + 'RewardToken: cannot migrate during rewards update' + ); + }); + + it('deducts penalty from user assets', async () => { + let penalty = ether('-10'); + await vaultMock.updateTotalRewards(penalty, { + from: vault, + }); + let totalPenalty = await rewardToken.totalPenalty(); + const totalRewards = await rewardToken.totalSupply(); + const totalStaked = await stakedToken.totalSupply(); + + await rewardToken.migrate(sender, stakedAmount, rewardAmount, { + from: sender, + }); + + expect(await rewardToken.totalPenalty()).to.be.bignumber.lessThan( + totalPenalty + ); + expect(await vaultMock.migratedAssets()).to.be.bignumber.lessThan( + stakedAmount.add(rewardAmount) + ); + + await checkStakedToken({ + stakedToken, + totalSupply: totalStaked.sub(stakedAmount), + account: sender, + balance: new BN(0), + }); + + await checkRewardToken({ + rewardToken, + totalSupply: totalRewards.sub(rewardAmount), + account: sender, + balance: new BN(0), + }); + }); + + it('cannot migrate zero assets', async () => { + await expectRevert( + rewardToken.migrate(sender, new BN(0), new BN(0), { + from: sender, + }), + 'RewardToken: zero assets' + ); + }); + + it('cannot migrate reward tokens larger than balance', async () => { + await expectRevert( + rewardToken.migrate(sender, stakedAmount.add(new BN(1)), rewardAmount, { + from: sender, + }), + 'SafeMath: subtraction overflow' + ); + }); + + it('cannot migrate staked tokens larger than balance', async () => { + await expectRevert( + rewardToken.migrate(sender, stakedAmount, rewardAmount.add(new BN(1)), { + from: sender, + }), + 'SafeMath: subtraction overflow' + ); + }); + + it('can migrate reward and staked tokens', async () => { + let totalRewards = await rewardToken.totalSupply(); + let totalStaked = await stakedToken.totalSupply(); + let receipt = await rewardToken.migrate( + sender, + stakedAmount, + rewardAmount, + { + from: sender, + } + ); + const assets = stakedAmount.add(rewardAmount); + totalRewards = totalRewards.sub(rewardAmount); + totalStaked = totalStaked.sub(stakedAmount); + + await expectEvent.inTransaction(receipt.tx, VaultMock, 'Migrated', { + receiver: sender, + assets, + }); + + await expectEvent.inTransaction(receipt.tx, RewardToken, 'Transfer', { + from: sender, + to: constants.ZERO_ADDRESS, + value: rewardAmount, + }); + + await expectEvent.inTransaction(receipt.tx, StakedToken, 'Transfer', { + from: sender, + to: constants.ZERO_ADDRESS, + value: stakedAmount, + }); + + await checkStakedToken({ + stakedToken, + totalSupply: totalStaked, + account: sender, + balance: new BN(0), + }); + + await checkRewardToken({ + rewardToken, + totalSupply: totalRewards, + account: sender, + balance: new BN(0), + }); + + expect(await rewardToken.totalSupply()).to.be.bignumber.equal( + totalRewards + ); + expect(await stakedToken.totalSupply()).to.be.bignumber.equal( + totalStaked + ); + expect(await vaultMock.migratedAssets()).to.be.bignumber.equal( + stakedAmount.add(rewardAmount) + ); + }); + }); }); diff --git a/test/tokens/StakedToken.test.js b/test/tokens/StakedToken.test.js index d0a457fc..327d1d19 100644 --- a/test/tokens/StakedToken.test.js +++ b/test/tokens/StakedToken.test.js @@ -1,4 +1,3 @@ -const { hexlify, keccak256, defaultAbiCoder } = require('ethers/lib/utils'); const { expect } = require('chai'); const { expectRevert, @@ -13,65 +12,34 @@ const { stopImpersonatingAccount, resetFork, checkStakedToken, - setupOracleAccounts, setTotalRewards, - stakeGNO, + addStakedToken, } = require('../utils'); -const { upgradeContracts } = require('../../deployments'); -const { contractSettings } = require('../../deployments/settings'); +const { upgradeContracts, upgradeRewardToken } = require('../../deployments'); +const { contractSettings, contracts } = require('../../deployments/settings'); +const { ethers } = require('hardhat'); const StakedToken = artifacts.require('StakedToken'); const RewardToken = artifacts.require('RewardToken'); -const Pool = artifacts.require('Pool'); const Oracles = artifacts.require('Oracles'); const MulticallMock = artifacts.require('MulticallMock'); contract('StakedToken', (accounts) => { const admin = contractSettings.admin; - const [merkleDistributor, sender1, sender2, ...otherAccounts] = accounts; - let stakedToken, - rewardToken, - pool, - totalSupply, - oracles, - oracleAccounts, - activatedValidators, - totalRewards, - signatures, - contracts; + const [merkleDistributor, sender1, sender2, vault] = accounts; + let stakedToken, rewardToken, oracles, totalSupply, totalRewards; beforeEach(async () => { await impersonateAccount(admin); await send.ether(sender1, admin, ether('5')); - contracts = await upgradeContracts(); + await upgradeContracts(vault); + stakedToken = await StakedToken.at(contracts.stakedToken); - pool = await Pool.at(contracts.pool); oracles = await Oracles.at(contracts.oracles); - oracleAccounts = await setupOracleAccounts({ - oracles, - admin, - accounts: otherAccounts, - }); rewardToken = await RewardToken.at(contracts.rewardToken); - totalRewards = (await rewardToken.totalRewards()).add(ether('10')); - let currentNonce = await oracles.currentRewardsNonce(); - activatedValidators = await pool.activatedValidators(); - let encoded = defaultAbiCoder.encode( - ['uint256', 'uint256', 'uint256'], - [ - currentNonce.toString(), - activatedValidators.toString(), - totalRewards.toString(), - ] - ); - signatures = []; - let candidateId = hexlify(keccak256(encoded)); - for (const oracleAccount of oracleAccounts) { - signatures.push(await web3.eth.sign(candidateId, oracleAccount)); - } - + totalRewards = await rewardToken.totalRewards(); totalSupply = await stakedToken.totalSupply(); }); @@ -79,49 +47,12 @@ contract('StakedToken', (accounts) => { afterEach(async () => resetFork()); - describe('mint', () => { - it('anyone cannot mint sGNO tokens', async () => { - await expectRevert( - stakedToken.mint(sender1, ether('10'), { - from: sender1, - }), - 'StakedToken: access denied' - ); - await checkStakedToken({ - stakedToken, - totalSupply, - account: sender1, - balance: new BN(0), - }); - }); - - it('updates distributor principal when deposited by account with disabled rewards', async () => { - // disable rewards - let prevPrincipal = await stakedToken.distributorPrincipal(); - await stakedToken.toggleRewards(sender1, true, { from: admin }); - let amount = ether('10'); - let receipt = await stakeGNO({ account: sender1, amount, pool }); - await expectEvent.inTransaction(receipt.tx, StakedToken, 'Transfer', { - from: constants.ZERO_ADDRESS, - to: sender1, - value: amount, - }); - expect(await stakedToken.distributorPrincipal()).to.bignumber.equal( - prevPrincipal.add(amount) - ); - }); - }); - describe('transfer', () => { let value = ether('10'); let distributorPrincipal; beforeEach(async () => { - await pool.setMinActivatingDeposit(value.add(ether('1')), { - from: admin, - }); - await stakeGNO({ account: sender1, amount: value, pool }); - totalSupply = totalSupply.add(value); + await addStakedToken(stakedToken, sender1, value); distributorPrincipal = await stakedToken.distributorPrincipal(); }); @@ -212,7 +143,7 @@ contract('StakedToken', (accounts) => { }); }); - it('can transfer sGNO tokens to different account', async () => { + it('can transfer staked tokens to different account', async () => { let receipt = await stakedToken.transfer(sender2, value, { from: sender1, }); @@ -238,14 +169,12 @@ contract('StakedToken', (accounts) => { }); }); - it('preserves rewards during sGNO transfer', async () => { + it('preserves rewards during staked tokens transfer', async () => { let totalRewards = (await rewardToken.totalSupply()).add(ether('10')); await setTotalRewards({ totalRewards, rewardToken, - pool, - oracles, - oracleAccounts, + vault, }); let rewardAmount = await rewardToken.balanceOf(sender1); @@ -377,20 +306,18 @@ contract('StakedToken', (accounts) => { contracts.rewardToken, merkleDistributor ); - await oracles.addOracle(multicallMock.address, { - from: admin, - }); + + const signer = await ethers.provider.getSigner(contractSettings.admin); + await upgradeRewardToken(signer, multicallMock.address); await stakedToken.approve(multicallMock.address, value, { from: sender1, }); await expectRevert( - multicallMock.updateTotalRewardsAndTransferStakedTokens( + multicallMock.updateTotalRewardsAndTransferStakedEth( totalRewards, - activatedValidators, sender2, - signatures, { from: sender1, } @@ -407,24 +334,21 @@ contract('StakedToken', (accounts) => { contracts.rewardToken, merkleDistributor ); - await oracles.addOracle(multicallMock.address, { - from: admin, - }); + + const signer = await ethers.provider.getSigner(contractSettings.admin); + await upgradeRewardToken(signer, multicallMock.address); await stakedToken.approve(multicallMock.address, value, { from: sender1, }); - let receipt = - await multicallMock.transferStakedTokensAndUpdateTotalRewards( - totalRewards, - activatedValidators, - sender2, - signatures, - { - from: sender1, - } - ); + let receipt = await multicallMock.transferStakedEthAndUpdateTotalRewards( + totalRewards, + sender2, + { + from: sender1, + } + ); await expectEvent.inTransaction(receipt.tx, StakedToken, 'Transfer', { from: sender1, @@ -432,5 +356,21 @@ contract('StakedToken', (accounts) => { value, }); }); + + it('cannot burn from not rewardToken', async () => { + await expectRevert( + stakedToken.burn(sender1, value, { + from: sender1, + }), + 'StakedToken: access denied' + ); + + await checkStakedToken({ + stakedToken, + totalSupply, + account: sender1, + balance: value, + }); + }); }); }); diff --git a/test/tokens/toggleRewards.test.js b/test/tokens/toggleRewards.test.js index c61cad52..1b921085 100644 --- a/test/tokens/toggleRewards.test.js +++ b/test/tokens/toggleRewards.test.js @@ -10,28 +10,19 @@ const { impersonateAccount, stopImpersonatingAccount, resetFork, - setupOracleAccounts, setTotalRewards, - stakeGNO, + addStakedToken, } = require('../utils'); -const { contractSettings } = require('../../deployments/settings'); +const { contractSettings, contracts } = require('../../deployments/settings'); const { upgradeContracts } = require('../../deployments'); const RewardToken = artifacts.require('RewardToken'); -const Oracles = artifacts.require('Oracles'); -const Pool = artifacts.require('Pool'); const StakedToken = artifacts.require('StakedToken'); contract('StakedToken (toggle rewards)', ([_, ...accounts]) => { let admin = contractSettings.admin; - let oracles, - rewardToken, - stakedToken, - distributorReward, - pool, - oracleAccounts, - distributorPrincipal; - let [account, anyone] = accounts; + let rewardToken, stakedToken, distributorReward, distributorPrincipal; + let [account, anyone, vault] = accounts; after(async () => stopImpersonatingAccount(admin)); @@ -39,12 +30,9 @@ contract('StakedToken (toggle rewards)', ([_, ...accounts]) => { await impersonateAccount(admin); await send.ether(anyone, admin, ether('5')); - let contracts = await upgradeContracts(); - oracles = await Oracles.at(contracts.oracles); - pool = await Pool.at(contracts.pool); + await upgradeContracts(vault); rewardToken = await RewardToken.at(contracts.rewardToken); stakedToken = await StakedToken.at(contracts.stakedToken); - oracleAccounts = await setupOracleAccounts({ oracles, admin, accounts }); distributorPrincipal = await stakedToken.distributorPrincipal(); distributorReward = await rewardToken.balanceOf(constants.ZERO_ADDRESS); }); @@ -83,7 +71,7 @@ contract('StakedToken (toggle rewards)', ([_, ...accounts]) => { let deposit = ether('5'); // mint sGNO for disabled account - await stakeGNO({ account: account, amount: deposit, pool }); + await addStakedToken(stakedToken, account, deposit); let receipt = await stakedToken.toggleRewards(account, true, { from: admin, @@ -125,8 +113,7 @@ contract('StakedToken (toggle rewards)', ([_, ...accounts]) => { let deposit = ether('5'); // mint sGNO for disabled account - await stakeGNO({ account, amount: deposit, pool }); - + await addStakedToken(stakedToken, account, deposit); expect(await stakedToken.balanceOf(account)).to.be.bignumber.equal( deposit ); @@ -143,8 +130,7 @@ contract('StakedToken (toggle rewards)', ([_, ...accounts]) => { ).to.be.bignumber.equal(distributorReward); // mint sGNO for normal account - await stakeGNO({ account: anyone, amount: ether('5'), pool }); - + await addStakedToken(stakedToken, anyone, deposit); expect(await stakedToken.balanceOf(anyone)).to.be.bignumber.equal( deposit ); @@ -156,10 +142,8 @@ contract('StakedToken (toggle rewards)', ([_, ...accounts]) => { let totalRewards = (await rewardToken.totalRewards()).add(ether('10')); await setTotalRewards({ rewardToken, - oracles, - oracleAccounts, - pool, totalRewards, + vault, }); // arrived reward @@ -202,13 +186,9 @@ contract('StakedToken (toggle rewards)', ([_, ...accounts]) => { }); it('toggling rewards does not affect current rewards balance', async () => { - await rewardToken.setProtocolFeeRecipient(admin, { - from: admin, - }); - // mint sGNO for disabled account - let deposit = ether('32'); - await stakeGNO({ account, amount: deposit, pool }); + let deposit = ether('5'); + await addStakedToken(stakedToken, account, deposit); // manual checkpoints update await rewardToken.updateRewardCheckpoint(account); @@ -221,10 +201,8 @@ contract('StakedToken (toggle rewards)', ([_, ...accounts]) => { let totalRewards = (await rewardToken.totalRewards()).add(ether('10')); await setTotalRewards({ rewardToken, - oracles, - oracleAccounts, - pool, totalRewards, + vault, }); // manual checkpoints update @@ -286,10 +264,8 @@ contract('StakedToken (toggle rewards)', ([_, ...accounts]) => { totalRewards = totalRewards.add(ether('10')); await setTotalRewards({ rewardToken, - oracles, - oracleAccounts, - pool, totalRewards, + vault, }); // manual checkpoints update diff --git a/test/utils.js b/test/utils.js index dd661b35..845e2389 100644 --- a/test/utils.js +++ b/test/utils.js @@ -1,51 +1,19 @@ const { expect } = require('chai'); const hre = require('hardhat'); -const { fromRpcSig } = require('ethereumjs-util'); -const ethSigUtil = require('eth-sig-util'); const { hexlify, keccak256, defaultAbiCoder } = require('ethers/lib/utils'); -const { - BN, - ether, - expectEvent, - constants, - send, -} = require('@openzeppelin/test-helpers'); -const { contracts } = require('../deployments/settings'); - -const IDepositContract = artifacts.require('IDepositContract'); -const IGCToken = artifacts.require('IGCToken'); - -function getDepositAmount({ min = new BN('1'), max = ether('1000') } = {}) { - return ether(Math.random().toFixed(8)) - .mul(max.sub(min)) - .div(ether('1')) - .add(min); -} +const { BN, send, ether } = require('@openzeppelin/test-helpers'); -async function checkValidatorRegistered({ - transaction, - pubKey, - signature, - withdrawalCredentials, - validatorDepositAmount = ether('32'), -}) { - // Check VRC record created - await expectEvent.inTransaction( - transaction, - IDepositContract, - 'DepositEvent', - { - pubkey: pubKey, - withdrawal_credentials: withdrawalCredentials, - amount: web3.utils.bytesToHex( - new BN(web3.utils.fromWei(validatorDepositAmount, 'gwei')).toArray( - 'le', - 8 - ) - ), - signature: signature, - } - ); +async function mintTokens(token, to, amount) { + let owner = await token.owner(); + await impersonateAccount(owner); + await hre.network.provider.request({ + method: 'hardhat_setCode', + params: [owner, '0x'], + }); + await send.ether(to, owner, ether('1')); + await token.mint(to, amount, { + from: owner, + }); } async function checkStakedToken({ @@ -124,44 +92,26 @@ async function setActivatedValidators({ return receipt; } -async function setTotalRewards({ - rewardToken, - oracles, - oracleAccounts, - pool, - totalRewards, -}) { - if ((await rewardToken.totalSupply()).eq(totalRewards)) { - return; - } - // calculate candidate ID - let activatedValidators = await pool.activatedValidators(); - let nonce = await oracles.currentRewardsNonce(); - let encoded = defaultAbiCoder.encode( - ['uint256', 'uint256', 'uint256'], - [nonce.toString(), activatedValidators.toString(), totalRewards.toString()] - ); - let candidateId = hexlify(keccak256(encoded)); - - // prepare signatures - let signatures = []; - for (let i = 0; i < oracleAccounts.length; i++) { - await impersonateAccount(oracleAccounts[i]); - let signature = await web3.eth.sign(candidateId, oracleAccounts[i]); - signatures.push(signature); - } +async function setTotalRewards({ rewardToken, totalRewards, vault }) { + const totalSupply = await rewardToken.totalSupply(); + const totalPenalty = await rewardToken.totalPenalty(); + let delta = totalRewards.sub(totalSupply); // update total rewards - let receipt = await oracles.submitRewards( - totalRewards, - activatedValidators, - signatures, - { - from: oracleAccounts[0], - } + let receipt = await rewardToken.updateTotalRewards(delta, { + from: vault, + }); + if (delta.isNeg()) { + delta = new BN(0); + } + if (totalPenalty.gt(delta)) { + delta = new BN(0); + } else { + delta = delta.sub(totalPenalty); + } + expect(await rewardToken.totalSupply()).to.bignumber.equal( + totalSupply.add(delta) ); - expect(await rewardToken.totalSupply()).to.bignumber.equal(totalRewards); - return receipt; } @@ -236,16 +186,6 @@ async function registerValidators({ } async function impersonateAccount(account) { - // remove code if it's a contract - await hre.network.provider.send('hardhat_setCode', [account, '0x']); - - // set balance to 1000 xDAI - await hre.network.provider.send('hardhat_setBalance', [ - account, - '0x3635c9adc5dea00000', - ]); - - // impersonate account return hre.network.provider.request({ method: 'hardhat_impersonateAccount', params: [account], @@ -259,6 +199,50 @@ async function stopImpersonatingAccount(account) { }); } +async function mintMGNOTokens(mgnoToken, account, value) { + // random mGNO holder + let holder = '0x6b504204a85e0231b1a1b1926b9264939f29c65e'; + await send.ether(account, holder, ether('5')); + await impersonateAccount(holder); + await mgnoToken.transfer(account, value, { + from: holder, + }); + await stopImpersonatingAccount(holder); +} + +async function mintGNOTokens(gnoToken, account, value) { + // random GNO holder + let holder = '0x458cd345b4c05e8df39d0a07220feb4ec19f5e6f'; + await send.ether(account, holder, ether('5')); + await impersonateAccount(holder); + await gnoToken.transfer(account, value, { + from: holder, + }); + await stopImpersonatingAccount(holder); +} + +async function addStakedToken(stakedToken, account, value) { + // random sGNO holder + let holder = '0x411fA33f660EDded5dddc57cd50ac33eB4684C6B'; + await send.ether(account, holder, ether('5')); + await impersonateAccount(holder); + await stakedToken.transfer(account, value, { + from: holder, + }); + await stopImpersonatingAccount(holder); +} + +async function addRewardToken(rewardToken, account, value) { + // random rGNO holder + let holder = '0xb0e83C2D71A991017e0116d58c5765Abc57384af'; + await send.ether(account, holder, ether('5')); + await impersonateAccount(holder); + await rewardToken.transfer(account, value, { + from: holder, + }); + await stopImpersonatingAccount(holder); +} + async function resetFork() { await hre.network.provider.request({ method: 'hardhat_reset', @@ -287,7 +271,7 @@ async function setupOracleAccounts({ admin, oracles, accounts }) { // add oracles let oracleAccounts = []; - for (let i = 0; i < 4; i++) { + for (let i = 0; i < totalOracles; i++) { let newOracle = accounts[i]; await oracles.addOracle(newOracle, { from: admin, @@ -298,169 +282,12 @@ async function setupOracleAccounts({ admin, oracles, accounts }) { return oracleAccounts; } -async function mintTokens(token, to, amount) { - let owner = await token.owner(); - await impersonateAccount(owner); - await send.ether(to, owner, ether('1')); - await token.mint(to, amount, { - from: owner, - }); -} - -async function mintMGNOTokens(to, amount) { - let gnoToken = await IGCToken.at(contracts.GNOToken); - await mintTokens(gnoToken, to, amount); - - return gnoToken.transferAndCall(contracts.MGNOWrapper, amount, '0x', { - from: to, - }); -} - -async function stakeGNO({ - account, - amount, - pool, - recipient = constants.ZERO_ADDRESS, - referrer = constants.ZERO_ADDRESS, - hasRevenueShare = false, - noAllowance = false, -}) { - let gnoToken = await IGCToken.at(contracts.GNOToken); - await mintTokens(gnoToken, account, amount); - - if (!noAllowance) { - await gnoToken.approve(pool.address, amount, { from: account }); - } - return pool.stakeGNO(amount, recipient, referrer, hasRevenueShare, { - from: account, - }); -} - -const buildPermitData = ({ - verifyingContract, - holder, - spender, - name, - expiry = constants.MAX_UINT256, - allowed = true, - version = '1', - chainId = '100', - nonce = 0, -}) => ({ - primaryType: 'Permit', - types: { - EIP712Domain: [ - { name: 'name', type: 'string' }, - { name: 'version', type: 'string' }, - { name: 'chainId', type: 'uint256' }, - { name: 'verifyingContract', type: 'address' }, - ], - Permit: [ - { name: 'holder', type: 'address' }, - { name: 'spender', type: 'address' }, - { name: 'nonce', type: 'uint256' }, - { name: 'expiry', type: 'uint256' }, - { name: 'allowed', type: 'bool' }, - ], - }, - domain: { name, version, chainId, verifyingContract }, - message: { holder, spender, nonce, expiry, allowed }, -}); - -async function stakeGNOWithPermit({ - account, - amount, - pool, - minter, - recipient = constants.ZERO_ADDRESS, - referrer = constants.ZERO_ADDRESS, - hasRevenueShare = false, - invalidHolder = false, -}) { - let gnoToken = await IGCToken.at(contracts.GNOToken); - - // generate signature - let nonce = await gnoToken.nonces(account.address); - let expiry = constants.MAX_UINT256; - const data = buildPermitData({ - nonce, - verifyingContract: gnoToken.address, - holder: invalidHolder ? constants.ZERO_ADDRESS : account.address, - spender: pool.address, - name: await gnoToken.name(), - expiry, - }); - const signature = ethSigUtil.signTypedMessage( - Buffer.from(account.privateKey.substring(2), 'hex'), - { data } - ); - let { v, r, s } = fromRpcSig(signature); - - // mint tokens - let owner = await gnoToken.owner(); - await impersonateAccount(owner); - await send.ether(minter, owner, ether('1')); - await gnoToken.mint(account.address, amount, { - from: owner, - }); - - let encodedData = pool.contract.methods - .stakeGNOWithPermit( - amount, - recipient, - referrer, - hasRevenueShare, - nonce, - expiry, - v, - r, - s - ) - .encodeABI(); - - const tx = { - from: account.address, - to: pool.address, - data: encodedData, - gas: 1000000, - }; - - let signedTx = await web3.eth.accounts.signTransaction( - tx, - account.privateKey - ); - return web3.eth.sendSignedTransaction(signedTx.rawTransaction); -} - -async function stakeMGNO({ - account, - amount, - pool, - recipient = constants.ZERO_ADDRESS, - referrer = constants.ZERO_ADDRESS, - hasRevenueShare = false, - noAllowance = false, -}) { - await mintMGNOTokens(account, amount); - let mgnoToken = await IGCToken.at(contracts.MGNOToken); - if (!noAllowance) { - await mgnoToken.approve(pool.address, amount, { from: account }); - } - return pool.stakeMGNO(amount, recipient, referrer, hasRevenueShare, { - from: account, - }); -} - module.exports = { - checkValidatorRegistered, - getDepositAmount, checkStakedToken, checkRewardToken, mintMGNOTokens, mintTokens, - stakeGNO, - stakeGNOWithPermit, - stakeMGNO, + mintGNOTokens, impersonateAccount, stopImpersonatingAccount, resetFork, @@ -469,4 +296,6 @@ module.exports = { setMerkleRoot, setupOracleAccounts, registerValidators, + addStakedToken, + addRewardToken, }; From c68ff0d4745623ab5326a42303847525739473c9 Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Sat, 9 Mar 2024 13:27:36 +0200 Subject: [PATCH 2/3] Add chiado, update StakedTokenMock --- .openzeppelin/unknown-10200.json | 1131 +++++++++++++++++++++++++++ contracts/mocks/StakedTokenMock.sol | 2 +- 2 files changed, 1132 insertions(+), 1 deletion(-) create mode 100644 .openzeppelin/unknown-10200.json diff --git a/.openzeppelin/unknown-10200.json b/.openzeppelin/unknown-10200.json new file mode 100644 index 00000000..86f2d8a9 --- /dev/null +++ b/.openzeppelin/unknown-10200.json @@ -0,0 +1,1131 @@ +{ + "manifestVersion": "3.2", + "admin": { + "address": "0xDF82E5D27E175618e5bC4581ee336F59AdabfBB2", + "txHash": "0xbcf0a4fc8e66afad53704c0277cb5538383255fb5fdb0708901691f66d63fbc3", + "deployTransaction": { + "hash": "0xbcf0a4fc8e66afad53704c0277cb5538383255fb5fdb0708901691f66d63fbc3", + "type": 2, + "accessList": [], + "blockHash": null, + "blockNumber": null, + "transactionIndex": null, + "confirmations": 0, + "from": "0xFF2B6d2d5c205b99E2e6f607B6aFA3127B9957B6", + "maxPriorityFeePerGas": { + "type": "BigNumber", + "hex": "0x0287e70979" + }, + "maxFeePerGas": { + "type": "BigNumber", + "hex": "0x0287e70981" + }, + "gasLimit": { + "type": "BigNumber", + "hex": "0x076330" + }, + "to": null, + "value": { + "type": "BigNumber", + "hex": "0x00" + }, + "nonce": 2, + "data": "0x608060405234801561001057600080fd5b50600080546001600160a01b031916339081178255604051909182917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a350610759806100616000396000f3fe60806040526004361061007b5760003560e01c80639623609d1161004e5780639623609d1461011157806399a88ec414610124578063f2fde38b14610144578063f3b7dead146101645761007b565b8063204e1c7a14610080578063715018a6146100bc5780637eff275e146100d35780638da5cb5b146100f3575b600080fd5b34801561008c57600080fd5b506100a061009b366004610515565b610184565b6040516001600160a01b03909116815260200160405180910390f35b3480156100c857600080fd5b506100d1610215565b005b3480156100df57600080fd5b506100d16100ee366004610554565b610292565b3480156100ff57600080fd5b506000546001600160a01b03166100a0565b6100d161011f36600461058c565b61031c565b34801561013057600080fd5b506100d161013f366004610554565b6103ad565b34801561015057600080fd5b506100d161015f366004610515565b610405565b34801561017057600080fd5b506100a061017f366004610515565b6104ef565b6000806000836001600160a01b03166040516101aa90635c60da1b60e01b815260040190565b600060405180830381855afa9150503d80600081146101e5576040519150601f19603f3d011682016040523d82523d6000602084013e6101ea565b606091505b5091509150816101f957600080fd5b8080602001905181019061020d9190610538565b949350505050565b6000546001600160a01b031633146102485760405162461bcd60e51b815260040161023f906106c0565b60405180910390fd5b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b6000546001600160a01b031633146102bc5760405162461bcd60e51b815260040161023f906106c0565b6040516308f2839760e41b81526001600160a01b038281166004830152831690638f283970906024015b600060405180830381600087803b15801561030057600080fd5b505af1158015610314573d6000803e3d6000fd5b505050505050565b6000546001600160a01b031633146103465760405162461bcd60e51b815260040161023f906106c0565b60405163278f794360e11b81526001600160a01b03841690634f1ef286903490610376908690869060040161065d565b6000604051808303818588803b15801561038f57600080fd5b505af11580156103a3573d6000803e3d6000fd5b5050505050505050565b6000546001600160a01b031633146103d75760405162461bcd60e51b815260040161023f906106c0565b604051631b2ce7f360e11b81526001600160a01b038281166004830152831690633659cfe6906024016102e6565b6000546001600160a01b0316331461042f5760405162461bcd60e51b815260040161023f906106c0565b6001600160a01b0381166104945760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840161023f565b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b6000806000836001600160a01b03166040516101aa906303e1469160e61b815260040190565b600060208284031215610526578081fd5b81356105318161070b565b9392505050565b600060208284031215610549578081fd5b81516105318161070b565b60008060408385031215610566578081fd5b82356105718161070b565b915060208301356105818161070b565b809150509250929050565b6000806000606084860312156105a0578081fd5b83356105ab8161070b565b925060208401356105bb8161070b565b9150604084013567ffffffffffffffff808211156105d7578283fd5b818601915086601f8301126105ea578283fd5b8135818111156105fc576105fc6106f5565b604051601f8201601f19908116603f01168101908382118183101715610624576106246106f5565b8160405282815289602084870101111561063c578586fd5b82602086016020830137856020848301015280955050505050509250925092565b600060018060a01b038416825260206040818401528351806040850152825b818110156106985785810183015185820160600152820161067c565b818111156106a95783606083870101525b50601f01601f191692909201606001949350505050565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b634e487b7160e01b600052604160045260246000fd5b6001600160a01b038116811461072057600080fd5b5056fea2646970667358221220d849f96f3086b9f82cdcf665adb8c697ace05638da1c7c16ab2d26293717af6764736f6c63430008020033", + "r": "0x0023cff77ce6e99d8452e3a7f9979c70b55fd9dc21f8b6f4cad4c57b063c432f", + "s": "0x467782ecfb786d3cb48f29edc9761bd22d77736b5989b07a3ce8ceb63414fff0", + "v": 0, + "creates": "0xDF82E5D27E175618e5bC4581ee336F59AdabfBB2", + "chainId": 10200 + } + }, + "proxies": [ + { + "address": "0xfe076029B7D46fbe2ad4B9CBf377aA10B309e560", + "txHash": "0x52c706e479cd8676f595abab30f9ff2a929d2cd30238b22f2ab3ba759d9d5803", + "kind": "transparent" + }, + { + "address": "0xF2f5A23f849e02001da0DfdeC0F4CD3c3a79337e", + "txHash": "0xc86a764323ea8a0c0a1dcd5683271819f1ed4f8c213ad4aa9fd7e8075c9d1062", + "kind": "transparent" + } + ], + "impls": { + "a5f0f709f937a117c0db3ad4822068afa3d7a853b0aa491aaba513301f4ecdc6": { + "address": "0x9747e1fF73f1759217AFD212Dd36d21360D0880A", + "txHash": "0x1da222debb4bb65b994844e20abbdfc965bbe81812bde70b5e470c32fcaab4f3", + "layout": { + "storage": [ + { + "contract": "Initializable", + "label": "_initialized", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol:25" + }, + { + "contract": "Initializable", + "label": "_initializing", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol:30" + }, + { + "contract": "ContextUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)50_storage", + "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:31" + }, + { + "contract": "PausableUpgradeable", + "label": "_paused", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol:28" + }, + { + "contract": "PausableUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol:96" + }, + { + "contract": "AccessControlUpgradeable", + "label": "_roles", + "type": "t_mapping(t_bytes32,t_struct(RoleData)39_storage)", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:61" + }, + { + "contract": "AccessControlUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:225" + }, + { + "contract": "ERC20Upgradeable", + "label": "_allowances", + "type": "t_mapping(t_address,t_mapping(t_address,t_uint256))", + "src": "contracts/tokens/ERC20Upgradeable.sol:38" + }, + { + "contract": "ERC20Upgradeable", + "label": "_name", + "type": "t_string_storage", + "src": "contracts/tokens/ERC20Upgradeable.sol:40" + }, + { + "contract": "ERC20Upgradeable", + "label": "_symbol", + "type": "t_string_storage", + "src": "contracts/tokens/ERC20Upgradeable.sol:41" + }, + { + "contract": "ERC20Upgradeable", + "label": "_decimals", + "type": "t_uint8", + "src": "contracts/tokens/ERC20Upgradeable.sol:42" + }, + { + "contract": "ERC20Upgradeable", + "label": "__gap", + "type": "t_array(t_uint256)44_storage", + "src": "contracts/tokens/ERC20Upgradeable.sol:225" + }, + { + "contract": "EIP712Upgradeable", + "label": "_HASHED_NAME", + "type": "t_bytes32", + "src": "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol:27" + }, + { + "contract": "EIP712Upgradeable", + "label": "_HASHED_VERSION", + "type": "t_bytes32", + "src": "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol:28" + }, + { + "contract": "EIP712Upgradeable", + "label": "__gap", + "type": "t_array(t_uint256)50_storage", + "src": "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol:120" + }, + { + "contract": "ERC20PermitUpgradeable", + "label": "_nonces", + "type": "t_mapping(t_address,t_struct(Counter)1701_storage)", + "src": "contracts/tokens/ERC20PermitUpgradeable.sol:26" + }, + { + "contract": "ERC20PermitUpgradeable", + "label": "_PERMIT_TYPEHASH", + "type": "t_bytes32", + "src": "contracts/tokens/ERC20PermitUpgradeable.sol:29" + }, + { + "contract": "ERC20PermitUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "contracts/tokens/ERC20PermitUpgradeable.sol:88" + }, + { + "contract": "RewardToken", + "label": "stakedToken", + "type": "t_contract(IStakedToken)5984", + "src": "contracts/tokens/RewardToken.sol:28" + }, + { + "contract": "RewardToken", + "label": "oracles", + "type": "t_address", + "src": "contracts/tokens/RewardToken.sol:31" + }, + { + "contract": "RewardToken", + "label": "checkpoints", + "type": "t_mapping(t_address,t_struct(Checkpoint)5720_storage)", + "src": "contracts/tokens/RewardToken.sol:34" + }, + { + "contract": "RewardToken", + "label": "protocolFeeRecipient", + "type": "t_address", + "src": "contracts/tokens/RewardToken.sol:37" + }, + { + "contract": "RewardToken", + "label": "protocolFee", + "type": "t_uint256", + "src": "contracts/tokens/RewardToken.sol:40" + }, + { + "contract": "RewardToken", + "label": "totalRewards", + "type": "t_uint128", + "src": "contracts/tokens/RewardToken.sol:43" + }, + { + "contract": "RewardToken", + "label": "rewardPerToken", + "type": "t_uint128", + "src": "contracts/tokens/RewardToken.sol:46" + }, + { + "contract": "RewardToken", + "label": "lastUpdateBlockNumber", + "type": "t_uint256", + "src": "contracts/tokens/RewardToken.sol:49" + }, + { + "contract": "RewardToken", + "label": "merkleDistributor", + "type": "t_address", + "src": "contracts/tokens/RewardToken.sol:52" + }, + { + "contract": "RewardToken", + "label": "rewardsDisabled", + "type": "t_mapping(t_address,t_bool)", + "src": "contracts/tokens/RewardToken.sol:55" + }, + { + "contract": "RewardToken", + "label": "totalPenalty", + "type": "t_uint256", + "src": "contracts/tokens/RewardToken.sol:58" + } + ], + "types": { + "t_contract(IStakedToken)5984": { + "label": "contract IStakedToken" + }, + "t_address": { + "label": "address" + }, + "t_mapping(t_address,t_struct(Checkpoint)5720_storage)": { + "label": "mapping(address => struct IRewardToken.Checkpoint)" + }, + "t_struct(Checkpoint)5720_storage": { + "label": "struct IRewardToken.Checkpoint", + "members": [ + { + "label": "reward", + "type": "t_uint128" + }, + { + "label": "rewardPerToken", + "type": "t_uint128" + } + ] + }, + "t_uint128": { + "label": "uint128" + }, + "t_uint256": { + "label": "uint256" + }, + "t_mapping(t_address,t_bool)": { + "label": "mapping(address => bool)" + }, + "t_bool": { + "label": "bool" + }, + "t_mapping(t_address,t_struct(Counter)1701_storage)": { + "label": "mapping(address => struct CountersUpgradeable.Counter)" + }, + "t_struct(Counter)1701_storage": { + "label": "struct CountersUpgradeable.Counter", + "members": [ + { + "label": "_value", + "type": "t_uint256" + } + ] + }, + "t_bytes32": { + "label": "bytes32" + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]" + }, + "t_mapping(t_address,t_mapping(t_address,t_uint256))": { + "label": "mapping(address => mapping(address => uint256))" + }, + "t_mapping(t_address,t_uint256)": { + "label": "mapping(address => uint256)" + }, + "t_string_storage": { + "label": "string" + }, + "t_uint8": { + "label": "uint8" + }, + "t_array(t_uint256)44_storage": { + "label": "uint256[44]" + }, + "t_mapping(t_bytes32,t_struct(RoleData)39_storage)": { + "label": "mapping(bytes32 => struct AccessControlUpgradeable.RoleData)" + }, + "t_struct(RoleData)39_storage": { + "label": "struct AccessControlUpgradeable.RoleData", + "members": [ + { + "label": "members", + "type": "t_struct(AddressSet)2017_storage" + }, + { + "label": "adminRole", + "type": "t_bytes32" + } + ] + }, + "t_struct(AddressSet)2017_storage": { + "label": "struct EnumerableSetUpgradeable.AddressSet", + "members": [ + { + "label": "_inner", + "type": "t_struct(Set)1752_storage" + } + ] + }, + "t_struct(Set)1752_storage": { + "label": "struct EnumerableSetUpgradeable.Set", + "members": [ + { + "label": "_values", + "type": "t_array(t_bytes32)dyn_storage" + }, + { + "label": "_indexes", + "type": "t_mapping(t_bytes32,t_uint256)" + } + ] + }, + "t_array(t_bytes32)dyn_storage": { + "label": "bytes32[]" + }, + "t_mapping(t_bytes32,t_uint256)": { + "label": "mapping(bytes32 => uint256)" + } + } + } + }, + "e9e7add0c133c6daa882e3480e246b95998208b479a99bb2f7740d3714921ae3": { + "address": "0x80fC05f62C35C1b1361bc8908ea0aF06C510D390", + "txHash": "0x34565e56e058764d28104fa5551bf140fecd53e9d039b2ce734732c5f74883f0", + "layout": { + "storage": [ + { + "contract": "Initializable", + "label": "_initialized", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol:25" + }, + { + "contract": "Initializable", + "label": "_initializing", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol:30" + }, + { + "contract": "ContextUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)50_storage", + "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:31" + }, + { + "contract": "PausableUpgradeable", + "label": "_paused", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol:28" + }, + { + "contract": "PausableUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol:96" + }, + { + "contract": "AccessControlUpgradeable", + "label": "_roles", + "type": "t_mapping(t_bytes32,t_struct(RoleData)39_storage)", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:61" + }, + { + "contract": "AccessControlUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:225" + }, + { + "contract": "ERC20Upgradeable", + "label": "_allowances", + "type": "t_mapping(t_address,t_mapping(t_address,t_uint256))", + "src": "contracts/tokens/ERC20Upgradeable.sol:38" + }, + { + "contract": "ERC20Upgradeable", + "label": "_name", + "type": "t_string_storage", + "src": "contracts/tokens/ERC20Upgradeable.sol:40" + }, + { + "contract": "ERC20Upgradeable", + "label": "_symbol", + "type": "t_string_storage", + "src": "contracts/tokens/ERC20Upgradeable.sol:41" + }, + { + "contract": "ERC20Upgradeable", + "label": "_decimals", + "type": "t_uint8", + "src": "contracts/tokens/ERC20Upgradeable.sol:42" + }, + { + "contract": "ERC20Upgradeable", + "label": "__gap", + "type": "t_array(t_uint256)44_storage", + "src": "contracts/tokens/ERC20Upgradeable.sol:225" + }, + { + "contract": "EIP712Upgradeable", + "label": "_HASHED_NAME", + "type": "t_bytes32", + "src": "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol:27" + }, + { + "contract": "EIP712Upgradeable", + "label": "_HASHED_VERSION", + "type": "t_bytes32", + "src": "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol:28" + }, + { + "contract": "EIP712Upgradeable", + "label": "__gap", + "type": "t_array(t_uint256)50_storage", + "src": "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol:120" + }, + { + "contract": "ERC20PermitUpgradeable", + "label": "_nonces", + "type": "t_mapping(t_address,t_struct(Counter)1701_storage)", + "src": "contracts/tokens/ERC20PermitUpgradeable.sol:26" + }, + { + "contract": "ERC20PermitUpgradeable", + "label": "_PERMIT_TYPEHASH", + "type": "t_bytes32", + "src": "contracts/tokens/ERC20PermitUpgradeable.sol:29" + }, + { + "contract": "ERC20PermitUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "contracts/tokens/ERC20PermitUpgradeable.sol:88" + }, + { + "contract": "StakedToken", + "label": "totalDeposits", + "type": "t_uint256", + "src": "contracts/tokens/StakedToken.sol:20" + }, + { + "contract": "StakedToken", + "label": "deposits", + "type": "t_mapping(t_address,t_uint256)", + "src": "contracts/tokens/StakedToken.sol:23" + }, + { + "contract": "StakedToken", + "label": "pool", + "type": "t_address", + "src": "contracts/tokens/StakedToken.sol:26" + }, + { + "contract": "StakedToken", + "label": "rewardToken", + "type": "t_contract(IRewardToken)5887", + "src": "contracts/tokens/StakedToken.sol:29" + }, + { + "contract": "StakedToken", + "label": "distributorPrincipal", + "type": "t_uint256", + "src": "contracts/tokens/StakedToken.sol:32" + } + ], + "types": { + "t_uint256": { + "label": "uint256" + }, + "t_mapping(t_address,t_uint256)": { + "label": "mapping(address => uint256)" + }, + "t_address": { + "label": "address" + }, + "t_contract(IRewardToken)5887": { + "label": "contract IRewardToken" + }, + "t_mapping(t_address,t_struct(Counter)1701_storage)": { + "label": "mapping(address => struct CountersUpgradeable.Counter)" + }, + "t_struct(Counter)1701_storage": { + "label": "struct CountersUpgradeable.Counter", + "members": [ + { + "label": "_value", + "type": "t_uint256" + } + ] + }, + "t_bytes32": { + "label": "bytes32" + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]" + }, + "t_mapping(t_address,t_mapping(t_address,t_uint256))": { + "label": "mapping(address => mapping(address => uint256))" + }, + "t_string_storage": { + "label": "string" + }, + "t_uint8": { + "label": "uint8" + }, + "t_array(t_uint256)44_storage": { + "label": "uint256[44]" + }, + "t_mapping(t_bytes32,t_struct(RoleData)39_storage)": { + "label": "mapping(bytes32 => struct AccessControlUpgradeable.RoleData)" + }, + "t_struct(RoleData)39_storage": { + "label": "struct AccessControlUpgradeable.RoleData", + "members": [ + { + "label": "members", + "type": "t_struct(AddressSet)2017_storage" + }, + { + "label": "adminRole", + "type": "t_bytes32" + } + ] + }, + "t_struct(AddressSet)2017_storage": { + "label": "struct EnumerableSetUpgradeable.AddressSet", + "members": [ + { + "label": "_inner", + "type": "t_struct(Set)1752_storage" + } + ] + }, + "t_struct(Set)1752_storage": { + "label": "struct EnumerableSetUpgradeable.Set", + "members": [ + { + "label": "_values", + "type": "t_array(t_bytes32)dyn_storage" + }, + { + "label": "_indexes", + "type": "t_mapping(t_bytes32,t_uint256)" + } + ] + }, + "t_array(t_bytes32)dyn_storage": { + "label": "bytes32[]" + }, + "t_mapping(t_bytes32,t_uint256)": { + "label": "mapping(bytes32 => uint256)" + }, + "t_bool": { + "label": "bool" + } + } + } + }, + "9700b56fd6648267e0dd17e2b00fe4378fd9c5978c3a9d31215eb7f94f658c65": { + "address": "0x191599BAF9ad40Ae28EeD7ee6F5C38B5325746E7", + "txHash": "0x6507c00b894ab366e19d22c8646f4949641e1bfb37dd237b637e3e19260f9024", + "layout": { + "storage": [ + { + "contract": "Initializable", + "label": "_initialized", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol:25" + }, + { + "contract": "Initializable", + "label": "_initializing", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol:30" + }, + { + "contract": "ContextUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)50_storage", + "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:31" + }, + { + "contract": "PausableUpgradeable", + "label": "_paused", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol:28" + }, + { + "contract": "PausableUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol:96" + }, + { + "contract": "AccessControlUpgradeable", + "label": "_roles", + "type": "t_mapping(t_bytes32,t_struct(RoleData)39_storage)", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:61" + }, + { + "contract": "AccessControlUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:225" + }, + { + "contract": "ERC20Upgradeable", + "label": "_allowances", + "type": "t_mapping(t_address,t_mapping(t_address,t_uint256))", + "src": "contracts/tokens/ERC20Upgradeable.sol:38" + }, + { + "contract": "ERC20Upgradeable", + "label": "_name", + "type": "t_string_storage", + "src": "contracts/tokens/ERC20Upgradeable.sol:40" + }, + { + "contract": "ERC20Upgradeable", + "label": "_symbol", + "type": "t_string_storage", + "src": "contracts/tokens/ERC20Upgradeable.sol:41" + }, + { + "contract": "ERC20Upgradeable", + "label": "_decimals", + "type": "t_uint8", + "src": "contracts/tokens/ERC20Upgradeable.sol:42" + }, + { + "contract": "ERC20Upgradeable", + "label": "__gap", + "type": "t_array(t_uint256)44_storage", + "src": "contracts/tokens/ERC20Upgradeable.sol:225" + }, + { + "contract": "EIP712Upgradeable", + "label": "_HASHED_NAME", + "type": "t_bytes32", + "src": "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol:27" + }, + { + "contract": "EIP712Upgradeable", + "label": "_HASHED_VERSION", + "type": "t_bytes32", + "src": "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol:28" + }, + { + "contract": "EIP712Upgradeable", + "label": "__gap", + "type": "t_array(t_uint256)50_storage", + "src": "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol:120" + }, + { + "contract": "ERC20PermitUpgradeable", + "label": "_nonces", + "type": "t_mapping(t_address,t_struct(Counter)1701_storage)", + "src": "contracts/tokens/ERC20PermitUpgradeable.sol:26" + }, + { + "contract": "ERC20PermitUpgradeable", + "label": "_PERMIT_TYPEHASH", + "type": "t_bytes32", + "src": "contracts/tokens/ERC20PermitUpgradeable.sol:29" + }, + { + "contract": "ERC20PermitUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "contracts/tokens/ERC20PermitUpgradeable.sol:88" + }, + { + "contract": "RewardToken", + "label": "stakedToken", + "type": "t_contract(IStakedToken)5984", + "src": "contracts/tokens/RewardToken.sol:28" + }, + { + "contract": "RewardToken", + "label": "oracles", + "type": "t_address", + "src": "contracts/tokens/RewardToken.sol:31" + }, + { + "contract": "RewardToken", + "label": "checkpoints", + "type": "t_mapping(t_address,t_struct(Checkpoint)5720_storage)", + "src": "contracts/tokens/RewardToken.sol:34" + }, + { + "contract": "RewardToken", + "label": "protocolFeeRecipient", + "type": "t_address", + "src": "contracts/tokens/RewardToken.sol:37" + }, + { + "contract": "RewardToken", + "label": "protocolFee", + "type": "t_uint256", + "src": "contracts/tokens/RewardToken.sol:40" + }, + { + "contract": "RewardToken", + "label": "totalRewards", + "type": "t_uint128", + "src": "contracts/tokens/RewardToken.sol:43" + }, + { + "contract": "RewardToken", + "label": "rewardPerToken", + "type": "t_uint128", + "src": "contracts/tokens/RewardToken.sol:46" + }, + { + "contract": "RewardToken", + "label": "lastUpdateBlockNumber", + "type": "t_uint256", + "src": "contracts/tokens/RewardToken.sol:49" + }, + { + "contract": "RewardToken", + "label": "merkleDistributor", + "type": "t_address", + "src": "contracts/tokens/RewardToken.sol:52" + }, + { + "contract": "RewardToken", + "label": "rewardsDisabled", + "type": "t_mapping(t_address,t_bool)", + "src": "contracts/tokens/RewardToken.sol:55" + }, + { + "contract": "RewardToken", + "label": "totalPenalty", + "type": "t_uint256", + "src": "contracts/tokens/RewardToken.sol:58" + } + ], + "types": { + "t_contract(IStakedToken)5984": { + "label": "contract IStakedToken" + }, + "t_address": { + "label": "address" + }, + "t_mapping(t_address,t_struct(Checkpoint)5720_storage)": { + "label": "mapping(address => struct IRewardToken.Checkpoint)" + }, + "t_struct(Checkpoint)5720_storage": { + "label": "struct IRewardToken.Checkpoint", + "members": [ + { + "label": "reward", + "type": "t_uint128" + }, + { + "label": "rewardPerToken", + "type": "t_uint128" + } + ] + }, + "t_uint128": { + "label": "uint128" + }, + "t_uint256": { + "label": "uint256" + }, + "t_mapping(t_address,t_bool)": { + "label": "mapping(address => bool)" + }, + "t_bool": { + "label": "bool" + }, + "t_mapping(t_address,t_struct(Counter)1701_storage)": { + "label": "mapping(address => struct CountersUpgradeable.Counter)" + }, + "t_struct(Counter)1701_storage": { + "label": "struct CountersUpgradeable.Counter", + "members": [ + { + "label": "_value", + "type": "t_uint256" + } + ] + }, + "t_bytes32": { + "label": "bytes32" + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]" + }, + "t_mapping(t_address,t_mapping(t_address,t_uint256))": { + "label": "mapping(address => mapping(address => uint256))" + }, + "t_mapping(t_address,t_uint256)": { + "label": "mapping(address => uint256)" + }, + "t_string_storage": { + "label": "string" + }, + "t_uint8": { + "label": "uint8" + }, + "t_array(t_uint256)44_storage": { + "label": "uint256[44]" + }, + "t_mapping(t_bytes32,t_struct(RoleData)39_storage)": { + "label": "mapping(bytes32 => struct AccessControlUpgradeable.RoleData)" + }, + "t_struct(RoleData)39_storage": { + "label": "struct AccessControlUpgradeable.RoleData", + "members": [ + { + "label": "members", + "type": "t_struct(AddressSet)2017_storage" + }, + { + "label": "adminRole", + "type": "t_bytes32" + } + ] + }, + "t_struct(AddressSet)2017_storage": { + "label": "struct EnumerableSetUpgradeable.AddressSet", + "members": [ + { + "label": "_inner", + "type": "t_struct(Set)1752_storage" + } + ] + }, + "t_struct(Set)1752_storage": { + "label": "struct EnumerableSetUpgradeable.Set", + "members": [ + { + "label": "_values", + "type": "t_array(t_bytes32)dyn_storage" + }, + { + "label": "_indexes", + "type": "t_mapping(t_bytes32,t_uint256)" + } + ] + }, + "t_array(t_bytes32)dyn_storage": { + "label": "bytes32[]" + }, + "t_mapping(t_bytes32,t_uint256)": { + "label": "mapping(bytes32 => uint256)" + } + } + } + }, + "36e9cf3673ac7673e9fe10aea4e0e503a585da99ba3a253dc9f940d69f603d80": { + "address": "0xa95C0BAA54037b9Cf70Cfe8b59732246604C97D2", + "txHash": "0xc8dbc888aec57b35a2649f3bc206061edb4bfcb6c226b3d30f5e48f71e96df3d", + "layout": { + "storage": [ + { + "contract": "Initializable", + "label": "_initialized", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol:25" + }, + { + "contract": "Initializable", + "label": "_initializing", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol:30" + }, + { + "contract": "ContextUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)50_storage", + "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:31" + }, + { + "contract": "PausableUpgradeable", + "label": "_paused", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol:28" + }, + { + "contract": "PausableUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol:96" + }, + { + "contract": "AccessControlUpgradeable", + "label": "_roles", + "type": "t_mapping(t_bytes32,t_struct(RoleData)39_storage)", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:61" + }, + { + "contract": "AccessControlUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:225" + }, + { + "contract": "ERC20Upgradeable", + "label": "_allowances", + "type": "t_mapping(t_address,t_mapping(t_address,t_uint256))", + "src": "contracts/tokens/ERC20Upgradeable.sol:38" + }, + { + "contract": "ERC20Upgradeable", + "label": "_name", + "type": "t_string_storage", + "src": "contracts/tokens/ERC20Upgradeable.sol:40" + }, + { + "contract": "ERC20Upgradeable", + "label": "_symbol", + "type": "t_string_storage", + "src": "contracts/tokens/ERC20Upgradeable.sol:41" + }, + { + "contract": "ERC20Upgradeable", + "label": "_decimals", + "type": "t_uint8", + "src": "contracts/tokens/ERC20Upgradeable.sol:42" + }, + { + "contract": "ERC20Upgradeable", + "label": "__gap", + "type": "t_array(t_uint256)44_storage", + "src": "contracts/tokens/ERC20Upgradeable.sol:225" + }, + { + "contract": "EIP712Upgradeable", + "label": "_HASHED_NAME", + "type": "t_bytes32", + "src": "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol:27" + }, + { + "contract": "EIP712Upgradeable", + "label": "_HASHED_VERSION", + "type": "t_bytes32", + "src": "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol:28" + }, + { + "contract": "EIP712Upgradeable", + "label": "__gap", + "type": "t_array(t_uint256)50_storage", + "src": "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol:120" + }, + { + "contract": "ERC20PermitUpgradeable", + "label": "_nonces", + "type": "t_mapping(t_address,t_struct(Counter)1701_storage)", + "src": "contracts/tokens/ERC20PermitUpgradeable.sol:26" + }, + { + "contract": "ERC20PermitUpgradeable", + "label": "_PERMIT_TYPEHASH", + "type": "t_bytes32", + "src": "contracts/tokens/ERC20PermitUpgradeable.sol:29" + }, + { + "contract": "ERC20PermitUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "contracts/tokens/ERC20PermitUpgradeable.sol:88" + }, + { + "contract": "StakedToken", + "label": "totalDeposits", + "type": "t_uint256", + "src": "contracts/tokens/StakedToken.sol:20" + }, + { + "contract": "StakedToken", + "label": "deposits", + "type": "t_mapping(t_address,t_uint256)", + "src": "contracts/tokens/StakedToken.sol:23" + }, + { + "contract": "StakedToken", + "label": "pool", + "type": "t_address", + "src": "contracts/tokens/StakedToken.sol:26" + }, + { + "contract": "StakedToken", + "label": "rewardToken", + "type": "t_contract(IRewardToken)5887", + "src": "contracts/tokens/StakedToken.sol:29" + }, + { + "contract": "StakedToken", + "label": "distributorPrincipal", + "type": "t_uint256", + "src": "contracts/tokens/StakedToken.sol:32" + } + ], + "types": { + "t_uint256": { + "label": "uint256" + }, + "t_mapping(t_address,t_uint256)": { + "label": "mapping(address => uint256)" + }, + "t_address": { + "label": "address" + }, + "t_contract(IRewardToken)5887": { + "label": "contract IRewardToken" + }, + "t_mapping(t_address,t_struct(Counter)1701_storage)": { + "label": "mapping(address => struct CountersUpgradeable.Counter)" + }, + "t_struct(Counter)1701_storage": { + "label": "struct CountersUpgradeable.Counter", + "members": [ + { + "label": "_value", + "type": "t_uint256" + } + ] + }, + "t_bytes32": { + "label": "bytes32" + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]" + }, + "t_mapping(t_address,t_mapping(t_address,t_uint256))": { + "label": "mapping(address => mapping(address => uint256))" + }, + "t_string_storage": { + "label": "string" + }, + "t_uint8": { + "label": "uint8" + }, + "t_array(t_uint256)44_storage": { + "label": "uint256[44]" + }, + "t_mapping(t_bytes32,t_struct(RoleData)39_storage)": { + "label": "mapping(bytes32 => struct AccessControlUpgradeable.RoleData)" + }, + "t_struct(RoleData)39_storage": { + "label": "struct AccessControlUpgradeable.RoleData", + "members": [ + { + "label": "members", + "type": "t_struct(AddressSet)2017_storage" + }, + { + "label": "adminRole", + "type": "t_bytes32" + } + ] + }, + "t_struct(AddressSet)2017_storage": { + "label": "struct EnumerableSetUpgradeable.AddressSet", + "members": [ + { + "label": "_inner", + "type": "t_struct(Set)1752_storage" + } + ] + }, + "t_struct(Set)1752_storage": { + "label": "struct EnumerableSetUpgradeable.Set", + "members": [ + { + "label": "_values", + "type": "t_array(t_bytes32)dyn_storage" + }, + { + "label": "_indexes", + "type": "t_mapping(t_bytes32,t_uint256)" + } + ] + }, + "t_array(t_bytes32)dyn_storage": { + "label": "bytes32[]" + }, + "t_mapping(t_bytes32,t_uint256)": { + "label": "mapping(bytes32 => uint256)" + }, + "t_bool": { + "label": "bool" + } + } + } + } + } +} diff --git a/contracts/mocks/StakedTokenMock.sol b/contracts/mocks/StakedTokenMock.sol index bda5eda1..1a3bee42 100644 --- a/contracts/mocks/StakedTokenMock.sol +++ b/contracts/mocks/StakedTokenMock.sol @@ -45,7 +45,7 @@ contract StakedTokenMock is StakedToken { rewardToken = IRewardToken(_rewardToken); } - function mint(uint256 amount) external { + function mint(uint256 amount) external onlyAdmin { IERC20Upgradeable(gnoToken).safeTransferFrom(msg.sender, poolEscrow, amount); // start calculating account rewards with updated deposit amount From b7460c5d8cad982cbdf1c903593b87d1d941e400 Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Fri, 5 Jul 2024 18:23:13 +0300 Subject: [PATCH 3/3] Deploy implementation contracts for V3 --- .openzeppelin/unknown-100.json | 895 +++++++++++++++++++++++++++++++ .openzeppelin/unknown-10200.json | 580 +------------------- contracts/tokens/StakedToken.sol | 2 +- deployments/settings.js | 1 + networks/gnosis.md | 20 + 5 files changed, 941 insertions(+), 557 deletions(-) diff --git a/.openzeppelin/unknown-100.json b/.openzeppelin/unknown-100.json index 9a943a80..495c8bc3 100644 --- a/.openzeppelin/unknown-100.json +++ b/.openzeppelin/unknown-100.json @@ -1572,6 +1572,901 @@ } } } + }, + "f2674efbc4266204ce6016513d171dac2b072c00925bbb2ddde47c00f40692cd": { + "address": "0xE8822246F8864DA92015813A39ae776087Fb1Cd5", + "txHash": "0x26aebc3773eca8ed98ca40b37e6341edc035ccda4c34c4d1b49bd38b6e61b682", + "layout": { + "storage": [ + { + "contract": "Initializable", + "label": "_initialized", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol:25" + }, + { + "contract": "Initializable", + "label": "_initializing", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol:30" + }, + { + "contract": "ContextUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)50_storage", + "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:31" + }, + { + "contract": "PausableUpgradeable", + "label": "_paused", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol:28" + }, + { + "contract": "PausableUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol:96" + }, + { + "contract": "AccessControlUpgradeable", + "label": "_roles", + "type": "t_mapping(t_bytes32,t_struct(RoleData)39_storage)", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:61" + }, + { + "contract": "AccessControlUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:225" + }, + { + "contract": "Oracles", + "label": "rewardsNonce", + "type": "t_struct(Counter)1701_storage", + "src": "contracts/Oracles.sol:30" + }, + { + "contract": "Oracles", + "label": "validatorsNonce", + "type": "t_struct(Counter)1701_storage", + "src": "contracts/Oracles.sol:33" + }, + { + "contract": "Oracles", + "label": "rewardToken", + "type": "t_contract(IRewardToken)5887", + "src": "contracts/Oracles.sol:36" + }, + { + "contract": "Oracles", + "label": "pool", + "type": "t_contract(IPool)5543", + "src": "contracts/Oracles.sol:39" + }, + { + "contract": "Oracles", + "label": "poolValidators", + "type": "t_contract(IPoolValidators)5709", + "src": "contracts/Oracles.sol:42" + }, + { + "contract": "Oracles", + "label": "merkleDistributor", + "type": "t_contract(IMerkleDistributor)5296", + "src": "contracts/Oracles.sol:45" + } + ], + "types": { + "t_struct(Counter)1701_storage": { + "label": "struct CountersUpgradeable.Counter", + "members": [ + { + "label": "_value", + "type": "t_uint256" + } + ] + }, + "t_uint256": { + "label": "uint256" + }, + "t_contract(IRewardToken)5887": { + "label": "contract IRewardToken" + }, + "t_contract(IPool)5543": { + "label": "contract IPool" + }, + "t_contract(IPoolValidators)5709": { + "label": "contract IPoolValidators" + }, + "t_contract(IMerkleDistributor)5296": { + "label": "contract IMerkleDistributor" + }, + "t_mapping(t_bytes32,t_struct(RoleData)39_storage)": { + "label": "mapping(bytes32 => struct AccessControlUpgradeable.RoleData)" + }, + "t_bytes32": { + "label": "bytes32" + }, + "t_struct(RoleData)39_storage": { + "label": "struct AccessControlUpgradeable.RoleData", + "members": [ + { + "label": "members", + "type": "t_struct(AddressSet)2017_storage" + }, + { + "label": "adminRole", + "type": "t_bytes32" + } + ] + }, + "t_struct(AddressSet)2017_storage": { + "label": "struct EnumerableSetUpgradeable.AddressSet", + "members": [ + { + "label": "_inner", + "type": "t_struct(Set)1752_storage" + } + ] + }, + "t_struct(Set)1752_storage": { + "label": "struct EnumerableSetUpgradeable.Set", + "members": [ + { + "label": "_values", + "type": "t_array(t_bytes32)dyn_storage" + }, + { + "label": "_indexes", + "type": "t_mapping(t_bytes32,t_uint256)" + } + ] + }, + "t_array(t_bytes32)dyn_storage": { + "label": "bytes32[]" + }, + "t_mapping(t_bytes32,t_uint256)": { + "label": "mapping(bytes32 => uint256)" + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]" + }, + "t_bool": { + "label": "bool" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]" + } + } + } + }, + "ae7c3cbd388d6a53359d9cd09e23164993b8f0fe2b4bd784f6c202d3959570e9": { + "address": "0xba0B5ba961B108BFf8D761A256e9763a4FccFF23", + "txHash": "0x68415328fb29b22b835c3f0f1e15004e0627e169d9aeac7ecad9c1dd85bcf3eb", + "layout": { + "storage": [ + { + "contract": "Initializable", + "label": "_initialized", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol:25" + }, + { + "contract": "Initializable", + "label": "_initializing", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol:30" + }, + { + "contract": "ContextUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)50_storage", + "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:31" + }, + { + "contract": "PausableUpgradeable", + "label": "_paused", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol:28" + }, + { + "contract": "PausableUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol:96" + }, + { + "contract": "AccessControlUpgradeable", + "label": "_roles", + "type": "t_mapping(t_bytes32,t_struct(RoleData)39_storage)", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:61" + }, + { + "contract": "AccessControlUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:225" + }, + { + "contract": "Pool", + "label": "activatedValidators", + "type": "t_uint256", + "src": "contracts/pool/Pool.sol:36" + }, + { + "contract": "Pool", + "label": "withdrawalCredentials", + "type": "t_bytes32", + "src": "contracts/pool/Pool.sol:39" + }, + { + "contract": "Pool", + "label": "validatorRegistration", + "type": "t_contract(IDepositContract)5030", + "src": "contracts/pool/Pool.sol:42" + }, + { + "contract": "Pool", + "label": "stakedToken", + "type": "t_contract(IStakedToken)5984", + "src": "contracts/pool/Pool.sol:45" + }, + { + "contract": "Pool", + "label": "validators", + "type": "t_contract(IPoolValidators)5709", + "src": "contracts/pool/Pool.sol:48" + }, + { + "contract": "Pool", + "label": "oracles", + "type": "t_address", + "src": "contracts/pool/Pool.sol:51" + }, + { + "contract": "Pool", + "label": "activations", + "type": "t_mapping(t_address,t_mapping(t_uint256,t_uint256))", + "src": "contracts/pool/Pool.sol:54" + }, + { + "contract": "Pool", + "label": "pendingValidators", + "type": "t_uint256", + "src": "contracts/pool/Pool.sol:57" + }, + { + "contract": "Pool", + "label": "minActivatingDeposit", + "type": "t_uint256", + "src": "contracts/pool/Pool.sol:60" + }, + { + "contract": "Pool", + "label": "pendingValidatorsLimit", + "type": "t_uint256", + "src": "contracts/pool/Pool.sol:63" + } + ], + "types": { + "t_uint256": { + "label": "uint256" + }, + "t_bytes32": { + "label": "bytes32" + }, + "t_contract(IDepositContract)5030": { + "label": "contract IDepositContract" + }, + "t_contract(IStakedToken)5984": { + "label": "contract IStakedToken" + }, + "t_contract(IPoolValidators)5709": { + "label": "contract IPoolValidators" + }, + "t_address": { + "label": "address" + }, + "t_mapping(t_address,t_mapping(t_uint256,t_uint256))": { + "label": "mapping(address => mapping(uint256 => uint256))" + }, + "t_mapping(t_uint256,t_uint256)": { + "label": "mapping(uint256 => uint256)" + }, + "t_mapping(t_bytes32,t_struct(RoleData)39_storage)": { + "label": "mapping(bytes32 => struct AccessControlUpgradeable.RoleData)" + }, + "t_struct(RoleData)39_storage": { + "label": "struct AccessControlUpgradeable.RoleData", + "members": [ + { + "label": "members", + "type": "t_struct(AddressSet)2017_storage" + }, + { + "label": "adminRole", + "type": "t_bytes32" + } + ] + }, + "t_struct(AddressSet)2017_storage": { + "label": "struct EnumerableSetUpgradeable.AddressSet", + "members": [ + { + "label": "_inner", + "type": "t_struct(Set)1752_storage" + } + ] + }, + "t_struct(Set)1752_storage": { + "label": "struct EnumerableSetUpgradeable.Set", + "members": [ + { + "label": "_values", + "type": "t_array(t_bytes32)dyn_storage" + }, + { + "label": "_indexes", + "type": "t_mapping(t_bytes32,t_uint256)" + } + ] + }, + "t_array(t_bytes32)dyn_storage": { + "label": "bytes32[]" + }, + "t_mapping(t_bytes32,t_uint256)": { + "label": "mapping(bytes32 => uint256)" + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]" + }, + "t_bool": { + "label": "bool" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]" + } + } + } + }, + "09422dd951f11e6f3c8d0f5923090c6d6614a7f30fbe5ab928d07869e29c15fc": { + "address": "0x987852D5D6c221Ec03939421de4568C072eaE800", + "txHash": "0xc89493679ec1a32c99ab3f7d79d6f58a8b6388e46b2b6dcc721951167824d78b", + "layout": { + "storage": [ + { + "contract": "Initializable", + "label": "_initialized", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol:25" + }, + { + "contract": "Initializable", + "label": "_initializing", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol:30" + }, + { + "contract": "ContextUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)50_storage", + "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:31" + }, + { + "contract": "PausableUpgradeable", + "label": "_paused", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol:28" + }, + { + "contract": "PausableUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol:96" + }, + { + "contract": "AccessControlUpgradeable", + "label": "_roles", + "type": "t_mapping(t_bytes32,t_struct(RoleData)39_storage)", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:61" + }, + { + "contract": "AccessControlUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:225" + }, + { + "contract": "ERC20Upgradeable", + "label": "_allowances", + "type": "t_mapping(t_address,t_mapping(t_address,t_uint256))", + "src": "contracts/tokens/ERC20Upgradeable.sol:38" + }, + { + "contract": "ERC20Upgradeable", + "label": "_name", + "type": "t_string_storage", + "src": "contracts/tokens/ERC20Upgradeable.sol:40" + }, + { + "contract": "ERC20Upgradeable", + "label": "_symbol", + "type": "t_string_storage", + "src": "contracts/tokens/ERC20Upgradeable.sol:41" + }, + { + "contract": "ERC20Upgradeable", + "label": "_decimals", + "type": "t_uint8", + "src": "contracts/tokens/ERC20Upgradeable.sol:42" + }, + { + "contract": "ERC20Upgradeable", + "label": "__gap", + "type": "t_array(t_uint256)44_storage", + "src": "contracts/tokens/ERC20Upgradeable.sol:225" + }, + { + "contract": "EIP712Upgradeable", + "label": "_HASHED_NAME", + "type": "t_bytes32", + "src": "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol:27" + }, + { + "contract": "EIP712Upgradeable", + "label": "_HASHED_VERSION", + "type": "t_bytes32", + "src": "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol:28" + }, + { + "contract": "EIP712Upgradeable", + "label": "__gap", + "type": "t_array(t_uint256)50_storage", + "src": "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol:120" + }, + { + "contract": "ERC20PermitUpgradeable", + "label": "_nonces", + "type": "t_mapping(t_address,t_struct(Counter)1701_storage)", + "src": "contracts/tokens/ERC20PermitUpgradeable.sol:26" + }, + { + "contract": "ERC20PermitUpgradeable", + "label": "_PERMIT_TYPEHASH", + "type": "t_bytes32", + "src": "contracts/tokens/ERC20PermitUpgradeable.sol:29" + }, + { + "contract": "ERC20PermitUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "contracts/tokens/ERC20PermitUpgradeable.sol:88" + }, + { + "contract": "StakedToken", + "label": "totalDeposits", + "type": "t_uint256", + "src": "contracts/tokens/StakedToken.sol:20" + }, + { + "contract": "StakedToken", + "label": "deposits", + "type": "t_mapping(t_address,t_uint256)", + "src": "contracts/tokens/StakedToken.sol:23" + }, + { + "contract": "StakedToken", + "label": "pool", + "type": "t_address", + "src": "contracts/tokens/StakedToken.sol:26" + }, + { + "contract": "StakedToken", + "label": "rewardToken", + "type": "t_contract(IRewardToken)5887", + "src": "contracts/tokens/StakedToken.sol:29" + }, + { + "contract": "StakedToken", + "label": "distributorPrincipal", + "type": "t_uint256", + "src": "contracts/tokens/StakedToken.sol:32" + } + ], + "types": { + "t_uint256": { + "label": "uint256" + }, + "t_mapping(t_address,t_uint256)": { + "label": "mapping(address => uint256)" + }, + "t_address": { + "label": "address" + }, + "t_contract(IRewardToken)5887": { + "label": "contract IRewardToken" + }, + "t_mapping(t_address,t_struct(Counter)1701_storage)": { + "label": "mapping(address => struct CountersUpgradeable.Counter)" + }, + "t_struct(Counter)1701_storage": { + "label": "struct CountersUpgradeable.Counter", + "members": [ + { + "label": "_value", + "type": "t_uint256" + } + ] + }, + "t_bytes32": { + "label": "bytes32" + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]" + }, + "t_mapping(t_address,t_mapping(t_address,t_uint256))": { + "label": "mapping(address => mapping(address => uint256))" + }, + "t_string_storage": { + "label": "string" + }, + "t_uint8": { + "label": "uint8" + }, + "t_array(t_uint256)44_storage": { + "label": "uint256[44]" + }, + "t_mapping(t_bytes32,t_struct(RoleData)39_storage)": { + "label": "mapping(bytes32 => struct AccessControlUpgradeable.RoleData)" + }, + "t_struct(RoleData)39_storage": { + "label": "struct AccessControlUpgradeable.RoleData", + "members": [ + { + "label": "members", + "type": "t_struct(AddressSet)2017_storage" + }, + { + "label": "adminRole", + "type": "t_bytes32" + } + ] + }, + "t_struct(AddressSet)2017_storage": { + "label": "struct EnumerableSetUpgradeable.AddressSet", + "members": [ + { + "label": "_inner", + "type": "t_struct(Set)1752_storage" + } + ] + }, + "t_struct(Set)1752_storage": { + "label": "struct EnumerableSetUpgradeable.Set", + "members": [ + { + "label": "_values", + "type": "t_array(t_bytes32)dyn_storage" + }, + { + "label": "_indexes", + "type": "t_mapping(t_bytes32,t_uint256)" + } + ] + }, + "t_array(t_bytes32)dyn_storage": { + "label": "bytes32[]" + }, + "t_mapping(t_bytes32,t_uint256)": { + "label": "mapping(bytes32 => uint256)" + }, + "t_bool": { + "label": "bool" + } + } + } + }, + "30ac637580108c9572f9c810877fae9814d639dc420e928c602e8e0c1bdde0fc": { + "address": "0x7986e443DBBA8AD3FcC1f3d012e1e0a095601DAC", + "txHash": "0xb0bed225aecab7c89bd4f10f58eaaccefac9b3a4b19fd44810557e0c410339a7", + "layout": { + "storage": [ + { + "contract": "Initializable", + "label": "_initialized", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol:25" + }, + { + "contract": "Initializable", + "label": "_initializing", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol:30" + }, + { + "contract": "ContextUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)50_storage", + "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:31" + }, + { + "contract": "PausableUpgradeable", + "label": "_paused", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol:28" + }, + { + "contract": "PausableUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol:96" + }, + { + "contract": "AccessControlUpgradeable", + "label": "_roles", + "type": "t_mapping(t_bytes32,t_struct(RoleData)39_storage)", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:61" + }, + { + "contract": "AccessControlUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:225" + }, + { + "contract": "ERC20Upgradeable", + "label": "_allowances", + "type": "t_mapping(t_address,t_mapping(t_address,t_uint256))", + "src": "contracts/tokens/ERC20Upgradeable.sol:38" + }, + { + "contract": "ERC20Upgradeable", + "label": "_name", + "type": "t_string_storage", + "src": "contracts/tokens/ERC20Upgradeable.sol:40" + }, + { + "contract": "ERC20Upgradeable", + "label": "_symbol", + "type": "t_string_storage", + "src": "contracts/tokens/ERC20Upgradeable.sol:41" + }, + { + "contract": "ERC20Upgradeable", + "label": "_decimals", + "type": "t_uint8", + "src": "contracts/tokens/ERC20Upgradeable.sol:42" + }, + { + "contract": "ERC20Upgradeable", + "label": "__gap", + "type": "t_array(t_uint256)44_storage", + "src": "contracts/tokens/ERC20Upgradeable.sol:225" + }, + { + "contract": "EIP712Upgradeable", + "label": "_HASHED_NAME", + "type": "t_bytes32", + "src": "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol:27" + }, + { + "contract": "EIP712Upgradeable", + "label": "_HASHED_VERSION", + "type": "t_bytes32", + "src": "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol:28" + }, + { + "contract": "EIP712Upgradeable", + "label": "__gap", + "type": "t_array(t_uint256)50_storage", + "src": "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol:120" + }, + { + "contract": "ERC20PermitUpgradeable", + "label": "_nonces", + "type": "t_mapping(t_address,t_struct(Counter)1417_storage)", + "src": "contracts/tokens/ERC20PermitUpgradeable.sol:26" + }, + { + "contract": "ERC20PermitUpgradeable", + "label": "_PERMIT_TYPEHASH", + "type": "t_bytes32", + "src": "contracts/tokens/ERC20PermitUpgradeable.sol:29" + }, + { + "contract": "ERC20PermitUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "contracts/tokens/ERC20PermitUpgradeable.sol:88" + }, + { + "contract": "RewardToken", + "label": "stakedToken", + "type": "t_contract(IStakedToken)2909", + "src": "contracts/tokens/RewardToken.sol:28" + }, + { + "contract": "RewardToken", + "label": "oracles", + "type": "t_address", + "src": "contracts/tokens/RewardToken.sol:31" + }, + { + "contract": "RewardToken", + "label": "checkpoints", + "type": "t_mapping(t_address,t_struct(Checkpoint)2707_storage)", + "src": "contracts/tokens/RewardToken.sol:34" + }, + { + "contract": "RewardToken", + "label": "protocolFeeRecipient", + "type": "t_address", + "src": "contracts/tokens/RewardToken.sol:37" + }, + { + "contract": "RewardToken", + "label": "protocolFee", + "type": "t_uint256", + "src": "contracts/tokens/RewardToken.sol:40" + }, + { + "contract": "RewardToken", + "label": "totalRewards", + "type": "t_uint128", + "src": "contracts/tokens/RewardToken.sol:43" + }, + { + "contract": "RewardToken", + "label": "rewardPerToken", + "type": "t_uint128", + "src": "contracts/tokens/RewardToken.sol:46" + }, + { + "contract": "RewardToken", + "label": "lastUpdateBlockNumber", + "type": "t_uint256", + "src": "contracts/tokens/RewardToken.sol:49" + }, + { + "contract": "RewardToken", + "label": "merkleDistributor", + "type": "t_address", + "src": "contracts/tokens/RewardToken.sol:52" + }, + { + "contract": "RewardToken", + "label": "rewardsDisabled", + "type": "t_mapping(t_address,t_bool)", + "src": "contracts/tokens/RewardToken.sol:55" + }, + { + "contract": "RewardToken", + "label": "totalPenalty", + "type": "t_uint256", + "src": "contracts/tokens/RewardToken.sol:58" + } + ], + "types": { + "t_contract(IStakedToken)2909": { + "label": "contract IStakedToken" + }, + "t_address": { + "label": "address" + }, + "t_mapping(t_address,t_struct(Checkpoint)2707_storage)": { + "label": "mapping(address => struct IRewardToken.Checkpoint)" + }, + "t_struct(Checkpoint)2707_storage": { + "label": "struct IRewardToken.Checkpoint", + "members": [ + { + "label": "reward", + "type": "t_uint128" + }, + { + "label": "rewardPerToken", + "type": "t_uint128" + } + ] + }, + "t_uint128": { + "label": "uint128" + }, + "t_uint256": { + "label": "uint256" + }, + "t_mapping(t_address,t_bool)": { + "label": "mapping(address => bool)" + }, + "t_bool": { + "label": "bool" + }, + "t_mapping(t_address,t_struct(Counter)1417_storage)": { + "label": "mapping(address => struct CountersUpgradeable.Counter)" + }, + "t_struct(Counter)1417_storage": { + "label": "struct CountersUpgradeable.Counter", + "members": [ + { + "label": "_value", + "type": "t_uint256" + } + ] + }, + "t_bytes32": { + "label": "bytes32" + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]" + }, + "t_mapping(t_address,t_mapping(t_address,t_uint256))": { + "label": "mapping(address => mapping(address => uint256))" + }, + "t_mapping(t_address,t_uint256)": { + "label": "mapping(address => uint256)" + }, + "t_string_storage": { + "label": "string" + }, + "t_uint8": { + "label": "uint8" + }, + "t_array(t_uint256)44_storage": { + "label": "uint256[44]" + }, + "t_mapping(t_bytes32,t_struct(RoleData)39_storage)": { + "label": "mapping(bytes32 => struct AccessControlUpgradeable.RoleData)" + }, + "t_struct(RoleData)39_storage": { + "label": "struct AccessControlUpgradeable.RoleData", + "members": [ + { + "label": "members", + "type": "t_struct(AddressSet)1733_storage" + }, + { + "label": "adminRole", + "type": "t_bytes32" + } + ] + }, + "t_struct(AddressSet)1733_storage": { + "label": "struct EnumerableSetUpgradeable.AddressSet", + "members": [ + { + "label": "_inner", + "type": "t_struct(Set)1468_storage" + } + ] + }, + "t_struct(Set)1468_storage": { + "label": "struct EnumerableSetUpgradeable.Set", + "members": [ + { + "label": "_values", + "type": "t_array(t_bytes32)dyn_storage" + }, + { + "label": "_indexes", + "type": "t_mapping(t_bytes32,t_uint256)" + } + ] + }, + "t_array(t_bytes32)dyn_storage": { + "label": "bytes32[]" + }, + "t_mapping(t_bytes32,t_uint256)": { + "label": "mapping(bytes32 => uint256)" + } + } + } } } } diff --git a/.openzeppelin/unknown-10200.json b/.openzeppelin/unknown-10200.json index 86f2d8a9..d6b8ab9a 100644 --- a/.openzeppelin/unknown-10200.json +++ b/.openzeppelin/unknown-10200.json @@ -1,10 +1,10 @@ { "manifestVersion": "3.2", "admin": { - "address": "0xDF82E5D27E175618e5bC4581ee336F59AdabfBB2", - "txHash": "0xbcf0a4fc8e66afad53704c0277cb5538383255fb5fdb0708901691f66d63fbc3", + "address": "0xDa955A7aC6fCBbD917aCE7571850282A81b7Bce9", + "txHash": "0x9ae9fdcc09c32816c936d05dc1d9c9ca2246417c3e126a8b6cf3a438fc5c4872", "deployTransaction": { - "hash": "0xbcf0a4fc8e66afad53704c0277cb5538383255fb5fdb0708901691f66d63fbc3", + "hash": "0x9ae9fdcc09c32816c936d05dc1d9c9ca2246417c3e126a8b6cf3a438fc5c4872", "type": 2, "accessList": [], "blockHash": null, @@ -14,11 +14,11 @@ "from": "0xFF2B6d2d5c205b99E2e6f607B6aFA3127B9957B6", "maxPriorityFeePerGas": { "type": "BigNumber", - "hex": "0x0287e70979" + "hex": "0x65ec8779" }, "maxFeePerGas": { "type": "BigNumber", - "hex": "0x0287e70981" + "hex": "0x65ec8781" }, "gasLimit": { "type": "BigNumber", @@ -29,31 +29,31 @@ "type": "BigNumber", "hex": "0x00" }, - "nonce": 2, + "nonce": 534, "data": "0x608060405234801561001057600080fd5b50600080546001600160a01b031916339081178255604051909182917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a350610759806100616000396000f3fe60806040526004361061007b5760003560e01c80639623609d1161004e5780639623609d1461011157806399a88ec414610124578063f2fde38b14610144578063f3b7dead146101645761007b565b8063204e1c7a14610080578063715018a6146100bc5780637eff275e146100d35780638da5cb5b146100f3575b600080fd5b34801561008c57600080fd5b506100a061009b366004610515565b610184565b6040516001600160a01b03909116815260200160405180910390f35b3480156100c857600080fd5b506100d1610215565b005b3480156100df57600080fd5b506100d16100ee366004610554565b610292565b3480156100ff57600080fd5b506000546001600160a01b03166100a0565b6100d161011f36600461058c565b61031c565b34801561013057600080fd5b506100d161013f366004610554565b6103ad565b34801561015057600080fd5b506100d161015f366004610515565b610405565b34801561017057600080fd5b506100a061017f366004610515565b6104ef565b6000806000836001600160a01b03166040516101aa90635c60da1b60e01b815260040190565b600060405180830381855afa9150503d80600081146101e5576040519150601f19603f3d011682016040523d82523d6000602084013e6101ea565b606091505b5091509150816101f957600080fd5b8080602001905181019061020d9190610538565b949350505050565b6000546001600160a01b031633146102485760405162461bcd60e51b815260040161023f906106c0565b60405180910390fd5b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b6000546001600160a01b031633146102bc5760405162461bcd60e51b815260040161023f906106c0565b6040516308f2839760e41b81526001600160a01b038281166004830152831690638f283970906024015b600060405180830381600087803b15801561030057600080fd5b505af1158015610314573d6000803e3d6000fd5b505050505050565b6000546001600160a01b031633146103465760405162461bcd60e51b815260040161023f906106c0565b60405163278f794360e11b81526001600160a01b03841690634f1ef286903490610376908690869060040161065d565b6000604051808303818588803b15801561038f57600080fd5b505af11580156103a3573d6000803e3d6000fd5b5050505050505050565b6000546001600160a01b031633146103d75760405162461bcd60e51b815260040161023f906106c0565b604051631b2ce7f360e11b81526001600160a01b038281166004830152831690633659cfe6906024016102e6565b6000546001600160a01b0316331461042f5760405162461bcd60e51b815260040161023f906106c0565b6001600160a01b0381166104945760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840161023f565b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b6000806000836001600160a01b03166040516101aa906303e1469160e61b815260040190565b600060208284031215610526578081fd5b81356105318161070b565b9392505050565b600060208284031215610549578081fd5b81516105318161070b565b60008060408385031215610566578081fd5b82356105718161070b565b915060208301356105818161070b565b809150509250929050565b6000806000606084860312156105a0578081fd5b83356105ab8161070b565b925060208401356105bb8161070b565b9150604084013567ffffffffffffffff808211156105d7578283fd5b818601915086601f8301126105ea578283fd5b8135818111156105fc576105fc6106f5565b604051601f8201601f19908116603f01168101908382118183101715610624576106246106f5565b8160405282815289602084870101111561063c578586fd5b82602086016020830137856020848301015280955050505050509250925092565b600060018060a01b038416825260206040818401528351806040850152825b818110156106985785810183015185820160600152820161067c565b818111156106a95783606083870101525b50601f01601f191692909201606001949350505050565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b634e487b7160e01b600052604160045260246000fd5b6001600160a01b038116811461072057600080fd5b5056fea2646970667358221220d849f96f3086b9f82cdcf665adb8c697ace05638da1c7c16ab2d26293717af6764736f6c63430008020033", - "r": "0x0023cff77ce6e99d8452e3a7f9979c70b55fd9dc21f8b6f4cad4c57b063c432f", - "s": "0x467782ecfb786d3cb48f29edc9761bd22d77736b5989b07a3ce8ceb63414fff0", + "r": "0x952dbb088ae33b05011fe707d09ceb132e80894011c89c6477889d4a70931bc1", + "s": "0x1845b942db5d24d61635166a1f44ddc08820a80f172913b9c4bc341715302fcb", "v": 0, - "creates": "0xDF82E5D27E175618e5bC4581ee336F59AdabfBB2", + "creates": "0xDa955A7aC6fCBbD917aCE7571850282A81b7Bce9", "chainId": 10200 } }, "proxies": [ { - "address": "0xfe076029B7D46fbe2ad4B9CBf377aA10B309e560", - "txHash": "0x52c706e479cd8676f595abab30f9ff2a929d2cd30238b22f2ab3ba759d9d5803", + "address": "0x14c74b1C7eCa8362D4ABcCd71051Ce174d61a3D4", + "txHash": "0xefd998eccdfbc5ee6356d5599207408d73225739f5fb4104cf884386c9acd0e9", "kind": "transparent" }, { - "address": "0xF2f5A23f849e02001da0DfdeC0F4CD3c3a79337e", - "txHash": "0xc86a764323ea8a0c0a1dcd5683271819f1ed4f8c213ad4aa9fd7e8075c9d1062", + "address": "0xEE2493a42861a0A49F88525c44AaB8126D04b761", + "txHash": "0xba4483f4d710ecd8fd5fa090c52b17d31e09cfef5283713eebcff5bb608bd03e", "kind": "transparent" } ], "impls": { - "a5f0f709f937a117c0db3ad4822068afa3d7a853b0aa491aaba513301f4ecdc6": { - "address": "0x9747e1fF73f1759217AFD212Dd36d21360D0880A", - "txHash": "0x1da222debb4bb65b994844e20abbdfc965bbe81812bde70b5e470c32fcaab4f3", + "1ae8ef7fe49d75fa16b56f62467c883b22234ad298f77f2c3019585b39d05b7d": { + "address": "0xB92466142897fd25B6c23927Cd2fB33C1BeAB3C4", + "txHash": "0xd88fd797ed7a49607e7d2f15ccfa25002c7652fc238950cce5211a644fbe019a", "layout": { "storage": [ { @@ -166,541 +166,9 @@ }, { "contract": "RewardToken", - "label": "stakedToken", - "type": "t_contract(IStakedToken)5984", - "src": "contracts/tokens/RewardToken.sol:28" - }, - { - "contract": "RewardToken", - "label": "oracles", - "type": "t_address", - "src": "contracts/tokens/RewardToken.sol:31" - }, - { - "contract": "RewardToken", - "label": "checkpoints", - "type": "t_mapping(t_address,t_struct(Checkpoint)5720_storage)", - "src": "contracts/tokens/RewardToken.sol:34" - }, - { - "contract": "RewardToken", - "label": "protocolFeeRecipient", - "type": "t_address", - "src": "contracts/tokens/RewardToken.sol:37" - }, - { - "contract": "RewardToken", - "label": "protocolFee", - "type": "t_uint256", - "src": "contracts/tokens/RewardToken.sol:40" - }, - { - "contract": "RewardToken", - "label": "totalRewards", - "type": "t_uint128", - "src": "contracts/tokens/RewardToken.sol:43" - }, - { - "contract": "RewardToken", - "label": "rewardPerToken", - "type": "t_uint128", - "src": "contracts/tokens/RewardToken.sol:46" - }, - { - "contract": "RewardToken", - "label": "lastUpdateBlockNumber", - "type": "t_uint256", - "src": "contracts/tokens/RewardToken.sol:49" - }, - { - "contract": "RewardToken", - "label": "merkleDistributor", - "type": "t_address", - "src": "contracts/tokens/RewardToken.sol:52" - }, - { - "contract": "RewardToken", - "label": "rewardsDisabled", - "type": "t_mapping(t_address,t_bool)", - "src": "contracts/tokens/RewardToken.sol:55" - }, - { - "contract": "RewardToken", - "label": "totalPenalty", - "type": "t_uint256", - "src": "contracts/tokens/RewardToken.sol:58" - } - ], - "types": { - "t_contract(IStakedToken)5984": { - "label": "contract IStakedToken" - }, - "t_address": { - "label": "address" - }, - "t_mapping(t_address,t_struct(Checkpoint)5720_storage)": { - "label": "mapping(address => struct IRewardToken.Checkpoint)" - }, - "t_struct(Checkpoint)5720_storage": { - "label": "struct IRewardToken.Checkpoint", - "members": [ - { - "label": "reward", - "type": "t_uint128" - }, - { - "label": "rewardPerToken", - "type": "t_uint128" - } - ] - }, - "t_uint128": { - "label": "uint128" - }, - "t_uint256": { - "label": "uint256" - }, - "t_mapping(t_address,t_bool)": { - "label": "mapping(address => bool)" - }, - "t_bool": { - "label": "bool" - }, - "t_mapping(t_address,t_struct(Counter)1701_storage)": { - "label": "mapping(address => struct CountersUpgradeable.Counter)" - }, - "t_struct(Counter)1701_storage": { - "label": "struct CountersUpgradeable.Counter", - "members": [ - { - "label": "_value", - "type": "t_uint256" - } - ] - }, - "t_bytes32": { - "label": "bytes32" - }, - "t_array(t_uint256)49_storage": { - "label": "uint256[49]" - }, - "t_array(t_uint256)50_storage": { - "label": "uint256[50]" - }, - "t_mapping(t_address,t_mapping(t_address,t_uint256))": { - "label": "mapping(address => mapping(address => uint256))" - }, - "t_mapping(t_address,t_uint256)": { - "label": "mapping(address => uint256)" - }, - "t_string_storage": { - "label": "string" - }, - "t_uint8": { - "label": "uint8" - }, - "t_array(t_uint256)44_storage": { - "label": "uint256[44]" - }, - "t_mapping(t_bytes32,t_struct(RoleData)39_storage)": { - "label": "mapping(bytes32 => struct AccessControlUpgradeable.RoleData)" - }, - "t_struct(RoleData)39_storage": { - "label": "struct AccessControlUpgradeable.RoleData", - "members": [ - { - "label": "members", - "type": "t_struct(AddressSet)2017_storage" - }, - { - "label": "adminRole", - "type": "t_bytes32" - } - ] - }, - "t_struct(AddressSet)2017_storage": { - "label": "struct EnumerableSetUpgradeable.AddressSet", - "members": [ - { - "label": "_inner", - "type": "t_struct(Set)1752_storage" - } - ] - }, - "t_struct(Set)1752_storage": { - "label": "struct EnumerableSetUpgradeable.Set", - "members": [ - { - "label": "_values", - "type": "t_array(t_bytes32)dyn_storage" - }, - { - "label": "_indexes", - "type": "t_mapping(t_bytes32,t_uint256)" - } - ] - }, - "t_array(t_bytes32)dyn_storage": { - "label": "bytes32[]" - }, - "t_mapping(t_bytes32,t_uint256)": { - "label": "mapping(bytes32 => uint256)" - } - } - } - }, - "e9e7add0c133c6daa882e3480e246b95998208b479a99bb2f7740d3714921ae3": { - "address": "0x80fC05f62C35C1b1361bc8908ea0aF06C510D390", - "txHash": "0x34565e56e058764d28104fa5551bf140fecd53e9d039b2ce734732c5f74883f0", - "layout": { - "storage": [ - { - "contract": "Initializable", - "label": "_initialized", - "type": "t_bool", - "src": "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol:25" - }, - { - "contract": "Initializable", - "label": "_initializing", - "type": "t_bool", - "src": "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol:30" - }, - { - "contract": "ContextUpgradeable", - "label": "__gap", - "type": "t_array(t_uint256)50_storage", - "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:31" - }, - { - "contract": "PausableUpgradeable", - "label": "_paused", - "type": "t_bool", - "src": "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol:28" - }, - { - "contract": "PausableUpgradeable", - "label": "__gap", - "type": "t_array(t_uint256)49_storage", - "src": "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol:96" - }, - { - "contract": "AccessControlUpgradeable", - "label": "_roles", - "type": "t_mapping(t_bytes32,t_struct(RoleData)39_storage)", - "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:61" - }, - { - "contract": "AccessControlUpgradeable", - "label": "__gap", - "type": "t_array(t_uint256)49_storage", - "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:225" - }, - { - "contract": "ERC20Upgradeable", - "label": "_allowances", - "type": "t_mapping(t_address,t_mapping(t_address,t_uint256))", - "src": "contracts/tokens/ERC20Upgradeable.sol:38" - }, - { - "contract": "ERC20Upgradeable", - "label": "_name", - "type": "t_string_storage", - "src": "contracts/tokens/ERC20Upgradeable.sol:40" - }, - { - "contract": "ERC20Upgradeable", - "label": "_symbol", - "type": "t_string_storage", - "src": "contracts/tokens/ERC20Upgradeable.sol:41" - }, - { - "contract": "ERC20Upgradeable", - "label": "_decimals", - "type": "t_uint8", - "src": "contracts/tokens/ERC20Upgradeable.sol:42" - }, - { - "contract": "ERC20Upgradeable", - "label": "__gap", - "type": "t_array(t_uint256)44_storage", - "src": "contracts/tokens/ERC20Upgradeable.sol:225" - }, - { - "contract": "EIP712Upgradeable", - "label": "_HASHED_NAME", - "type": "t_bytes32", - "src": "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol:27" - }, - { - "contract": "EIP712Upgradeable", - "label": "_HASHED_VERSION", - "type": "t_bytes32", - "src": "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol:28" - }, - { - "contract": "EIP712Upgradeable", - "label": "__gap", - "type": "t_array(t_uint256)50_storage", - "src": "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol:120" - }, - { - "contract": "ERC20PermitUpgradeable", - "label": "_nonces", - "type": "t_mapping(t_address,t_struct(Counter)1701_storage)", - "src": "contracts/tokens/ERC20PermitUpgradeable.sol:26" - }, - { - "contract": "ERC20PermitUpgradeable", - "label": "_PERMIT_TYPEHASH", - "type": "t_bytes32", - "src": "contracts/tokens/ERC20PermitUpgradeable.sol:29" - }, - { - "contract": "ERC20PermitUpgradeable", - "label": "__gap", - "type": "t_array(t_uint256)49_storage", - "src": "contracts/tokens/ERC20PermitUpgradeable.sol:88" - }, - { - "contract": "StakedToken", - "label": "totalDeposits", - "type": "t_uint256", - "src": "contracts/tokens/StakedToken.sol:20" - }, - { - "contract": "StakedToken", - "label": "deposits", - "type": "t_mapping(t_address,t_uint256)", - "src": "contracts/tokens/StakedToken.sol:23" - }, - { - "contract": "StakedToken", - "label": "pool", + "label": "vault", "type": "t_address", - "src": "contracts/tokens/StakedToken.sol:26" - }, - { - "contract": "StakedToken", - "label": "rewardToken", - "type": "t_contract(IRewardToken)5887", - "src": "contracts/tokens/StakedToken.sol:29" - }, - { - "contract": "StakedToken", - "label": "distributorPrincipal", - "type": "t_uint256", - "src": "contracts/tokens/StakedToken.sol:32" - } - ], - "types": { - "t_uint256": { - "label": "uint256" - }, - "t_mapping(t_address,t_uint256)": { - "label": "mapping(address => uint256)" - }, - "t_address": { - "label": "address" - }, - "t_contract(IRewardToken)5887": { - "label": "contract IRewardToken" - }, - "t_mapping(t_address,t_struct(Counter)1701_storage)": { - "label": "mapping(address => struct CountersUpgradeable.Counter)" - }, - "t_struct(Counter)1701_storage": { - "label": "struct CountersUpgradeable.Counter", - "members": [ - { - "label": "_value", - "type": "t_uint256" - } - ] - }, - "t_bytes32": { - "label": "bytes32" - }, - "t_array(t_uint256)49_storage": { - "label": "uint256[49]" - }, - "t_array(t_uint256)50_storage": { - "label": "uint256[50]" - }, - "t_mapping(t_address,t_mapping(t_address,t_uint256))": { - "label": "mapping(address => mapping(address => uint256))" - }, - "t_string_storage": { - "label": "string" - }, - "t_uint8": { - "label": "uint8" - }, - "t_array(t_uint256)44_storage": { - "label": "uint256[44]" - }, - "t_mapping(t_bytes32,t_struct(RoleData)39_storage)": { - "label": "mapping(bytes32 => struct AccessControlUpgradeable.RoleData)" - }, - "t_struct(RoleData)39_storage": { - "label": "struct AccessControlUpgradeable.RoleData", - "members": [ - { - "label": "members", - "type": "t_struct(AddressSet)2017_storage" - }, - { - "label": "adminRole", - "type": "t_bytes32" - } - ] - }, - "t_struct(AddressSet)2017_storage": { - "label": "struct EnumerableSetUpgradeable.AddressSet", - "members": [ - { - "label": "_inner", - "type": "t_struct(Set)1752_storage" - } - ] - }, - "t_struct(Set)1752_storage": { - "label": "struct EnumerableSetUpgradeable.Set", - "members": [ - { - "label": "_values", - "type": "t_array(t_bytes32)dyn_storage" - }, - { - "label": "_indexes", - "type": "t_mapping(t_bytes32,t_uint256)" - } - ] - }, - "t_array(t_bytes32)dyn_storage": { - "label": "bytes32[]" - }, - "t_mapping(t_bytes32,t_uint256)": { - "label": "mapping(bytes32 => uint256)" - }, - "t_bool": { - "label": "bool" - } - } - } - }, - "9700b56fd6648267e0dd17e2b00fe4378fd9c5978c3a9d31215eb7f94f658c65": { - "address": "0x191599BAF9ad40Ae28EeD7ee6F5C38B5325746E7", - "txHash": "0x6507c00b894ab366e19d22c8646f4949641e1bfb37dd237b637e3e19260f9024", - "layout": { - "storage": [ - { - "contract": "Initializable", - "label": "_initialized", - "type": "t_bool", - "src": "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol:25" - }, - { - "contract": "Initializable", - "label": "_initializing", - "type": "t_bool", - "src": "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol:30" - }, - { - "contract": "ContextUpgradeable", - "label": "__gap", - "type": "t_array(t_uint256)50_storage", - "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:31" - }, - { - "contract": "PausableUpgradeable", - "label": "_paused", - "type": "t_bool", - "src": "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol:28" - }, - { - "contract": "PausableUpgradeable", - "label": "__gap", - "type": "t_array(t_uint256)49_storage", - "src": "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol:96" - }, - { - "contract": "AccessControlUpgradeable", - "label": "_roles", - "type": "t_mapping(t_bytes32,t_struct(RoleData)39_storage)", - "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:61" - }, - { - "contract": "AccessControlUpgradeable", - "label": "__gap", - "type": "t_array(t_uint256)49_storage", - "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:225" - }, - { - "contract": "ERC20Upgradeable", - "label": "_allowances", - "type": "t_mapping(t_address,t_mapping(t_address,t_uint256))", - "src": "contracts/tokens/ERC20Upgradeable.sol:38" - }, - { - "contract": "ERC20Upgradeable", - "label": "_name", - "type": "t_string_storage", - "src": "contracts/tokens/ERC20Upgradeable.sol:40" - }, - { - "contract": "ERC20Upgradeable", - "label": "_symbol", - "type": "t_string_storage", - "src": "contracts/tokens/ERC20Upgradeable.sol:41" - }, - { - "contract": "ERC20Upgradeable", - "label": "_decimals", - "type": "t_uint8", - "src": "contracts/tokens/ERC20Upgradeable.sol:42" - }, - { - "contract": "ERC20Upgradeable", - "label": "__gap", - "type": "t_array(t_uint256)44_storage", - "src": "contracts/tokens/ERC20Upgradeable.sol:225" - }, - { - "contract": "EIP712Upgradeable", - "label": "_HASHED_NAME", - "type": "t_bytes32", - "src": "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol:27" - }, - { - "contract": "EIP712Upgradeable", - "label": "_HASHED_VERSION", - "type": "t_bytes32", - "src": "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol:28" - }, - { - "contract": "EIP712Upgradeable", - "label": "__gap", - "type": "t_array(t_uint256)50_storage", - "src": "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol:120" - }, - { - "contract": "ERC20PermitUpgradeable", - "label": "_nonces", - "type": "t_mapping(t_address,t_struct(Counter)1701_storage)", - "src": "contracts/tokens/ERC20PermitUpgradeable.sol:26" - }, - { - "contract": "ERC20PermitUpgradeable", - "label": "_PERMIT_TYPEHASH", - "type": "t_bytes32", - "src": "contracts/tokens/ERC20PermitUpgradeable.sol:29" - }, - { - "contract": "ERC20PermitUpgradeable", - "label": "__gap", - "type": "t_array(t_uint256)49_storage", - "src": "contracts/tokens/ERC20PermitUpgradeable.sol:88" + "src": "contracts/tokens/RewardToken.sol:25" }, { "contract": "RewardToken", @@ -770,12 +238,12 @@ } ], "types": { - "t_contract(IStakedToken)5984": { - "label": "contract IStakedToken" - }, "t_address": { "label": "address" }, + "t_contract(IStakedToken)5984": { + "label": "contract IStakedToken" + }, "t_mapping(t_address,t_struct(Checkpoint)5720_storage)": { "label": "mapping(address => struct IRewardToken.Checkpoint)" }, @@ -887,9 +355,9 @@ } } }, - "36e9cf3673ac7673e9fe10aea4e0e503a585da99ba3a253dc9f940d69f603d80": { - "address": "0xa95C0BAA54037b9Cf70Cfe8b59732246604C97D2", - "txHash": "0xc8dbc888aec57b35a2649f3bc206061edb4bfcb6c226b3d30f5e48f71e96df3d", + "8c23632f477a0335761f1017aa3883f576198e3c2b4ca7e5c866d8e3f997ed0a": { + "address": "0x3362382DD1AaF81e39118529de697eE0279c53D4", + "txHash": "0x800e288e93088255fa198a8dcf56eefe4235be7040cfcaacd96e0e5ff243235f", "layout": { "storage": [ { diff --git a/contracts/tokens/StakedToken.sol b/contracts/tokens/StakedToken.sol index 7e390ff8..cfc11cbc 100644 --- a/contracts/tokens/StakedToken.sol +++ b/contracts/tokens/StakedToken.sol @@ -99,7 +99,7 @@ contract StakedToken is IStakedToken, OwnablePausableUpgradeable, ERC20PermitUpg require(account != address(0), "StakedToken: invalid account"); // start calculating account rewards with updated deposit amount - bool rewardsDisabled = rewardToken.updateRewardCheckpoint(account); + bool rewardsDisabled = _rewardToken.updateRewardCheckpoint(account); if (rewardsDisabled) { // update merkle distributor principal if account has disabled rewards distributorPrincipal = distributorPrincipal.sub(amount); diff --git a/deployments/settings.js b/deployments/settings.js index b9eef3ae..71d33262 100644 --- a/deployments/settings.js +++ b/deployments/settings.js @@ -22,6 +22,7 @@ let contracts = { merkleDistributor: '0x7dc30953CE236665d032329F6a922d67F0a33a2B', roles: '0x9b23e05AEfb37D5ea9b525016d19eb82b65F255c', contractChecker: '0x814f9c8C0269f11996138c77cc16A3A7f0A36b0C', + vault: '0x4b4406Ed8659D03423490D8b62a1639206dA0A7a', }; module.exports = { diff --git a/networks/gnosis.md b/networks/gnosis.md index 0d666ea8..e53c3a17 100644 --- a/networks/gnosis.md +++ b/networks/gnosis.md @@ -5,6 +5,11 @@ - Contract: [0x2f99472b727e15EECf9B9eFF9F7481B85d3b4444](https://blockscout.com/xdai/mainnet/address/0x2f99472b727e15EECf9B9eFF9F7481B85d3b4444) - Transaction: [0x336baf83a82a2bb26b0900803251c9c0d247bb5c64b4a7ac424a78a98a0804cb](https://blockscout.com/xdai/mainnet/tx/0x336baf83a82a2bb26b0900803251c9c0d247bb5c64b4a7ac424a78a98a0804cb) + ### Pool V2 + + - Implementation: [0xba0B5ba961B108BFf8D761A256e9763a4FccFF23](https://blockscout.com/xdai/mainnet/address/0xba0B5ba961B108BFf8D761A256e9763a4FccFF23) + - Transaction: [0x68415328fb29b22b835c3f0f1e15004e0627e169d9aeac7ecad9c1dd85bcf3eb](https://gnosis.blockscout.com/tx/0x68415328fb29b22b835c3f0f1e15004e0627e169d9aeac7ecad9c1dd85bcf3eb) + ## Pool Escrow - Contract: [0xfc9B67b6034F6B306EA9Bd8Ec1baf3eFA2490394](https://blockscout.com/xdai/mainnet/address/0xfc9B67b6034F6B306EA9Bd8Ec1baf3eFA2490394/contracts) @@ -20,11 +25,21 @@ - Contract: [0xA4eF9Da5BA71Cc0D2e5E877a910A37eC43420445](https://blockscout.com/xdai/mainnet/address/0xA4eF9Da5BA71Cc0D2e5E877a910A37eC43420445) - Transaction: [0xc4f20d1df7405da12affcc3cacc7687b867329806574149bfc4082491ec607ff](https://blockscout.com/xdai/mainnet/tx/0xc4f20d1df7405da12affcc3cacc7687b867329806574149bfc4082491ec607ff) + ### StakedToken V2 + + - Implementation: [0x987852D5D6c221Ec03939421de4568C072eaE800](https://blockscout.com/xdai/mainnet/address/0x987852D5D6c221Ec03939421de4568C072eaE800) + - Transaction: [0xc89493679ec1a32c99ab3f7d79d6f58a8b6388e46b2b6dcc721951167824d78b](https://gnosis.blockscout.com/tx/0xc89493679ec1a32c99ab3f7d79d6f58a8b6388e46b2b6dcc721951167824d78b) + ## RewardToken - Contract: [0x6aC78efae880282396a335CA2F79863A1e6831D4](https://blockscout.com/xdai/mainnet/address/0x6aC78efae880282396a335CA2F79863A1e6831D4) - Transaction: [0x26538aadb540883a2ccaad15aa57b5b45ac18bde63cff4dcb0505c123e10d9b3](https://blockscout.com/xdai/mainnet/tx/0x26538aadb540883a2ccaad15aa57b5b45ac18bde63cff4dcb0505c123e10d9b3) + ### RewardToken V2 + + - Implementation: [0x7986e443DBBA8AD3FcC1f3d012e1e0a095601DAC](https://blockscout.com/xdai/mainnet/address/0x7986e443DBBA8AD3FcC1f3d012e1e0a095601DAC) + - Transaction: [0xb0bed225aecab7c89bd4f10f58eaaccefac9b3a4b19fd44810557e0c410339a7](https://gnosis.blockscout.com/tx/0xb0bed225aecab7c89bd4f10f58eaaccefac9b3a4b19fd44810557e0c410339a7) + ## StakeWiseToken - Contract: [0xfdA94F056346d2320d4B5E468D6Ad099b2277746](https://blockscout.com/xdai/mainnet/address/0xfdA94F056346d2320d4B5E468D6Ad099b2277746) @@ -40,6 +55,11 @@ - Implementation: [0x972B98dce8Ad32D1cbB0514A2115217066b0ee41](https://blockscout.com/xdai/mainnet/address/0x972B98dce8Ad32D1cbB0514A2115217066b0ee41) - Transaction: [0xbc7f8c4f8864871ed3e5e56594efd828c2e15d550e5e5e049b03a3c38fbffcd3](https://blockscout.com/xdai/mainnet/tx/0xbc7f8c4f8864871ed3e5e56594efd828c2e15d550e5e5e049b03a3c38fbffcd3) + ### Oracles V3 + + - Implementation: [0xE8822246F8864DA92015813A39ae776087Fb1Cd5](https://blockscout.com/xdai/mainnet/address/0xE8822246F8864DA92015813A39ae776087Fb1Cd5) + - Transaction: [0x26aebc3773eca8ed98ca40b37e6341edc035ccda4c34c4d1b49bd38b6e61b682](https://gnosis.blockscout.com/tx/0x26aebc3773eca8ed98ca40b37e6341edc035ccda4c34c4d1b49bd38b6e61b682) + ## Merkle Distributor - Contract: [0x7dc30953CE236665d032329F6a922d67F0a33a2B](https://blockscout.com/xdai/mainnet/address/0x7dc30953CE236665d032329F6a922d67F0a33a2B)