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

Claim Helper contract #6

Open
wants to merge 23 commits into
base: feat/slashing-implementation
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
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
9 changes: 9 additions & 0 deletions contracts/interfaces/IClaimStakingRewardsHelper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
pragma solidity ^0.7.5;

interface IClaimStakingRewardsHelper {
function claimAllRewards(address to) external returns (uint256);

function claimAllRewardsAndStake(address to) external;

function claimAndStake(address to, address stakeToken) external;
}
8 changes: 8 additions & 0 deletions contracts/interfaces/IERC20WithNonce.sol
Original file line number Diff line number Diff line change
@@ -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);
}
6 changes: 6 additions & 0 deletions contracts/interfaces/IPriceOracle.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.7.5;

interface IPriceOracle {
function getAssetPrice(address asset) external view returns (uint256);
}
34 changes: 34 additions & 0 deletions contracts/interfaces/IStakeUIHelper.sol
Original file line number Diff line number Diff line change
@@ -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
);
}
80 changes: 80 additions & 0 deletions contracts/misc/ClaimStakingRewardsHelper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// SPDX-License-Identifier: agpl-3.0
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
* @notice Contract to claim all rewards on the different stake pools
* or claim all and stake Aave token
* @author Aave
**/
contract ClaimStakingRewardsHelper is IClaimStakingRewardsHelper {
using SafeMath for uint256;
address public immutable aaveStakeToken;
address public immutable bptStakeToken;

constructor(
address _aaveStakeToken,
address _bptStakeToken,
address aaveToken
) {
aaveStakeToken = _aaveStakeToken;
bptStakeToken = _bptStakeToken;

IERC20(aaveToken).approve(_aaveStakeToken, type(uint256).max);
}

/**
* @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 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);
}

/**
* @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 {
_claimAndStake(msg.sender, to, aaveStakeToken);
_claimAndStake(msg.sender, 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(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 from,
address to,
address stakeToken
) internal {
uint256 rewardsClaimed =
IStakedTokenV3(stakeToken).claimRewardsOnBehalf(from, address(this), type(uint256).max);
if (rewardsClaimed > 0) {
IStakedTokenV3(aaveStakeToken).stake(to, rewardsClaimed);
}
}
}
35 changes: 35 additions & 0 deletions contracts/misc/IStakedToken.sol
Original file line number Diff line number Diff line change
@@ -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;
}
121 changes: 121 additions & 0 deletions contracts/misc/StakeUIHelper.sol
Original file line number Diff line number Diff line change
@@ -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)
);
}
}
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ services:
INFURA_KEY: ${INFURA_KEY}
ETHERSCAN_NETWORK: ${ETHERSCAN_NETWORK}
GITLAB_ACCESS_TOKEN: ${GITLAB_ACCESS_TOKEN}
ALCHEMY_KEY: ${ALCHEMY_KEY}
42 changes: 40 additions & 2 deletions helpers/contracts-accessors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -22,6 +22,45 @@ import { DoubleTransferHelper } from '../types/DoubleTransferHelper';
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<StakeUiHelper>(id, args);
if (verify) {
await verifyContract(instance.address, args);
}
return instance;
};

export const deployClaimHelper = async (
[aaveStakeTokenAddress, bptStakeTokenAddress, aaveToken]: [
tEthereumAddress,
tEthereumAddress,
tEthereumAddress
],
verify?: boolean
) => {
const id = eContractid.ClaimStakingRewardsHelper;
const args: string[] = [aaveStakeTokenAddress, bptStakeTokenAddress, aaveToken];
const instance = await deployContract<ClaimStakingRewardsHelper>(id, args);
if (verify) {
await verifyContract(instance.address, args);
}
return instance;
};

export const deployStakedAave = async (
[
Expand Down Expand Up @@ -198,7 +237,6 @@ export const deployStakedTokenV3 = async (
return instance;
};


export const deployStakedAaveV3 = async (
[
stakedToken,
Expand Down
2 changes: 2 additions & 0 deletions helpers/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export enum eContractid {
IBPool = 'IBPool',
IControllerAaveEcosystemReserve = 'IControllerAaveEcosystemReserve',
MockSelfDestruct = 'SelfdestructTransfer',
ClaimStakingRewardsHelper = 'ClaimStakingRewardsHelper',
StakeUIHelper = 'StakeUIHelper',
}

export type tEthereumAddress = string;
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
"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 --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/",
Expand Down
Loading