From 58e1469c3ca59543f9d1fe65c9c4efbfd709184b Mon Sep 17 00:00:00 2001 From: cece-z Date: Mon, 20 Sep 2021 12:36:21 -0400 Subject: [PATCH 1/8] Add functions for multiplex --- .../integration/exchange/ZeroExApiAdapter.sol | 57 ++++++++++++++++++- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/contracts/protocol/integration/exchange/ZeroExApiAdapter.sol b/contracts/protocol/integration/exchange/ZeroExApiAdapter.sol index 1c1488a9c..5d3c9e6c7 100644 --- a/contracts/protocol/integration/exchange/ZeroExApiAdapter.sol +++ b/contracts/protocol/integration/exchange/ZeroExApiAdapter.sol @@ -129,8 +129,8 @@ contract ZeroExApiAdapter { ) } - if (selector == 0x415565b0) { - // transformERC20() + if (selector == 0x415565b0 || selector == 0x8182b61f) { + // transformERC20(), transformERC20Staging() (inputToken, outputToken, inputTokenAmount, minOutputTokenAmount) = abi.decode(_data[4:], (address, address, uint256, uint256)); } else if (selector == 0xf7fcd384) { @@ -201,7 +201,58 @@ contract ZeroExApiAdapter { (inputToken, outputToken) = _decodeTokensFromUniswapV3EncodedPath(encodedPath); require(inputToken == wethAddress, "First token must be WETH"); inputToken = ETH_ADDRESS; - } else { + } else if (selector == 0xf35b4733) { + // multiplexBatchSellEthForToken() + BatchFillData memory fillData; + (fillData, minOutputTokenAmount) = + abi.decode(_data[4:], (BatchFillData, uint256)); + inputToken = ETH_ADDRESS; + outputToken = fillData.outputToken; +// inputTokenAmount = fillData.sellAmount; + } else if (selector == 0x77725df6) { + // multiplexBatchSellTokenForEth() + BatchFillData memory fillData; + (fillData, minOutputTokenAmount) = + abi.decode(_data[4:], (BatchFillData, uint256)); + inputToken = fillData.inputToken; + outputToken = ETH_ADDRESS; + inputTokenAmount = fillData.sellAmount; + } else if (selector == 0x7a1eb1b9) { + // multiplexBatchSellTokenForToken() + BatchFillData memory fillData; + (fillData, minOutputTokenAmount) = + abi.decode(_data[4:], (BatchFillData, uint256)); + inputToken = fillData.inputToken; + outputToken = fillData.outputToken; + inputTokenAmount = fillData.sellAmount; + } else if (selector == 0x5161b966) { + // multiplexMultiHopSellEthForToken() + MultiHopFillData memory fillData; + (fillData, minOutputTokenAmount) = + abi.decode(_data[4:], (MultiHopFillData, uint256)); + require(fillData.tokens.length > 1, "Multihop token path too short"); + inputToken = ETH_ADDRESS; + outputToken = fillData.tokens[fillData.tokens.length - 1]; +// inputTokenAmount = fillData.sellAmount; + } else if (selector == 0x9a2967d2) { + // multiplexMultiHopSellTokenForEth() + MultiHopFillData memory fillData; + (fillData, minOutputTokenAmount) = + abi.decode(_data[4:], (MultiHopFillData, uint256)); + require(fillData.tokens.length > 1, "Multihop token path too short"); + inputToken = fillData.tokens[0]; + outputToken = ETH_ADDRESS; + inputTokenAmount = fillData.sellAmount; + } else if (selector == 0x0f3b31b2) { + // multiplexMultiHopSellTokenForToken() + MultiHopFillData memory fillData; + (fillData, minOutputTokenAmount) = + abi.decode(_data[4:], (MultiHopFillData, uint256)); + require(fillData.tokens.length > 1, "Multihop token path too short"); + inputToken = fillData.tokens[0]; + outputToken = fillData.tokens[fillData.tokens.length - 1]; + inputTokenAmount = fillData.sellAmount; + } else { revert("Unsupported 0xAPI function selector"); } } From 84210d6642af177ab697615dd4953c469bce3f36 Mon Sep 17 00:00:00 2001 From: cece-z Date: Tue, 21 Sep 2021 15:36:02 -0400 Subject: [PATCH 2/8] add separate struct --- .../integration/exchange/ZeroExApiAdapter.sol | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/contracts/protocol/integration/exchange/ZeroExApiAdapter.sol b/contracts/protocol/integration/exchange/ZeroExApiAdapter.sol index 5d3c9e6c7..cc532c6b2 100644 --- a/contracts/protocol/integration/exchange/ZeroExApiAdapter.sol +++ b/contracts/protocol/integration/exchange/ZeroExApiAdapter.sol @@ -52,6 +52,19 @@ contract ZeroExApiAdapter { bytes data; } + struct MultiplexBatchFillData { + address inputToken; + address outputToken; + uint256 sellAmount; + BatchSellSubcall[] calls; + } + + struct BatchSellSubcall { + uint8 subcall; + uint256 sellAmount; + bytes data; + } + /* ============ State Variables ============ */ // ETH pseudo-token address used by 0x API. @@ -203,25 +216,25 @@ contract ZeroExApiAdapter { inputToken = ETH_ADDRESS; } else if (selector == 0xf35b4733) { // multiplexBatchSellEthForToken() - BatchFillData memory fillData; + MultiplexBatchFillData memory fillData; (fillData, minOutputTokenAmount) = - abi.decode(_data[4:], (BatchFillData, uint256)); + abi.decode(_data[4:], (MultiplexBatchFillData, uint256)); inputToken = ETH_ADDRESS; outputToken = fillData.outputToken; -// inputTokenAmount = fillData.sellAmount; + inputTokenAmount = _sourceQuantity; } else if (selector == 0x77725df6) { // multiplexBatchSellTokenForEth() - BatchFillData memory fillData; + MultiplexBatchFillData memory fillData; (fillData, minOutputTokenAmount) = - abi.decode(_data[4:], (BatchFillData, uint256)); + abi.decode(_data[4:], (MultiplexBatchFillData, uint256)); inputToken = fillData.inputToken; outputToken = ETH_ADDRESS; inputTokenAmount = fillData.sellAmount; } else if (selector == 0x7a1eb1b9) { // multiplexBatchSellTokenForToken() - BatchFillData memory fillData; + MultiplexBatchFillData memory fillData; (fillData, minOutputTokenAmount) = - abi.decode(_data[4:], (BatchFillData, uint256)); + abi.decode(_data[4:], (MultiplexBatchFillData, uint256)); inputToken = fillData.inputToken; outputToken = fillData.outputToken; inputTokenAmount = fillData.sellAmount; @@ -233,7 +246,7 @@ contract ZeroExApiAdapter { require(fillData.tokens.length > 1, "Multihop token path too short"); inputToken = ETH_ADDRESS; outputToken = fillData.tokens[fillData.tokens.length - 1]; -// inputTokenAmount = fillData.sellAmount; + inputTokenAmount = _sourceQuantity; } else if (selector == 0x9a2967d2) { // multiplexMultiHopSellTokenForEth() MultiHopFillData memory fillData; From b145ad9b5d611c33233edd0e0972cb0f887e0968 Mon Sep 17 00:00:00 2001 From: cece-z Date: Tue, 21 Sep 2021 15:47:04 -0400 Subject: [PATCH 3/8] fix indent --- .../integration/exchange/ZeroExApiAdapter.sol | 90 +++++++++---------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/contracts/protocol/integration/exchange/ZeroExApiAdapter.sol b/contracts/protocol/integration/exchange/ZeroExApiAdapter.sol index cc532c6b2..f3f253a64 100644 --- a/contracts/protocol/integration/exchange/ZeroExApiAdapter.sol +++ b/contracts/protocol/integration/exchange/ZeroExApiAdapter.sol @@ -215,56 +215,56 @@ contract ZeroExApiAdapter { require(inputToken == wethAddress, "First token must be WETH"); inputToken = ETH_ADDRESS; } else if (selector == 0xf35b4733) { - // multiplexBatchSellEthForToken() - MultiplexBatchFillData memory fillData; - (fillData, minOutputTokenAmount) = - abi.decode(_data[4:], (MultiplexBatchFillData, uint256)); - inputToken = ETH_ADDRESS; - outputToken = fillData.outputToken; - inputTokenAmount = _sourceQuantity; + // multiplexBatchSellEthForToken() + MultiplexBatchFillData memory fillData; + (fillData, minOutputTokenAmount) = + abi.decode(_data[4:], (MultiplexBatchFillData, uint256)); + inputToken = ETH_ADDRESS; + outputToken = fillData.outputToken; + inputTokenAmount = _sourceQuantity; } else if (selector == 0x77725df6) { - // multiplexBatchSellTokenForEth() - MultiplexBatchFillData memory fillData; - (fillData, minOutputTokenAmount) = - abi.decode(_data[4:], (MultiplexBatchFillData, uint256)); - inputToken = fillData.inputToken; - outputToken = ETH_ADDRESS; - inputTokenAmount = fillData.sellAmount; + // multiplexBatchSellTokenForEth() + MultiplexBatchFillData memory fillData; + (fillData, minOutputTokenAmount) = + abi.decode(_data[4:], (MultiplexBatchFillData, uint256)); + inputToken = fillData.inputToken; + outputToken = ETH_ADDRESS; + inputTokenAmount = fillData.sellAmount; } else if (selector == 0x7a1eb1b9) { - // multiplexBatchSellTokenForToken() - MultiplexBatchFillData memory fillData; - (fillData, minOutputTokenAmount) = - abi.decode(_data[4:], (MultiplexBatchFillData, uint256)); - inputToken = fillData.inputToken; - outputToken = fillData.outputToken; - inputTokenAmount = fillData.sellAmount; + // multiplexBatchSellTokenForToken() + MultiplexBatchFillData memory fillData; + (fillData, minOutputTokenAmount) = + abi.decode(_data[4:], (MultiplexBatchFillData, uint256)); + inputToken = fillData.inputToken; + outputToken = fillData.outputToken; + inputTokenAmount = fillData.sellAmount; } else if (selector == 0x5161b966) { - // multiplexMultiHopSellEthForToken() - MultiHopFillData memory fillData; - (fillData, minOutputTokenAmount) = - abi.decode(_data[4:], (MultiHopFillData, uint256)); - require(fillData.tokens.length > 1, "Multihop token path too short"); - inputToken = ETH_ADDRESS; - outputToken = fillData.tokens[fillData.tokens.length - 1]; - inputTokenAmount = _sourceQuantity; + // multiplexMultiHopSellEthForToken() + MultiHopFillData memory fillData; + (fillData, minOutputTokenAmount) = + abi.decode(_data[4:], (MultiHopFillData, uint256)); + require(fillData.tokens.length > 1, "Multihop token path too short"); + inputToken = ETH_ADDRESS; + outputToken = fillData.tokens[fillData.tokens.length - 1]; + inputTokenAmount = _sourceQuantity; } else if (selector == 0x9a2967d2) { - // multiplexMultiHopSellTokenForEth() - MultiHopFillData memory fillData; - (fillData, minOutputTokenAmount) = - abi.decode(_data[4:], (MultiHopFillData, uint256)); - require(fillData.tokens.length > 1, "Multihop token path too short"); - inputToken = fillData.tokens[0]; - outputToken = ETH_ADDRESS; - inputTokenAmount = fillData.sellAmount; + // multiplexMultiHopSellTokenForEth() + MultiHopFillData memory fillData; + (fillData, minOutputTokenAmount) = + abi.decode(_data[4:], (MultiHopFillData, uint256)); + require(fillData.tokens.length > 1, "Multihop token path too short"); + inputToken = fillData.tokens[0]; + outputToken = ETH_ADDRESS; + inputTokenAmount = fillData.sellAmount; } else if (selector == 0x0f3b31b2) { - // multiplexMultiHopSellTokenForToken() - MultiHopFillData memory fillData; - (fillData, minOutputTokenAmount) = - abi.decode(_data[4:], (MultiHopFillData, uint256)); - require(fillData.tokens.length > 1, "Multihop token path too short"); - inputToken = fillData.tokens[0]; - outputToken = fillData.tokens[fillData.tokens.length - 1]; - inputTokenAmount = fillData.sellAmount; + // multiplexMultiHopSellTokenForToken() + MultiHopFillData memory fillData; + (fillData, minOutputTokenAmount) = + abi.decode(_data[4:], (MultiHopFillData, uint256)); + require(fillData.tokens.length > 1, "Multihop token path too short"); + inputToken = fillData.tokens[0]; + outputToken = fillData.tokens[fillData.tokens.length - 1]; + inputTokenAmount = fillData.sellAmount; } else { revert("Unsupported 0xAPI function selector"); } From 0f6088bb9b11fd1bbb7abef5d5c469e529415463 Mon Sep 17 00:00:00 2001 From: cece-z Date: Thu, 23 Sep 2021 00:37:21 -0400 Subject: [PATCH 4/8] Address comments --- .../integration/exchange/ZeroExApiAdapter.sol | 68 ++++++------------- 1 file changed, 22 insertions(+), 46 deletions(-) diff --git a/contracts/protocol/integration/exchange/ZeroExApiAdapter.sol b/contracts/protocol/integration/exchange/ZeroExApiAdapter.sol index f3f253a64..9a7a03061 100644 --- a/contracts/protocol/integration/exchange/ZeroExApiAdapter.sol +++ b/contracts/protocol/integration/exchange/ZeroExApiAdapter.sol @@ -52,19 +52,6 @@ contract ZeroExApiAdapter { bytes data; } - struct MultiplexBatchFillData { - address inputToken; - address outputToken; - uint256 sellAmount; - BatchSellSubcall[] calls; - } - - struct BatchSellSubcall { - uint8 subcall; - uint256 sellAmount; - bytes data; - } - /* ============ State Variables ============ */ // ETH pseudo-token address used by 0x API. @@ -216,55 +203,44 @@ contract ZeroExApiAdapter { inputToken = ETH_ADDRESS; } else if (selector == 0xf35b4733) { // multiplexBatchSellEthForToken() - MultiplexBatchFillData memory fillData; - (fillData, minOutputTokenAmount) = - abi.decode(_data[4:], (MultiplexBatchFillData, uint256)); + (outputToken, , minOutputTokenAmount) = + abi.decode(_data[4:], (address, uint256, uint256)); inputToken = ETH_ADDRESS; - outputToken = fillData.outputToken; inputTokenAmount = _sourceQuantity; } else if (selector == 0x77725df6) { // multiplexBatchSellTokenForEth() - MultiplexBatchFillData memory fillData; - (fillData, minOutputTokenAmount) = - abi.decode(_data[4:], (MultiplexBatchFillData, uint256)); - inputToken = fillData.inputToken; + (inputToken, , inputTokenAmount, minOutputTokenAmount) = + abi.decode(_data[4:], (address, uint256, uint256, uint256)); outputToken = ETH_ADDRESS; - inputTokenAmount = fillData.sellAmount; } else if (selector == 0x7a1eb1b9) { // multiplexBatchSellTokenForToken() - MultiplexBatchFillData memory fillData; - (fillData, minOutputTokenAmount) = - abi.decode(_data[4:], (MultiplexBatchFillData, uint256)); - inputToken = fillData.inputToken; - outputToken = fillData.outputToken; - inputTokenAmount = fillData.sellAmount; + (inputToken, outputToken, , inputTokenAmount, minOutputTokenAmount) = + abi.decode(_data[4:], (address, address, uint256, uint256, uint256)); } else if (selector == 0x5161b966) { // multiplexMultiHopSellEthForToken() - MultiHopFillData memory fillData; - (fillData, minOutputTokenAmount) = - abi.decode(_data[4:], (MultiHopFillData, uint256)); - require(fillData.tokens.length > 1, "Multihop token path too short"); + address[] memory tokens; + (tokens, , minOutputTokenAmount) = + abi.decode(_data[4:], (address[], uint256, uint256)); + require(tokens.length > 1, "Multihop token path too short"); inputToken = ETH_ADDRESS; - outputToken = fillData.tokens[fillData.tokens.length - 1]; + outputToken = tokens[tokens.length - 1]; inputTokenAmount = _sourceQuantity; } else if (selector == 0x9a2967d2) { // multiplexMultiHopSellTokenForEth() - MultiHopFillData memory fillData; - (fillData, minOutputTokenAmount) = - abi.decode(_data[4:], (MultiHopFillData, uint256)); - require(fillData.tokens.length > 1, "Multihop token path too short"); - inputToken = fillData.tokens[0]; + address[] memory tokens; + (tokens, , inputTokenAmount, minOutputTokenAmount) = + abi.decode(_data[4:], (address[], uint256, uint256, uint256)); + require(tokens.length > 1, "Multihop token path too short"); + inputToken = tokens[0]; outputToken = ETH_ADDRESS; - inputTokenAmount = fillData.sellAmount; } else if (selector == 0x0f3b31b2) { // multiplexMultiHopSellTokenForToken() - MultiHopFillData memory fillData; - (fillData, minOutputTokenAmount) = - abi.decode(_data[4:], (MultiHopFillData, uint256)); - require(fillData.tokens.length > 1, "Multihop token path too short"); - inputToken = fillData.tokens[0]; - outputToken = fillData.tokens[fillData.tokens.length - 1]; - inputTokenAmount = fillData.sellAmount; + address[] memory tokens; + (tokens, , inputTokenAmount, minOutputTokenAmount) = + abi.decode(_data[4:], (address[], uint256, uint256, uint256)); + require(tokens.length > 1, "Multihop token path too short"); + inputToken = tokens[0]; + outputToken = tokens[tokens.length - 1]; } else { revert("Unsupported 0xAPI function selector"); } From d86a269b4b79165d1b5b1d7abd37ffaafedcfa04 Mon Sep 17 00:00:00 2001 From: cece-z Date: Thu, 23 Sep 2021 18:16:42 -0400 Subject: [PATCH 5/8] tests for multiplex --- contracts/mocks/external/ZeroExMock.sol | 102 +++ .../exchange/zeroExApiAdapter.spec.ts | 714 +++++++++++++++++- 2 files changed, 815 insertions(+), 1 deletion(-) diff --git a/contracts/mocks/external/ZeroExMock.sol b/contracts/mocks/external/ZeroExMock.sol index bcf9247a1..1ce198064 100644 --- a/contracts/mocks/external/ZeroExMock.sol +++ b/contracts/mocks/external/ZeroExMock.sol @@ -53,6 +53,17 @@ contract ZeroExMock { bytes data; } + struct BatchSellSubcall { + uint8 subcall; + uint256 sellAmount; + bytes data; + } + + struct MultiHopSellSubcall { + uint8 subcall; + bytes data; + } + address public mockReceiveToken; address public mockSendToken; uint256 public mockReceiveAmount; @@ -91,6 +102,20 @@ contract ZeroExMock { _transferTokens(); } + function transformERC20Staging( + address /* inputToken */, + address /* outputToken */, + uint256 /* inputTokenAmount */, + uint256 /* minOutputTokenAmount */, + Transformation[] calldata /* transformations */ + ) + external + payable + returns (uint256) + { + _transferTokens(); + } + function sellToUniswap( address[] calldata /* tokens */, uint256 /* sellAmount */, @@ -178,6 +203,83 @@ contract ZeroExMock { _transferTokens(); } + function multiplexBatchSellEthForToken( + address /* outputToken */, + BatchSellSubcall[] memory /* calls */, + uint256 /* minBuyAmount */ + ) + external + payable + returns (uint256) + { + _transferTokens(); + } + + function multiplexBatchSellTokenForEth( + address /* inputToken */, + BatchSellSubcall[] memory /* calls */, + uint256 /* sellAmount */, + uint256 /* minBuyAmount */ + ) + external + payable + returns (uint256) + { + _transferTokens(); + } + + function multiplexBatchSellTokenForToken( + address /* inputToken */, + address /* outputToken */, + BatchSellSubcall[] memory /* calls */, + uint256 /* sellAmount */, + uint256 /* minBuyAmount */ + ) + external + payable + returns (uint256) + { + _transferTokens(); + } + + function multiplexMultiHopSellEthForToken( + address[] memory /* tokens */, + MultiHopSellSubcall[] memory /* calls */, + uint256 /* minBuyAmount */ + ) + external + payable + returns (uint256) + { + _transferTokens(); + } + + function multiplexMultiHopSellTokenForEth( + address[] memory /* tokens */, + MultiHopSellSubcall[] memory /* calls */, + uint256 /* sellAmount */, + uint256 /* minBuyAmount */ + ) + external + payable + returns (uint256) + { + _transferTokens(); + } + + function multiplexMultiHopSellTokenForToken( + address[] memory /* tokens */, + MultiHopSellSubcall[] memory /* calls */, + uint256 /* sellAmount */, + uint256 /* minBuyAmount */ + ) + external + payable + returns (uint256) + { + _transferTokens(); + } + function _transferTokens() private { diff --git a/test/protocol/integration/exchange/zeroExApiAdapter.spec.ts b/test/protocol/integration/exchange/zeroExApiAdapter.spec.ts index f613fdd86..27e526f81 100644 --- a/test/protocol/integration/exchange/zeroExApiAdapter.spec.ts +++ b/test/protocol/integration/exchange/zeroExApiAdapter.spec.ts @@ -170,6 +170,105 @@ describe("ZeroExApiAdapter", () => { }); }); + describe("transformERC20Staging", () => { + it("validates data", async () => { + const data = zeroExMock.interface.encodeFunctionData("transformERC20Staging", [ + sourceToken, + destToken, + sourceQuantity, + minDestinationQuantity, + [], + ]); + const [target, value, _data] = await zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + expect(target).to.eq(zeroExMock.address); + expect(value).to.deep.eq(ZERO); + expect(_data).to.deep.eq(data); + }); + + it("rejects wrong input token", async () => { + const data = zeroExMock.interface.encodeFunctionData("transformERC20Staging", [ + otherToken, + destToken, + sourceQuantity, + minDestinationQuantity, + [], + ]); + const tx = zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + await expect(tx).to.be.revertedWith("Mismatched input token"); + }); + + it("rejects wrong output token", async () => { + const data = zeroExMock.interface.encodeFunctionData("transformERC20Staging", [ + sourceToken, + otherToken, + sourceQuantity, + minDestinationQuantity, + [], + ]); + const tx = zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + await expect(tx).to.be.revertedWith("Mismatched output token"); + }); + + it("rejects wrong input token quantity", async () => { + const data = zeroExMock.interface.encodeFunctionData("transformERC20Staging", [ + sourceToken, + destToken, + otherQuantity, + minDestinationQuantity, + [], + ]); + const tx = zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + await expect(tx).to.be.revertedWith("Mismatched input token quantity"); + }); + + it("rejects wrong output token quantity", async () => { + const data = zeroExMock.interface.encodeFunctionData("transformERC20Staging", [ + sourceToken, + destToken, + sourceQuantity, + otherQuantity, + [], + ]); + const tx = zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + await expect(tx).to.be.revertedWith("Mismatched output token quantity"); + }); + }); + describe("sellToUniswap", () => { it("validates data", async () => { const data = zeroExMock.interface.encodeFunctionData("sellToUniswap", [ @@ -664,7 +763,620 @@ describe("ZeroExApiAdapter", () => { await expect(tx).to.be.revertedWith("Mismatched output token quantity"); }); }); - }); + + describe("multiplexBatchSellEthForToken", () => { + it("validates data", async () => { + const data = zeroExMock.interface.encodeFunctionData("multiplexBatchSellEthForToken", [ + destToken, + [], + minDestinationQuantity, + ]); + const [target, value, _data] = await zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + expect(target).to.eq(zeroExMock.address); + expect(value).to.deep.eq(ZERO); + expect(_data).to.deep.eq(data); + }); + + it("rejects wrong input token", async () => { + const data = zeroExMock.interface.encodeFunctionData("multiplexBatchSellEthForToken", [ + destToken, + [], + minDestinationQuantity, + ]); + const tx = zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + await expect(tx).to.be.revertedWith("Mismatched input token"); + }); + + it("rejects wrong output token", async () => { + const data = zeroExMock.interface.encodeFunctionData("multiplexBatchSellEthForToken", [ + destToken, + [], + minDestinationQuantity, + ]); + const tx = zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + await expect(tx).to.be.revertedWith("Mismatched output token"); + }); + + it("rejects wrong input token quantity", async () => { + const data = zeroExMock.interface.encodeFunctionData("multiplexBatchSellEthForToken", [ + destToken, + [], + minDestinationQuantity, + ]); + const tx = zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + await expect(tx).to.be.revertedWith("Mismatched input token quantity"); + }); + + it("rejects wrong output token quantity", async () => { + const data = zeroExMock.interface.encodeFunctionData("multiplexBatchSellEthForToken", [ + destToken, + [], + otherQuantity, + ]); + const tx = zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + await expect(tx).to.be.revertedWith("Mismatched output token quantity"); + }); + }); + + describe("multiplexBatchSellTokenForEth", () => { + it("validates data", async () => { + const data = zeroExMock.interface.encodeFunctionData("multiplexBatchSellTokenForEth", [ + sourceToken, + [], + sourceQuantity, + minDestinationQuantity, + ]); + const [target, value, _data] = await zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + expect(target).to.eq(zeroExMock.address); + expect(value).to.deep.eq(ZERO); + expect(_data).to.deep.eq(data); + }); + + it("rejects wrong input token", async () => { + const data = zeroExMock.interface.encodeFunctionData("multiplexBatchSellTokenForEth", [ + sourceToken, + [], + sourceQuantity, + minDestinationQuantity, + ]); + const tx = zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + await expect(tx).to.be.revertedWith("Mismatched input token"); + }); + + it("rejects wrong output token", async () => { + const data = zeroExMock.interface.encodeFunctionData("multiplexBatchSellTokenForEth", [ + sourceToken, + [], + sourceQuantity, + minDestinationQuantity, + ]); + const tx = zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + await expect(tx).to.be.revertedWith("Mismatched output token"); + }); + + it("rejects wrong input token quantity", async () => { + const data = zeroExMock.interface.encodeFunctionData("multiplexBatchSellTokenForEth", [ + sourceToken, + [], + sourceQuantity, + minDestinationQuantity, + ]); + const tx = zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + await expect(tx).to.be.revertedWith("Mismatched input token quantity"); + }); + + it("rejects wrong output token quantity", async () => { + const data = zeroExMock.interface.encodeFunctionData("multiplexBatchSellTokenForEth", [ + sourceToken, + [], + sourceQuantity, + otherQuantity, + ]); + const tx = zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + await expect(tx).to.be.revertedWith("Mismatched output token quantity"); + }); + }); + + describe("multiplexBatchSellTokenForToken", () => { + it("validates data", async () => { + const data = zeroExMock.interface.encodeFunctionData("multiplexBatchSellTokenForToken", [ + sourceToken, + destToken, + [], + sourceQuantity, + minDestinationQuantity, + ]); + const [target, value, _data] = await zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + expect(target).to.eq(zeroExMock.address); + expect(value).to.deep.eq(ZERO); + expect(_data).to.deep.eq(data); + }); + + it("rejects wrong input token", async () => { + const data = zeroExMock.interface.encodeFunctionData("multiplexBatchSellTokenForToken", [ + sourceToken, + destToken, + [], + sourceQuantity, + minDestinationQuantity, + ]); + const tx = zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + await expect(tx).to.be.revertedWith("Mismatched input token"); + }); + + it("rejects wrong output token", async () => { + const data = zeroExMock.interface.encodeFunctionData("multiplexBatchSellTokenForToken", [ + sourceToken, + destToken, + [], + sourceQuantity, + minDestinationQuantity, + ]); + const tx = zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + await expect(tx).to.be.revertedWith("Mismatched output token"); + }); + + it("rejects wrong input token quantity", async () => { + const data = zeroExMock.interface.encodeFunctionData("multiplexBatchSellTokenForToken", [ + sourceToken, + destToken, + [], + sourceQuantity, + minDestinationQuantity, + ]); + const tx = zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + await expect(tx).to.be.revertedWith("Mismatched input token quantity"); + }); + + it("rejects wrong output token quantity", async () => { + const data = zeroExMock.interface.encodeFunctionData("multiplexBatchSellTokenForToken", [ + sourceToken, + destToken, + [], + sourceQuantity, + otherQuantity, + ]); + const tx = zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + await expect(tx).to.be.revertedWith("Mismatched output token quantity"); + }); + }); + + describe("multiplexMultiHopSellEthForToken", () => { + it("validates data", async () => { + const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellEthForToken", [ + [sourceToken, destToken], + [], + minDestinationQuantity, + ]); + const [target, value, _data] = await zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + expect(target).to.eq(zeroExMock.address); + expect(value).to.deep.eq(ZERO); + expect(_data).to.deep.eq(data); + }); + + it("rejects wrong input token", async () => { + const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellEthForToken", [ + [sourceToken, destToken], + [], + minDestinationQuantity, + ]); + const tx = zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + await expect(tx).to.be.revertedWith("Mismatched input token"); + }); + + it("rejects went path too short", async () => { + const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellEthForToken", [ + [sourceToken, destToken], + [], + minDestinationQuantity, + ]); + const tx = zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + await expect(tx).to.be.revertedWith("Multihop token path too short"); + }); + + it("rejects wrong output token", async () => { + const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellEthForToken", [ + [sourceToken, destToken], + [], + minDestinationQuantity, + ]); + const tx = zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + await expect(tx).to.be.revertedWith("Mismatched output token"); + }); + + it("rejects wrong input token quantity", async () => { + const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellEthForToken", [ + [sourceToken, destToken], + [], + minDestinationQuantity, + ]); + const tx = zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + await expect(tx).to.be.revertedWith("Mismatched input token quantity"); + }); + + it("rejects wrong output token quantity", async () => { + const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellEthForToken", [ + [sourceToken, destToken], + [], + minDestinationQuantity, + ]); + const tx = zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + await expect(tx).to.be.revertedWith("Mismatched output token quantity"); + }); + }); + + describe("multiplexMultiHopSellTokenForEth", () => { + it("validates data", async () => { + const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellTokenForEth", [ + [sourceToken, destToken], + [], + sourceQuantity, + minDestinationQuantity, + ]); + const [target, value, _data] = await zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + expect(target).to.eq(zeroExMock.address); + expect(value).to.deep.eq(ZERO); + expect(_data).to.deep.eq(data); + }); + + it("rejects wrong input token", async () => { + const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellTokenForEth", [ + [sourceToken, destToken], + [], + sourceQuantity, + minDestinationQuantity, + ]); + const tx = zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + await expect(tx).to.be.revertedWith("Mismatched input token"); + }); + + it("rejects went path too short", async () => { + const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellTokenForEth", [ + [sourceToken, destToken], + [], + sourceQuantity, + minDestinationQuantity, + ]); + const tx = zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + await expect(tx).to.be.revertedWith("Multihop token path too short"); + }); + + it("rejects wrong output token", async () => { + const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellTokenForEth", [ + [sourceToken, destToken], + [], + sourceQuantity, + minDestinationQuantity, + ]); + const tx = zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + await expect(tx).to.be.revertedWith("Mismatched output token"); + }); + + it("rejects wrong input token quantity", async () => { + const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellTokenForEth", [ + [sourceToken, destToken], + [], + sourceQuantity, + minDestinationQuantity, + ]); + const tx = zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + await expect(tx).to.be.revertedWith("Mismatched input token quantity"); + }); + + it("rejects wrong output token quantity", async () => { + const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellTokenForEth", [ + [sourceToken, destToken], + [], + sourceQuantity, + otherQuantity, + ]); + const tx = zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + await expect(tx).to.be.revertedWith("Mismatched output token quantity"); + }); + }); + + describe("multiplexMultiHopSellTokenForToken", () => { + it("validates data", async () => { + const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellTokenForToken", [ + [sourceToken, destToken], + [], + sourceQuantity, + minDestinationQuantity, + ]); + const [target, value, _data] = await zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + expect(target).to.eq(zeroExMock.address); + expect(value).to.deep.eq(ZERO); + expect(_data).to.deep.eq(data); + }); + + it("rejects wrong input token", async () => { + const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellTokenForToken", [ + [sourceToken, destToken], + [], + sourceQuantity, + minDestinationQuantity, + ]); + const tx = zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + await expect(tx).to.be.revertedWith("Mismatched input token"); + }); + + it("rejects went path too short", async () => { + const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellTokenForToken", [ + [sourceToken, destToken], + [], + sourceQuantity, + minDestinationQuantity, + ]); + const tx = zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + await expect(tx).to.be.revertedWith("Multihop token path too short"); + }); + + it("rejects wrong output token", async () => { + const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellTokenForToken", [ + [sourceToken, destToken], + [], + sourceQuantity, + minDestinationQuantity, + ]); + const tx = zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + await expect(tx).to.be.revertedWith("Mismatched output token"); + }); + + it("rejects wrong input token quantity", async () => { + const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellTokenForToken", [ + [sourceToken, destToken], + [], + sourceQuantity, + minDestinationQuantity, + ]); + const tx = zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + await expect(tx).to.be.revertedWith("Mismatched input token quantity"); + }); + + it("rejects wrong output token quantity", async () => { + const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellTokenForToken", [ + [sourceToken, destToken], + [], + sourceQuantity, + otherQuantity, + ]); + const tx = zeroExApiAdapter.getTradeCalldata( + sourceToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + await expect(tx).to.be.revertedWith("Mismatched output token quantity"); + }); + }); + }); + describe("Uniswap V3", () => { const POOL_FEE = 1234; function encodePath(tokens_: string[]): string { From f093bd158d68b9c30d7892d97ba378cf13e7c3d6 Mon Sep 17 00:00:00 2001 From: cece-z Date: Fri, 24 Sep 2021 13:25:05 -0400 Subject: [PATCH 6/8] tests for multiplex --- .../integration/exchange/ZeroExApiAdapter.sol | 12 +- .../exchange/zeroExApiAdapter.spec.ts | 184 ++++-------------- 2 files changed, 46 insertions(+), 150 deletions(-) diff --git a/contracts/protocol/integration/exchange/ZeroExApiAdapter.sol b/contracts/protocol/integration/exchange/ZeroExApiAdapter.sol index 9a7a03061..c88558142 100644 --- a/contracts/protocol/integration/exchange/ZeroExApiAdapter.sol +++ b/contracts/protocol/integration/exchange/ZeroExApiAdapter.sol @@ -207,16 +207,16 @@ contract ZeroExApiAdapter { abi.decode(_data[4:], (address, uint256, uint256)); inputToken = ETH_ADDRESS; inputTokenAmount = _sourceQuantity; - } else if (selector == 0x77725df6) { + } else if (selector == 0x77725df6) { // multiplexBatchSellTokenForEth() (inputToken, , inputTokenAmount, minOutputTokenAmount) = abi.decode(_data[4:], (address, uint256, uint256, uint256)); outputToken = ETH_ADDRESS; - } else if (selector == 0x7a1eb1b9) { + } else if (selector == 0x7a1eb1b9) { // multiplexBatchSellTokenForToken() (inputToken, outputToken, , inputTokenAmount, minOutputTokenAmount) = abi.decode(_data[4:], (address, address, uint256, uint256, uint256)); - } else if (selector == 0x5161b966) { + } else if (selector == 0x5161b966) { // multiplexMultiHopSellEthForToken() address[] memory tokens; (tokens, , minOutputTokenAmount) = @@ -225,7 +225,7 @@ contract ZeroExApiAdapter { inputToken = ETH_ADDRESS; outputToken = tokens[tokens.length - 1]; inputTokenAmount = _sourceQuantity; - } else if (selector == 0x9a2967d2) { + } else if (selector == 0x9a2967d2) { // multiplexMultiHopSellTokenForEth() address[] memory tokens; (tokens, , inputTokenAmount, minOutputTokenAmount) = @@ -233,7 +233,7 @@ contract ZeroExApiAdapter { require(tokens.length > 1, "Multihop token path too short"); inputToken = tokens[0]; outputToken = ETH_ADDRESS; - } else if (selector == 0x0f3b31b2) { + } else if (selector == 0x0f3b31b2) { // multiplexMultiHopSellTokenForToken() address[] memory tokens; (tokens, , inputTokenAmount, minOutputTokenAmount) = @@ -241,7 +241,7 @@ contract ZeroExApiAdapter { require(tokens.length > 1, "Multihop token path too short"); inputToken = tokens[0]; outputToken = tokens[tokens.length - 1]; - } else { + } else { revert("Unsupported 0xAPI function selector"); } } diff --git a/test/protocol/integration/exchange/zeroExApiAdapter.spec.ts b/test/protocol/integration/exchange/zeroExApiAdapter.spec.ts index 27e526f81..912a855e2 100644 --- a/test/protocol/integration/exchange/zeroExApiAdapter.spec.ts +++ b/test/protocol/integration/exchange/zeroExApiAdapter.spec.ts @@ -12,6 +12,7 @@ const expect = getWaffleExpect(); describe("ZeroExApiAdapter", () => { let owner: Account; + const ethToken = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; const sourceToken = "0x6cf5f1d59fddae3a688210953a512b6aee6ea643"; const destToken = "0x5e5d0bea9d4a15db2d0837aff0435faba166190d"; const otherToken = "0xae9902bb655de1a67f334d8661b3ae6a96723d5b"; @@ -772,7 +773,7 @@ describe("ZeroExApiAdapter", () => { minDestinationQuantity, ]); const [target, value, _data] = await zeroExApiAdapter.getTradeCalldata( - sourceToken, + ethToken, destToken, destination, sourceQuantity, @@ -780,7 +781,7 @@ describe("ZeroExApiAdapter", () => { data, ); expect(target).to.eq(zeroExMock.address); - expect(value).to.deep.eq(ZERO); + expect(value).to.deep.eq(sourceQuantity); expect(_data).to.deep.eq(data); }); @@ -803,12 +804,12 @@ describe("ZeroExApiAdapter", () => { it("rejects wrong output token", async () => { const data = zeroExMock.interface.encodeFunctionData("multiplexBatchSellEthForToken", [ - destToken, + otherToken, [], minDestinationQuantity, ]); const tx = zeroExApiAdapter.getTradeCalldata( - sourceToken, + ethToken, destToken, destination, sourceQuantity, @@ -818,23 +819,6 @@ describe("ZeroExApiAdapter", () => { await expect(tx).to.be.revertedWith("Mismatched output token"); }); - it("rejects wrong input token quantity", async () => { - const data = zeroExMock.interface.encodeFunctionData("multiplexBatchSellEthForToken", [ - destToken, - [], - minDestinationQuantity, - ]); - const tx = zeroExApiAdapter.getTradeCalldata( - sourceToken, - destToken, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - await expect(tx).to.be.revertedWith("Mismatched input token quantity"); - }); - it("rejects wrong output token quantity", async () => { const data = zeroExMock.interface.encodeFunctionData("multiplexBatchSellEthForToken", [ destToken, @@ -842,7 +826,7 @@ describe("ZeroExApiAdapter", () => { otherQuantity, ]); const tx = zeroExApiAdapter.getTradeCalldata( - sourceToken, + ethToken, destToken, destination, sourceQuantity, @@ -863,7 +847,7 @@ describe("ZeroExApiAdapter", () => { ]); const [target, value, _data] = await zeroExApiAdapter.getTradeCalldata( sourceToken, - destToken, + ethToken, destination, sourceQuantity, minDestinationQuantity, @@ -876,14 +860,14 @@ describe("ZeroExApiAdapter", () => { it("rejects wrong input token", async () => { const data = zeroExMock.interface.encodeFunctionData("multiplexBatchSellTokenForEth", [ - sourceToken, + otherToken, [], sourceQuantity, minDestinationQuantity, ]); const tx = zeroExApiAdapter.getTradeCalldata( sourceToken, - destToken, + ethToken, destination, sourceQuantity, minDestinationQuantity, @@ -892,34 +876,16 @@ describe("ZeroExApiAdapter", () => { await expect(tx).to.be.revertedWith("Mismatched input token"); }); - it("rejects wrong output token", async () => { - const data = zeroExMock.interface.encodeFunctionData("multiplexBatchSellTokenForEth", [ - sourceToken, - [], - sourceQuantity, - minDestinationQuantity, - ]); - const tx = zeroExApiAdapter.getTradeCalldata( - sourceToken, - destToken, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - await expect(tx).to.be.revertedWith("Mismatched output token"); - }); - it("rejects wrong input token quantity", async () => { const data = zeroExMock.interface.encodeFunctionData("multiplexBatchSellTokenForEth", [ sourceToken, [], - sourceQuantity, + otherQuantity, minDestinationQuantity, ]); const tx = zeroExApiAdapter.getTradeCalldata( sourceToken, - destToken, + ethToken, destination, sourceQuantity, minDestinationQuantity, @@ -927,24 +893,6 @@ describe("ZeroExApiAdapter", () => { ); await expect(tx).to.be.revertedWith("Mismatched input token quantity"); }); - - it("rejects wrong output token quantity", async () => { - const data = zeroExMock.interface.encodeFunctionData("multiplexBatchSellTokenForEth", [ - sourceToken, - [], - sourceQuantity, - otherQuantity, - ]); - const tx = zeroExApiAdapter.getTradeCalldata( - sourceToken, - destToken, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - await expect(tx).to.be.revertedWith("Mismatched output token quantity"); - }); }); describe("multiplexBatchSellTokenForToken", () => { @@ -971,7 +919,7 @@ describe("ZeroExApiAdapter", () => { it("rejects wrong input token", async () => { const data = zeroExMock.interface.encodeFunctionData("multiplexBatchSellTokenForToken", [ - sourceToken, + otherToken, destToken, [], sourceQuantity, @@ -991,7 +939,7 @@ describe("ZeroExApiAdapter", () => { it("rejects wrong output token", async () => { const data = zeroExMock.interface.encodeFunctionData("multiplexBatchSellTokenForToken", [ sourceToken, - destToken, + otherToken, [], sourceQuantity, minDestinationQuantity, @@ -1012,7 +960,7 @@ describe("ZeroExApiAdapter", () => { sourceToken, destToken, [], - sourceQuantity, + otherQuantity, minDestinationQuantity, ]); const tx = zeroExApiAdapter.getTradeCalldata( @@ -1049,12 +997,12 @@ describe("ZeroExApiAdapter", () => { describe("multiplexMultiHopSellEthForToken", () => { it("validates data", async () => { const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellEthForToken", [ - [sourceToken, destToken], + [ethToken, destToken], [], minDestinationQuantity, ]); const [target, value, _data] = await zeroExApiAdapter.getTradeCalldata( - sourceToken, + ethToken, destToken, destination, sourceQuantity, @@ -1062,35 +1010,18 @@ describe("ZeroExApiAdapter", () => { data, ); expect(target).to.eq(zeroExMock.address); - expect(value).to.deep.eq(ZERO); + expect(value).to.deep.eq(sourceQuantity); expect(_data).to.deep.eq(data); }); - it("rejects wrong input token", async () => { + it("rejects when path too short", async () => { const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellEthForToken", [ - [sourceToken, destToken], + [ethToken], [], minDestinationQuantity, ]); const tx = zeroExApiAdapter.getTradeCalldata( - sourceToken, - destToken, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - await expect(tx).to.be.revertedWith("Mismatched input token"); - }); - - it("rejects went path too short", async () => { - const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellEthForToken", [ - [sourceToken, destToken], - [], - minDestinationQuantity, - ]); - const tx = zeroExApiAdapter.getTradeCalldata( - sourceToken, + ethToken, destToken, destination, sourceQuantity, @@ -1102,13 +1033,13 @@ describe("ZeroExApiAdapter", () => { it("rejects wrong output token", async () => { const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellEthForToken", [ - [sourceToken, destToken], + [ethToken, destToken], [], minDestinationQuantity, ]); const tx = zeroExApiAdapter.getTradeCalldata( - sourceToken, - destToken, + ethToken, + otherToken, destination, sourceQuantity, minDestinationQuantity, @@ -1117,31 +1048,14 @@ describe("ZeroExApiAdapter", () => { await expect(tx).to.be.revertedWith("Mismatched output token"); }); - it("rejects wrong input token quantity", async () => { - const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellEthForToken", [ - [sourceToken, destToken], - [], - minDestinationQuantity, - ]); - const tx = zeroExApiAdapter.getTradeCalldata( - sourceToken, - destToken, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - await expect(tx).to.be.revertedWith("Mismatched input token quantity"); - }); - it("rejects wrong output token quantity", async () => { const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellEthForToken", [ - [sourceToken, destToken], + [ethToken, destToken], [], - minDestinationQuantity, + otherQuantity, ]); const tx = zeroExApiAdapter.getTradeCalldata( - sourceToken, + ethToken, destToken, destination, sourceQuantity, @@ -1155,14 +1069,14 @@ describe("ZeroExApiAdapter", () => { describe("multiplexMultiHopSellTokenForEth", () => { it("validates data", async () => { const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellTokenForEth", [ - [sourceToken, destToken], + [sourceToken, ethToken], [], sourceQuantity, minDestinationQuantity, ]); const [target, value, _data] = await zeroExApiAdapter.getTradeCalldata( sourceToken, - destToken, + ethToken, destination, sourceQuantity, minDestinationQuantity, @@ -1175,14 +1089,14 @@ describe("ZeroExApiAdapter", () => { it("rejects wrong input token", async () => { const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellTokenForEth", [ - [sourceToken, destToken], + [otherToken, ethToken], [], sourceQuantity, minDestinationQuantity, ]); const tx = zeroExApiAdapter.getTradeCalldata( sourceToken, - destToken, + ethToken, destination, sourceQuantity, minDestinationQuantity, @@ -1193,14 +1107,14 @@ describe("ZeroExApiAdapter", () => { it("rejects went path too short", async () => { const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellTokenForEth", [ - [sourceToken, destToken], + [ethToken], [], sourceQuantity, minDestinationQuantity, ]); const tx = zeroExApiAdapter.getTradeCalldata( sourceToken, - destToken, + ethToken, destination, sourceQuantity, minDestinationQuantity, @@ -1209,34 +1123,16 @@ describe("ZeroExApiAdapter", () => { await expect(tx).to.be.revertedWith("Multihop token path too short"); }); - it("rejects wrong output token", async () => { - const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellTokenForEth", [ - [sourceToken, destToken], - [], - sourceQuantity, - minDestinationQuantity, - ]); - const tx = zeroExApiAdapter.getTradeCalldata( - sourceToken, - destToken, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - await expect(tx).to.be.revertedWith("Mismatched output token"); - }); - it("rejects wrong input token quantity", async () => { const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellTokenForEth", [ [sourceToken, destToken], [], - sourceQuantity, + otherQuantity, minDestinationQuantity, ]); const tx = zeroExApiAdapter.getTradeCalldata( sourceToken, - destToken, + ethToken, destination, sourceQuantity, minDestinationQuantity, @@ -1247,14 +1143,14 @@ describe("ZeroExApiAdapter", () => { it("rejects wrong output token quantity", async () => { const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellTokenForEth", [ - [sourceToken, destToken], + [sourceToken, ethToken], [], sourceQuantity, otherQuantity, ]); const tx = zeroExApiAdapter.getTradeCalldata( sourceToken, - destToken, + ethToken, destination, sourceQuantity, minDestinationQuantity, @@ -1287,7 +1183,7 @@ describe("ZeroExApiAdapter", () => { it("rejects wrong input token", async () => { const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellTokenForToken", [ - [sourceToken, destToken], + [otherToken, destToken], [], sourceQuantity, minDestinationQuantity, @@ -1305,7 +1201,7 @@ describe("ZeroExApiAdapter", () => { it("rejects went path too short", async () => { const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellTokenForToken", [ - [sourceToken, destToken], + [sourceToken], [], sourceQuantity, minDestinationQuantity, @@ -1323,7 +1219,7 @@ describe("ZeroExApiAdapter", () => { it("rejects wrong output token", async () => { const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellTokenForToken", [ - [sourceToken, destToken], + [sourceToken, otherToken], [], sourceQuantity, minDestinationQuantity, @@ -1343,7 +1239,7 @@ describe("ZeroExApiAdapter", () => { const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellTokenForToken", [ [sourceToken, destToken], [], - sourceQuantity, + otherQuantity, minDestinationQuantity, ]); const tx = zeroExApiAdapter.getTradeCalldata( From 4a3b28cd482dee23a81aafc4d31ba3886bf420af Mon Sep 17 00:00:00 2001 From: cece-z Date: Mon, 27 Sep 2021 14:09:56 -0400 Subject: [PATCH 7/8] remove eth related func --- contracts/mocks/external/ZeroExMock.sol | 74 --- .../integration/exchange/ZeroExApiAdapter.sol | 53 -- package.json | 8 +- .../exchange/zeroExApiAdapter.spec.ts | 619 +----------------- 4 files changed, 5 insertions(+), 749 deletions(-) diff --git a/contracts/mocks/external/ZeroExMock.sol b/contracts/mocks/external/ZeroExMock.sol index 1ce198064..f54e9f0a2 100644 --- a/contracts/mocks/external/ZeroExMock.sol +++ b/contracts/mocks/external/ZeroExMock.sol @@ -167,30 +167,6 @@ contract ZeroExMock { _transferTokens(); } - function sellEthForTokenToUniswapV3( - bytes memory /* encodedPath */, - uint256 /* minBuyAmount */, - address /* recipient */ - ) - external - payable - returns (uint256) - { - _transferTokens(); - } - - function sellTokenForEthToUniswapV3( - bytes memory /* encodedPath */, - uint256 /* sellAmount */, - uint256 /* minBuyAmount */, - address payable /* recipient */ - ) - external - returns (uint256) - { - _transferTokens(); - } - function sellTokenForTokenToUniswapV3( bytes memory /* encodedPath */, uint256 /* sellAmount */, @@ -203,31 +179,6 @@ contract ZeroExMock { _transferTokens(); } - function multiplexBatchSellEthForToken( - address /* outputToken */, - BatchSellSubcall[] memory /* calls */, - uint256 /* minBuyAmount */ - ) - external - payable - returns (uint256) - { - _transferTokens(); - } - - function multiplexBatchSellTokenForEth( - address /* inputToken */, - BatchSellSubcall[] memory /* calls */, - uint256 /* sellAmount */, - uint256 /* minBuyAmount */ - ) - external - payable - returns (uint256) - { - _transferTokens(); - } - function multiplexBatchSellTokenForToken( address /* inputToken */, address /* outputToken */, @@ -242,31 +193,6 @@ contract ZeroExMock { _transferTokens(); } - function multiplexMultiHopSellEthForToken( - address[] memory /* tokens */, - MultiHopSellSubcall[] memory /* calls */, - uint256 /* minBuyAmount */ - ) - external - payable - returns (uint256) - { - _transferTokens(); - } - - function multiplexMultiHopSellTokenForEth( - address[] memory /* tokens */, - MultiHopSellSubcall[] memory /* calls */, - uint256 /* sellAmount */, - uint256 /* minBuyAmount */ - ) - external - payable - returns (uint256) - { - _transferTokens(); - } - function multiplexMultiHopSellTokenForToken( address[] memory /* tokens */, MultiHopSellSubcall[] memory /* calls */, diff --git a/contracts/protocol/integration/exchange/ZeroExApiAdapter.sol b/contracts/protocol/integration/exchange/ZeroExApiAdapter.sol index c88558142..423eb6707 100644 --- a/contracts/protocol/integration/exchange/ZeroExApiAdapter.sol +++ b/contracts/protocol/integration/exchange/ZeroExApiAdapter.sol @@ -176,63 +176,10 @@ contract ZeroExApiAdapter { recipient = _destinationAddress; } (inputToken, outputToken) = _decodeTokensFromUniswapV3EncodedPath(encodedPath); - } else if (selector == 0x803ba26d) { - // sellTokenForEthToUniswapV3() - bytes memory encodedPath; - (encodedPath, inputTokenAmount, minOutputTokenAmount, recipient) = - abi.decode(_data[4:], (bytes, uint256, uint256, address)); - supportsRecipient = true; - if (recipient == address(0)) { - recipient = _destinationAddress; - } - (inputToken, outputToken) = _decodeTokensFromUniswapV3EncodedPath(encodedPath); - require(outputToken == wethAddress, "Last token must be WETH"); - outputToken = ETH_ADDRESS; - } else if (selector == 0x3598d8ab) { - // sellEthForTokenToUniswapV3() - inputTokenAmount = _sourceQuantity; - bytes memory encodedPath; - (encodedPath, minOutputTokenAmount, recipient) = - abi.decode(_data[4:], (bytes, uint256, address)); - supportsRecipient = true; - if (recipient == address(0)) { - recipient = _destinationAddress; - } - (inputToken, outputToken) = _decodeTokensFromUniswapV3EncodedPath(encodedPath); - require(inputToken == wethAddress, "First token must be WETH"); - inputToken = ETH_ADDRESS; - } else if (selector == 0xf35b4733) { - // multiplexBatchSellEthForToken() - (outputToken, , minOutputTokenAmount) = - abi.decode(_data[4:], (address, uint256, uint256)); - inputToken = ETH_ADDRESS; - inputTokenAmount = _sourceQuantity; - } else if (selector == 0x77725df6) { - // multiplexBatchSellTokenForEth() - (inputToken, , inputTokenAmount, minOutputTokenAmount) = - abi.decode(_data[4:], (address, uint256, uint256, uint256)); - outputToken = ETH_ADDRESS; } else if (selector == 0x7a1eb1b9) { // multiplexBatchSellTokenForToken() (inputToken, outputToken, , inputTokenAmount, minOutputTokenAmount) = abi.decode(_data[4:], (address, address, uint256, uint256, uint256)); - } else if (selector == 0x5161b966) { - // multiplexMultiHopSellEthForToken() - address[] memory tokens; - (tokens, , minOutputTokenAmount) = - abi.decode(_data[4:], (address[], uint256, uint256)); - require(tokens.length > 1, "Multihop token path too short"); - inputToken = ETH_ADDRESS; - outputToken = tokens[tokens.length - 1]; - inputTokenAmount = _sourceQuantity; - } else if (selector == 0x9a2967d2) { - // multiplexMultiHopSellTokenForEth() - address[] memory tokens; - (tokens, , inputTokenAmount, minOutputTokenAmount) = - abi.decode(_data[4:], (address[], uint256, uint256, uint256)); - require(tokens.length > 1, "Multihop token path too short"); - inputToken = tokens[0]; - outputToken = ETH_ADDRESS; } else if (selector == 0x0f3b31b2) { // multiplexMultiHopSellTokenForToken() address[] memory tokens; diff --git a/package.json b/package.json index 80feee10d..ea8efd597 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@setprotocol/set-protocol-v2", - "version": "0.1.0", + "version": "0.1.1", "description": "", "main": "dist", "types": "dist/types", @@ -66,7 +66,7 @@ "coveralls": "^3.0.1", "dotenv": "^8.2.0", "ethereum-waffle": "^3.4.0", - "hardhat": "^2.6.1", + "hardhat": "^2.6.4", "husky": "^4.2.5", "istanbul-combine-updated": "^0.3.0", "lint-staged": "^10.2.11", @@ -75,11 +75,11 @@ "solhint": "^3.1.0", "solidity-coverage": "^0.7.17", "ts-generator": "^0.1.1", - "ts-node": "^8.10.1", + "ts-node": "^8.10.2", "tslint": "^5.8.0", "tslint-eslint-rules": "^5.3.1", "typechain": "5.1.2", - "typescript": "4", + "typescript": "^4.4.3", "web3": "^1.2.9" }, "dependencies": { diff --git a/test/protocol/integration/exchange/zeroExApiAdapter.spec.ts b/test/protocol/integration/exchange/zeroExApiAdapter.spec.ts index 912a855e2..3e252ac11 100644 --- a/test/protocol/integration/exchange/zeroExApiAdapter.spec.ts +++ b/test/protocol/integration/exchange/zeroExApiAdapter.spec.ts @@ -1,7 +1,7 @@ import "module-alias/register"; import { Account } from "@utils/test/types"; -import { ADDRESS_ZERO, ONE, ZERO, EMPTY_BYTES, ETH_ADDRESS } from "@utils/constants"; +import { ADDRESS_ZERO, ONE, ZERO, EMPTY_BYTES } from "@utils/constants"; import { ZeroExApiAdapter, ZeroExMock } from "@utils/contracts"; import DeployHelper from "@utils/deploys"; import { addSnapshotBeforeRestoreAfterEach, getAccounts, getWaffleExpect } from "@utils/test/index"; @@ -12,7 +12,6 @@ const expect = getWaffleExpect(); describe("ZeroExApiAdapter", () => { let owner: Account; - const ethToken = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; const sourceToken = "0x6cf5f1d59fddae3a688210953a512b6aee6ea643"; const destToken = "0x5e5d0bea9d4a15db2d0837aff0435faba166190d"; const otherToken = "0xae9902bb655de1a67f334d8661b3ae6a96723d5b"; @@ -765,136 +764,6 @@ describe("ZeroExApiAdapter", () => { }); }); - describe("multiplexBatchSellEthForToken", () => { - it("validates data", async () => { - const data = zeroExMock.interface.encodeFunctionData("multiplexBatchSellEthForToken", [ - destToken, - [], - minDestinationQuantity, - ]); - const [target, value, _data] = await zeroExApiAdapter.getTradeCalldata( - ethToken, - destToken, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - expect(target).to.eq(zeroExMock.address); - expect(value).to.deep.eq(sourceQuantity); - expect(_data).to.deep.eq(data); - }); - - it("rejects wrong input token", async () => { - const data = zeroExMock.interface.encodeFunctionData("multiplexBatchSellEthForToken", [ - destToken, - [], - minDestinationQuantity, - ]); - const tx = zeroExApiAdapter.getTradeCalldata( - sourceToken, - destToken, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - await expect(tx).to.be.revertedWith("Mismatched input token"); - }); - - it("rejects wrong output token", async () => { - const data = zeroExMock.interface.encodeFunctionData("multiplexBatchSellEthForToken", [ - otherToken, - [], - minDestinationQuantity, - ]); - const tx = zeroExApiAdapter.getTradeCalldata( - ethToken, - destToken, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - await expect(tx).to.be.revertedWith("Mismatched output token"); - }); - - it("rejects wrong output token quantity", async () => { - const data = zeroExMock.interface.encodeFunctionData("multiplexBatchSellEthForToken", [ - destToken, - [], - otherQuantity, - ]); - const tx = zeroExApiAdapter.getTradeCalldata( - ethToken, - destToken, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - await expect(tx).to.be.revertedWith("Mismatched output token quantity"); - }); - }); - - describe("multiplexBatchSellTokenForEth", () => { - it("validates data", async () => { - const data = zeroExMock.interface.encodeFunctionData("multiplexBatchSellTokenForEth", [ - sourceToken, - [], - sourceQuantity, - minDestinationQuantity, - ]); - const [target, value, _data] = await zeroExApiAdapter.getTradeCalldata( - sourceToken, - ethToken, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - expect(target).to.eq(zeroExMock.address); - expect(value).to.deep.eq(ZERO); - expect(_data).to.deep.eq(data); - }); - - it("rejects wrong input token", async () => { - const data = zeroExMock.interface.encodeFunctionData("multiplexBatchSellTokenForEth", [ - otherToken, - [], - sourceQuantity, - minDestinationQuantity, - ]); - const tx = zeroExApiAdapter.getTradeCalldata( - sourceToken, - ethToken, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - await expect(tx).to.be.revertedWith("Mismatched input token"); - }); - - it("rejects wrong input token quantity", async () => { - const data = zeroExMock.interface.encodeFunctionData("multiplexBatchSellTokenForEth", [ - sourceToken, - [], - otherQuantity, - minDestinationQuantity, - ]); - const tx = zeroExApiAdapter.getTradeCalldata( - sourceToken, - ethToken, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - await expect(tx).to.be.revertedWith("Mismatched input token quantity"); - }); - }); - describe("multiplexBatchSellTokenForToken", () => { it("validates data", async () => { const data = zeroExMock.interface.encodeFunctionData("multiplexBatchSellTokenForToken", [ @@ -994,172 +863,6 @@ describe("ZeroExApiAdapter", () => { }); }); - describe("multiplexMultiHopSellEthForToken", () => { - it("validates data", async () => { - const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellEthForToken", [ - [ethToken, destToken], - [], - minDestinationQuantity, - ]); - const [target, value, _data] = await zeroExApiAdapter.getTradeCalldata( - ethToken, - destToken, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - expect(target).to.eq(zeroExMock.address); - expect(value).to.deep.eq(sourceQuantity); - expect(_data).to.deep.eq(data); - }); - - it("rejects when path too short", async () => { - const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellEthForToken", [ - [ethToken], - [], - minDestinationQuantity, - ]); - const tx = zeroExApiAdapter.getTradeCalldata( - ethToken, - destToken, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - await expect(tx).to.be.revertedWith("Multihop token path too short"); - }); - - it("rejects wrong output token", async () => { - const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellEthForToken", [ - [ethToken, destToken], - [], - minDestinationQuantity, - ]); - const tx = zeroExApiAdapter.getTradeCalldata( - ethToken, - otherToken, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - await expect(tx).to.be.revertedWith("Mismatched output token"); - }); - - it("rejects wrong output token quantity", async () => { - const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellEthForToken", [ - [ethToken, destToken], - [], - otherQuantity, - ]); - const tx = zeroExApiAdapter.getTradeCalldata( - ethToken, - destToken, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - await expect(tx).to.be.revertedWith("Mismatched output token quantity"); - }); - }); - - describe("multiplexMultiHopSellTokenForEth", () => { - it("validates data", async () => { - const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellTokenForEth", [ - [sourceToken, ethToken], - [], - sourceQuantity, - minDestinationQuantity, - ]); - const [target, value, _data] = await zeroExApiAdapter.getTradeCalldata( - sourceToken, - ethToken, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - expect(target).to.eq(zeroExMock.address); - expect(value).to.deep.eq(ZERO); - expect(_data).to.deep.eq(data); - }); - - it("rejects wrong input token", async () => { - const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellTokenForEth", [ - [otherToken, ethToken], - [], - sourceQuantity, - minDestinationQuantity, - ]); - const tx = zeroExApiAdapter.getTradeCalldata( - sourceToken, - ethToken, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - await expect(tx).to.be.revertedWith("Mismatched input token"); - }); - - it("rejects went path too short", async () => { - const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellTokenForEth", [ - [ethToken], - [], - sourceQuantity, - minDestinationQuantity, - ]); - const tx = zeroExApiAdapter.getTradeCalldata( - sourceToken, - ethToken, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - await expect(tx).to.be.revertedWith("Multihop token path too short"); - }); - - it("rejects wrong input token quantity", async () => { - const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellTokenForEth", [ - [sourceToken, destToken], - [], - otherQuantity, - minDestinationQuantity, - ]); - const tx = zeroExApiAdapter.getTradeCalldata( - sourceToken, - ethToken, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - await expect(tx).to.be.revertedWith("Mismatched input token quantity"); - }); - - it("rejects wrong output token quantity", async () => { - const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellTokenForEth", [ - [sourceToken, ethToken], - [], - sourceQuantity, - otherQuantity, - ]); - const tx = zeroExApiAdapter.getTradeCalldata( - sourceToken, - ethToken, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - await expect(tx).to.be.revertedWith("Mismatched output token quantity"); - }); - }); - describe("multiplexMultiHopSellTokenForToken", () => { it("validates data", async () => { const data = zeroExMock.interface.encodeFunctionData("multiplexMultiHopSellTokenForToken", [ @@ -1441,325 +1144,5 @@ describe("ZeroExApiAdapter", () => { await expect(tx).to.be.revertedWith("Mismatched recipient"); }); }); - - describe("sellTokenForEthToUniswapV3", () => { - const additionalHops = [otherToken, extraHopToken]; - for (let i = 0; i <= additionalHops.length; i++) { - const hops = take(additionalHops, i); - it(`validates data for ${i + 1} hops`, async () => { - const path = [sourceToken, ...hops, wethToken]; - - const data = zeroExMock.interface.encodeFunctionData("sellTokenForEthToUniswapV3", [ - encodePath(path), - sourceQuantity, - minDestinationQuantity, - destination, - ]); - const [target, value, _data] = await zeroExApiAdapter.getTradeCalldata( - sourceToken, - ETH_ADDRESS, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - expect(target).to.eq(zeroExMock.address); - expect(value).to.deep.eq(ZERO); - expect(_data).to.deep.eq(data); - }); - } - - it("permits any destination address when recipient is null", async () => { - const path = [sourceToken, wethToken]; - - const data = zeroExMock.interface.encodeFunctionData("sellTokenForEthToUniswapV3", [ - encodePath(path), - sourceQuantity, - minDestinationQuantity, - ADDRESS_ZERO, - ]); - await zeroExApiAdapter.getTradeCalldata( - sourceToken, - ETH_ADDRESS, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - }); - - it("rejects wrong input token", async () => { - const data = zeroExMock.interface.encodeFunctionData("sellTokenForEthToUniswapV3", [ - encodePath([otherToken, wethToken]), - sourceQuantity, - minDestinationQuantity, - destination, - ]); - const tx = zeroExApiAdapter.getTradeCalldata( - sourceToken, - ETH_ADDRESS, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - await expect(tx).to.be.revertedWith("Mismatched input token"); - }); - - it("rejects wrong output token", async () => { - const data = zeroExMock.interface.encodeFunctionData("sellTokenForEthToUniswapV3", [ - encodePath([sourceToken, wethToken]), - sourceQuantity, - minDestinationQuantity, - destination, - ]); - const tx = zeroExApiAdapter.getTradeCalldata( - sourceToken, - otherToken, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - await expect(tx).to.be.revertedWith("Mismatched output token"); - }); - - it("rejects non-WETH output token in encoded path", async () => { - const data = zeroExMock.interface.encodeFunctionData("sellTokenForEthToUniswapV3", [ - encodePath([sourceToken, otherToken]), - sourceQuantity, - minDestinationQuantity, - destination, - ]); - const tx = zeroExApiAdapter.getTradeCalldata( - sourceToken, - ETH_ADDRESS, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - await expect(tx).to.be.revertedWith("Last token must be WETH"); - }); - - it("rejects wrong input token quantity", async () => { - const data = zeroExMock.interface.encodeFunctionData("sellTokenForEthToUniswapV3", [ - encodePath([sourceToken, wethToken]), - otherQuantity, - minDestinationQuantity, - destination, - ]); - const tx = zeroExApiAdapter.getTradeCalldata( - sourceToken, - ETH_ADDRESS, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - await expect(tx).to.be.revertedWith("Mismatched input token quantity"); - }); - - it("rejects wrong output token quantity", async () => { - const data = zeroExMock.interface.encodeFunctionData("sellTokenForEthToUniswapV3", [ - encodePath([sourceToken, wethToken]), - sourceQuantity, - otherQuantity, - destination, - ]); - const tx = zeroExApiAdapter.getTradeCalldata( - sourceToken, - ETH_ADDRESS, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - await expect(tx).to.be.revertedWith("Mismatched output token quantity"); - }); - - it("rejects invalid uniswap path", async () => { - const data = zeroExMock.interface.encodeFunctionData("sellTokenForEthToUniswapV3", [ - encodePath([sourceToken]), - sourceQuantity, - minDestinationQuantity, - destination, - ]); - const tx = zeroExApiAdapter.getTradeCalldata( - sourceToken, - ETH_ADDRESS, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - await expect(tx).to.be.revertedWith("UniswapV3 token path too short"); - }); - - it("rejects wrong destination", async () => { - const data = zeroExMock.interface.encodeFunctionData("sellTokenForEthToUniswapV3", [ - encodePath([sourceToken, wethToken]), - sourceQuantity, - minDestinationQuantity, - destination, - ]); - const tx = zeroExApiAdapter.getTradeCalldata( - sourceToken, - ETH_ADDRESS, - otherDestination, - sourceQuantity, - minDestinationQuantity, - data, - ); - await expect(tx).to.be.revertedWith("Mismatched recipient"); - }); - }); - - describe("sellEthForTokenToUniswapV3", () => { - const additionalHops = [otherToken, extraHopToken]; - for (let i = 0; i <= additionalHops.length; i++) { - const hops = take(additionalHops, i); - it(`validates data for ${i + 1} hops`, async () => { - const path = [wethToken, ...hops, destToken]; - - const data = zeroExMock.interface.encodeFunctionData("sellEthForTokenToUniswapV3", [ - encodePath(path), - minDestinationQuantity, - destination, - ]); - const [target, value, _data] = await zeroExApiAdapter.getTradeCalldata( - ETH_ADDRESS, - destToken, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - expect(target).to.eq(zeroExMock.address); - expect(value).to.deep.eq(sourceQuantity); - expect(_data).to.deep.eq(data); - }); - } - - it("permits any destination address when recipient is null", async () => { - const path = [wethToken, destToken]; - - const data = zeroExMock.interface.encodeFunctionData("sellEthForTokenToUniswapV3", [ - encodePath(path), - minDestinationQuantity, - ADDRESS_ZERO, - ]); - await zeroExApiAdapter.getTradeCalldata( - ETH_ADDRESS, - destToken, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - }); - - it("rejects non-WETH input token in encoded path", async () => { - const data = zeroExMock.interface.encodeFunctionData("sellEthForTokenToUniswapV3", [ - encodePath([otherToken, destToken]), - minDestinationQuantity, - destination, - ]); - const tx = zeroExApiAdapter.getTradeCalldata( - ETH_ADDRESS, - destToken, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - await expect(tx).to.be.revertedWith("First token must be WETH"); - }); - - it("rejects wrong input token", async () => { - const data = zeroExMock.interface.encodeFunctionData("sellEthForTokenToUniswapV3", [ - encodePath([wethToken, destToken]), - minDestinationQuantity, - destination, - ]); - const tx = zeroExApiAdapter.getTradeCalldata( - otherToken, - destToken, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - await expect(tx).to.be.revertedWith("Mismatched input token"); - }); - - it("rejects wrong output token", async () => { - const data = zeroExMock.interface.encodeFunctionData("sellEthForTokenToUniswapV3", [ - encodePath([wethToken, otherToken]), - minDestinationQuantity, - destination, - ]); - const tx = zeroExApiAdapter.getTradeCalldata( - ETH_ADDRESS, - destToken, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - await expect(tx).to.be.revertedWith("Mismatched output token"); - }); - - it("rejects wrong output token quantity", async () => { - const data = zeroExMock.interface.encodeFunctionData("sellEthForTokenToUniswapV3", [ - encodePath([wethToken, destToken]), - otherQuantity, - destination, - ]); - const tx = zeroExApiAdapter.getTradeCalldata( - ETH_ADDRESS, - destToken, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - await expect(tx).to.be.revertedWith("Mismatched output token quantity"); - }); - - it("rejects invalid uniswap path", async () => { - const data = zeroExMock.interface.encodeFunctionData("sellEthForTokenToUniswapV3", [ - encodePath([wethToken]), - minDestinationQuantity, - destination, - ]); - const tx = zeroExApiAdapter.getTradeCalldata( - ETH_ADDRESS, - destToken, - destination, - sourceQuantity, - minDestinationQuantity, - data, - ); - await expect(tx).to.be.revertedWith("UniswapV3 token path too short"); - }); - - it("rejects wrong destination", async () => { - const data = zeroExMock.interface.encodeFunctionData("sellEthForTokenToUniswapV3", [ - encodePath([wethToken, destToken]), - minDestinationQuantity, - destination, - ]); - const tx = zeroExApiAdapter.getTradeCalldata( - ETH_ADDRESS, - destToken, - otherDestination, - sourceQuantity, - minDestinationQuantity, - data, - ); - await expect(tx).to.be.revertedWith("Mismatched recipient"); - }); - }); }); }); From 5a3d22d7d50378f16999fff32e83a90d6224475f Mon Sep 17 00:00:00 2001 From: cece-z Date: Mon, 27 Sep 2021 14:38:04 -0400 Subject: [PATCH 8/8] more tweaks --- .../integration/exchange/ZeroExApiAdapter.sol | 3 +- .../exchange/zeroExApiAdapter.spec.ts | 39 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/contracts/protocol/integration/exchange/ZeroExApiAdapter.sol b/contracts/protocol/integration/exchange/ZeroExApiAdapter.sol index 423eb6707..f0b420cdf 100644 --- a/contracts/protocol/integration/exchange/ZeroExApiAdapter.sol +++ b/contracts/protocol/integration/exchange/ZeroExApiAdapter.sol @@ -193,6 +193,7 @@ contract ZeroExApiAdapter { } } + require(inputToken != ETH_ADDRESS && outputToken != ETH_ADDRESS, "ETH not supported"); require(inputToken == _sourceToken, "Mismatched input token"); require(outputToken == _destinationToken, "Mismatched output token"); require(!supportsRecipient || recipient == _destinationAddress, "Mismatched recipient"); @@ -202,7 +203,7 @@ contract ZeroExApiAdapter { return ( zeroExAddress, // Note: Does not account for limit order protocol fees. - inputToken == ETH_ADDRESS ? inputTokenAmount : 0, + 0, _data ); } diff --git a/test/protocol/integration/exchange/zeroExApiAdapter.spec.ts b/test/protocol/integration/exchange/zeroExApiAdapter.spec.ts index 3e252ac11..26fe46099 100644 --- a/test/protocol/integration/exchange/zeroExApiAdapter.spec.ts +++ b/test/protocol/integration/exchange/zeroExApiAdapter.spec.ts @@ -12,6 +12,7 @@ const expect = getWaffleExpect(); describe("ZeroExApiAdapter", () => { let owner: Account; + const ethToken = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; const sourceToken = "0x6cf5f1d59fddae3a688210953a512b6aee6ea643"; const destToken = "0x5e5d0bea9d4a15db2d0837aff0435faba166190d"; const otherToken = "0xae9902bb655de1a67f334d8661b3ae6a96723d5b"; @@ -93,6 +94,44 @@ describe("ZeroExApiAdapter", () => { expect(_data).to.deep.eq(data); }); + it("rejects ETH output token", async () => { + const data = zeroExMock.interface.encodeFunctionData("transformERC20", [ + sourceToken, + ethToken, + sourceQuantity, + minDestinationQuantity, + [], + ]); + const tx = zeroExApiAdapter.getTradeCalldata( + sourceToken, + ethToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + await expect(tx).to.be.revertedWith("ETH not supported"); + }); + + it("rejects ETH input token", async () => { + const data = zeroExMock.interface.encodeFunctionData("transformERC20", [ + ethToken, + destToken, + sourceQuantity, + minDestinationQuantity, + [], + ]); + const tx = zeroExApiAdapter.getTradeCalldata( + ethToken, + destToken, + destination, + sourceQuantity, + minDestinationQuantity, + data, + ); + await expect(tx).to.be.revertedWith("ETH not supported"); + }); + it("rejects wrong input token", async () => { const data = zeroExMock.interface.encodeFunctionData("transformERC20", [ otherToken,