Skip to content
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

Tasks: Implement KyberSwap v2 swapper #154

Merged
merged 3 commits into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

pragma solidity >=0.8.0;

/**
* @title KyberSwap V2 connector interface
*/
interface IKyberSwapV2Connector {
/**
* @dev The token in is the same as the token out
*/
error KyberSwapV2SwapSameToken(address token);

/**
* @dev The amount out is lower than the minimum amount out
*/
error KyberSwapV2BadAmountOut(uint256 amountOut, uint256 minAmountOut);

/**
* @dev The post token in balance is lower than the previous token in balance minus the amount in
*/
error KyberSwapV2BadPostTokenInBalance(uint256 postBalanceIn, uint256 preBalanceIn, uint256 amountIn);

/**
* @dev Tells the reference to KyberSwap aggregation router v2
*/
function kyberSwapV2Router() external view returns (address);

/**
* @dev Executes a token swap in KyberSwap V2
* @param tokenIn Token to be sent
* @param tokenOut Token to be received
* @param amountIn Amount of token in to be swapped
* @param minAmountOut Minimum amount of token out willing to receive
* @param data Calldata to be sent to the KyberSwap aggregation router
*/
function execute(address tokenIn, address tokenOut, uint256 amountIn, uint256 minAmountOut, bytes memory data)
external
returns (uint256 amountOut);
}
70 changes: 70 additions & 0 deletions packages/connectors/contracts/kyberswap/KyberSwapV2Connector.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

pragma solidity ^0.8.0;

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';
import '@openzeppelin/contracts/utils/Address.sol';

import '@mimic-fi/v3-helpers/contracts/utils/ERC20Helpers.sol';

import '../interfaces/kyberswap/IKyberSwapV2Connector.sol';

/**
* @title KyberSwapV2Connector
* @dev Interfaces with KyberSwap V2 to swap tokens
*/
contract KyberSwapV2Connector is IKyberSwapV2Connector {
// Reference to KyberSwap aggregation router v2
address public immutable override kyberSwapV2Router;

/**
* @dev Creates a new KyberSwapV2Connector contract
* @param _kyberSwapV2Router KyberSwap aggregation router v2 reference
*/
constructor(address _kyberSwapV2Router) {
kyberSwapV2Router = _kyberSwapV2Router;
}

/**
* @dev Executes a token swap in KyberSwap V2
* @param tokenIn Token to be sent
* @param tokenOut Token to be received
* @param amountIn Amount of token in to be swapped
* @param minAmountOut Minimum amount of token out willing to receive
* @param data Calldata to be sent to the KyberSwap aggregation router
*/
function execute(address tokenIn, address tokenOut, uint256 amountIn, uint256 minAmountOut, bytes memory data)
external
override
returns (uint256 amountOut)
{
if (tokenIn == tokenOut) revert KyberSwapV2SwapSameToken(tokenIn);

uint256 preBalanceIn = IERC20(tokenIn).balanceOf(address(this));
uint256 preBalanceOut = IERC20(tokenOut).balanceOf(address(this));

ERC20Helpers.approve(tokenIn, kyberSwapV2Router, amountIn);
Address.functionCall(kyberSwapV2Router, data, 'KYBER_SWAP_V2_SWAP_FAILED');

uint256 postBalanceIn = IERC20(tokenIn).balanceOf(address(this));
bool isPostBalanceInUnexpected = postBalanceIn < preBalanceIn - amountIn;
if (isPostBalanceInUnexpected) revert KyberSwapV2BadPostTokenInBalance(postBalanceIn, preBalanceIn, amountIn);

uint256 postBalanceOut = IERC20(tokenOut).balanceOf(address(this));
amountOut = postBalanceOut - preBalanceOut;
if (amountOut < minAmountOut) revert KyberSwapV2BadAmountOut(amountOut, minAmountOut);
}
}
4 changes: 2 additions & 2 deletions packages/connectors/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"lint:solidity": "solhint 'contracts/**/*.sol' --config ../../node_modules/solhint-config-mimic/index.js",
"lint:typescript": "eslint . --ext .ts",
"test": "hardhat test",
"test:mainnet": "yarn test --fork mainnet --block-number 17525323 --chain-id 1",
"test:mainnet": "yarn test --fork mainnet --block-number 19932950 --chain-id 1",
"test:polygon": "yarn test --fork polygon --block-number 44153231 --chain-id 137",
"test:optimism": "yarn test --fork optimism --block-number 105914596 --chain-id 10",
"test:arbitrum": "yarn test --fork arbitrum --block-number 212259071 --chain-id 42161",
Expand All @@ -24,7 +24,7 @@
"test:bsc": "yarn test --fork bsc --block-number 27925272 --chain-id 56",
"test:fantom": "yarn test --fork fantom --block-number 61485606 --chain-id 250",
"test:zkevm": "yarn test --fork zkevm --block-number 9014946 --chain-id 1101",
"test:base": "yarn test --fork base --block-number 14589312 --chain-id 8453",
"test:base": "yarn test --fork base --block-number 14845449 --chain-id 8453",
"prepare": "yarn build"
},
"dependencies": {
Expand Down
5 changes: 3 additions & 2 deletions packages/connectors/src/1inch-v5.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import axios, { AxiosError } from 'axios'
import { BigNumber, Contract } from 'ethers'

const ONE_INCH_URL = 'https://api.1inch.io/v5.0'
const ONE_INCH_URL = 'https://api.1inch.dev/swap/v5.2'
const ONE_INCH_API_KEY = process.env.ONE_INCH_API_KEY

export type SwapResponse = { data: { tx: { data: string } } }

Expand Down Expand Up @@ -32,7 +33,7 @@ async function getSwap(
): Promise<SwapResponse> {
return axios.get(`${ONE_INCH_URL}/${chainId}/swap`, {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${ONE_INCH_API_KEY}`,
Accept: 'application/json',
},
params: {
Expand Down
68 changes: 68 additions & 0 deletions packages/connectors/src/kyberswap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import axios, { AxiosError } from 'axios'
import { BigNumber, Contract } from 'ethers'

const KYBER_SWAP_URL = 'https://aggregator-api.kyberswap.com'
export type SwapResponse = { data: { data: { data: string } } }

const CHAINS: { [key: number]: string } = {
1: 'ethereum',
8453: 'base',
}

export async function getKyberSwapSwapData(
chainId: number,
sender: Contract,
tokenIn: Contract,
tokenOut: Contract,
amountIn: BigNumber,
slippage: number
): Promise<string> {
try {
const response = await getSwap(chainId, sender, tokenIn, tokenOut, amountIn, slippage)
return response.data.data.data
} catch (error) {
if (error instanceof AxiosError) throw Error(error.toString() + ' - ' + error.response?.data?.description)
else throw error
}
}

async function getSwap(
chainId: number,
sender: Contract,
tokenIn: Contract,
tokenOut: Contract,
amountIn: BigNumber,
slippage: number
): Promise<SwapResponse> {
const chain = CHAINS[chainId]
const response = await axios.get(`${KYBER_SWAP_URL}/${chain}/api/v1/routes`, {
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
params: {
tokenIn: tokenIn.address,
tokenOut: tokenOut.address,
amountIn: amountIn.toString(),
saveGas: true,
},
})

// The value is in ranges [0, 2000], 10 means 0.1%
const slippageTolerance = Math.floor(slippage < 1 ? slippage * 10000 : slippage)
return await axios.post(
`${KYBER_SWAP_URL}/${chain}/api/v1/route/build`,
{
routeSummary: response.data.data.routeSummary,
slippageTolerance,
sender: sender.address,
recipient: sender.address,
},
{
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
}
)
}
16 changes: 0 additions & 16 deletions packages/connectors/test/1inch/OneInchV5Connector.behavior.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,22 +84,6 @@ export function itBehavesLikeOneInchV5Connector(
})

if (WBTC !== ZERO_ADDRESS) {
context('USDC-WBTC', () => {
const amountIn = toUSDC(10e3)

it('swaps correctly USDC-WBTC', async function () {
const previousBalance = await wbtc.balanceOf(this.connector.address)
await usdc.connect(whale).transfer(this.connector.address, amountIn)

const data = await loadOrGet1inchSwapData(CHAIN, this.connector, usdc, wbtc, amountIn, SLIPPAGE)
await this.connector.connect(whale).execute(USDC, WBTC, amountIn, 0, data)

const currentBalance = await wbtc.balanceOf(this.connector.address)
const expectedMinAmountOut = await getExpectedMinAmountOut(USDC, WBTC, amountIn, SLIPPAGE)
expect(currentBalance.sub(previousBalance)).to.be.at.least(expectedMinAmountOut)
})
})

context('WBTC-USDC', () => {
const amountIn = toWBTC(1)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ describe('BalancerV2PoolConnector', function () {
})

context('when the min amount out is not enough', () => {
const minAmountOut = fp(1000)
const minAmountOut = fp(2000)

it('reverts', async () => {
await expect(connector.join(poolId, tokenIn, joinAmount, minAmountOut)).to.be.revertedWith('BAL#208')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const CHAINLINK_USDC_ETH = '0x986b5E1e1755e3C2440e960477f25201B0a8bbD4'
const CHAINLINK_WBTC_ETH = '0xdeb288F737066589598e9214E782fa5A8eD689e8'

describe('BalancerV2SwapConnector', () => {
const SLIPPAGE = 0.06
const SLIPPAGE = 0.09
const WETH_USDC_POOL_ID = '0x96646936b91d6b9d7d0c47c496afbf3d6ec7b6f8000200000000000000000019'
const WETH_WBTC_POOL_ID = '0xa6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e'

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"tokenIn": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"tokenOut": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
"amountIn": "10000000000",
"slippage": 0.015,
"data": "0x12aa3caf000000000000000000000000e37e799d5077682fa0a244d46e5649f71457bd09000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c599000000000000000000000000e37e799d5077682fa0a244d46e5649f71457bd09000000000000000000000000f42ec71a4440f5e9871c643696dd6dc9a38911f800000000000000000000000000000000000000000000000000000002540be4000000000000000000000000000000000000000000000000000000000000ddb859000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002540000000000000000000000000000000000000000000002360002080001be00a007e5c0d200000000000000000000000000000000000000000000019a0000ca0000b051204dece678ceceb27446b35c672dc7d61f30bad69ea0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800443df02124000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000217507ae7cc9d125d3b0020d6bdbf78f939e0a03fb07f59a73314e73794be0e57ac1b4e5122e0438eb3703bf871e31ce639bd351109c88666eaf939e0a03fb07f59a73314e73794be0e57ac1b4e0044a64833a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e37e799d5077682fa0a244d46e5649f71457bd0900a0f2fa6b662260fac5e5542a773aa44fbcfedf7c193bc2c5990000000000000000000000000000000000000000000000000000000000e118b800000000000000000000000000001cac80a06c4eca272260fac5e5542a773aa44fbcfedf7c193bc2c5991111111254eeb25477b68fb85ed929f73a9605820000000000000000000000008bb21a3d"
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
"tokenOut": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"amountIn": "10000000000",
"slippage": 0.015,
"data": "0xe449022e00000000000000000000000000000000000000000000000000000002540be4000000000000000000000000000000000000000000000000004b88924a476a67cd0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000088e6a0c2ddd26feeb64f039a2c41296fcb3f5640cfee7c08"
"data": "0xe449022e00000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000024405ef5d36b90c10000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000088e6a0c2ddd26feeb64f039a2c41296fcb3f56408bb21a3d"
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
"tokenOut": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"amountIn": "100000000",
"slippage": 0.015,
"data": "0xe449022e0000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000695097f210000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000099ac8ca7087fa4a2a1fb6357269965a2014abc35cfee7c08"
"data": "0xe449022e0000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000f99780dd4000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000020000000000000000000000004585fe77225b41b697c938b018e2ac67ac5a20c080000000000000000000000088e6a0c2ddd26feeb64f039a2c41296fcb3f56408bb21a3d"
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
"tokenOut": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"amountIn": "1000000000000000000",
"slippage": 0.015,
"data": "0xe449022e0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006a1d73080000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000180000000000000000000000088e6a0c2ddd26feeb64f039a2c41296fcb3f5640cfee7c08"
"data": "0xe449022e0000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000dd28edaa0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000180000000000000000000000088e6a0c2ddd26feeb64f039a2c41296fcb3f56408bb21a3d"
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"tokenIn": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"tokenOut": "0x4200000000000000000000000000000000000006",
"amountIn": "10000000000",
"data": "0x4dcebcba000000000000000000000000000000000000000000000000000000006647a7a600000000000000000000000034b40ba116d5dec75548a9e9a8f15411461e8c7000000000000000000000000051c72848c68a965f66fa7a88855f9f7784502a7f0000000000000000000000000000000000000000000000000000031ef93f5bf7000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda02913000000000000000000000000420000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000002540be4000000000000000000000000000000000000000000000000002ce1a31db374011a00000000000000000000000034b40ba116d5dec75548a9e9a8f15411461e8c70000000000000000000000000000000000000000000000000000000000000000085543df12d753b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000410503ae0427d1300d5d9092c65def74ef08b144804dd062976cde63a2813ece05729017fa0ccc9ac2b9587a5a347d189aac529cf55b65185a9585e2f4f50b640d1c00000000000000000000000000000000000000000000000000000000000000"
"data": "0x4dcebcba00000000000000000000000000000000000000000000000000000000664f699a000000000000000000000000faaddc93baf78e89dcf37ba67943e1be8f37bb8c00000000000000000000000051c72848c68a965f66fa7a88855f9f7784502a7f0000000000000000000000000000000000000000000000000000031ef93f76ba000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda02913000000000000000000000000420000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000024454cd9c93eef31000000000000000000000000faaddc93baf78e89dcf37ba67943e1be8f37bb8c0000000000000000000000000000000000000000000000000000000000000000311627ff2adae9c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000041342f31efd4fa1a26eb099aaefeab0c13627cfb74f5dc0b1fda580d1cdc3df8a2775e6e0db3ea045debbac8bb307602500d90ca0ed6c0f02f39f31262ebf3db661b00000000000000000000000000000000000000000000000000000000000000"
}
Loading
Loading