Skip to content

Commit

Permalink
Added features and tests (1/2)
Browse files Browse the repository at this point in the history
  • Loading branch information
dhadrien committed Feb 15, 2021
1 parent 8c1403f commit a224495
Show file tree
Hide file tree
Showing 5 changed files with 470 additions and 59 deletions.
16 changes: 14 additions & 2 deletions contracts/interfaces/IStakedTokenV3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,19 @@ interface IStakedTokenV3 is IStakedToken {
uint256 amount
) external;

function claimRewardsAndStake(uint256 amount) external;
function claimRewardsAndStake(address to, uint256 amount) external;

function claimRewardsAndStakeOnBehalf(address user, uint256 amount) external;
function claimRewardsAndUnstake(address to, uint256 amount) external;

function claimRewardsAndStakeOnBehalf(
address user,
address to,
uint256 amount
) external;

function claimRewardsAndUnstakeOnBehalf(
address user,
address to,
uint256 amount
) external;
}
11 changes: 6 additions & 5 deletions contracts/stake/StakedTokenV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@ contract StakedTokenV2 is
using SafeMath for uint256;
using SafeERC20 for IERC20;

function REVISION() public virtual pure returns(uint256) {
function REVISION() public pure virtual returns (uint256) {
return 2;
}

IERC20 public immutable STAKED_TOKEN;
IERC20 public immutable REWARD_TOKEN;
uint256 public immutable COOLDOWN_SECONDS;
Expand Down Expand Up @@ -117,7 +118,7 @@ contract StakedTokenV2 is
);
}

function stake(address onBehalfOf, uint256 amount) external override virtual {
function stake(address onBehalfOf, uint256 amount) external virtual override {
require(amount != 0, 'INVALID_ZERO_AMOUNT');
uint256 balanceOfUser = balanceOf(onBehalfOf);

Expand All @@ -141,7 +142,7 @@ contract StakedTokenV2 is
* @param to Address to redeem to
* @param amount Amount to redeem
**/
function redeem(address to, uint256 amount) external override virtual {
function redeem(address to, uint256 amount) external virtual override {
require(amount != 0, 'INVALID_ZERO_AMOUNT');
//solium-disable-next-line
uint256 cooldownStartTimestamp = stakersCooldowns[msg.sender];
Expand Down Expand Up @@ -187,7 +188,7 @@ contract StakedTokenV2 is
* @param to Address to stake for
* @param amount Amount to stake
**/
function claimRewards(address to, uint256 amount) external override {
function claimRewards(address to, uint256 amount) external virtual override {
uint256 newTotalRewards =
_updateCurrentUnclaimedRewards(msg.sender, balanceOf(msg.sender), false);
uint256 amountToClaim = (amount == type(uint256).max) ? newTotalRewards : amount;
Expand Down Expand Up @@ -331,7 +332,7 @@ contract StakedTokenV2 is
* @dev returns the revision of the implementation contract
* @return The revision
*/
function getRevision() internal virtual pure override returns (uint256) {
function getRevision() internal pure virtual override returns (uint256) {
return REVISION();
}

Expand Down
156 changes: 113 additions & 43 deletions contracts/stake/StakedTokenV3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ contract StakedTokenV3 is StakedTokenV2, IStakedTokenV3, RoleManager {
_;
}


event Staked(
address indexed from,
address indexed onBehalfOf,
Expand Down Expand Up @@ -162,6 +161,8 @@ contract StakedTokenV3 is StakedTokenV2, IStakedTokenV3, RoleManager {
_initAdmins(adminsRoles, adminsAddresses);

_maxSlashablePercentage = maxSlashablePercentage;

IERC20(STAKED_TOKEN).approve(address(this), type(uint256).max);
}

/**
Expand Down Expand Up @@ -204,67 +205,80 @@ contract StakedTokenV3 is StakedTokenV2, IStakedTokenV3, RoleManager {
* @param amount Amount to redeem
**/
function redeem(address to, uint256 amount) external override(IStakedToken, StakedTokenV2) {
require(amount != 0, 'INVALID_ZERO_AMOUNT');
//solium-disable-next-line
uint256 cooldownStartTimestamp = stakersCooldowns[msg.sender];

require(
!_cooldownPaused && block.timestamp > cooldownStartTimestamp.add(COOLDOWN_SECONDS),
'INSUFFICIENT_COOLDOWN'
);
require(
block.timestamp.sub(cooldownStartTimestamp.add(COOLDOWN_SECONDS)) <= UNSTAKE_WINDOW,
'UNSTAKE_WINDOW_FINISHED'
);
uint256 balanceOfMessageSender = balanceOf(msg.sender);

uint256 amountToRedeem = (amount > balanceOfMessageSender) ? balanceOfMessageSender : amount;

_updateCurrentUnclaimedRewards(msg.sender, balanceOfMessageSender, true);

uint256 underlyingToRedeem = amountToRedeem.mul(exchangeRate()).div(1e18);

_burn(msg.sender, amountToRedeem);

if (balanceOfMessageSender.sub(amountToRedeem) == 0) {
stakersCooldowns[msg.sender] = 0;
}

IERC20(STAKED_TOKEN).safeTransfer(to, underlyingToRedeem);

emit Redeem(msg.sender, to, amountToRedeem, underlyingToRedeem);
_redeem(msg.sender, to, amount);
}

/**
/**
* @dev Claims an `amount` of `REWARD_TOKEN` to the address `to` on behalf of the user. Only the claim helper contract is allowed to call this function
* @param user The address of the user
* @param to Address to claim for
* @param amount Amount to claim
**/
function claimRewardsOnBehalf(address user, address to, uint256 amount) external override onlyClaimHelper {
uint256 newTotalRewards =
_updateCurrentUnclaimedRewards(user, balanceOf(user), false);
uint256 amountToClaim = (amount == type(uint256).max) ? newTotalRewards : amount;

stakerRewardsToClaim[user] = newTotalRewards.sub(amountToClaim, 'INVALID_AMOUNT');
REWARD_TOKEN.safeTransferFrom(REWARDS_VAULT, to, amountToClaim);
function claimRewardsOnBehalf(
address user,
address to,
uint256 amount
) external override onlyClaimHelper {
_claimRewards(user, to, amount);
}

emit RewardsClaimed(user, to, amountToClaim);
/**
* @dev Claims an `amount` of `REWARD_TOKEN` to the address `to`
* @param to Address to stake for
* @param amount Amount to stake
**/
function claimRewards(address to, uint256 amount) external override(StakedTokenV2, IStakedToken) {
_claimRewards(msg.sender, to, amount);
}

/**
* @dev Claims an `amount` of `REWARD_TOKEN` amd restakes
* @param to Address to stake to
* @param amount Amount to claim
**/
function claimRewardsAndStake(uint256 amount) external override {
function claimRewardsAndStake(address to, uint256 amount) external override {
uint256 rewardsClaimed = _claimRewards(msg.sender, address(this), amount);
_stake(address(this), to, rewardsClaimed);
}

/**
* @dev Claims an `amount` of `REWARD_TOKEN` and restakes. Only the claim helper contract is allowed to call this function
* @param user The address of the user from which to claim
* @param to Address to stake to
* @param amount Amount to claim
**/
function claimRewardsAndStakeOnBehalf(
address user,
address to,
uint256 amount
) external override onlyClaimHelper {
uint256 rewardsClaimed = _claimRewards(user, address(this), amount);
_stake(address(this), to, rewardsClaimed);
}

/**
* @dev Claims an `amount` of `REWARD_TOKEN` amd unstakes
* @param amount Amount to claim
* @param to Address to claim and unstake to
**/
function claimRewardsAndUnstake(address to, uint256 amount) external override {
_claimRewards(msg.sender, to, amount);
_redeem(msg.sender, to, amount);
}

/**
* @dev Claims an `amount` of `REWARD_TOKEN` and unstakes. Only the claim helper contract is allowed to call this function
* @param user The address of the user
* @param to Address to claim and unstake to
* @param amount Amount to claim
**/
function claimRewardsAndStakeOnBehalf(address user, uint256 amount) external override onlyClaimHelper {
function claimRewardsAndUnstakeOnBehalf(
address user,
address to,
uint256 amount
) external override onlyClaimHelper {
_claimRewards(user, to, amount);
_redeem(user, to, amount);
}

/**
Expand All @@ -289,7 +303,6 @@ contract StakedTokenV3 is StakedTokenV2, IStakedTokenV3, RoleManager {
* @param amount the amount
**/
function slash(address destination, uint256 amount) external override onlySlashingAdmin {

uint256 balance = STAKED_TOKEN.balanceOf(address(this));

uint256 maxSlashable = balance.percentMul(_maxSlashablePercentage);
Expand Down Expand Up @@ -343,6 +356,22 @@ contract StakedTokenV3 is StakedTokenV2, IStakedTokenV3, RoleManager {
return REVISION();
}

function _claimRewards(
address from,
address to,
uint256 amount
) internal returns (uint256) {
uint256 newTotalRewards = _updateCurrentUnclaimedRewards(from, balanceOf(from), false);
uint256 amountToClaim = (amount == type(uint256).max) ? newTotalRewards : amount;

stakerRewardsToClaim[from] = newTotalRewards.sub(amountToClaim, 'INVALID_AMOUNT');

REWARD_TOKEN.safeTransferFrom(REWARDS_VAULT, to, amountToClaim);

emit RewardsClaimed(from, to, amountToClaim);
return (amountToClaim);
}

function _stake(
address user,
address onBehalfOf,
Expand All @@ -368,5 +397,46 @@ contract StakedTokenV3 is StakedTokenV2, IStakedTokenV3, RoleManager {
STAKED_TOKEN.safeTransferFrom(user, address(this), amount);

emit Staked(user, onBehalfOf, amount, sharesToMint);
}
}

/**
* @dev Redeems staked tokens, and stop earning rewards
* @param to Address to redeem to
* @param amount Amount to redeem
**/
function _redeem(
address from,
address to,
uint256 amount
) internal {
require(amount != 0, 'INVALID_ZERO_AMOUNT');
//solium-disable-next-line
uint256 cooldownStartTimestamp = stakersCooldowns[from];

require(
!_cooldownPaused && block.timestamp > cooldownStartTimestamp.add(COOLDOWN_SECONDS),
'INSUFFICIENT_COOLDOWN'
);
require(
block.timestamp.sub(cooldownStartTimestamp.add(COOLDOWN_SECONDS)) <= UNSTAKE_WINDOW,
'UNSTAKE_WINDOW_FINISHED'
);
uint256 balanceOfFrom = balanceOf(from);

uint256 amountToRedeem = (amount > balanceOfFrom) ? balanceOfFrom : amount;

_updateCurrentUnclaimedRewards(from, balanceOfFrom, true);

uint256 underlyingToRedeem = amountToRedeem.mul(exchangeRate()).div(1e18);

_burn(from, amountToRedeem);

if (balanceOfFrom.sub(amountToRedeem) == 0) {
stakersCooldowns[from] = 0;
}

IERC20(STAKED_TOKEN).safeTransfer(to, underlyingToRedeem);

emit Redeem(from, to, amountToRedeem, underlyingToRedeem);
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"test-pei": "npm run test test/__setup.spec.ts test/AaveIncentivesController/*.spec.ts",
"test-psi": "npm run test test/__setup.spec.ts test/StakedAave/*.spec.ts",
"test-psi2": "npm run test test/__setup.spec.ts test/StakedAaveV2/*.spec.ts",
"test-stk-aave-3": "hardhat test test/__setup.spec.ts test/StakedAaveV3/*.spec.ts",
"test-bpt": "npm run compile:force:quiet && FORKING_BLOCK=11730175 MAINNET_FORK=true hardhat test test/StakedBPT/create-bpt-and-stakebpt.spec.ts",
"coverage": "SKIP_LOAD=true npx hardhat typechain && node --max_old_space_size=6144 node_modules/.bin/hardhat coverage",
"dev:deployment": "hardhat dev-deployment",
Expand Down
Loading

0 comments on commit a224495

Please sign in to comment.