-
Notifications
You must be signed in to change notification settings - Fork 94
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
CurveAmmAdapter #259
base: master
Are you sure you want to change the base?
CurveAmmAdapter #259
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you also add to the PR description with all the curve contracts that you've looked at in the process of writing this contract, and your approach + various design decisions you've made and why you've made them?
poolMinter = _poolMinter; | ||
isCurveV1 = _isCurveV1; | ||
coinCount = _coinCount; | ||
for (uint256 i = 0 ; i < _coinCount ; ++i) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to execute these functions without storing coins and coinIndex?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this coins and coinIndex is to used to check if component token is valid. without storing coins and coinIndex, it will require more gas each time trying to get calldata.
) public { | ||
poolToken = _poolToken; | ||
poolMinter = _poolMinter; | ||
isCurveV1 = _isCurveV1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way to ensure that the contract passed in is curveV1 or curveV2?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is no proper way to check the interface
I've only looked closely at the contract so far, leaving a more detailed look to the tests later |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks really good - just a few more comments on the tests and some validation checks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm after fixing the comments on the tests
params: [ | ||
{ | ||
forking: { | ||
jsonRpcUrl: `https://eth-mainnet.alchemyapi.io/v2/${process.env.ALCHEMY_TOKEN}`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if it's mainnet - isn't hardhat already set up for forking?
} | ||
|
||
const expectedNewLpTokens = | ||
coinCount === 2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use a switch statement here for readability
// `calc_token_amount` of `USDT/WBTC/WETH` pool return correct amount out for add_liquidity, but it doesn't return for `MIM/3CRV` pools. | ||
// there is some external logic for fees, here we test actual output and expected output with 0.1% slippage | ||
expectCloseTo( | ||
(await poolToken.balanceOf(setToken.address)).sub(lpBalanceBefore), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extract this out to separate variable
let ammModule: AmmModule; | ||
|
||
before(async () => { | ||
await network.provider.request({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is unit test, shouldn't need these hardhat fork setup
There are a few things to consider about the CurveAmmAdapter.
All curve pools have 2 ~ 4 component coins.
CVXETH - 0x3A283D9c08E8b55966afb64C515f5143cf907611 (two tokens - WETH/CVX)
TRICRYPTO - 0xc4AD29ba4B3c580e6D59105FFf484999997675Ff (three tokens - USDT/WBTC/WETH)
There is no generalized query function to get component coin count of the pool.
To support different number of component coins, the CurveAmmAdapter will accept
coinCount
in constructor.Each curve pool has token contract (which has IERC20 standard interface) and minter contract (which has addLiquidity / removeLiquidity functionality).
Example curve pool with different token/minter contract address
CVXETH token contract - 0x3A283D9c08E8b55966afb64C515f5143cf907611
CVXETH minter contract - 0xb576491f1e6e5e62f1d8f26062ee822b40b0e0d4
Example curve pool with same token/minter contract address
MIM token contract - 0x5a6A4D54456819380173272A5E8E9B9904BdF41B
MIM minter contract - 0x5a6A4D54456819380173272A5E8E9B9904BdF41B
Since some has the same token/minter contract and some has different token/minter contract, the CurveAmmAdapter will accept
poolToken
andpoolMinter
contract addresses in constructor.The curve pools support
removeLiquidityOneCoin
functionality, but some pools useint128
for the coin index, some pools useuint256
for the coin index.MIM token contract - 0x5a6A4D54456819380173272A5E8E9B9904BdF41B (use
int128
for the coin index)TRICRYPTO - 0xc4AD29ba4B3c580e6D59105FFf484999997675Ff (use
uint256
for the coin index)To solve this problem, the CurveAmmAdapter will accept a flag that tells if the pool uses
int128
oruint256
for coin index.The curve pools has addLiquidity and removeLiquidity functionality but it doesn't accept any recipient address. So we can't specify setToken address in the calldata.
This is fixed by introducing
addLiquidity
,removeLiquidity
andremoveLiquidityOneCoin
wrapper functions in CurveAmmAdapter contract.E.g. addLiquidity wrapper function will accept the recipient address. It will get component tokens from msg.sender, add liquidity & mint pool tokens, transfer pool tokens back to recipient address.