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

[New Operator] - Beefy LP Vault #114

Merged
merged 16 commits into from
May 18, 2022
Merged

[New Operator] - Beefy LP Vault #114

merged 16 commits into from
May 18, 2022

Conversation

maximebrugel
Copy link
Contributor

Context

On the Beefy app, we can find two types of vaults : LPs and Single Assets
image

The first Beefy Operator is focused on Single Asset Vaults, see PR : #107.
But now, we need a new operator for the LPs vaults.

How the operator works :

There are two steps :

  • Zapping
  • Interacting with Beefy

The protocols takes one of the paired tokens.
image
For example, in the BTCB-ETH LP vault, we can deposit BTCB or ETH and withdraw BTCB or ETH. The users is not managing Beefy vault tokens or LP Tokens.

Deposit (steps)

  • Input => Token from the pair
  • Swap half of the token for the second paired token.
  • Add liquidity to get the LP Token.
  • Deposit the token in the Beefy Vault.
  • Output => Beefy Vault token (shares).

Withdraw (steps)

  • Input => Beefy Vault token (shares)
  • Withdraw from beefy and receive the LP.
  • Remove liquidity (will burn the LP) and receive the paired tokens.
  • Swap one of the tokens for the chosen one.
  • Output => Token from the pair

You can get more details in the README.md file for the price calculation (operator folder).

1 Operator = 1 Router Type

Since that the operator is interacting directly with the AMM router to swap and add/remove liquidity, we need to be aware of the router interface. Some AMMs does not have the same interface.
We created an operator for Biswap (where one function needed the fee parameter) and one for the classic UniswapV2 router.

One Operator will list all the supported routers with the same interface. If we need an other router interface, we need an other operator. Supporting Biswap and UniswapV2, will cover a lot of beefy vaults.

@maximebrugel maximebrugel added the To review Let people know this PR is ready for a review label May 4, 2022
Copy link
Contributor

@adrien-supizet adrien-supizet left a comment

Choose a reason for hiding this comment

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

1st review:

Left to review:

  • private functions from contracts/operators/Beefy/BeefyZapBiswapLPVaultOperator.sol in details
  • Unit tests
  • Optimal amount computation

// Deploy ParaswapOperator
const paraswapOperator = await paraswapOperatorFactory.deploy(tokenTransferProxy, augustusSwapper);
await paraswapOperator.deployed();
console.log
console.log;
Copy link
Contributor

Choose a reason for hiding this comment

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

💅

Comment on lines 14 to 15
/// Note: "Zap" means that we are converting an asset for the LP Token by
/// swapping and adding liquidity.
Copy link
Contributor

Choose a reason for hiding this comment

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

Who is we?
💅 It's better to phrase comments with impersonal type as the deployed contracts are standalone pieces of code. We just happen to deploy and maintain them.

Suggested change
/// Note: "Zap" means that we are converting an asset for the LP Token by
/// swapping and adding liquidity.
/// Note: "Zapping" means that the asset is converted for the LP Token by
/// swapping and adding liquidity.

Comment on lines 11 to 12
/// @param underlying The underlying token address or zapper
event VaultAdded(address vault, address underlying);
Copy link
Contributor

Choose a reason for hiding this comment

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

💅 underlying is not explicit enough imo
I even prefer the explicit namingtokenOrZapper if there's no other way to describe the variable


To solve the problem of remaining dust after adding liquidity in a uniswap like liquidity pool, we need to perform a calculation to minimize the amount of unused token by the uniswap like router.

> For this exemple, we will use an WBNB-USDT Uniswap luqidity pool, and we have 1 WBNB as a total investment.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
> For this exemple, we will use an WBNB-USDT Uniswap luqidity pool, and we have 1 WBNB as a total investment.
> For this example, we will use a WBNB-USDT Uniswap liquidity pool, and we have 1 WBNB as a total investment.


## Optimal swap amount

To solve the problem of remaining dust after adding liquidity in a uniswap like liquidity pool, we need to perform a calculation to minimize the amount of unused token by the uniswap like router.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
To solve the problem of remaining dust after adding liquidity in a uniswap like liquidity pool, we need to perform a calculation to minimize the amount of unused token by the uniswap like router.
To solve the problem of remaining dust after adding liquidity in a Uniswap-like liquidity pool, we need to perform a calculation to minimize the amount of unused tokens by the Uniswap-like router.

After the swap, the ratio between **A** and **B** in the liquidity pool is:
$${A+s \over B-b} = {a-s \over b}$$

From this equation we can solve for s as using [b espression](#find-b) as follow:
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
From this equation we can solve for s as using [b espression](#find-b) as follow:
From this equation we can solve for s using [b espression](#find-b) as follow:


b = B(1 - f)s / (A + (1 - f)s)

##### Replace b by his value expressed in terms of s
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
##### Replace b by his value expressed in terms of s
##### Replace b with the value expressed in terms of s


import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";

interface IBiswapRouter02 is IUniswapV2Router02 {
Copy link
Contributor

Choose a reason for hiding this comment

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

Since IBiswapRouter02 is IUniswapV2Router02:

I believe that the functions getAmountOut and getAmountIn are overloaded, not overridden. This would allow us to use IBiswapRouter02 instead of IUniswapV2Router02 in the uniswapLPVaultOperator and allow factorization of the two contracts, for better legibility and maintenance

The biswapLPVaultOperator would use the same code as uniswapLpvault but override the only two functions whose implementation is different.

@adrien-supizet adrien-supizet removed the To review Let people know this PR is ready for a review label May 11, 2022
}

/// @notice Perform a vault token withdraw (moo) from Beefy, and
/// transfer the rest as one of the paired token.abi
Copy link
Contributor

Choose a reason for hiding this comment

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

token.abi? Not sure I get what you mean here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Remove the .abi

uint256 amount,
address token
) private {
IBiswapRouter02 biswapRouter = IBiswapRouter02(router);
Copy link
Contributor

Choose a reason for hiding this comment

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

🔧 This can be declared further down in the scope to save gas fees if the execution reverts

// LP Tokens needs to be sent back to the pair address to be burned
IERC20(pair).safeTransfer(pair, IERC20(pair).balanceOf(address(this)));

// We are removing liquidity by burning the LP Token and not
Copy link
Contributor

Choose a reason for hiding this comment

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

💅 Same comment: refrain from using "we" which does not translate well in English in this context

require(token0 == token || token1 == token, "BLVO: INVALID_TOKEN");

// LP Tokens needs to be sent back to the pair address to be burned
IERC20(pair).safeTransfer(pair, IERC20(pair).balanceOf(address(this)));
Copy link
Contributor

Choose a reason for hiding this comment

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

❔ Not sure I understand why balanceOf is chained after the transfer

Copy link
Contributor Author

@maximebrugel maximebrugel May 12, 2022

Choose a reason for hiding this comment

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

We are transferring the received LP Tokens to the LP Token address to burn the LP tokens... This mechanism a little bit weird, but correct. However we must compute the balance before/after withdrawing to send the right amount, i'm fixing this issue.


/// @dev Zap one of the paired tokens for the LP Token, deposit the
/// asset in the Beefy vault and receive the vault token (moo)
/// @param router The uniswap v2 router address to use for swaping and adding liquidity
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// @param router The uniswap v2 router address to use for swaping and adding liquidity
/// @param router The Uniswap v2 router address to use for swapping and adding liquidity

1,
path,
address(this),
block.timestamp
Copy link
Contributor

Choose a reason for hiding this comment

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

❔ Why is the interface expecting the block timestamp for a swap? Please enlighten me, I am not very familiar with the Biswap router

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You can specify the unix timestamp after which the transaction will revert (for e.g I want to execute my transaction and i need my tokens to be swapped in 1 min max).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

image

Comment on lines 257 to 260
/// @dev Calculate the optimal amount of tokenA to swap in order
/// to obtain the same market value of tokenB after the trade
/// in order to add as many tokensA and tokensB as possible
/// to the liquidity so that as few as possible remain.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// @dev Calculate the optimal amount of tokenA to swap in order
/// to obtain the same market value of tokenB after the trade
/// in order to add as many tokensA and tokensB as possible
/// to the liquidity so that as few as possible remain.
/// @dev Calculate the optimal amount of tokenA to swap to obtain
/// the same market value of tokenB after the trade.
/// This allows to add as many tokensA and tokensB as possible
/// to the liquidity to minimize the remaining amount.

@maximebrugel maximebrugel added the Fix and merge Minor changes required label May 12, 2022
@maximebrugel maximebrugel merged commit 3191665 into master May 18, 2022
@maximebrugel maximebrugel deleted the beefy-lp-operator branch May 18, 2022 07:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Fix and merge Minor changes required
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants