Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated claimHelper address management, added deployment script for i… #7

Open
wants to merge 3 commits into
base: feat/claim-all-rewards-helper
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions contracts/interfaces/IStakedTokenV3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,8 @@ interface IStakedTokenV3 is IStakedToken {
uint256 claimAmount,
uint256 redeemAmount
) external;

function setClaimHelper(address claimHelper) external;

function getClaimHelper() external view returns (address);
}
51 changes: 35 additions & 16 deletions contracts/stake/StakedTokenV3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,8 @@ contract StakedTokenV3 is StakedTokenV2, IStakedTokenV3, RoleManager {
using SafeERC20 for IERC20;
using PercentageMath for uint256;

uint256 public constant SLASH_ADMIN_ROLE = 0;
uint256 public constant MAIN_ADMIN_ROLE = 0;
uint256 public constant COOLDOWN_ADMIN_ROLE = 1;
uint256 public constant CLAIM_HELPER_ROLE = 2;

function REVISION() public pure virtual override returns (uint256) {
return 3;
Expand All @@ -43,8 +42,10 @@ contract StakedTokenV3 is StakedTokenV2, IStakedTokenV3, RoleManager {
uint256 internal _maxSlashablePercentage;
bool _cooldownPaused;

modifier onlySlashingAdmin {
require(msg.sender == getAdmin(SLASH_ADMIN_ROLE), 'CALLER_NOT_SLASHING_ADMIN');
address internal _claimHelper;

modifier onlyAdmin {
require(msg.sender == getAdmin(MAIN_ADMIN_ROLE), 'CALLER_NOT_MAIN_ADMIN');
_;
}

Expand All @@ -54,7 +55,7 @@ contract StakedTokenV3 is StakedTokenV2, IStakedTokenV3, RoleManager {
}

modifier onlyClaimHelper {
require(msg.sender == getAdmin(CLAIM_HELPER_ROLE), 'CALLER_NOT_CLAIM_HELPER');
require(msg.sender == _claimHelper, 'CALLER_NOT_CLAIM_HELPER');
_;
}

Expand All @@ -70,6 +71,7 @@ contract StakedTokenV3 is StakedTokenV2, IStakedTokenV3, RoleManager {
event Slashed(address indexed destination, uint256 amount);
event CooldownPauseAdminChanged(address indexed newAdmin);
event SlashingAdminChanged(address indexed newAdmin);
event ClaimHelperChanged(address indexed newClaimHelper);

constructor(
IERC20 stakedToken,
Expand Down Expand Up @@ -142,18 +144,17 @@ contract StakedTokenV3 is StakedTokenV2, IStakedTokenV3, RoleManager {
_setupDecimals(decimals);
}

address[] memory adminsAddresses = new address[](3);
uint256[] memory adminsRoles = new uint256[](3);
address[] memory adminsAddresses = new address[](2);
uint256[] memory adminsRoles = new uint256[](2);

adminsAddresses[0] = slashingAdmin;
adminsAddresses[1] = cooldownPauseAdmin;
adminsAddresses[2] = claimHelper;

adminsRoles[0] = SLASH_ADMIN_ROLE;
adminsRoles[0] = MAIN_ADMIN_ROLE;
adminsRoles[1] = COOLDOWN_ADMIN_ROLE;
adminsRoles[2] = CLAIM_HELPER_ROLE;

_initAdmins(adminsRoles, adminsAddresses);
_claimHelper = claimHelper;

_maxSlashablePercentage = maxSlashablePercentage;
}
Expand Down Expand Up @@ -321,7 +322,7 @@ contract StakedTokenV3 is StakedTokenV2, IStakedTokenV3, RoleManager {
* @param destination the address where seized funds will be transferred
* @param amount the amount
**/
function slash(address destination, uint256 amount) external override onlySlashingAdmin {
function slash(address destination, uint256 amount) external override onlyAdmin {
uint256 balance = STAKED_TOKEN.balanceOf(address(this));

uint256 maxSlashable = balance.percentMul(_maxSlashablePercentage);
Expand All @@ -334,10 +335,13 @@ contract StakedTokenV3 is StakedTokenV2, IStakedTokenV3, RoleManager {
}

/**
* @dev returns true if the unstake cooldown is paused
*/
function getCooldownPaused() external view override returns (bool) {
return _cooldownPaused;
* @dev Set the address of the contract with priviledge, the ClaimHelper contract
* It speicifically enables to claim from several contracts at once
* @param claimHelper new address of the claim helper
**/
function setClaimHelper(address claimHelper) external override onlyAdmin {
_claimHelper = claimHelper;
emit ClaimHelperChanged(claimHelper);
}

/**
Expand All @@ -353,13 +357,28 @@ contract StakedTokenV3 is StakedTokenV2, IStakedTokenV3, RoleManager {
* @dev sets the admin of the slashing pausing function
* @param percentage the new maximum slashable percentage
*/
function setMaxSlashablePercentage(uint256 percentage) external override onlySlashingAdmin {
function setMaxSlashablePercentage(uint256 percentage) external override onlyAdmin {
require(percentage <= PercentageMath.PERCENTAGE_FACTOR, 'INVALID_SLASHING_PERCENTAGE');

_maxSlashablePercentage = percentage;
emit MaxSlashablePercentageChanged(percentage);
}

/**
* @dev returns the current address of the claimHelper Contract, contract with priviledge
* It speicifically enables to claim from several contracts at once
*/
function getClaimHelper() external view override returns (address) {
return _claimHelper;
}

/**
* @dev returns true if the unstake cooldown is paused
*/
function getCooldownPaused() external view override returns (bool) {
return _cooldownPaused;
}

/**
* @dev returns the current maximum slashable percentage of the stake
*/
Expand Down
11 changes: 6 additions & 5 deletions helpers/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export const getAaveTokenPerNetwork = (network: eEthereumNetwork): tEthereumAddr
{
[eEthereumNetwork.coverage]: ZERO_ADDRESS,
[eEthereumNetwork.hardhat]: ZERO_ADDRESS,
[eEthereumNetwork.kovan]: '0xe4483afcf0d612c011679C76B61F5b0d27bAF93C',
[eEthereumNetwork.kovan]: '0xb597cd8d3217ea6477232f9217fa70837ff667af',
[eEthereumNetwork.ropsten]: '0x74dA004A1B81b4d0C79F5820f9FF22647cb1dD95',
[eEthereumNetwork.main]: '0x9c0435779F5E52CEC404D957C9bAa6f7d674C8bA',
},
Expand All @@ -63,7 +63,7 @@ export const getCooldownSecondsPerNetwork = (network: eEthereumNetwork): tEthere
{
[eEthereumNetwork.coverage]: COOLDOWN_SECONDS,
[eEthereumNetwork.hardhat]: COOLDOWN_SECONDS,
[eEthereumNetwork.kovan]: '21600', // 8h
[eEthereumNetwork.kovan]: '180', // 5m
[eEthereumNetwork.ropsten]: '180', // 3m
[eEthereumNetwork.main]: '864000', // 10d
},
Expand All @@ -87,7 +87,7 @@ export const getAaveAdminPerNetwork = (network: eEthereumNetwork): tEthereumAddr
{
[eEthereumNetwork.coverage]: ZERO_ADDRESS,
[eEthereumNetwork.hardhat]: ZERO_ADDRESS,
[eEthereumNetwork.kovan]: '0x8134929c3dcb1b8b82f27f53424b959fb82182f2', // Aave Governance
[eEthereumNetwork.kovan]: '0x2012b02574f32a96b9cfb8ba7fdfd589d5c70f50', // Short Executor
[eEthereumNetwork.ropsten]: '0xEd93e49A2d75beA505fD4D1A0Dff745f69F2E997', // Aave Governance
[eEthereumNetwork.main]: '0x8a2Efd9A790199F4c94c6effE210fce0B4724f52', // Aave Governance
},
Expand All @@ -99,7 +99,8 @@ export const getDistributionDurationPerNetwork = (network: eEthereumNetwork): tE
{
[eEthereumNetwork.coverage]: DISTRIBUTION_DURATION,
[eEthereumNetwork.hardhat]: DISTRIBUTION_DURATION,
[eEthereumNetwork.kovan]: '864000',
// tslint:disable-next-line:max-line-length
[eEthereumNetwork.kovan]: '129600000', // 50 months
[eEthereumNetwork.ropsten]: '864000',
[eEthereumNetwork.main]: '12960000', // 5 months (30 days) in seconds
},
Expand All @@ -111,7 +112,7 @@ export const getAaveIncentivesVaultPerNetwork = (network: eEthereumNetwork): tEt
{
[eEthereumNetwork.coverage]: '',
[eEthereumNetwork.hardhat]: '',
[eEthereumNetwork.kovan]: '',
[eEthereumNetwork.kovan]: '0xE8b7fff5035dACa574259dC67FD9639f805edFbf',
[eEthereumNetwork.ropsten]: '',
[eEthereumNetwork.main]: '0x253f7b06c1d60c1fbbc9d82c301327eb86e3ba81',
},
Expand Down
71 changes: 71 additions & 0 deletions tasks/deployments/deploy-stakedAaveV3-impl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { task } from 'hardhat/config';

import { eContractid, eEthereumNetwork, tEthereumAddress } from '../../helpers/types';
import { registerContractInJsonDb } from '../../helpers/contracts-helpers';
import {
getAaveTokenPerNetwork,
getCooldownSecondsPerNetwork,
getUnstakeWindowPerNetwork,
getAaveAdminPerNetwork,
getDistributionDurationPerNetwork,
getAaveIncentivesVaultPerNetwork,
ZERO_ADDRESS,
} from '../../helpers/constants';
import { deployStakedAaveV3, deployStakedTokenV3 } from '../../helpers/contracts-accessors';
import { checkVerification, verifyContract } from '../../helpers/etherscan-verification';

const { StakedAave, StakedAaveImpl } = eContractid;

task(`deploy-StakeAave-implementation`, `Deploys the ${StakedAave} V3 implementation contract`)
.addFlag('verify', 'Verify StakedAave contract via Etherscan API.')
.addOptionalParam(
'vaultAddress',
'Use AaveIncentivesVault address by param instead of configuration.'
)
.addOptionalParam('aaveAddress', 'Use AaveToken address by param instead of configuration.')
.setAction(async ({ verify, vaultAddress, aaveAddress }, localBRE) => {
await localBRE.run('set-dre');

if (verify) {
checkVerification();
}

if (!localBRE.network.config.chainId) {
throw new Error('INVALID_CHAIN_ID');
}

const network = localBRE.network.name as eEthereumNetwork;

console.log(`\n- ${StakedAave}-V3 deployment`);

// stakedAAVE V3 and stakedTokenV3 share the same bytecode, they compete when trying to verify.

// NOTE : CONFIG WORKING FOR KOVAN, NOT BEEN VERIFIED FOR MAINNET
console.log(`\tDeploying ${StakedAave} implementation ...`);
const constructorArguments = [
aaveAddress || getAaveTokenPerNetwork(network),
aaveAddress || getAaveTokenPerNetwork(network),
getCooldownSecondsPerNetwork(network),
getUnstakeWindowPerNetwork(network),
vaultAddress || getAaveIncentivesVaultPerNetwork(network),
getAaveAdminPerNetwork(network),
getDistributionDurationPerNetwork(network),
'Staked Aave',
'stkAAVE',
'18',
ZERO_ADDRESS,
] as [string, string, string, string, string, string, string, string, string, string, string];

const stakedAaveImpl = await deployStakedTokenV3(
constructorArguments,
false // disable verify due not supported by current buidler etherscan plugin
);
await stakedAaveImpl.deployTransaction.wait();
await registerContractInJsonDb(StakedAaveImpl, stakedAaveImpl);

if (verify) {
await verifyContract(stakedAaveImpl.address, constructorArguments);
}

console.log(`\tFinished ${StakedAave} proxy and implementation deployment`);
});
4 changes: 2 additions & 2 deletions test/StakedAaveV3/claimHelper.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,10 @@ makeSuite('StakedAave V3 Claim Helper', (testEnv: TestEnv) => {
);
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 claimAdmin = await stakeAave2V3.getClaimHelper(); //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
const claimAdmin2 = await stakeAave2V3.getClaimHelper(); //claim admin // helper contract

expect(slashingAdmin).to.be.equal(users[0].address);
expect(cooldownAdmin).to.be.equal(users[1].address);
Expand Down
33 changes: 16 additions & 17 deletions test/StakedAaveV3/stakedAave-V3.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ makeSuite('StakedAave V3 slashing tests', (testEnv: TestEnv) => {

const slashingAdmin = await stakeV3.getAdmin(SLASHING_ADMIN); //slash admin
const cooldownAdmin = await stakeV3.getAdmin(COOLDOWN_ADMIN); //cooldown admin
const claimAdmin = await stakeV3.getAdmin(CLAIM_HELPER_ROLE); //claim admin // helper contract
const claimAdmin = await stakeV3.getClaimHelper(); //claim admin // helper contract

expect(slashingAdmin).to.be.equal(users[0].address);
expect(cooldownAdmin).to.be.equal(users[1].address);
Expand All @@ -86,6 +86,7 @@ makeSuite('StakedAave V3 slashing tests', (testEnv: TestEnv) => {
await expect(stakeV3.connect(staker.signer).stake(staker.address, amount)).to.be.revertedWith(
'INVALID_ZERO_AMOUNT'
);
console.log((await stakeV3.balanceOf(staker.address)).toString());
});

it('User 1 stakes 10 AAVE: receives 10 stkAAVE, StakedAave balance of AAVE is 10 and his rewards to claim are 0', async () => {
Expand Down Expand Up @@ -353,9 +354,7 @@ makeSuite('StakedAave V3 slashing tests', (testEnv: TestEnv) => {
it('Tries to slash with an account that is not the slashing admin', async () => {
const { users } = testEnv;

await expect(stakeV3.slash(users[2].address, '1')).to.be.revertedWith(
'CALLER_NOT_SLASHING_ADMIN'
);
await expect(stakeV3.slash(users[2].address, '1')).to.be.revertedWith('CALLER_NOT_MAIN_ADMIN');
});

it('Tries to pause the cooldown with an account that is not the cooldown admin', async () => {
Expand Down Expand Up @@ -583,10 +582,7 @@ makeSuite('StakedAave V3 slashing tests', (testEnv: TestEnv) => {
balanceBefore.add(amount.mul(ether).div(exchangeRate))
);

expect(aaveStakedAfter).to.be.eql(
aaveStakedBefore.add(amount)
);

expect(aaveStakedAfter).to.be.eql(aaveStakedBefore.add(amount));
});
it('Fails claim rewards for someone using claimRewardsOnBehalf if not helper', async () => {
const {
Expand Down Expand Up @@ -718,17 +714,20 @@ makeSuite('StakedAave V3 slashing tests', (testEnv: TestEnv) => {
await stakeV3.balanceOf(staker.address),
];

expect(userBalanceAfterActions[0]).to.be.eq(
saveUserBalance[0],
'Invalid aave user balance after action'
);

expect(userBalanceAfterActions[0]).to.be.eq(saveUserBalance[0], "Invalid aave user balance after action");

expect(
userBalanceAfterActions[1]
).to.be.eq(
saveUserBalance[1].add(halfRewards.mul(ether).div(currentExchangeRate))
,"invalid stkAAVE user balance after action")
expect(userBalanceAfterActions[1]).to.be.eq(
saveUserBalance[1].add(halfRewards.mul(ether).div(currentExchangeRate)),
'invalid stkAAVE user balance after action'
);

expect(aaveStakedAfter).to.be.equal(aaveStakedBefore.add(halfRewards), "Invalid underlying balance");

expect(aaveStakedAfter).to.be.equal(
aaveStakedBefore.add(halfRewards),
'Invalid underlying balance'
);
});
it('Claim & stake higher reward than current rewards balance', async () => {
const {
Expand Down