add kyber swap

This commit is contained in:
q1q0 2023-06-13 13:24:06 -04:00
parent af738706be
commit 72ba571ce3
9 changed files with 323 additions and 3 deletions

View File

@ -1,8 +1,8 @@
//SPDX-License-Identifier: MIT //SPDX-License-Identifier: MIT
pragma solidity ^0.7.0; pragma solidity ^0.7.0;
import { DSMath } from "../../common/math.sol"; import { DSMath } from "../../../common/math.sol";
import { Basic } from "../../common/basic.sol"; import { Basic } from "../../../common/basic.sol";
import { KyberInterface } from "./interface.sol"; import { KyberInterface } from "./interface.sol";
abstract contract Helpers is DSMath, Basic { abstract contract Helpers is DSMath, Basic {

View File

@ -6,7 +6,7 @@ pragma solidity ^0.7.0;
* @dev Decentralised Swapping. * @dev Decentralised Swapping.
*/ */
import { TokenInterface } from "../../common/interfaces.sol"; import { TokenInterface } from "../../../common/interfaces.sol";
import { Helpers } from "./helpers.sol"; import { Helpers } from "./helpers.sol";
import { Events } from "./events.sol"; import { Events } from "./events.sol";

View File

@ -0,0 +1,12 @@
//SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
contract Events {
event LogSwap(
address buyToken,
address sellToken,
uint256 buyAmt,
uint256 sellAmt,
uint256 setId
);
}

View File

@ -0,0 +1,71 @@
//SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import {DSMath} from "../../../common/math.sol";
import {Basic} from "../../../common/basic.sol";
import {TokenInterface} from "../../../common/interfaces.sol";
import {AugustusSwapperInterface} from "./interface.sol";
abstract contract Helpers is DSMath, Basic {
struct SwapData {
TokenInterface sellToken;
TokenInterface buyToken;
uint256 _sellAmt;
uint256 _buyAmt;
uint256 unitAmt;
bytes callData;
}
address internal constant kyberswap =
0x6131B5fae19EA4f9D964eAc0408E4408b66337b5;
function _swapHelper(SwapData memory swapData, uint256 wethAmt)
internal
returns (uint256 buyAmt)
{
TokenInterface buyToken = swapData.buyToken;
(uint256 _buyDec, uint256 _sellDec) = getTokensDec(
buyToken,
swapData.sellToken
);
uint256 _sellAmt18 = convertTo18(_sellDec, swapData._sellAmt);
uint256 _slippageAmt = convert18ToDec(
_buyDec,
wmul(swapData.unitAmt, _sellAmt18)
);
uint256 initalBal = getTokenBal(buyToken);
(bool success, ) = kyberswap.call{value: wethAmt}(swapData.callData);
if (!success) revert("kyberswap-failed");
uint256 finalBal = getTokenBal(buyToken);
buyAmt = sub(finalBal, initalBal);
require(_slippageAmt <= buyAmt, "Too much slippage");
}
function _swap(SwapData memory swapData, uint256 setId)
internal
returns (SwapData memory)
{
TokenInterface _sellAddr = swapData.sellToken;
uint256 ethAmt;
if (address(_sellAddr) == ethAddr) {
ethAmt = swapData._sellAmt;
} else {
address tokenProxy = AugustusSwapperInterface(kyberswap)
.getTokenTransferProxy();
approve(TokenInterface(_sellAddr), tokenProxy, swapData._sellAmt);
}
swapData._buyAmt = _swapHelper(swapData, ethAmt);
setUint(setId, swapData._buyAmt);
return swapData;
}
}

View File

@ -0,0 +1,6 @@
//SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
interface AugustusSwapperInterface {
function getTokenTransferProxy() external view returns (address);
}

View File

@ -0,0 +1,60 @@
//SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
/**
* @title Kyber.
* @dev DEX Aggregator.
*/
import {TokenInterface} from "../../../common/interfaces.sol";
import {Stores} from "../../../common/stores.sol";
import {Helpers} from "./helpers.sol";
abstract contract KyberResolver is Helpers {
/**
* @dev Sell ETH/ERC20_Token using KyberSwap.
* @notice Swap tokens from exchanges like kyber, 0x etc, with calculation done off-chain.
* @param buyAddr The address of the token to buy.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
* @param sellAddr The address of the token to sell.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
* @param sellAmt The amount of the token to sell.
* @param unitAmt The amount of buyAmt/sellAmt with slippage.
* @param callData Data from kyberswap API.
* @param setId ID stores the amount of token brought.
*/
function swap(
address buyAddr,
address sellAddr,
uint256 sellAmt,
uint256 unitAmt,
bytes calldata callData,
uint256 setId
)
external
payable
returns (string memory _eventName, bytes memory _eventParam)
{
Helpers.SwapData memory swapData = Helpers.SwapData({
buyToken: TokenInterface(buyAddr),
sellToken: TokenInterface(sellAddr),
unitAmt: unitAmt,
callData: callData,
_sellAmt: sellAmt,
_buyAmt: 0
});
swapData = _swap(swapData, setId);
_eventName = "LogSwap(address,address,uint256,uint256,uint256)";
_eventParam = abi.encode(
address(swapData.buyToken),
address(swapData.sellToken),
swapData._buyAmt,
swapData._sellAmt,
setId
);
}
}
contract ConnectV2KyberV3 is KyberResolver {
string public name = "Kyber-v3";
}

View File

@ -0,0 +1,171 @@
import hre from "hardhat";
import { expect } from "chai";
import { abis } from "../../../scripts/constant/abis";
import { addresses } from "../../../scripts/tests/mainnet/addresses";
import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector";
import { getMasterSigner } from "../../../scripts/tests/getMasterSigner";
import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2";
import {
ConnectV2KyberV3__factory,
} from "../../../typechain"
import { parseEther } from "@ethersproject/units";
import { encodeSpells } from "../../../scripts/tests/encodeSpells";
import { tokens } from "../../../scripts/tests/mainnet/tokens";
import { constants } from "../../../scripts/constant/constant";
import { addLiquidity } from "../../../scripts/tests/addLiquidity";
import BigNumber from "bignumber.js";
import axios from "axios";
const { web3, deployments, waffle, ethers } = hre;
const { provider, deployContract } = waffle;
describe("Kyberswap", function() {
const connectorName = "kyberswap-test";
let dsaWallet0: any;
let masterSigner: any;
let instaConnectorsV2: any;
let connector: any;
const wallets = provider.getWallets();
const [wallet0, wallet1, wallet2, wallet3] = wallets;
before(async () => {
await hre.network.provider.request({
method: "hardhat_reset",
params: [
{
forking: {
// @ts-ignore
jsonRpcUrl: hre.config.networks.hardhat.forking.url,
// blockNumber: 13300000,
},
},
],
});
masterSigner = await getMasterSigner();
const erc20 = abis.basic.erc20;
instaConnectorsV2 = await ethers.getContractAt(
abis.core.connectorsV2,
addresses.core.connectorsV2
);
connector = await deployAndEnableConnector({
connectorName,
contractArtifact: ConnectV2KyberV3__factory,
signer: masterSigner,
connectors: instaConnectorsV2,
});
// console.log("Connector address", connector.address);
});
it("Should have contracts deployed.", async function() {
expect(!!instaConnectorsV2.address).to.be.true;
expect(!!connector.address).to.be.true;
expect(!!masterSigner.address).to.be.true;
});
describe("DSA wallet setup", function() {
it("Should build DSA v2", async function() {
dsaWallet0 = await buildDSAv2(wallet0.address);
expect(!!dsaWallet0.address).to.be.true;
});
it("Deposit ETH into DSA wallet", async function() {
await wallet0.sendTransaction({
to: dsaWallet0.address,
value: ethers.utils.parseEther("10"),
});
expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(
ethers.utils.parseEther("10")
);
});
});
describe("Main", function() {
it("should swap successfully", async function() {
async function getArg() {
const slippage = 1;
/* eth -> USDT */
const sellTokenAddress = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; // eth, decimals 18
const sellTokenDecimals = 18;
const buyTokenAddress = "0xdac17f958d2ee523a2206206994597c13d831ec7"; // USDT, decimals 6
const buyTokenDecimals = 6;
const amount = 2;
const srcAmount = new BigNumber(amount)
.times(new BigNumber(10).pow(sellTokenDecimals))
.toFixed(0);
const fromAddress = dsaWallet0.address;
let url = `https://api.kyber.network/quote_amount`;
let params = {
base: sellTokenAddress,
quote: buyTokenAddress,
amount: srcAmount,
type: "sell",
};
const buyTokenAmount = await axios
.get(url, { params: params })
.then((data) => data.data);
console.log("--------buy amount--------",buyTokenAmount)
let minAmount = new BigNumber(buyTokenAmount)
.times(1 - slippage / 100)
.toFixed(0);
let txConfig = {
user_address: fromAddress,
src_id: sellTokenAddress,
dst_id: buyTokenAddress,
src_qty: srcAmount,
min_dst_qty: minAmount,
gas_price: 'high'
};
let url2 = "https://api.kyber.network/trade_data";
const calldata = await axios
.post(url2, txConfig)
.then((data) => data.data.data);
function caculateUnitAmt(
buyAmount: any,
sellAmount: any,
buyDecimal: any,
sellDecimal: any,
maxSlippage: any
) {
let unitAmt: any;
unitAmt = new BigNumber(buyAmount)
.dividedBy(10 ** buyDecimal)
.dividedBy(new BigNumber(sellAmount).dividedBy(10 ** sellDecimal));
unitAmt = unitAmt.multipliedBy((100 - maxSlippage) / 100);
unitAmt = unitAmt.multipliedBy(1e18).toFixed(0);
return unitAmt;
}
let unitAmt = caculateUnitAmt(
buyTokenAmount,
srcAmount,
buyTokenDecimals,
sellTokenDecimals,
1
);
return [
buyTokenAddress,
sellTokenAddress,
srcAmount,
unitAmt,
calldata,
0,
];
}
let arg = await getArg();
const spells = [
{
connector: connectorName,
method: "swap",
args: arg,
},
];
const tx = await dsaWallet0
.connect(wallet0)
.cast(...encodeSpells(spells), wallet1.address);
const receipt = await tx.wait();
expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(
ethers.utils.parseEther("8")
);
});
});
});