diff --git a/audits/2021-08 - Enzyme_Aave_IncentivesController_Improvement.pdf b/audits/2021-08 - Enzyme_Aave_IncentivesController_Improvement.pdf new file mode 100644 index 0000000..dfd6a08 Binary files /dev/null and b/audits/2021-08 - Enzyme_Aave_IncentivesController_Improvement.pdf differ diff --git a/contracts/incentives/StakedTokenIncentivesController.sol b/contracts/incentives/StakedTokenIncentivesController.sol index 905989a..7152d96 100644 --- a/contracts/incentives/StakedTokenIncentivesController.sol +++ b/contracts/incentives/StakedTokenIncentivesController.sol @@ -27,7 +27,7 @@ contract StakedTokenIncentivesController is using SafeMath for uint256; using SafeERC20 for IERC20; - uint256 public constant REVISION = 1; + uint256 public constant REVISION = 2; IStakedTokenWithConfig public immutable STAKE_TOKEN; @@ -49,13 +49,9 @@ contract StakedTokenIncentivesController is } /** - * @dev Initialize IStakedTokenIncentivesController - * @param addressesProvider the address of the corresponding addresses provider + * @dev Initialize IStakedTokenIncentivesController. Empty after REVISION 1, but maintains the expected interface. **/ - function initialize(address addressesProvider) external initializer { - //approves the safety module to allow staking - IERC20(STAKE_TOKEN.STAKED_TOKEN()).safeApprove(address(STAKE_TOKEN), type(uint256).max); - } + function initialize(address) external initializer {} /// @inheritdoc IAaveIncentivesController function configureAssets(address[] calldata assets, uint256[] calldata emissionsPerSecond) @@ -134,6 +130,15 @@ contract StakedTokenIncentivesController is return _claimRewards(assets, amount, msg.sender, user, to); } + /// @inheritdoc IAaveIncentivesController + function claimRewardsToSelf(address[] calldata assets, uint256 amount) + external + override + returns (uint256) + { + return _claimRewards(assets, amount, msg.sender, msg.sender, msg.sender); + } + /** * @dev Claims reward for an user on behalf, on all the assets of the lending pool, accumulating the pending rewards. * @param amount Amount of rewards to claim diff --git a/contracts/interfaces/IAaveIncentivesController.sol b/contracts/interfaces/IAaveIncentivesController.sol index c81232f..a30d49f 100644 --- a/contracts/interfaces/IAaveIncentivesController.sol +++ b/contracts/interfaces/IAaveIncentivesController.sol @@ -90,6 +90,14 @@ interface IAaveIncentivesController is IAaveDistributionManager { address to ) external returns (uint256); + /** + * @dev Claims rewards for a user, on the specified assets of the lending pool, distributing the pending rewards to self + * @param assets Incentivized assets on which to claim rewards + * @param amount Amount of rewards to claim + * @return Rewards claimed + **/ + function claimRewardsToSelf(address[] calldata assets, uint256 amount) external returns (uint256); + /** * @dev returns the unclaimed rewards of the user * @param user the address of the user diff --git a/contracts/interfaces/IProposalIncentivesV2Executor.sol b/contracts/interfaces/IProposalIncentivesV2Executor.sol new file mode 100644 index 0000000..5782235 --- /dev/null +++ b/contracts/interfaces/IProposalIncentivesV2Executor.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity 0.7.5; + +interface IProposalIncentivesV2Executor { + function execute() external; +} diff --git a/contracts/proposals/ProposalIncentivesV2Executor.sol b/contracts/proposals/ProposalIncentivesV2Executor.sol new file mode 100644 index 0000000..6553ed9 --- /dev/null +++ b/contracts/proposals/ProposalIncentivesV2Executor.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity 0.7.5; + +import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol'; +import {IProposalIncentivesV2Executor} from '../interfaces/IProposalIncentivesV2Executor.sol'; + +contract ProposalIncentivesV2Executor is IProposalIncentivesV2Executor { + bytes32 constant INCENTIVES_CONTROLLER_ID = bytes32(keccak256(bytes('INCENTIVES_CONTROLLER'))); + address constant INCENTIVES_CONTROLLER_IMPL_ADDRESS = 0xD9ED413bCF58c266F95fE6BA63B13cf79299CE31; + address constant LENDING_POOL_ADDRESS_PROVIDER = 0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5; + + function execute() external override { + ILendingPoolAddressesProvider(LENDING_POOL_ADDRESS_PROVIDER).setAddressAsProxy( + INCENTIVES_CONTROLLER_ID, + INCENTIVES_CONTROLLER_IMPL_ADDRESS + ); + } +} diff --git a/hardhat.config.ts b/hardhat.config.ts index c670f45..f2333de 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -21,7 +21,7 @@ import 'solidity-coverage'; const SKIP_LOAD = process.env.SKIP_LOAD === 'true'; const DEFAULT_BLOCK_GAS_LIMIT = 12450000; const DEFAULT_GAS_MUL = 5; -const HARDFORK = 'istanbul'; +const HARDFORK = 'london'; const ETHERSCAN_KEY = process.env.ETHERSCAN_KEY || ''; const MNEMONIC_PATH = "m/44'/60'/0'/0"; const MNEMONIC = process.env.MNEMONIC || ''; @@ -58,7 +58,7 @@ const getCommonNetworkConfig = (networkName: eNetwork, networkId: number) => ({ const mainnetFork = MAINNET_FORK ? { - blockNumber: 12290275, + blockNumber: parseInt(process.env.FORKING_BLOCK as string), url: NETWORKS_RPC_URL['main'], } : undefined; diff --git a/helpers/types.ts b/helpers/types.ts index 1343587..0aeaa3b 100644 --- a/helpers/types.ts +++ b/helpers/types.ts @@ -13,7 +13,7 @@ export enum eContractid { ATokenMock = 'ATokenMock', IERC20Detailed = 'IERC20Detailed', StakedTokenIncentivesController = 'StakedTokenIncentivesController', - MockSelfDestruct = 'MockSelfDestruct', + MockSelfDestruct = 'SelfdestructTransfer', StakedAaveV3 = 'StakedAaveV3', } diff --git a/package-lock.json b/package-lock.json index cb3c6d0..141d358 100644 --- a/package-lock.json +++ b/package-lock.json @@ -259,15 +259,15 @@ } }, "@ethereumjs/block": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@ethereumjs/block/-/block-3.2.1.tgz", - "integrity": "sha512-FCxo5KwwULne2A2Yuae4iaGGqSsRjwzXOlDhGalOFiBbLfP3hE04RHaHGw4c8vh1PfOrLauwi0dQNUBkOG3zIA==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@ethereumjs/block/-/block-3.5.1.tgz", + "integrity": "sha512-MoY9bHKABOBK6BW0v1N1Oc0Cve4x/giX67M3TtrVBUsKQTj2eznLGKpydoitxWSZ+WgKKSVhfRMzbCGRwk7T5w==", "dev": true, "requires": { - "@ethereumjs/common": "^2.2.0", - "@ethereumjs/tx": "^3.1.3", - "ethereumjs-util": "^7.0.10", - "merkle-patricia-tree": "^4.1.0" + "@ethereumjs/common": "^2.5.0", + "@ethereumjs/tx": "^3.3.1", + "ethereumjs-util": "^7.1.1", + "merkle-patricia-tree": "^4.2.1" }, "dependencies": { "bn.js": { @@ -277,9 +277,9 @@ "dev": true }, "ethereumjs-util": { - "version": "7.0.10", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.0.10.tgz", - "integrity": "sha512-c/xThw6A+EAnej5Xk5kOzFzyoSnw0WX0tSlZ6pAsfGVvQj3TItaDg9b1+Fz1RJXA+y2YksKwQnuzgt1eY6LKzw==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.2.tgz", + "integrity": "sha512-xCV3PTAhW8Q2k88XZn9VcO4OrjpeXAlDm5LQTaOLp81SjNSSY6+MwuGXrx6vafOMheWSmZGxIXUbue5e9UvUBw==", "dev": true, "requires": { "@types/bn.js": "^5.1.0", @@ -293,16 +293,16 @@ } }, "@ethereumjs/blockchain": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@ethereumjs/blockchain/-/blockchain-5.2.1.tgz", - "integrity": "sha512-+hshP2qSOOFsiYvZCbaDQFG7jYTWafE8sfBi+pAsdhAHfP7BN7VLyob7qoQISgwS1s7NTR4c4+2t/woU9ahItw==", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/@ethereumjs/blockchain/-/blockchain-5.4.2.tgz", + "integrity": "sha512-AOAAwz/lw2lciG9gf5wHi7M/qknraXXnLR66lYgbQ04qfyFC3ZE5x/5rLVm1Vu+kfJLlKrYZTmA0IbOkc7kvgw==", "dev": true, "requires": { - "@ethereumjs/block": "^3.2.0", - "@ethereumjs/common": "^2.2.0", - "@ethereumjs/ethash": "^1.0.0", + "@ethereumjs/block": "^3.5.1", + "@ethereumjs/common": "^2.5.0", + "@ethereumjs/ethash": "^1.1.0", "debug": "^2.2.0", - "ethereumjs-util": "^7.0.9", + "ethereumjs-util": "^7.1.1", "level-mem": "^5.0.1", "lru-cache": "^5.1.1", "rlp": "^2.2.4", @@ -325,9 +325,9 @@ } }, "ethereumjs-util": { - "version": "7.0.10", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.0.10.tgz", - "integrity": "sha512-c/xThw6A+EAnej5Xk5kOzFzyoSnw0WX0tSlZ6pAsfGVvQj3TItaDg9b1+Fz1RJXA+y2YksKwQnuzgt1eY6LKzw==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.2.tgz", + "integrity": "sha512-xCV3PTAhW8Q2k88XZn9VcO4OrjpeXAlDm5LQTaOLp81SjNSSY6+MwuGXrx6vafOMheWSmZGxIXUbue5e9UvUBw==", "dev": true, "requires": { "@types/bn.js": "^5.1.0", @@ -347,13 +347,13 @@ } }, "@ethereumjs/common": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-2.2.0.tgz", - "integrity": "sha512-PyQiTG00MJtBRkJmv46ChZL8u2XWxNBeAthznAUIUiefxPAXjbkuiCZOuncgJS34/XkMbNc9zMt/PlgKRBElig==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-2.5.0.tgz", + "integrity": "sha512-DEHjW6e38o+JmB/NO3GZBpW4lpaiBpkFgXF6jLcJ6gETBYpEyaA5nTimsWBUJR3Vmtm/didUEbNjajskugZORg==", "dev": true, "requires": { "crc-32": "^1.2.0", - "ethereumjs-util": "^7.0.9" + "ethereumjs-util": "^7.1.1" }, "dependencies": { "bn.js": { @@ -363,9 +363,9 @@ "dev": true }, "ethereumjs-util": { - "version": "7.0.10", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.0.10.tgz", - "integrity": "sha512-c/xThw6A+EAnej5Xk5kOzFzyoSnw0WX0tSlZ6pAsfGVvQj3TItaDg9b1+Fz1RJXA+y2YksKwQnuzgt1eY6LKzw==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.2.tgz", + "integrity": "sha512-xCV3PTAhW8Q2k88XZn9VcO4OrjpeXAlDm5LQTaOLp81SjNSSY6+MwuGXrx6vafOMheWSmZGxIXUbue5e9UvUBw==", "dev": true, "requires": { "@types/bn.js": "^5.1.0", @@ -379,14 +379,15 @@ } }, "@ethereumjs/ethash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@ethereumjs/ethash/-/ethash-1.0.0.tgz", - "integrity": "sha512-iIqnGG6NMKesyOxv2YctB2guOVX18qMAWlj3QlZyrc+GqfzLqoihti+cVNQnyNxr7eYuPdqwLQOFuPe6g/uKjw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/ethash/-/ethash-1.1.0.tgz", + "integrity": "sha512-/U7UOKW6BzpA+Vt+kISAoeDie1vAvY4Zy2KF5JJb+So7+1yKmJeJEHOGSnQIj330e9Zyl3L5Nae6VZyh2TJnAA==", "dev": true, "requires": { + "@ethereumjs/block": "^3.5.0", "@types/levelup": "^4.3.0", "buffer-xor": "^2.0.1", - "ethereumjs-util": "^7.0.7", + "ethereumjs-util": "^7.1.1", "miller-rabin": "^4.0.0" }, "dependencies": { @@ -406,9 +407,9 @@ } }, "ethereumjs-util": { - "version": "7.0.10", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.0.10.tgz", - "integrity": "sha512-c/xThw6A+EAnej5Xk5kOzFzyoSnw0WX0tSlZ6pAsfGVvQj3TItaDg9b1+Fz1RJXA+y2YksKwQnuzgt1eY6LKzw==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.2.tgz", + "integrity": "sha512-xCV3PTAhW8Q2k88XZn9VcO4OrjpeXAlDm5LQTaOLp81SjNSSY6+MwuGXrx6vafOMheWSmZGxIXUbue5e9UvUBw==", "dev": true, "requires": { "@types/bn.js": "^5.1.0", @@ -422,13 +423,13 @@ } }, "@ethereumjs/tx": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-3.1.3.tgz", - "integrity": "sha512-DJBu6cbwYtiPTFeCUR8DF5p+PF0jxs+0rALJZiEcTz2tiRPIEkM72GEbrkGuqzENLCzBrJHT43O0DxSYTqeo+g==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-3.3.2.tgz", + "integrity": "sha512-6AaJhwg4ucmwTvw/1qLaZUX5miWrwZ4nLOUsKyb/HtzS3BMw/CasKhdi1ims9mBKeK9sOJCH4qGKOBGyJCeeog==", "dev": true, "requires": { - "@ethereumjs/common": "^2.2.0", - "ethereumjs-util": "^7.0.10" + "@ethereumjs/common": "^2.5.0", + "ethereumjs-util": "^7.1.2" }, "dependencies": { "bn.js": { @@ -438,9 +439,9 @@ "dev": true }, "ethereumjs-util": { - "version": "7.0.10", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.0.10.tgz", - "integrity": "sha512-c/xThw6A+EAnej5Xk5kOzFzyoSnw0WX0tSlZ6pAsfGVvQj3TItaDg9b1+Fz1RJXA+y2YksKwQnuzgt1eY6LKzw==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.2.tgz", + "integrity": "sha512-xCV3PTAhW8Q2k88XZn9VcO4OrjpeXAlDm5LQTaOLp81SjNSSY6+MwuGXrx6vafOMheWSmZGxIXUbue5e9UvUBw==", "dev": true, "requires": { "@types/bn.js": "^5.1.0", @@ -454,22 +455,22 @@ } }, "@ethereumjs/vm": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/@ethereumjs/vm/-/vm-5.3.2.tgz", - "integrity": "sha512-QmCUQrW6xbhgEbQh9njue4kAJdM056C+ytBFUTF/kDYa3kNDm4Qxp9HUyTlt1OCSXvDhws0qqlh8+q+pmXpN7g==", + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/@ethereumjs/vm/-/vm-5.5.3.tgz", + "integrity": "sha512-0k5OreWnlgXYs54wohgO11jtGI05GDasj2EYxzuaStxTi15CS3vow5wGYELC1pG9xngE1F/mFmKi/f14XRuDow==", "dev": true, "requires": { - "@ethereumjs/block": "^3.2.1", - "@ethereumjs/blockchain": "^5.2.1", - "@ethereumjs/common": "^2.2.0", - "@ethereumjs/tx": "^3.1.3", + "@ethereumjs/block": "^3.5.0", + "@ethereumjs/blockchain": "^5.4.1", + "@ethereumjs/common": "^2.5.0", + "@ethereumjs/tx": "^3.3.1", "async-eventemitter": "^0.2.4", "core-js-pure": "^3.0.1", "debug": "^2.2.0", - "ethereumjs-util": "^7.0.10", + "ethereumjs-util": "^7.1.1", "functional-red-black-tree": "^1.0.1", "mcl-wasm": "^0.7.1", - "merkle-patricia-tree": "^4.1.0", + "merkle-patricia-tree": "^4.2.1", "rustbn.js": "~0.2.0", "util.promisify": "^1.0.1" }, @@ -490,9 +491,9 @@ } }, "ethereumjs-util": { - "version": "7.0.10", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.0.10.tgz", - "integrity": "sha512-c/xThw6A+EAnej5Xk5kOzFzyoSnw0WX0tSlZ6pAsfGVvQj3TItaDg9b1+Fz1RJXA+y2YksKwQnuzgt1eY6LKzw==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.2.tgz", + "integrity": "sha512-xCV3PTAhW8Q2k88XZn9VcO4OrjpeXAlDm5LQTaOLp81SjNSSY6+MwuGXrx6vafOMheWSmZGxIXUbue5e9UvUBw==", "dev": true, "requires": { "@types/bn.js": "^5.1.0", @@ -1334,9 +1335,9 @@ "dev": true }, "@types/abstract-leveldown": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@types/abstract-leveldown/-/abstract-leveldown-5.0.1.tgz", - "integrity": "sha512-wYxU3kp5zItbxKmeRYCEplS2MW7DzyBnxPGj+GJVHZEUZiK/nn5Ei1sUFgURDh+X051+zsGe28iud3oHjrYWQQ==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/abstract-leveldown/-/abstract-leveldown-5.0.2.tgz", + "integrity": "sha512-+jA1XXF3jsz+Z7FcuiNqgK53hTa/luglT2TyTpKPqoYbxVY+mCPF22Rm+q3KPBrMHJwNXFrTViHszBOfU4vftQ==", "dev": true }, "@types/bn.js": { @@ -1382,13 +1383,20 @@ "@types/node": "*" } }, + "@types/level-errors": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/level-errors/-/level-errors-3.0.0.tgz", + "integrity": "sha512-/lMtoq/Cf/2DVOm6zE6ORyOM+3ZVm/BvzEZVxUhf6bgh8ZHglXlBqxbxSlJeVp8FCbD3IVvk/VbsaNmDjrQvqQ==", + "dev": true + }, "@types/levelup": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@types/levelup/-/levelup-4.3.1.tgz", - "integrity": "sha512-n//PeTpbHLjMLTIgW5B/g06W/6iuTBHuvUka2nFL9APMSVMNe2r4enADfu3CIE9IyV9E+uquf9OEQQqrDeg24A==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@types/levelup/-/levelup-4.3.3.tgz", + "integrity": "sha512-K+OTIjJcZHVlZQN1HmU64VtrC0jC3dXWQozuEIR9zVvltIk90zaGPM2AgT+fIkChpzHhFE3YnvFLCbLtzAmexA==", "dev": true, "requires": { "@types/abstract-leveldown": "*", + "@types/level-errors": "*", "@types/node": "*" } }, @@ -1408,9 +1416,9 @@ } }, "@types/lru-cache": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.0.tgz", - "integrity": "sha512-RaE0B+14ToE4l6UqdarKPnXwVDuigfFv+5j9Dze/Nqr23yyuqdNvzcZi3xB+3Agvi5R4EOgAksfv3lXX4vBt9w==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==", "dev": true }, "@types/minimatch": { @@ -2673,9 +2681,9 @@ "dev": true }, "core-js-pure": { - "version": "3.10.2", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.10.2.tgz", - "integrity": "sha512-uu18pVHQ21n4mzfuSlCXpucu5VKsck3j2m5fjrBOBqqdgWAxwdCgUuGWj6cDDPN1zLj/qtiqKvBMxWgDeeu49Q==", + "version": "3.18.3", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.18.3.tgz", + "integrity": "sha512-qfskyO/KjtbYn09bn1IPkuhHl5PlJ6IzJ9s9sraJ1EqcuGyLGKzhSM1cY0zgyL9hx42eulQLZ6WaeK5ycJCkqw==", "dev": true }, "core-util-is": { @@ -13399,7 +13407,7 @@ } }, "ethereumjs-abi": { - "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#1a27c59c15ab1e95ee8e5c4ed6ad814c49cc439e", + "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#ee3994657fa7a427238e6ba92a84d0b529bbcde0", "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git", "dev": true, "requires": { @@ -14154,16 +14162,17 @@ } }, "hardhat": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.2.0.tgz", - "integrity": "sha512-3g0qFoQTkR4gfcHDZr59vPfbSH2PiAyxzYblkAAHUNTPBadO5W26z5RWzDv6/lRu8SqZZ9/8AdGZX4IZEa8EDg==", + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.6.5.tgz", + "integrity": "sha512-sBhREWZjQTtR/KMMp2F3ySuDqL0norjNq68geR3nlXRHXYKuNKeL7xqVsmldekt3sVB5Wh1WX7xDX79kvUr+fA==", "dev": true, "requires": { - "@ethereumjs/block": "^3.2.1", - "@ethereumjs/blockchain": "^5.2.1", - "@ethereumjs/common": "^2.2.0", - "@ethereumjs/tx": "^3.1.3", - "@ethereumjs/vm": "^5.3.2", + "@ethereumjs/block": "^3.4.0", + "@ethereumjs/blockchain": "^5.4.0", + "@ethereumjs/common": "^2.4.0", + "@ethereumjs/tx": "^3.3.0", + "@ethereumjs/vm": "^5.5.2", + "@ethersproject/abi": "^5.1.2", "@sentry/node": "^5.18.1", "@solidity-parser/parser": "^0.11.0", "@types/bn.js": "^5.1.0", @@ -14180,15 +14189,16 @@ "eth-sig-util": "^2.5.2", "ethereum-cryptography": "^0.1.2", "ethereumjs-abi": "^0.6.8", - "ethereumjs-util": "^7.0.10", + "ethereumjs-util": "^7.1.0", "find-up": "^2.1.0", "fp-ts": "1.19.3", "fs-extra": "^7.0.1", "glob": "^7.1.3", + "https-proxy-agent": "^5.0.0", "immutable": "^4.0.0-rc.12", "io-ts": "1.10.4", "lodash": "^4.17.11", - "merkle-patricia-tree": "^4.1.0", + "merkle-patricia-tree": "^4.2.0", "mnemonist": "^0.38.0", "mocha": "^7.1.2", "node-fetch": "^2.6.0", @@ -14203,9 +14213,220 @@ "true-case-path": "^2.2.1", "tsort": "0.0.1", "uuid": "^3.3.2", - "ws": "^7.2.1" + "ws": "^7.4.6" }, "dependencies": { + "@ethersproject/abi": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.4.1.tgz", + "integrity": "sha512-9mhbjUk76BiSluiiW4BaYyI58KSbDMMQpCLdsAR+RsT2GyATiNYxVv+pGWRrekmsIdY3I+hOqsYQSTkc8L/mcg==", + "dev": true, + "requires": { + "@ethersproject/address": "^5.4.0", + "@ethersproject/bignumber": "^5.4.0", + "@ethersproject/bytes": "^5.4.0", + "@ethersproject/constants": "^5.4.0", + "@ethersproject/hash": "^5.4.0", + "@ethersproject/keccak256": "^5.4.0", + "@ethersproject/logger": "^5.4.0", + "@ethersproject/properties": "^5.4.0", + "@ethersproject/strings": "^5.4.0" + } + }, + "@ethersproject/abstract-provider": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.4.1.tgz", + "integrity": "sha512-3EedfKI3LVpjSKgAxoUaI+gB27frKsxzm+r21w9G60Ugk+3wVLQwhi1LsEJAKNV7WoZc8CIpNrATlL1QFABjtQ==", + "dev": true, + "requires": { + "@ethersproject/bignumber": "^5.4.0", + "@ethersproject/bytes": "^5.4.0", + "@ethersproject/logger": "^5.4.0", + "@ethersproject/networks": "^5.4.0", + "@ethersproject/properties": "^5.4.0", + "@ethersproject/transactions": "^5.4.0", + "@ethersproject/web": "^5.4.0" + } + }, + "@ethersproject/abstract-signer": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.4.1.tgz", + "integrity": "sha512-SkkFL5HVq1k4/25dM+NWP9MILgohJCgGv5xT5AcRruGz4ILpfHeBtO/y6j+Z3UN/PAjDeb4P7E51Yh8wcGNLGA==", + "dev": true, + "requires": { + "@ethersproject/abstract-provider": "^5.4.0", + "@ethersproject/bignumber": "^5.4.0", + "@ethersproject/bytes": "^5.4.0", + "@ethersproject/logger": "^5.4.0", + "@ethersproject/properties": "^5.4.0" + } + }, + "@ethersproject/address": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.4.0.tgz", + "integrity": "sha512-SD0VgOEkcACEG/C6xavlU1Hy3m5DGSXW3CUHkaaEHbAPPsgi0coP5oNPsxau8eTlZOk/bpa/hKeCNoK5IzVI2Q==", + "dev": true, + "requires": { + "@ethersproject/bignumber": "^5.4.0", + "@ethersproject/bytes": "^5.4.0", + "@ethersproject/keccak256": "^5.4.0", + "@ethersproject/logger": "^5.4.0", + "@ethersproject/rlp": "^5.4.0" + } + }, + "@ethersproject/base64": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.4.0.tgz", + "integrity": "sha512-CjQw6E17QDSSC5jiM9YpF7N1aSCHmYGMt9bWD8PWv6YPMxjsys2/Q8xLrROKI3IWJ7sFfZ8B3flKDTM5wlWuZQ==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.4.0" + } + }, + "@ethersproject/bignumber": { + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.4.2.tgz", + "integrity": "sha512-oIBDhsKy5bs7j36JlaTzFgNPaZjiNDOXsdSgSpXRucUl+UA6L/1YLlFeI3cPAoodcenzF4nxNPV13pcy7XbWjA==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.4.0", + "@ethersproject/logger": "^5.4.0", + "bn.js": "^4.11.9" + } + }, + "@ethersproject/bytes": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.4.0.tgz", + "integrity": "sha512-H60ceqgTHbhzOj4uRc/83SCN9d+BSUnOkrr2intevqdtEMO1JFVZ1XL84OEZV+QjV36OaZYxtnt4lGmxcGsPfA==", + "dev": true, + "requires": { + "@ethersproject/logger": "^5.4.0" + } + }, + "@ethersproject/constants": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.4.0.tgz", + "integrity": "sha512-tzjn6S7sj9+DIIeKTJLjK9WGN2Tj0P++Z8ONEIlZjyoTkBuODN+0VfhAyYksKi43l1Sx9tX2VlFfzjfmr5Wl3Q==", + "dev": true, + "requires": { + "@ethersproject/bignumber": "^5.4.0" + } + }, + "@ethersproject/hash": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.4.0.tgz", + "integrity": "sha512-xymAM9tmikKgbktOCjW60Z5sdouiIIurkZUr9oW5NOex5uwxrbsYG09kb5bMcNjlVeJD3yPivTNzViIs1GCbqA==", + "dev": true, + "requires": { + "@ethersproject/abstract-signer": "^5.4.0", + "@ethersproject/address": "^5.4.0", + "@ethersproject/bignumber": "^5.4.0", + "@ethersproject/bytes": "^5.4.0", + "@ethersproject/keccak256": "^5.4.0", + "@ethersproject/logger": "^5.4.0", + "@ethersproject/properties": "^5.4.0", + "@ethersproject/strings": "^5.4.0" + } + }, + "@ethersproject/keccak256": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.4.0.tgz", + "integrity": "sha512-FBI1plWet+dPUvAzPAeHzRKiPpETQzqSUWR1wXJGHVWi4i8bOSrpC3NwpkPjgeXG7MnugVc1B42VbfnQikyC/A==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.4.0", + "js-sha3": "0.5.7" + } + }, + "@ethersproject/logger": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.4.1.tgz", + "integrity": "sha512-DZ+bRinnYLPw1yAC64oRl0QyVZj43QeHIhVKfD/+YwSz4wsv1pfwb5SOFjz+r710YEWzU6LrhuSjpSO+6PeE4A==", + "dev": true + }, + "@ethersproject/networks": { + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.4.2.tgz", + "integrity": "sha512-eekOhvJyBnuibfJnhtK46b8HimBc5+4gqpvd1/H9LEl7Q7/qhsIhM81dI9Fcnjpk3jB1aTy6bj0hz3cifhNeYw==", + "dev": true, + "requires": { + "@ethersproject/logger": "^5.4.0" + } + }, + "@ethersproject/properties": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.4.1.tgz", + "integrity": "sha512-cyCGlF8wWlIZyizsj2PpbJ9I7rIlUAfnHYwy/T90pdkSn/NFTa5YWZx2wTJBe9V7dD65dcrrEMisCRUJiq6n3w==", + "dev": true, + "requires": { + "@ethersproject/logger": "^5.4.0" + } + }, + "@ethersproject/rlp": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.4.0.tgz", + "integrity": "sha512-0I7MZKfi+T5+G8atId9QaQKHRvvasM/kqLyAH4XxBCBchAooH2EX5rL9kYZWwcm3awYV+XC7VF6nLhfeQFKVPg==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.4.0", + "@ethersproject/logger": "^5.4.0" + } + }, + "@ethersproject/signing-key": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.4.0.tgz", + "integrity": "sha512-q8POUeywx6AKg2/jX9qBYZIAmKSB4ubGXdQ88l40hmATj29JnG5pp331nAWwwxPn2Qao4JpWHNZsQN+bPiSW9A==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.4.0", + "@ethersproject/logger": "^5.4.0", + "@ethersproject/properties": "^5.4.0", + "bn.js": "^4.11.9", + "elliptic": "6.5.4", + "hash.js": "1.1.7" + } + }, + "@ethersproject/strings": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.4.0.tgz", + "integrity": "sha512-k/9DkH5UGDhv7aReXLluFG5ExurwtIpUfnDNhQA29w896Dw3i4uDTz01Quaptbks1Uj9kI8wo9tmW73wcIEaWA==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.4.0", + "@ethersproject/constants": "^5.4.0", + "@ethersproject/logger": "^5.4.0" + } + }, + "@ethersproject/transactions": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.4.0.tgz", + "integrity": "sha512-s3EjZZt7xa4BkLknJZ98QGoIza94rVjaEed0rzZ/jB9WrIuu/1+tjvYCWzVrystXtDswy7TPBeIepyXwSYa4WQ==", + "dev": true, + "requires": { + "@ethersproject/address": "^5.4.0", + "@ethersproject/bignumber": "^5.4.0", + "@ethersproject/bytes": "^5.4.0", + "@ethersproject/constants": "^5.4.0", + "@ethersproject/keccak256": "^5.4.0", + "@ethersproject/logger": "^5.4.0", + "@ethersproject/properties": "^5.4.0", + "@ethersproject/rlp": "^5.4.0", + "@ethersproject/signing-key": "^5.4.0" + } + }, + "@ethersproject/web": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.4.0.tgz", + "integrity": "sha512-1bUusGmcoRLYgMn6c1BLk1tOKUIFuTg8j+6N8lYlbMpDesnle+i3pGSagGNvwjaiLo4Y5gBibwctpPRmjrh4Og==", + "dev": true, + "requires": { + "@ethersproject/base64": "^5.4.0", + "@ethersproject/bytes": "^5.4.0", + "@ethersproject/logger": "^5.4.0", + "@ethersproject/properties": "^5.4.0", + "@ethersproject/strings": "^5.4.0" + } + }, "ethereumjs-abi": { "version": "0.6.8", "resolved": "https://registry.npmjs.org/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz", @@ -14243,9 +14464,9 @@ } }, "ethereumjs-util": { - "version": "7.0.10", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.0.10.tgz", - "integrity": "sha512-c/xThw6A+EAnej5Xk5kOzFzyoSnw0WX0tSlZ6pAsfGVvQj3TItaDg9b1+Fz1RJXA+y2YksKwQnuzgt1eY6LKzw==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.2.tgz", + "integrity": "sha512-xCV3PTAhW8Q2k88XZn9VcO4OrjpeXAlDm5LQTaOLp81SjNSSY6+MwuGXrx6vafOMheWSmZGxIXUbue5e9UvUBw==", "dev": true, "requires": { "@types/bn.js": "^5.1.0", @@ -14273,6 +14494,12 @@ "locate-path": "^2.0.0" } }, + "js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=", + "dev": true + }, "jsonfile": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", @@ -14348,6 +14575,12 @@ "rimraf": "^2.2.8" } }, + "js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", + "dev": true + }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -14364,6 +14597,12 @@ "requires": { "os-tmpdir": "~1.0.2" } + }, + "ws": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", + "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==", + "dev": true } } }, @@ -14743,9 +14982,9 @@ "dev": true }, "immutable": { - "version": "4.0.0-rc.12", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0-rc.12.tgz", - "integrity": "sha512-0M2XxkZLx/mi3t8NVwIm1g8nHoEmM9p9UBl/G9k4+hm0kBgOVdMV/B3CY5dQ8qG8qc80NN4gDV4HQv6FTJ5q7A==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz", + "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==", "dev": true }, "import-fresh": { @@ -15528,9 +15767,9 @@ "integrity": "sha512-0EESkXiTkWzrQQntBu2uzKvLu6vVkUGz40nGPbSZuegcfE5UuSzNjLaIu76zJWuaT/2I3Z/8M06OlUOZLGwLlQ==" }, "mcl-wasm": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/mcl-wasm/-/mcl-wasm-0.7.6.tgz", - "integrity": "sha512-cbRl3sUOkBeRY2hsM4t1EIln2TIdQBkSiTOqNTv/4Hu5KOECnMWCgjIf+a9Ebunyn22VKqkMF3zj6ejRzz7YBw==", + "version": "0.7.9", + "resolved": "https://registry.npmjs.org/mcl-wasm/-/mcl-wasm-0.7.9.tgz", + "integrity": "sha512-iJIUcQWA88IJB/5L15GnJVnSQJmf/YaxxV6zRavv83HILHaJQb6y0iFyDMdDO0gN8X37tdxmAOrH/P8B6RB8sQ==", "dev": true }, "md5.js": { @@ -15620,17 +15859,17 @@ "dev": true }, "merkle-patricia-tree": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/merkle-patricia-tree/-/merkle-patricia-tree-4.1.0.tgz", - "integrity": "sha512-vmP1J7FwIpprFMVjjSMM1JAwFce85Q+tp0TYIedYv8qaMh2oLUZ3ETXn9wbgi9S6elySzKzGa+Ai6VNKGEwSlg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/merkle-patricia-tree/-/merkle-patricia-tree-4.2.2.tgz", + "integrity": "sha512-eqZYNTshcYx9aESkSPr71EqwsR/QmpnObDEV4iLxkt/x/IoLYZYjJvKY72voP/27Vy61iMOrfOG6jrn7ttXD+Q==", "dev": true, "requires": { "@types/levelup": "^4.3.0", - "ethereumjs-util": "^7.0.8", + "ethereumjs-util": "^7.1.2", "level-mem": "^5.0.1", "level-ws": "^2.0.0", "readable-stream": "^3.6.0", - "rlp": "^2.2.3", + "rlp": "^2.2.4", "semaphore-async-await": "^1.5.1" }, "dependencies": { @@ -15641,9 +15880,9 @@ "dev": true }, "ethereumjs-util": { - "version": "7.0.10", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.0.10.tgz", - "integrity": "sha512-c/xThw6A+EAnej5Xk5kOzFzyoSnw0WX0tSlZ6pAsfGVvQj3TItaDg9b1+Fz1RJXA+y2YksKwQnuzgt1eY6LKzw==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.2.tgz", + "integrity": "sha512-xCV3PTAhW8Q2k88XZn9VcO4OrjpeXAlDm5LQTaOLp81SjNSSY6+MwuGXrx6vafOMheWSmZGxIXUbue5e9UvUBw==", "dev": true, "requires": { "@types/bn.js": "^5.1.0", @@ -15784,9 +16023,9 @@ } }, "mnemonist": { - "version": "0.38.3", - "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.3.tgz", - "integrity": "sha512-2K9QYubXx/NAjv4VLq1d1Ly8pWNC5L3BrixtdkyTegXWJIqY+zLNDhhX/A+ZwWt70tB1S8H4BE8FLYEFyNoOBw==", + "version": "0.38.4", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.4.tgz", + "integrity": "sha512-mflgW0gEWmVLbDDE2gJbOh3+RltTN7CgV9jV25qyCnyLN9FtoltWr7ZtAEDeD9u8W4oFAoolR6fBWieXdn3u8Q==", "dev": true, "requires": { "obliterator": "^1.6.1" diff --git a/package.json b/package.json index c898e78..607a1ea 100644 --- a/package.json +++ b/package.json @@ -19,10 +19,12 @@ "coverage": "hardhat coverage", "test": "npm run test-incentives", "test-incentives": "TS_NODE_TRANSPILE_ONLY=1 hardhat test ./test/__setup.spec.ts ./test/AaveIncentivesController/*.spec.ts", - "test-proposal": "TS_NODE_TRANSPILE_ONLY=1 MAINNET_FORK=true hardhat test ./test-fork/incentivesProposal.spec.ts", - "test-proposal:tenderly": "TS_NODE_TRANSPILE_ONLY=1 TENDERLY=true npm run hardhat:tenderly -- test ./test-fork/incentivesProposal.spec.ts", - "test-proposal-skip": "TS_NODE_TRANSPILE_ONLY=1 MAINNET_FORK=true hardhat test ./test-fork/incentives-skip.spec.ts", - "test-proposal-skip:tenderly": "TS_NODE_TRANSPILE_ONLY=1 TENDERLY=true npm run hardhat:tenderly -- test ./test-fork/incentives-skip.spec.ts", + "test-incentives-upgrade-v2": "FORKING_BLOCK=13410750 TS_NODE_TRANSPILE_ONLY=1 MAINNET_FORK=true hardhat test ./test-fork/incentives-v2-upgrade.spec.ts", + "test-proposal": "FORKING_BLOCK=12290275 TS_NODE_TRANSPILE_ONLY=1 MAINNET_FORK=true hardhat test ./test-fork/incentivesProposal.spec.ts", + "test-proposal-v2": "FORKING_BLOCK=13410750 TS_NODE_TRANSPILE_ONLY=1 MAINNET_FORK=true hardhat test ./test-fork/incentivesProposalV2.spec.ts", + "test-proposal:tenderly": "FORKING_BLOCK=12290275 TS_NODE_TRANSPILE_ONLY=1 TENDERLY=true npm run hardhat:tenderly -- test ./test-fork/incentivesProposal.spec.ts", + "test-proposal-skip": "FORKING_BLOCK=12290275 TS_NODE_TRANSPILE_ONLY=1 MAINNET_FORK=true hardhat test ./test-fork/incentives-skip.spec.ts", + "test-proposal-skip:tenderly": "FORKING_BLOCK=12290275 TS_NODE_TRANSPILE_ONLY=1 TENDERLY=true npm run hardhat:tenderly -- test ./test-fork/incentives-skip.spec.ts", "spin-incentives:tenderly": "TENDERLY=true npx hardhat --network tenderly incentives-proposal:tenderly", "compile": "SKIP_LOAD=true hardhat compile", "console:fork": "MAINNET_FORK=true hardhat console", @@ -63,7 +65,7 @@ "ethereumjs-util": "7.0.2", "ethers": "^5.0.19", "globby": "^11.0.1", - "hardhat": "^2.1.2", + "hardhat": "^2.6.5", "hardhat-gas-reporter": "^1.0.0", "husky": "^4.2.5", "is-ipfs": "^5.0.0", diff --git a/test-fork/incentives-v2-upgrade.spec.ts b/test-fork/incentives-v2-upgrade.spec.ts new file mode 100644 index 0000000..b5ea8a1 --- /dev/null +++ b/test-fork/incentives-v2-upgrade.spec.ts @@ -0,0 +1,283 @@ +import { JsonRpcSigner } from '@ethersproject/providers'; +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/dist/src/signer-with-address'; +import { expect } from 'chai'; +import { BigNumber, constants, utils } from 'ethers'; +import rawHRE from 'hardhat'; +import { Address } from 'hardhat-deploy/dist/types'; +import { deploySelfDestruct } from '../helpers/contracts-accessors'; +import { + advanceBlockTo, + DRE, + impersonateAccountsHardhat, + increaseTime, + latestBlock, + timeLatest, +} from '../helpers/misc-utils'; +import { getUserIndex } from '../test/DistributionManager/data-helpers/asset-user-data'; +import { getRewards } from '../test/DistributionManager/data-helpers/base-math'; +import { + IAaveGovernanceV2, + IAaveIncentivesController, + IERC20, + ILendingPool, + ILendingPoolAddressesProvider, + ProposalIncentivesV2Executor__factory, + StakedTokenIncentivesController, +} from '../types'; + +const AAVE_GOVERNANCE_V2 = '0xEC568fffba86c094cf06b22134B23074DFE2252c'; +const AAVE_LENDING_POOL_ADDRESSES_PROVIDER = '0xb53c1a33016b2dc2ff3653530bff1848a515c8c5'; +const AAVE_SHORT_EXECUTOR = '0xee56e2b3d491590b5b31738cc34d5232f378a8d5'; +const INCENTIVES_PROXY = '0xd784927Ff2f95ba542BfC824c8a8a98F3495f6b5'; + +const AAVE_DAI_TOKEN = '0x028171bCA77440897B824Ca71D1c56caC55b68A3'; +const AAVE_TOKEN = '0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9'; +const AAVE_WHALE = '0x25f2226b597e8f9514b3f68f00f494cf4f286491'; +const DAI_TOKEN = '0x6b175474e89094c44da98b954eedeac495271d0f'; +const DAI_WHALE = '0x1e3d6eab4bcf24bcd04721caa11c478a2e59852d'; +const STAKED_AAVE = '0x4da27a545c0c5B758a6BA100e3a049001de870f5'; + +describe('Upgrade to and test Revision #2 of the implementation', () => { + const VOTING_DURATION = 19200; + + let ethers; + + let gov: IAaveGovernanceV2; + let incentives: IAaveIncentivesController, pool: ILendingPool; + let aave: IERC20, aDai: IERC20, dai: IERC20, stkAave: IERC20, rewardsAssets: Address[]; + let aaveWhale: SignerWithAddress, + claimer: SignerWithAddress, + daiWhale: JsonRpcSigner, + proposer: SignerWithAddress; + let preUpgradeRewardsBalance: BigNumber; + let rewardsAccrualDuration: number; + + before(async () => { + await rawHRE.run('set-DRE'); + ethers = DRE.ethers; + [claimer, proposer] = await ethers.getSigners(); + + // Define ERC20 tokens + aave = (await ethers.getContractAt( + '@aave/protocol-v2/contracts/dependencies/openzeppelin/contracts/IERC20.sol:IERC20', + AAVE_TOKEN + )) as IERC20; + aDai = (await ethers.getContractAt( + '@aave/protocol-v2/contracts/protocol/tokenization/AToken.sol:AToken', + AAVE_DAI_TOKEN + )) as IERC20; + dai = (await ethers.getContractAt( + '@aave/protocol-v2/contracts/dependencies/openzeppelin/contracts/IERC20.sol:IERC20', + DAI_TOKEN + )) as IERC20; + stkAave = (await ethers.getContractAt( + '@aave/protocol-v2/contracts/dependencies/openzeppelin/contracts/IERC20.sol:IERC20', + STAKED_AAVE + )) as IERC20; + + // Define system contracts + gov = (await ethers.getContractAt( + 'IAaveGovernanceV2', + AAVE_GOVERNANCE_V2, + proposer + )) as IAaveGovernanceV2; + incentives = (await ethers.getContractAt( + 'StakedTokenIncentivesController', + INCENTIVES_PROXY + )) as IAaveIncentivesController; + const poolAddressesProvider = (await ethers.getContractAt( + '@aave/protocol-v2/contracts/interfaces/ILendingPoolAddressesProvider.sol:ILendingPoolAddressesProvider', + AAVE_LENDING_POOL_ADDRESSES_PROVIDER + )) as ILendingPoolAddressesProvider; + pool = (await ethers.getContractAt( + 'ILendingPool', + await poolAddressesProvider.getLendingPool() + )) as ILendingPool; + + // Spoof signers for the necessary accounts + await impersonateAccountsHardhat([AAVE_WHALE, DAI_WHALE]); + aaveWhale = ethers.provider.getSigner(AAVE_WHALE); + daiWhale = ethers.provider.getSigner(DAI_WHALE); + + // Seed the impersonated accounts with some ETH for gas + for (const ethBeneficiary of [AAVE_WHALE, DAI_WHALE]) { + const selfDestructContract = await deploySelfDestruct(); + await selfDestructContract.destroyAndTransfer(ethBeneficiary, { + value: ethers.utils.parseEther('10'), + }); + } + + rewardsAssets = [aDai.address]; + }); + + it('pre-upgrade: starts to accrue rewards', async () => { + // Seed claimer with DAI + const daiDepositAmount = ethers.utils.parseEther('10000'); + await dai.connect(daiWhale).transfer(claimer.address, daiDepositAmount); + + // Deposit DAI for aDAI + await dai.connect(claimer).approve(pool.address, daiDepositAmount); + await pool.connect(claimer).deposit(dai.address, daiDepositAmount, claimer.address, 0); + expect(await aDai.balanceOf(claimer.address)).to.be.gt(0); + + // Claim all rewards to zero out previously-accrued rewards + await incentives + .connect(claimer) + .claimRewards(rewardsAssets, constants.MaxUint256, claimer.address); + + // This will accrue rewards during the time it takes to perform the upgrade process + }); + + it('upgrades', async () => { + const blocktimePreUpgrade = await timeLatest(); + + // Deploy the executor contract + const proposalIncentivesRev2Executor = await new ProposalIncentivesV2Executor__factory( + proposer + ).deploy(); + const proposalExecutionPayload = proposalIncentivesRev2Executor.address; + + // Seed proposer with AAVE + await ( + await aave.connect(aaveWhale).transfer(proposer.address, utils.parseEther('2000000')) + ).wait(); + + // 1. Create proposal + + await advanceBlockTo((await latestBlock()) + 10); + + try { + const balance = await aave.balanceOf(proposer.address); + console.log('AAVE Balance proposer', utils.formatEther(balance)); + } catch (error) { + console.log(error); + } + + const proposalId = await gov.getProposalsCount(); + + // Not the actual ipfsHash + const ipfsHash = '0xf7a1f565fcd7684fba6fea5d77c5e699653e21cb6ae25fbf8c5dbc8d694c7949'; + + try { + const tx = await gov.create( + AAVE_SHORT_EXECUTOR, + [proposalExecutionPayload], + ['0'], + ['execute()'], + ['0x'], + [true], + ipfsHash, + { gasLimit: 3000000 } + ); + console.log('- Proposal submitted to Governance'); + await tx.wait(); + console.log('submitted'); + } catch (error) { + console.log(error); + } + + // 2. Queue proposal + + // Mine block due flash loan voting protection + await advanceBlockTo((await latestBlock()) + 1); + + // Submit vote and advance block to Queue phase + await (await gov.submitVote(proposalId, true)).wait(); + await advanceBlockTo((await latestBlock()) + VOTING_DURATION + 1); + + // Queue and advance block to Execution phase + await (await gov.queue(proposalId)).wait(); + let proposalState = await gov.getProposalState(proposalId); + expect(proposalState).to.be.equal(5); + + await increaseTime(86400 + 10); + + // Prior to executing the proposal, checkpoint the user's owed rewards + preUpgradeRewardsBalance = await incentives.getRewardsBalance(rewardsAssets, claimer.address); + expect(preUpgradeRewardsBalance).to.be.gt(0); + + rewardsAccrualDuration = (await timeLatest()).minus(blocktimePreUpgrade).toNumber(); + + // 3. Execute proposal + + try { + await (await gov.execute(proposalId, { gasLimit: 3000000 })).wait(); + } catch (error) { + console.log(error); + } + + console.log('Proposal executed'); + + proposalState = await gov.getProposalState(proposalId); + expect(proposalState).to.be.equal(7); + + // Revision number should be updated + expect(await incentives.REVISION()).to.be.equal(2); + }); + + it('post-upgrade: continues to accrue rewards correctly, and allows aToken holder to claim incentives using claimRewardsToSelf()', async () => { + // Tolerance for imprecise passage of time between actions + const rewardsCorrectnessTolerance = 10 ** 12; + + // Rewards should be accruing for aDai + expect((await incentives.getAssetData(aDai.address))[1]).to.be.gt(0); + + // Confirm the accrued rewards balance is more-or-less the same as pre-upgrade + const postUpgradeRewardsBalance = await incentives.getRewardsBalance( + rewardsAssets, + claimer.address + ); + expect(postUpgradeRewardsBalance).to.be.gte(preUpgradeRewardsBalance); + expect(postUpgradeRewardsBalance).to.be.lte( + preUpgradeRewardsBalance.add(rewardsCorrectnessTolerance) + ); + + // Wait for some time to accrue more rewards + await increaseTime(rewardsAccrualDuration); + + // Rewards accrued post-upgrade should be the same as those accrued pre-upgrade (equal durations) + const postUpgradeIncreasedTimeRewardsBalance = await incentives.getRewardsBalance( + rewardsAssets, + claimer.address + ); + + expect(postUpgradeIncreasedTimeRewardsBalance).to.be.gte(preUpgradeRewardsBalance.mul(2)); + expect(postUpgradeIncreasedTimeRewardsBalance).to.be.lte( + preUpgradeRewardsBalance.mul(2).add(rewardsCorrectnessTolerance) + ); + + // Checkpoint claimer data prior to claim + const aTokenBalance = await aDai.connect(claimer).scaledBalanceOf(claimer.address); + const stkAaveBalanceBefore = await stkAave.balanceOf(claimer.address); + const userIndexBefore = await getUserIndex( + incentives as StakedTokenIncentivesController, + claimer.address, + aDai.address + ); + + // Claim rewards to self + const tx = await incentives + .connect(claimer) + .claimRewardsToSelf(rewardsAssets, constants.MaxUint256); + + // Checkpoint claimer data post-claim + const stkAaveBalanceAfter = await stkAave.balanceOf(claimer.address); + const userIndexAfter = await getUserIndex( + incentives as StakedTokenIncentivesController, + claimer.address, + aDai.address + ); + + // Assert the correct event emission + expect(tx).to.emit(incentives, 'RewardsClaimed'); + + // Assert the rewards accrued as expected + expect(stkAaveBalanceAfter).to.be.gt(stkAaveBalanceBefore); + const expectedAccruedRewards = getRewards( + aTokenBalance, + userIndexAfter, + userIndexBefore + ).toString(); + expect(stkAaveBalanceAfter.sub(stkAaveBalanceBefore)).to.be.eq(expectedAccruedRewards); + }); +}); diff --git a/test-fork/incentivesProposalV2.spec.ts b/test-fork/incentivesProposalV2.spec.ts new file mode 100644 index 0000000..9e4d0fa --- /dev/null +++ b/test-fork/incentivesProposalV2.spec.ts @@ -0,0 +1,155 @@ +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/dist/src/signer-with-address'; +import { expect } from 'chai'; +import { BigNumber, utils } from 'ethers'; +import rawHRE from 'hardhat'; +import { deploySelfDestruct } from '../helpers/contracts-accessors'; +import { + advanceBlockTo, + DRE, + impersonateAccountsHardhat, + increaseTime, + latestBlock, +} from '../helpers/misc-utils'; +import { tEthereumAddress } from '../helpers/types'; +import { + IAaveGovernanceV2, + IERC20, + IAaveIncentivesController, + ProposalIncentivesV2Executor__factory, +} from '../types'; + +const AAVE_GOVERNANCE_V2 = '0xEC568fffba86c094cf06b22134B23074DFE2252c'; +const AAVE_SHORT_EXECUTOR = '0xee56e2b3d491590b5b31738cc34d5232f378a8d5'; +const AAVE_TOKEN = '0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9'; +const AAVE_WHALE = '0x25f2226b597e8f9514b3f68f00f494cf4f286491'; +const INCENTIVES_PROXY = '0xd784927Ff2f95ba542BfC824c8a8a98F3495f6b5'; +const VOTING_DURATION = 19200; + +describe('Upgrade to and test Revision #2 of the implementation', () => { + let ethers; + + let gov: IAaveGovernanceV2; + let incentivesController: IAaveIncentivesController; + let aave: IERC20; + let aaveWhale: SignerWithAddress, proposer: SignerWithAddress; + let proposalExecutionPayload: tEthereumAddress; + let proposalId: BigNumber; + + before(async () => { + await rawHRE.run('set-DRE'); + ethers = DRE.ethers; + [proposer] = await ethers.getSigners(); + + // Define ERC20 tokens + aave = (await ethers.getContractAt( + '@aave/protocol-v2/contracts/dependencies/openzeppelin/contracts/IERC20.sol:IERC20', + AAVE_TOKEN + )) as IERC20; + + // Define system contracts + incentivesController = (await ethers.getContractAt( + 'StakedTokenIncentivesController', + INCENTIVES_PROXY + )) as IAaveIncentivesController; + gov = (await ethers.getContractAt( + 'IAaveGovernanceV2', + AAVE_GOVERNANCE_V2, + proposer + )) as IAaveGovernanceV2; + + // Deploy the executor contract + const proposalIncentivesRev2Executor = await new ProposalIncentivesV2Executor__factory( + proposer + ).deploy(); + // await proposalIncentivesRev2Executor.deployTransaction.wait(); + proposalExecutionPayload = proposalIncentivesRev2Executor.address; + + // Spoof signer for AAVE_WHALE + await impersonateAccountsHardhat([AAVE_WHALE]); + aaveWhale = ethers.provider.getSigner(AAVE_WHALE); + + // Seed AAVE_WHALE with some ETH for gas + const selfDestructContract = await deploySelfDestruct(); + await selfDestructContract.destroyAndTransfer(AAVE_WHALE, { + value: ethers.utils.parseEther('10'), + }); + + // Transfer enough AAVE to proposer + await ( + await aave.connect(aaveWhale).transfer(proposer.address, utils.parseEther('2000000')) + ).wait(); + }); + + it('Incentives implementation should be rev1', async () => { + expect(await incentivesController.REVISION()).to.be.equal(1); + }); + + it('Proposal should be created', async () => { + await advanceBlockTo((await latestBlock()) + 10); + + try { + const balance = await aave.balanceOf(proposer.address); + console.log('AAVE Balance proposer', utils.formatEther(balance)); + } catch (error) { + console.log(error); + } + + // Submit proposal + proposalId = await gov.getProposalsCount(); + + // Not the actual ipfsHash + const ipfsHash = '0xf7a1f565fcd7684fba6fea5d77c5e699653e21cb6ae25fbf8c5dbc8d694c7949'; + + try { + const tx = await gov.create( + AAVE_SHORT_EXECUTOR, + [proposalExecutionPayload], + ['0'], + ['execute()'], + ['0x'], + [true], + ipfsHash, + { gasLimit: 3000000 } + ); + console.log('- Proposal submitted to Governance'); + await tx.wait(); + console.log('submitted'); + } catch (error) { + console.log(error); + } + + // Mine block due flash loan voting protection + await advanceBlockTo((await latestBlock()) + 1); + + // Submit vote and advance block to Queue phase + await (await gov.submitVote(proposalId, true)).wait(); + await advanceBlockTo((await latestBlock()) + VOTING_DURATION + 1); + }); + + it('Proposal should be queued', async () => { + // Queue and advance block to Execution phase + await (await gov.queue(proposalId)).wait(); + let proposalState = await gov.getProposalState(proposalId); + expect(proposalState).to.be.equal(5); + + await increaseTime(86400 + 10); + }); + + it('Proposal should be executed', async () => { + // Execute payload + try { + await (await gov.execute(proposalId, { gasLimit: 3000000 })).wait(); + } catch (error) { + console.log(error); + } + + console.log('Proposal executed'); + + const proposalState = await gov.getProposalState(proposalId); + expect(proposalState).to.be.equal(7); + }); + + it('Incentives implementation should be rev2', async () => { + expect(await incentivesController.REVISION()).to.be.equal(2); + }); +}); diff --git a/test/AaveIncentivesController/claim-to-self.spec.ts b/test/AaveIncentivesController/claim-to-self.spec.ts new file mode 100644 index 0000000..240eeb2 --- /dev/null +++ b/test/AaveIncentivesController/claim-to-self.spec.ts @@ -0,0 +1,32 @@ +import { expect } from 'chai'; +import { BigNumber } from 'ethers'; +import { makeSuite, TestEnv } from '../helpers/make-suite'; +import { waitForTx } from '../../helpers/misc-utils'; + +makeSuite('AaveIncentivesController - Claim rewards to self', (testEnv: TestEnv) => { + it('Should withdraw to the claimer', async () => { + const { aaveIncentivesController, users, aDaiMock, stakedAave } = testEnv; + const [userWithRewards] = users; + + await waitForTx(await aaveIncentivesController.configureAssets([aDaiMock.address], ['2000'])); + await waitForTx(await aDaiMock.setUserBalanceAndSupply('300000', '30000')); + + const priorStkBalance = await stakedAave.balanceOf(userWithRewards.address); + + const amountToClaim = BigNumber.from(123); + + await expect( + aaveIncentivesController + .connect(userWithRewards.signer) + .claimRewardsToSelf( + [aDaiMock.address], + amountToClaim + ) + ) + .to.emit(aaveIncentivesController, 'RewardsClaimed') + .withArgs(userWithRewards.address, userWithRewards.address, userWithRewards.address, amountToClaim); + + const afterStkBalance = await stakedAave.balanceOf(userWithRewards.address); + expect(afterStkBalance).to.be.eq(priorStkBalance.add(amountToClaim)); + }); +}); diff --git a/test/helpers/make-suite.ts b/test/helpers/make-suite.ts index 5a045df..77252a4 100644 --- a/test/helpers/make-suite.ts +++ b/test/helpers/make-suite.ts @@ -1,5 +1,6 @@ -import { evmRevert, evmSnapshot, DRE } from '../../helpers/misc-utils'; -import { Signer } from 'ethers'; +import { evmRevert, evmSnapshot, DRE, impersonateAccountsHardhat } from '../../helpers/misc-utils'; +import { constants, Signer, utils } from 'ethers'; +import { deploySelfDestruct } from '../../helpers/contracts-accessors'; import { getEthersSigners } from '../../helpers/contracts-helpers'; import { tEthereumAddress } from '../../helpers/types'; @@ -85,6 +86,26 @@ export function makeSuite(name: string, tests: (testEnv: TestEnv) => void) { describe(name, () => { before(async () => { setBuidlerevmSnapshotId(await evmSnapshot()); + + // Below mocks the behavior of V1 StakedTokenIncentivesController.initialize(), + // which grants max allowance of AAVE to the stkAAVE contract + + // Impersonate incentives controller + await impersonateAccountsHardhat([testEnv.aaveIncentivesController.address]); + const incentivesControllerAsSigner = DRE.ethers.provider.getSigner( + testEnv.aaveIncentivesController.address + ); + + // Seed incentives controller with eth + const selfDestructContract = await deploySelfDestruct(); + await selfDestructContract.destroyAndTransfer(testEnv.aaveIncentivesController.address, { + value: utils.parseEther('10'), + }); + + // Grants max allowance of incentives controller's AAVE to the stkAAVE contract + await testEnv.aaveToken + .connect(incentivesControllerAsSigner) + .approve(testEnv.stakedAave.address, constants.MaxUint256); }); tests(testEnv); after(async () => {