From 60058375d6c00db5c4db7c1d8a6fb6fa2dfe5654 Mon Sep 17 00:00:00 2001 From: sendra Date: Mon, 15 Mar 2021 12:51:47 +0100 Subject: [PATCH 01/22] Claim Helper contract --- .../interfaces/IClaimStakingRewardsHelper.sol | 15 +++++++++ contracts/misc/ClaimStakingRewardsHelper.sol | 33 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 contracts/interfaces/IClaimStakingRewardsHelper.sol create mode 100644 contracts/misc/ClaimStakingRewardsHelper.sol diff --git a/contracts/interfaces/IClaimStakingRewardsHelper.sol b/contracts/interfaces/IClaimStakingRewardsHelper.sol new file mode 100644 index 0000000..f2a060f --- /dev/null +++ b/contracts/interfaces/IClaimStakingRewardsHelper.sol @@ -0,0 +1,15 @@ +pragma solidity ^0.7.5; + +interface IClaimStakingRewardsHelper { + function claimAllRewards( + address from, + address to, + uint256 amount + ) external; + + function claimAllRewardsAndStake( + address from, + address to, + uint256 amount + ) external; +} diff --git a/contracts/misc/ClaimStakingRewardsHelper.sol b/contracts/misc/ClaimStakingRewardsHelper.sol new file mode 100644 index 0000000..b1261d5 --- /dev/null +++ b/contracts/misc/ClaimStakingRewardsHelper.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.7.5; + +import {IClaimStakingRewardsHelper} from '../interfaces/IClaimStakingRewardsHelper.sol'; +import {IStakedTokenV3} from '../interfaces/IStakedTokenV3.sol'; + +contract ClaimStakingRewardsHelper is IClaimStakingRewardsHelper { + address[] public stakeTokens; + + constructor(address[] memory _stakeTokens) { + stakeTokens = _stakeTokens; + } + + function claimAllRewards( + address from, + address to, + uint256 amount + ) external override { + for (uint256 i = 0; i < stakeTokens.length; i++) { + IStakedTokenV3(stakeTokens[i]).claimRewardsOnBehalf(from, to, amount); + } + } + + function claimAllRewardsAndStake( + address from, + address to, + uint256 amount + ) external override { + for (uint256 i = 0; i < stakeTokens.length; i++) { + IStakedTokenV3(stakeTokens[i]).claimRewardsAndStakeOnBehalf(from, to, amount); + } + } +} From c3f142241b83fa988285abb6eb39b788522fd9d6 Mon Sep 17 00:00:00 2001 From: sendra Date: Mon, 15 Mar 2021 13:29:27 +0100 Subject: [PATCH 02/22] fixed from bug --- .../interfaces/IClaimStakingRewardsHelper.sol | 12 ++---------- contracts/misc/ClaimStakingRewardsHelper.sol | 16 ++++------------ 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/contracts/interfaces/IClaimStakingRewardsHelper.sol b/contracts/interfaces/IClaimStakingRewardsHelper.sol index f2a060f..e013cc1 100644 --- a/contracts/interfaces/IClaimStakingRewardsHelper.sol +++ b/contracts/interfaces/IClaimStakingRewardsHelper.sol @@ -1,15 +1,7 @@ pragma solidity ^0.7.5; interface IClaimStakingRewardsHelper { - function claimAllRewards( - address from, - address to, - uint256 amount - ) external; + function claimAllRewards(address to, uint256 amount) external; - function claimAllRewardsAndStake( - address from, - address to, - uint256 amount - ) external; + function claimAllRewardsAndStake(address to, uint256 amount) external; } diff --git a/contracts/misc/ClaimStakingRewardsHelper.sol b/contracts/misc/ClaimStakingRewardsHelper.sol index b1261d5..2864123 100644 --- a/contracts/misc/ClaimStakingRewardsHelper.sol +++ b/contracts/misc/ClaimStakingRewardsHelper.sol @@ -11,23 +11,15 @@ contract ClaimStakingRewardsHelper is IClaimStakingRewardsHelper { stakeTokens = _stakeTokens; } - function claimAllRewards( - address from, - address to, - uint256 amount - ) external override { + function claimAllRewards(address to, uint256 amount) external override { for (uint256 i = 0; i < stakeTokens.length; i++) { - IStakedTokenV3(stakeTokens[i]).claimRewardsOnBehalf(from, to, amount); + IStakedTokenV3(stakeTokens[i]).claimRewardsOnBehalf(msg.sender, to, amount); } } - function claimAllRewardsAndStake( - address from, - address to, - uint256 amount - ) external override { + function claimAllRewardsAndStake(address to, uint256 amount) external override { for (uint256 i = 0; i < stakeTokens.length; i++) { - IStakedTokenV3(stakeTokens[i]).claimRewardsAndStakeOnBehalf(from, to, amount); + IStakedTokenV3(stakeTokens[i]).claimRewardsAndStakeOnBehalf(msg.sender, to, amount); } } } From dd97e09d2f8c47a0214dd88c6130a840323880a0 Mon Sep 17 00:00:00 2001 From: sendra Date: Mon, 15 Mar 2021 14:29:58 +0100 Subject: [PATCH 03/22] send stake addresses separately instead of array --- contracts/misc/ClaimStakingRewardsHelper.sol | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/contracts/misc/ClaimStakingRewardsHelper.sol b/contracts/misc/ClaimStakingRewardsHelper.sol index 2864123..1ea0d6e 100644 --- a/contracts/misc/ClaimStakingRewardsHelper.sol +++ b/contracts/misc/ClaimStakingRewardsHelper.sol @@ -5,21 +5,21 @@ import {IClaimStakingRewardsHelper} from '../interfaces/IClaimStakingRewardsHelp import {IStakedTokenV3} from '../interfaces/IStakedTokenV3.sol'; contract ClaimStakingRewardsHelper is IClaimStakingRewardsHelper { - address[] public stakeTokens; + address public immutable aaveStakeToken; + address public immutable bptStakeToken; - constructor(address[] memory _stakeTokens) { - stakeTokens = _stakeTokens; + constructor(address _aaveStakeToken, address _bptStakeToken) { + aaveStakeToken = _aaveStakeToken; + bptStakeToken = _bptStakeToken; } function claimAllRewards(address to, uint256 amount) external override { - for (uint256 i = 0; i < stakeTokens.length; i++) { - IStakedTokenV3(stakeTokens[i]).claimRewardsOnBehalf(msg.sender, to, amount); - } + IStakedTokenV3(aaveStakeToken).claimRewardsOnBehalf(msg.sender, to, amount); + IStakedTokenV3(bptStakeToken).claimRewardsOnBehalf(msg.sender, to, amount); } function claimAllRewardsAndStake(address to, uint256 amount) external override { - for (uint256 i = 0; i < stakeTokens.length; i++) { - IStakedTokenV3(stakeTokens[i]).claimRewardsAndStakeOnBehalf(msg.sender, to, amount); - } + IStakedTokenV3(aaveStakeToken).claimRewardsAndStakeOnBehalf(msg.sender, to, amount); + IStakedTokenV3(bptStakeToken).claimRewardsAndStakeOnBehalf(msg.sender, to, amount); } } From 2fe6416c5e6e5808019c7f671309de3f7e5ccff1 Mon Sep 17 00:00:00 2001 From: sendra Date: Mon, 15 Mar 2021 15:26:55 +0100 Subject: [PATCH 04/22] added deployer, and scripts --- package.json | 2 ++ tasks/deployments/deploy-ClaimHelper.ts | 40 +++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 tasks/deployments/deploy-ClaimHelper.ts diff --git a/package.json b/package.json index c4c933d..1825076 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,8 @@ "ropsten:deployment": "npm run hardhat-ropsten -- common-deployment --verify", "kovan:deployment": "npm run hardhat-kovan -- common-deployment --verify", "main:deployment": "npm run hardhat-main -- common-deployment --verify", + "kovan:deploy-ClaimHelper": "hardhat --network kovan deploy-ClaimHelper", + "main:deploy-ClaimHelper": "hardhat --network main deploy-OracleAnchor", "prettier:check": "npx prettier -c 'tasks/**/*.ts' 'contracts/**/*.sol' 'helpers/**/*.ts' 'test/**/*.ts'", "prettier:write": "prettier --write 'tasks/**/*.ts' 'contracts/**/*.sol' 'helpers/**/*.ts' 'test/**/*.ts'", "ci:clean": "rm -rf types/ cache/ artifacts/", diff --git a/tasks/deployments/deploy-ClaimHelper.ts b/tasks/deployments/deploy-ClaimHelper.ts new file mode 100644 index 0000000..5876bda --- /dev/null +++ b/tasks/deployments/deploy-ClaimHelper.ts @@ -0,0 +1,40 @@ +import { task } from 'hardhat/config'; +import { verifyContract } from '../../helpers/etherscan-verification'; +import { eEthereumNetwork } from '../../helpers/types'; +import { ClaimStakingRewardsHelper__factory } from '../../types'; + +task(`deploy-ClaimHelper`, `Deploys the ClaimStakingRewardsHelper contract`) + .addFlag('verify', 'Verify ClaimStakingRewardsHelper contract via Etherscan API.') + .setAction(async ({ verify }, localBRE) => { + await localBRE.run('set-DRE'); + + if (!localBRE.network.config.chainId) { + throw new Error('INVALID_CHAIN_ID'); + } + + const stakeTokens: { + [network: string]: { aaveStakeTokenAddress: string; bptStakeTokenAddress: string }; + } = { + [eEthereumNetwork.kovan]: { + aaveStakeTokenAddress: '0xf2fbf9A6710AfDa1c4AaB2E922DE9D69E0C97fd2', + bptStakeTokenAddress: '0x31ce45Ab6E26C72c47C52c27498D460099545ef2', + }, + [eEthereumNetwork.main]: { + aaveStakeTokenAddress: '0x4da27a545c0c5b758a6ba100e3a049001de870f5', + bptStakeTokenAddress: '0xa1116930326D21fB917d5A27F1E9943A9595fb47', + }, + }; + + console.log(`\tDeploying ClaimHelper implementation ...`); + const ClaimHelper = await new ClaimStakingRewardsHelper__factory( + await localBRE.ethers.provider.getSigner() + ).deploy( + stakeTokens[localBRE.network.name].aaveStakeTokenAddress, + stakeTokens[localBRE.network.name].bptStakeTokenAddress + ); + await ClaimHelper.deployTransaction.wait(); + console.log('ClaimHelper.address', ClaimHelper.address); + await verifyContract(ClaimHelper.address, []); + + console.log(`\tFinished ClaimHelper deployment`); + }); From 0e488f3f078cdc1f8b64814b4574b4e284f482f0 Mon Sep 17 00:00:00 2001 From: sendra Date: Mon, 15 Mar 2021 16:52:47 +0100 Subject: [PATCH 05/22] added deployment test --- docker-compose.yml | 1 + hardhat.config.ts | 2 +- helpers/contracts-accessors.ts | 17 +++- helpers/types.ts | 1 + package-lock.json | 2 +- package.json | 4 +- tasks/deployments/deploy-ClaimHelper.ts | 21 ++--- test/StakedAaveV3/claimHelper.spec.ts | 115 ++++++++++++++++++++++++ 8 files changed, 145 insertions(+), 18 deletions(-) create mode 100644 test/StakedAaveV3/claimHelper.spec.ts diff --git a/docker-compose.yml b/docker-compose.yml index 6672ed8..897a51a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,3 +16,4 @@ services: INFURA_KEY: ${INFURA_KEY} ETHERSCAN_NETWORK: ${ETHERSCAN_NETWORK} GITLAB_ACCESS_TOKEN: ${GITLAB_ACCESS_TOKEN} + ALCHEMY_KEY: ${ALCHEMY_KEY} diff --git a/hardhat.config.ts b/hardhat.config.ts index 7692493..f4c1a4c 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -38,7 +38,7 @@ if (!SKIP_LOAD) { } require(`${path.join(__dirname, 'tasks/misc')}/set-dre.ts`); - +console.log('----------------------> ', ALCHEMY_KEY); const mainnetFork = MAINNET_FORK ? { blockNumber: FORKING_BLOCK, diff --git a/helpers/contracts-accessors.ts b/helpers/contracts-accessors.ts index cd36d41..2643fb7 100644 --- a/helpers/contracts-accessors.ts +++ b/helpers/contracts-accessors.ts @@ -3,7 +3,7 @@ import { eContractid, tEthereumAddress } from './types'; import { MintableErc20 } from '../types/MintableErc20'; import { StakedAave } from '../types/StakedAave'; import { StakedAaveV2 } from '../types/StakedAaveV2'; -import {StakedAaveV3 } from '../types/StakedAaveV3'; +import { StakedAaveV3 } from '../types/StakedAaveV3'; import { IcrpFactory } from '../types/IcrpFactory'; // Configurable right pool factory import { IConfigurableRightsPool } from '../types/IConfigurableRightsPool'; import { IControllerAaveEcosystemReserve } from '../types/IControllerAaveEcosystemReserve'; @@ -22,6 +22,20 @@ import { DoubleTransferHelper } from '../types/DoubleTransferHelper'; import { zeroAddress } from 'ethereumjs-util'; import { ZERO_ADDRESS } from './constants'; import { Signer } from 'ethers'; +import { ClaimStakingRewardsHelper } from '../types'; + +export const deployClaimHelper = async ( + [aaveStakeTokenAddress, bptStakeTokenAddress]: [tEthereumAddress, tEthereumAddress], + verify?: boolean +) => { + const id = eContractid.ClaimStakingRewardsHelper; + const args: string[] = [aaveStakeTokenAddress, bptStakeTokenAddress]; + const instance = await deployContract(id, args); + if (verify) { + await verifyContract(instance.address, args); + } + return instance; +}; export const deployStakedAave = async ( [ @@ -198,7 +212,6 @@ export const deployStakedTokenV3 = async ( return instance; }; - export const deployStakedAaveV3 = async ( [ stakedToken, diff --git a/helpers/types.ts b/helpers/types.ts index 37ffaf2..a41cb06 100644 --- a/helpers/types.ts +++ b/helpers/types.ts @@ -30,6 +30,7 @@ export enum eContractid { IBPool = 'IBPool', IControllerAaveEcosystemReserve = 'IControllerAaveEcosystemReserve', MockSelfDestruct = 'SelfdestructTransfer', + ClaimStakingRewardsHelper = 'ClaimStakingRewardsHelper', } export type tEthereumAddress = string; diff --git a/package-lock.json b/package-lock.json index a3d3ef1..52718ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13509,7 +13509,7 @@ } }, "ethereumjs-abi": { - "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#1ce6a1d64235fabe2aaf827fd606def55693508f", + "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#1a27c59c15ab1e95ee8e5c4ed6ad814c49cc439e", "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git", "dev": true, "requires": { diff --git a/package.json b/package.json index 1825076..e87eeb2 100644 --- a/package.json +++ b/package.json @@ -28,8 +28,8 @@ "ropsten:deployment": "npm run hardhat-ropsten -- common-deployment --verify", "kovan:deployment": "npm run hardhat-kovan -- common-deployment --verify", "main:deployment": "npm run hardhat-main -- common-deployment --verify", - "kovan:deploy-ClaimHelper": "hardhat --network kovan deploy-ClaimHelper", - "main:deploy-ClaimHelper": "hardhat --network main deploy-OracleAnchor", + "kovan:deploy-ClaimHelper": "hardhat --network kovan deploy-ClaimHelper --verify", + "main:deploy-ClaimHelper": "hardhat --network main deploy-OracleAnchor --verify", "prettier:check": "npx prettier -c 'tasks/**/*.ts' 'contracts/**/*.sol' 'helpers/**/*.ts' 'test/**/*.ts'", "prettier:write": "prettier --write 'tasks/**/*.ts' 'contracts/**/*.sol' 'helpers/**/*.ts' 'test/**/*.ts'", "ci:clean": "rm -rf types/ cache/ artifacts/", diff --git a/tasks/deployments/deploy-ClaimHelper.ts b/tasks/deployments/deploy-ClaimHelper.ts index 5876bda..bb44ebf 100644 --- a/tasks/deployments/deploy-ClaimHelper.ts +++ b/tasks/deployments/deploy-ClaimHelper.ts @@ -1,12 +1,11 @@ import { task } from 'hardhat/config'; -import { verifyContract } from '../../helpers/etherscan-verification'; +import { deployClaimHelper } from '../../helpers/contracts-accessors'; import { eEthereumNetwork } from '../../helpers/types'; -import { ClaimStakingRewardsHelper__factory } from '../../types'; task(`deploy-ClaimHelper`, `Deploys the ClaimStakingRewardsHelper contract`) .addFlag('verify', 'Verify ClaimStakingRewardsHelper contract via Etherscan API.') .setAction(async ({ verify }, localBRE) => { - await localBRE.run('set-DRE'); + await localBRE.run('set-dre'); if (!localBRE.network.config.chainId) { throw new Error('INVALID_CHAIN_ID'); @@ -26,15 +25,13 @@ task(`deploy-ClaimHelper`, `Deploys the ClaimStakingRewardsHelper contract`) }; console.log(`\tDeploying ClaimHelper implementation ...`); - const ClaimHelper = await new ClaimStakingRewardsHelper__factory( - await localBRE.ethers.provider.getSigner() - ).deploy( - stakeTokens[localBRE.network.name].aaveStakeTokenAddress, - stakeTokens[localBRE.network.name].bptStakeTokenAddress + const ClaimHelper = await deployClaimHelper( + [ + stakeTokens[localBRE.network.name].aaveStakeTokenAddress, + stakeTokens[localBRE.network.name].bptStakeTokenAddress, + ], + verify ); - await ClaimHelper.deployTransaction.wait(); - console.log('ClaimHelper.address', ClaimHelper.address); - await verifyContract(ClaimHelper.address, []); - console.log(`\tFinished ClaimHelper deployment`); + console.log(`\tFinished ClaimHelper deployment: ${ClaimHelper.address}`); }); diff --git a/test/StakedAaveV3/claimHelper.spec.ts b/test/StakedAaveV3/claimHelper.spec.ts new file mode 100644 index 0000000..490f700 --- /dev/null +++ b/test/StakedAaveV3/claimHelper.spec.ts @@ -0,0 +1,115 @@ +import { makeSuite, TestEnv } from '../helpers/make-suite'; +import { COOLDOWN_SECONDS, UNSTAKE_WINDOW, MAX_UINT_AMOUNT, WAD } from '../../helpers/constants'; +import { + waitForTx, + timeLatest, + advanceBlock, + increaseTimeAndMine, + DRE, + evmRevert, + evmSnapshot, +} from '../../helpers/misc-utils'; +import { ethers } from 'ethers'; +import BigNumber from 'bignumber.js'; +import { + buildPermitParams, + getContract, + getEthersSigners, + getSignatureFromTypedData, +} from '../../helpers/contracts-helpers'; +import { + deployClaimHelper, + deployStakedAaveV3, + getStakedAaveProxy, +} from '../../helpers/contracts-accessors'; +import { StakedTokenV3 } from '../../types/StakedTokenV3'; +import { StakedAaveV3 } from '../../types/StakedAaveV3'; +import { getUserIndex } from '../DistributionManager/data-helpers/asset-user-data'; +import { getRewards } from '../DistributionManager/data-helpers/base-math'; +import { compareRewardsAtAction } from '../StakedAaveV2/data-helpers/reward'; +import { fail } from 'assert'; +import { parseEther } from 'ethers/lib/utils'; +import { ClaimStakingRewardsHelper } from '../../types'; + +const { expect } = require('chai'); + +const SLASHING_ADMIN = 0; +const COOLDOWN_ADMIN = 1; +const CLAIM_HELPER_ROLE = 2; + +makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { + let stakeAaveV3: StakedAaveV3; + let stakeAave2V3: StakedAaveV3; + let claimHelper: ClaimStakingRewardsHelper; + let snap: string; + + it('Deploys 2 stake tokens with claimHelper address', async () => { + const { aaveToken, users } = testEnv; + + const [deployer, rewardsVault] = await getEthersSigners(); + + const rewardsVaultAddress = (await rewardsVault.getAddress()).toString(); + const emissionManager = await deployer.getAddress(); + + stakeAaveV3 = await deployStakedAaveV3([ + aaveToken.address, + aaveToken.address, + COOLDOWN_SECONDS, + UNSTAKE_WINDOW, + rewardsVaultAddress, + emissionManager, + (1000 * 60 * 60).toString(), + ]); + + stakeAave2V3 = await deployStakedAaveV3([ + aaveToken.address, + aaveToken.address, + COOLDOWN_SECONDS, + UNSTAKE_WINDOW, + rewardsVaultAddress, + emissionManager, + (1000 * 60 * 60).toString(), + ]); + + await aaveToken.connect(rewardsVault).approve(stakeAaveV3.address, MAX_UINT_AMOUNT); + await aaveToken.connect(rewardsVault).approve(stakeAave2V3.address, MAX_UINT_AMOUNT); + + // deploy claim helper contract + claimHelper = await deployClaimHelper([stakeAaveV3.address, stakeAave2V3.address], false); + + //initialize the stake instance + + await stakeAaveV3['initialize(address,address,address,uint256,string,string,uint8)']( + users[0].address, + users[1].address, + claimHelper.address, + '2000', + 'Staked AAVE', + 'stkAAVE', + 18 + ); + await stakeAave2V3['initialize(address,address,address,uint256,string,string,uint8)']( + users[0].address, + users[1].address, + claimHelper.address, + '2000', + 'Staked AAVE', + 'stkAAVE', + 18 + ); + + const slashingAdmin = await stakeAaveV3.getAdmin(SLASHING_ADMIN); //slash admin + const cooldownAdmin = await stakeAaveV3.getAdmin(COOLDOWN_ADMIN); //cooldown admin + const claimAdmin = await stakeAave2V3.getAdmin(CLAIM_HELPER_ROLE); //claim admin // helper contract + const slashingAdmin2 = await stakeAave2V3.getAdmin(SLASHING_ADMIN); //slash admin + const cooldownAdmin2 = await stakeAave2V3.getAdmin(COOLDOWN_ADMIN); //cooldown admin + const claimAdmin2 = await stakeAave2V3.getAdmin(CLAIM_HELPER_ROLE); //claim admin // helper contract + + expect(slashingAdmin).to.be.equal(users[0].address); + expect(cooldownAdmin).to.be.equal(users[1].address); + expect(claimAdmin).to.be.equal(claimHelper.address); + expect(slashingAdmin2).to.be.equal(users[0].address); + expect(cooldownAdmin2).to.be.equal(users[1].address); + expect(claimAdmin2).to.be.equal(claimHelper.address); + }); +}); From 5983acc3cea348c09410187776dd1cc03c00a2ad Mon Sep 17 00:00:00 2001 From: sendra Date: Mon, 15 Mar 2021 17:34:54 +0100 Subject: [PATCH 06/22] added claim all test, commented claim and restake --- contracts/misc/ClaimStakingRewardsHelper.sol | 8 ++--- test/StakedAaveV3/claimHelper.spec.ts | 38 ++++++++++++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/contracts/misc/ClaimStakingRewardsHelper.sol b/contracts/misc/ClaimStakingRewardsHelper.sol index 1ea0d6e..8ae6135 100644 --- a/contracts/misc/ClaimStakingRewardsHelper.sol +++ b/contracts/misc/ClaimStakingRewardsHelper.sol @@ -18,8 +18,8 @@ contract ClaimStakingRewardsHelper is IClaimStakingRewardsHelper { IStakedTokenV3(bptStakeToken).claimRewardsOnBehalf(msg.sender, to, amount); } - function claimAllRewardsAndStake(address to, uint256 amount) external override { - IStakedTokenV3(aaveStakeToken).claimRewardsAndStakeOnBehalf(msg.sender, to, amount); - IStakedTokenV3(bptStakeToken).claimRewardsAndStakeOnBehalf(msg.sender, to, amount); - } + // function claimAllRewardsAndStake(address to, uint256 amount) external override { + // IStakedTokenV3(aaveStakeToken).claimRewardsAndStakeOnBehalf(msg.sender, to, amount); + // IStakedTokenV3(bptStakeToken).claimRewardsAndStakeOnBehalf(msg.sender, to, amount); + // } } diff --git a/test/StakedAaveV3/claimHelper.spec.ts b/test/StakedAaveV3/claimHelper.spec.ts index 490f700..5602d78 100644 --- a/test/StakedAaveV3/claimHelper.spec.ts +++ b/test/StakedAaveV3/claimHelper.spec.ts @@ -112,4 +112,42 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { expect(cooldownAdmin2).to.be.equal(users[1].address); expect(claimAdmin2).to.be.equal(claimHelper.address); }); + it('Claims all rewards from both stakes', async () => { + const { + aaveToken, + users: [, staker], + } = testEnv; + const amount = ethers.utils.parseEther('10'); + + const saveBalanceBefore = new BigNumber( + (await stakeAaveV3.balanceOf(staker.address)).toString() + ); + const saveBalanceBefore2 = new BigNumber( + (await stakeAave2V3.balanceOf(staker.address)).toString() + ); + + // Prepare actions for the test case + aaveToken.connect(staker.signer).approve(stakeAaveV3.address, amount); + aaveToken.connect(staker.signer).approve(stakeAave2V3.address, amount); + + stakeAaveV3.connect(staker.signer).stake(staker.address, amount); + stakeAave2V3.connect(staker.signer).stake(staker.address, amount); + + // Increase time for bigger rewards + await increaseTimeAndMine(1000); + + // user1 claims all + const rewards = await stakeAaveV3.stakerRewardsToClaim(staker.address); + const rewards2 = await stakeAave2V3.stakerRewardsToClaim(staker.address); + + const saveUserBalance = await aaveToken.balanceOf(staker.address); + + await claimHelper + .connect(staker.signer) + .claimAllRewards(staker.address, ethers.constants.MaxUint256.toString()); + + const userBalanceAfterActions = await aaveToken.balanceOf(staker.address); + + expect(userBalanceAfterActions.eq(saveUserBalance.add(rewards.add(rewards2)))).to.be.ok; + }); }); From 2ac266c9ce95e3a422401b35b5ac271d4f368165 Mon Sep 17 00:00:00 2001 From: sendra Date: Mon, 15 Mar 2021 17:37:18 +0100 Subject: [PATCH 07/22] commented claim and restake --- contracts/interfaces/IClaimStakingRewardsHelper.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/interfaces/IClaimStakingRewardsHelper.sol b/contracts/interfaces/IClaimStakingRewardsHelper.sol index e013cc1..1a2f54e 100644 --- a/contracts/interfaces/IClaimStakingRewardsHelper.sol +++ b/contracts/interfaces/IClaimStakingRewardsHelper.sol @@ -3,5 +3,5 @@ pragma solidity ^0.7.5; interface IClaimStakingRewardsHelper { function claimAllRewards(address to, uint256 amount) external; - function claimAllRewardsAndStake(address to, uint256 amount) external; + // function claimAllRewardsAndStake(address to, uint256 amount) external; } From 1cadef73f8fdcbfae0770dd75d0090c41457218d Mon Sep 17 00:00:00 2001 From: sendra Date: Tue, 16 Mar 2021 11:42:20 +0100 Subject: [PATCH 08/22] WIP: tests not fully working --- .../interfaces/IClaimStakingRewardsHelper.sol | 2 +- contracts/misc/ClaimStakingRewardsHelper.sol | 11 ++-- hardhat.config.ts | 2 +- test/StakedAaveV3/claimHelper.spec.ts | 58 ++++++++++++++++--- 4 files changed, 60 insertions(+), 13 deletions(-) diff --git a/contracts/interfaces/IClaimStakingRewardsHelper.sol b/contracts/interfaces/IClaimStakingRewardsHelper.sol index 1a2f54e..e013cc1 100644 --- a/contracts/interfaces/IClaimStakingRewardsHelper.sol +++ b/contracts/interfaces/IClaimStakingRewardsHelper.sol @@ -3,5 +3,5 @@ pragma solidity ^0.7.5; interface IClaimStakingRewardsHelper { function claimAllRewards(address to, uint256 amount) external; - // function claimAllRewardsAndStake(address to, uint256 amount) external; + function claimAllRewardsAndStake(address to, uint256 amount) external; } diff --git a/contracts/misc/ClaimStakingRewardsHelper.sol b/contracts/misc/ClaimStakingRewardsHelper.sol index 8ae6135..c16b6a5 100644 --- a/contracts/misc/ClaimStakingRewardsHelper.sol +++ b/contracts/misc/ClaimStakingRewardsHelper.sol @@ -18,8 +18,11 @@ contract ClaimStakingRewardsHelper is IClaimStakingRewardsHelper { IStakedTokenV3(bptStakeToken).claimRewardsOnBehalf(msg.sender, to, amount); } - // function claimAllRewardsAndStake(address to, uint256 amount) external override { - // IStakedTokenV3(aaveStakeToken).claimRewardsAndStakeOnBehalf(msg.sender, to, amount); - // IStakedTokenV3(bptStakeToken).claimRewardsAndStakeOnBehalf(msg.sender, to, amount); - // } + function claimAllRewardsAndStake(address to, uint256 amount) external override { + IStakedTokenV3(aaveStakeToken).claimRewardsAndStakeOnBehalf(msg.sender, to, amount); + + uint256 rewardsClaimed = + IStakedTokenV3(bptStakeToken).claimRewardsOnBehalf(msg.sender, address(this), amount); + IStakedTokenV3(aaveStakeToken).stake(to, rewardsClaimed); + } } diff --git a/hardhat.config.ts b/hardhat.config.ts index f4c1a4c..7692493 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -38,7 +38,7 @@ if (!SKIP_LOAD) { } require(`${path.join(__dirname, 'tasks/misc')}/set-dre.ts`); -console.log('----------------------> ', ALCHEMY_KEY); + const mainnetFork = MAINNET_FORK ? { blockNumber: FORKING_BLOCK, diff --git a/test/StakedAaveV3/claimHelper.spec.ts b/test/StakedAaveV3/claimHelper.spec.ts index 5602d78..a19696f 100644 --- a/test/StakedAaveV3/claimHelper.spec.ts +++ b/test/StakedAaveV3/claimHelper.spec.ts @@ -119,13 +119,6 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { } = testEnv; const amount = ethers.utils.parseEther('10'); - const saveBalanceBefore = new BigNumber( - (await stakeAaveV3.balanceOf(staker.address)).toString() - ); - const saveBalanceBefore2 = new BigNumber( - (await stakeAave2V3.balanceOf(staker.address)).toString() - ); - // Prepare actions for the test case aaveToken.connect(staker.signer).approve(stakeAaveV3.address, amount); aaveToken.connect(staker.signer).approve(stakeAave2V3.address, amount); @@ -150,4 +143,55 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { expect(userBalanceAfterActions.eq(saveUserBalance.add(rewards.add(rewards2)))).to.be.ok; }); + it('Claims all rewards from both stakes and stakes claimed amount', async () => { + const { + aaveToken, + users: [, staker], + } = testEnv; + const amount = ethers.utils.parseEther('10'); + + // Prepare actions for the test case + aaveToken.connect(staker.signer).approve(stakeAaveV3.address, amount); + aaveToken.connect(staker.signer).approve(stakeAave2V3.address, amount); + + stakeAaveV3.connect(staker.signer).stake(staker.address, amount); + stakeAave2V3.connect(staker.signer).stake(staker.address, amount); + + // Increase time for bigger rewards + await increaseTimeAndMine(1000); + + // save state + const saveUserBalance = await aaveToken.balanceOf(staker.address); + const saveBalanceBefore = new BigNumber( + (await stakeAaveV3.balanceOf(staker.address)).toString() + ); + const saveBalanceBefore2 = new BigNumber( + (await stakeAave2V3.balanceOf(staker.address)).toString() + ); + const rewards = new BigNumber( + (await stakeAaveV3.stakerRewardsToClaim(staker.address)).toString() + ); + const rewards2 = new BigNumber( + (await stakeAave2V3.stakerRewardsToClaim(staker.address)).toString() + ); + console.log(`rewards: ${rewards.toString()} | rewards2: ${rewards2.toString()}`); + + // claim and stake + await claimHelper + .connect(staker.signer) + .claimAllRewardsAndStake(staker.address, ethers.constants.MaxUint256.toString()); + + // current state + const userBalanceAfterActions = await aaveToken.balanceOf(staker.address); + const stakeBalanceAfter = new BigNumber( + (await stakeAaveV3.balanceOf(staker.address)).toString() + ); + const stakeBalanceAfter2 = new BigNumber( + (await stakeAave2V3.balanceOf(staker.address)).toString() + ); + + expect(userBalanceAfterActions.eq(saveUserBalance)).to.be.ok; + expect(stakeBalanceAfter2.eq(saveBalanceBefore2)).to.be.ok; + expect(stakeBalanceAfter.eq(saveBalanceBefore.plus(rewards2).plus(rewards))).to.be.ok; + }); }); From 65b28b0de0e3ba2d10fdb2e075b241a298b626e5 Mon Sep 17 00:00:00 2001 From: sendra Date: Tue, 16 Mar 2021 12:08:09 +0100 Subject: [PATCH 09/22] WIP: added emitters --- test/StakedAaveV3/claimHelper.spec.ts | 28 ++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/test/StakedAaveV3/claimHelper.spec.ts b/test/StakedAaveV3/claimHelper.spec.ts index a19696f..72f8ac9 100644 --- a/test/StakedAaveV3/claimHelper.spec.ts +++ b/test/StakedAaveV3/claimHelper.spec.ts @@ -1,5 +1,11 @@ import { makeSuite, TestEnv } from '../helpers/make-suite'; -import { COOLDOWN_SECONDS, UNSTAKE_WINDOW, MAX_UINT_AMOUNT, WAD } from '../../helpers/constants'; +import { + COOLDOWN_SECONDS, + UNSTAKE_WINDOW, + MAX_UINT_AMOUNT, + SHORT_EXECUTOR, + WAD, +} from '../../helpers/constants'; import { waitForTx, timeLatest, @@ -98,6 +104,26 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { 18 ); + await waitForTx( + await stakeAaveV3.connect(emissionManager).configureAssets([ + { + emissionPerSecond: parseEther('0.01').toString(), + totalStaked: parseEther('1000').toString(), + underlyingAsset: aaveToken.address, + }, + ]) + ); + + await waitForTx( + await stakeAave2V3.connect(emissionManager).configureAssets([ + { + emissionPerSecond: parseEther('0.01').toString(), + totalStaked: parseEther('1000').toString(), + underlyingAsset: aaveToken.address, + }, + ]) + ); + const slashingAdmin = await stakeAaveV3.getAdmin(SLASHING_ADMIN); //slash admin const cooldownAdmin = await stakeAaveV3.getAdmin(COOLDOWN_ADMIN); //cooldown admin const claimAdmin = await stakeAave2V3.getAdmin(CLAIM_HELPER_ROLE); //claim admin // helper contract From 373549a141caef869ab7579d92033a5a47160fe3 Mon Sep 17 00:00:00 2001 From: sendra Date: Tue, 16 Mar 2021 12:26:53 +0100 Subject: [PATCH 10/22] wip: added logs --- test/StakedAaveV3/claimHelper.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/StakedAaveV3/claimHelper.spec.ts b/test/StakedAaveV3/claimHelper.spec.ts index 72f8ac9..f51986a 100644 --- a/test/StakedAaveV3/claimHelper.spec.ts +++ b/test/StakedAaveV3/claimHelper.spec.ts @@ -105,7 +105,7 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { ); await waitForTx( - await stakeAaveV3.connect(emissionManager).configureAssets([ + await stakeAaveV3.connect(deployer).configureAssets([ { emissionPerSecond: parseEther('0.01').toString(), totalStaked: parseEther('1000').toString(), @@ -115,7 +115,7 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { ); await waitForTx( - await stakeAave2V3.connect(emissionManager).configureAssets([ + await stakeAave2V3.connect(deployer).configureAssets([ { emissionPerSecond: parseEther('0.01').toString(), totalStaked: parseEther('1000').toString(), From 67595c1a708a3b01b75b8e26ee5cdea6bad0d528 Mon Sep 17 00:00:00 2001 From: sendra Date: Tue, 16 Mar 2021 19:39:48 +0100 Subject: [PATCH 11/22] WIP: tests not working --- contracts/misc/ClaimStakingRewardsHelper.sol | 11 ++++- helpers/contracts-accessors.ts | 8 +++- tasks/deployments/deploy-ClaimHelper.ts | 9 +++- test/StakedAaveV3/claimHelper.spec.ts | 46 +++++++++++++------- 4 files changed, 54 insertions(+), 20 deletions(-) diff --git a/contracts/misc/ClaimStakingRewardsHelper.sol b/contracts/misc/ClaimStakingRewardsHelper.sol index c16b6a5..143c1f9 100644 --- a/contracts/misc/ClaimStakingRewardsHelper.sol +++ b/contracts/misc/ClaimStakingRewardsHelper.sol @@ -3,14 +3,21 @@ pragma solidity 0.7.5; import {IClaimStakingRewardsHelper} from '../interfaces/IClaimStakingRewardsHelper.sol'; import {IStakedTokenV3} from '../interfaces/IStakedTokenV3.sol'; +import {IERC20} from '../interfaces/IERC20.sol'; contract ClaimStakingRewardsHelper is IClaimStakingRewardsHelper { address public immutable aaveStakeToken; address public immutable bptStakeToken; + address public immutable aaveToken; - constructor(address _aaveStakeToken, address _bptStakeToken) { + constructor( + address _aaveStakeToken, + address _bptStakeToken, + address _aaveToken + ) { aaveStakeToken = _aaveStakeToken; bptStakeToken = _bptStakeToken; + aaveToken = _aaveToken; } function claimAllRewards(address to, uint256 amount) external override { @@ -23,6 +30,8 @@ contract ClaimStakingRewardsHelper is IClaimStakingRewardsHelper { uint256 rewardsClaimed = IStakedTokenV3(bptStakeToken).claimRewardsOnBehalf(msg.sender, address(this), amount); + + IERC20(aaveToken).approve(address(this), rewardsClaimed); IStakedTokenV3(aaveStakeToken).stake(to, rewardsClaimed); } } diff --git a/helpers/contracts-accessors.ts b/helpers/contracts-accessors.ts index 2643fb7..1a87c65 100644 --- a/helpers/contracts-accessors.ts +++ b/helpers/contracts-accessors.ts @@ -25,11 +25,15 @@ import { Signer } from 'ethers'; import { ClaimStakingRewardsHelper } from '../types'; export const deployClaimHelper = async ( - [aaveStakeTokenAddress, bptStakeTokenAddress]: [tEthereumAddress, tEthereumAddress], + [aaveStakeTokenAddress, bptStakeTokenAddress, aaveToken]: [ + tEthereumAddress, + tEthereumAddress, + tEthereumAddress + ], verify?: boolean ) => { const id = eContractid.ClaimStakingRewardsHelper; - const args: string[] = [aaveStakeTokenAddress, bptStakeTokenAddress]; + const args: string[] = [aaveStakeTokenAddress, bptStakeTokenAddress, aaveToken]; const instance = await deployContract(id, args); if (verify) { await verifyContract(instance.address, args); diff --git a/tasks/deployments/deploy-ClaimHelper.ts b/tasks/deployments/deploy-ClaimHelper.ts index bb44ebf..313c649 100644 --- a/tasks/deployments/deploy-ClaimHelper.ts +++ b/tasks/deployments/deploy-ClaimHelper.ts @@ -12,15 +12,21 @@ task(`deploy-ClaimHelper`, `Deploys the ClaimStakingRewardsHelper contract`) } const stakeTokens: { - [network: string]: { aaveStakeTokenAddress: string; bptStakeTokenAddress: string }; + [network: string]: { + aaveStakeTokenAddress: string; + bptStakeTokenAddress: string; + aaveToken: string; + }; } = { [eEthereumNetwork.kovan]: { aaveStakeTokenAddress: '0xf2fbf9A6710AfDa1c4AaB2E922DE9D69E0C97fd2', bptStakeTokenAddress: '0x31ce45Ab6E26C72c47C52c27498D460099545ef2', + aaveToken: '0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9', }, [eEthereumNetwork.main]: { aaveStakeTokenAddress: '0x4da27a545c0c5b758a6ba100e3a049001de870f5', bptStakeTokenAddress: '0xa1116930326D21fB917d5A27F1E9943A9595fb47', + aaveToken: '0xb597cd8d3217ea6477232f9217fa70837ff667af', }, }; @@ -29,6 +35,7 @@ task(`deploy-ClaimHelper`, `Deploys the ClaimStakingRewardsHelper contract`) [ stakeTokens[localBRE.network.name].aaveStakeTokenAddress, stakeTokens[localBRE.network.name].bptStakeTokenAddress, + stakeTokens[localBRE.network.name].aaveToken, ], verify ); diff --git a/test/StakedAaveV3/claimHelper.spec.ts b/test/StakedAaveV3/claimHelper.spec.ts index f51986a..a7cad15 100644 --- a/test/StakedAaveV3/claimHelper.spec.ts +++ b/test/StakedAaveV3/claimHelper.spec.ts @@ -48,7 +48,6 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { let stakeAave2V3: StakedAaveV3; let claimHelper: ClaimStakingRewardsHelper; let snap: string; - it('Deploys 2 stake tokens with claimHelper address', async () => { const { aaveToken, users } = testEnv; @@ -81,7 +80,10 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { await aaveToken.connect(rewardsVault).approve(stakeAave2V3.address, MAX_UINT_AMOUNT); // deploy claim helper contract - claimHelper = await deployClaimHelper([stakeAaveV3.address, stakeAave2V3.address], false); + claimHelper = await deployClaimHelper( + [stakeAaveV3.address, stakeAave2V3.address, aaveToken.address], + false + ); //initialize the stake instance @@ -109,7 +111,7 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { { emissionPerSecond: parseEther('0.01').toString(), totalStaked: parseEther('1000').toString(), - underlyingAsset: aaveToken.address, + underlyingAsset: stakeAaveV3.address, }, ]) ); @@ -119,11 +121,10 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { { emissionPerSecond: parseEther('0.01').toString(), totalStaked: parseEther('1000').toString(), - underlyingAsset: aaveToken.address, + underlyingAsset: stakeAave2V3.address, }, ]) ); - const slashingAdmin = await stakeAaveV3.getAdmin(SLASHING_ADMIN); //slash admin const cooldownAdmin = await stakeAaveV3.getAdmin(COOLDOWN_ADMIN); //cooldown admin const claimAdmin = await stakeAave2V3.getAdmin(CLAIM_HELPER_ROLE); //claim admin // helper contract @@ -141,35 +142,48 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { it('Claims all rewards from both stakes', async () => { const { aaveToken, - users: [, staker], + users: [, , , , staker], } = testEnv; const amount = ethers.utils.parseEther('10'); + const balance = await aaveToken.balanceOf(staker.address); + console.log('balance:: ', balance.toString()); // Prepare actions for the test case - aaveToken.connect(staker.signer).approve(stakeAaveV3.address, amount); - aaveToken.connect(staker.signer).approve(stakeAave2V3.address, amount); + await aaveToken.connect(staker.signer).approve(stakeAaveV3.address, amount); + await aaveToken.connect(staker.signer).approve(stakeAave2V3.address, amount); - stakeAaveV3.connect(staker.signer).stake(staker.address, amount); - stakeAave2V3.connect(staker.signer).stake(staker.address, amount); + await stakeAaveV3.connect(staker.signer).stake(staker.address, amount); + await stakeAave2V3.connect(staker.signer).stake(staker.address, amount); + + const stakeBalance = await stakeAaveV3.balanceOf(staker.address); + const stakeBalance2 = await stakeAave2V3.balanceOf(staker.address); + console.log('stake balance: ', stakeBalance.toString()); + console.log('stake balance2: ', stakeBalance2.toString()); // Increase time for bigger rewards - await increaseTimeAndMine(1000); + await increaseTimeAndMine(100000); // user1 claims all + const rewards = await stakeAaveV3.stakerRewardsToClaim(staker.address); const rewards2 = await stakeAave2V3.stakerRewardsToClaim(staker.address); + console.log('rewards:: ', rewards.toString()); + console.log('rewards2: ', rewards2.toString()); const saveUserBalance = await aaveToken.balanceOf(staker.address); + console.log('balance before: ', saveUserBalance.toString()); await claimHelper .connect(staker.signer) .claimAllRewards(staker.address, ethers.constants.MaxUint256.toString()); const userBalanceAfterActions = await aaveToken.balanceOf(staker.address); + console.log('userBalanceAfter: ', userBalanceAfterActions.toString()); + console.log('balance total: ', saveUserBalance.add(rewards.add(rewards2)).toString()); expect(userBalanceAfterActions.eq(saveUserBalance.add(rewards.add(rewards2)))).to.be.ok; }); - it('Claims all rewards from both stakes and stakes claimed amount', async () => { + xit('Claims all rewards from both stakes and stakes claimed amount', async () => { const { aaveToken, users: [, staker], @@ -177,11 +191,11 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { const amount = ethers.utils.parseEther('10'); // Prepare actions for the test case - aaveToken.connect(staker.signer).approve(stakeAaveV3.address, amount); - aaveToken.connect(staker.signer).approve(stakeAave2V3.address, amount); + await aaveToken.connect(staker.signer).approve(stakeAaveV3.address, amount); + await aaveToken.connect(staker.signer).approve(stakeAave2V3.address, amount); - stakeAaveV3.connect(staker.signer).stake(staker.address, amount); - stakeAave2V3.connect(staker.signer).stake(staker.address, amount); + await stakeAaveV3.connect(staker.signer).stake(staker.address, amount); + await stakeAave2V3.connect(staker.signer).stake(staker.address, amount); // Increase time for bigger rewards await increaseTimeAndMine(1000); From 623c30dd961f3921c18669d356d6b19d22d0273b Mon Sep 17 00:00:00 2001 From: sendra Date: Wed, 17 Mar 2021 11:17:30 +0100 Subject: [PATCH 12/22] claim all test working --- .../data-helpers/asset-user-data.ts | 4 +- test/StakedAaveV3/claimHelper.spec.ts | 100 ++++++++++++------ 2 files changed, 69 insertions(+), 35 deletions(-) diff --git a/test/DistributionManager/data-helpers/asset-user-data.ts b/test/DistributionManager/data-helpers/asset-user-data.ts index ee8f26e..7209188 100644 --- a/test/DistributionManager/data-helpers/asset-user-data.ts +++ b/test/DistributionManager/data-helpers/asset-user-data.ts @@ -3,6 +3,7 @@ import { AaveDistributionManager } from '../../../types/AaveDistributionManager' import { StakedAave } from '../../../types/StakedAave'; import { AaveIncentivesController } from '../../../types/AaveIncentivesController'; import { StakedAaveV2 } from '../../../types/StakedAaveV2'; +import { StakedAaveV3 } from '../../../types/StakedAaveV3'; export type UserStakeInput = { underlyingAsset: string; @@ -18,7 +19,8 @@ export async function getUserIndex( | AaveDistributionManager | AaveIncentivesController | StakedAave - | StakedAaveV2, + | StakedAaveV2 + | StakedAaveV3, user: string, asset: string ): Promise { diff --git a/test/StakedAaveV3/claimHelper.spec.ts b/test/StakedAaveV3/claimHelper.spec.ts index a7cad15..0606414 100644 --- a/test/StakedAaveV3/claimHelper.spec.ts +++ b/test/StakedAaveV3/claimHelper.spec.ts @@ -109,8 +109,8 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { await waitForTx( await stakeAaveV3.connect(deployer).configureAssets([ { - emissionPerSecond: parseEther('0.01').toString(), - totalStaked: parseEther('1000').toString(), + emissionPerSecond: parseEther('0.001').toString(), + totalStaked: parseEther('0').toString(), underlyingAsset: stakeAaveV3.address, }, ]) @@ -119,8 +119,8 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { await waitForTx( await stakeAave2V3.connect(deployer).configureAssets([ { - emissionPerSecond: parseEther('0.01').toString(), - totalStaked: parseEther('1000').toString(), + emissionPerSecond: parseEther('0.001').toString(), + totalStaked: parseEther('0').toString(), underlyingAsset: stakeAave2V3.address, }, ]) @@ -155,6 +155,8 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { await stakeAaveV3.connect(staker.signer).stake(staker.address, amount); await stakeAave2V3.connect(staker.signer).stake(staker.address, amount); + const userAaveBalance = await aaveToken.balanceOf(staker.address); + const stakeBalance = await stakeAaveV3.balanceOf(staker.address); const stakeBalance2 = await stakeAave2V3.balanceOf(staker.address); console.log('stake balance: ', stakeBalance.toString()); @@ -165,25 +167,42 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { // user1 claims all - const rewards = await stakeAaveV3.stakerRewardsToClaim(staker.address); - const rewards2 = await stakeAave2V3.stakerRewardsToClaim(staker.address); - console.log('rewards:: ', rewards.toString()); - console.log('rewards2: ', rewards2.toString()); - const saveUserBalance = await aaveToken.balanceOf(staker.address); console.log('balance before: ', saveUserBalance.toString()); + const userIndexBefore = await getUserIndex(stakeAaveV3, staker.address, aaveToken.address); + const userIndexBefore2 = await getUserIndex(stakeAave2V3, staker.address, aaveToken.address); + await claimHelper .connect(staker.signer) .claimAllRewards(staker.address, ethers.constants.MaxUint256.toString()); + const userIndexAfter = await getUserIndex(stakeAaveV3, staker.address, stakeAaveV3.address); + const userIndexAfter2 = await getUserIndex(stakeAave2V3, staker.address, stakeAave2V3.address); + + const expectedAccruedRewards = getRewards( + stakeBalance, + userIndexAfter, + userIndexBefore + ).toString(); + console.log('expected accrued rewards: ', expectedAccruedRewards.toString()); + + const expectedAccruedRewards2 = getRewards( + stakeBalance2, + userIndexAfter2, + userIndexBefore2 + ).toString(); + console.log('expected accrued rewards2: ', expectedAccruedRewards2.toString()); + const userBalanceAfterActions = await aaveToken.balanceOf(staker.address); console.log('userBalanceAfter: ', userBalanceAfterActions.toString()); - console.log('balance total: ', saveUserBalance.add(rewards.add(rewards2)).toString()); - expect(userBalanceAfterActions.eq(saveUserBalance.add(rewards.add(rewards2)))).to.be.ok; + // console.log('balance total: ', saveUserBalance.add(rewards.add(rewards2)).toString()); + expect(userBalanceAfterActions).to.be.equal( + userAaveBalance.add(expectedAccruedRewards).add(expectedAccruedRewards2).toString() + ); }); - xit('Claims all rewards from both stakes and stakes claimed amount', async () => { + it('Claims all rewards from both stakes and stakes claimed amount', async () => { const { aaveToken, users: [, staker], @@ -200,38 +219,51 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { // Increase time for bigger rewards await increaseTimeAndMine(1000); + const stakeBalance = await stakeAaveV3.balanceOf(staker.address); + const stakeBalance2 = await stakeAave2V3.balanceOf(staker.address); + console.log('stake balance: ', stakeBalance.toString()); + console.log('stake balance2: ', stakeBalance2.toString()); + // save state const saveUserBalance = await aaveToken.balanceOf(staker.address); - const saveBalanceBefore = new BigNumber( - (await stakeAaveV3.balanceOf(staker.address)).toString() - ); - const saveBalanceBefore2 = new BigNumber( - (await stakeAave2V3.balanceOf(staker.address)).toString() - ); - const rewards = new BigNumber( - (await stakeAaveV3.stakerRewardsToClaim(staker.address)).toString() - ); - const rewards2 = new BigNumber( - (await stakeAave2V3.stakerRewardsToClaim(staker.address)).toString() - ); - console.log(`rewards: ${rewards.toString()} | rewards2: ${rewards2.toString()}`); + + const saveBalanceBefore = await stakeAaveV3.balanceOf(staker.address); + const saveBalanceBefore2 = await stakeAave2V3.balanceOf(staker.address); + + const userIndexBefore = await getUserIndex(stakeAaveV3, staker.address, aaveToken.address); + const userIndexBefore2 = await getUserIndex(stakeAave2V3, staker.address, aaveToken.address); // claim and stake await claimHelper .connect(staker.signer) .claimAllRewardsAndStake(staker.address, ethers.constants.MaxUint256.toString()); + const userIndexAfter = await getUserIndex(stakeAaveV3, staker.address, stakeAaveV3.address); + const userIndexAfter2 = await getUserIndex(stakeAave2V3, staker.address, stakeAave2V3.address); + + const expectedAccruedRewards = getRewards( + stakeBalance, + userIndexAfter, + userIndexBefore + ).toString(); + console.log('expected accrued rewards: ', expectedAccruedRewards.toString()); + + const expectedAccruedRewards2 = getRewards( + stakeBalance2, + userIndexAfter2, + userIndexBefore2 + ).toString(); + console.log('expected accrued rewards2: ', expectedAccruedRewards2.toString()); + // current state const userBalanceAfterActions = await aaveToken.balanceOf(staker.address); - const stakeBalanceAfter = new BigNumber( - (await stakeAaveV3.balanceOf(staker.address)).toString() - ); - const stakeBalanceAfter2 = new BigNumber( - (await stakeAave2V3.balanceOf(staker.address)).toString() - ); - expect(userBalanceAfterActions.eq(saveUserBalance)).to.be.ok; - expect(stakeBalanceAfter2.eq(saveBalanceBefore2)).to.be.ok; - expect(stakeBalanceAfter.eq(saveBalanceBefore.plus(rewards2).plus(rewards))).to.be.ok; + const stakeBalanceAfter = await stakeAaveV3.balanceOf(staker.address); + const stakeBalanceAfter2 = await stakeAave2V3.balanceOf(staker.address); + + expect(userBalanceAfterActions).to.be.equal(saveUserBalance); + expect(stakeBalanceAfter).to.be.equal( + saveBalanceBefore.add(expectedAccruedRewards).add(expectedAccruedRewards2) + ); }); }); From c58efcbd06eca8722b6895f69176cc6ef75e34c2 Mon Sep 17 00:00:00 2001 From: sendra Date: Wed, 17 Mar 2021 13:04:40 +0100 Subject: [PATCH 13/22] fixed contract, and tests. Test not exactly right, as pass of time modifies amounts --- contracts/misc/ClaimStakingRewardsHelper.sol | 8 ++---- test/StakedAaveV3/claimHelper.spec.ts | 29 ++++++-------------- 2 files changed, 12 insertions(+), 25 deletions(-) diff --git a/contracts/misc/ClaimStakingRewardsHelper.sol b/contracts/misc/ClaimStakingRewardsHelper.sol index 143c1f9..06c5535 100644 --- a/contracts/misc/ClaimStakingRewardsHelper.sol +++ b/contracts/misc/ClaimStakingRewardsHelper.sol @@ -8,16 +8,16 @@ import {IERC20} from '../interfaces/IERC20.sol'; contract ClaimStakingRewardsHelper is IClaimStakingRewardsHelper { address public immutable aaveStakeToken; address public immutable bptStakeToken; - address public immutable aaveToken; constructor( address _aaveStakeToken, address _bptStakeToken, - address _aaveToken + address aaveToken ) { aaveStakeToken = _aaveStakeToken; bptStakeToken = _bptStakeToken; - aaveToken = _aaveToken; + + IERC20(aaveToken).approve(_aaveStakeToken, type(uint256).max); } function claimAllRewards(address to, uint256 amount) external override { @@ -30,8 +30,6 @@ contract ClaimStakingRewardsHelper is IClaimStakingRewardsHelper { uint256 rewardsClaimed = IStakedTokenV3(bptStakeToken).claimRewardsOnBehalf(msg.sender, address(this), amount); - - IERC20(aaveToken).approve(address(this), rewardsClaimed); IStakedTokenV3(aaveStakeToken).stake(to, rewardsClaimed); } } diff --git a/test/StakedAaveV3/claimHelper.spec.ts b/test/StakedAaveV3/claimHelper.spec.ts index 0606414..bb84e13 100644 --- a/test/StakedAaveV3/claimHelper.spec.ts +++ b/test/StakedAaveV3/claimHelper.spec.ts @@ -48,7 +48,8 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { let stakeAave2V3: StakedAaveV3; let claimHelper: ClaimStakingRewardsHelper; let snap: string; - it('Deploys 2 stake tokens with claimHelper address', async () => { + // it('Deploys 2 stake tokens with claimHelper address', async () => { + beforeEach(async () => { const { aaveToken, users } = testEnv; const [deployer, rewardsVault] = await getEthersSigners(); @@ -146,8 +147,6 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { } = testEnv; const amount = ethers.utils.parseEther('10'); - const balance = await aaveToken.balanceOf(staker.address); - console.log('balance:: ', balance.toString()); // Prepare actions for the test case await aaveToken.connect(staker.signer).approve(stakeAaveV3.address, amount); await aaveToken.connect(staker.signer).approve(stakeAave2V3.address, amount); @@ -159,17 +158,11 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { const stakeBalance = await stakeAaveV3.balanceOf(staker.address); const stakeBalance2 = await stakeAave2V3.balanceOf(staker.address); - console.log('stake balance: ', stakeBalance.toString()); - console.log('stake balance2: ', stakeBalance2.toString()); // Increase time for bigger rewards - await increaseTimeAndMine(100000); + await increaseTimeAndMine(1000); // user1 claims all - - const saveUserBalance = await aaveToken.balanceOf(staker.address); - console.log('balance before: ', saveUserBalance.toString()); - const userIndexBefore = await getUserIndex(stakeAaveV3, staker.address, aaveToken.address); const userIndexBefore2 = await getUserIndex(stakeAave2V3, staker.address, aaveToken.address); @@ -185,7 +178,6 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { userIndexAfter, userIndexBefore ).toString(); - console.log('expected accrued rewards: ', expectedAccruedRewards.toString()); const expectedAccruedRewards2 = getRewards( stakeBalance2, @@ -195,7 +187,6 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { console.log('expected accrued rewards2: ', expectedAccruedRewards2.toString()); const userBalanceAfterActions = await aaveToken.balanceOf(staker.address); - console.log('userBalanceAfter: ', userBalanceAfterActions.toString()); // console.log('balance total: ', saveUserBalance.add(rewards.add(rewards2)).toString()); expect(userBalanceAfterActions).to.be.equal( @@ -219,16 +210,14 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { // Increase time for bigger rewards await increaseTimeAndMine(1000); + const saveUserBalance = await aaveToken.balanceOf(staker.address); + const stakeBalance = await stakeAaveV3.balanceOf(staker.address); const stakeBalance2 = await stakeAave2V3.balanceOf(staker.address); console.log('stake balance: ', stakeBalance.toString()); console.log('stake balance2: ', stakeBalance2.toString()); - // save state - const saveUserBalance = await aaveToken.balanceOf(staker.address); - - const saveBalanceBefore = await stakeAaveV3.balanceOf(staker.address); - const saveBalanceBefore2 = await stakeAave2V3.balanceOf(staker.address); + console.log('stake balance Before: ', stakeBalance.toString()); const userIndexBefore = await getUserIndex(stakeAaveV3, staker.address, aaveToken.address); const userIndexBefore2 = await getUserIndex(stakeAave2V3, staker.address, aaveToken.address); @@ -259,11 +248,11 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { const userBalanceAfterActions = await aaveToken.balanceOf(staker.address); const stakeBalanceAfter = await stakeAaveV3.balanceOf(staker.address); - const stakeBalanceAfter2 = await stakeAave2V3.balanceOf(staker.address); + console.log('stakeBalance after: ', stakeBalanceAfter.toString()); - expect(userBalanceAfterActions).to.be.equal(saveUserBalance); + // expect(userBalanceAfterActions).to.be.equal(saveUserBalance); expect(stakeBalanceAfter).to.be.equal( - saveBalanceBefore.add(expectedAccruedRewards).add(expectedAccruedRewards2) + stakeBalance.add(expectedAccruedRewards).add(expectedAccruedRewards2) ); }); }); From 392a465f9018d60b25fcdd866ca0b7d578305006 Mon Sep 17 00:00:00 2001 From: sendra Date: Wed, 17 Mar 2021 13:26:26 +0100 Subject: [PATCH 14/22] cleaned code --- test/StakedAaveV3/claimHelper.spec.ts | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/test/StakedAaveV3/claimHelper.spec.ts b/test/StakedAaveV3/claimHelper.spec.ts index bb84e13..c7c4f36 100644 --- a/test/StakedAaveV3/claimHelper.spec.ts +++ b/test/StakedAaveV3/claimHelper.spec.ts @@ -214,10 +214,6 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { const stakeBalance = await stakeAaveV3.balanceOf(staker.address); const stakeBalance2 = await stakeAave2V3.balanceOf(staker.address); - console.log('stake balance: ', stakeBalance.toString()); - console.log('stake balance2: ', stakeBalance2.toString()); - - console.log('stake balance Before: ', stakeBalance.toString()); const userIndexBefore = await getUserIndex(stakeAaveV3, staker.address, aaveToken.address); const userIndexBefore2 = await getUserIndex(stakeAave2V3, staker.address, aaveToken.address); @@ -235,23 +231,21 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { userIndexAfter, userIndexBefore ).toString(); - console.log('expected accrued rewards: ', expectedAccruedRewards.toString()); const expectedAccruedRewards2 = getRewards( stakeBalance2, userIndexAfter2, userIndexBefore2 ).toString(); - console.log('expected accrued rewards2: ', expectedAccruedRewards2.toString()); // current state const userBalanceAfterActions = await aaveToken.balanceOf(staker.address); const stakeBalanceAfter = await stakeAaveV3.balanceOf(staker.address); - console.log('stakeBalance after: ', stakeBalanceAfter.toString()); - // expect(userBalanceAfterActions).to.be.equal(saveUserBalance); - expect(stakeBalanceAfter).to.be.equal( + expect(userBalanceAfterActions).to.be.equal(saveUserBalance); + expect(stakeBalanceAfter).to.be.gt(stakeBalance.add(expectedAccruedRewards)); + expect(stakeBalanceAfter).to.be.lte( stakeBalance.add(expectedAccruedRewards).add(expectedAccruedRewards2) ); }); From 5f0a3293feb519fd2c97ebf43dca2a7cc999b613 Mon Sep 17 00:00:00 2001 From: sendra Date: Wed, 17 Mar 2021 14:17:48 +0100 Subject: [PATCH 15/22] removed amount. added docs --- .../interfaces/IClaimStakingRewardsHelper.sol | 4 +-- contracts/misc/ClaimStakingRewardsHelper.sol | 30 +++++++++++++++---- test/StakedAaveV3/claimHelper.spec.ts | 8 ++--- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/contracts/interfaces/IClaimStakingRewardsHelper.sol b/contracts/interfaces/IClaimStakingRewardsHelper.sol index e013cc1..8f24506 100644 --- a/contracts/interfaces/IClaimStakingRewardsHelper.sol +++ b/contracts/interfaces/IClaimStakingRewardsHelper.sol @@ -1,7 +1,7 @@ pragma solidity ^0.7.5; interface IClaimStakingRewardsHelper { - function claimAllRewards(address to, uint256 amount) external; + function claimAllRewards(address to) external; - function claimAllRewardsAndStake(address to, uint256 amount) external; + function claimAllRewardsAndStake(address to) external; } diff --git a/contracts/misc/ClaimStakingRewardsHelper.sol b/contracts/misc/ClaimStakingRewardsHelper.sol index 06c5535..c0695f0 100644 --- a/contracts/misc/ClaimStakingRewardsHelper.sol +++ b/contracts/misc/ClaimStakingRewardsHelper.sol @@ -5,6 +5,12 @@ import {IClaimStakingRewardsHelper} from '../interfaces/IClaimStakingRewardsHelp import {IStakedTokenV3} from '../interfaces/IStakedTokenV3.sol'; import {IERC20} from '../interfaces/IERC20.sol'; +/** + * @title ClaimStakingRewardsHelper + * @notice Contract to claim all rewards on the different stake pools + * or claim all and stake Aave token + * @author Aave + **/ contract ClaimStakingRewardsHelper is IClaimStakingRewardsHelper { address public immutable aaveStakeToken; address public immutable bptStakeToken; @@ -20,16 +26,28 @@ contract ClaimStakingRewardsHelper is IClaimStakingRewardsHelper { IERC20(aaveToken).approve(_aaveStakeToken, type(uint256).max); } - function claimAllRewards(address to, uint256 amount) external override { - IStakedTokenV3(aaveStakeToken).claimRewardsOnBehalf(msg.sender, to, amount); - IStakedTokenV3(bptStakeToken).claimRewardsOnBehalf(msg.sender, to, amount); + /** + * @dev Claims all reward for an user, on all the different staked assets. + * @param to Address that will be receiving the rewards + **/ + function claimAllRewards(address to) external override { + IStakedTokenV3(aaveStakeToken).claimRewardsOnBehalf(msg.sender, to, type(uint256).max); + IStakedTokenV3(bptStakeToken).claimRewardsOnBehalf(msg.sender, to, type(uint256).max); } - function claimAllRewardsAndStake(address to, uint256 amount) external override { - IStakedTokenV3(aaveStakeToken).claimRewardsAndStakeOnBehalf(msg.sender, to, amount); + /** + * @dev Claims all reward for an user, on all the different staked assets, and stakes this amount on the aave stake pool. + * @param to Address that will be receiving the stk Token representing the staked amount + **/ + function claimAllRewardsAndStake(address to) external override { + IStakedTokenV3(aaveStakeToken).claimRewardsAndStakeOnBehalf(msg.sender, to, type(uint256).max); uint256 rewardsClaimed = - IStakedTokenV3(bptStakeToken).claimRewardsOnBehalf(msg.sender, address(this), amount); + IStakedTokenV3(bptStakeToken).claimRewardsOnBehalf( + msg.sender, + address(this), + type(uint256).max + ); IStakedTokenV3(aaveStakeToken).stake(to, rewardsClaimed); } } diff --git a/test/StakedAaveV3/claimHelper.spec.ts b/test/StakedAaveV3/claimHelper.spec.ts index c7c4f36..911e74d 100644 --- a/test/StakedAaveV3/claimHelper.spec.ts +++ b/test/StakedAaveV3/claimHelper.spec.ts @@ -166,9 +166,7 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { const userIndexBefore = await getUserIndex(stakeAaveV3, staker.address, aaveToken.address); const userIndexBefore2 = await getUserIndex(stakeAave2V3, staker.address, aaveToken.address); - await claimHelper - .connect(staker.signer) - .claimAllRewards(staker.address, ethers.constants.MaxUint256.toString()); + await claimHelper.connect(staker.signer).claimAllRewards(staker.address); const userIndexAfter = await getUserIndex(stakeAaveV3, staker.address, stakeAaveV3.address); const userIndexAfter2 = await getUserIndex(stakeAave2V3, staker.address, stakeAave2V3.address); @@ -219,9 +217,7 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { const userIndexBefore2 = await getUserIndex(stakeAave2V3, staker.address, aaveToken.address); // claim and stake - await claimHelper - .connect(staker.signer) - .claimAllRewardsAndStake(staker.address, ethers.constants.MaxUint256.toString()); + await claimHelper.connect(staker.signer).claimAllRewardsAndStake(staker.address); const userIndexAfter = await getUserIndex(stakeAaveV3, staker.address, stakeAaveV3.address); const userIndexAfter2 = await getUserIndex(stakeAave2V3, staker.address, stakeAave2V3.address); From fef5978daf04f70860407031ed63eed14902c229 Mon Sep 17 00:00:00 2001 From: sendra Date: Wed, 17 Mar 2021 14:59:10 +0100 Subject: [PATCH 16/22] added a few logs to debug --- test/StakedAaveV3/claimHelper.spec.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test/StakedAaveV3/claimHelper.spec.ts b/test/StakedAaveV3/claimHelper.spec.ts index 911e74d..9f9e682 100644 --- a/test/StakedAaveV3/claimHelper.spec.ts +++ b/test/StakedAaveV3/claimHelper.spec.ts @@ -182,7 +182,6 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { userIndexAfter2, userIndexBefore2 ).toString(); - console.log('expected accrued rewards2: ', expectedAccruedRewards2.toString()); const userBalanceAfterActions = await aaveToken.balanceOf(staker.address); @@ -227,18 +226,24 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { userIndexAfter, userIndexBefore ).toString(); + console.log('expected accrued rewards: ', expectedAccruedRewards.toString()); const expectedAccruedRewards2 = getRewards( stakeBalance2, userIndexAfter2, userIndexBefore2 ).toString(); + console.log('expected accrued rewards2: ', expectedAccruedRewards2.toString()); // current state const userBalanceAfterActions = await aaveToken.balanceOf(staker.address); const stakeBalanceAfter = await stakeAaveV3.balanceOf(staker.address); - + console.log('stake after: ', stakeBalanceAfter.toString()); + console.log( + 'stake expected: ', + stakeBalance.add(expectedAccruedRewards).add(expectedAccruedRewards2) + ); expect(userBalanceAfterActions).to.be.equal(saveUserBalance); expect(stakeBalanceAfter).to.be.gt(stakeBalance.add(expectedAccruedRewards)); expect(stakeBalanceAfter).to.be.lte( From 340678cff96ce99c7a90b3b1e0d564ffc165869f Mon Sep 17 00:00:00 2001 From: sendra Date: Wed, 17 Mar 2021 15:15:05 +0100 Subject: [PATCH 17/22] added return of added rewards for claim all --- contracts/interfaces/IClaimStakingRewardsHelper.sol | 2 +- contracts/misc/ClaimStakingRewardsHelper.sol | 11 ++++++++--- test/StakedAaveV3/claimHelper.spec.ts | 6 +++--- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/contracts/interfaces/IClaimStakingRewardsHelper.sol b/contracts/interfaces/IClaimStakingRewardsHelper.sol index 8f24506..2936d83 100644 --- a/contracts/interfaces/IClaimStakingRewardsHelper.sol +++ b/contracts/interfaces/IClaimStakingRewardsHelper.sol @@ -1,7 +1,7 @@ pragma solidity ^0.7.5; interface IClaimStakingRewardsHelper { - function claimAllRewards(address to) external; + function claimAllRewards(address to) external returns (uint256); function claimAllRewardsAndStake(address to) external; } diff --git a/contracts/misc/ClaimStakingRewardsHelper.sol b/contracts/misc/ClaimStakingRewardsHelper.sol index c0695f0..76e05a3 100644 --- a/contracts/misc/ClaimStakingRewardsHelper.sol +++ b/contracts/misc/ClaimStakingRewardsHelper.sol @@ -4,6 +4,7 @@ pragma solidity 0.7.5; import {IClaimStakingRewardsHelper} from '../interfaces/IClaimStakingRewardsHelper.sol'; import {IStakedTokenV3} from '../interfaces/IStakedTokenV3.sol'; import {IERC20} from '../interfaces/IERC20.sol'; +import {SafeMath} from '../lib/SafeMath.sol'; /** * @title ClaimStakingRewardsHelper @@ -12,6 +13,7 @@ import {IERC20} from '../interfaces/IERC20.sol'; * @author Aave **/ contract ClaimStakingRewardsHelper is IClaimStakingRewardsHelper { + using SafeMath for uint256; address public immutable aaveStakeToken; address public immutable bptStakeToken; @@ -30,9 +32,12 @@ contract ClaimStakingRewardsHelper is IClaimStakingRewardsHelper { * @dev Claims all reward for an user, on all the different staked assets. * @param to Address that will be receiving the rewards **/ - function claimAllRewards(address to) external override { - IStakedTokenV3(aaveStakeToken).claimRewardsOnBehalf(msg.sender, to, type(uint256).max); - IStakedTokenV3(bptStakeToken).claimRewardsOnBehalf(msg.sender, to, type(uint256).max); + function claimAllRewards(address to) external override returns (uint256) { + uint256 claimedFromAave = + IStakedTokenV3(aaveStakeToken).claimRewardsOnBehalf(msg.sender, to, type(uint256).max); + uint256 claimedFromBPT = + IStakedTokenV3(bptStakeToken).claimRewardsOnBehalf(msg.sender, to, type(uint256).max); + return claimedFromAave.add(claimedFromBPT); } /** diff --git a/test/StakedAaveV3/claimHelper.spec.ts b/test/StakedAaveV3/claimHelper.spec.ts index 9f9e682..c8b3787 100644 --- a/test/StakedAaveV3/claimHelper.spec.ts +++ b/test/StakedAaveV3/claimHelper.spec.ts @@ -166,8 +166,8 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { const userIndexBefore = await getUserIndex(stakeAaveV3, staker.address, aaveToken.address); const userIndexBefore2 = await getUserIndex(stakeAave2V3, staker.address, aaveToken.address); - await claimHelper.connect(staker.signer).claimAllRewards(staker.address); - + const claimedTotal = await claimHelper.connect(staker.signer).claimAllRewards(staker.address); + console.log('total claimed == > ', claimedTotal); const userIndexAfter = await getUserIndex(stakeAaveV3, staker.address, stakeAaveV3.address); const userIndexAfter2 = await getUserIndex(stakeAave2V3, staker.address, stakeAave2V3.address); @@ -242,7 +242,7 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { console.log('stake after: ', stakeBalanceAfter.toString()); console.log( 'stake expected: ', - stakeBalance.add(expectedAccruedRewards).add(expectedAccruedRewards2) + stakeBalance.add(expectedAccruedRewards).add(expectedAccruedRewards2).toString() ); expect(userBalanceAfterActions).to.be.equal(saveUserBalance); expect(stakeBalanceAfter).to.be.gt(stakeBalance.add(expectedAccruedRewards)); From 481afd35826d2271c3052ab9d740be71c470e29c Mon Sep 17 00:00:00 2001 From: sendra Date: Wed, 17 Mar 2021 17:16:14 +0100 Subject: [PATCH 18/22] Tests working. added claimAndStake method to helper --- .../interfaces/IClaimStakingRewardsHelper.sol | 2 + contracts/misc/ClaimStakingRewardsHelper.sol | 28 ++++++++-- test/StakedAaveV3/claimHelper.spec.ts | 55 +++++++++++++++---- 3 files changed, 68 insertions(+), 17 deletions(-) diff --git a/contracts/interfaces/IClaimStakingRewardsHelper.sol b/contracts/interfaces/IClaimStakingRewardsHelper.sol index 2936d83..4c18aca 100644 --- a/contracts/interfaces/IClaimStakingRewardsHelper.sol +++ b/contracts/interfaces/IClaimStakingRewardsHelper.sol @@ -4,4 +4,6 @@ interface IClaimStakingRewardsHelper { function claimAllRewards(address to) external returns (uint256); function claimAllRewardsAndStake(address to) external; + + function claimAndStake(address to, address stakeToken) external; } diff --git a/contracts/misc/ClaimStakingRewardsHelper.sol b/contracts/misc/ClaimStakingRewardsHelper.sol index 76e05a3..510cba1 100644 --- a/contracts/misc/ClaimStakingRewardsHelper.sol +++ b/contracts/misc/ClaimStakingRewardsHelper.sol @@ -45,14 +45,30 @@ contract ClaimStakingRewardsHelper is IClaimStakingRewardsHelper { * @param to Address that will be receiving the stk Token representing the staked amount **/ function claimAllRewardsAndStake(address to) external override { - IStakedTokenV3(aaveStakeToken).claimRewardsAndStakeOnBehalf(msg.sender, to, type(uint256).max); + _claimAndStake(to, aaveStakeToken); + _claimAndStake(to, bptStakeToken); + } + /** + * @dev Claims reward from stakedToken and stakes it into the aave stake pool + * @param to Address that will be receiving the stk Token representing the staked amount + * @param stakeToken Address of the stake token where to claim the rewards + **/ + function claimAndStake(address to, address stakeToken) external override { + require( + stakeToken == aaveStakeToken || stakeToken == bptStakeToken, + 'Staked Token address must exists' + ); + _claimAndStake(to, stakeToken); + } + + /** + * @dev Claims reward from stakedToken and stakes it into the aave stake pool + * @param to Address that will be receiving the stk Token representing the staked amount + **/ + function _claimAndStake(address to, address stakeToken) internal { uint256 rewardsClaimed = - IStakedTokenV3(bptStakeToken).claimRewardsOnBehalf( - msg.sender, - address(this), - type(uint256).max - ); + IStakedTokenV3(stakeToken).claimRewardsOnBehalf(msg.sender, address(this), type(uint256).max); IStakedTokenV3(aaveStakeToken).stake(to, rewardsClaimed); } } diff --git a/test/StakedAaveV3/claimHelper.spec.ts b/test/StakedAaveV3/claimHelper.spec.ts index c8b3787..18efff5 100644 --- a/test/StakedAaveV3/claimHelper.spec.ts +++ b/test/StakedAaveV3/claimHelper.spec.ts @@ -166,8 +166,8 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { const userIndexBefore = await getUserIndex(stakeAaveV3, staker.address, aaveToken.address); const userIndexBefore2 = await getUserIndex(stakeAave2V3, staker.address, aaveToken.address); - const claimedTotal = await claimHelper.connect(staker.signer).claimAllRewards(staker.address); - console.log('total claimed == > ', claimedTotal); + await claimHelper.connect(staker.signer).claimAllRewards(staker.address); + const userIndexAfter = await getUserIndex(stakeAaveV3, staker.address, stakeAaveV3.address); const userIndexAfter2 = await getUserIndex(stakeAave2V3, staker.address, stakeAave2V3.address); @@ -226,28 +226,61 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { userIndexAfter, userIndexBefore ).toString(); - console.log('expected accrued rewards: ', expectedAccruedRewards.toString()); const expectedAccruedRewards2 = getRewards( stakeBalance2, userIndexAfter2, userIndexBefore2 ).toString(); - console.log('expected accrued rewards2: ', expectedAccruedRewards2.toString()); // current state const userBalanceAfterActions = await aaveToken.balanceOf(staker.address); const stakeBalanceAfter = await stakeAaveV3.balanceOf(staker.address); - console.log('stake after: ', stakeBalanceAfter.toString()); - console.log( - 'stake expected: ', - stakeBalance.add(expectedAccruedRewards).add(expectedAccruedRewards2).toString() - ); + expect(userBalanceAfterActions).to.be.equal(saveUserBalance); - expect(stakeBalanceAfter).to.be.gt(stakeBalance.add(expectedAccruedRewards)); - expect(stakeBalanceAfter).to.be.lte( + expect(stakeBalanceAfter).to.be.equal( stakeBalance.add(expectedAccruedRewards).add(expectedAccruedRewards2) ); }); + it('Claims the rewards from a stake and stakes claimed amount', async () => { + const { + aaveToken, + users: [, staker], + } = testEnv; + const amount = ethers.utils.parseEther('10'); + + // Prepare actions for the test case + await aaveToken.connect(staker.signer).approve(stakeAaveV3.address, amount); + + await stakeAaveV3.connect(staker.signer).stake(staker.address, amount); + + // Increase time for bigger rewards + await increaseTimeAndMine(1000); + + const saveUserBalance = await aaveToken.balanceOf(staker.address); + + const stakeBalance = await stakeAaveV3.balanceOf(staker.address); + + const userIndexBefore = await getUserIndex(stakeAaveV3, staker.address, aaveToken.address); + + // claim and stake + await claimHelper.connect(staker.signer).claimAndStake(staker.address, stakeAaveV3.address); + + const userIndexAfter = await getUserIndex(stakeAaveV3, staker.address, stakeAaveV3.address); + + const expectedAccruedRewards = getRewards( + stakeBalance, + userIndexAfter, + userIndexBefore + ).toString(); + + // current state + const userBalanceAfterActions = await aaveToken.balanceOf(staker.address); + + const stakeBalanceAfter = await stakeAaveV3.balanceOf(staker.address); + + expect(userBalanceAfterActions).to.be.equal(saveUserBalance); + expect(stakeBalanceAfter).to.be.equal(stakeBalance.add(expectedAccruedRewards)); + }); }); From 01a19189fcc3ed30382713caabb769a1800e5eba Mon Sep 17 00:00:00 2001 From: sendra Date: Thu, 18 Mar 2021 12:40:17 +0100 Subject: [PATCH 19/22] fixed deployment --- tasks/deployments/deploy-ClaimHelper.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tasks/deployments/deploy-ClaimHelper.ts b/tasks/deployments/deploy-ClaimHelper.ts index 313c649..1fae608 100644 --- a/tasks/deployments/deploy-ClaimHelper.ts +++ b/tasks/deployments/deploy-ClaimHelper.ts @@ -21,16 +21,16 @@ task(`deploy-ClaimHelper`, `Deploys the ClaimStakingRewardsHelper contract`) [eEthereumNetwork.kovan]: { aaveStakeTokenAddress: '0xf2fbf9A6710AfDa1c4AaB2E922DE9D69E0C97fd2', bptStakeTokenAddress: '0x31ce45Ab6E26C72c47C52c27498D460099545ef2', - aaveToken: '0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9', + aaveToken: '0xb597cd8d3217ea6477232f9217fa70837ff667af', }, [eEthereumNetwork.main]: { aaveStakeTokenAddress: '0x4da27a545c0c5b758a6ba100e3a049001de870f5', bptStakeTokenAddress: '0xa1116930326D21fB917d5A27F1E9943A9595fb47', - aaveToken: '0xb597cd8d3217ea6477232f9217fa70837ff667af', + aaveToken: '0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9', }, }; - console.log(`\tDeploying ClaimHelper implementation ...`); + console.log(`\tDeploying ClaimHelper ...`); const ClaimHelper = await deployClaimHelper( [ stakeTokens[localBRE.network.name].aaveStakeTokenAddress, From a29f6a661889b2d86b6e953642a1de59c7765bb4 Mon Sep 17 00:00:00 2001 From: sendra Date: Fri, 19 Mar 2021 16:30:23 +0100 Subject: [PATCH 20/22] updated logic to not stake if rewards are 0. added tests --- contracts/misc/ClaimStakingRewardsHelper.sol | 4 +- test/StakedAaveV3/claimHelper.spec.ts | 90 ++++++++++++++++++++ 2 files changed, 93 insertions(+), 1 deletion(-) diff --git a/contracts/misc/ClaimStakingRewardsHelper.sol b/contracts/misc/ClaimStakingRewardsHelper.sol index 510cba1..0ff2d90 100644 --- a/contracts/misc/ClaimStakingRewardsHelper.sol +++ b/contracts/misc/ClaimStakingRewardsHelper.sol @@ -69,6 +69,8 @@ contract ClaimStakingRewardsHelper is IClaimStakingRewardsHelper { function _claimAndStake(address to, address stakeToken) internal { uint256 rewardsClaimed = IStakedTokenV3(stakeToken).claimRewardsOnBehalf(msg.sender, address(this), type(uint256).max); - IStakedTokenV3(aaveStakeToken).stake(to, rewardsClaimed); + if (rewardsClaimed > 0) { + IStakedTokenV3(aaveStakeToken).stake(to, rewardsClaimed); + } } } diff --git a/test/StakedAaveV3/claimHelper.spec.ts b/test/StakedAaveV3/claimHelper.spec.ts index 18efff5..7a44aad 100644 --- a/test/StakedAaveV3/claimHelper.spec.ts +++ b/test/StakedAaveV3/claimHelper.spec.ts @@ -190,6 +190,45 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { userAaveBalance.add(expectedAccruedRewards).add(expectedAccruedRewards2).toString() ); }); + it('Claims all rewards from both stakes even if we have no rewards on one of them', async () => { + const { + aaveToken, + users: [, , , , staker], + } = testEnv; + const amount = ethers.utils.parseEther('10'); + + // Prepare actions for the test case + await aaveToken.connect(staker.signer).approve(stakeAaveV3.address, amount); + + await stakeAaveV3.connect(staker.signer).stake(staker.address, amount); + + const userAaveBalance = await aaveToken.balanceOf(staker.address); + + const stakeBalance = await stakeAaveV3.balanceOf(staker.address); + const stakeBalance2 = await stakeAave2V3.balanceOf(staker.address); + expect(stakeBalance2).to.be.equal(0); + + // Increase time for bigger rewards + await increaseTimeAndMine(1000); + + // user1 claims all + const userIndexBefore = await getUserIndex(stakeAaveV3, staker.address, aaveToken.address); + + await claimHelper.connect(staker.signer).claimAllRewards(staker.address); + + const userIndexAfter = await getUserIndex(stakeAaveV3, staker.address, stakeAaveV3.address); + + const expectedAccruedRewards = getRewards( + stakeBalance, + userIndexAfter, + userIndexBefore + ).toString(); + + const userBalanceAfterActions = await aaveToken.balanceOf(staker.address); + + // console.log('balance total: ', saveUserBalance.add(rewards.add(rewards2)).toString()); + expect(userBalanceAfterActions).to.be.equal(userAaveBalance.add(expectedAccruedRewards)); + }); it('Claims all rewards from both stakes and stakes claimed amount', async () => { const { aaveToken, @@ -243,6 +282,57 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => { stakeBalance.add(expectedAccruedRewards).add(expectedAccruedRewards2) ); }); + it('Claims all rewards from both stakes and stakes claimed amount even if it has no stake in one of the stakes', async () => { + const { + aaveToken, + users: [, staker], + } = testEnv; + const amount = ethers.utils.parseEther('10'); + + // Prepare actions for the test case + await aaveToken.connect(staker.signer).approve(stakeAaveV3.address, amount); + + await stakeAaveV3.connect(staker.signer).stake(staker.address, amount); + + // Increase time for bigger rewards + await increaseTimeAndMine(1000); + + const saveUserBalance = await aaveToken.balanceOf(staker.address); + + const stakeBalance = await stakeAaveV3.balanceOf(staker.address); + const stakeBalance2 = await stakeAave2V3.balanceOf(staker.address); + + const userIndexBefore = await getUserIndex(stakeAaveV3, staker.address, aaveToken.address); + const userIndexBefore2 = await getUserIndex(stakeAave2V3, staker.address, aaveToken.address); + + // claim and stake + await claimHelper.connect(staker.signer).claimAllRewardsAndStake(staker.address); + + const userIndexAfter = await getUserIndex(stakeAaveV3, staker.address, stakeAaveV3.address); + const userIndexAfter2 = await getUserIndex(stakeAave2V3, staker.address, stakeAave2V3.address); + + const expectedAccruedRewards = getRewards( + stakeBalance, + userIndexAfter, + userIndexBefore + ).toString(); + + const expectedAccruedRewards2 = getRewards( + stakeBalance2, + userIndexAfter2, + userIndexBefore2 + ).toString(); + + // current state + const userBalanceAfterActions = await aaveToken.balanceOf(staker.address); + + const stakeBalanceAfter = await stakeAaveV3.balanceOf(staker.address); + + expect(userBalanceAfterActions).to.be.equal(saveUserBalance); + expect(stakeBalanceAfter).to.be.equal( + stakeBalance.add(expectedAccruedRewards).add(expectedAccruedRewards2) + ); + }); it('Claims the rewards from a stake and stakes claimed amount', async () => { const { aaveToken, From b54e3eb17d02a4b4bb51f1588b9d209b7aee3e43 Mon Sep 17 00:00:00 2001 From: sendra Date: Fri, 19 Mar 2021 17:04:26 +0100 Subject: [PATCH 21/22] deployed with same stake aave, until we have a bpt mock or another stk mock --- contracts/misc/ClaimStakingRewardsHelper.sol | 14 +++++++++----- tasks/deployments/deploy-ClaimHelper.ts | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/contracts/misc/ClaimStakingRewardsHelper.sol b/contracts/misc/ClaimStakingRewardsHelper.sol index 0ff2d90..428a46d 100644 --- a/contracts/misc/ClaimStakingRewardsHelper.sol +++ b/contracts/misc/ClaimStakingRewardsHelper.sol @@ -45,8 +45,8 @@ contract ClaimStakingRewardsHelper is IClaimStakingRewardsHelper { * @param to Address that will be receiving the stk Token representing the staked amount **/ function claimAllRewardsAndStake(address to) external override { - _claimAndStake(to, aaveStakeToken); - _claimAndStake(to, bptStakeToken); + _claimAndStake(msg.sender, to, aaveStakeToken); + _claimAndStake(msg.sender, to, bptStakeToken); } /** @@ -59,16 +59,20 @@ contract ClaimStakingRewardsHelper is IClaimStakingRewardsHelper { stakeToken == aaveStakeToken || stakeToken == bptStakeToken, 'Staked Token address must exists' ); - _claimAndStake(to, stakeToken); + _claimAndStake(msg.sender, to, stakeToken); } /** * @dev Claims reward from stakedToken and stakes it into the aave stake pool * @param to Address that will be receiving the stk Token representing the staked amount **/ - function _claimAndStake(address to, address stakeToken) internal { + function _claimAndStake( + address from, + address to, + address stakeToken + ) internal { uint256 rewardsClaimed = - IStakedTokenV3(stakeToken).claimRewardsOnBehalf(msg.sender, address(this), type(uint256).max); + IStakedTokenV3(stakeToken).claimRewardsOnBehalf(from, address(this), type(uint256).max); if (rewardsClaimed > 0) { IStakedTokenV3(aaveStakeToken).stake(to, rewardsClaimed); } diff --git a/tasks/deployments/deploy-ClaimHelper.ts b/tasks/deployments/deploy-ClaimHelper.ts index 1fae608..b315d8e 100644 --- a/tasks/deployments/deploy-ClaimHelper.ts +++ b/tasks/deployments/deploy-ClaimHelper.ts @@ -20,7 +20,7 @@ task(`deploy-ClaimHelper`, `Deploys the ClaimStakingRewardsHelper contract`) } = { [eEthereumNetwork.kovan]: { aaveStakeTokenAddress: '0xf2fbf9A6710AfDa1c4AaB2E922DE9D69E0C97fd2', - bptStakeTokenAddress: '0x31ce45Ab6E26C72c47C52c27498D460099545ef2', + bptStakeTokenAddress: '0xf2fbf9A6710AfDa1c4AaB2E922DE9D69E0C97fd2', aaveToken: '0xb597cd8d3217ea6477232f9217fa70837ff667af', }, [eEthereumNetwork.main]: { From d403fd564899de4c028a2d1086d9f5802cf3adc2 Mon Sep 17 00:00:00 2001 From: sendra Date: Tue, 23 Mar 2021 16:14:46 +0100 Subject: [PATCH 22/22] added stakeUIHelper contract, and updated claimHelper with new bpt for kovan --- contracts/interfaces/IERC20WithNonce.sol | 8 ++ contracts/interfaces/IPriceOracle.sol | 6 ++ contracts/interfaces/IStakeUIHelper.sol | 34 ++++++ contracts/misc/IStakedToken.sol | 35 +++++++ contracts/misc/StakeUIHelper.sol | 121 ++++++++++++++++++++++ helpers/contracts-accessors.ts | 21 ++++ helpers/types.ts | 1 + package.json | 1 + tasks/deployments/deploy-ClaimHelper.ts | 2 +- tasks/deployments/deploy-StakeUIHelper.ts | 56 ++++++++++ 10 files changed, 284 insertions(+), 1 deletion(-) create mode 100644 contracts/interfaces/IERC20WithNonce.sol create mode 100644 contracts/interfaces/IPriceOracle.sol create mode 100644 contracts/interfaces/IStakeUIHelper.sol create mode 100644 contracts/misc/IStakedToken.sol create mode 100644 contracts/misc/StakeUIHelper.sol create mode 100644 tasks/deployments/deploy-StakeUIHelper.ts diff --git a/contracts/interfaces/IERC20WithNonce.sol b/contracts/interfaces/IERC20WithNonce.sol new file mode 100644 index 0000000..b28ea81 --- /dev/null +++ b/contracts/interfaces/IERC20WithNonce.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.7.5; + +import {IERC20} from './IERC20.sol'; + +interface IERC20WithNonce is IERC20 { + function _nonces(address user) external view returns (uint256); +} diff --git a/contracts/interfaces/IPriceOracle.sol b/contracts/interfaces/IPriceOracle.sol new file mode 100644 index 0000000..e7610fc --- /dev/null +++ b/contracts/interfaces/IPriceOracle.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.7.5; + +interface IPriceOracle { + function getAssetPrice(address asset) external view returns (uint256); +} diff --git a/contracts/interfaces/IStakeUIHelper.sol b/contracts/interfaces/IStakeUIHelper.sol new file mode 100644 index 0000000..06822e5 --- /dev/null +++ b/contracts/interfaces/IStakeUIHelper.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.7.5; +pragma experimental ABIEncoderV2; + +interface IStakeUIHelper { + struct AssetUIData { + uint256 stakeTokenTotalSupply; + uint256 stakeCooldownSeconds; + uint256 stakeUnstakeWindow; + uint256 stakeTokenPriceEth; + uint256 rewardTokenPriceEth; + uint256 stakeApy; + uint128 distributionPerSecond; + uint256 distributionEnd; + uint256 stakeTokenUserBalance; + uint256 underlyingTokenUserBalance; + uint256 userCooldown; + uint256 userIncentivesToClaim; + uint256 userPermitNonce; + } + + function getStkAaveData(address user) external view returns (AssetUIData memory); + + function getStkBptData(address user) external view returns (AssetUIData memory); + + function getUserUIData(address user) + external + view + returns ( + AssetUIData memory, + AssetUIData memory, + uint256 + ); +} diff --git a/contracts/misc/IStakedToken.sol b/contracts/misc/IStakedToken.sol new file mode 100644 index 0000000..c93b470 --- /dev/null +++ b/contracts/misc/IStakedToken.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.7.5; +pragma experimental ABIEncoderV2; + +interface IStakedToken { + struct AssetData { + uint128 emissionPerSecond; + uint128 lastUpdateTimestamp; + uint256 index; + } + + function totalSupply() external view returns (uint256); + + function COOLDOWN_SECONDS() external view returns (uint256); + + function UNSTAKE_WINDOW() external view returns (uint256); + + function DISTRIBUTION_END() external view returns (uint256); + + function assets(address asset) external view returns (AssetData memory); + + function balanceOf(address user) external view returns (uint256); + + function getTotalRewardsBalance(address user) external view returns (uint256); + + function stakersCooldowns(address user) external view returns (uint256); + + function stake(address to, uint256 amount) external; + + function redeem(address to, uint256 amount) external; + + function cooldown() external; + + function claimRewards(address to, uint256 amount) external; +} diff --git a/contracts/misc/StakeUIHelper.sol b/contracts/misc/StakeUIHelper.sol new file mode 100644 index 0000000..42ee9e5 --- /dev/null +++ b/contracts/misc/StakeUIHelper.sol @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.7.5; +pragma experimental ABIEncoderV2; + +import {IStakedToken} from './IStakedToken.sol'; +import {IStakeUIHelper} from '../interfaces/IStakeUIHelper.sol'; +import {IERC20WithNonce} from '../interfaces/IERC20WithNonce.sol'; +import {IERC20} from '../interfaces/IERC20.sol'; +import {IPriceOracle} from '../interfaces/IPriceOracle.sol'; + +interface BPTPriceFeedI { + function latestAnswer() external view returns (uint256); +} + +contract StakeUIHelper is IStakeUIHelper { + IPriceOracle public immutable PRICE_ORACLE; + BPTPriceFeedI public immutable BPT_PRICE_FEED; + + address public immutable AAVE; + IStakedToken public immutable STAKED_AAVE; + + address public immutable BPT; + IStakedToken public immutable STAKED_BPT; + + uint256 constant SECONDS_PER_YEAR = 365 * 24 * 60 * 60; + uint256 constant APY_PRECISION = 10000; + address constant MOCK_USD_ADDRESS = 0x10F7Fc1F91Ba351f9C629c5947AD69bD03C05b96; + uint256 internal constant USD_BASE = 1e26; + + constructor( + IPriceOracle priceOracle, + BPTPriceFeedI bptPriceFeed, + address aave, + IStakedToken stkAave, + address bpt, + IStakedToken stkBpt + ) public { + PRICE_ORACLE = priceOracle; + BPT_PRICE_FEED = bptPriceFeed; + + AAVE = aave; + STAKED_AAVE = stkAave; + + BPT = bpt; + STAKED_BPT = stkBpt; + } + + function _getStakedAssetData( + IStakedToken stakeToken, + address underlyingToken, + address user, + bool isNonceAvailable + ) internal view returns (AssetUIData memory) { + AssetUIData memory data; + + data.stakeTokenTotalSupply = stakeToken.totalSupply(); + data.stakeCooldownSeconds = stakeToken.COOLDOWN_SECONDS(); + data.stakeUnstakeWindow = stakeToken.UNSTAKE_WINDOW(); + data.rewardTokenPriceEth = PRICE_ORACLE.getAssetPrice(AAVE); + data.distributionEnd = stakeToken.DISTRIBUTION_END(); + if (block.timestamp < data.distributionEnd) { + data.distributionPerSecond = stakeToken.assets(address(stakeToken)).emissionPerSecond; + } + + if (user != address(0)) { + data.underlyingTokenUserBalance = IERC20(underlyingToken).balanceOf(user); + data.stakeTokenUserBalance = stakeToken.balanceOf(user); + data.userIncentivesToClaim = stakeToken.getTotalRewardsBalance(user); + data.userCooldown = stakeToken.stakersCooldowns(user); + data.userPermitNonce = isNonceAvailable ? IERC20WithNonce(underlyingToken)._nonces(user) : 0; + } + return data; + } + + function _calculateApy(uint256 distributionPerSecond, uint256 stakeTokenTotalSupply) + internal + pure + returns (uint256) + { + return (distributionPerSecond * SECONDS_PER_YEAR * APY_PRECISION) / stakeTokenTotalSupply; + } + + function getStkAaveData(address user) public view override returns (AssetUIData memory) { + AssetUIData memory data = _getStakedAssetData(STAKED_AAVE, AAVE, user, true); + + data.stakeTokenPriceEth = data.rewardTokenPriceEth; + data.stakeApy = _calculateApy(data.distributionPerSecond, data.stakeTokenTotalSupply); + return data; + } + + function getStkBptData(address user) public view override returns (AssetUIData memory) { + AssetUIData memory data = _getStakedAssetData(STAKED_BPT, BPT, user, false); + + data.stakeTokenPriceEth = address(BPT_PRICE_FEED) != address(0) + ? BPT_PRICE_FEED.latestAnswer() + : PRICE_ORACLE.getAssetPrice(BPT); + data.stakeApy = _calculateApy( + data.distributionPerSecond * data.rewardTokenPriceEth, + data.stakeTokenTotalSupply * data.stakeTokenPriceEth + ); + + return data; + } + + function getUserUIData(address user) + external + view + override + returns ( + AssetUIData memory, + AssetUIData memory, + uint256 + ) + { + return ( + getStkAaveData(user), + getStkBptData(user), + USD_BASE / PRICE_ORACLE.getAssetPrice(MOCK_USD_ADDRESS) + ); + } +} diff --git a/helpers/contracts-accessors.ts b/helpers/contracts-accessors.ts index 1a87c65..698d0f1 100644 --- a/helpers/contracts-accessors.ts +++ b/helpers/contracts-accessors.ts @@ -23,6 +23,27 @@ import { zeroAddress } from 'ethereumjs-util'; import { ZERO_ADDRESS } from './constants'; import { Signer } from 'ethers'; import { ClaimStakingRewardsHelper } from '../types'; +import { StakeUiHelper } from '../types'; + +export const deployStakeUIHelper = async ( + [priceOracle, bptPriceFeed, aave, stkAave, bpt, stkBpt]: [ + tEthereumAddress, + tEthereumAddress, + tEthereumAddress, + tEthereumAddress, + tEthereumAddress, + tEthereumAddress + ], + verify?: boolean +) => { + const id = eContractid.StakeUIHelper; + const args: string[] = [priceOracle, bptPriceFeed, aave, stkAave, bpt, stkBpt]; + const instance = await deployContract(id, args); + if (verify) { + await verifyContract(instance.address, args); + } + return instance; +}; export const deployClaimHelper = async ( [aaveStakeTokenAddress, bptStakeTokenAddress, aaveToken]: [ diff --git a/helpers/types.ts b/helpers/types.ts index a41cb06..b279453 100644 --- a/helpers/types.ts +++ b/helpers/types.ts @@ -31,6 +31,7 @@ export enum eContractid { IControllerAaveEcosystemReserve = 'IControllerAaveEcosystemReserve', MockSelfDestruct = 'SelfdestructTransfer', ClaimStakingRewardsHelper = 'ClaimStakingRewardsHelper', + StakeUIHelper = 'StakeUIHelper', } export type tEthereumAddress = string; diff --git a/package.json b/package.json index e87eeb2..9c2ca94 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "main:deployment": "npm run hardhat-main -- common-deployment --verify", "kovan:deploy-ClaimHelper": "hardhat --network kovan deploy-ClaimHelper --verify", "main:deploy-ClaimHelper": "hardhat --network main deploy-OracleAnchor --verify", + "kovan:deploy-StakeUIHelper": "hardhat --network kovan deploy-StakeUIHelper --verify", "prettier:check": "npx prettier -c 'tasks/**/*.ts' 'contracts/**/*.sol' 'helpers/**/*.ts' 'test/**/*.ts'", "prettier:write": "prettier --write 'tasks/**/*.ts' 'contracts/**/*.sol' 'helpers/**/*.ts' 'test/**/*.ts'", "ci:clean": "rm -rf types/ cache/ artifacts/", diff --git a/tasks/deployments/deploy-ClaimHelper.ts b/tasks/deployments/deploy-ClaimHelper.ts index b315d8e..deac9f0 100644 --- a/tasks/deployments/deploy-ClaimHelper.ts +++ b/tasks/deployments/deploy-ClaimHelper.ts @@ -20,7 +20,7 @@ task(`deploy-ClaimHelper`, `Deploys the ClaimStakingRewardsHelper contract`) } = { [eEthereumNetwork.kovan]: { aaveStakeTokenAddress: '0xf2fbf9A6710AfDa1c4AaB2E922DE9D69E0C97fd2', - bptStakeTokenAddress: '0xf2fbf9A6710AfDa1c4AaB2E922DE9D69E0C97fd2', + bptStakeTokenAddress: '0xCe7021eDabaf82D28adBBea449Bc4dF70261F33E', // mock, need aave to stake aaveToken: '0xb597cd8d3217ea6477232f9217fa70837ff667af', }, [eEthereumNetwork.main]: { diff --git a/tasks/deployments/deploy-StakeUIHelper.ts b/tasks/deployments/deploy-StakeUIHelper.ts new file mode 100644 index 0000000..20b42e9 --- /dev/null +++ b/tasks/deployments/deploy-StakeUIHelper.ts @@ -0,0 +1,56 @@ +import { task } from 'hardhat/config'; +import { deployStakeUIHelper } from '../../helpers/contracts-accessors'; +import { eEthereumNetwork } from '../../helpers/types'; + +task(`deploy-StakeUIHelper`, `Deploys the StakeUIHelper contract`) + .addFlag('verify', 'Verify StakeUIHelper contract via Etherscan API.') + .setAction(async ({ verify }, localBRE) => { + await localBRE.run('set-dre'); + + if (!localBRE.network.config.chainId) { + throw new Error('INVALID_CHAIN_ID'); + } + + const stakeTokens: { + [network: string]: { + priceOracle: string; + bptPriceFeed: string; + aave: string; + stkAave: string; + bpt: string; + stkBpt: string; + }; + } = { + [eEthereumNetwork.kovan]: { + priceOracle: '0x276c4793f2ee3d5bf18c5b879529dd4270ba4814', + bptPriceFeed: '0x0000000000000000000000000000000000000000', + aave: '0xb597cd8d3217ea6477232f9217fa70837ff667af', + stkAave: '0xf2fbf9a6710afda1c4aab2e922de9d69e0c97fd2', + bpt: '0xb597cd8d3217ea6477232f9217fa70837ff667af', + stkBpt: '0xCe7021eDabaf82D28adBBea449Bc4dF70261F33E', + }, + [eEthereumNetwork.main]: { + priceOracle: '0x0000000000000000000000000000000000000000', + bptPriceFeed: '0x0000000000000000000000000000000000000000', + aave: '0x0000000000000000000000000000000000000000', + stkAave: '0x0000000000000000000000000000000000000000', + bpt: '0x0000000000000000000000000000000000000000', + stkBpt: '0x0000000000000000000000000000000000000000', + }, + }; + + console.log(`\tDeploying StakeUIHelper ...`); + const StakeUIHelper = await deployStakeUIHelper( + [ + stakeTokens[localBRE.network.name].priceOracle, + stakeTokens[localBRE.network.name].bptPriceFeed, + stakeTokens[localBRE.network.name].aave, + stakeTokens[localBRE.network.name].stkAave, + stakeTokens[localBRE.network.name].bpt, + stakeTokens[localBRE.network.name].stkBpt, + ], + verify + ); + + console.log(`\tFinished StakeUIHelper deployment: ${StakeUIHelper.address}`); + });