Skip to content

Commit 36f9257

Browse files
committed
Merge remote-tracking branch 'edward_set2/zishan/uniswapv3-amm-adapter' into feature/uniswapv3-amm-adapter
2 parents 78bbd9f + 206a476 commit 36f9257

File tree

14 files changed

+3443
-1
lines changed

14 files changed

+3443
-1
lines changed
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// SPDX-License-Identifier: GPL-3.0
2+
3+
/*
4+
Copyright 2022 Set Labs Inc.
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
SPDX-License-Identifier: Apache License, Version 2.0
15+
*/
16+
17+
pragma solidity 0.6.10;
18+
19+
import {IERC20} from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
20+
21+
import {
22+
IUniswapV3Pool
23+
} from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
24+
25+
interface IArrakisVaultV1 {
26+
function mint(uint256 mintAmount, address receiver)
27+
external
28+
returns (
29+
uint256 amount0,
30+
uint256 amount1,
31+
uint128 liquidityMinted
32+
);
33+
34+
function burn(uint256 burnAmount, address receiver)
35+
external
36+
returns (
37+
uint256 amount0,
38+
uint256 amount1,
39+
uint128 liquidityBurned
40+
);
41+
42+
function getMintAmounts(uint256 amount0Max, uint256 amount1Max)
43+
external
44+
view
45+
returns (
46+
uint256 amount0,
47+
uint256 amount1,
48+
uint256 mintAmount
49+
);
50+
51+
function getUnderlyingBalances()
52+
external
53+
view
54+
returns (uint256 amount0, uint256 amount1);
55+
56+
function getUnderlyingBalancesAtPrice(uint160 sqrtRatioX96)
57+
external
58+
view
59+
returns (uint256 amount0Current, uint256 amount1Current);
60+
61+
function getPositionID() external view returns (bytes32 positionID);
62+
63+
function token0() external view returns (IERC20);
64+
65+
function token1() external view returns (IERC20);
66+
67+
function upperTick() external view returns (int24);
68+
69+
function lowerTick() external view returns (int24);
70+
71+
function pool() external view returns (IUniswapV3Pool);
72+
73+
function totalSupply() external view returns (uint256);
74+
75+
function balanceOf(address account) external view returns (uint256);
76+
77+
function executiveRebalance(
78+
int24 newLowerTick,
79+
int24 newUpperTick,
80+
uint160 swapThresholdPrice,
81+
uint256 swapAmountBPS,
82+
bool zeroForOne
83+
) external;
84+
85+
function withdrawManagerBalance() external;
86+
}
Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
/*
2+
Copyright 2022 Set Labs Inc.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
16+
SPDX-License-Identifier: Apache License, Version 2.0
17+
*/
18+
19+
pragma solidity 0.6.10;
20+
21+
import "@openzeppelin/contracts/math/SafeMath.sol";
22+
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
23+
import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
24+
import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol";
25+
26+
import "../../../interfaces/IAmmAdapter.sol";
27+
import "../../../interfaces/external/IArrakisVaultV1.sol";
28+
29+
/**
30+
* @title UniswapV3AmmAdapter
31+
* @author Zishan Sami
32+
*
33+
* Adapter for Arrakis Vault representing Uniswap V3 liquidity position that encodes adding and removing liquidty
34+
*/
35+
contract ArrakisUniswapV3AmmAdapter is IAmmAdapter {
36+
using SafeMath for uint256;
37+
38+
/* ============ State Variables ============ */
39+
40+
// Address of Arrakis Router contract
41+
address public immutable router;
42+
43+
// UniswapV3 factory contract
44+
IUniswapV3Factory public immutable uniV3Factory;
45+
46+
// Internal function string for adding liquidity
47+
string internal constant ADD_LIQUIDITY =
48+
"addLiquidity(address,uint256,uint256,uint256,uint256,address)";
49+
// Internal function string for removing liquidity
50+
string internal constant REMOVE_LIQUIDITY =
51+
"removeLiquidity(address,uint256,uint256,uint256,address)";
52+
53+
/* ============ Constructor ============ */
54+
55+
/**
56+
* Set state variables
57+
*
58+
* @param _router Address of Arrakis Router contract
59+
* @param _uniV3Factory Address of UniswapV3 Factory contract
60+
*/
61+
constructor(address _router, address _uniV3Factory) public {
62+
require(_router != address(0),"_router address must not be zero address");
63+
require(_uniV3Factory != address(0),"_uniV3Factory address must not be zero address");
64+
router = _router;
65+
uniV3Factory = IUniswapV3Factory(_uniV3Factory);
66+
}
67+
68+
/* ============ External Getter Functions ============ */
69+
70+
/**
71+
* Return calldata for the add liquidity call
72+
*
73+
* @param _setToken Address of the SetToken
74+
* @param _pool Address of liquidity token
75+
* @param _components Token address array required to remove liquidity
76+
* @param _maxTokensIn AmountsIn desired to add liquidity
77+
* @param _minLiquidity Min liquidity amount to add
78+
*/
79+
function getProvideLiquidityCalldata(
80+
address _setToken,
81+
address _pool,
82+
address[] calldata _components,
83+
uint256[] calldata _maxTokensIn,
84+
uint256 _minLiquidity
85+
)
86+
external
87+
view
88+
override
89+
returns (address target, uint256 value, bytes memory data)
90+
{
91+
address setToken = _setToken;
92+
address[] memory components = _components;
93+
uint256[] memory maxTokensIn = _maxTokensIn;
94+
uint256 minLiquidity = _minLiquidity;
95+
96+
require(maxTokensIn[0] > 0 && maxTokensIn[1] > 0, "Component quantity must be nonzero");
97+
98+
IArrakisVaultV1 arrakisVaultPool = IArrakisVaultV1(_pool);
99+
100+
// Sort the amount in order of tokens stored in Arrakis Pool
101+
(uint256 maxTokensInA, uint256 maxTokensInB) = _getOrderedAmount(components[0], components[1], maxTokensIn[0], maxTokensIn[1]);
102+
103+
(uint256 amountAMin, uint256 amountBMin, uint256 liquidityExpectedFromSuppliedTokens) = arrakisVaultPool.getMintAmounts(maxTokensInA, maxTokensInB);
104+
105+
require(
106+
minLiquidity <= liquidityExpectedFromSuppliedTokens,
107+
"_minLiquidity is too high for input token limit"
108+
);
109+
110+
target = router;
111+
value = 0;
112+
data = abi.encodeWithSignature(
113+
ADD_LIQUIDITY,
114+
arrakisVaultPool,
115+
maxTokensInA,
116+
maxTokensInB,
117+
amountAMin,
118+
amountBMin,
119+
setToken
120+
);
121+
}
122+
123+
/**
124+
* Return calldata for the add liquidity call for a single asset
125+
*/
126+
function getProvideLiquiditySingleAssetCalldata(
127+
address /*_setToken*/,
128+
address /*_pool*/,
129+
address /*_component*/,
130+
uint256 /*_maxTokenIn*/,
131+
uint256 /*_minLiquidity*/
132+
)
133+
external
134+
view
135+
override
136+
returns (address /*target*/, uint256 /*value*/, bytes memory /*data*/)
137+
{
138+
revert("Arrakis single asset addition is not supported");
139+
}
140+
141+
/**
142+
* Return calldata for the remove liquidity call
143+
*
144+
* @param _setToken Address of the SetToken
145+
* @param _pool Address of liquidity token
146+
* @param _components Token address array required to remove liquidity
147+
* @param _minTokensOut AmountsOut minimum to remove liquidity
148+
* @param _liquidity Liquidity amount to remove
149+
*/
150+
function getRemoveLiquidityCalldata(
151+
address _setToken,
152+
address _pool,
153+
address[] calldata _components,
154+
uint256[] calldata _minTokensOut,
155+
uint256 _liquidity
156+
)
157+
external
158+
view
159+
override
160+
returns (address target, uint256 value, bytes memory data)
161+
{
162+
address setToken = _setToken;
163+
address[] memory components = _components;
164+
uint256[] memory minTokensOut = _minTokensOut;
165+
uint256 liquidity = _liquidity;
166+
IArrakisVaultV1 arrakisVaultPool = IArrakisVaultV1(_pool);
167+
168+
// Make sure that only up to the amount of liquidity tokens owned by the Set Token are redeemed
169+
uint256 setTokenLiquidityBalance = arrakisVaultPool.balanceOf(setToken);
170+
require(liquidity <= setTokenLiquidityBalance, "_liquidity must be <= to current balance");
171+
172+
// Checks for minTokensOut
173+
require(minTokensOut[0] > 0 && minTokensOut[1] > 0, "Minimum quantity must be nonzero");
174+
175+
// Sort the amount in order of tokens stored in Arrakis Pool
176+
(uint256 minTokensOutA, uint256 minTokensOutB) = _getOrderedAmount(components[0], components[1], minTokensOut[0], minTokensOut[1]);
177+
178+
target = router;
179+
value = 0;
180+
data = abi.encodeWithSignature(
181+
REMOVE_LIQUIDITY,
182+
arrakisVaultPool,
183+
liquidity,
184+
minTokensOutA,
185+
minTokensOutB,
186+
setToken
187+
);
188+
}
189+
190+
/**
191+
* Return calldata for the remove liquidity single asset call
192+
*/
193+
function getRemoveLiquiditySingleAssetCalldata(
194+
address /* _setToken */,
195+
address /*_pool*/,
196+
address /*_component*/,
197+
uint256 /*_minTokenOut*/,
198+
uint256 /*_liquidity*/
199+
)
200+
external
201+
view
202+
override
203+
returns (address /*target*/, uint256 /*value*/, bytes memory /*data*/)
204+
{
205+
revert("Arrakis single asset removal is not supported");
206+
}
207+
208+
/**
209+
* Returns the address of the spender
210+
*/
211+
function getSpenderAddress(address /*_pool*/)
212+
external
213+
view
214+
override
215+
returns (address spender)
216+
{
217+
spender = router;
218+
}
219+
220+
/**
221+
* Verifies that this is an Arrakis Vault pool holding valid UniswapV3 position
222+
*
223+
* @param _pool Address of liquidity token
224+
* @param _components Address array of supplied/requested tokens
225+
*/
226+
function isValidPool(address _pool, address[] memory _components)
227+
external
228+
view
229+
override
230+
returns (bool)
231+
{
232+
// Attempt to get the tokens of the provided pool
233+
address token0;
234+
address token1;
235+
try IArrakisVaultV1(_pool).token0() returns (IERC20 _token0) {
236+
token0 = address(_token0);
237+
} catch {
238+
return false;
239+
}
240+
try IArrakisVaultV1(_pool).token1() returns (IERC20 _token1) {
241+
token1 = address(_token1);
242+
} catch {
243+
return false;
244+
}
245+
246+
// Make sure that components length is two
247+
if (_components.length != 2) {
248+
return false;
249+
}
250+
251+
// Make sure that _components[0] is either of token0 or token1
252+
if (!(_components[0] == token0 || _components[0] == token1) ) {
253+
return false;
254+
}
255+
256+
// Make sure that _components[1] is either of token0 or token1
257+
if (!(_components[1] == token0 || _components[1] == token1) ) {
258+
return false;
259+
}
260+
261+
// Make sure the pool address follows IERC20 interface
262+
try IArrakisVaultV1(_pool).totalSupply() returns (uint256) {
263+
} catch {
264+
return false;
265+
}
266+
267+
return true;
268+
}
269+
270+
/**
271+
* Sorts the amount in order of tokens stored in Arrakis/UniswapV3 Pool
272+
*
273+
* @param _token0 Address of token0
274+
* @param _token1 Address of token1
275+
* @param _amount0 Amount of token0
276+
* @param _amount1 Amount of token1
277+
*/
278+
function _getOrderedAmount(address _token0, address _token1, uint256 _amount0, uint256 _amount1) private pure returns(uint256, uint256) {
279+
return _token0 < _token1 ? (_amount0, _amount1) : (_amount1, _amount0);
280+
}
281+
}

0 commit comments

Comments
 (0)