Diff
checker
텍스트
텍스트
이미지
문서
Excel
폴더
Legal
Enterprise
데스크톱
요금제
로그인
데스크톱 앱 다운로드
텍스트 비교
두 텍스트 파일의 차이점을 찾아보세요
도구
기록
실시간 편집
변경 없는 행 숨기기
줄바꿈 비활성화
레이아웃
나란히 보기
합쳐 보기
비교 단위
스마트
단어
글자
구문 강조
언어 선택
제외
텍스트 변환
첫 변경으로
수정
Diffchecker Desktop
가장 안전하게 Diffchecker를 사용하는 방법. 데스크톱 앱을 사용하면 비교 데이터가 외부로 전송되지 않습니다!
데스크톱 앱 받기
auraBal
생성일
4년 전
비교 결과 만료 없음
초기화
내보내기
공유
두 텍스트가 동일합니다
두 텍스트 간 차이점이 없습니다
0 삭제
행
총
삭제
글자
총
삭제
이 기능을 계속 사용하려면 업그레이드해 주세요
Diff
checker
Pro
요금제 보기
278 행
복사
0 추가
행
총
추가
글자
총
추가
이 기능을 계속 사용하려면 업그레이드해 주세요
Diff
checker
Pro
요금제 보기
278 행
복사
contract AuraBalStakerStrategy is BaseStrategy {
contract AuraBalStakerStrategy is BaseStrategy {
using SafeMathUpgradeable for uint256;
using SafeMathUpgradeable for uint256;
using SafeERC20Upgradeable for IERC20Upgradeable;
using SafeERC20Upgradeable for IERC20Upgradeable;
bool public claimRewardsOnWithdrawAll;
bool public claimRewardsOnWithdrawAll;
uint256 public balEthBptToAuraBalMinOutBps;
uint256 public balEthBptToAuraBalMinOutBps;
IBaseRewardPool public constant AURABAL_REWARDS =
IBaseRewardPool public constant AURABAL_REWARDS =
IBaseRewardPool(0x5e5ea2048475854a5702F5B8468A51Ba1296EFcC);
IBaseRewardPool(0x5e5ea2048475854a5702F5B8468A51Ba1296EFcC);
IVault public constant GRAVIAURA =
IVault public constant GRAVIAURA =
IVault(0xBA485b556399123261a5F9c95d413B4f93107407);
IVault(0xBA485b556399123261a5F9c95d413B4f93107407);
IBalancerVault public constant BALANCER_VAULT =
IBalancerVault public constant BALANCER_VAULT =
IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8);
IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8);
IAuraToken public constant AURA =
IAuraToken public constant AURA =
IAuraToken(0xC0c293ce456fF0ED870ADd98a0828Dd4d2903DBF);
IAuraToken(0xC0c293ce456fF0ED870ADd98a0828Dd4d2903DBF);
IERC20Upgradeable public constant AURABAL =
IERC20Upgradeable public constant AURABAL =
IERC20Upgradeable(0x616e8BfA43F920657B3497DBf40D6b1A02D4608d);
IERC20Upgradeable(0x616e8BfA43F920657B3497DBf40D6b1A02D4608d);
IERC20Upgradeable public constant WETH =
IERC20Upgradeable public constant WETH =
IERC20Upgradeable(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
IERC20Upgradeable(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
IERC20Upgradeable public constant BAL =
IERC20Upgradeable public constant BAL =
IERC20Upgradeable(0xba100000625a3754423978a60c9317c58a424e3D);
IERC20Upgradeable(0xba100000625a3754423978a60c9317c58a424e3D);
IERC20Upgradeable public constant BALETH_BPT =
IERC20Upgradeable public constant BALETH_BPT =
IERC20Upgradeable(0x5c6Ee304399DBdB9C8Ef030aB642B10820DB8F56);
IERC20Upgradeable(0x5c6Ee304399DBdB9C8Ef030aB642B10820DB8F56);
IERC20Upgradeable public constant BB_A_USD =
IERC20Upgradeable public constant BB_A_USD =
IERC20Upgradeable(0x7B50775383d3D6f0215A8F290f2C9e2eEBBEceb2);
IERC20Upgradeable(0x7B50775383d3D6f0215A8F290f2C9e2eEBBEceb2);
bytes32 public constant BAL_ETH_POOL_ID =
bytes32 public constant BAL_ETH_POOL_ID =
0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014;
0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014;
bytes32 public constant AURABAL_BALETH_BPT_POOL_ID =
bytes32 public constant AURABAL_BALETH_BPT_POOL_ID =
0x3dd0843a028c86e0b760b1a76929d1c5ef93a2dd000200000000000000000249;
0x3dd0843a028c86e0b760b1a76929d1c5ef93a2dd000200000000000000000249;
/// @dev Initialize the Strategy with security settings as well as tokens
/// @dev Initialize the Strategy with security settings as well as tokens
/// @notice Proxies will set any non constant variable you declare as default value
/// @notice Proxies will set any non constant variable you declare as default value
/// @dev add any extra changeable variable at end of initializer as shown
/// @dev add any extra changeable variable at end of initializer as shown
function initialize(address _vault) public initializer {
function initialize(address _vault) public initializer {
require(IVault(_vault).token() == address(AURABAL));
require(IVault(_vault).token() == address(AURABAL));
__BaseStrategy_init(_vault);
__BaseStrategy_init(_vault);
want = address(AURABAL);
want = address(AURABAL);
claimRewardsOnWithdrawAll = true;
claimRewardsOnWithdrawAll = true;
balEthBptToAuraBalMinOutBps = 9500; // max 5% slippage
balEthBptToAuraBalMinOutBps = 9500; // max 5% slippage
AURABAL.safeApprove(address(AURABAL_REWARDS), type(uint256).max);
AURABAL.safeApprove(address(AURABAL_REWARDS), type(uint256).max);
BAL.safeApprove(address(BALANCER_VAULT), type(uint256).max);
BAL.safeApprove(address(BALANCER_VAULT), type(uint256).max);
BALETH_BPT.safeApprove(address(BALANCER_VAULT), type(uint256).max);
BALETH_BPT.safeApprove(address(BALANCER_VAULT), type(uint256).max);
AURA.approve(address(GRAVIAURA), type(uint256).max);
AURA.approve(address(GRAVIAURA), type(uint256).max);
}
}
function setClaimRewardsOnWithdrawAll(bool _claimRewardsOnWithdrawAll)
function setClaimRewardsOnWithdrawAll(bool _claimRewardsOnWithdrawAll)
external
external
{
{
_onlyGovernanceOrStrategist();
_onlyGovernanceOrStrategist();
claimRewardsOnWithdrawAll = _claimRewardsOnWithdrawAll;
claimRewardsOnWithdrawAll = _claimRewardsOnWithdrawAll;
}
}
function setBalEthBptToAuraBalMinOutBps(uint256 _minOutBps) external {
function setBalEthBptToAuraBalMinOutBps(uint256 _minOutBps) external {
_onlyGovernanceOrStrategist();
_onlyGovernanceOrStrategist();
require(_minOutBps <= MAX_BPS, "Invalid minOutBps");
require(_minOutBps <= MAX_BPS, "Invalid minOutBps");
balEthBptToAuraBalMinOutBps = _minOutBps;
balEthBptToAuraBalMinOutBps = _minOutBps;
}
}
/// @dev Return the name of the strategy
/// @dev Return the name of the strategy
function getName() external pure override returns (string memory) {
function getName() external pure override returns (string memory) {
return "AuraBalStakerStrategy";
return "AuraBalStakerStrategy";
}
}
/// @dev Return a list of protected tokens
/// @dev Return a list of protected tokens
/// @notice It's very important all tokens that are meant to be in the strategy to be marked as protected
/// @notice It's very important all tokens that are meant to be in the strategy to be marked as protected
/// @notice this provides security guarantees to the depositors they can't be sweeped away
/// @notice this provides security guarantees to the depositors they can't be sweeped away
function getProtectedTokens()
function getProtectedTokens()
public
public
view
view
virtual
virtual
override
override
returns (address[] memory)
returns (address[] memory)
{
{
address[] memory protectedTokens = new address[](3);
address[] memory protectedTokens = new address[](3);
protectedTokens[0] = want; // AURABAL
protectedTokens[0] = want; // AURABAL
protectedTokens[1] = address(AURA);
protectedTokens[1] = address(AURA);
protectedTokens[2] = address(BAL);
protectedTokens[2] = address(BAL);
return protectedTokens;
return protectedTokens;
}
}
/// @dev Deposit `_amount` of want, investing it to earn yield
/// @dev Deposit `_amount` of want, investing it to earn yield
function _deposit(uint256 _amount) internal override {
function _deposit(uint256 _amount) internal override {
// Add code here to invest `_amount` of want to earn yield
// Add code here to invest `_amount` of want to earn yield
AURABAL_REWARDS.stake(_amount);
AURABAL_REWARDS.stake(_amount);
}
}
/// @dev Withdraw all funds, this is used for migrations, most of the time for emergency reasons
/// @dev Withdraw all funds, this is used for migrations, most of the time for emergency reasons
function _withdrawAll() internal override {
function _withdrawAll() internal override {
uint256 poolBalance = balanceOfPool();
uint256 poolBalance = balanceOfPool();
if (poolBalance > 0) {
if (poolBalance > 0) {
AURABAL_REWARDS.withdrawAll(claimRewardsOnWithdrawAll);
AURABAL_REWARDS.withdrawAll(claimRewardsOnWithdrawAll);
}
}
}
}
/// @dev Withdraw `_amount` of want, so that it can be sent to the vault / depositor
/// @dev Withdraw `_amount` of want, so that it can be sent to the vault / depositor
/// @notice just unlock the funds and return the amount you could unlock
/// @notice just unlock the funds and return the amount you could unlock
function _withdrawSome(uint256 _amount)
function _withdrawSome(uint256 _amount)
internal
internal
override
override
returns (uint256)
returns (uint256)
{
{
uint256 wantBalance = balanceOfWant();
uint256 wantBalance = balanceOfWant();
if (wantBalance < _amount) {
if (wantBalance < _amount) {
uint256 toWithdraw = _amount.sub(wantBalance);
uint256 toWithdraw = _amount.sub(wantBalance);
AURABAL_REWARDS.withdraw(toWithdraw, false);
AURABAL_REWARDS.withdraw(toWithdraw, false);
}
}
return MathUpgradeable.min(_amount, balanceOfWant());
return MathUpgradeable.min(_amount, balanceOfWant());
}
}
/// @dev Does this function require `tend` to be called?
/// @dev Does this function require `tend` to be called?
function _isTendable() internal pure override returns (bool) {
function _isTendable() internal pure override returns (bool) {
return false; // Change to true if the strategy should be tended
return false; // Change to true if the strategy should be tended
}
}
function _harvest()
function _harvest()
internal
internal
override
override
returns (TokenAmount[] memory harvested)
returns (TokenAmount[] memory harvested)
{
{
AURABAL_REWARDS.getReward();
AURABAL_REWARDS.getReward();
// Rewards are handled like this:
// Rewards are handled like this:
// BAL --> BAL/ETH BPT --> AURABAL (autocompounded)
// BAL --> BAL/ETH BPT --> AURABAL (autocompounded)
// AURA --> GRAVIAURA (emitted)
// AURA --> GRAVIAURA (emitted)
// BB_A_USD --> Left in strategy to be sweeped later
// BB_A_USD --> Left in strategy to be sweeped later
harvested = new TokenAmount[](2);
harvested = new TokenAmount[](2);
harvested[0].token = address(AURABAL);
harvested[0].token = address(AURABAL);
harvested[1].token = address(GRAVIAURA);
harvested[1].token = address(GRAVIAURA);
// BAL --> BAL/ETH BPT --> AURABAL
// BAL --> BAL/ETH BPT --> AURABAL
uint256 balBalance = BAL.balanceOf(address(this));
uint256 balBalance = BAL.balanceOf(address(this));
uint256 auraBalEarned;
uint256 auraBalEarned;
if (balBalance > 0) {
if (balBalance > 0) {
// Deposit BAL --> BAL/ETH BPT
// Deposit BAL --> BAL/ETH BPT
IAsset[] memory assets = new IAsset[](2);
IAsset[] memory assets = new IAsset[](2);
assets[0] = IAsset(address(BAL));
assets[0] = IAsset(address(BAL));
assets[1] = IAsset(address(WETH));
assets[1] = IAsset(address(WETH));
uint256[] memory maxAmountsIn = new uint256[](2);
uint256[] memory maxAmountsIn = new uint256[](2);
maxAmountsIn[0] = balBalance;
maxAmountsIn[0] = balBalance;
maxAmountsIn[1] = 0;
maxAmountsIn[1] = 0;
BALANCER_VAULT.joinPool(
BALANCER_VAULT.joinPool(
BAL_ETH_POOL_ID,
BAL_ETH_POOL_ID,
address(this),
address(this),
address(this),
address(this),
IBalancerVault.JoinPoolRequest({
IBalancerVault.JoinPoolRequest({
assets: assets,
assets: assets,
maxAmountsIn: maxAmountsIn,
maxAmountsIn: maxAmountsIn,
userData: abi.encode(
userData: abi.encode(
JoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT,
JoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT,
maxAmountsIn,
maxAmountsIn,
0 // minOut
0 // minOut
),
),
fromInternalBalance: false
fromInternalBalance: false
})
})
);
);
// Swap BAL/ETH BPT --> AURABAL
// Swap BAL/ETH BPT --> AURABAL
uint256 balEthBptBalance = IERC20Upgradeable(BALETH_BPT).balanceOf(
uint256 balEthBptBalance = IERC20Upgradeable(BALETH_BPT).balanceOf(
address(this)
address(this)
);
);
// Swap BAL/ETH BPT --> auraBal
// Swap BAL/ETH BPT --> auraBal
IBalancerVault.FundManagement memory fundManagement = IBalancerVault
IBalancerVault.FundManagement memory fundManagement = IBalancerVault
.FundManagement({
.FundManagement({
sender: address(this),
sender: address(this),
fromInternalBalance: false,
fromInternalBalance: false,
recipient: payable(address(this)),
recipient: payable(address(this)),
toInternalBalance: false
toInternalBalance: false
});
});
IBalancerVault.SingleSwap memory singleSwap = IBalancerVault
IBalancerVault.SingleSwap memory singleSwap = IBalancerVault
.SingleSwap({
.SingleSwap({
poolId: AURABAL_BALETH_BPT_POOL_ID,
poolId: AURABAL_BALETH_BPT_POOL_ID,
kind: IBalancerVault.SwapKind.GIVEN_IN,
kind: IBalancerVault.SwapKind.GIVEN_IN,
assetIn: IAsset(address(BALETH_BPT)),
assetIn: IAsset(address(BALETH_BPT)),
assetOut: IAsset(address(AURABAL)),
assetOut: IAsset(address(AURABAL)),
amount: balEthBptBalance,
amount: balEthBptBalance,
userData: new bytes(0)
userData: new bytes(0)
});
});
uint256 minOut = (balEthBptBalance * balEthBptToAuraBalMinOutBps) /
uint256 minOut = (balEthBptBalance * balEthBptToAuraBalMinOutBps) /
MAX_BPS;
MAX_BPS;
auraBalEarned = BALANCER_VAULT.swap(
auraBalEarned = BALANCER_VAULT.swap(
singleSwap,
singleSwap,
fundManagement,
fundManagement,
minOut,
minOut,
type(uint256).max
type(uint256).max
);
);
harvested[0].amount = auraBalEarned;
harvested[0].amount = auraBalEarned;
}
}
// AURA --> graviAURA
// AURA --> graviAURA
uint256 auraBalance = AURA.balanceOf(address(this));
uint256 auraBalance = AURA.balanceOf(address(this));
if (auraBalance > 0) {
if (auraBalance > 0) {
GRAVIAURA.deposit(auraBalance);
GRAVIAURA.deposit(auraBalance);
uint256 graviAuraBalance = GRAVIAURA.balanceOf(address(this));
uint256 graviAuraBalance = GRAVIAURA.balanceOf(address(this));
harvested[1].amount = graviAuraBalance;
harvested[1].amount = graviAuraBalance;
_processExtraToken(address(GRAVIAURA), graviAuraBalance);
_processExtraToken(address(GRAVIAURA), graviAuraBalance);
}
}
// Report harvest
// Report harvest
_reportToVault(auraBalEarned);
_reportToVault(auraBalEarned);
// Stake whatever is earned
// Stake whatever is earned
if (auraBalEarned > 0) {
if (auraBalEarned > 0) {
_deposit(auraBalEarned);
_deposit(auraBalEarned);
}
}
}
}
// Example tend is a no-op which returns the values, could also just revert
// Example tend is a no-op which returns the values, could also just revert
function _tend() internal override returns (TokenAmount[] memory tended) {
function _tend() internal override returns (TokenAmount[] memory tended) {
revert("no op");
revert("no op");
}
}
/// @dev Return the balance (in want) that the strategy has invested somewhere
/// @dev Return the balance (in want) that the strategy has invested somewhere
function balanceOfPool() public view override returns (uint256) {
function balanceOfPool() public view override returns (uint256) {
// Change this to return the amount of want invested in another protocol
// Change this to return the amount of want invested in another protocol
return AURABAL_REWARDS.balanceOf(address(this));
return AURABAL_REWARDS.balanceOf(address(this));
}
}
/// @dev Return the balance of rewards that the strategy has accrued
/// @dev Return the balance of rewards that the strategy has accrued
/// @notice Used for offChain APY and Harvest Health monitoring
/// @notice Used for offChain APY and Harvest Health monitoring
function balanceOfRewards()
function balanceOfRewards()
external
external
view
view
override
override
returns (TokenAmount[] memory rewards)
returns (TokenAmount[] memory rewards)
{
{
uint256 balEarned = AURABAL_REWARDS.earned(address(this));
uint256 balEarned = AURABAL_REWARDS.earned(address(this));
rewards = new TokenAmount[](2);
rewards = new TokenAmount[](2);
rewards[0] = TokenAmount(address(BAL), balEarned);
rewards[0] = TokenAmount(address(BAL), balEarned);
rewards[1] = TokenAmount(
rewards[1] = TokenAmount(
address(AURA),
address(AURA),
getMintableAuraRewards(balEarned)
getMintableAuraRewards(balEarned)
);
);
}
}
/// @notice Returns the expected amount of AURA to be minted given an amount of BAL rewards
/// @notice Returns the expected amount of AURA to be minted given an amount of BAL rewards
/// @dev ref: https://etherscan.io/address/0xc0c293ce456ff0ed870add98a0828dd4d2903dbf#code#F1#L86
/// @dev ref: https://etherscan.io/address/0xc0c293ce456ff0ed870add98a0828dd4d2903dbf#code#F1#L86
function getMintableAuraRewards(uint256 _balAmount)
function getMintableAuraRewards(uint256 _balAmount)
public
public
view
view
returns (uint256 amount)
returns (uint256 amount)
{
{
// NOTE: Only correct if AURA.minterMinted() == 0
// NOTE: Only correct if AURA.minterMinted() == 0
// minterMinted is a private var in the contract, so we can't access it directly
// minterMinted is a private var in the contract, so we can't access it directly
uint256 emissionsMinted = AURA.totalSupply() - AURA.INIT_MINT_AMOUNT();
uint256 emissionsMinted = AURA.totalSupply() - AURA.INIT_MINT_AMOUNT();
uint256 cliff = emissionsMinted.div(AURA.reductionPerCliff());
uint256 cliff = emissionsMinted.div(AURA.reductionPerCliff());
uint256 totalCliffs = AURA.totalCliffs();
uint256 totalCliffs = AURA.totalCliffs();
if (cliff < totalCliffs) {
if (cliff < totalCliffs) {
uint256 reduction = totalCliffs.sub(cliff).mul(5).div(2).add(700);
uint256 reduction = totalCliffs.sub(cliff).mul(5).div(2).add(700);
amount = _balAmount.mul(reduction).div(totalCliffs);
amount = _balAmount.mul(reduction).div(totalCliffs);
uint256 amtTillMax = AURA.EMISSIONS_MAX_SUPPLY().sub(
uint256 amtTillMax = AURA.EMISSIONS_MAX_SUPPLY().sub(
emissionsMinted
emissionsMinted
);
);
if (amount > amtTillMax) {
if (amount > amtTillMax) {
amount = amtTillMax;
amount = amtTillMax;
}
}
}
}
}
}
}
}
저장된 비교 결과
원본
파일 열기
contract AuraBalStakerStrategy is BaseStrategy { using SafeMathUpgradeable for uint256; using SafeERC20Upgradeable for IERC20Upgradeable; bool public claimRewardsOnWithdrawAll; uint256 public balEthBptToAuraBalMinOutBps; IBaseRewardPool public constant AURABAL_REWARDS = IBaseRewardPool(0x5e5ea2048475854a5702F5B8468A51Ba1296EFcC); IVault public constant GRAVIAURA = IVault(0xBA485b556399123261a5F9c95d413B4f93107407); IBalancerVault public constant BALANCER_VAULT = IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8); IAuraToken public constant AURA = IAuraToken(0xC0c293ce456fF0ED870ADd98a0828Dd4d2903DBF); IERC20Upgradeable public constant AURABAL = IERC20Upgradeable(0x616e8BfA43F920657B3497DBf40D6b1A02D4608d); IERC20Upgradeable public constant WETH = IERC20Upgradeable(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); IERC20Upgradeable public constant BAL = IERC20Upgradeable(0xba100000625a3754423978a60c9317c58a424e3D); IERC20Upgradeable public constant BALETH_BPT = IERC20Upgradeable(0x5c6Ee304399DBdB9C8Ef030aB642B10820DB8F56); IERC20Upgradeable public constant BB_A_USD = IERC20Upgradeable(0x7B50775383d3D6f0215A8F290f2C9e2eEBBEceb2); bytes32 public constant BAL_ETH_POOL_ID = 0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014; bytes32 public constant AURABAL_BALETH_BPT_POOL_ID = 0x3dd0843a028c86e0b760b1a76929d1c5ef93a2dd000200000000000000000249; /// @dev Initialize the Strategy with security settings as well as tokens /// @notice Proxies will set any non constant variable you declare as default value /// @dev add any extra changeable variable at end of initializer as shown function initialize(address _vault) public initializer { require(IVault(_vault).token() == address(AURABAL)); __BaseStrategy_init(_vault); want = address(AURABAL); claimRewardsOnWithdrawAll = true; balEthBptToAuraBalMinOutBps = 9500; // max 5% slippage AURABAL.safeApprove(address(AURABAL_REWARDS), type(uint256).max); BAL.safeApprove(address(BALANCER_VAULT), type(uint256).max); BALETH_BPT.safeApprove(address(BALANCER_VAULT), type(uint256).max); AURA.approve(address(GRAVIAURA), type(uint256).max); } function setClaimRewardsOnWithdrawAll(bool _claimRewardsOnWithdrawAll) external { _onlyGovernanceOrStrategist(); claimRewardsOnWithdrawAll = _claimRewardsOnWithdrawAll; } function setBalEthBptToAuraBalMinOutBps(uint256 _minOutBps) external { _onlyGovernanceOrStrategist(); require(_minOutBps <= MAX_BPS, "Invalid minOutBps"); balEthBptToAuraBalMinOutBps = _minOutBps; } /// @dev Return the name of the strategy function getName() external pure override returns (string memory) { return "AuraBalStakerStrategy"; } /// @dev Return a list of protected tokens /// @notice It's very important all tokens that are meant to be in the strategy to be marked as protected /// @notice this provides security guarantees to the depositors they can't be sweeped away function getProtectedTokens() public view virtual override returns (address[] memory) { address[] memory protectedTokens = new address[](3); protectedTokens[0] = want; // AURABAL protectedTokens[1] = address(AURA); protectedTokens[2] = address(BAL); return protectedTokens; } /// @dev Deposit `_amount` of want, investing it to earn yield function _deposit(uint256 _amount) internal override { // Add code here to invest `_amount` of want to earn yield AURABAL_REWARDS.stake(_amount); } /// @dev Withdraw all funds, this is used for migrations, most of the time for emergency reasons function _withdrawAll() internal override { uint256 poolBalance = balanceOfPool(); if (poolBalance > 0) { AURABAL_REWARDS.withdrawAll(claimRewardsOnWithdrawAll); } } /// @dev Withdraw `_amount` of want, so that it can be sent to the vault / depositor /// @notice just unlock the funds and return the amount you could unlock function _withdrawSome(uint256 _amount) internal override returns (uint256) { uint256 wantBalance = balanceOfWant(); if (wantBalance < _amount) { uint256 toWithdraw = _amount.sub(wantBalance); AURABAL_REWARDS.withdraw(toWithdraw, false); } return MathUpgradeable.min(_amount, balanceOfWant()); } /// @dev Does this function require `tend` to be called? function _isTendable() internal pure override returns (bool) { return false; // Change to true if the strategy should be tended } function _harvest() internal override returns (TokenAmount[] memory harvested) { AURABAL_REWARDS.getReward(); // Rewards are handled like this: // BAL --> BAL/ETH BPT --> AURABAL (autocompounded) // AURA --> GRAVIAURA (emitted) // BB_A_USD --> Left in strategy to be sweeped later harvested = new TokenAmount[](2); harvested[0].token = address(AURABAL); harvested[1].token = address(GRAVIAURA); // BAL --> BAL/ETH BPT --> AURABAL uint256 balBalance = BAL.balanceOf(address(this)); uint256 auraBalEarned; if (balBalance > 0) { // Deposit BAL --> BAL/ETH BPT IAsset[] memory assets = new IAsset[](2); assets[0] = IAsset(address(BAL)); assets[1] = IAsset(address(WETH)); uint256[] memory maxAmountsIn = new uint256[](2); maxAmountsIn[0] = balBalance; maxAmountsIn[1] = 0; BALANCER_VAULT.joinPool( BAL_ETH_POOL_ID, address(this), address(this), IBalancerVault.JoinPoolRequest({ assets: assets, maxAmountsIn: maxAmountsIn, userData: abi.encode( JoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT, maxAmountsIn, 0 // minOut ), fromInternalBalance: false }) ); // Swap BAL/ETH BPT --> AURABAL uint256 balEthBptBalance = IERC20Upgradeable(BALETH_BPT).balanceOf( address(this) ); // Swap BAL/ETH BPT --> auraBal IBalancerVault.FundManagement memory fundManagement = IBalancerVault .FundManagement({ sender: address(this), fromInternalBalance: false, recipient: payable(address(this)), toInternalBalance: false }); IBalancerVault.SingleSwap memory singleSwap = IBalancerVault .SingleSwap({ poolId: AURABAL_BALETH_BPT_POOL_ID, kind: IBalancerVault.SwapKind.GIVEN_IN, assetIn: IAsset(address(BALETH_BPT)), assetOut: IAsset(address(AURABAL)), amount: balEthBptBalance, userData: new bytes(0) }); uint256 minOut = (balEthBptBalance * balEthBptToAuraBalMinOutBps) / MAX_BPS; auraBalEarned = BALANCER_VAULT.swap( singleSwap, fundManagement, minOut, type(uint256).max ); harvested[0].amount = auraBalEarned; } // AURA --> graviAURA uint256 auraBalance = AURA.balanceOf(address(this)); if (auraBalance > 0) { GRAVIAURA.deposit(auraBalance); uint256 graviAuraBalance = GRAVIAURA.balanceOf(address(this)); harvested[1].amount = graviAuraBalance; _processExtraToken(address(GRAVIAURA), graviAuraBalance); } // Report harvest _reportToVault(auraBalEarned); // Stake whatever is earned if (auraBalEarned > 0) { _deposit(auraBalEarned); } } // Example tend is a no-op which returns the values, could also just revert function _tend() internal override returns (TokenAmount[] memory tended) { revert("no op"); } /// @dev Return the balance (in want) that the strategy has invested somewhere function balanceOfPool() public view override returns (uint256) { // Change this to return the amount of want invested in another protocol return AURABAL_REWARDS.balanceOf(address(this)); } /// @dev Return the balance of rewards that the strategy has accrued /// @notice Used for offChain APY and Harvest Health monitoring function balanceOfRewards() external view override returns (TokenAmount[] memory rewards) { uint256 balEarned = AURABAL_REWARDS.earned(address(this)); rewards = new TokenAmount[](2); rewards[0] = TokenAmount(address(BAL), balEarned); rewards[1] = TokenAmount( address(AURA), getMintableAuraRewards(balEarned) ); } /// @notice Returns the expected amount of AURA to be minted given an amount of BAL rewards /// @dev ref: https://etherscan.io/address/0xc0c293ce456ff0ed870add98a0828dd4d2903dbf#code#F1#L86 function getMintableAuraRewards(uint256 _balAmount) public view returns (uint256 amount) { // NOTE: Only correct if AURA.minterMinted() == 0 // minterMinted is a private var in the contract, so we can't access it directly uint256 emissionsMinted = AURA.totalSupply() - AURA.INIT_MINT_AMOUNT(); uint256 cliff = emissionsMinted.div(AURA.reductionPerCliff()); uint256 totalCliffs = AURA.totalCliffs(); if (cliff < totalCliffs) { uint256 reduction = totalCliffs.sub(cliff).mul(5).div(2).add(700); amount = _balAmount.mul(reduction).div(totalCliffs); uint256 amtTillMax = AURA.EMISSIONS_MAX_SUPPLY().sub( emissionsMinted ); if (amount > amtTillMax) { amount = amtTillMax; } } } }
수정본
파일 열기
contract AuraBalStakerStrategy is BaseStrategy { using SafeMathUpgradeable for uint256; using SafeERC20Upgradeable for IERC20Upgradeable; bool public claimRewardsOnWithdrawAll; uint256 public balEthBptToAuraBalMinOutBps; IBaseRewardPool public constant AURABAL_REWARDS = IBaseRewardPool(0x5e5ea2048475854a5702F5B8468A51Ba1296EFcC); IVault public constant GRAVIAURA = IVault(0xBA485b556399123261a5F9c95d413B4f93107407); IBalancerVault public constant BALANCER_VAULT = IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8); IAuraToken public constant AURA = IAuraToken(0xC0c293ce456fF0ED870ADd98a0828Dd4d2903DBF); IERC20Upgradeable public constant AURABAL = IERC20Upgradeable(0x616e8BfA43F920657B3497DBf40D6b1A02D4608d); IERC20Upgradeable public constant WETH = IERC20Upgradeable(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); IERC20Upgradeable public constant BAL = IERC20Upgradeable(0xba100000625a3754423978a60c9317c58a424e3D); IERC20Upgradeable public constant BALETH_BPT = IERC20Upgradeable(0x5c6Ee304399DBdB9C8Ef030aB642B10820DB8F56); IERC20Upgradeable public constant BB_A_USD = IERC20Upgradeable(0x7B50775383d3D6f0215A8F290f2C9e2eEBBEceb2); bytes32 public constant BAL_ETH_POOL_ID = 0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014; bytes32 public constant AURABAL_BALETH_BPT_POOL_ID = 0x3dd0843a028c86e0b760b1a76929d1c5ef93a2dd000200000000000000000249; /// @dev Initialize the Strategy with security settings as well as tokens /// @notice Proxies will set any non constant variable you declare as default value /// @dev add any extra changeable variable at end of initializer as shown function initialize(address _vault) public initializer { require(IVault(_vault).token() == address(AURABAL)); __BaseStrategy_init(_vault); want = address(AURABAL); claimRewardsOnWithdrawAll = true; balEthBptToAuraBalMinOutBps = 9500; // max 5% slippage AURABAL.safeApprove(address(AURABAL_REWARDS), type(uint256).max); BAL.safeApprove(address(BALANCER_VAULT), type(uint256).max); BALETH_BPT.safeApprove(address(BALANCER_VAULT), type(uint256).max); AURA.approve(address(GRAVIAURA), type(uint256).max); } function setClaimRewardsOnWithdrawAll(bool _claimRewardsOnWithdrawAll) external { _onlyGovernanceOrStrategist(); claimRewardsOnWithdrawAll = _claimRewardsOnWithdrawAll; } function setBalEthBptToAuraBalMinOutBps(uint256 _minOutBps) external { _onlyGovernanceOrStrategist(); require(_minOutBps <= MAX_BPS, "Invalid minOutBps"); balEthBptToAuraBalMinOutBps = _minOutBps; } /// @dev Return the name of the strategy function getName() external pure override returns (string memory) { return "AuraBalStakerStrategy"; } /// @dev Return a list of protected tokens /// @notice It's very important all tokens that are meant to be in the strategy to be marked as protected /// @notice this provides security guarantees to the depositors they can't be sweeped away function getProtectedTokens() public view virtual override returns (address[] memory) { address[] memory protectedTokens = new address[](3); protectedTokens[0] = want; // AURABAL protectedTokens[1] = address(AURA); protectedTokens[2] = address(BAL); return protectedTokens; } /// @dev Deposit `_amount` of want, investing it to earn yield function _deposit(uint256 _amount) internal override { // Add code here to invest `_amount` of want to earn yield AURABAL_REWARDS.stake(_amount); } /// @dev Withdraw all funds, this is used for migrations, most of the time for emergency reasons function _withdrawAll() internal override { uint256 poolBalance = balanceOfPool(); if (poolBalance > 0) { AURABAL_REWARDS.withdrawAll(claimRewardsOnWithdrawAll); } } /// @dev Withdraw `_amount` of want, so that it can be sent to the vault / depositor /// @notice just unlock the funds and return the amount you could unlock function _withdrawSome(uint256 _amount) internal override returns (uint256) { uint256 wantBalance = balanceOfWant(); if (wantBalance < _amount) { uint256 toWithdraw = _amount.sub(wantBalance); AURABAL_REWARDS.withdraw(toWithdraw, false); } return MathUpgradeable.min(_amount, balanceOfWant()); } /// @dev Does this function require `tend` to be called? function _isTendable() internal pure override returns (bool) { return false; // Change to true if the strategy should be tended } function _harvest() internal override returns (TokenAmount[] memory harvested) { AURABAL_REWARDS.getReward(); // Rewards are handled like this: // BAL --> BAL/ETH BPT --> AURABAL (autocompounded) // AURA --> GRAVIAURA (emitted) // BB_A_USD --> Left in strategy to be sweeped later harvested = new TokenAmount[](2); harvested[0].token = address(AURABAL); harvested[1].token = address(GRAVIAURA); // BAL --> BAL/ETH BPT --> AURABAL uint256 balBalance = BAL.balanceOf(address(this)); uint256 auraBalEarned; if (balBalance > 0) { // Deposit BAL --> BAL/ETH BPT IAsset[] memory assets = new IAsset[](2); assets[0] = IAsset(address(BAL)); assets[1] = IAsset(address(WETH)); uint256[] memory maxAmountsIn = new uint256[](2); maxAmountsIn[0] = balBalance; maxAmountsIn[1] = 0; BALANCER_VAULT.joinPool( BAL_ETH_POOL_ID, address(this), address(this), IBalancerVault.JoinPoolRequest({ assets: assets, maxAmountsIn: maxAmountsIn, userData: abi.encode( JoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT, maxAmountsIn, 0 // minOut ), fromInternalBalance: false }) ); // Swap BAL/ETH BPT --> AURABAL uint256 balEthBptBalance = IERC20Upgradeable(BALETH_BPT).balanceOf( address(this) ); // Swap BAL/ETH BPT --> auraBal IBalancerVault.FundManagement memory fundManagement = IBalancerVault .FundManagement({ sender: address(this), fromInternalBalance: false, recipient: payable(address(this)), toInternalBalance: false }); IBalancerVault.SingleSwap memory singleSwap = IBalancerVault .SingleSwap({ poolId: AURABAL_BALETH_BPT_POOL_ID, kind: IBalancerVault.SwapKind.GIVEN_IN, assetIn: IAsset(address(BALETH_BPT)), assetOut: IAsset(address(AURABAL)), amount: balEthBptBalance, userData: new bytes(0) }); uint256 minOut = (balEthBptBalance * balEthBptToAuraBalMinOutBps) / MAX_BPS; auraBalEarned = BALANCER_VAULT.swap( singleSwap, fundManagement, minOut, type(uint256).max ); harvested[0].amount = auraBalEarned; } // AURA --> graviAURA uint256 auraBalance = AURA.balanceOf(address(this)); if (auraBalance > 0) { GRAVIAURA.deposit(auraBalance); uint256 graviAuraBalance = GRAVIAURA.balanceOf(address(this)); harvested[1].amount = graviAuraBalance; _processExtraToken(address(GRAVIAURA), graviAuraBalance); } // Report harvest _reportToVault(auraBalEarned); // Stake whatever is earned if (auraBalEarned > 0) { _deposit(auraBalEarned); } } // Example tend is a no-op which returns the values, could also just revert function _tend() internal override returns (TokenAmount[] memory tended) { revert("no op"); } /// @dev Return the balance (in want) that the strategy has invested somewhere function balanceOfPool() public view override returns (uint256) { // Change this to return the amount of want invested in another protocol return AURABAL_REWARDS.balanceOf(address(this)); } /// @dev Return the balance of rewards that the strategy has accrued /// @notice Used for offChain APY and Harvest Health monitoring function balanceOfRewards() external view override returns (TokenAmount[] memory rewards) { uint256 balEarned = AURABAL_REWARDS.earned(address(this)); rewards = new TokenAmount[](2); rewards[0] = TokenAmount(address(BAL), balEarned); rewards[1] = TokenAmount( address(AURA), getMintableAuraRewards(balEarned) ); } /// @notice Returns the expected amount of AURA to be minted given an amount of BAL rewards /// @dev ref: https://etherscan.io/address/0xc0c293ce456ff0ed870add98a0828dd4d2903dbf#code#F1#L86 function getMintableAuraRewards(uint256 _balAmount) public view returns (uint256 amount) { // NOTE: Only correct if AURA.minterMinted() == 0 // minterMinted is a private var in the contract, so we can't access it directly uint256 emissionsMinted = AURA.totalSupply() - AURA.INIT_MINT_AMOUNT(); uint256 cliff = emissionsMinted.div(AURA.reductionPerCliff()); uint256 totalCliffs = AURA.totalCliffs(); if (cliff < totalCliffs) { uint256 reduction = totalCliffs.sub(cliff).mul(5).div(2).add(700); amount = _balAmount.mul(reduction).div(totalCliffs); uint256 amtTillMax = AURA.EMISSIONS_MAX_SUPPLY().sub( emissionsMinted ); if (amount > amtTillMax) { amount = amtTillMax; } } } }
비교하기