From 8e89d6652135b54ca0289d81a379adc0f2923dcd Mon Sep 17 00:00:00 2001 From: Richa-iitr Date: Sun, 5 Jun 2022 00:34:58 +0530 Subject: [PATCH] swap-mainnet,avalanche --- contracts/avalanche/common/interfaces.sol | 4 + .../avalanche/connectors/swap/events.sol | 14 +++ .../avalanche/connectors/swap/helpers.sol | 102 ++++++++++++++++++ contracts/avalanche/connectors/swap/main.sol | 78 ++++++++++++++ contracts/mainnet/common/interfaces.sol | 4 + contracts/mainnet/connectors/swap/events.sol | 14 +++ contracts/mainnet/connectors/swap/helpers.sol | 102 ++++++++++++++++++ contracts/mainnet/connectors/swap/main.sol | 78 ++++++++++++++ 8 files changed, 396 insertions(+) create mode 100644 contracts/avalanche/connectors/swap/events.sol create mode 100644 contracts/avalanche/connectors/swap/helpers.sol create mode 100644 contracts/avalanche/connectors/swap/main.sol create mode 100644 contracts/mainnet/connectors/swap/events.sol create mode 100644 contracts/mainnet/connectors/swap/helpers.sol create mode 100644 contracts/mainnet/connectors/swap/main.sol diff --git a/contracts/avalanche/common/interfaces.sol b/contracts/avalanche/common/interfaces.sol index cffe7136..e9a3b0c1 100644 --- a/contracts/avalanche/common/interfaces.sol +++ b/contracts/avalanche/common/interfaces.sol @@ -21,3 +21,7 @@ interface AccountInterface { function disable(address) external; function isAuth(address) external view returns (bool); } + +interface InstaConnectors { + function connectors(string memory) external returns (address); +} diff --git a/contracts/avalanche/connectors/swap/events.sol b/contracts/avalanche/connectors/swap/events.sol new file mode 100644 index 00000000..0440abf0 --- /dev/null +++ b/contracts/avalanche/connectors/swap/events.sol @@ -0,0 +1,14 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +contract Events { + event LogSwap( + string _connector, + address indexed buyToken, + address indexed sellToken, + uint256 buyAmt, + uint256 sellAmt, + uint256 getId, + uint256 setId + ); +} diff --git a/contracts/avalanche/connectors/swap/helpers.sol b/contracts/avalanche/connectors/swap/helpers.sol new file mode 100644 index 00000000..5285982f --- /dev/null +++ b/contracts/avalanche/connectors/swap/helpers.sol @@ -0,0 +1,102 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma abicoder v2; + +import { InstaConnectors } from "../../common/interfaces.sol"; + +abstract contract Helper { + /** + * @dev Instadapp Connectors Registry + */ + InstaConnectors internal constant instaConnectors = + InstaConnectors(0x127d8cD0E2b2E0366D522DeA53A787bfE9002C14); + + struct InputData { + address buyAddr; + address sellAddr; + uint256 sellAmt; + uint256[] unitAmts; + bytes[] callDatas; + uint256 setId; + } +} + +contract SwapHelpers is Helper { + /** + *@dev Swap using the dex aggregators. + *@param _connectors name of the connectors in preference order. + *@param _inputData data for the swap cast. + */ + function _swap(string[] memory _connectors, InputData memory _inputData) + internal + returns ( + bool success, + bytes memory returnData, + string memory _connector + ) + { + require(_connectors.length > 0, "zero-length-not-allowed"); + require( + _inputData.unitAmts.length == _connectors.length, + "unitAmts-length-invalid" + ); + require( + _inputData.callDatas.length == _connectors.length, + "callDatas-length-invalid" + ); + + // require _connectors[i] == "1INCH-A" || "ZEROX-A" || "PARASWAP-A" || similar connectors + + for (uint256 i = 0; i < _connectors.length; i++) { + bytes4 swapData = bytes4( + keccak256("swap(address,address,uint256,uint256,bytes,uint256)") + ); + + string memory _1INCH = "1INCH-A"; + if (keccak256(bytes(_connectors[i])) == keccak256(bytes(_1INCH))) { + swapData = bytes4( + keccak256( + "sell(address,address,uint256,uint256,bytes,uint256)" + ) + ); + } + + bytes memory _data = abi.encodeWithSelector( + swapData, + _inputData.buyAddr, + _inputData.sellAddr, + _inputData.sellAmt, + _inputData.unitAmts[i], + _inputData.callDatas[i], + _inputData.setId + ); + + (success, returnData) = instaConnectors + .connectors(_connectors[i]) + .delegatecall(_data); + if (success) { + _connector = _connectors[i]; + break; + } + } + } + + function decodeEvents(string memory _connector, bytes memory returnData) + internal + view + returns (uint256 _buyAmt, uint256 _sellAmt) + { + (, bytes memory _eventParam) = abi.decode(returnData, (string, bytes)); + if (keccak256(bytes(_connector)) == keccak256(bytes("PARASWAP-A"))) { + (, , _buyAmt, _sellAmt, ) = abi.decode( + _eventParam, + (address, address, uint256, uint256, uint256) + ); + } else { + (, , _buyAmt, _sellAmt, , ) = abi.decode( + _eventParam, + (address, address, uint256, uint256, uint256, uint256) + ); + } + } +} diff --git a/contracts/avalanche/connectors/swap/main.sol b/contracts/avalanche/connectors/swap/main.sol new file mode 100644 index 00000000..a25bcbc2 --- /dev/null +++ b/contracts/avalanche/connectors/swap/main.sol @@ -0,0 +1,78 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +/** + * @title Swap. + * @dev Swap integration for DEX Aggregators. + */ + +// import files +import { SwapHelpers } from "./helpers.sol"; +import { Events } from "./events.sol"; + +abstract contract Swap is SwapHelpers, Events { + /** + * @dev Swap ETH/ERC20_Token using dex aggregators. + * @notice Swap tokens from exchanges like 1INCH, 0x etc, with calculation done off-chain. + * @param _connectors The name of the connectors like 1INCH-A, 0x etc, in order of their priority. + * @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 unitAmts The amount of buyAmt/sellAmt with slippage for respective DEXs. + * @param callDatas Data from APIs for respective DEXs. + * @param setId ID stores the amount of token brought. + */ + function swap( + string[] memory _connectors, + address buyAddr, + address sellAddr, + uint256 sellAmt, + uint256[] memory unitAmts, + bytes[] calldata callDatas, + uint256 setId + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + InputData memory inputData = InputData({ + buyAddr: buyAddr, + sellAddr: sellAddr, + sellAmt: sellAmt, + unitAmts: unitAmts, + callDatas: callDatas, + setId: setId + }); + + ( + bool success, + bytes memory returnData, + string memory _connector + ) = _swap(_connectors, inputData); + + uint256 _buyAmt; + uint256 _sellAmt; + + if (!success) { + revert("swap-failed"); + } else { + (_buyAmt, _sellAmt) = decodeEvents(_connector, returnData); + } + + _eventName = "LogSwap(string,address,address,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode( + _connector, + buyAddr, + sellAddr, + _buyAmt, + _sellAmt, + 0, + setId + ); + } +} + +contract ConnectV2SwapAvalanche is Swap { + string public name = "Swap-v1"; +} diff --git a/contracts/mainnet/common/interfaces.sol b/contracts/mainnet/common/interfaces.sol index 0ccaf951..e1ed2ca2 100644 --- a/contracts/mainnet/common/interfaces.sol +++ b/contracts/mainnet/common/interfaces.sol @@ -27,3 +27,7 @@ interface AccountInterface { function disable(address) external; function isAuth(address) external view returns (bool); } + +interface InstaConnectors { + function connectors(string memory) external returns (address); +} \ No newline at end of file diff --git a/contracts/mainnet/connectors/swap/events.sol b/contracts/mainnet/connectors/swap/events.sol new file mode 100644 index 00000000..0440abf0 --- /dev/null +++ b/contracts/mainnet/connectors/swap/events.sol @@ -0,0 +1,14 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +contract Events { + event LogSwap( + string _connector, + address indexed buyToken, + address indexed sellToken, + uint256 buyAmt, + uint256 sellAmt, + uint256 getId, + uint256 setId + ); +} diff --git a/contracts/mainnet/connectors/swap/helpers.sol b/contracts/mainnet/connectors/swap/helpers.sol new file mode 100644 index 00000000..c111767b --- /dev/null +++ b/contracts/mainnet/connectors/swap/helpers.sol @@ -0,0 +1,102 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma abicoder v2; + +import { InstaConnectors } from "../../common/interfaces.sol"; + +abstract contract Helper { + /** + * @dev Instadapp Connectors Registry + */ + InstaConnectors internal constant instaConnectors = + InstaConnectors(0x97b0B3A8bDeFE8cB9563a3c610019Ad10DB8aD11); + + struct InputData { + address buyAddr; + address sellAddr; + uint256 sellAmt; + uint256[] unitAmts; + bytes[] callDatas; + uint256 setId; + } +} + +contract SwapHelpers is Helper { + /** + *@dev Swap using the dex aggregators. + *@param _connectors name of the connectors in preference order. + *@param _inputData data for the swap cast. + */ + function _swap(string[] memory _connectors, InputData memory _inputData) + internal + returns ( + bool success, + bytes memory returnData, + string memory _connector + ) + { + require(_connectors.length > 0, "zero-length-not-allowed"); + require( + _inputData.unitAmts.length == _connectors.length, + "unitAmts-length-invalid" + ); + require( + _inputData.callDatas.length == _connectors.length, + "callDatas-length-invalid" + ); + + // require _connectors[i] == "1INCH-A" || "ZEROX-A" || "PARASWAP-A" || similar connectors + + for (uint256 i = 0; i < _connectors.length; i++) { + bytes4 swapData = bytes4( + keccak256("swap(address,address,uint256,uint256,bytes,uint256)") + ); + + string memory _1INCH = "1INCH-A"; + if (keccak256(bytes(_connectors[i])) == keccak256(bytes(_1INCH))) { + swapData = bytes4( + keccak256( + "sell(address,address,uint256,uint256,bytes,uint256)" + ) + ); + } + + bytes memory _data = abi.encodeWithSelector( + swapData, + _inputData.buyAddr, + _inputData.sellAddr, + _inputData.sellAmt, + _inputData.unitAmts[i], + _inputData.callDatas[i], + _inputData.setId + ); + + (success, returnData) = instaConnectors + .connectors(_connectors[i]) + .delegatecall(_data); + if (success) { + _connector = _connectors[i]; + break; + } + } + } + + function decodeEvents(string memory _connector, bytes memory returnData) + internal + view + returns (uint256 _buyAmt, uint256 _sellAmt) + { + (, bytes memory _eventParam) = abi.decode(returnData, (string, bytes)); + if (keccak256(bytes(_connector)) == keccak256(bytes("PARASWAP-A"))) { + (, , _buyAmt, _sellAmt, ) = abi.decode( + _eventParam, + (address, address, uint256, uint256, uint256) + ); + } else { + (, , _buyAmt, _sellAmt, , ) = abi.decode( + _eventParam, + (address, address, uint256, uint256, uint256, uint256) + ); + } + } +} diff --git a/contracts/mainnet/connectors/swap/main.sol b/contracts/mainnet/connectors/swap/main.sol new file mode 100644 index 00000000..494e5281 --- /dev/null +++ b/contracts/mainnet/connectors/swap/main.sol @@ -0,0 +1,78 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +/** + * @title Swap. + * @dev Swap integration for DEX Aggregators. + */ + +// import files +import { SwapHelpers } from "./helpers.sol"; +import { Events } from "./events.sol"; + +abstract contract Swap is SwapHelpers, Events { + /** + * @dev Swap ETH/ERC20_Token using dex aggregators. + * @notice Swap tokens from exchanges like 1INCH, 0x etc, with calculation done off-chain. + * @param _connectors The name of the connectors like 1INCH-A, 0x etc, in order of their priority. + * @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 unitAmts The amount of buyAmt/sellAmt with slippage for respective DEXs. + * @param callDatas Data from APIs for respective DEXs. + * @param setId ID stores the amount of token brought. + */ + function swap( + string[] memory _connectors, + address buyAddr, + address sellAddr, + uint256 sellAmt, + uint256[] memory unitAmts, + bytes[] calldata callDatas, + uint256 setId + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + InputData memory inputData = InputData({ + buyAddr: buyAddr, + sellAddr: sellAddr, + sellAmt: sellAmt, + unitAmts: unitAmts, + callDatas: callDatas, + setId: setId + }); + + ( + bool success, + bytes memory returnData, + string memory _connector + ) = _swap(_connectors, inputData); + + uint256 _buyAmt; + uint256 _sellAmt; + + if (!success) { + revert("swap-failed"); + } else { + (_buyAmt, _sellAmt) = decodeEvents(_connector, returnData); + } + + _eventName = "LogSwap(string,address,address,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode( + _connector, + buyAddr, + sellAddr, + _buyAmt, + _sellAmt, + 0, + setId + ); + } +} + +contract ConnectV2Swap is Swap { + string public name = "Swap-v1"; +}