Skip to content
Open
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
101 changes: 52 additions & 49 deletions contracts/Strategy2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@ contract Strategy is BaseStrategy {
address public constant LooksRareStaking = 0xBcD7254A1D759EFA08eC7c3291B2E85c5dCC12ce;

//$LOOKS token
address public constant LOOKSToken = 0xf4d2888d29D722226FafA5d9B24F9164c092421E;
IERC20 public constant LOOKSToken = IERC20(0xf4d2888d29D722226FafA5d9B24F9164c092421E);

//$WETH as I don't think we get actual ETH
address public constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
IERC20 public constant WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);

//hard code univ2 router
IUniswapV2Router02 public constant univ2router = IUniswapV2Router02(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
Expand All @@ -78,6 +78,9 @@ contract Strategy is BaseStrategy {
//max for 256-1 on approve
uint256 public constant max = type(uint256).max;

//Base amount for floor $LOOKS token
uint256 private constant base = 1e18;

constructor(address _vault) public BaseStrategy(_vault) {
// You can set these parameters on deployment to whatever you want
// maxReportDelay = 6300;
Expand Down Expand Up @@ -117,19 +120,28 @@ contract Strategy is BaseStrategy {
return balanceOfWant().add(currentShareValue);
}

function withdrawProfitOrFloor(uint profit) internal {
function withdrawProfitOrFloor(uint profit, uint256 debtOutstanding) internal {
//Ensure we're not going to attempt to sub from unneeded balance
uint256 sharePrice = _sharePrice();
uint256 amount = 0;
if(profit > balanceOfWant()){
uint256 amount = profit.sub(balanceOfWant());
amount = profit.sub(balanceOfWant());
}
if(amount > 0){
amount = amount.mul(10 ** IDecimals(address(want)).decimals()).div(sharePrice);
}
uint256 base = 1e18;
//Check that our staked amount is greater than 1 token which is min
//This way we do not attempt to withdraw 0 (which reverts)
if(_stakedAmount() >= base){
uint256 convertedBase = base.mul(10 ** IDecimals(address(want)).decimals()).div(sharePrice);
if(debtOutstanding > 0){
debtOutstanding = debtOutstanding.mul(10 ** IDecimals(address(want)).decimals()).div(sharePrice);
}
(uint256 shares, , ) = looksRareContract.userInfo(address(this));
uint256 upperlimit = Math.max(debtOutstanding,shares);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be more than what it is staked in the LOOKS contract. If debtOutstanding > shares you are going to do a withdraw on line 144 of looks that you don't have.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes this should min(). I need the largest number I have, which ironically is min. Good catch.

//Withdraw the larger of the needed amount or of the base number.
//In the case where we only need 0.5 LOOKS to meet our balance obligations we round up to 1 LOOKS to meet the withdraw floor.
looksRareContract.withdraw(Math.max(amount,base),false);
looksRareContract.withdraw(Math.max(amount,Math.max(convertedBase,upperlimit)),false);
}
}

Expand Down Expand Up @@ -173,14 +185,6 @@ event withdrawPreReportEvent(uint256 _amount);
swapAmount = _sellRewards();
}

//calculate the amount of LOOKS we need, cannot be less than 1 due to contract restrictions.
if(_debtOutstanding <= 1e18 && balanceOfWant() < _debtOutstanding){
uint256 sharePrice = _sharePrice();
uint256 withdrawPreReport = _debtOutstanding.mul(10 ** IDecimals(address(want)).decimals()).div(sharePrice);
//Withdraw any extra needed LOOKS > 1. Do not claim rewards as they were already claimed so we can save gas here.
looksRareContract.withdraw(withdrawPreReport,false);
}

//Get current total LOOKS value of our share position
uint256 currentShareValue = _stakedAmount();
emit prepareReturnSharesValueEvent(currentShareValue);
Expand All @@ -196,56 +200,43 @@ event withdrawPreReportEvent(uint256 _amount);
//Invoke funciton to withdraw the difference between profit and gain in the LOOKS token
//This is because in Yearn Vaults line #1746 enforces a check that the balance must be greater than gain + debtpayment
//We must calculate the growth in staked LOOKS otherwise it isn't ever going to get claimed, and we must withdraw it to pass this revert check.
withdrawProfitOrFloor(finalProfit);
withdrawProfitOrFloor(finalProfit, _debtOutstanding);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't it better to call liquidatePosition(finalProfit) ?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could, but the liquidatePosition(finalProfit) assumes the goal is to liquidate 'X' and that 'X' is already greater than the floor price.

It also means that if profit was 0, we wouldn't be attempting to withdraw debtOutstanding at all?


//If there is no debt outstanding, deposit all looks back into contract and report back profit
emit debtOutstandingEvent(_debtOutstanding);
if(_debtOutstanding == 0){
return(finalProfit,0,0);
if(finalProfit < _debtOutstanding){
_profit = 0;
_debtPayment = balanceOfWant();
_loss = _debtOutstanding.sub(_debtPayment);
} else {
//If there is debt but our balance exceeds the debt
if(balanceOfWant() > _debtOutstanding){
return(finalProfit,0,_debtOutstanding);
} else {
//Otherwise report back the balance all as debt payment
return(finalProfit,0,balanceOfWant());
}
_profit = finalProfit.sub(_debtOutstanding);
_debtPayment = _debtOutstanding;
}
}

event toDepositEvent(uint256 _amount);
event toDepositFloorEvent(uint256 _amount);
function adjustPosition(uint256 _debtOutstanding) internal override {
// TODO: Do something to invest excess `want` tokens (from the Vault) into your positions
// NOTE: Try to adjust positions so that `_debtOutstanding` can be freed up on *next* harvest (not immediately)

//no need for us to use debt as we can always pull it out. This is just here to silence warnings in compiler of unused var.
uint256 debt = _debtOutstanding;

//Figure out how much can be deposited
uint256 toDeposit = want.balanceOf(address(this));
emit toDepositEvent(toDeposit);

//set deposit floor as the looks contract doesn't allow staking below 1 LOOKS
uint256 depositFloor = 1 * 10 ** 18;
emit toDepositFloorEvent(depositFloor);
uint256 looseWant = balanceOfWant();

//Deposit without claim as we'll claim rewards only on a prepare to better track.
if(toDeposit > depositFloor){
looksRareContract.deposit(toDeposit,false);
if(looseWant > base){
looksRareContract.deposit(looseWant,false);
}
}

function firstTimeApprovals() internal {
//check if tokens are approved as needed.
if(IERC20(LOOKSToken).allowance(address(this),LooksRareStaking) == 0){
IERC20(LOOKSToken).approve(LooksRareStaking, max);
if(LOOKSToken.allowance(address(this),LooksRareStaking) == 0){
LOOKSToken.approve(LooksRareStaking, max);
}
if(IERC20(LOOKSToken).allowance(address(this),address(univ2router)) == 0){
IERC20(LOOKSToken).approve(address(univ2router), max);
if(LOOKSToken.allowance(address(this),address(univ2router)) == 0){
LOOKSToken.approve(address(univ2router), max);
}
if(IERC20(WETH).allowance(address(this),address(univ2router)) == 0){
IERC20(WETH).approve(address(univ2router), max);
if(WETH.allowance(address(this),address(univ2router)) == 0){
WETH.approve(address(univ2router), max);
}

}
Expand All @@ -268,7 +259,7 @@ event postWithdrawBalanceEvent(uint256 _amount);
// NOTE: Maintain invariant `_liquidatedAmount + _loss <= _amountNeeded`

//Sanitycheck that amount needed is not 0
if(_amountNeeded != 0){
if(_amountNeeded > 0){
uint256 unusedBalance = balanceOfWant();

if(unusedBalance < _amountNeeded){
Expand Down Expand Up @@ -300,7 +291,7 @@ event postWithdrawBalanceEvent(uint256 _amount);
}

//We now check the total assets amount we have post withdraw.
uint256 totalAssets = want.balanceOf(address(this));
uint256 totalAssets = balanceOfWant();
emit postWithdrawBalanceEvent(totalAssets);

//If the amount we need is more than the total amount of assets we have.
Expand All @@ -320,8 +311,8 @@ event postWithdrawBalanceEvent(uint256 _amount);
function liquidateAllPositions() internal override returns (uint256) {
// TODO: Liquidate all positions and return the amount freed.

//We then withdraw with claim to get the full balance including the latest rewards
looksRareContract.withdrawAll(true);
//Full withdraw excluding rewards incase of reward errors.
looksRareContract.withdrawAll(false);

//We do a check to see if we have any WETH from the last rewards set that isn't converted.
uint256 wethCheck = IERC20(WETH).balanceOf(address(this));
Expand All @@ -332,7 +323,7 @@ event postWithdrawBalanceEvent(uint256 _amount);
}

//Now that all possible rewards are fully in LOOKS and in this address, we return the balance expressed in LOOKS
return want.balanceOf(address(this));
return balanceOfWant();
}


Expand All @@ -352,7 +343,19 @@ event postWithdrawBalanceEvent(uint256 _amount);
function prepareMigration(address _newStrategy) internal override {
// TODO: Transfer any non-`want` tokens to the new strategy
// NOTE: `migrate` will automatically forward all `want` in this strategy to the new one
liquidateAllPositions();

//Full withdraw including rewards as this is not an emergency liquidation
looksRareContract.withdrawAll(false);

//We do a check to see if we have any WETH from the last rewards set that isn't converted.
uint256 wethCheck = IERC20(WETH).balanceOf(address(this));

//If the WETH balance is non-zero we'll initiate a swap.
if(wethCheck != 0){
WETH.safeTransferFrom(address(this), address(_newStrategy), wethCheck);
}


}

// Override this to add all tokens/tokenized positions this contract manages
Expand Down