diff --git a/contracts/arbitrum/connectors/kyber_aggregator/events.sol b/contracts/arbitrum/connectors/kyber_aggregator/events.sol new file mode 100644 index 00000000..dcba616e --- /dev/null +++ b/contracts/arbitrum/connectors/kyber_aggregator/events.sol @@ -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 + ); +} \ No newline at end of file diff --git a/contracts/arbitrum/connectors/kyber_aggregator/helpers.sol b/contracts/arbitrum/connectors/kyber_aggregator/helpers.sol new file mode 100644 index 00000000..5d2d297a --- /dev/null +++ b/contracts/arbitrum/connectors/kyber_aggregator/helpers.sol @@ -0,0 +1,66 @@ +//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"; + +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 ethAmt) + 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: ethAmt}(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 { + approve(TokenInterface(_sellAddr), kyberswap, swapData._sellAmt); + } + swapData._buyAmt = _swapHelper(swapData, ethAmt); + + setUint(setId, swapData._buyAmt); + + return swapData; + } +} diff --git a/contracts/arbitrum/connectors/kyber_aggregator/main.sol b/contracts/arbitrum/connectors/kyber_aggregator/main.sol new file mode 100644 index 00000000..6d151700 --- /dev/null +++ b/contracts/arbitrum/connectors/kyber_aggregator/main.sol @@ -0,0 +1,59 @@ +//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 KyberArbitrumResolver 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 ConnectV2KyberAggregatorArbitrum is KyberArbitrumResolver { + string public name = "Kyber-aggregator-v1.0"; +} diff --git a/contracts/avalanche/connectors/kyber_aggregator/events.sol b/contracts/avalanche/connectors/kyber_aggregator/events.sol new file mode 100644 index 00000000..dcba616e --- /dev/null +++ b/contracts/avalanche/connectors/kyber_aggregator/events.sol @@ -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 + ); +} \ No newline at end of file diff --git a/contracts/avalanche/connectors/kyber_aggregator/helpers.sol b/contracts/avalanche/connectors/kyber_aggregator/helpers.sol new file mode 100644 index 00000000..ac5c7651 --- /dev/null +++ b/contracts/avalanche/connectors/kyber_aggregator/helpers.sol @@ -0,0 +1,66 @@ +//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"; + +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 avaxAmt) + 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: avaxAmt}(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 avaxAmt; + + if (address(_sellAddr) == avaxAddr) { + avaxAmt = swapData._sellAmt; + } else { + approve(TokenInterface(_sellAddr), kyberswap, swapData._sellAmt); + } + swapData._buyAmt = _swapHelper(swapData, avaxAmt); + + setUint(setId, swapData._buyAmt); + + return swapData; + } +} diff --git a/contracts/avalanche/connectors/kyber_aggregator/main.sol b/contracts/avalanche/connectors/kyber_aggregator/main.sol new file mode 100644 index 00000000..5bd2f25c --- /dev/null +++ b/contracts/avalanche/connectors/kyber_aggregator/main.sol @@ -0,0 +1,59 @@ +//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 KyberAvalancheResolver is Helpers { + /** + * @dev Sell AVAX/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 AVAX: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param sellAddr The address of the token to sell.(For AVAX: 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 ConnectV2KyberAggregatorAvalanche is KyberAvalancheResolver { + string public name = "Kyber-aggregator-v1.0"; +} diff --git a/contracts/fantom/connectors/kyber_aggregator/events.sol b/contracts/fantom/connectors/kyber_aggregator/events.sol new file mode 100644 index 00000000..dcba616e --- /dev/null +++ b/contracts/fantom/connectors/kyber_aggregator/events.sol @@ -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 + ); +} \ No newline at end of file diff --git a/contracts/fantom/connectors/kyber_aggregator/helpers.sol b/contracts/fantom/connectors/kyber_aggregator/helpers.sol new file mode 100644 index 00000000..188f7da1 --- /dev/null +++ b/contracts/fantom/connectors/kyber_aggregator/helpers.sol @@ -0,0 +1,66 @@ +//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"; + +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 ftmAmt) + 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: ftmAmt}(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 ftmAmt; + + if (address(_sellAddr) == ftmAddr) { + ftmAmt = swapData._sellAmt; + } else { + approve(TokenInterface(_sellAddr), kyberswap, swapData._sellAmt); + } + swapData._buyAmt = _swapHelper(swapData, ftmAmt); + + setUint(setId, swapData._buyAmt); + + return swapData; + } +} diff --git a/contracts/fantom/connectors/kyber_aggregator/main.sol b/contracts/fantom/connectors/kyber_aggregator/main.sol new file mode 100644 index 00000000..fa5603a6 --- /dev/null +++ b/contracts/fantom/connectors/kyber_aggregator/main.sol @@ -0,0 +1,59 @@ +//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 KyberFantomResolver is Helpers { + /** + * @dev Sell ftm/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 FTM: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param sellAddr The address of the token to sell.(For FTM: 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 ConnectV2KyberAggregatorFantom is KyberFantomResolver { + string public name = "Kyber-aggregator-v1.0"; +} diff --git a/contracts/mainnet/connectors/kyber/kyber_aggregator/events.sol b/contracts/mainnet/connectors/kyber/kyber_aggregator/events.sol new file mode 100644 index 00000000..dcba616e --- /dev/null +++ b/contracts/mainnet/connectors/kyber/kyber_aggregator/events.sol @@ -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 + ); +} \ No newline at end of file diff --git a/contracts/mainnet/connectors/kyber/kyber_aggregator/helpers.sol b/contracts/mainnet/connectors/kyber/kyber_aggregator/helpers.sol new file mode 100644 index 00000000..1b429f2d --- /dev/null +++ b/contracts/mainnet/connectors/kyber/kyber_aggregator/helpers.sol @@ -0,0 +1,66 @@ +//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"; + +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 ethAmt) + 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: ethAmt}(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 { + approve(TokenInterface(_sellAddr), kyberswap, swapData._sellAmt); + } + swapData._buyAmt = _swapHelper(swapData, ethAmt); + + setUint(setId, swapData._buyAmt); + + return swapData; + } +} diff --git a/contracts/mainnet/connectors/kyber/kyber_aggregator/main.sol b/contracts/mainnet/connectors/kyber/kyber_aggregator/main.sol new file mode 100644 index 00000000..4385016b --- /dev/null +++ b/contracts/mainnet/connectors/kyber/kyber_aggregator/main.sol @@ -0,0 +1,59 @@ +//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 ConnectV2KyberAggregator is KyberResolver { + string public name = "Kyber-aggregator-v1.0"; +} diff --git a/contracts/mainnet/connectors/kyber/events.sol b/contracts/mainnet/connectors/kyber/kyber_v2/events.sol similarity index 100% rename from contracts/mainnet/connectors/kyber/events.sol rename to contracts/mainnet/connectors/kyber/kyber_v2/events.sol diff --git a/contracts/mainnet/connectors/kyber/helpers.sol b/contracts/mainnet/connectors/kyber/kyber_v2/helpers.sol similarity index 79% rename from contracts/mainnet/connectors/kyber/helpers.sol rename to contracts/mainnet/connectors/kyber/kyber_v2/helpers.sol index 7c0e6f17..721228cd 100644 --- a/contracts/mainnet/connectors/kyber/helpers.sol +++ b/contracts/mainnet/connectors/kyber/kyber_v2/helpers.sol @@ -1,8 +1,8 @@ //SPDX-License-Identifier: MIT pragma solidity ^0.7.0; -import { DSMath } from "../../common/math.sol"; -import { Basic } from "../../common/basic.sol"; +import { DSMath } from "../../../common/math.sol"; +import { Basic } from "../../../common/basic.sol"; import { KyberInterface } from "./interface.sol"; abstract contract Helpers is DSMath, Basic { diff --git a/contracts/mainnet/connectors/kyber/interface.sol b/contracts/mainnet/connectors/kyber/kyber_v2/interface.sol similarity index 100% rename from contracts/mainnet/connectors/kyber/interface.sol rename to contracts/mainnet/connectors/kyber/kyber_v2/interface.sol diff --git a/contracts/mainnet/connectors/kyber/main.sol b/contracts/mainnet/connectors/kyber/kyber_v2/main.sol similarity index 96% rename from contracts/mainnet/connectors/kyber/main.sol rename to contracts/mainnet/connectors/kyber/kyber_v2/main.sol index 8b106565..ac81389f 100644 --- a/contracts/mainnet/connectors/kyber/main.sol +++ b/contracts/mainnet/connectors/kyber/kyber_v2/main.sol @@ -6,7 +6,7 @@ pragma solidity ^0.7.0; * @dev Decentralised Swapping. */ -import { TokenInterface } from "../../common/interfaces.sol"; +import { TokenInterface } from "../../../common/interfaces.sol"; import { Helpers } from "./helpers.sol"; import { Events } from "./events.sol"; diff --git a/contracts/optimism/connectors/kyber_aggregator/events.sol b/contracts/optimism/connectors/kyber_aggregator/events.sol new file mode 100644 index 00000000..dcba616e --- /dev/null +++ b/contracts/optimism/connectors/kyber_aggregator/events.sol @@ -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 + ); +} \ No newline at end of file diff --git a/contracts/optimism/connectors/kyber_aggregator/helpers.sol b/contracts/optimism/connectors/kyber_aggregator/helpers.sol new file mode 100644 index 00000000..5d2d297a --- /dev/null +++ b/contracts/optimism/connectors/kyber_aggregator/helpers.sol @@ -0,0 +1,66 @@ +//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"; + +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 ethAmt) + 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: ethAmt}(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 { + approve(TokenInterface(_sellAddr), kyberswap, swapData._sellAmt); + } + swapData._buyAmt = _swapHelper(swapData, ethAmt); + + setUint(setId, swapData._buyAmt); + + return swapData; + } +} diff --git a/contracts/optimism/connectors/kyber_aggregator/main.sol b/contracts/optimism/connectors/kyber_aggregator/main.sol new file mode 100644 index 00000000..4e8acb9c --- /dev/null +++ b/contracts/optimism/connectors/kyber_aggregator/main.sol @@ -0,0 +1,59 @@ +//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 KyberOptimismResolver 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 ConnectV2KyberAggregatorOptimism is KyberOptimismResolver { + string public name = "Kyber-aggregator-v1.0"; +} diff --git a/contracts/polygon/connectors/kyber_aggregator/events.sol b/contracts/polygon/connectors/kyber_aggregator/events.sol new file mode 100644 index 00000000..dcba616e --- /dev/null +++ b/contracts/polygon/connectors/kyber_aggregator/events.sol @@ -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 + ); +} \ No newline at end of file diff --git a/contracts/polygon/connectors/kyber_aggregator/helpers.sol b/contracts/polygon/connectors/kyber_aggregator/helpers.sol new file mode 100644 index 00000000..1cc47870 --- /dev/null +++ b/contracts/polygon/connectors/kyber_aggregator/helpers.sol @@ -0,0 +1,66 @@ +//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"; + +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 maticAmt) + 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: maticAmt}(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 maticAmt; + + if (address(_sellAddr) == maticAddr) { + maticAmt = swapData._sellAmt; + } else { + approve(TokenInterface(_sellAddr), kyberswap, swapData._sellAmt); + } + swapData._buyAmt = _swapHelper(swapData, maticAmt); + + setUint(setId, swapData._buyAmt); + + return swapData; + } +} diff --git a/contracts/polygon/connectors/kyber_aggregator/main.sol b/contracts/polygon/connectors/kyber_aggregator/main.sol new file mode 100644 index 00000000..134ab153 --- /dev/null +++ b/contracts/polygon/connectors/kyber_aggregator/main.sol @@ -0,0 +1,59 @@ +//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 KyberPolygonResolver is Helpers { + /** + * @dev Sell MATIC/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 MATIC: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param sellAddr The address of the token to sell.(For MATIC: 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 ConnectV2KyberAggregatorPolygon is KyberPolygonResolver { + string public name = "Kyber-aggregator-v1.0"; +} diff --git a/test/mainnet/kyberswap/kyberswap.test.ts b/test/mainnet/kyberswap/kyberswap.test.ts new file mode 100644 index 00000000..62e1bb04 --- /dev/null +++ b/test/mainnet/kyberswap/kyberswap.test.ts @@ -0,0 +1,167 @@ +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 { + ConnectV2KyberAggregator__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: ConnectV2KyberAggregator__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", async function() { + it("should swap successfully", async function() { + const latestBlock = await hre.ethers.provider.getBlock("latest"); + console.log('latestBlock: ', latestBlock.number) + 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://aggregator-api.kyberswap.com/ethereum/api/v1/routes`; + let params = { + tokenIn: sellTokenAddress, + tokenOut: buyTokenAddress, + amountIn: srcAmount, + }; + + const routeSummary = await axios + .get(url, { params: params }) + .then((response) => response.data.data.routeSummary); + + let txConfig = { + routeSummary: routeSummary, + sender: fromAddress, + recipient: fromAddress, + }; + + let url2 = "https://aggregator-api.kyberswap.com/ethereum/api/v1/route/build"; + const calldata = await axios + .post(url2, txConfig) + .then((response) => response.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( + routeSummary.amountOut, + srcAmount, + buyTokenDecimals, + sellTokenDecimals, + 1 + ); + console.log('unitAmt: ', unitAmt.toString()) + return [ + buyTokenAddress, + sellTokenAddress, + srcAmount, + unitAmt, + calldata.data, + 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") + ); + }); + }); +});