Skip to content

Commit 32b50f1

Browse files
authored
Add Kyber V3 Index Exchange adapter (#101)
* Add KyberV3 Index Exchange adapter contract * fix typo * Add Kyber V3 DMM fixture * Remove getPoolWithBestLiquidity() from adapter contract * Add tests for adapter contract * Add integration tests with GIM * Add tests for Kyber V3 DMM fixture * Fix bug * Fix bug in uniswap V3 fixture tests * Validate pool address in exchange adapter * Bump package to 0.0.43 * Move down pool address validation
1 parent 2a066ad commit 32b50f1

File tree

23 files changed

+3727
-39
lines changed

23 files changed

+3727
-39
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity 0.6.10;
3+
4+
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
5+
6+
interface IDMMFactory {
7+
function createPool(
8+
IERC20 tokenA,
9+
IERC20 tokenB,
10+
uint32 ampBps
11+
) external returns (address pool);
12+
13+
function setFeeConfiguration(address feeTo, uint16 governmentFeeBps) external;
14+
15+
function setFeeToSetter(address) external;
16+
17+
function getFeeConfiguration() external view returns (address feeTo, uint16 governmentFeeBps);
18+
19+
function feeToSetter() external view returns (address);
20+
21+
function allPools(uint256) external view returns (address pool);
22+
23+
function allPoolsLength() external view returns (uint256);
24+
25+
function getUnamplifiedPool(IERC20 token0, IERC20 token1) external view returns (address);
26+
27+
function getPools(IERC20 token0, IERC20 token1)
28+
external
29+
view
30+
returns (address[] memory _tokenPools);
31+
32+
function isPool(
33+
IERC20 token0,
34+
IERC20 token1,
35+
address pool
36+
) external view returns (bool);
37+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity 0.6.10;
3+
4+
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
5+
6+
import "./IDMMFactory.sol";
7+
8+
interface IDMMPool {
9+
function mint(address to) external returns (uint256 liquidity);
10+
11+
function burn(address to) external returns (uint256 amount0, uint256 amount1);
12+
13+
function swap(
14+
uint256 amount0Out,
15+
uint256 amount1Out,
16+
address to,
17+
bytes calldata data
18+
) external;
19+
20+
function sync() external;
21+
22+
function getReserves() external view returns (uint112 reserve0, uint112 reserve1);
23+
24+
function getTradeInfo()
25+
external
26+
view
27+
returns (
28+
uint112 _vReserve0,
29+
uint112 _vReserve1,
30+
uint112 reserve0,
31+
uint112 reserve1,
32+
uint256 feeInPrecision
33+
);
34+
35+
function token0() external view returns (IERC20);
36+
37+
function token1() external view returns (IERC20);
38+
39+
function ampBps() external view returns (uint32);
40+
41+
function factory() external view returns (IDMMFactory);
42+
43+
function kLast() external view returns (uint256);
44+
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/*
2+
Copyright 2021 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+
pragma experimental "ABIEncoderV2";
21+
22+
import { BytesLib } from "external/contracts/uniswap/v3/lib/BytesLib.sol";
23+
import { IDMMFactory } from "../../../interfaces/external/IDMMFactory.sol";
24+
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
25+
import { IIndexExchangeAdapter } from "../../../interfaces/IIndexExchangeAdapter.sol";
26+
27+
/**
28+
* @title KyberV3IndexExchangeAdapter
29+
* @author Set Protocol
30+
*
31+
* A Kyber V3 DMM exchange adapter that returns calldata for trading with GeneralIndexModule, allows encoding a trade with a fixed input quantity or
32+
* a fixed output quantity.
33+
*/
34+
contract KyberV3IndexExchangeAdapter is IIndexExchangeAdapter {
35+
36+
using BytesLib for bytes;
37+
38+
/* ============ Constants ============ */
39+
40+
// DMMRouter function string for swapping exact tokens for a minimum of receive tokens
41+
string internal constant SWAP_EXACT_TOKENS_FOR_TOKENS = "swapExactTokensForTokens(uint256,uint256,address[],address[],address,uint256)";
42+
// DMMRouter function string for swapping tokens for an exact amount of receive tokens
43+
string internal constant SWAP_TOKENS_FOR_EXACT_TOKENS = "swapTokensForExactTokens(uint256,uint256,address[],address[],address,uint256)";
44+
45+
/* ============ State Variables ============ */
46+
47+
address public immutable dmmRouter;
48+
IDMMFactory public immutable dmmFactory;
49+
50+
/* ============ Constructor ============ */
51+
52+
/**
53+
* Set state variables
54+
*
55+
* @param _dmmRouter Address of Kyber V3 DMM Router
56+
* @param _dmmFactory Address of Kyber V3 DMM Factory
57+
*/
58+
constructor(address _dmmRouter, IDMMFactory _dmmFactory) public {
59+
dmmRouter = _dmmRouter;
60+
dmmFactory = _dmmFactory;
61+
}
62+
63+
/* ============ External Getter Functions ============ */
64+
65+
/**
66+
* Return calldata for trading with Kyber V3 DMM Router. Trade paths are created from _sourceToken and
67+
* _destinationToken. On Kyber DMM exchange, for each token pair, there can be possibly many multiple pools with
68+
* different configurations for the pricing curve. Hence the address of the pool to be used for trading must be passed
69+
* in the _data parameter.
70+
*
71+
* ---------------------------------------------------------------------------------------------------------------
72+
* _isSendTokenFixed | Parameter | Amount |
73+
* ---------------------------------------------------------------------------------------------------------------
74+
* True | _sourceQuantity | Fixed amount of _sourceToken to trade |
75+
* | _destinationQuantity | Minimum amount of _destinationToken willing to receive |
76+
* ---------------------------------------------------------------------------------------------------------------
77+
* False | _sourceQuantity | Maximum amount of _sourceToken to trade |
78+
* | _destinationQuantity | Fixed amount of _destinationToken want to receive |
79+
* ---------------------------------------------------------------------------------------------------------------
80+
*
81+
* @param _sourceToken Address of source token to be sold
82+
* @param _destinationToken Address of destination token to buy
83+
* @param _destinationAddress Address that assets should be transferred to
84+
* @param _isSendTokenFixed Boolean indicating if the send quantity is fixed, used to determine correct trade interface
85+
* @param _sourceQuantity Fixed/Max amount of source token to sell
86+
* @param _destinationQuantity Min/Fixed amount of destination token to buy
87+
* @param _data Arbitray bytes containing the pool address to be used for trading. Can use
88+
* `getPoolWithBestLiquidity()` to get the most liquid pool for a given pair of tokens
89+
* on the Kyber DMM exchange.
90+
*
91+
* @return address Target contract address
92+
* @return uint256 Call value
93+
* @return bytes Trade calldata
94+
*/
95+
function getTradeCalldata(
96+
address _sourceToken,
97+
address _destinationToken,
98+
address _destinationAddress,
99+
bool _isSendTokenFixed,
100+
uint256 _sourceQuantity,
101+
uint256 _destinationQuantity,
102+
bytes memory _data
103+
)
104+
external
105+
view
106+
override
107+
returns (address, uint256, bytes memory)
108+
{
109+
110+
address[] memory path = new address[](2);
111+
path[0] = _sourceToken;
112+
path[1] = _destinationToken;
113+
114+
address[] memory poolsPath = new address[](1);
115+
poolsPath[0] = _data.toAddress(0);
116+
117+
require(dmmFactory.isPool(IERC20(_sourceToken), IERC20(_destinationToken), poolsPath[0]), "Invalid pool address");
118+
119+
bytes memory callData = abi.encodeWithSignature(
120+
_isSendTokenFixed ? SWAP_EXACT_TOKENS_FOR_TOKENS : SWAP_TOKENS_FOR_EXACT_TOKENS,
121+
_isSendTokenFixed ? _sourceQuantity : _destinationQuantity,
122+
_isSendTokenFixed ? _destinationQuantity : _sourceQuantity,
123+
poolsPath,
124+
path,
125+
_destinationAddress,
126+
block.timestamp
127+
);
128+
return (dmmRouter, 0, callData);
129+
}
130+
131+
/**
132+
* Returns the address to approve source tokens to for trading. This is the Kyber DMM Router.
133+
*
134+
* @return address Address of the contract to approve tokens to
135+
*/
136+
function getSpender() external view override returns (address) {
137+
return dmmRouter;
138+
}
139+
}

0 commit comments

Comments
 (0)