From e904ed718dae2f50e11a838706041c6830174715 Mon Sep 17 00:00:00 2001 From: Richa-iitr Date: Sat, 30 Apr 2022 15:10:43 +0530 Subject: [PATCH] added helpers --- .../uniswap/{v3-swap => v3_swap}/events.sol | 0 .../connectors/uniswap/v3_swap/helpers.sol | 16 + .../{v3-swap => v3_swap}/interface.sol | 0 .../uniswap/{v3-swap => v3_swap}/main.sol | 15 +- .../uniswap/{v3-swap => v3_swap}/events.sol | 0 .../connectors/uniswap/v3_swap/helpers.sol | 16 + .../{v3-swap => v3_swap}/interface.sol | 0 .../uniswap/{v3-swap => v3_swap}/main.sol | 14 +- .../connectors/uniswap/v3-swap/events.sol | 22 - .../connectors/uniswap/v3-swap/interface.sol | 107 ----- .../connectors/uniswap/v3_swap/events.sol | 22 + .../connectors/uniswap/v3_swap/helpers.sol | 16 + .../connectors/uniswap/v3_swap/interface.sol | 110 +++++ .../uniswap/{v3-swap => v3_swap}/main.sol | 14 +- .../uniswap/{v3-swap => v3_swap}/events.sol | 0 .../connectors/uniswap/v3_swap/helpers.sol | 16 + .../{v3-swap => v3_swap}/interface.sol | 0 .../uniswap/{v3-swap => v3_swap}/main.sol | 15 +- test/mainnet/uniswap/uniswap-swap.test.ts | 408 ++++++++++++++++++ 19 files changed, 620 insertions(+), 171 deletions(-) rename contracts/arbitrum/connectors/uniswap/{v3-swap => v3_swap}/events.sol (100%) create mode 100644 contracts/arbitrum/connectors/uniswap/v3_swap/helpers.sol rename contracts/arbitrum/connectors/uniswap/{v3-swap => v3_swap}/interface.sol (100%) rename contracts/arbitrum/connectors/uniswap/{v3-swap => v3_swap}/main.sol (90%) rename contracts/mainnet/connectors/uniswap/{v3-swap => v3_swap}/events.sol (100%) create mode 100644 contracts/mainnet/connectors/uniswap/v3_swap/helpers.sol rename contracts/mainnet/connectors/uniswap/{v3-swap => v3_swap}/interface.sol (100%) rename contracts/mainnet/connectors/uniswap/{v3-swap => v3_swap}/main.sol (90%) delete mode 100644 contracts/optimism/connectors/uniswap/v3-swap/events.sol delete mode 100644 contracts/optimism/connectors/uniswap/v3-swap/interface.sol create mode 100644 contracts/optimism/connectors/uniswap/v3_swap/events.sol create mode 100644 contracts/optimism/connectors/uniswap/v3_swap/helpers.sol create mode 100644 contracts/optimism/connectors/uniswap/v3_swap/interface.sol rename contracts/optimism/connectors/uniswap/{v3-swap => v3_swap}/main.sol (90%) rename contracts/polygon/connectors/uniswap/{v3-swap => v3_swap}/events.sol (100%) create mode 100644 contracts/polygon/connectors/uniswap/v3_swap/helpers.sol rename contracts/polygon/connectors/uniswap/{v3-swap => v3_swap}/interface.sol (100%) rename contracts/polygon/connectors/uniswap/{v3-swap => v3_swap}/main.sol (89%) create mode 100644 test/mainnet/uniswap/uniswap-swap.test.ts diff --git a/contracts/arbitrum/connectors/uniswap/v3-swap/events.sol b/contracts/arbitrum/connectors/uniswap/v3_swap/events.sol similarity index 100% rename from contracts/arbitrum/connectors/uniswap/v3-swap/events.sol rename to contracts/arbitrum/connectors/uniswap/v3_swap/events.sol diff --git a/contracts/arbitrum/connectors/uniswap/v3_swap/helpers.sol b/contracts/arbitrum/connectors/uniswap/v3_swap/helpers.sol new file mode 100644 index 00000000..554cb719 --- /dev/null +++ b/contracts/arbitrum/connectors/uniswap/v3_swap/helpers.sol @@ -0,0 +1,16 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.6; +pragma abicoder v2; + +import {TokenInterface} from "../../../common/interfaces.sol"; +import {DSMath} from "../../../common/math.sol"; +import {Basic} from "../../../common/basic.sol"; +import "./interface.sol"; + +abstract contract Helpers is DSMath, Basic { + /** + * @dev uniswap v3 Swap Router + */ + ISwapRouter constant swapRouter = + ISwapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564); +} \ No newline at end of file diff --git a/contracts/arbitrum/connectors/uniswap/v3-swap/interface.sol b/contracts/arbitrum/connectors/uniswap/v3_swap/interface.sol similarity index 100% rename from contracts/arbitrum/connectors/uniswap/v3-swap/interface.sol rename to contracts/arbitrum/connectors/uniswap/v3_swap/interface.sol diff --git a/contracts/arbitrum/connectors/uniswap/v3-swap/main.sol b/contracts/arbitrum/connectors/uniswap/v3_swap/main.sol similarity index 90% rename from contracts/arbitrum/connectors/uniswap/v3-swap/main.sol rename to contracts/arbitrum/connectors/uniswap/v3_swap/main.sol index 119877e6..b02b6d93 100644 --- a/contracts/arbitrum/connectors/uniswap/v3-swap/main.sol +++ b/contracts/arbitrum/connectors/uniswap/v3_swap/main.sol @@ -7,19 +7,12 @@ pragma abicoder v2; * @dev Decentralized Exchange. */ -import { TokenInterface } from "../../../common/interfaces.sol"; -import { Events } from "./events.sol"; -import { DSMath } from "../../../common/math.sol"; -import { Basic } from "../../../common/basic.sol"; +import {TokenInterface} from "../../../common/interfaces.sol"; +import {Helpers} from "./helpers.sol"; +import {Events} from "./events.sol"; import "./interface.sol"; -abstract contract UniswapResolver is DSMath, Events, Basic { - /** - * @dev uniswap v3 Swap Router - */ - ISwapRouter constant swapRouter = - ISwapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564); - +abstract contract UniswapResolver is Helpers, Events { /** * @dev Buy Function * @notice Swap token(sellAddr) with token(buyAddr), buy token with minimum sell token diff --git a/contracts/mainnet/connectors/uniswap/v3-swap/events.sol b/contracts/mainnet/connectors/uniswap/v3_swap/events.sol similarity index 100% rename from contracts/mainnet/connectors/uniswap/v3-swap/events.sol rename to contracts/mainnet/connectors/uniswap/v3_swap/events.sol diff --git a/contracts/mainnet/connectors/uniswap/v3_swap/helpers.sol b/contracts/mainnet/connectors/uniswap/v3_swap/helpers.sol new file mode 100644 index 00000000..554cb719 --- /dev/null +++ b/contracts/mainnet/connectors/uniswap/v3_swap/helpers.sol @@ -0,0 +1,16 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.6; +pragma abicoder v2; + +import {TokenInterface} from "../../../common/interfaces.sol"; +import {DSMath} from "../../../common/math.sol"; +import {Basic} from "../../../common/basic.sol"; +import "./interface.sol"; + +abstract contract Helpers is DSMath, Basic { + /** + * @dev uniswap v3 Swap Router + */ + ISwapRouter constant swapRouter = + ISwapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564); +} \ No newline at end of file diff --git a/contracts/mainnet/connectors/uniswap/v3-swap/interface.sol b/contracts/mainnet/connectors/uniswap/v3_swap/interface.sol similarity index 100% rename from contracts/mainnet/connectors/uniswap/v3-swap/interface.sol rename to contracts/mainnet/connectors/uniswap/v3_swap/interface.sol diff --git a/contracts/mainnet/connectors/uniswap/v3-swap/main.sol b/contracts/mainnet/connectors/uniswap/v3_swap/main.sol similarity index 90% rename from contracts/mainnet/connectors/uniswap/v3-swap/main.sol rename to contracts/mainnet/connectors/uniswap/v3_swap/main.sol index 1c82766f..ddd14a75 100644 --- a/contracts/mainnet/connectors/uniswap/v3-swap/main.sol +++ b/contracts/mainnet/connectors/uniswap/v3_swap/main.sol @@ -7,18 +7,12 @@ pragma abicoder v2; * @dev Decentralized Exchange. */ -import { TokenInterface } from "../../../common/interfaces.sol"; -import { Events } from "./events.sol"; -import { DSMath } from "../../../common/math.sol"; -import { Basic } from "../../../common/basic.sol"; +import {TokenInterface} from "../../../common/interfaces.sol"; +import {Helpers} from "./helpers.sol"; +import {Events} from "./events.sol"; import "./interface.sol"; -abstract contract UniswapResolver is DSMath, Events, Basic { - /** - * @dev uniswap v3 Swap Router - */ - ISwapRouter constant swapRouter = - ISwapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564); +abstract contract UniswapResolver is Helpers, Events { /** * @dev Buy Function diff --git a/contracts/optimism/connectors/uniswap/v3-swap/events.sol b/contracts/optimism/connectors/uniswap/v3-swap/events.sol deleted file mode 100644 index 13b9699c..00000000 --- a/contracts/optimism/connectors/uniswap/v3-swap/events.sol +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.7.0; - -contract Events { - event LogBuy( - address indexed buyToken, - address indexed sellToken, - uint256 buyAmt, - uint256 sellAmt, - uint256 getId, - uint256 setId - ); - - event LogSell( - address indexed buyToken, - address indexed sellToken, - uint256 buyAmt, - uint256 sellAmt, - uint256 getId, - uint256 setId - ); -} diff --git a/contracts/optimism/connectors/uniswap/v3-swap/interface.sol b/contracts/optimism/connectors/uniswap/v3-swap/interface.sol deleted file mode 100644 index bd6f1255..00000000 --- a/contracts/optimism/connectors/uniswap/v3-swap/interface.sol +++ /dev/null @@ -1,107 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.7.6; -pragma abicoder v2; - -import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; -import "@openzeppelin/contracts/token/ERC721/IERC721Metadata.sol"; -import "@openzeppelin/contracts/token/ERC721/IERC721Enumerable.sol"; - -/** - * @title Callback for IUniswapV3PoolActions#swap - * @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface - */ -interface IUniswapV3SwapCallback { - /** - * @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap. - * @dev In the implementation you must pay the pool tokens owed for the swap. - * The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory. - * amount0Delta and amount1Delta can both be 0 if no tokens were swapped. - * @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by - * the end of the swap. If positive, the callback must send that amount of token0 to the pool. - * @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by - * the end of the swap. If positive, the callback must send that amount of token1 to the pool. - * @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call - */ - function uniswapV3SwapCallback( - int256 amount0Delta, - int256 amount1Delta, - bytes calldata data - ) external; -} - -interface ISwapRouter is IUniswapV3SwapCallback { - struct ExactInputSingleParams { - address tokenIn; - address tokenOut; - uint24 fee; - address recipient; - uint256 deadline; - uint256 amountIn; - uint256 amountOutMinimum; - uint160 sqrtPriceLimitX96; - } - /** - * @notice Swaps `amountIn` of one token for as much as possible of another token - * @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata - * @return amountOut The amount of the received token - */ - function exactInputSingle(ExactInputSingleParams calldata params) - external - payable - returns (uint256 amountOut); - - struct ExactInputParams { - bytes path; - address recipient; - uint256 deadline; - uint256 amountIn; - uint256 amountOutMinimum; - } - /** - * @notice Swaps `amountIn` of one token for as much as possible of another along the specified path - * @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata - * @return amountOut The amount of the received token - */ - function exactInput(ExactInputParams calldata params) - external - payable - returns (uint256 amountOut); - - struct ExactOutputSingleParams { - address tokenIn; - address tokenOut; - uint24 fee; - address recipient; - uint256 deadline; - uint256 amountOut; - uint256 amountInMaximum; - uint160 sqrtPriceLimitX96; - } - /** - * @notice Swaps as little as possible of one token for `amountOut` of another token - * @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata - * @return amountIn The amount of the input token - */ - function exactOutputSingle(ExactOutputSingleParams calldata params) - external - payable - returns (uint256 amountIn); - - struct ExactOutputParams { - bytes path; - address recipient; - uint256 deadline; - uint256 amountOut; - uint256 amountInMaximum; - } - - /** - * @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed) - * @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata - * @return amountIn The amount of the input token - */ - function exactOutput(ExactOutputParams calldata params) - external - payable - returns (uint256 amountIn); -} \ No newline at end of file diff --git a/contracts/optimism/connectors/uniswap/v3_swap/events.sol b/contracts/optimism/connectors/uniswap/v3_swap/events.sol new file mode 100644 index 00000000..107e5f77 --- /dev/null +++ b/contracts/optimism/connectors/uniswap/v3_swap/events.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +contract Events { + event LogBuy( + address indexed buyToken, + address indexed sellToken, + uint256 buyAmt, + uint256 sellAmt, + uint256 getId, + uint256 setId + ); + + event LogSell( + address indexed buyToken, + address indexed sellToken, + uint256 buyAmt, + uint256 sellAmt, + uint256 getId, + uint256 setId + ); +} diff --git a/contracts/optimism/connectors/uniswap/v3_swap/helpers.sol b/contracts/optimism/connectors/uniswap/v3_swap/helpers.sol new file mode 100644 index 00000000..554cb719 --- /dev/null +++ b/contracts/optimism/connectors/uniswap/v3_swap/helpers.sol @@ -0,0 +1,16 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.6; +pragma abicoder v2; + +import {TokenInterface} from "../../../common/interfaces.sol"; +import {DSMath} from "../../../common/math.sol"; +import {Basic} from "../../../common/basic.sol"; +import "./interface.sol"; + +abstract contract Helpers is DSMath, Basic { + /** + * @dev uniswap v3 Swap Router + */ + ISwapRouter constant swapRouter = + ISwapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564); +} \ No newline at end of file diff --git a/contracts/optimism/connectors/uniswap/v3_swap/interface.sol b/contracts/optimism/connectors/uniswap/v3_swap/interface.sol new file mode 100644 index 00000000..905fce49 --- /dev/null +++ b/contracts/optimism/connectors/uniswap/v3_swap/interface.sol @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.6; +pragma abicoder v2; + +import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import "@openzeppelin/contracts/token/ERC721/IERC721Metadata.sol"; +import "@openzeppelin/contracts/token/ERC721/IERC721Enumerable.sol"; + +/** + * @title Callback for IUniswapV3PoolActions#swap + * @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface + */ +interface IUniswapV3SwapCallback { + /** + * @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap. + * @dev In the implementation you must pay the pool tokens owed for the swap. + * The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory. + * amount0Delta and amount1Delta can both be 0 if no tokens were swapped. + * @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by + * the end of the swap. If positive, the callback must send that amount of token0 to the pool. + * @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by + * the end of the swap. If positive, the callback must send that amount of token1 to the pool. + * @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call + */ + function uniswapV3SwapCallback( + int256 amount0Delta, + int256 amount1Delta, + bytes calldata data + ) external; +} + +interface ISwapRouter is IUniswapV3SwapCallback { + struct ExactInputSingleParams { + address tokenIn; + address tokenOut; + uint24 fee; + address recipient; + uint256 deadline; + uint256 amountIn; + uint256 amountOutMinimum; + uint160 sqrtPriceLimitX96; + } + + /** + * @notice Swaps `amountIn` of one token for as much as possible of another token + * @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata + * @return amountOut The amount of the received token + */ + function exactInputSingle(ExactInputSingleParams calldata params) + external + payable + returns (uint256 amountOut); + + struct ExactInputParams { + bytes path; + address recipient; + uint256 deadline; + uint256 amountIn; + uint256 amountOutMinimum; + } + + /** + * @notice Swaps `amountIn` of one token for as much as possible of another along the specified path + * @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata + * @return amountOut The amount of the received token + */ + function exactInput(ExactInputParams calldata params) + external + payable + returns (uint256 amountOut); + + struct ExactOutputSingleParams { + address tokenIn; + address tokenOut; + uint24 fee; + address recipient; + uint256 deadline; + uint256 amountOut; + uint256 amountInMaximum; + uint160 sqrtPriceLimitX96; + } + + /** + * @notice Swaps as little as possible of one token for `amountOut` of another token + * @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata + * @return amountIn The amount of the input token + */ + function exactOutputSingle(ExactOutputSingleParams calldata params) + external + payable + returns (uint256 amountIn); + + struct ExactOutputParams { + bytes path; + address recipient; + uint256 deadline; + uint256 amountOut; + uint256 amountInMaximum; + } + + /** + * @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed) + * @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata + * @return amountIn The amount of the input token + */ + function exactOutput(ExactOutputParams calldata params) + external + payable + returns (uint256 amountIn); +} diff --git a/contracts/optimism/connectors/uniswap/v3-swap/main.sol b/contracts/optimism/connectors/uniswap/v3_swap/main.sol similarity index 90% rename from contracts/optimism/connectors/uniswap/v3-swap/main.sol rename to contracts/optimism/connectors/uniswap/v3_swap/main.sol index fd5c30c1..9ae367d3 100644 --- a/contracts/optimism/connectors/uniswap/v3-swap/main.sol +++ b/contracts/optimism/connectors/uniswap/v3_swap/main.sol @@ -7,18 +7,12 @@ pragma abicoder v2; * @dev Decentralized Exchange. */ -import { TokenInterface } from "../../../common/interfaces.sol"; -import { DSMath } from "../../../common/math.sol"; -import { Basic } from "../../../common/basic.sol"; -import { Events } from "./events.sol"; +import {TokenInterface} from "../../../common/interfaces.sol"; +import {Helpers} from "./helpers.sol"; +import {Events} from "./events.sol"; import "./interface.sol"; -abstract contract UniswapResolver is DSMath, Events, Basic { - /** - * @dev uniswap v3 Swap Router - */ - ISwapRouter constant swapRouter = - ISwapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564); +abstract contract UniswapResolver is Helpers, Events { /** * @dev Buy Function diff --git a/contracts/polygon/connectors/uniswap/v3-swap/events.sol b/contracts/polygon/connectors/uniswap/v3_swap/events.sol similarity index 100% rename from contracts/polygon/connectors/uniswap/v3-swap/events.sol rename to contracts/polygon/connectors/uniswap/v3_swap/events.sol diff --git a/contracts/polygon/connectors/uniswap/v3_swap/helpers.sol b/contracts/polygon/connectors/uniswap/v3_swap/helpers.sol new file mode 100644 index 00000000..c7527145 --- /dev/null +++ b/contracts/polygon/connectors/uniswap/v3_swap/helpers.sol @@ -0,0 +1,16 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.6; +pragma abicoder v2; + +import { TokenInterface} from "../../../common/interfaces.sol"; +import { DSMath } from "../../../common/math.sol"; +import { Basic } from "../../../common/basic.sol"; +import "./interface.sol"; + +abstract contract Helpers is DSMath, Basic { + /** + * @dev uniswap v3 Swap Router + */ + ISwapRouter constant swapRouter = + ISwapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564); +} \ No newline at end of file diff --git a/contracts/polygon/connectors/uniswap/v3-swap/interface.sol b/contracts/polygon/connectors/uniswap/v3_swap/interface.sol similarity index 100% rename from contracts/polygon/connectors/uniswap/v3-swap/interface.sol rename to contracts/polygon/connectors/uniswap/v3_swap/interface.sol diff --git a/contracts/polygon/connectors/uniswap/v3-swap/main.sol b/contracts/polygon/connectors/uniswap/v3_swap/main.sol similarity index 89% rename from contracts/polygon/connectors/uniswap/v3-swap/main.sol rename to contracts/polygon/connectors/uniswap/v3_swap/main.sol index 33b0e9ba..2b9efdff 100644 --- a/contracts/polygon/connectors/uniswap/v3-swap/main.sol +++ b/contracts/polygon/connectors/uniswap/v3_swap/main.sol @@ -7,19 +7,12 @@ pragma abicoder v2; * @dev Decentralized Exchange. */ -import { TokenInterface } from "../../../common/interfaces.sol"; -import { Events } from "./events.sol"; -import { DSMath } from "../../../common/math.sol"; -import { Basic } from "../../../common/basic.sol"; +import {TokenInterface} from "../../../common/interfaces.sol"; +import {Helpers} from "./helpers.sol"; +import {Events} from "./events.sol"; import "./interface.sol"; -abstract contract UniswapResolver is DSMath, Events, Basic { - /** - * @dev uniswap v3 Swap Router - */ - ISwapRouter constant swapRouter = - ISwapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564); - +abstract contract UniswapResolver is Helpers, Events { /** * @dev Buy Function * @notice Swap token(sellAddr) with token(buyAddr), buy token with minimum sell token diff --git a/test/mainnet/uniswap/uniswap-swap.test.ts b/test/mainnet/uniswap/uniswap-swap.test.ts new file mode 100644 index 00000000..988c6664 --- /dev/null +++ b/test/mainnet/uniswap/uniswap-swap.test.ts @@ -0,0 +1,408 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { web3, deployments, waffle, ethers } = hre; +const { provider, deployContract } = waffle; + +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 { addLiquidity } from "../../../scripts/tests/addLiquidity"; +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import type { Signer, Contract } from "ethers"; + +import { abi } from "@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json"; +import { ConnectV2UniswapV3__factory } from "../../../typechain"; + +const FeeAmount = { + LOW: 500, + MEDIUM: 3000, + HIGH: 10000, +}; + +const TICK_SPACINGS: Record = { + 500: 10, + 3000: 60, + 10000: 200, +}; + +const USDT_ADDR = "0xdac17f958d2ee523a2206206994597c13d831ec7"; +const DAI_ADDR = "0x6b175474e89094c44da98b954eedeac495271d0f"; + +let tokenIds: any[] = []; +let liquidities: any[] = []; +const abiCoder = ethers.utils.defaultAbiCoder; + +describe("UniswapV3", function() { + const connectorName = "UniswapV3-v1"; + + let dsaWallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + let nftManager: Contract; + + 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: 14674358, + }, + }, + ], + }); + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt( + abis.core.connectorsV2, + addresses.core.connectorsV2 + ); + nftManager = await ethers.getContractAt( + abi, + "0xC36442b4a4522E871399CD717aBDD847Ab11FE88" + ); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2UniswapV3__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(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH & DAI 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") + ); + + await addLiquidity( + "dai", + dsaWallet0.address, + ethers.utils.parseEther("100000") + ); + }); + + it("Deposit ETH & USDT 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") + ); + + await addLiquidity( + "usdt", + dsaWallet0.address, + ethers.utils.parseEther("100000") + ); + }); + }); + + describe("Main", function() { + it("Should mint successfully", async function() { + const ethAmount = ethers.utils.parseEther("0.1"); // 1 ETH + const daiAmount = ethers.utils.parseEther("400"); // 1 ETH + const usdtAmount = Number(ethers.utils.parseEther("400")) / Math.pow(10, 12); // 1 ETH + const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; + + const getIds = ["0", "0"]; + const setId = "0"; + + const spells = [ + { + connector: connectorName, + method: "mint", + args: [ + ethAddress, + DAI_ADDR, + FeeAmount.MEDIUM, + getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + ethAmount, + daiAmount, + "500000000000000000", + getIds, + setId, + ], + }, + { + connector: connectorName, + method: "mint", + args: [ + DAI_ADDR, + USDT_ADDR, + FeeAmount.MEDIUM, + getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + daiAmount, + usdtAmount, + "300000000000000000", + getIds, + setId, + ], + }, + { + connector: connectorName, + method: "mint", + args: [ + ethAddress, + USDT_ADDR, + FeeAmount.MEDIUM, + getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + ethAmount, + usdtAmount, + "300000000000000000", + getIds, + setId, + ], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + let receipt = await tx.wait(); + let castEvent = new Promise((resolve, reject) => { + dsaWallet0.on( + "LogCast", + ( + origin: any, + sender: any, + value: any, + targetNames: any, + targets: any, + eventNames: any, + eventParams: any, + event: any + ) => { + const params = abiCoder.decode( + ["uint256", "uint256", "uint256", "uint256", "int24", "int24"], + eventParams[0] + ); + const params1 = abiCoder.decode( + ["uint256", "uint256", "uint256", "uint256", "int24", "int24"], + eventParams[2] + ); + tokenIds.push(params[0]); + tokenIds.push(params1[0]); + liquidities.push(params[1]); + event.removeListener(); + + resolve({ + eventNames, + }); + } + ); + + setTimeout(() => { + reject(new Error("timeout")); + }, 60000); + }); + + let event = await castEvent; + + const data = await nftManager.positions(tokenIds[0]); + + expect(data.liquidity).to.be.equals(liquidities[0]); + }).timeout(10000000000); + + it("Should deposit successfully", async function() { + const daiAmount = ethers.utils.parseEther("400"); // 1 ETH + const ethAmount = ethers.utils.parseEther("0.1"); // 1 ETH + + const getIds = ["0", "0"]; + const setId = "0"; + + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [ + tokenIds[0], + daiAmount, + ethAmount, + "500000000000000000", + getIds, + setId, + ], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + let castEvent = new Promise((resolve, reject) => { + dsaWallet0.on( + "LogCast", + ( + origin: any, + sender: any, + value: any, + targetNames: any, + targets: any, + eventNames: any, + eventParams: any, + event: any + ) => { + const params = abiCoder.decode( + ["uint256", "uint256", "uint256", "uint256"], + eventParams[0] + ); + liquidities[0] = liquidities[0].add(params[1]); + event.removeListener(); + + resolve({ + eventNames, + }); + } + ); + + setTimeout(() => { + reject(new Error("timeout")); + }, 60000); + }); + + let event = await castEvent; + + const data = await nftManager.positions(tokenIds[0]); + expect(data.liquidity).to.be.equals(liquidities[0]); + }); + + it("Should withdraw successfully", async function() { + const getId = "0"; + const setIds = ["0", "0"]; + + const data = await nftManager.positions(tokenIds[0]); + let data1 = await nftManager.positions(tokenIds[1]); + + const spells = [ + { + connector: connectorName, + method: "withdraw", + args: [tokenIds[0], data.liquidity, 0, 0, getId, setIds], + }, + { + connector: connectorName, + method: "withdraw", + args: [0, data1.liquidity, 0, 0, getId, setIds], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + data1 = await nftManager.positions(tokenIds[1]); + expect(data1.liquidity.toNumber()).to.be.equals(0); + }); + + it("Should collect successfully", async function() { + const ethAmount = ethers.utils.parseEther("0.2"); // 1 ETH + const daiAmount = ethers.utils.parseEther("800"); // 1 ETH + const getIds = ["0", "0"]; + const setIds = ["0", "0"]; + + const spells = [ + { + connector: connectorName, + method: "collect", + args: [tokenIds[0], daiAmount, ethAmount, getIds, setIds], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + }); + + it("Should burn successfully", async function() { + const spells = [ + { + connector: connectorName, + method: "burn", + args: [tokenIds[0]], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + }); + + it("Should buy successfully", async function() { + const daiAmount = ethers.utils.parseEther("400"); + const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; + const getId = "0"; + const setId = "0"; + + const spells = [ + { + connector: connectorName, + method: "buy", + args: [DAI_ADDR, ethAddress, FeeAmount.MEDIUM, daiAmount, getId, setId], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + console.log(receipt); + }); + + it("Should sell successfully", async function() { + const ethAmount = ethers.utils.parseEther("0.1"); + const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; + const getId = "0"; + const setId = "0"; + + const spells = [ + { + connector: connectorName, + method: "sell", + args: [DAI_ADDR, ethAddress, FeeAmount.MEDIUM, ethAmount, getId, setId], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + console.log(receipt); + }); + }); +}); + +const getMinTick = (tickSpacing: number) => + Math.ceil(-887272 / tickSpacing) * tickSpacing; +const getMaxTick = (tickSpacing: number) => + Math.floor(887272 / tickSpacing) * tickSpacing;