• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IndexCoop / index-protocol / 18fb5263-4d4e-40e6-9580-9d6079fdfed8

pending completion
18fb5263-4d4e-40e6-9580-9d6079fdfed8

Pull #10

circleci

SnakePoison
test: adds missing branch in isvalid
Pull Request #10: uniswapv3 amm adapter

1199 of 1200 branches covered (99.92%)

Branch coverage included in aggregate %.

49 of 49 new or added lines in 1 file covered. (100.0%)

3268 of 3269 relevant lines covered (99.97%)

271.03 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.01
/contracts/protocol/integration/amm/ArrakisUniswapV3AmmAdapter.sol
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-2.0
17
*/
18

19
pragma solidity 0.8.17;
20

21
import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
22
import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol";
23

24
import "../../../interfaces/IAmmAdapter.sol";
25
import "../../../interfaces/external/IArrakisVaultV1.sol";
26

27
/**
28
 * @title UniswapV3AmmAdapter
29
 * @author Zishan Sami
30
 *
31
 * Adapter for Arrakis Vault representing Uniswap V3 liquidity position that encodes adding and removing liquidty
32
 */
33
contract ArrakisUniswapV3AmmAdapter is IAmmAdapter {
34
    /* ============ State Variables ============ */
35

36
    // Address of Arrakis Router contract
37
    address public immutable router;
38

39
    // UniswapV3 factory contract
40
    IUniswapV3Factory public immutable uniV3Factory;
41

42
    // Internal function string for adding liquidity
43
    string internal constant ADD_LIQUIDITY =
44
        "addLiquidity(address,uint256,uint256,uint256,uint256,address)";
45
    // Internal function string for removing liquidity
46
    string internal constant REMOVE_LIQUIDITY =
47
        "removeLiquidity(address,uint256,uint256,uint256,address)";
48

49
    /* ============ Constructor ============ */
50

51
    /**
52
     * Set state variables
53
     *
54
     * @param _router          Address of Arrakis Router contract
55
     * @param _uniV3Factory    Address of UniswapV3 Factory contract
56
     */
57
    constructor(address _router, address _uniV3Factory){
58
        require(_router != address(0),"_router address must not be zero address");
11✔
59
        require(_uniV3Factory != address(0),"_uniV3Factory address must not be zero address");
10✔
60
        router = _router;
9✔
61
        uniV3Factory = IUniswapV3Factory(_uniV3Factory);
9✔
62
    }
63

64
    /* ============ External Getter Functions ============ */
65

66
    /**
67
     * Return calldata for the add liquidity call
68
     *
69
     * @param  _setToken                Address of the SetToken
70
     * @param  _pool                    Address of liquidity token
71
     * @param  _components              Token address array required to remove liquidity
72
     * @param  _maxTokensIn             AmountsIn desired to add liquidity
73
     * @param  _minLiquidity            Min liquidity amount to add
74
     */
75
    function getProvideLiquidityCalldata(
76
        address _setToken,
77
        address _pool,
78
        address[] calldata _components,
79
        uint256[] calldata _maxTokensIn,
80
        uint256 _minLiquidity
81
    )
82
        external
83
        view
84
        override
85
        returns (address target, uint256 value, bytes memory data)
86
    {   
87
        address setToken = _setToken;
3✔
88
        address[] memory components = _components;
3✔
89
        uint256[] memory maxTokensIn = _maxTokensIn;
3✔
90
        uint256 minLiquidity = _minLiquidity;
3✔
91

92
        require(maxTokensIn[0] > 0 && maxTokensIn[1] > 0, "Component quantity must be nonzero");
3✔
93

94
        IArrakisVaultV1 arrakisVaultPool = IArrakisVaultV1(_pool);
2✔
95

96
        // Sort the amount in order of tokens stored in Arrakis Pool
97
        (uint256 maxTokensInA, uint256 maxTokensInB) = _getOrderedAmount(components[0], components[1], maxTokensIn[0], maxTokensIn[1]);
2✔
98

99
        (uint256 amountAMin, uint256 amountBMin, uint256 liquidityExpectedFromSuppliedTokens) = arrakisVaultPool.getMintAmounts(maxTokensInA, maxTokensInB);
2✔
100
        
101
        require(
2✔
102
            minLiquidity <= liquidityExpectedFromSuppliedTokens,
103
            "_minLiquidity is too high for input token limit"
104
        );
105

106
        target = router;
1✔
107
        value = 0;
1✔
108
        data = abi.encodeWithSignature( 
1✔
109
            ADD_LIQUIDITY,
110
            arrakisVaultPool,
111
            maxTokensInA,
112
            maxTokensInB,
113
            amountAMin,
114
            amountBMin,
115
            setToken
116
        );
117
    }
118

119
    /**
120
     * Return calldata for the add liquidity call for a single asset
121
     */
122
    function getProvideLiquiditySingleAssetCalldata(
123
        address /*_setToken*/,
124
        address /*_pool*/,
125
        address /*_component*/,
126
        uint256 /*_maxTokenIn*/,
127
        uint256 /*_minLiquidity*/
128
    )
129
        external
130
        pure
131
        override
132
        returns (address /*target*/, uint256 /*value*/, bytes memory /*data*/)
133
    {
134
        revert("Arrakis single asset addition is not supported");
1✔
135
    }
136

137
    /**
138
     * Return calldata for the remove liquidity call
139
     *
140
     * @param  _setToken                Address of the SetToken
141
     * @param  _pool                    Address of liquidity token
142
     * @param  _components              Token address array required to remove liquidity
143
     * @param  _minTokensOut            AmountsOut minimum to remove liquidity
144
     * @param  _liquidity               Liquidity amount to remove
145
     */
146
    function getRemoveLiquidityCalldata(
147
        address _setToken,
148
        address _pool,
149
        address[] calldata _components,
150
        uint256[] calldata _minTokensOut,
151
        uint256 _liquidity
152
    )
153
        external
154
        view
155
        override
156
        returns (address target, uint256 value, bytes memory data)
157
    {   
158
        address setToken = _setToken;
2✔
159
        address[] memory components = _components;
2✔
160
        uint256[] memory minTokensOut = _minTokensOut;
2✔
161
        uint256 liquidity = _liquidity;
2✔
162
        IArrakisVaultV1 arrakisVaultPool = IArrakisVaultV1(_pool);
2✔
163

164
        // Make sure that only up to the amount of liquidity tokens owned by the Set Token are redeemed
165
        uint256 setTokenLiquidityBalance = arrakisVaultPool.balanceOf(setToken);
2✔
166
        require(liquidity <= setTokenLiquidityBalance, "_liquidity must be <= to current balance");
2✔
167

168
        // Checks for minTokensOut
169
        require(minTokensOut[0] > 0 && minTokensOut[1] > 0, "Minimum quantity must be nonzero");
1!
170

171
        // Sort the amount in order of tokens stored in Arrakis Pool
172
        (uint256 minTokensOutA, uint256 minTokensOutB) = _getOrderedAmount(components[0], components[1], minTokensOut[0], minTokensOut[1]);
1✔
173

174
        target = router;
1✔
175
        value = 0;
1✔
176
        data = abi.encodeWithSignature(
1✔
177
            REMOVE_LIQUIDITY,
178
            arrakisVaultPool,
179
            liquidity,
180
            minTokensOutA,
181
            minTokensOutB,
182
            setToken
183
        );
184
    }
185

186
    /**
187
     * Return calldata for the remove liquidity single asset call
188
     */
189
    function getRemoveLiquiditySingleAssetCalldata(
190
        address /* _setToken */,
191
        address /*_pool*/,
192
        address /*_component*/,
193
        uint256 /*_minTokenOut*/,
194
        uint256 /*_liquidity*/
195
    )
196
        external
197
        pure
198
        override
199
        returns (address /*target*/, uint256 /*value*/, bytes memory /*data*/)
200
    {
201
        revert("Arrakis single asset removal is not supported");
1✔
202
    }
203

204
    /**
205
     * Returns the address of the spender
206
     */
207
    function getSpenderAddress(address /*_pool*/)
208
        external
209
        view
210
        override
211
        returns (address spender)
212
    {
213
        spender = router;
1✔
214
    }
215

216
    /**
217
     * Verifies that this is an Arrakis Vault pool holding valid UniswapV3 position
218
     *
219
     * @param  _pool          Address of liquidity token
220
     * @param  _components    Address array of supplied/requested tokens
221
     */
222
    function isValidPool(address _pool, address[] memory _components)
223
        external
224
        view
225
        override
226
        returns (bool)
227
    {
228
        // Attempt to get the tokens of the provided pool
229
        address token0;
6✔
230
        address token1;
6✔
231
        try IArrakisVaultV1(_pool).token0() returns (address _token0) {
6✔
232
            token0 = _token0;
5✔
233
        } catch {
234
            return false;
1✔
235
        }
236
        try IArrakisVaultV1(_pool).token1() returns (address _token1) {
5✔
237
            token1 = _token1;
5✔
238
        } catch {
239
            return false;
×
240
        }
241

242
        // Make sure that components length is two
243
        if (_components.length != 2) {
5✔
244
            return false;
1✔
245
        }
246

247
        // Make sure that _components[0] is either of token0 or token1
248
        if (!(_components[0] == token0 || _components[0] == token1) ) {
4✔
249
            return false;
1✔
250
        }
251

252
        // Make sure that _components[1] is either of token0 or token1
253
        if (!(_components[1] == token0 || _components[1] == token1) ) {
3✔
254
            return false;
1✔
255
        }
256

257
        // Make sure the pool address follows IERC20 interface
258
        try IArrakisVaultV1(_pool).totalSupply() returns (uint256) {
2✔
259
        } catch {
260
            return false;
1✔
261
        }
262
        
263
        return true;
1✔
264
    }
265

266
    /**
267
     * Sorts the amount in order of tokens stored in Arrakis/UniswapV3 Pool
268
     *
269
     * @param  _token0        Address of token0
270
     * @param  _token1        Address of token1
271
     * @param  _amount0       Amount of token0
272
     * @param  _amount1       Amount of token1
273
     */
274
    function _getOrderedAmount(address _token0, address _token1, uint256 _amount0, uint256 _amount1) private pure returns(uint256, uint256) {
275
        return _token0 < _token1 ? (_amount0, _amount1) : (_amount1, _amount0);
3✔
276
    }
277
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc