diff --git a/contracts/arbitrum/common/interfaces.sol b/contracts/arbitrum/common/interfaces.sol index f843d5fb..5fbd1b6d 100644 --- a/contracts/arbitrum/common/interfaces.sol +++ b/contracts/arbitrum/common/interfaces.sol @@ -1,5 +1,6 @@ //SPDX-License-Identifier: MIT pragma solidity ^0.7.0; +pragma abicoder v2; interface TokenInterface { function approve(address, uint256) external; @@ -26,3 +27,7 @@ interface AccountInterface { function disable(address) external; function isAuth(address) external view returns (bool); } + +interface InstaConnectors { + function isConnectors(string[] calldata) external returns (bool, address[] memory); +} diff --git a/contracts/arbitrum/connectors/swap/events.sol b/contracts/arbitrum/connectors/swap/events.sol new file mode 100644 index 00000000..45e71522 --- /dev/null +++ b/contracts/arbitrum/connectors/swap/events.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma abicoder v2; + +contract Events { + event LogSwapAggregator( + string[] connectors, + string connectorName, + string eventName, + bytes eventParam + ); +} diff --git a/contracts/arbitrum/connectors/swap/helpers.sol b/contracts/arbitrum/connectors/swap/helpers.sol new file mode 100644 index 00000000..e35584c2 --- /dev/null +++ b/contracts/arbitrum/connectors/swap/helpers.sol @@ -0,0 +1,44 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma abicoder v2; + +import { InstaConnectors } from "../../common/interfaces.sol"; + +contract SwapHelpers { + /** + * @dev Instadapp Connectors Registry + */ + InstaConnectors internal constant instaConnectors = + InstaConnectors(0x67fCE99Dd6d8d659eea2a1ac1b8881c57eb6592B); + + /** + *@dev Swap using the dex aggregators. + *@param _connectors name of the connectors in preference order. + *@param _datas data for the swap cast. + */ + function _swap(string[] memory _connectors, bytes[] memory _datas) + internal + returns ( + bool success, + bytes memory returnData, + string memory connector + ) + { + uint256 _length = _connectors.length; + require(_length > 0, "zero-length-not-allowed"); + require(_datas.length == _length, "calldata-length-invalid"); + + (bool isOk, address[] memory connectors) = instaConnectors.isConnectors( + _connectors + ); + require(isOk, "connector-names-invalid"); + + for (uint256 i = 0; i < _length; i++) { + (success, returnData) = connectors[i].delegatecall(_datas[i]); + if (success) { + connector = _connectors[i]; + break; + } + } + } +} diff --git a/contracts/arbitrum/connectors/swap/main.sol b/contracts/arbitrum/connectors/swap/main.sol new file mode 100644 index 00000000..cf6014c9 --- /dev/null +++ b/contracts/arbitrum/connectors/swap/main.sol @@ -0,0 +1,44 @@ +//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 _datas Encoded function call data including function selector encoded with parameters. + */ + function swap(string[] memory _connectors, bytes[] memory _datas) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + (bool success, bytes memory returnData, string memory connector) = _swap( + _connectors, + _datas + ); + + require(success, "swap-Aggregator-failed"); + (string memory eventName, bytes memory eventParam) = abi.decode( + returnData, + (string, bytes) + ); + + _eventName = "LogSwapAggregator(string[],string,string,bytes)"; + _eventParam = abi.encode(_connectors, connector, eventName, eventParam); + } +} + +contract ConnectV2SwapAggregatorArbitrum is Swap { + string public name = "Swap-Aggregator-v1"; +} diff --git a/contracts/avalanche/common/interfaces.sol b/contracts/avalanche/common/interfaces.sol index cffe7136..912e57b1 100644 --- a/contracts/avalanche/common/interfaces.sol +++ b/contracts/avalanche/common/interfaces.sol @@ -1,5 +1,6 @@ //SPDX-License-Identifier: MIT pragma solidity ^0.7.0; +pragma abicoder v2; interface TokenInterface { function approve(address, uint256) external; @@ -21,3 +22,7 @@ interface AccountInterface { function disable(address) external; function isAuth(address) external view returns (bool); } + +interface InstaConnectors { + function isConnectors(string[] calldata) external returns (bool, address[] memory); +} diff --git a/contracts/avalanche/connectors/swap/events.sol b/contracts/avalanche/connectors/swap/events.sol new file mode 100644 index 00000000..45e71522 --- /dev/null +++ b/contracts/avalanche/connectors/swap/events.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma abicoder v2; + +contract Events { + event LogSwapAggregator( + string[] connectors, + string connectorName, + string eventName, + bytes eventParam + ); +} diff --git a/contracts/avalanche/connectors/swap/helpers.sol b/contracts/avalanche/connectors/swap/helpers.sol new file mode 100644 index 00000000..b9207a26 --- /dev/null +++ b/contracts/avalanche/connectors/swap/helpers.sol @@ -0,0 +1,44 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma abicoder v2; + +import { InstaConnectors } from "../../common/interfaces.sol"; + +contract SwapHelpers { + /** + * @dev Instadapp Connectors Registry + */ + InstaConnectors internal constant instaConnectors = + InstaConnectors(0x127d8cD0E2b2E0366D522DeA53A787bfE9002C14); + + /** + *@dev Swap using the dex aggregators. + *@param _connectors name of the connectors in preference order. + *@param _datas data for the swap cast. + */ + function _swap(string[] memory _connectors, bytes[] memory _datas) + internal + returns ( + bool success, + bytes memory returnData, + string memory connector + ) + { + uint256 _length = _connectors.length; + require(_length > 0, "zero-length-not-allowed"); + require(_datas.length == _length, "calldata-length-invalid"); + + (bool isOk, address[] memory connectors) = instaConnectors.isConnectors( + _connectors + ); + require(isOk, "connector-names-invalid"); + + for (uint256 i = 0; i < _length; i++) { + (success, returnData) = connectors[i].delegatecall(_datas[i]); + if (success) { + connector = _connectors[i]; + break; + } + } + } +} diff --git a/contracts/avalanche/connectors/swap/main.sol b/contracts/avalanche/connectors/swap/main.sol new file mode 100644 index 00000000..a14a0ddd --- /dev/null +++ b/contracts/avalanche/connectors/swap/main.sol @@ -0,0 +1,44 @@ +//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 _datas Encoded function call data including function selector encoded with parameters. + */ + function swap(string[] memory _connectors, bytes[] memory _datas) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + (bool success, bytes memory returnData, string memory connector) = _swap( + _connectors, + _datas + ); + + require(success, "swap-Aggregator-failed"); + (string memory eventName, bytes memory eventParam) = abi.decode( + returnData, + (string, bytes) + ); + + _eventName = "LogSwapAggregator(string[],string,string,bytes)"; + _eventParam = abi.encode(_connectors, connector, eventName, eventParam); + } +} + +contract ConnectV2SwapAggregatorAvalanche is Swap { + string public name = "Swap-Aggregator-v1"; +} diff --git a/contracts/fantom/common/interfaces.sol b/contracts/fantom/common/interfaces.sol index 93042689..cf1abfee 100644 --- a/contracts/fantom/common/interfaces.sol +++ b/contracts/fantom/common/interfaces.sol @@ -1,5 +1,6 @@ //SPDX-License-Identifier: MIT pragma solidity ^0.7.0; +pragma abicoder v2; interface TokenInterface { function approve(address, uint256) external; @@ -34,3 +35,9 @@ interface AccountInterface { function isAuth(address) external view returns (bool); } + +interface InstaConnectors { + function isConnectors(string[] calldata) + external + returns (bool, address[] memory); +} diff --git a/contracts/fantom/connectors/swap/events.sol b/contracts/fantom/connectors/swap/events.sol new file mode 100644 index 00000000..45e71522 --- /dev/null +++ b/contracts/fantom/connectors/swap/events.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma abicoder v2; + +contract Events { + event LogSwapAggregator( + string[] connectors, + string connectorName, + string eventName, + bytes eventParam + ); +} diff --git a/contracts/fantom/connectors/swap/helpers.sol b/contracts/fantom/connectors/swap/helpers.sol new file mode 100644 index 00000000..c6c07ad3 --- /dev/null +++ b/contracts/fantom/connectors/swap/helpers.sol @@ -0,0 +1,44 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma abicoder v2; + +import { InstaConnectors } from "../../common/interfaces.sol"; + +contract SwapHelpers { + /** + * @dev Instadapp Connectors Registry + */ + InstaConnectors internal constant instaConnectors = + InstaConnectors(0x819910794a030403F69247E1e5C0bBfF1593B968); + + /** + *@dev Swap using the dex aggregators. + *@param _connectors name of the connectors in preference order. + *@param _datas data for the swap cast. + */ + function _swap(string[] memory _connectors, bytes[] memory _datas) + internal + returns ( + bool success, + bytes memory returnData, + string memory connector + ) + { + uint256 _length = _connectors.length; + require(_length > 0, "zero-length-not-allowed"); + require(_datas.length == _length, "calldata-length-invalid"); + + (bool isOk, address[] memory connectors) = instaConnectors.isConnectors( + _connectors + ); + require(isOk, "connector-names-invalid"); + + for (uint256 i = 0; i < _length; i++) { + (success, returnData) = connectors[i].delegatecall(_datas[i]); + if (success) { + connector = _connectors[i]; + break; + } + } + } +} diff --git a/contracts/fantom/connectors/swap/main.sol b/contracts/fantom/connectors/swap/main.sol new file mode 100644 index 00000000..e8784945 --- /dev/null +++ b/contracts/fantom/connectors/swap/main.sol @@ -0,0 +1,44 @@ +//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 _datas Encoded function call data including function selector encoded with parameters. + */ + function swap(string[] memory _connectors, bytes[] memory _datas) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + (bool success, bytes memory returnData, string memory connector) = _swap( + _connectors, + _datas + ); + + require(success, "swap-Aggregator-failed"); + (string memory eventName, bytes memory eventParam) = abi.decode( + returnData, + (string, bytes) + ); + + _eventName = "LogSwapAggregator(string[],string,string,bytes)"; + _eventParam = abi.encode(_connectors, connector, eventName, eventParam); + } +} + +contract ConnectV2SwapAggregatorFantom is Swap { + string public name = "Swap-Aggregator-v1"; +} diff --git a/contracts/mainnet/common/interfaces.sol b/contracts/mainnet/common/interfaces.sol index 0ccaf951..29f60e4b 100644 --- a/contracts/mainnet/common/interfaces.sol +++ b/contracts/mainnet/common/interfaces.sol @@ -1,5 +1,6 @@ //SPDX-License-Identifier: MIT pragma solidity ^0.7.0; +pragma abicoder v2; interface TokenInterface { function approve(address, uint256) external; @@ -27,3 +28,7 @@ interface AccountInterface { function disable(address) external; function isAuth(address) external view returns (bool); } + +interface InstaConnectors { + function isConnectors(string[] calldata) external returns (bool, address[] memory); +} diff --git a/contracts/mainnet/connectors/swap/events.sol b/contracts/mainnet/connectors/swap/events.sol new file mode 100644 index 00000000..45e71522 --- /dev/null +++ b/contracts/mainnet/connectors/swap/events.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma abicoder v2; + +contract Events { + event LogSwapAggregator( + string[] connectors, + string connectorName, + string eventName, + bytes eventParam + ); +} diff --git a/contracts/mainnet/connectors/swap/helpers.sol b/contracts/mainnet/connectors/swap/helpers.sol new file mode 100644 index 00000000..1405be37 --- /dev/null +++ b/contracts/mainnet/connectors/swap/helpers.sol @@ -0,0 +1,44 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma abicoder v2; + +import { InstaConnectors } from "../../common/interfaces.sol"; + +contract SwapHelpers { + /** + * @dev Instadapp Connectors Registry + */ + InstaConnectors internal constant instaConnectors = + InstaConnectors(0x97b0B3A8bDeFE8cB9563a3c610019Ad10DB8aD11); + + /** + *@dev Swap using the dex aggregators. + *@param _connectors name of the connectors in preference order. + *@param _datas data for the swap cast. + */ + function _swap(string[] memory _connectors, bytes[] memory _datas) + internal + returns ( + bool success, + bytes memory returnData, + string memory connector + ) + { + uint256 _length = _connectors.length; + require(_length > 0, "zero-length-not-allowed"); + require(_datas.length == _length, "calldata-length-invalid"); + + (bool isOk, address[] memory connectors) = instaConnectors.isConnectors( + _connectors + ); + require(isOk, "connector-names-invalid"); + + for (uint256 i = 0; i < _length; i++) { + (success, returnData) = connectors[i].delegatecall(_datas[i]); + if (success) { + connector = _connectors[i]; + break; + } + } + } +} diff --git a/contracts/mainnet/connectors/swap/main.sol b/contracts/mainnet/connectors/swap/main.sol new file mode 100644 index 00000000..1b539fa7 --- /dev/null +++ b/contracts/mainnet/connectors/swap/main.sol @@ -0,0 +1,44 @@ +//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 _datas Encoded function call data including function selector encoded with parameters. + */ + function swap(string[] memory _connectors, bytes[] memory _datas) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + (bool success, bytes memory returnData, string memory connector) = _swap( + _connectors, + _datas + ); + + require(success, "swap-Aggregator-failed"); + (string memory eventName, bytes memory eventParam) = abi.decode( + returnData, + (string, bytes) + ); + + _eventName = "LogSwapAggregator(string[],string,string,bytes)"; + _eventParam = abi.encode(_connectors, connector, eventName, eventParam); + } +} + +contract ConnectV2SwapAggregator is Swap { + string public name = "Swap-Aggregator-v1"; +} diff --git a/contracts/optimism/common/interfaces.sol b/contracts/optimism/common/interfaces.sol index 03211b06..797bce15 100644 --- a/contracts/optimism/common/interfaces.sol +++ b/contracts/optimism/common/interfaces.sol @@ -1,5 +1,6 @@ //SPDX-License-Identifier: MIT pragma solidity ^0.7.0; +pragma abicoder v2; interface TokenInterface { function approve(address, uint256) external; @@ -23,3 +24,7 @@ interface AccountInterface { function disable(address) external; function isAuth(address) external view returns (bool); } + +interface InstaConnectors { + function isConnectors(string[] calldata) external returns (bool, address[] memory); +} \ No newline at end of file diff --git a/contracts/optimism/connectors/swap/events.sol b/contracts/optimism/connectors/swap/events.sol new file mode 100644 index 00000000..45e71522 --- /dev/null +++ b/contracts/optimism/connectors/swap/events.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma abicoder v2; + +contract Events { + event LogSwapAggregator( + string[] connectors, + string connectorName, + string eventName, + bytes eventParam + ); +} diff --git a/contracts/optimism/connectors/swap/helpers.sol b/contracts/optimism/connectors/swap/helpers.sol new file mode 100644 index 00000000..b9207a26 --- /dev/null +++ b/contracts/optimism/connectors/swap/helpers.sol @@ -0,0 +1,44 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma abicoder v2; + +import { InstaConnectors } from "../../common/interfaces.sol"; + +contract SwapHelpers { + /** + * @dev Instadapp Connectors Registry + */ + InstaConnectors internal constant instaConnectors = + InstaConnectors(0x127d8cD0E2b2E0366D522DeA53A787bfE9002C14); + + /** + *@dev Swap using the dex aggregators. + *@param _connectors name of the connectors in preference order. + *@param _datas data for the swap cast. + */ + function _swap(string[] memory _connectors, bytes[] memory _datas) + internal + returns ( + bool success, + bytes memory returnData, + string memory connector + ) + { + uint256 _length = _connectors.length; + require(_length > 0, "zero-length-not-allowed"); + require(_datas.length == _length, "calldata-length-invalid"); + + (bool isOk, address[] memory connectors) = instaConnectors.isConnectors( + _connectors + ); + require(isOk, "connector-names-invalid"); + + for (uint256 i = 0; i < _length; i++) { + (success, returnData) = connectors[i].delegatecall(_datas[i]); + if (success) { + connector = _connectors[i]; + break; + } + } + } +} diff --git a/contracts/optimism/connectors/swap/main.sol b/contracts/optimism/connectors/swap/main.sol new file mode 100644 index 00000000..008fa4b9 --- /dev/null +++ b/contracts/optimism/connectors/swap/main.sol @@ -0,0 +1,44 @@ +//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 _datas Encoded function call data including function selector encoded with parameters. + */ + function swap(string[] memory _connectors, bytes[] memory _datas) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + (bool success, bytes memory returnData, string memory connector) = _swap( + _connectors, + _datas + ); + + require(success, "swap-Aggregator-failed"); + (string memory eventName, bytes memory eventParam) = abi.decode( + returnData, + (string, bytes) + ); + + _eventName = "LogSwapAggregator(string[],string,string,bytes)"; + _eventParam = abi.encode(_connectors, connector, eventName, eventParam); + } +} + +contract ConnectV2SwapAggregatorOptimism is Swap { + string public name = "Swap-Aggregator-v1"; +} diff --git a/contracts/polygon/common/interfaces.sol b/contracts/polygon/common/interfaces.sol index f843d5fb..94f054e1 100644 --- a/contracts/polygon/common/interfaces.sol +++ b/contracts/polygon/common/interfaces.sol @@ -1,5 +1,6 @@ //SPDX-License-Identifier: MIT pragma solidity ^0.7.0; +pragma abicoder v2; interface TokenInterface { function approve(address, uint256) external; @@ -26,3 +27,8 @@ interface AccountInterface { function disable(address) external; function isAuth(address) external view returns (bool); } + +interface InstaConnectors { + function isConnectors(string[] calldata) external returns (bool, address[] memory); +} + diff --git a/contracts/polygon/connectors/swap/events.sol b/contracts/polygon/connectors/swap/events.sol new file mode 100644 index 00000000..45e71522 --- /dev/null +++ b/contracts/polygon/connectors/swap/events.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma abicoder v2; + +contract Events { + event LogSwapAggregator( + string[] connectors, + string connectorName, + string eventName, + bytes eventParam + ); +} diff --git a/contracts/polygon/connectors/swap/helpers.sol b/contracts/polygon/connectors/swap/helpers.sol new file mode 100644 index 00000000..b1badc57 --- /dev/null +++ b/contracts/polygon/connectors/swap/helpers.sol @@ -0,0 +1,44 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma abicoder v2; + +import { InstaConnectors } from "../../common/interfaces.sol"; + +contract SwapHelpers { + /** + * @dev Instadapp Connectors Registry + */ + InstaConnectors internal constant instaConnectors = + InstaConnectors(0x2A00684bFAb9717C21271E0751BCcb7d2D763c88); + + /** + *@dev Swap using the dex aggregators. + *@param _connectors name of the connectors in preference order. + *@param _datas data for the swap cast. + */ + function _swap(string[] memory _connectors, bytes[] memory _datas) + internal + returns ( + bool success, + bytes memory returnData, + string memory connector + ) + { + uint256 _length = _connectors.length; + require(_length > 0, "zero-length-not-allowed"); + require(_datas.length == _length, "calldata-length-invalid"); + + (bool isOk, address[] memory connectors) = instaConnectors.isConnectors( + _connectors + ); + require(isOk, "connector-names-invalid"); + + for (uint256 i = 0; i < _length; i++) { + (success, returnData) = connectors[i].delegatecall(_datas[i]); + if (success) { + connector = _connectors[i]; + break; + } + } + } +} diff --git a/contracts/polygon/connectors/swap/main.sol b/contracts/polygon/connectors/swap/main.sol new file mode 100644 index 00000000..8d6e8dad --- /dev/null +++ b/contracts/polygon/connectors/swap/main.sol @@ -0,0 +1,44 @@ +//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 _datas Encoded function call data including function selector encoded with parameters. + */ + function swap(string[] memory _connectors, bytes[] memory _datas) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + (bool success, bytes memory returnData, string memory connector) = _swap( + _connectors, + _datas + ); + + require(success, "swap-Aggregator-failed"); + (string memory eventName, bytes memory eventParam) = abi.decode( + returnData, + (string, bytes) + ); + + _eventName = "LogSwapAggregator(string[],string,string,bytes)"; + _eventParam = abi.encode(_connectors, connector, eventName, eventParam); + } +} + +contract ConnectV2SwapAggregatorPolygon is Swap { + string public name = "Swap-Aggregator-v1"; +} diff --git a/scripts/tests/addLiquidity.ts b/scripts/tests/addLiquidity.ts index 057f384b..7263e786 100644 --- a/scripts/tests/addLiquidity.ts +++ b/scripts/tests/addLiquidity.ts @@ -6,6 +6,7 @@ import { tokenMapping as polygonMapping } from "./polygon/tokens"; import { tokenMapping as avalancheMapping } from "./avalanche/tokens"; import { tokenMapping as optimismMapping } from "./optimism/tokens"; import { tokenMapping as arbitrumMapping } from "./arbitrum/tokens"; +import { tokenMapping as fantomMapping } from "./fantom/tokens"; const mineTx = async (tx: any) => { await (await tx).wait(); @@ -16,7 +17,8 @@ const tokenMapping: Record> = { polygon: polygonMapping, avalanche: avalancheMapping, optimism: optimismMapping, - arbitrum: arbitrumMapping + arbitrum: arbitrumMapping, + fantom: fantomMapping }; export async function addLiquidity(tokenName: string, address: any, amt: any) { diff --git a/scripts/tests/buildDSAv2.ts b/scripts/tests/buildDSAv2.ts index e54dd68d..311a5f0e 100644 --- a/scripts/tests/buildDSAv2.ts +++ b/scripts/tests/buildDSAv2.ts @@ -4,6 +4,7 @@ import { addresses as addressesPolygon } from "./polygon/addresses"; import { addresses as addressesArbitrum } from "./arbitrum/addresses"; import { addresses as addressesAvalanche } from "./avalanche/addresses"; import { addresses as addressesOptimism } from "./optimism/addresses"; +import { addresses as addressesFantom } from "./fantom/addresses"; import { addresses } from "./mainnet/addresses"; import { abis } from "../constant/abis"; import { abi } from "../../deployements/mainnet/Implementation_m1.sol/InstaImplementationM1.json"; @@ -13,6 +14,7 @@ function getAddress(network: string | undefined) { else if (network === "arbitrum") return addressesArbitrum.core.instaIndex; else if (network === "avalanche") return addressesAvalanche.core.instaIndex; else if (network === "optimism") return addressesOptimism.core.instaIndex; + else if (network === "fantom") return addressesFantom.core.instaIndex; else return addresses.core.instaIndex; } diff --git a/scripts/tests/deployAndEnableConnector.ts b/scripts/tests/deployAndEnableConnector.ts index fe3d71dd..7a713c69 100644 --- a/scripts/tests/deployAndEnableConnector.ts +++ b/scripts/tests/deployAndEnableConnector.ts @@ -4,6 +4,7 @@ import { abis } from "../constant/abis"; import { addresses as addressesArbitrum } from "./arbitrum/addresses"; import { addresses as addressesAvalanche } from "./avalanche/addresses"; import { addresses as addressesOptimism } from "./optimism/addresses"; +import { addresses as addressesFantom } from "./fantom/addresses"; import hre from "hardhat"; import type { Signer, Contract } from "ethers"; @@ -24,6 +25,7 @@ function getAddress(network: string | undefined) { else if (network === "arbitrum") return addressesArbitrum; else if (network === "avalanche") return addressesAvalanche; else if (network === "optimism") return addressesOptimism; + else if (network === "fantom") return addressesFantom; else return addresses; } diff --git a/scripts/tests/fantom/addresses.ts b/scripts/tests/fantom/addresses.ts new file mode 100644 index 00000000..53113e1d --- /dev/null +++ b/scripts/tests/fantom/addresses.ts @@ -0,0 +1,11 @@ +export const addresses: Record = { + connectors: { + // basic: "0x6214f9c4F9700fc7a50B5f9aEEB819d647406Ac7", + // auth: "0xD6daA927ad756a4022858dddcc4E26137b30DB4D", + // "INSTAPOOL-A": "0x8f1e38c53af7bD2b2bE01b9580911b7Cca504F1b", + }, + core: { + connectorsV2: "0x819910794a030403F69247E1e5C0bBfF1593B968", + instaIndex: "0x2fa042BEEB7A40A7078EaA5aC755e3842248292b", + }, +}; diff --git a/scripts/tests/fantom/tokens.ts b/scripts/tests/fantom/tokens.ts new file mode 100644 index 00000000..17523914 --- /dev/null +++ b/scripts/tests/fantom/tokens.ts @@ -0,0 +1,64 @@ +import { Provider } from "@ethersproject/abstract-provider"; +import { Signer } from "@ethersproject/abstract-signer"; +import { ethers } from "hardhat"; + +const mineTx = async (tx: any) => { + await (await tx).wait(); +}; + +export const tokens = { + ftm: { + type: "token", + symbol: "FTM", + name: "Fantom", + address: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + decimals: 18, + }, + dai: { + type: "token", + symbol: "DAI", + name: "DAI Stable", + address: "0x8D11eC38a3EB5E956B052f67Da8Bdc9bef8Abf3E", + decimals: 18, + }, + usdc: { + type: "token", + symbol: "USDC", + name: "USD Coin", + address: "0x04068DA6C83AFCFA0e13ba15A6696662335D5B75", + decimals: 6, + }, +}; + +export const tokenMapping: Record = { + usdc: { + impersonateSigner: "0x4188663a85C92EEa35b5AD3AA5cA7CeB237C6fe9", + address: "0x04068DA6C83AFCFA0e13ba15A6696662335D5B75", + abi: [ + "function mint(address _to, uint256 _amount) external returns (bool);", + ], + process: async function (owner: Signer | Provider, to: any, amt: any) { + const contract = new ethers.Contract(this.address, this.abi, owner); + + await mineTx(contract.mint(to, amt)); + }, + }, + dai: { + impersonateSigner: "0x9bdB521a97E95177BF252C253E256A60C3e14447", + address: "0x8D11eC38a3EB5E956B052f67Da8Bdc9bef8Abf3E", + abi: ["function transfer(address to, uint value)"], + process: async function (owner: Signer | Provider, to: any, amt: any) { + const contract = new ethers.Contract(this.address, this.abi, owner); + await mineTx(contract.transfer(to, amt)); + }, + }, + // inst: { + // impersonateSigner: "0x75e89d5979E4f6Fba9F97c104c2F0AFB3F1dcB88", + // address: "0x6f40d4a6237c257fff2db00fa0510deeecd303eb", + // abi: ["function transfer(address to, uint value)"], + // process: async function (owner: Signer | Provider, address: any, amt: any) { + // const contract = new ethers.Contract(this.address, this.abi, owner); + // await mineTx(contract.transfer(address, amt)); + // }, + // }, +}; diff --git a/scripts/tests/getMasterSigner.ts b/scripts/tests/getMasterSigner.ts index 040e4e4f..f97a21d0 100644 --- a/scripts/tests/getMasterSigner.ts +++ b/scripts/tests/getMasterSigner.ts @@ -4,6 +4,7 @@ import { addresses as addressesPolygon } from "./polygon/addresses"; import { addresses as addressesArbitrum } from "./arbitrum/addresses"; import { addresses as addressesAvalanche } from "./avalanche/addresses"; import { addresses as addressesOptimism } from "./optimism/addresses"; +import { addresses as addressesFantom } from "./fantom/addresses"; import { abis } from "../constant/abis"; function getAddress(network: string | undefined) { @@ -11,6 +12,7 @@ function getAddress(network: string | undefined) { else if (network === "arbitrum") return addressesArbitrum.core.instaIndex; else if (network === "avalanche") return addressesAvalanche.core.instaIndex; else if (network === "optimism") return addressesOptimism.core.instaIndex; + else if (network === "fantom") return addressesFantom.core.instaIndex; else return addresses.core.instaIndex; } diff --git a/scripts/tests/run-tests.ts b/scripts/tests/run-tests.ts index 5b2f22bd..d8edc081 100644 --- a/scripts/tests/run-tests.ts +++ b/scripts/tests/run-tests.ts @@ -12,7 +12,7 @@ async function testRunner() { name: "chain", message: "What chain do you want to run tests on?", type: "list", - choices: ["mainnet", "polygon", "avalanche", "arbitrum", "optimism"], + choices: ["mainnet", "polygon", "avalanche", "arbitrum", "optimism", "fantom"], }, ]); const testsPath = join(__dirname, "../../test", chain); diff --git a/test/arbitrum/swap/swap-test.ts b/test/arbitrum/swap/swap-test.ts new file mode 100644 index 00000000..7ea8416e --- /dev/null +++ b/test/arbitrum/swap/swap-test.ts @@ -0,0 +1,151 @@ +import hre from "hardhat"; +import axios from "axios"; +import { expect } from "chai"; +const { ethers } = hre; //check +import { BigNumber } from "bignumber.js"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/arbitrum/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2SwapAggregatorArbitrum__factory } from "../../../typechain"; +import er20abi from "../../../scripts/constant/abi/basics/erc20.json"; +import type { Signer, Contract } from "ethers"; + +describe("Swap | Arbitrum", function () { + const connectorName = "swap-test"; + + let dsaWallet0: Contract; + let wallet0: Signer, wallet1: Signer; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url + } + } + ] + }); + [wallet0, wallet1] = await ethers.getSigners(); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2SwapAggregatorArbitrum__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(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(await wallet0.getAddress()); + console.log(dsaWallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit matic 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 the tokens", async function () { + let buyTokenAmount1Inch: any; + let buyTokenAmountParaswap: any; + + async function getArg() { + const slippage = 0.5; + /* eth -> dai */ + const sellTokenAddress = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; // eth, decimals 18 + const sellTokenDecimals = 18; + const buyTokenAddress = "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1"; // DAI, decimals 18 + const buyTokenDecimals = 18; + const amount = 1; + + const srcAmount = new BigNumber(amount).times(new BigNumber(10).pow(sellTokenDecimals)).toFixed(0); + + //1inch + const paramDaiUsdc = { + buyToken: buyTokenAddress, + sellToken: sellTokenAddress, + sellAmount: "1000000000000000000", + dsaAddress: dsaWallet0.address + }; + const response1 = await axios.get("https://api.instadapp.io/defi/arbitrum/1inch/swap", { + params: paramDaiUsdc + }); + + const data1 = response1.data; + // console.log(data1); + let unitAmt1Inch = data1.unitAmt; + const calldata1Inch = data1.calldata; + buyTokenAmount1Inch = data1.buyTokenAmount; + console.log(buyTokenAmount1Inch); + + function getCallData(connector: string, unitAmt: any, callData: any) { + var abi = [ + "function swap(address,address,uint256,uint256,bytes,uint256)", + "function sell(address,address,uint256,uint256,bytes,uint256)" + ]; + var iface = new ethers.utils.Interface(abi); + const spell = connector === "1INCH-A" ? "sell" : "swap"; + let data = iface.encodeFunctionData(spell, [ + buyTokenAddress, + sellTokenAddress, + srcAmount, + unitAmt, + callData, + 0 + ]); + return data; + } + let data1Inch = ethers.utils.hexlify(await getCallData("1INCH-A", unitAmt1Inch, calldata1Inch)); + let datas = [data1Inch]; + + let connectors = ["1INCH-A"]; + return [connectors, datas]; + } + + let arg = await getArg(); + const spells = [ + { + connector: connectorName, + method: "swap", + args: arg + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), await wallet1.getAddress()); + const receipt = await tx.wait(); + + const daiToken = await ethers.getContractAt( + er20abi, + "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1" // dai address + ); + + expect(await daiToken.balanceOf(dsaWallet0.address)).to.be.gte(buyTokenAmount1Inch); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("9")); + }); + }); +}); diff --git a/test/avalanche/swap/swap-test.ts b/test/avalanche/swap/swap-test.ts new file mode 100644 index 00000000..e1aee935 --- /dev/null +++ b/test/avalanche/swap/swap-test.ts @@ -0,0 +1,188 @@ +import hre from "hardhat"; +import axios from "axios"; +import { expect } from "chai"; +const { ethers } = hre; //check +import { BigNumber } from "bignumber.js"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/avalanche/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2SwapAggregatorAvalanche__factory } from "../../../typechain"; +import er20abi from "../../../scripts/constant/abi/basics/erc20.json"; +import type { Signer, Contract } from "ethers"; + +describe("Swap | Avalanche", function () { + const connectorName = "swap-test"; + + let dsaWallet0: Contract; + let wallet0: Signer, wallet1: Signer; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url + } + } + ] + }); + [wallet0, wallet1] = await ethers.getSigners(); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2SwapAggregatorAvalanche__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(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(await wallet0.getAddress()); + console.log(dsaWallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit matic 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 the tokens", async function () { + let buyTokenAmountZeroX: any; + let unitAmount1Inch: any; + let calldata1Inch: any; + // let buyTokenAmount1Inch: any; + let buyTokenAmountParaswap: any; + + async function getArg() { + // const slippage = 0.5; + /* avax -> usdt */ + const sellTokenAddress = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; // matic, decimals 18 + const sellTokenDecimals = 18; + const buyTokenAddress = "0xd586E7F844cEa2F87f50152665BCbc2C279D8d70"; // USDT, decimals 6 + const buyTokenDecimals = 18; + const amount = 1; + + const srcAmount = new BigNumber(amount).times(new BigNumber(10).pow(sellTokenDecimals)).toFixed(0); + + let zeroXUrl = `https://avalanche.api.0x.org/swap/v1/quote`; + let paraswapUrl1 = `https://apiv5.paraswap.io/prices/`; + let paraswapUrl2 = `https://apiv5.paraswap.io/transactions/43114?ignoreChecks=true`; + + //paraswap + let paramsPara = { + srcToken: sellTokenAddress, + destToken: buyTokenAddress, + srcDecimals: sellTokenDecimals, + destDecimals: buyTokenDecimals, + amount: srcAmount, + side: "SELL", + network: 43114 + }; + + const priceRoute = (await axios.get(paraswapUrl1, { params: paramsPara })).data.priceRoute; + buyTokenAmountParaswap = priceRoute.destAmount; + let minAmount = new BigNumber(priceRoute.destAmount).times((100 - 1) / 100).toFixed(0); + + let txConfig = { + priceRoute: priceRoute, + srcToken: sellTokenAddress, + destToken: buyTokenAddress, + srcDecimals: sellTokenDecimals, + destDecimals: buyTokenDecimals, + srcAmount: srcAmount, + destAmount: minAmount, + userAddress: dsaWallet0.address + }; + const calldataPara = (await axios.post(paraswapUrl2, txConfig)).data.data; + + // zeroX + const paramsZeroX = { + buyToken: buyTokenAddress, + sellToken: sellTokenAddress, + sellAmount: "1000000000000000000" // Always denominated in wei + }; + + const responseZeroX = await axios.get(zeroXUrl, { params: paramsZeroX }).then((data: any) => data); + buyTokenAmountZeroX = responseZeroX.data.buyAmount; + const calldataZeroX = responseZeroX.data.data; + + let calculateUnitAmt = (buyAmount: any) => { + const buyTokenAmountRes = new BigNumber(buyAmount) + .dividedBy(new BigNumber(10).pow(buyTokenDecimals)) + .toFixed(8); + + let unitAmt: any = new BigNumber(buyTokenAmountRes).dividedBy(new BigNumber(amount)); + + unitAmt = unitAmt.multipliedBy((100 - 1) / 100); + unitAmt = unitAmt.multipliedBy(1e18).toFixed(0); + return unitAmt; + }; + + let unitAmt0x = calculateUnitAmt(buyTokenAmountZeroX); + let unitAmtParaswap = calculateUnitAmt(buyTokenAmountParaswap); + + function getCallData(connector: string, unitAmt: any, callData: any) { + var abi = [ + "function swap(address,address,uint256,uint256,bytes,uint256)", + "function sell(address,address,uint256,uint256,bytes,uint256)" + ]; + var iface = new ethers.utils.Interface(abi); + const spell = connector === "1INCH-A" ? "sell" : "swap"; + let data = iface.encodeFunctionData(spell, [buyTokenAddress, sellTokenAddress, srcAmount, unitAmt, callData, 0]); + return data; + } + let dataPara = ethers.utils.hexlify(await getCallData("PARASWAP-A", unitAmtParaswap, calldataPara)); + let dataZeroX = ethers.utils.hexlify(await getCallData("ZEROX-A", unitAmt0x, calldataZeroX)); + let datas = [dataPara, dataZeroX]; + + let connectors = ["PARASWAP-A", "ZEROX-A"]; + + return [connectors, datas]; + } + + let arg = await getArg(); + const spells = [ + { + connector: connectorName, + method: "swap", + args: arg + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), await wallet1.getAddress()); + const receipt = await tx.wait(); + + const usdtToken = await ethers.getContractAt( + er20abi, + "0xd586E7F844cEa2F87f50152665BCbc2C279D8d70" // usdt address + ); + + expect(await usdtToken.balanceOf(dsaWallet0.address)).to.be.gte(buyTokenAmountParaswap); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("9")); + }); + }); +}); diff --git a/test/fantom/swap/swap-test.ts b/test/fantom/swap/swap-test.ts new file mode 100644 index 00000000..dacfdd02 --- /dev/null +++ b/test/fantom/swap/swap-test.ts @@ -0,0 +1,175 @@ +import hre from "hardhat"; +import axios from "axios"; +import { expect } from "chai"; +const { ethers } = hre; //check +import { BigNumber } from "bignumber.js"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/fantom/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2SwapAggregatorFantom__factory } from "../../../typechain"; +import er20abi from "../../../scripts/constant/abi/basics/erc20.json"; +import type { Signer, Contract } from "ethers"; + +describe("Swap | Fantom", function () { + const connectorName = "swap-test"; + + let dsaWallet0: Contract; + let wallet0: Signer, wallet1: Signer; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url + } + } + ] + }); + [wallet0, wallet1] = await ethers.getSigners(); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2SwapAggregatorFantom__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(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(await wallet0.getAddress()); + console.log(dsaWallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit fantom 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 the tokens", async function () { + let buyTokenAmountParaswap: any; + + async function getArg() { + const slippage = 0.5; + /* eth -> dai */ + const sellTokenAddress = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; // FTM, decimals 18 + const sellTokenDecimals = 18; + const buyTokenAddress = "0x8D11eC38a3EB5E956B052f67Da8Bdc9bef8Abf3E"; // DAI, decimals 18 + const buyTokenDecimals = 18; + const amount = 1; + + const srcAmount = new BigNumber(amount).times(new BigNumber(10).pow(sellTokenDecimals)).toFixed(0); + + let paraswapUrl1 = `https://apiv5.paraswap.io/prices/`; + let paraswapUrl2 = `https://apiv5.paraswap.io/transactions/250?ignoreChecks=true`; + + //paraswap + let paramsPara = { + srcToken: sellTokenAddress, + destToken: buyTokenAddress, + srcDecimals: sellTokenDecimals, + destDecimals: buyTokenDecimals, + amount: srcAmount, + side: "SELL", + network: 250 + }; + + const priceRoute = (await axios.get(paraswapUrl1, { params: paramsPara })).data.priceRoute; + buyTokenAmountParaswap = priceRoute.destAmount; + let minAmount = new BigNumber(priceRoute.destAmount).times((100 - 1) / 100).toFixed(0); + + let txConfig = { + priceRoute: priceRoute, + srcToken: sellTokenAddress, + destToken: buyTokenAddress, + srcDecimals: sellTokenDecimals, + destDecimals: buyTokenDecimals, + srcAmount: srcAmount, + destAmount: minAmount, + userAddress: dsaWallet0.address + }; + const calldataPara = (await axios.post(paraswapUrl2, txConfig)).data.data; + + let calculateUnitAmt = (buyAmount: any) => { + const buyTokenAmountRes = new BigNumber(buyAmount) + .dividedBy(new BigNumber(10).pow(buyTokenDecimals)) + .toFixed(8); + + let unitAmt: any = new BigNumber(buyTokenAmountRes).dividedBy(new BigNumber(amount)); + + unitAmt = unitAmt.multipliedBy((100 - 1) / 100); + unitAmt = unitAmt.multipliedBy(1e18).toFixed(0); + return unitAmt; + }; + let unitAmtParaswap = calculateUnitAmt(buyTokenAmountParaswap); + + function getCallData(connector: string, unitAmt: any, callData: any) { + var abi = [ + "function swap(address,address,uint256,uint256,bytes,uint256)", + "function sell(address,address,uint256,uint256,bytes,uint256)" + ]; + var iface = new ethers.utils.Interface(abi); + const spell = connector === "1INCH-A" ? "sell" : "swap"; + let data = iface.encodeFunctionData(spell, [ + buyTokenAddress, + sellTokenAddress, + srcAmount, + unitAmt, + callData, + 0 + ]); + return data; + } + let dataPara = ethers.utils.hexlify(await getCallData("PARASWAP-A", unitAmtParaswap, calldataPara)); + let datas = [dataPara]; + + let connectors = ["PARASWAP-A"]; + return [connectors, datas]; + } + + let arg = await getArg(); + const spells = [ + { + connector: connectorName, + method: "swap", + args: arg + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), await wallet1.getAddress()); + const receipt = await tx.wait(); + + const daiToken = await ethers.getContractAt( + er20abi, + "0x8D11eC38a3EB5E956B052f67Da8Bdc9bef8Abf3E" // dai address + ); + + expect(await daiToken.balanceOf(dsaWallet0.address)).to.be.gte(buyTokenAmountParaswap); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("9")); + }); + }); +}); diff --git a/test/mainnet/swap/swap-test.ts b/test/mainnet/swap/swap-test.ts new file mode 100644 index 00000000..3c9102f1 --- /dev/null +++ b/test/mainnet/swap/swap-test.ts @@ -0,0 +1,211 @@ +import hre from "hardhat"; +import axios from "axios"; +import { expect } from "chai"; +const { ethers } = hre; //check +import { BigNumber } from "bignumber.js"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2SwapAggregator__factory } from "../../../typechain"; +import er20abi from "../../../scripts/constant/abi/basics/erc20.json"; +import type { Signer, Contract } from "ethers"; + +describe("Swap | Mainnet", function () { + const connectorName = "swap-test"; + + let dsaWallet0: Contract; + let wallet0: Signer, wallet1: Signer; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url + } + } + ] + }); + [wallet0, wallet1] = await ethers.getSigners(); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2SwapAggregator__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(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(await wallet0.getAddress()); + console.log(dsaWallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit matic 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 the tokens", async function () { + let buyTokenAmountZeroX: any; + let buyTokenAmount1Inch: any; + let buyTokenAmountParaswap: any; + + async function getArg() { + const slippage = 0.5; + /* eth -> dai */ + const sellTokenAddress = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; // eth, decimals 18 + const sellTokenDecimals = 18; + const buyTokenAddress = "0x6B175474E89094C44Da98b954EedeAC495271d0F"; // DAI, decimals 18 + const buyTokenDecimals = 18; + const amount = 1; + + const srcAmount = new BigNumber(amount).times(new BigNumber(10).pow(sellTokenDecimals)).toFixed(0); + + let zeroXUrl = `https://api.0x.org/swap/v1/quote`; + let inchUrl = `https://api.1inch.exchange/v4.0/1/swap`; + let paraswapUrl1 = `https://apiv5.paraswap.io/prices/`; + let paraswapUrl2 = `https://apiv5.paraswap.io/transactions/1?ignoreChecks=true`; + + //zeroX + const paramsZeroX = { + buyToken: "DAI", + sellToken: "ETH", + sellAmount: "1000000000000000000" // Always denominated in wei + }; + + const responseZeroX = await axios.get(zeroXUrl, { params: paramsZeroX }).then((data: any) => data); + buyTokenAmountZeroX = responseZeroX.data.buyAmount; + const calldataZeroX = responseZeroX.data.data; + + //paraswap + let paramsPara = { + srcToken: sellTokenAddress, + destToken: buyTokenAddress, + srcDecimals: sellTokenDecimals, + destDecimals: buyTokenDecimals, + amount: srcAmount, + side: "SELL", + network: 1 + }; + + const priceRoute = (await axios.get(paraswapUrl1, { params: paramsPara })).data.priceRoute; + buyTokenAmountParaswap = priceRoute.destAmount; + let minAmount = new BigNumber(priceRoute.destAmount).times((100 - 1) / 100).toFixed(0); + + let txConfig = { + priceRoute: priceRoute, + srcToken: sellTokenAddress, + destToken: buyTokenAddress, + srcDecimals: sellTokenDecimals, + destDecimals: buyTokenDecimals, + srcAmount: srcAmount, + destAmount: minAmount, + userAddress: dsaWallet0.address + }; + const calldataPara = (await axios.post(paraswapUrl2, txConfig)).data.data; + + //1inch + const paramDaiUsdc = { + buyToken: buyTokenAddress, + sellToken: sellTokenAddress, + sellAmount: "1000000000000000000", + dsaAddress: dsaWallet0.address + }; + const response1 = await axios.get("https://api.instadapp.io/defi/mainnet/1inch/swap", { + params: paramDaiUsdc + }); + + const data1 = response1.data; + // console.log(data1); + let unitAmt1Inch = data1.unitAmt; + const calldata1Inch = data1.calldata; + buyTokenAmount1Inch = data1.buyTokenAmount; + console.log(buyTokenAmount1Inch); + + let calculateUnitAmt = (buyAmount: any) => { + const buyTokenAmountRes = new BigNumber(buyAmount) + .dividedBy(new BigNumber(10).pow(buyTokenDecimals)) + .toFixed(8); + + let unitAmt: any = new BigNumber(buyTokenAmountRes).dividedBy(new BigNumber(amount)); + + unitAmt = unitAmt.multipliedBy((100 - 1) / 100); + unitAmt = unitAmt.multipliedBy(1e18).toFixed(0); + return unitAmt; + }; + let unitAmt0x = calculateUnitAmt(buyTokenAmountZeroX); + let unitAmtParaswap = calculateUnitAmt(buyTokenAmountParaswap); + + function getCallData(connector: string, unitAmt: any, callData: any) { + var abi = [ + "function swap(address,address,uint256,uint256,bytes,uint256)", + "function sell(address,address,uint256,uint256,bytes,uint256)" + ]; + var iface = new ethers.utils.Interface(abi); + const spell = connector === "1INCH-A" ? "sell" : "swap"; + let data = iface.encodeFunctionData(spell, [ + buyTokenAddress, + sellTokenAddress, + srcAmount, + unitAmt, + callData, + 0 + ]); + return data; + } + let data1Inch = ethers.utils.hexlify(await getCallData("1INCH-A", unitAmt1Inch, calldata1Inch)); + let dataPara = ethers.utils.hexlify(await getCallData("PARASWAP-A", unitAmtParaswap, calldataPara)); + let dataZeroX = ethers.utils.hexlify(await getCallData("ZEROX-A", unitAmt0x, calldataZeroX)); + let datas = [data1Inch, dataPara, dataZeroX]; + + let connectors = ["1INCH-A", "PARASWAP-A", "ZEROX-A"]; + return [connectors, datas]; + } + + let arg = await getArg(); + const spells = [ + { + connector: connectorName, + method: "swap", + args: arg + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), await wallet1.getAddress()); + const receipt = await tx.wait(); + + const daiToken = await ethers.getContractAt( + er20abi, + "0x6B175474E89094C44Da98b954EedeAC495271d0F" // dai address + ); + + expect(await daiToken.balanceOf(dsaWallet0.address)).to.be.gte(buyTokenAmount1Inch); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("9")); + }); + }); +}); diff --git a/test/optimism/swap/swap-test.ts b/test/optimism/swap/swap-test.ts new file mode 100644 index 00000000..90949d16 --- /dev/null +++ b/test/optimism/swap/swap-test.ts @@ -0,0 +1,179 @@ +import hre from "hardhat"; +import axios from "axios"; +import { expect } from "chai"; +const { ethers } = hre; //check +import { BigNumber } from "bignumber.js"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/optimism/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2SwapAggregatorOptimism__factory } from "../../../typechain"; +import er20abi from "../../../scripts/constant/abi/basics/erc20.json"; +import type { Signer, Contract } from "ethers"; + +describe("Swap | Optimism", function () { + const connectorName = "swap-test"; + + let dsaWallet0: Contract; + let wallet0: Signer, wallet1: Signer; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url + } + } + ] + }); + [wallet0, wallet1] = await ethers.getSigners(); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2SwapAggregatorOptimism__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(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(await wallet0.getAddress()); + console.log(dsaWallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit matic 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 the tokens", async function () { + let buyTokenAmountZeroX: any; + let buyTokenAmount1Inch: any; + let buyTokenAmountParaswap: any; + + async function getArg() { + const slippage = 0.5; + /* eth -> dai */ + const sellTokenAddress = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; // eth, decimals 18 + const sellTokenDecimals = 18; + const buyTokenAddress = "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1"; // DAI, decimals 6 + const buyTokenDecimals = 18; + const amount = 1; + + const srcAmount = new BigNumber(amount).times(new BigNumber(10).pow(sellTokenDecimals)).toFixed(0); + + let zeroXUrl = `https://optimism.api.0x.org/swap/v1/quote`; + + //zeroX + const paramsZeroX = { + buyToken: "DAI", + sellToken: "ETH", + sellAmount: "1000000000000000000" // Always denominated in wei + }; + + const responseZeroX = await axios.get(zeroXUrl, { params: paramsZeroX }).then((data: any) => data); + buyTokenAmountZeroX = responseZeroX.data.buyAmount; + const calldataZeroX = responseZeroX.data.data; + + //1inch + const paramDaiUsdc = { + buyToken: buyTokenAddress, + sellToken: sellTokenAddress, + sellAmount: "1000000000000000000", + dsaAddress: dsaWallet0.address + }; + const response1 = await axios.get("https://api.instadapp.io/defi/optimism/1inch/swap", { + params: paramDaiUsdc + }); + + const data1 = response1.data; + let unitAmt1Inch = data1.unitAmt; + const calldata1Inch = data1.calldata; + buyTokenAmount1Inch = data1.buyTokenAmount; + console.log(buyTokenAmount1Inch); + + let calculateUnitAmt = (buyAmount: any) => { + const buyTokenAmountRes = new BigNumber(buyAmount) + .dividedBy(new BigNumber(10).pow(buyTokenDecimals)) + .toFixed(8); + + let unitAmt: any = new BigNumber(buyTokenAmountRes).dividedBy(new BigNumber(amount)); + + unitAmt = unitAmt.multipliedBy((100 - 1) / 100); + unitAmt = unitAmt.multipliedBy(1e18).toFixed(0); + return unitAmt; + }; + let unitAmt0x = calculateUnitAmt(buyTokenAmountZeroX); + let unitAmtParaswap = calculateUnitAmt(buyTokenAmountParaswap); + + function getCallData(connector: string, unitAmt: any, callData: any) { + var abi = [ + "function swap(address,address,uint256,uint256,bytes,uint256)", + "function sell(address,address,uint256,uint256,bytes,uint256)" + ]; + var iface = new ethers.utils.Interface(abi); + const spell = connector === "1INCH-A" ? "sell" : "swap"; + let data = iface.encodeFunctionData(spell, [ + buyTokenAddress, + sellTokenAddress, + srcAmount, + unitAmt, + callData, + 0 + ]); + return data; + } + let data1Inch = ethers.utils.hexlify(await getCallData("1INCH-A", unitAmt1Inch, calldata1Inch)); + let dataZeroX = ethers.utils.hexlify(await getCallData("ZEROX-A", unitAmt0x, calldataZeroX)); + let datas = [data1Inch]; + + let connectors = ["1INCH-A"]; + return [connectors, datas]; + } + + let arg = await getArg(); + const spells = [ + { + connector: connectorName, + method: "swap", + args: arg + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), await wallet1.getAddress()); + const receipt = await tx.wait(); + + const daiToken = await ethers.getContractAt( + er20abi, + "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1" // dai address + ); + + expect(await daiToken.balanceOf(dsaWallet0.address)).to.be.gte(buyTokenAmount1Inch); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("9")); + }); + }); +}); diff --git a/test/polygon/swap/swap-test.ts b/test/polygon/swap/swap-test.ts new file mode 100644 index 00000000..0e7cb9a3 --- /dev/null +++ b/test/polygon/swap/swap-test.ts @@ -0,0 +1,203 @@ +import hre from "hardhat"; +import axios from "axios"; +import { expect } from "chai"; +const { ethers } = hre; //check +import { BigNumber } from "bignumber.js"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/polygon/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2SwapAggregatorPolygon__factory } from "../../../typechain"; +import er20abi from "../../../scripts/constant/abi/basics/erc20.json"; +import type { Signer, Contract } from "ethers"; + +describe("Swap", function () { + const connectorName = "swap-test"; + + let dsaWallet0: Contract; + let wallet0: Signer, wallet1: Signer; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url + } + } + ] + }); + [wallet0, wallet1] = await ethers.getSigners(); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2SwapAggregatorPolygon__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(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(await wallet0.getAddress()); + console.log(dsaWallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit matic into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("50") + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("Main", function () { + it("should swap the tokens", async function () { + let buyTokenAmountZeroX: any; + let buyTokenAmount1Inch: any; + let buyTokenAmountParaswap: any; + let unitAmount1Inch: any; + let calldata1Inch: any; + + async function getSelector(connector: string) { + var abi = [ + "function swap(address,address,uint256,uint256,bytes,uint256)", + "function sell(address,address,uint256,uint256,bytes,uint256)" + ]; + var iface = new ethers.utils.Interface(abi); + var id; + if (connector == "1INCH-A") { + id = iface.getSighash("sell"); + } else { + id = iface.getSighash("swap"); + } + + return id; + } + + async function getArg() { + // const slippage = 0.5; + /* matic -> usdt */ + const sellTokenAddress = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; // matic, decimals 18 + const sellTokenDecimals = 18; + const buyTokenAddress = "0xc2132d05d31c914a87c6611c10748aeb04b58e8f"; // USDT, decimals 6 + const buyTokenDecimals = 6; + const amount = 1; + + const srcAmount = new BigNumber(amount).times(new BigNumber(10).pow(sellTokenDecimals)).toFixed(0); + + let zeroXUrl = `https://polygon.api.0x.org/swap/v1/quote`; + let paraswapUrl1 = `https://apiv5.paraswap.io/prices/`; + let paraswapUrl2 = `https://apiv5.paraswap.io/transactions/137?ignoreChecks=true`; + + //zeroX + const paramsZeroX = { + buyToken: "USDT", + sellToken: "MATIC", + sellAmount: "1000000000000000000" // Always denominated in wei + }; + + const responseZeroX = await axios.get(zeroXUrl, { params: paramsZeroX }).then((data: any) => data); + buyTokenAmountZeroX = responseZeroX.data.buyAmount; + const calldataZeroX = responseZeroX.data.data; + + //paraswap + let paramsPara = { + srcToken: sellTokenAddress, + destToken: buyTokenAddress, + srcDecimals: sellTokenDecimals, + destDecimals: buyTokenDecimals, + amount: srcAmount, + side: "SELL", + network: 137 + }; + + const priceRoute = (await axios.get(paraswapUrl1, { params: paramsPara })).data.priceRoute; + buyTokenAmountParaswap = priceRoute.destAmount; + let minAmount = new BigNumber(priceRoute.destAmount).times((100 - 1) / 100).toFixed(0); + + let txConfig = { + priceRoute: priceRoute, + srcToken: sellTokenAddress, + destToken: buyTokenAddress, + srcDecimals: sellTokenDecimals, + destDecimals: buyTokenDecimals, + srcAmount: srcAmount, + destAmount: minAmount, + userAddress: dsaWallet0.address + }; + const calldataPara = (await axios.post(paraswapUrl2, txConfig)).data.data; + + let calculateUnitAmt = (buyAmount: any) => { + const buyTokenAmountRes = new BigNumber(buyAmount) + .dividedBy(new BigNumber(10).pow(buyTokenDecimals)) + .toFixed(8); + + let unitAmt: any = new BigNumber(buyTokenAmountRes).dividedBy(new BigNumber(amount)); + + unitAmt = unitAmt.multipliedBy((100 - 1) / 100); + unitAmt = unitAmt.multipliedBy(1e18).toFixed(0); + return unitAmt; + }; + let unitAmt0x = calculateUnitAmt(buyTokenAmountZeroX); + let unitAmtParaswap = calculateUnitAmt(buyTokenAmountParaswap); + + function getCallData(connector: string, unitAmt: any, callData: any) { + var abi = [ + "function swap(address,address,uint256,uint256,bytes,uint256)", + "function sell(address,address,uint256,uint256,bytes,uint256)" + ]; + var iface = new ethers.utils.Interface(abi); + const spell = connector === "1INCH-A" ? "sell" : "swap"; + let data = iface.encodeFunctionData(spell, [buyTokenAddress, sellTokenAddress, srcAmount, unitAmt, callData, 0]); + return data; + } + let dataPara = ethers.utils.hexlify(await getCallData("PARASWAP-A", unitAmtParaswap, calldataPara)); + let dataZeroX = ethers.utils.hexlify(await getCallData("ZEROX-A", unitAmt0x, calldataZeroX)); + let datas = [dataZeroX, dataPara]; + + let connectors = ["ZEROX-A", "PARASWAP-A"]; + + return [connectors, datas]; + } + + let arg = await getArg(); + const spells = [ + { + connector: connectorName, + method: "swap", + args: arg + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), await wallet1.getAddress()); + const receipt = await tx.wait(); + + const usdtToken = await ethers.getContractAt( + er20abi, + "0xc2132d05d31c914a87c6611c10748aeb04b58e8f" // usdt address + ); + + expect(await usdtToken.balanceOf(dsaWallet0.address)).to.be.gte(buyTokenAmountZeroX); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("9")); + }); + }); +});