-
Notifications
You must be signed in to change notification settings - Fork 103
Curve amm adapter #155
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
Curve amm adapter #155
Changes from all commits
387074a
af36e46
56ab821
26693ba
74b6e46
1a07717
7fe0ea2
31e4f51
661f544
3c45b14
04ac1fd
281164b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
pragma solidity 0.6.10; | ||
|
||
interface IMetaPoolZap { | ||
function add_liquidity( | ||
uint256[2] calldata _depositAmounts, | ||
uint256 _minMintAmount | ||
) external returns (uint256); | ||
|
||
function remove_liquidity( | ||
address _pool, | ||
uint256 _burnAmount, | ||
uint256[2] calldata _minAmounts | ||
) external; | ||
|
||
function remove_liquidity_one_coin( | ||
uint256 _burnAmount, | ||
int128 _i, | ||
uint256 _minAmount | ||
) external; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
/* | ||
Copyright 2021 Set Labs Inc. | ||
|
||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
|
||
SPDX-License-Identifier: Apache License, Version 2.0 | ||
*/ | ||
|
||
pragma solidity 0.6.10; | ||
|
||
interface IMetapoolFactory { | ||
//Will return [exoticStable, 3CRV] | ||
function get_coins(address _pool) external view returns (address[2] memory); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
/* | ||
Copyright 2021 Set Labs Inc. | ||
|
||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
|
||
SPDX-License-Identifier: Apache License, Version 2.0 | ||
*/ | ||
|
||
pragma solidity 0.6.10; | ||
|
||
interface ITriPoolZap { | ||
function add_liquidity( | ||
address _pool, | ||
uint256[4] calldata _depositAmounts, | ||
uint256 _minMintAmount, | ||
address _receiver | ||
) external returns (uint256); | ||
|
||
function remove_liquidity( | ||
address _pool, | ||
uint256 _burnAmount, | ||
uint256[4] calldata _minAmounts, | ||
address _receiver | ||
) external; | ||
|
||
function remove_liquidity_one_coin( | ||
address _pool, | ||
uint256 _burnAmount, | ||
int128 _i, | ||
uint256 _minAmount, | ||
address _receiver | ||
) external; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
/* | ||
Copyright 2021 Set Labs Inc. | ||
|
||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
|
||
http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
|
||
SPDX-License-Identifier: Apache License, Version 2.0 | ||
*/ | ||
|
||
pragma solidity 0.6.10; | ||
|
||
import { SafeCast } from "@openzeppelin/contracts/utils/SafeCast.sol"; | ||
|
||
import { IAmmAdapter } from "../../../interfaces/IAmmAdapter.sol"; | ||
import { IMetapoolFactory } from "../../../interfaces/external/IMetapoolFactory.sol"; | ||
import { IMetaPoolZap } from "../../../interfaces/external/IMetaPoolZap.sol"; | ||
|
||
contract CurveFactoryMetapoolAmmAdapter is IAmmAdapter { | ||
using SafeCast for uint256; | ||
using SafeCast for int256; | ||
|
||
IMetapoolFactory public metapoolFactory; | ||
|
||
string public constant ADD_LIQUIDITY = "add_liquidity(uint256[2],uint256,address)"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You probably want to use the ITriPoolZap functions rather than the IMetaPoolZap ones. This function requires depositing a combination of the 3Pool token and the extra token. The ITriPoolZap interface lets you deposit any of the 3Pool's pools' underlying tokens and the extra token. Makes it a lot more usable. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ncitron it turns out that this interface & adapter is specifically for the factory metapool and not the legacy metapools. Do you know if the TriPoolZap supports the factory metapools? we had issues with the TriPoolZap reverting without any reason string response, so we ended up switching focus to the factory metapool with the intention to revisit the Zap and legacy metapool implementation afterwards. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe they do. I had checked that it works on a whole list of pools. I think one of the gotchas on some of the different pools though is that the LP token address is different than the pool address though. I believe there is a contract for converting from LP token address to pool address and vice versa somewhere in the docs. I had previously confirmed that the zap contract works with the LUSD, alUSD, and USDN pools though (and IIRC alUSD is one of the legacy ones) |
||
string public constant REMOVE_LIQUIDITY = "remove_liquidity(uint256,uint256[2],address)"; | ||
string public constant REMOVE_LIQUIDITY_SINGLE = "remove_liquidity_one_coin(uint256,int128,uint256,address)"; | ||
|
||
constructor(IMetapoolFactory _metapoolFactory) public { | ||
metapoolFactory = _metapoolFactory; | ||
} | ||
|
||
function getProvideLiquidityCalldata( | ||
address _setToken, | ||
address _pool, | ||
address[] calldata _components, | ||
uint256[] calldata _maxTokensIn, | ||
uint256 _minLiquidity | ||
) | ||
external | ||
view | ||
override | ||
returns (address, uint256, bytes memory) | ||
{ | ||
require(_maxTokensIn[0] > 0 && _maxTokensIn[1] > 0, "tokens in must be nonzero"); | ||
uint256[2] memory inputAmounts = _convertUintArrayLiteral(_maxTokensIn); | ||
|
||
bytes memory callData = abi.encodeWithSignature( | ||
ADD_LIQUIDITY, | ||
inputAmounts, | ||
_minLiquidity, | ||
_setToken | ||
); | ||
|
||
return (_pool, 0, callData); | ||
} | ||
|
||
function getProvideLiquiditySingleAssetCalldata( | ||
address _setToken, | ||
address _pool, | ||
address _component, | ||
uint256 _maxTokenIn, | ||
uint256 _minLiquidity | ||
) | ||
external | ||
view | ||
override | ||
returns (address, uint256, bytes memory) | ||
{ | ||
require(_maxTokenIn > 0, "tokens in must be nonzero"); | ||
uint256 tokenIndex = _getTokenIndex(_pool, _component); | ||
|
||
uint256[2] memory inputAmounts; | ||
inputAmounts[tokenIndex] = _maxTokenIn; | ||
|
||
bytes memory callData = abi.encodeWithSignature( | ||
ADD_LIQUIDITY, | ||
inputAmounts, | ||
_minLiquidity, | ||
_setToken | ||
); | ||
|
||
return (_pool, 0, callData); | ||
} | ||
|
||
function getRemoveLiquidityCalldata( | ||
address _setToken, | ||
address _pool, | ||
address[] calldata _components, | ||
uint256[] calldata _minTokensOut, | ||
uint256 _liquidity | ||
) | ||
external | ||
view | ||
override | ||
returns (address, uint256, bytes memory) | ||
{ | ||
uint256[2] memory outputAmounts = _convertUintArrayLiteral(_minTokensOut); | ||
|
||
bytes memory callData = abi.encodeWithSignature( | ||
REMOVE_LIQUIDITY, | ||
_liquidity, | ||
outputAmounts, | ||
_setToken | ||
); | ||
|
||
return (_pool, 0, callData); | ||
} | ||
|
||
function getRemoveLiquiditySingleAssetCalldata( | ||
address _setToken, | ||
address _pool, | ||
address _component, | ||
uint256 _minTokenOut, | ||
uint256 _liquidity | ||
) | ||
external | ||
view | ||
override | ||
returns (address, uint256, bytes memory) | ||
{ | ||
int128 i = _getTokenIndex(_pool, _component).toInt256().toInt128(); | ||
|
||
bytes memory callData = abi.encodeWithSignature( | ||
REMOVE_LIQUIDITY_SINGLE, | ||
_liquidity, | ||
i, | ||
_minTokenOut, | ||
_setToken | ||
); | ||
|
||
return (_pool, 0, callData); | ||
} | ||
|
||
function getSpenderAddress(address _pool) external view override returns(address) { | ||
return _pool; | ||
} | ||
|
||
function isValidPool(address _pool, address[] memory _components) external view override returns(bool) { | ||
return _isValidPool(_pool, _components); | ||
} | ||
|
||
function _isValidPool(address _pool, address[] memory _components) internal view returns(bool) { | ||
address[2] memory expectedTokens = metapoolFactory.get_coins(_pool); | ||
|
||
for (uint256 i = 0; i < _components.length; i++) { | ||
if (!(_components[i] == expectedTokens[0] || _components[i] == expectedTokens[1])) { | ||
return false; | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
|
||
function _convertUintArrayLiteral(uint256[] memory _arr) internal pure returns (uint256[2] memory _literal) { | ||
for (uint256 i = 0; i < 2; i++) { | ||
_literal[i] = _arr[i]; | ||
} | ||
return _literal; | ||
} | ||
|
||
function _getTokenIndex(address _pool, address _token) internal view returns (uint256) { | ||
address[2] memory underlying = metapoolFactory.get_coins(_pool); | ||
for (uint256 i = 0; i < 2; i++) { | ||
if (underlying[i] == _token) return i; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
## Curve FactoryMetapool Amm Adapter | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is great context, lets put it in the javadocs in the adapter contract and remove this markdown file. We like to keep all the context in the contract itself. |
||
This PR contains a new amm adapter for curve and test for it. | ||
|
||
### What for? | ||
The CurveFactoryMetapoolAmmAdapter allows user to add or remove liquidity in curve metapools that were created with the metapoolFactory (0x0959158b6040D32d04c301A72CBFD6b39E21c9AE). An example for a pool like this is MIM-3CRV (0x5a6A4D54456819380173272A5E8E9B9904BdF41B). <b>Its important to notice that only the exotic stable coin (MIM) and 3CRV can be deposited or withdrawn.</b> Using dai/usdc/usdt would need some extra zap interaction. | ||
|
||
### Why only these pool? | ||
Curve has in large two types of metapools. Metapools that were created before the metapoolFactory and pools afterwards. They are quite different in terms of interfaces. Before the factory for example LP-Token were standalone ERC-20 contracts that didnt share the address with their pools. This alone validates some assumptions of the AmmModule. Additionally they do not allow to remove or add liquidity for a recipient. | ||
It might be possible to also create an adapter for older modules but its atleast far more complicated. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"contractName":"CurveAddressProvider", | ||
"abi":[{"name":"NewAddressIdentifier","inputs":[{"type":"uint256","name":"id","indexed":true},{"type":"address","name":"addr","indexed":false},{"type":"string","name":"description","indexed":false}],"anonymous":false,"type":"event"},{"name":"AddressModified","inputs":[{"type":"uint256","name":"id","indexed":true},{"type":"address","name":"new_address","indexed":false},{"type":"uint256","name":"version","indexed":false}],"anonymous":false,"type":"event"},{"name":"CommitNewAdmin","inputs":[{"type":"uint256","name":"deadline","indexed":true},{"type":"address","name":"admin","indexed":true}],"anonymous":false,"type":"event"},{"name":"NewAdmin","inputs":[{"type":"address","name":"admin","indexed":true}],"anonymous":false,"type":"event"},{"outputs":[],"inputs":[{"type":"address","name":"_admin"}],"stateMutability":"nonpayable","type":"constructor"},{"name":"get_registry","outputs":[{"type":"address","name":""}],"inputs":[],"stateMutability":"view","type":"function","gas":"1061"},{"name":"max_id","outputs":[{"type":"uint256","name":""}],"inputs":[],"stateMutability":"view","type":"function","gas":"1258"},{"name":"get_address","outputs":[{"type":"address","name":""}],"inputs":[{"type":"uint256","name":"_id"}],"stateMutability":"view","type":"function","gas":"1308"},{"name":"add_new_id","outputs":[{"type":"uint256","name":""}],"inputs":[{"type":"address","name":"_address"},{"type":"string","name":"_description"}],"stateMutability":"nonpayable","type":"function","gas":"291275"},{"name":"set_address","outputs":[{"type":"bool","name":""}],"inputs":[{"type":"uint256","name":"_id"},{"type":"address","name":"_address"}],"stateMutability":"nonpayable","type":"function","gas":"182430"},{"name":"unset_address","outputs":[{"type":"bool","name":""}],"inputs":[{"type":"uint256","name":"_id"}],"stateMutability":"nonpayable","type":"function","gas":"101348"},{"name":"commit_transfer_ownership","outputs":[{"type":"bool","name":""}],"inputs":[{"type":"address","name":"_new_admin"}],"stateMutability":"nonpayable","type":"function","gas":"74048"},{"name":"apply_transfer_ownership","outputs":[{"type":"bool","name":""}],"inputs":[],"stateMutability":"nonpayable","type":"function","gas":"60125"},{"name":"revert_transfer_ownership","outputs":[{"type":"bool","name":""}],"inputs":[],"stateMutability":"nonpayable","type":"function","gas":"21400"},{"name":"admin","outputs":[{"type":"address","name":""}],"inputs":[],"stateMutability":"view","type":"function","gas":"1331"},{"name":"transfer_ownership_deadline","outputs":[{"type":"uint256","name":""}],"inputs":[],"stateMutability":"view","type":"function","gas":"1361"},{"name":"future_admin","outputs":[{"type":"address","name":""}],"inputs":[],"stateMutability":"view","type":"function","gas":"1391"},{"name":"get_id_info","outputs":[{"type":"address","name":"addr"},{"type":"bool","name":"is_active"},{"type":"uint256","name":"version"},{"type":"uint256","name":"last_modified"},{"type":"string","name":"description"}],"inputs":[{"type":"uint256","name":"arg0"}],"stateMutability":"view","type":"function","gas":"12168"}],"bytecode":"602061089561014039602061089560c03960c05160a01c1561002057600080fd5b610140516001556001600455600d610160527f4d61696e20526567697374727900000000000000000000000000000000000000610180526101608060046005600060e05260c052604060c02060c052602060c0200160c052602060c020602082510161012060006002818352015b826101205160200211156100a1576100c3565b61012051602002850151610120518501555b815160010180835281141561008e575b50505050505061087d56341561000a57600080fd5b6004361015610018576107a9565b600035601c5263a262904b600051141561003a5760005460005260206000f350005b630c6d784f600051141561006c5760045460018082101561005a57600080fd5b8082039050905060005260206000f350005b63493f4f74600051141561009e57600560043560e05260c052604060c02060c052602060c0205460005260206000f350005b63168f957960005114156102735760043560a01c156100bc57600080fd5b60606024356004016101403760406024356004013511156100dc57600080fd5b60015433146100ea57600080fd5b60006004353b116100fa57600080fd5b6004546101c05260056101c05160e05260c052604060c02060c052602060c02060043581556001600182015560016002820155426003820155610140806004830160c052602060c020602082510161012060006003818352015b8261012051602002111561016757610189565b61012051602002850151610120518501555b8151600101808352811415610154575b505050505050506101c05160018181830110156101a557600080fd5b808201905090506004556004356102205260406101e0526101e051610240526101408051602001806101e051610220018284600060045af16101e657600080fd5b50506101e05161022001518060206101e051610220010101818260206001820306601f820103905003368237505060206101e051610220015160206001820306601f82010390506101e05101016101e0526101c0517f5b0f9b31dc08c19adcc0181c1b97ad54a84487faf0a4fdcb88c8681724298af96101e051610220a26101c05160005260206000f350005b636a84cad060005114156103c45760243560a01c1561029157600080fd5b600154331461029f57600080fd5b60006024353b116102af57600080fd5b600435600454116102bf57600080fd5b6002600560043560e05260c052604060c02060c052602060c020015460018181830110156102ec57600080fd5b8082019050905061014052602435600560043560e05260c052604060c02060c052602060c0205560016001600560043560e05260c052604060c02060c052602060c0200155610140516002600560043560e05260c052604060c02060c052602060c0200155426003600560043560e05260c052604060c02060c052602060c0200155600435151561037e576024356000555b6024356101605261014051610180526004357fe7a6334c4f573efdf292d404d59adacec345f4f7c76495a034008edda0acef476040610160a2600160005260206000f350005b635eec0daa60005114156104c75760015433146103e057600080fd5b6001600560043560e05260c052604060c02060c052602060c020015461040557600080fd5b60006001600560043560e05260c052604060c02060c052602060c02001556000600560043560e05260c052604060c02060c052602060c02055426003600560043560e05260c052604060c02060c052602060c0200155600435151561046a5760006000555b6000610140526002600560043560e05260c052604060c02060c052602060c0200154610160526004357fe7a6334c4f573efdf292d404d59adacec345f4f7c76495a034008edda0acef476040610140a2600160005260206000f350005b636b441a4060005114156105665760043560a01c156104e557600080fd5b60015433146104f357600080fd5b6002541561050057600080fd5b426203f48081818301101561051457600080fd5b808201905090506101405261014051600255600435600355600435610140517f181aa3aa17d4cbf99265dd4443eba009433d3cde79d60164fde1d1a192beb93560006000a3600160005260206000f350005b636a1c05ae60005114156105ea57600154331461058257600080fd5b60006002541861059157600080fd5b6002544210156105a057600080fd5b60035461014052610140516001556000600255610140517f71614071b88dee5e0b2ae578a9dd7b2ebbe9ae832ba419dc0242cd065a290b6c60006000a2600160005260206000f350005b6386fbf193600051141561061857600154331461060657600080fd5b6000600255600160005260206000f350005b63f851a44060005114156106345760015460005260206000f350005b63e0a0b58660005114156106505760025460005260206000f350005b6317f7182a600051141561066c5760035460005260206000f350005b6392668ecb60005114156107a857600560043560e05260c052604060c0206101408060a081808560c052602060c0205481525050602082019150818060018660c052602060c020015481525050602082019150818060028660c052602060c020015481525050602082019150818060038660c052602060c0200154815250506020820191508082528083018060048660c052602060c020018060c052602060c02082602082540161012060006003818352015b8261012051602002111561073257610754565b61012051850154610120516020028501525b815160010180835281141561071f575b5050505050508051806020830101818260206001820306601f8201039050033682375050805160200160206001820306601f820103905090509050810190508090509050905060c05260c051610140f39050005b5b60006000fd5b6100ce61087d036100ce6000396100ce61087d036000f30000000000000000000000007eeac6cddbd1d0b8af061742d41877d7f707289a", | ||
"deployedBytecode":"", | ||
"linkReferences":{}, | ||
"deployedLinkReferences":{} | ||
} |
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{"contractName":"MetapoolFactory", "abi":[{"name":"get_coins","outputs":[{"type":"address[2]","name":""}],"inputs":[{"type":"address","name":"_pool"}],"stateMutability":"view","type":"function","gas":"2427"}]} |
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 we can add some of the javadocs and section headers you see here for consistency.