From f327bb1a47acf90a8c778f99cf641f07c247dffd Mon Sep 17 00:00:00 2001 From: Andres Aiello Date: Wed, 1 Nov 2023 15:50:35 -0300 Subject: [PATCH] Add deadline to uniswap v2 contract --- .../multi-chain-swap/MultiChainSwap.sol | 6 +- .../MultiChainSwapTrident.strategy.sol | 54 ++++++++++++- .../MultiChainSwapUniV2.strategy.sol | 75 ++++++++++++++----- .../MultiChainSwapUniV3.strategy.sol | 54 ++++++++++++- .../test/MultiChainSwap.spec.ts | 12 +++ .../test/MultiChainSwapUniswapV3.spec.ts | 13 +++- .../example-contracts/test/test.helpers.ts | 7 +- 7 files changed, 191 insertions(+), 30 deletions(-) diff --git a/packages/example-contracts/contracts/multi-chain-swap/MultiChainSwap.sol b/packages/example-contracts/contracts/multi-chain-swap/MultiChainSwap.sol index f1f6e6b1..b0a97dfe 100644 --- a/packages/example-contracts/contracts/multi-chain-swap/MultiChainSwap.sol +++ b/packages/example-contracts/contracts/multi-chain-swap/MultiChainSwap.sol @@ -47,7 +47,8 @@ interface MultiChainSwap is ZetaReceiver { */ uint256 outTokenMinAmount, uint256 destinationChainId, - uint256 crossChaindestinationGasLimit + uint256 crossChaindestinationGasLimit, + uint deadline ) external payable; function swapTokensForTokensCrossChain( @@ -62,6 +63,7 @@ interface MultiChainSwap is ZetaReceiver { */ uint256 outTokenMinAmount, uint256 destinationChainId, - uint256 crossChaindestinationGasLimit + uint256 crossChaindestinationGasLimit, + uint deadline ) external; } diff --git a/packages/example-contracts/contracts/multi-chain-swap/MultiChainSwapTrident.strategy.sol b/packages/example-contracts/contracts/multi-chain-swap/MultiChainSwapTrident.strategy.sol index 49c64171..5e6b3eda 100644 --- a/packages/example-contracts/contracts/multi-chain-swap/MultiChainSwapTrident.strategy.sol +++ b/packages/example-contracts/contracts/multi-chain-swap/MultiChainSwapTrident.strategy.sol @@ -33,8 +33,31 @@ contract MultiChainSwapTrident is MultiChainSwap, ZetaInteractor, MultiChainSwap */ uint256 outTokenMinAmount, uint256 destinationChainId, - uint256 crossChaindestinationGasLimit + uint256 crossChaindestinationGasLimit, + uint /* deadline */ ) external payable override { + _swapETHForTokensCrossChain( + receiverAddress, + destinationOutToken, + isDestinationOutETH, + outTokenMinAmount, + destinationChainId, + crossChaindestinationGasLimit + ); + } + + function _swapETHForTokensCrossChain( + bytes calldata receiverAddress, + address destinationOutToken, + bool isDestinationOutETH, + /** + * @dev The minimum amount of tokens that receiverAddress should get, + * if it's not reached, the transaction will revert on the destination chain + */ + uint256 outTokenMinAmount, + uint256 destinationChainId, + uint256 crossChaindestinationGasLimit + ) internal { if (!_isValidChainId(destinationChainId)) revert InvalidDestinationChainId(); if (msg.value == 0) revert ValueShouldBeGreaterThanZero(); @@ -85,8 +108,35 @@ contract MultiChainSwapTrident is MultiChainSwap, ZetaInteractor, MultiChainSwap */ uint256 outTokenMinAmount, uint256 destinationChainId, - uint256 crossChaindestinationGasLimit + uint256 crossChaindestinationGasLimit, + uint /* deadline */ ) external override { + _swapTokensForTokensCrossChain( + sourceInputToken, + inputTokenAmount, + receiverAddress, + destinationOutToken, + isDestinationOutETH, + outTokenMinAmount, + destinationChainId, + crossChaindestinationGasLimit + ); + } + + function _swapTokensForTokensCrossChain( + address sourceInputToken, + uint256 inputTokenAmount, + bytes calldata receiverAddress, + address destinationOutToken, + bool isDestinationOutETH, + /** + * @dev The minimum amount of tokens that receiverAddress should get, + * if it's not reached, the transaction will revert on the destination chain + */ + uint256 outTokenMinAmount, + uint256 destinationChainId, + uint256 crossChaindestinationGasLimit + ) internal { if (!_isValidChainId(destinationChainId)) revert InvalidDestinationChainId(); if (sourceInputToken == address(0)) revert MissingSourceInputTokenAddress(); diff --git a/packages/example-contracts/contracts/multi-chain-swap/MultiChainSwapUniV2.strategy.sol b/packages/example-contracts/contracts/multi-chain-swap/MultiChainSwapUniV2.strategy.sol index 2e0a5d57..cb26d62a 100644 --- a/packages/example-contracts/contracts/multi-chain-swap/MultiChainSwapUniV2.strategy.sol +++ b/packages/example-contracts/contracts/multi-chain-swap/MultiChainSwapUniV2.strategy.sol @@ -10,7 +10,7 @@ import "./MultiChainSwap.sol"; contract MultiChainSwapUniV2 is MultiChainSwap, ZetaInteractor, MultiChainSwapErrors { using SafeERC20 for IERC20; - uint16 internal constant MAX_DEADLINE = 200; + uint internal constant MAX_DEADLINE = 200; bytes32 public constant CROSS_CHAIN_SWAP_MESSAGE = keccak256("CROSS_CHAIN_SWAP"); address public immutable uniswapV2RouterAddress; @@ -37,38 +37,57 @@ contract MultiChainSwapUniV2 is MultiChainSwap, ZetaInteractor, MultiChainSwapEr */ uint256 outTokenMinAmount, uint256 destinationChainId, - uint256 crossChaindestinationGasLimit + uint256 crossChaindestinationGasLimit, + uint deadline ) external payable override { if (!_isValidChainId(destinationChainId)) revert InvalidDestinationChainId(); - if (msg.value == 0) revert ValueShouldBeGreaterThanZero(); if ( (destinationOutToken != address(0) && isDestinationOutETH) || (destinationOutToken == address(0) && !isDestinationOutETH) ) revert OutTokenInvariant(); - uint256 zetaValueAndGas; { address[] memory path = new address[](2); path[0] = wETH; path[1] = zetaToken; - uint256[] memory amounts = uniswapV2Router.swapExactETHForTokens{value: msg.value}( 0, /// @todo Add min amount path, address(this), - block.timestamp + MAX_DEADLINE + deadline ); - zetaValueAndGas = amounts[path.length - 1]; } if (zetaValueAndGas == 0) revert ErrorSwappingTokens(); - { bool success = IERC20(zetaToken).approve(address(connector), zetaValueAndGas); if (!success) revert ErrorApprovingTokens(zetaToken); } + _swapETHForTokensCrossChain( + receiverAddress, + destinationOutToken, + isDestinationOutETH, + outTokenMinAmount, + destinationChainId, + crossChaindestinationGasLimit, + zetaValueAndGas + ); + } + function _swapETHForTokensCrossChain( + bytes calldata receiverAddress, + address destinationOutToken, + bool isDestinationOutETH, + /** + * @dev The minimum amount of tokens that receiverAddress should get, + * if it's not reached, the transaction will revert on the destination chain + */ + uint256 outTokenMinAmount, + uint256 destinationChainId, + uint256 crossChaindestinationGasLimit, + uint256 zetaValueAndGas + ) internal { connector.send( ZetaInterfaces.SendInput({ destinationChainId: destinationChainId, @@ -103,23 +122,20 @@ contract MultiChainSwapUniV2 is MultiChainSwap, ZetaInteractor, MultiChainSwapEr */ uint256 outTokenMinAmount, uint256 destinationChainId, - uint256 crossChaindestinationGasLimit + uint256 crossChaindestinationGasLimit, + uint deadline ) external override { if (!_isValidChainId(destinationChainId)) revert InvalidDestinationChainId(); - if (sourceInputToken == address(0)) revert MissingSourceInputTokenAddress(); if ( (destinationOutToken != address(0) && isDestinationOutETH) || (destinationOutToken == address(0) && !isDestinationOutETH) ) revert OutTokenInvariant(); - uint256 zetaValueAndGas; - if (sourceInputToken == zetaToken) { bool success1 = IERC20(zetaToken).transferFrom(msg.sender, address(this), inputTokenAmount); bool success2 = IERC20(zetaToken).approve(address(connector), inputTokenAmount); if (!success1 || !success2) revert ErrorTransferringTokens(zetaToken); - zetaValueAndGas = inputTokenAmount; } else { /** @@ -129,7 +145,6 @@ contract MultiChainSwapUniV2 is MultiChainSwap, ZetaInteractor, MultiChainSwapEr IERC20(sourceInputToken).safeTransferFrom(msg.sender, address(this), inputTokenAmount); IERC20(sourceInputToken).safeApprove(uniswapV2RouterAddress, inputTokenAmount); } - address[] memory path; if (sourceInputToken == wETH) { path = new address[](2); @@ -141,24 +156,48 @@ contract MultiChainSwapUniV2 is MultiChainSwap, ZetaInteractor, MultiChainSwapEr path[1] = wETH; path[2] = zetaToken; } - uint256[] memory amounts = uniswapV2Router.swapExactTokensForTokens( inputTokenAmount, 0, /// @todo Add min amount path, address(this), - block.timestamp + MAX_DEADLINE + deadline ); - zetaValueAndGas = amounts[path.length - 1]; if (zetaValueAndGas == 0) revert ErrorSwappingTokens(); } - { bool success = IERC20(zetaToken).approve(address(connector), zetaValueAndGas); if (!success) revert ErrorApprovingTokens(zetaToken); } + _swapTokensForTokensCrossChain( + sourceInputToken, + inputTokenAmount, + receiverAddress, + destinationOutToken, + isDestinationOutETH, + outTokenMinAmount, + destinationChainId, + crossChaindestinationGasLimit, + zetaValueAndGas + ); + } + function _swapTokensForTokensCrossChain( + address sourceInputToken, + uint256 inputTokenAmount, + bytes calldata receiverAddress, + address destinationOutToken, + bool isDestinationOutETH, + /** + * @dev The minimum amount of tokens that receiverAddress should get, + * if it's not reached, the transaction will revert on the destination chain + */ + uint256 outTokenMinAmount, + uint256 destinationChainId, + uint256 crossChaindestinationGasLimit, + uint256 zetaValueAndGas + ) internal { connector.send( ZetaInterfaces.SendInput({ destinationChainId: destinationChainId, diff --git a/packages/example-contracts/contracts/multi-chain-swap/MultiChainSwapUniV3.strategy.sol b/packages/example-contracts/contracts/multi-chain-swap/MultiChainSwapUniV3.strategy.sol index 3dc44410..da1f0fc7 100644 --- a/packages/example-contracts/contracts/multi-chain-swap/MultiChainSwapUniV3.strategy.sol +++ b/packages/example-contracts/contracts/multi-chain-swap/MultiChainSwapUniV3.strategy.sol @@ -42,8 +42,31 @@ contract MultiChainSwapUniV3 is MultiChainSwap, ZetaInteractor, MultiChainSwapEr */ uint256 outTokenMinAmount, uint256 destinationChainId, - uint256 crossChaindestinationGasLimit + uint256 crossChaindestinationGasLimit, + uint /* deadline */ ) external payable override { + _swapETHForTokensCrossChain( + receiverAddress, + destinationOutToken, + isDestinationOutETH, + outTokenMinAmount, + destinationChainId, + crossChaindestinationGasLimit + ); + } + + function _swapETHForTokensCrossChain( + bytes calldata receiverAddress, + address destinationOutToken, + bool isDestinationOutETH, + /** + * @dev The minimum amount of tokens that receiverAddress should get, + * if it's not reached, the transaction will revert on the destination chain + */ + uint256 outTokenMinAmount, + uint256 destinationChainId, + uint256 crossChaindestinationGasLimit + ) internal { if (!_isValidChainId(destinationChainId)) revert InvalidDestinationChainId(); if (msg.value == 0) revert ValueShouldBeGreaterThanZero(); @@ -94,8 +117,35 @@ contract MultiChainSwapUniV3 is MultiChainSwap, ZetaInteractor, MultiChainSwapEr */ uint256 outTokenMinAmount, uint256 destinationChainId, - uint256 crossChaindestinationGasLimit + uint256 crossChaindestinationGasLimit, + uint /* deadline */ ) external override { + _swapTokensForTokensCrossChain( + sourceInputToken, + inputTokenAmount, + receiverAddress, + destinationOutToken, + isDestinationOutETH, + outTokenMinAmount, + destinationChainId, + crossChaindestinationGasLimit + ); + } + + function _swapTokensForTokensCrossChain( + address sourceInputToken, + uint256 inputTokenAmount, + bytes calldata receiverAddress, + address destinationOutToken, + bool isDestinationOutETH, + /** + * @dev The minimum amount of tokens that receiverAddress should get, + * if it's not reached, the transaction will revert on the destination chain + */ + uint256 outTokenMinAmount, + uint256 destinationChainId, + uint256 crossChaindestinationGasLimit + ) internal { if (!_isValidChainId(destinationChainId)) revert InvalidDestinationChainId(); if (sourceInputToken == address(0)) revert MissingSourceInputTokenAddress(); diff --git a/packages/example-contracts/test/MultiChainSwap.spec.ts b/packages/example-contracts/test/MultiChainSwap.spec.ts index f7856d0e..2c27c52f 100644 --- a/packages/example-contracts/test/MultiChainSwap.spec.ts +++ b/packages/example-contracts/test/MultiChainSwap.spec.ts @@ -135,6 +135,7 @@ describe("MultiChainSwap tests", () => { 0, 10, MaxUint256, + MaxUint256, { value: parseUnits("1") } @@ -152,6 +153,7 @@ describe("MultiChainSwap tests", () => { false, 0, chainBId, + MaxUint256, MaxUint256 ) ).to.be.revertedWith("MissingSourceInputTokenAddress"); @@ -167,6 +169,7 @@ describe("MultiChainSwap tests", () => { false, 0, chainBId, + MaxUint256, MaxUint256 ) ).to.be.revertedWith("OutTokenInvariant"); @@ -194,6 +197,7 @@ describe("MultiChainSwap tests", () => { false, 0, chainBId, + MaxUint256, MaxUint256 ); @@ -224,6 +228,7 @@ describe("MultiChainSwap tests", () => { false, 0, chainBId, + MaxUint256, MaxUint256 ); @@ -254,6 +259,7 @@ describe("MultiChainSwap tests", () => { false, 0, chainBId, + MaxUint256, MaxUint256 ); @@ -284,6 +290,7 @@ describe("MultiChainSwap tests", () => { false, 0, chainBId, + MaxUint256, MaxUint256 ); @@ -314,6 +321,7 @@ describe("MultiChainSwap tests", () => { false, 0, chainBId, + MaxUint256, MaxUint256 ); @@ -343,6 +351,7 @@ describe("MultiChainSwap tests", () => { false, 0, chainBId, + MaxUint256, MaxUint256 ); @@ -361,6 +370,7 @@ describe("MultiChainSwap tests", () => { false, 0, chainBId + 5, + MaxUint256, MaxUint256 ); @@ -376,6 +386,7 @@ describe("MultiChainSwap tests", () => { false, 0, chainBId, + MaxUint256, MaxUint256 ); @@ -391,6 +402,7 @@ describe("MultiChainSwap tests", () => { false, 0, chainBId, + MaxUint256, MaxUint256 ); diff --git a/packages/example-contracts/test/MultiChainSwapUniswapV3.spec.ts b/packages/example-contracts/test/MultiChainSwapUniswapV3.spec.ts index 744e1640..7e88e2f9 100644 --- a/packages/example-contracts/test/MultiChainSwapUniswapV3.spec.ts +++ b/packages/example-contracts/test/MultiChainSwapUniswapV3.spec.ts @@ -3,7 +3,6 @@ import { BigNumber } from "@ethersproject/bignumber"; import { AddressZero, MaxUint256 } from "@ethersproject/constants"; import { parseEther, parseUnits } from "@ethersproject/units"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; -import { IERC20__factory } from "@zetachain/protocol-contracts/dist/typechain-types"; import chai, { expect } from "chai"; import { ethers } from "hardhat"; @@ -13,6 +12,7 @@ import { getNow } from "../lib/shared/deploy.helpers"; import { ERC20__factory, IERC20, + IERC20__factory, MultiChainSwapUniV3, MultiChainSwapZetaConnector, UniswapV2Router02__factory @@ -153,6 +153,7 @@ describe("MultiChainSwap tests", () => { 0, 10, MaxUint256, + MaxUint256, { value: parseUnits("1") } @@ -170,6 +171,7 @@ describe("MultiChainSwap tests", () => { false, 0, chainBId, + MaxUint256, MaxUint256 ) ).to.be.revertedWith("MissingSourceInputTokenAddress"); @@ -185,6 +187,7 @@ describe("MultiChainSwap tests", () => { false, 0, chainBId, + MaxUint256, MaxUint256 ) ).to.be.revertedWith("OutTokenInvariant"); @@ -208,6 +211,7 @@ describe("MultiChainSwap tests", () => { 0, chainBId, MaxUint256, + MaxUint256, { value: ZETA_TO_TRANSFER } ); const result = await tx3.wait(); @@ -243,6 +247,7 @@ describe("MultiChainSwap tests", () => { true, 0, chainBId, + MaxUint256, MaxUint256 ); const result = await tx3.wait(); @@ -276,6 +281,7 @@ describe("MultiChainSwap tests", () => { false, 0, chainBId, + MaxUint256, MaxUint256 ); await tx3.wait(); @@ -312,6 +318,7 @@ describe("MultiChainSwap tests", () => { false, 0, chainBId, + MaxUint256, MaxUint256 ); const result = await tx3.wait(); @@ -347,6 +354,7 @@ describe("MultiChainSwap tests", () => { false, 0, chainBId, + MaxUint256, MaxUint256 ); const result = await tx3.wait(); @@ -382,6 +390,7 @@ describe("MultiChainSwap tests", () => { false, 0, chainBId, + MaxUint256, MaxUint256 ); const result = await tx3.wait(); @@ -417,6 +426,7 @@ describe("MultiChainSwap tests", () => { false, 0, chainBId, + MaxUint256, MaxUint256 ); @@ -442,6 +452,7 @@ describe("MultiChainSwap tests", () => { false, 0, chainBId, + MaxUint256, MaxUint256 ); await tx3.wait(); diff --git a/packages/example-contracts/test/test.helpers.ts b/packages/example-contracts/test/test.helpers.ts index 84603c54..9603bcba 100644 --- a/packages/example-contracts/test/test.helpers.ts +++ b/packages/example-contracts/test/test.helpers.ts @@ -1,6 +1,5 @@ import { MaxUint256 } from "@ethersproject/constants"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; -import { ZetaTokenConsumerUniV3__factory } from "@zetachain/protocol-contracts/dist/typechain-types"; import { BigNumber, ContractReceipt } from "ethers"; import { getAddress } from "../lib/shared/address.helpers"; @@ -9,7 +8,8 @@ import { ERC20__factory, IUniswapV2Pair__factory, MultiChainSwapUniV2__factory, - UniswapV2Router02__factory + UniswapV2Router02__factory, + ZetaTokenConsumerUniV3__factory } from "../typechain-types"; export const getMintTokenId = (mintTx: ContractReceipt) => mintTx.events?.[0].args?.tokenId; @@ -48,17 +48,14 @@ export const parseZetaLog = (logs: ContractReceipt["logs"]) => { export const parseInteractorLog = (logs: ContractReceipt["logs"]) => { const iface = ZetaTokenConsumerUniV3__factory.createInterface(); - const eventNames = logs.map(log => { try { const parsedLog = iface.parseLog(log); - return parsedLog.name; } catch (e) { return "NO_ZETA_LOG"; } }); - return eventNames; };