diff --git a/contracts/zero-ex/contracts/deps/forge-std b/contracts/zero-ex/contracts/deps/forge-std index a2edd39db9..058d2004ac 160000 --- a/contracts/zero-ex/contracts/deps/forge-std +++ b/contracts/zero-ex/contracts/deps/forge-std @@ -1 +1 @@ -Subproject commit a2edd39db95df7e9dd3f9ef9edc8c55fefddb6df +Subproject commit 058d2004ac10cc8f194625fb107fb7a87c4e702d diff --git a/contracts/zero-ex/contracts/src/features/MetaTransactionsFeatureV2.sol b/contracts/zero-ex/contracts/src/features/MetaTransactionsFeatureV2.sol index bfc6656819..ac5be32d26 100644 --- a/contracts/zero-ex/contracts/src/features/MetaTransactionsFeatureV2.sol +++ b/contracts/zero-ex/contracts/src/features/MetaTransactionsFeatureV2.sol @@ -15,6 +15,7 @@ pragma solidity ^0.6.5; pragma experimental ABIEncoderV2; +import "@0x/contracts-erc20/src/IEtherToken.sol"; import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol"; import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol"; import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol"; @@ -27,6 +28,7 @@ import "../migrations/LibMigrate.sol"; import "../storage/LibMetaTransactionsStorage.sol"; import "./interfaces/IFeature.sol"; import "./interfaces/IMetaTransactionsFeatureV2.sol"; +import "./interfaces/IMultiplexFeature.sol"; import "./interfaces/INativeOrdersFeature.sol"; import "./interfaces/ITransformERC20Feature.sol"; import "./libs/LibSignature.sol"; @@ -63,8 +65,8 @@ contract MetaTransactionsFeatureV2 is /// @dev Arguments for a `TransformERC20.transformERC20()` call. struct ExternalTransformERC20Args { - IERC20TokenV06 inputToken; - IERC20TokenV06 outputToken; + IERC20Token inputToken; + IERC20Token outputToken; uint256 inputTokenAmount; uint256 minOutputTokenAmount; ITransformERC20Feature.Transformation[] transformations; @@ -92,6 +94,9 @@ contract MetaTransactionsFeatureV2 is "uint256 amount" ")" ); + + /// @dev The WETH token contract. + IEtherToken private immutable WETH; /// @dev Refunds up to `msg.value` leftover ETH at the end of the call. modifier refundsAttachedEth() { @@ -110,7 +115,12 @@ contract MetaTransactionsFeatureV2 is require(initialBalance <= address(this).balance, "MetaTransactionDataV2/ETH_LEAK"); } - constructor(address zeroExAddress) public FixinCommon() FixinEIP712(zeroExAddress) {} + constructor( + address zeroExAddress, + IEtherToken weth + ) public FixinCommon() FixinEIP712(zeroExAddress) { + WETH = weth; + } /// @dev Initialize and register this feature. /// Should be delegatecalled by `Migrate.migrate()`. @@ -244,6 +254,14 @@ contract MetaTransactionsFeatureV2 is returnResult = _executeFillLimitOrderCall(state); } else if (state.selector == INativeOrdersFeature.fillRfqOrder.selector) { returnResult = _executeFillRfqOrderCall(state); + } else if (state.selector == IMultiplexFeature.multiplexBatchSellTokenForToken.selector) { + returnResult = _executeMultiplexBatchSellTokenForTokenCall(state); + } else if (state.selector == IMultiplexFeature.multiplexBatchSellTokenForEth.selector) { + returnResult = _executeMultiplexBatchSellTokenForEthCall(state); + } else if (state.selector == IMultiplexFeature.multiplexMultiHopSellTokenForToken.selector) { + returnResult = _executeMultiplexMultiHopSellTokenForTokenCall(state); + } else if (state.selector == IMultiplexFeature.multiplexMultiHopSellTokenForEth.selector) { + returnResult = _executeMultiplexMultiHopSellTokenForEthCall(state); } else { LibMetaTransactionsRichErrors.MetaTransactionUnsupportedFunctionError(state.hash, state.selector).rrevert(); } @@ -410,7 +428,7 @@ contract MetaTransactionsFeatureV2 is /// @dev Execute a `INativeOrdersFeature.fillRfqOrder()` meta-transaction call /// by decoding the call args and translating the call to the internal - /// `INativeOrdersFeature._fillRfqOrder()` variant, where we can overrideunimpleme + /// `INativeOrdersFeature._fillRfqOrder()` variant, where we can override /// the taker address. function _executeFillRfqOrderCall(ExecuteState memory state) private returns (bytes memory returnResult) { LibNativeOrder.RfqOrder memory order; @@ -438,6 +456,158 @@ contract MetaTransactionsFeatureV2 is ); } + /// @dev Execute a `IMultiplexFeature.multiplexBatchSellTokenForToken()` meta-transaction + /// call by decoding the call args and translating the call to the internal + /// `IMultiplexFeature._multiplexBatchSell()` variant, where we can override the + /// msgSender address. + function _executeMultiplexBatchSellTokenForTokenCall(ExecuteState memory state) private returns (bytes memory returnResult) { + IERC20Token inputToken; + IERC20Token outputToken; + IMultiplexFeature.BatchSellSubcall[] memory calls; + uint256 sellAmount; + uint256 minBuyAmount; + + bytes memory args = _extractArgumentsFromCallData(state.mtx.callData); + (inputToken, outputToken, calls, sellAmount, minBuyAmount) = abi.decode( + args, + (IERC20Token, IERC20Token, IMultiplexFeature.BatchSellSubcall[], uint256, uint256) + ); + + return + _callSelf( + state.hash, + abi.encodeWithSelector( + IMultiplexFeature._multiplexBatchSell.selector, + IMultiplexFeature.BatchSellParams({ + inputToken: inputToken, + outputToken: outputToken, + sellAmount: sellAmount, + calls: calls, + useSelfBalance: false, + recipient: state.mtx.signer, + msgSender: state.mtx.signer + }), + minBuyAmount + ) + ); + } + + /// @dev Execute a `IMultiplexFeature.multiplexBatchSellTokenForEth()` meta-transaction + /// call by decoding the call args and translating the call to the internal + /// `IMultiplexFeature._multiplexBatchSellTokenForEth()` variant, where we can override the + /// msgSender address. + function _executeMultiplexBatchSellTokenForEthCall(ExecuteState memory state) private returns (bytes memory returnResult) { + IERC20Token inputToken; + IMultiplexFeature.BatchSellSubcall[] memory calls; + uint256 sellAmount; + uint256 minBuyAmount; + + bytes memory args = _extractArgumentsFromCallData(state.mtx.callData); + (inputToken, calls, sellAmount, minBuyAmount) = abi.decode( + args, + (IERC20Token, IMultiplexFeature.BatchSellSubcall[], uint256, uint256) + ); + + returnResult = _callSelf( + state.hash, + abi.encodeWithSelector( + IMultiplexFeature._multiplexBatchSell.selector, + IMultiplexFeature.BatchSellParams({ + inputToken: inputToken, + outputToken: IERC20Token(WETH), + sellAmount: sellAmount, + calls: calls, + useSelfBalance: false, + recipient: address(this), + msgSender: state.mtx.signer + }), + minBuyAmount + ) + ); + + // Unwrap and transfer WETH + uint256 boughtAmount = abi.decode(returnResult, (uint256)); + WETH.withdraw(boughtAmount); + _transferEth(state.mtx.signer, boughtAmount); + } + + /// @dev Execute a `IMultiplexFeature.multiplexMultiHopSellTokenForToken()` meta-transaction + /// call by decoding the call args and translating the call to the internal + /// `IMultiplexFeature._multiplexMultiHopSell()` variant, where we can override the + /// msgSender address. + function _executeMultiplexMultiHopSellTokenForTokenCall(ExecuteState memory state) private returns (bytes memory returnResult) { + address[] memory tokens; + IMultiplexFeature.MultiHopSellSubcall[] memory calls; + uint256 sellAmount; + uint256 minBuyAmount; + + bytes memory args = _extractArgumentsFromCallData(state.mtx.callData); + (tokens, calls, sellAmount, minBuyAmount) = abi.decode( + args, + (address[], IMultiplexFeature.MultiHopSellSubcall[], uint256, uint256) + ); + + return + _callSelf( + state.hash, + abi.encodeWithSelector( + IMultiplexFeature._multiplexMultiHopSell.selector, + IMultiplexFeature.MultiHopSellParams({ + tokens: tokens, + sellAmount: sellAmount, + calls: calls, + useSelfBalance: false, + recipient: state.mtx.signer, + msgSender: state.mtx.signer + }), + minBuyAmount + ) + ); + } + + /// @dev Execute a `IMultiplexFeature.multiplexMultiHopSellTokenForEth()` meta-transaction + /// call by decoding the call args and translating the call to the internal + /// `IMultiplexFeature._multiplexMultiHopSellTokenForEth()` variant, where we can override the + /// msgSender address. + function _executeMultiplexMultiHopSellTokenForEthCall(ExecuteState memory state) private returns (bytes memory returnResult) { + address[] memory tokens; + IMultiplexFeature.MultiHopSellSubcall[] memory calls; + uint256 sellAmount; + uint256 minBuyAmount; + + bytes memory args = _extractArgumentsFromCallData(state.mtx.callData); + (tokens, calls, sellAmount, minBuyAmount) = abi.decode( + args, + (address[], IMultiplexFeature.MultiHopSellSubcall[], uint256, uint256) + ); + + require( + tokens[tokens.length - 1] == address(WETH), + "MetaTransactionsFeature::multiplexMultiHopSellTokenForEth/NOT_WETH" + ); + + returnResult = _callSelf( + state.hash, + abi.encodeWithSelector( + IMultiplexFeature._multiplexMultiHopSell.selector, + IMultiplexFeature.MultiHopSellParams({ + tokens: tokens, + sellAmount: sellAmount, + calls: calls, + useSelfBalance: false, + recipient: address(this), + msgSender: state.mtx.signer + }), + minBuyAmount + ) + ); + + // Unwrap and transfer WETH + uint256 boughtAmount = abi.decode(returnResult, (uint256)); + WETH.withdraw(boughtAmount); + _transferEth(state.mtx.signer, boughtAmount); + } + /// @dev Make an arbitrary internal, meta-transaction call. /// Warning: Do not let unadulterated `callData` into this function. function _callSelf(bytes32 hash, bytes memory callData) private returns (bytes memory returnResult) { diff --git a/contracts/zero-ex/contracts/src/features/UniswapV3Feature.sol b/contracts/zero-ex/contracts/src/features/UniswapV3Feature.sol index 43f77b1e03..0366aa8014 100644 --- a/contracts/zero-ex/contracts/src/features/UniswapV3Feature.sol +++ b/contracts/zero-ex/contracts/src/features/UniswapV3Feature.sol @@ -70,6 +70,7 @@ contract UniswapV3Feature is IFeature, IUniswapV3Feature, FixinCommon, FixinToke _registerFeatureFunction(this.sellEthForTokenToUniswapV3.selector); _registerFeatureFunction(this.sellTokenForEthToUniswapV3.selector); _registerFeatureFunction(this.sellTokenForTokenToUniswapV3.selector); + _registerFeatureFunction(this._sellTokenForTokenToUniswapV3.selector); _registerFeatureFunction(this._sellHeldTokenForTokenToUniswapV3.selector); _registerFeatureFunction(this.uniswapV3SwapCallback.selector); return LibMigrate.MIGRATE_SUCCESS; @@ -139,6 +140,23 @@ contract UniswapV3Feature is IFeature, IUniswapV3Feature, FixinCommon, FixinToke buyAmount = _swap(encodedPath, sellAmount, minBuyAmount, msg.sender, _normalizeRecipient(recipient)); } + /// @dev Sell a token for another token directly against uniswap v3. Internal variant. + /// @param encodedPath Uniswap-encoded path. + /// @param sellAmount amount of the first token in the path to sell. + /// @param minBuyAmount Minimum amount of the last token in the path to buy. + /// @param recipient The recipient of the bought tokens. Can be zero for payer. + /// @param payer The address to pull the sold tokens from. + /// @return buyAmount Amount of the last token in the path bought. + function _sellTokenForTokenToUniswapV3( + bytes memory encodedPath, + uint256 sellAmount, + uint256 minBuyAmount, + address recipient, + address payer + ) public override onlySelf returns (uint256 buyAmount) { + buyAmount = _swap(encodedPath, sellAmount, minBuyAmount, payer, _normalizeRecipient(recipient, payer)); + } + /// @dev Sell a token for another token directly against uniswap v3. /// Private variant, uses tokens held by `address(this)`. /// @param encodedPath Uniswap-encoded path. @@ -337,8 +355,13 @@ contract UniswapV3Feature is IFeature, IUniswapV3Feature, FixinCommon, FixinToke } } + // Convert null address values to alternative address. + function _normalizeRecipient(address recipient, address alternative) private pure returns (address payable normalizedRecipient) { + return recipient == address(0) ? payable(alternative) : payable(recipient); + } + // Convert null address values to msg.sender. function _normalizeRecipient(address recipient) private view returns (address payable normalizedRecipient) { - return recipient == address(0) ? msg.sender : payable(recipient); + return _normalizeRecipient(recipient, msg.sender); } } diff --git a/contracts/zero-ex/contracts/src/features/interfaces/IMetaTransactionsFeatureV2.sol b/contracts/zero-ex/contracts/src/features/interfaces/IMetaTransactionsFeatureV2.sol index 7a369ca858..b00748c277 100644 --- a/contracts/zero-ex/contracts/src/features/interfaces/IMetaTransactionsFeatureV2.sol +++ b/contracts/zero-ex/contracts/src/features/interfaces/IMetaTransactionsFeatureV2.sol @@ -15,7 +15,7 @@ pragma solidity ^0.6.5; pragma experimental ABIEncoderV2; -import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol"; +import "@0x/contracts-erc20/src/IERC20Token.sol"; import "../libs/LibSignature.sol"; /// @dev Meta-transactions feature. @@ -40,7 +40,7 @@ interface IMetaTransactionsFeatureV2 { // Encoded call data to a function on the exchange proxy. bytes callData; // ERC20 fee `signer` pays `sender`. - IERC20TokenV06 feeToken; + IERC20Token feeToken; // ERC20 fees. MetaTransactionFeeData[] fees; } diff --git a/contracts/zero-ex/contracts/src/features/interfaces/IMultiplexFeature.sol b/contracts/zero-ex/contracts/src/features/interfaces/IMultiplexFeature.sol index eecf260287..5368197418 100644 --- a/contracts/zero-ex/contracts/src/features/interfaces/IMultiplexFeature.sol +++ b/contracts/zero-ex/contracts/src/features/interfaces/IMultiplexFeature.sol @@ -46,6 +46,8 @@ interface IMultiplexFeature { bool useSelfBalance; // The recipient of the bought output tokens. address recipient; + // The sender of the transaction. + address msgSender; } // Represents a constituent call of a batch sell. @@ -75,6 +77,8 @@ interface IMultiplexFeature { bool useSelfBalance; // The recipient of the bought output tokens. address recipient; + // The sender of the transaction. + address msgSender; } // Represents a constituent call of a multi-hop sell. @@ -153,6 +157,17 @@ interface IMultiplexFeature { uint256 minBuyAmount ) external returns (uint256 boughtAmount); + /// @dev Executes a multiplex BatchSell using the given + /// parameters. Internal only. + /// @param params The parameters for the BatchSell. + /// @param minBuyAmount The minimum amount of `params.outputToken` + /// that must be bought for this function to not revert. + /// @return boughtAmount The amount of `params.outputToken` bought. + function _multiplexBatchSell( + BatchSellParams memory params, + uint256 minBuyAmount + ) external returns (uint256 boughtAmount); + /// @dev Sells attached ETH via the given sequence of tokens /// and calls. `tokens[0]` must be WETH. /// The last token in `tokens` is the output token that @@ -204,4 +219,15 @@ interface IMultiplexFeature { uint256 sellAmount, uint256 minBuyAmount ) external returns (uint256 boughtAmount); + + /// @dev Executes a multiplex MultiHopSell using the given + /// parameters. Internal only. + /// @param params The parameters for the MultiHopSell. + /// @param minBuyAmount The minimum amount of the output token + /// that must be bought for this function to not revert. + /// @return boughtAmount The amount of the output token bought. + function _multiplexMultiHopSell( + MultiHopSellParams memory params, + uint256 minBuyAmount + ) external returns (uint256 boughtAmount); } diff --git a/contracts/zero-ex/contracts/src/features/interfaces/IUniswapV3Feature.sol b/contracts/zero-ex/contracts/src/features/interfaces/IUniswapV3Feature.sol index 89588d7fd3..21105a1fda 100644 --- a/contracts/zero-ex/contracts/src/features/interfaces/IUniswapV3Feature.sol +++ b/contracts/zero-ex/contracts/src/features/interfaces/IUniswapV3Feature.sol @@ -54,6 +54,21 @@ interface IUniswapV3Feature { address recipient ) external returns (uint256 buyAmount); + /// @dev Sell a token for another token directly against uniswap v3. Internal variant. + /// @param encodedPath Uniswap-encoded path. + /// @param sellAmount amount of the first token in the path to sell. + /// @param minBuyAmount Minimum amount of the last token in the path to buy. + /// @param recipient The recipient of the bought tokens. Can be zero for payer. + /// @param payer The address to pull the sold tokens from. + /// @return buyAmount Amount of the last token in the path bought. + function _sellTokenForTokenToUniswapV3( + bytes memory encodedPath, + uint256 sellAmount, + uint256 minBuyAmount, + address recipient, + address payer + ) external returns (uint256 buyAmount); + /// @dev Sell a token for another token directly against uniswap v3. /// Private variant, uses tokens held by `address(this)`. /// @param encodedPath Uniswap-encoded path. diff --git a/contracts/zero-ex/contracts/src/features/multiplex/MultiplexFeature.sol b/contracts/zero-ex/contracts/src/features/multiplex/MultiplexFeature.sol index ca6481602a..ba539be3c4 100644 --- a/contracts/zero-ex/contracts/src/features/multiplex/MultiplexFeature.sol +++ b/contracts/zero-ex/contracts/src/features/multiplex/MultiplexFeature.sol @@ -80,9 +80,11 @@ contract MultiplexFeature is _registerFeatureFunction(this.multiplexBatchSellEthForToken.selector); _registerFeatureFunction(this.multiplexBatchSellTokenForEth.selector); _registerFeatureFunction(this.multiplexBatchSellTokenForToken.selector); + _registerFeatureFunction(this._multiplexBatchSell.selector); _registerFeatureFunction(this.multiplexMultiHopSellEthForToken.selector); _registerFeatureFunction(this.multiplexMultiHopSellTokenForEth.selector); _registerFeatureFunction(this.multiplexMultiHopSellTokenForToken.selector); + _registerFeatureFunction(this._multiplexMultiHopSell.selector); return LibMigrate.MIGRATE_SUCCESS; } @@ -103,14 +105,15 @@ contract MultiplexFeature is // WETH is now held by this contract, // so `useSelfBalance` is true. return - _multiplexBatchSell( + _multiplexBatchSellPrivate( BatchSellParams({ inputToken: WETH, outputToken: outputToken, sellAmount: msg.value, calls: calls, useSelfBalance: true, - recipient: msg.sender + recipient: msg.sender, + msgSender: msg.sender }), minBuyAmount ); @@ -133,14 +136,15 @@ contract MultiplexFeature is // The outputToken is implicitly WETH. The `recipient` // of the WETH is set to this contract, since we // must unwrap the WETH and transfer the resulting ETH. - boughtAmount = _multiplexBatchSell( + boughtAmount = _multiplexBatchSellPrivate( BatchSellParams({ inputToken: inputToken, outputToken: WETH, sellAmount: sellAmount, calls: calls, useSelfBalance: false, - recipient: address(this) + recipient: address(this), + msgSender: msg.sender }), minBuyAmount ); @@ -167,26 +171,40 @@ contract MultiplexFeature is uint256 minBuyAmount ) public override returns (uint256 boughtAmount) { return - _multiplexBatchSell( + _multiplexBatchSellPrivate( BatchSellParams({ inputToken: inputToken, outputToken: outputToken, sellAmount: sellAmount, calls: calls, useSelfBalance: false, - recipient: msg.sender + recipient: msg.sender, + msgSender: msg.sender }), minBuyAmount ); } + /// @dev Executes a batch sell and checks that at least + /// `minBuyAmount` of `outputToken` was bought. Internal variant. + /// @param params Batch sell parameters. + /// @param minBuyAmount The minimum amount of `outputToken` that + /// must be bought for this function to not revert. + /// @return boughtAmount The amount of `outputToken` bought. + function _multiplexBatchSell( + BatchSellParams memory params, + uint256 minBuyAmount + ) public override onlySelf returns (uint256 boughtAmount) { + return _multiplexBatchSellPrivate(params, minBuyAmount); + } + /// @dev Executes a batch sell and checks that at least /// `minBuyAmount` of `outputToken` was bought. /// @param params Batch sell parameters. /// @param minBuyAmount The minimum amount of `outputToken` that /// must be bought for this function to not revert. /// @return boughtAmount The amount of `outputToken` bought. - function _multiplexBatchSell( + function _multiplexBatchSellPrivate( BatchSellParams memory params, uint256 minBuyAmount ) private returns (uint256 boughtAmount) { @@ -226,13 +244,14 @@ contract MultiplexFeature is // WETH is now held by this contract, // so `useSelfBalance` is true. return - _multiplexMultiHopSell( + _multiplexMultiHopSellPrivate( MultiHopSellParams({ tokens: tokens, sellAmount: msg.value, calls: calls, useSelfBalance: true, - recipient: msg.sender + recipient: msg.sender, + msgSender: msg.sender }), minBuyAmount ); @@ -262,13 +281,14 @@ contract MultiplexFeature is ); // The `recipient of the WETH is set to this contract, since // we must unwrap the WETH and transfer the resulting ETH. - boughtAmount = _multiplexMultiHopSell( + boughtAmount = _multiplexMultiHopSellPrivate( MultiHopSellParams({ tokens: tokens, sellAmount: sellAmount, calls: calls, useSelfBalance: false, - recipient: address(this) + recipient: address(this), + msgSender: msg.sender }), minBuyAmount ); @@ -297,25 +317,38 @@ contract MultiplexFeature is uint256 minBuyAmount ) public override returns (uint256 boughtAmount) { return - _multiplexMultiHopSell( + _multiplexMultiHopSellPrivate( MultiHopSellParams({ tokens: tokens, sellAmount: sellAmount, calls: calls, useSelfBalance: false, - recipient: msg.sender + recipient: msg.sender, + msgSender: msg.sender }), minBuyAmount ); } + /// @dev Executes a multi-hop sell. Internal variant. + /// @param params Multi-hop sell parameters. + /// @param minBuyAmount The minimum amount of output tokens that + /// must be bought for this function to not revert. + /// @return boughtAmount The amount of output tokens bought. + function _multiplexMultiHopSell( + MultiHopSellParams memory params, + uint256 minBuyAmount + ) public override onlySelf returns (uint256 boughtAmount) { + return _multiplexMultiHopSellPrivate(params, minBuyAmount); + } + /// @dev Executes a multi-hop sell and checks that at least /// `minBuyAmount` of output tokens were bought. /// @param params Multi-hop sell parameters. /// @param minBuyAmount The minimum amount of output tokens that /// must be bought for this function to not revert. /// @return boughtAmount The amount of output tokens bought. - function _multiplexMultiHopSell( + function _multiplexMultiHopSellPrivate( MultiHopSellParams memory params, uint256 minBuyAmount ) private returns (uint256 boughtAmount) { @@ -387,14 +420,14 @@ contract MultiplexFeature is // amount of the multi-hop fill. state.outputTokenAmount = params.sellAmount; // The first call may expect the input tokens to be held by - // `msg.sender`, `address(this)`, or some other address. + // `msgSender`, `address(this)`, or some other address. // Compute the expected address and transfer the input tokens // there if necessary. state.from = _computeHopTarget(params, 0); - // If the input tokens are currently held by `msg.sender` but + // If the input tokens are currently held by `msgSender` but // the first hop expects them elsewhere, perform a `transferFrom`. - if (!params.useSelfBalance && state.from != msg.sender) { - _transferERC20TokensFrom(IERC20Token(params.tokens[0]), msg.sender, state.from, params.sellAmount); + if (!params.useSelfBalance && state.from != params.msgSender) { + _transferERC20TokensFrom(IERC20Token(params.tokens[0]), params.msgSender, state.from, params.sellAmount); } // If the input tokens are currently held by `address(this)` but // the first hop expects them elsewhere, perform a `transfer`. @@ -411,7 +444,7 @@ contract MultiplexFeature is if (subcall.id == MultiplexSubcall.UniswapV2) { _multiHopSellUniswapV2(state, params, subcall.data); } else if (subcall.id == MultiplexSubcall.UniswapV3) { - _multiHopSellUniswapV3(state, subcall.data); + _multiHopSellUniswapV3(state, params, subcall.data); } else if (subcall.id == MultiplexSubcall.LiquidityProvider) { _multiHopSellLiquidityProvider(state, params, subcall.data); } else if (subcall.id == MultiplexSubcall.BatchSell) { @@ -443,6 +476,8 @@ contract MultiplexFeature is // Likewise, the recipient of the multi-hop sell is // equal to the recipient of its containing batch sell. multiHopParams.recipient = params.recipient; + // The msgSender is the same too. + multiHopParams.msgSender = params.msgSender; // Execute the nested multi-hop sell. uint256 outputTokenAmount = _executeMultiHopSell(multiHopParams).outputTokenAmount; // Increment the sold and bought amounts. @@ -469,7 +504,7 @@ contract MultiplexFeature is // If the nested batch sell is the first hop // and `useSelfBalance` for the containing multi- // hop sell is false, the nested batch sell should - // pull tokens from `msg.sender` (so `batchSellParams.useSelfBalance` + // pull tokens from `msgSender` (so `batchSellParams.useSelfBalance` // should be false). Otherwise `batchSellParams.useSelfBalance` // should be true. batchSellParams.useSelfBalance = state.hopIndex > 0 || params.useSelfBalance; @@ -477,6 +512,8 @@ contract MultiplexFeature is // that should receive the output tokens of the // batch sell. batchSellParams.recipient = state.to; + // msgSender shound be the same too. + batchSellParams.msgSender = params.msgSender; // Execute the nested batch sell. state.outputTokenAmount = _executeBatchSell(batchSellParams).boughtAmount; } @@ -509,25 +546,25 @@ contract MultiplexFeature is // UniswapV3 uses a callback to pull in the tokens being // sold to it. The callback implemented in `UniswapV3Feature` // can either: - // - call `transferFrom` to move tokens from `msg.sender` to the + // - call `transferFrom` to move tokens from `msgSender` to the // UniswapV3 pool, or // - call `transfer` to move tokens from `address(this)` to the // UniswapV3 pool. // A nested batch sell is similar, in that it can either: - // - use tokens from `msg.sender`, or + // - use tokens from `msgSender`, or // - use tokens held by `address(this)`. // Suppose UniswapV3/BatchSell is the first call in the multi-hop - // path. The input tokens are either held by `msg.sender`, + // path. The input tokens are either held by `msgSender`, // or in the case of `multiplexMultiHopSellEthForToken` WETH is // held by `address(this)`. The target is set accordingly. // If this is _not_ the first call in the multi-hop path, we // are dealing with an "intermediate" token in the multi-hop path, - // which `msg.sender` may not have an allowance set for. Thus + // which `msgSender` may not have an allowance set for. Thus // target must be set to `address(this)` for `i > 0`. if (i == 0 && !params.useSelfBalance) { - target = msg.sender; + target = params.msgSender; } else { target = address(this); } diff --git a/contracts/zero-ex/contracts/src/features/multiplex/MultiplexLiquidityProvider.sol b/contracts/zero-ex/contracts/src/features/multiplex/MultiplexLiquidityProvider.sol index c880750f19..612214fad4 100644 --- a/contracts/zero-ex/contracts/src/features/multiplex/MultiplexLiquidityProvider.sol +++ b/contracts/zero-ex/contracts/src/features/multiplex/MultiplexLiquidityProvider.sol @@ -67,7 +67,7 @@ abstract contract MultiplexLiquidityProvider is FixinCommon, FixinTokenSpender { _transferERC20Tokens(params.inputToken, provider, sellAmount); } else { // Otherwise, transfer the input tokens from `msg.sender`. - _transferERC20TokensFrom(params.inputToken, msg.sender, provider, sellAmount); + _transferERC20TokensFrom(params.inputToken, params.msgSender, provider, sellAmount); } // Cache the recipient's balance of the output token. uint256 balanceBefore = params.outputToken.balanceOf(params.recipient); diff --git a/contracts/zero-ex/contracts/src/features/multiplex/MultiplexOtc.sol b/contracts/zero-ex/contracts/src/features/multiplex/MultiplexOtc.sol index 49fd673efa..7d66cc9d30 100644 --- a/contracts/zero-ex/contracts/src/features/multiplex/MultiplexOtc.sol +++ b/contracts/zero-ex/contracts/src/features/multiplex/MultiplexOtc.sol @@ -55,7 +55,7 @@ abstract contract MultiplexOtc is FixinEIP712 { order, signature, sellAmount.safeDowncastToUint128(), - msg.sender, + params.msgSender, params.useSelfBalance, params.recipient ) diff --git a/contracts/zero-ex/contracts/src/features/multiplex/MultiplexRfq.sol b/contracts/zero-ex/contracts/src/features/multiplex/MultiplexRfq.sol index 633e9e4efb..510765c230 100644 --- a/contracts/zero-ex/contracts/src/features/multiplex/MultiplexRfq.sol +++ b/contracts/zero-ex/contracts/src/features/multiplex/MultiplexRfq.sol @@ -54,7 +54,7 @@ abstract contract MultiplexRfq is FixinEIP712 { order, signature, sellAmount.safeDowncastToUint128(), - msg.sender, + params.msgSender, params.useSelfBalance, params.recipient ) diff --git a/contracts/zero-ex/contracts/src/features/multiplex/MultiplexTransformERC20.sol b/contracts/zero-ex/contracts/src/features/multiplex/MultiplexTransformERC20.sol index 289401e175..ea6c06740d 100644 --- a/contracts/zero-ex/contracts/src/features/multiplex/MultiplexTransformERC20.sol +++ b/contracts/zero-ex/contracts/src/features/multiplex/MultiplexTransformERC20.sol @@ -30,8 +30,8 @@ abstract contract MultiplexTransformERC20 { ) internal { ITransformERC20Feature.TransformERC20Args memory args; // We want the TransformedERC20 event to have - // `msg.sender` as the taker. - args.taker = msg.sender; + // `msgSender` as the taker. + args.taker = payable(params.msgSender); args.inputToken = params.inputToken; args.outputToken = params.outputToken; args.inputTokenAmount = sellAmount; diff --git a/contracts/zero-ex/contracts/src/features/multiplex/MultiplexUniswapV2.sol b/contracts/zero-ex/contracts/src/features/multiplex/MultiplexUniswapV2.sol index 6582e40b0b..058aa046db 100644 --- a/contracts/zero-ex/contracts/src/features/multiplex/MultiplexUniswapV2.sol +++ b/contracts/zero-ex/contracts/src/features/multiplex/MultiplexUniswapV2.sol @@ -77,7 +77,7 @@ abstract contract MultiplexUniswapV2 is FixinCommon, FixinTokenSpender { if (params.useSelfBalance) { _transferERC20Tokens(IERC20Token(tokens[0]), firstPairAddress, sellAmount); } else { - _transferERC20TokensFrom(IERC20Token(tokens[0]), msg.sender, firstPairAddress, sellAmount); + _transferERC20TokensFrom(IERC20Token(tokens[0]), params.msgSender, firstPairAddress, sellAmount); } // Execute the Uniswap/Sushiswap trade. return _sellToUniswapV2(tokens, sellAmount, isSushi, firstPairAddress, params.recipient); diff --git a/contracts/zero-ex/contracts/src/features/multiplex/MultiplexUniswapV3.sol b/contracts/zero-ex/contracts/src/features/multiplex/MultiplexUniswapV3.sol index 4b34dfd32b..d6992ef677 100644 --- a/contracts/zero-ex/contracts/src/features/multiplex/MultiplexUniswapV3.sol +++ b/contracts/zero-ex/contracts/src/features/multiplex/MultiplexUniswapV3.sol @@ -45,16 +45,16 @@ abstract contract MultiplexUniswapV3 is FixinTokenSpender { ) ); } else { - // Otherwise, we self-delegatecall the normal variant - // `sellTokenForTokenToUniswapV3`, which pulls the input token - // from `msg.sender`. + // Otherwise, we self-delegatecall `_sellTokenForTokenToUniswapV3`, + // which pulls the input token from a specified `payer`. (success, resultData) = address(this).delegatecall( abi.encodeWithSelector( - IUniswapV3Feature.sellTokenForTokenToUniswapV3.selector, + IUniswapV3Feature._sellTokenForTokenToUniswapV3.selector, wrappedCallData, sellAmount, 0, - params.recipient + params.recipient, + params.msgSender ) ); } @@ -69,6 +69,7 @@ abstract contract MultiplexUniswapV3 is FixinTokenSpender { function _multiHopSellUniswapV3( IMultiplexFeature.MultiHopSellState memory state, + IMultiplexFeature.MultiHopSellParams memory params, bytes memory wrappedCallData ) internal { bool success; @@ -87,16 +88,16 @@ abstract contract MultiplexUniswapV3 is FixinTokenSpender { ) ); } else { - // Otherwise, we self-delegatecall the normal variant - // `sellTokenForTokenToUniswapV3`, which pulls the input token - // from `msg.sender`. + // Otherwise, we self-delegatecall `_sellTokenForTokenToUniswapV3`, + // which pulls the input token from `msgSender`. (success, resultData) = address(this).delegatecall( abi.encodeWithSelector( - IUniswapV3Feature.sellTokenForTokenToUniswapV3.selector, + IUniswapV3Feature._sellTokenForTokenToUniswapV3.selector, wrappedCallData, state.outputTokenAmount, 0, - state.to + state.to, + params.msgSender ) ); } diff --git a/contracts/zero-ex/tests/MetaTransactionV2Test.t.sol b/contracts/zero-ex/tests/MetaTransactionV2Test.t.sol index 297ca13b2d..80f9232b12 100644 --- a/contracts/zero-ex/tests/MetaTransactionV2Test.t.sol +++ b/contracts/zero-ex/tests/MetaTransactionV2Test.t.sol @@ -26,16 +26,16 @@ import "../contracts/src/features/libs/LibSignature.sol"; import "src/features/libs/LibNativeOrder.sol"; import "../contracts/test/tokens/TestMintableERC20Token.sol"; import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol"; -import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol"; +import "@0x/contracts-erc20/src/IEtherToken.sol"; contract MetaTransactionTest is BaseTest, TestUtils { DeployZeroEx.ZeroExDeployed zeroExDeployed; address private constant ZERO_ADDRESS = 0x0000000000000000000000000000000000000000; address private constant USER_ADDRESS = 0x6dc3a54FeAE57B65d185A7B159c5d3FA7fD7FD0F; uint256 private constant USER_KEY = 0x1fc1630343b31e60b7a197a53149ca571ed9d9791e2833337bbd8110c30710ec; - IEtherTokenV06 private wethToken; - IERC20TokenV06 private usdcToken; - IERC20TokenV06 private zrxToken; + IEtherToken private wethToken; + IERC20Token private usdcToken; + IERC20Token private zrxToken; uint256 private constant oneEth = 1e18; address private signerAddress; uint256 private signerKey; @@ -47,8 +47,8 @@ contract MetaTransactionTest is BaseTest, TestUtils { (signerAddress, signerKey) = getSigner(); zeroExDeployed = new DeployZeroEx().deployZeroEx(); wethToken = zeroExDeployed.weth; - usdcToken = IERC20TokenV06(address(new TestMintableERC20Token())); - zrxToken = IERC20TokenV06(address(new TestMintableERC20Token())); + usdcToken = IERC20Token(address(new TestMintableERC20Token())); + zrxToken = IERC20Token(address(new TestMintableERC20Token())); transformerNonce = zeroExDeployed.transformerDeployer.nonce(); vm.prank(zeroExDeployed.transformerDeployer.authorities(0)); diff --git a/contracts/zero-ex/tests/utils/DeployZeroEx.sol b/contracts/zero-ex/tests/utils/DeployZeroEx.sol index f33af66543..56268173fe 100644 --- a/contracts/zero-ex/tests/utils/DeployZeroEx.sol +++ b/contracts/zero-ex/tests/utils/DeployZeroEx.sol @@ -204,7 +204,7 @@ contract DeployZeroEx is Test { ); ZERO_EX_DEPLOYED.features.fundRecoveryFeature = new FundRecoveryFeature(); ZERO_EX_DEPLOYED.features.metaTransactionsFeature = new MetaTransactionsFeature(address(ZERO_EX)); - ZERO_EX_DEPLOYED.features.metaTransactionsFeatureV2 = new MetaTransactionsFeatureV2(address(ZERO_EX)); + ZERO_EX_DEPLOYED.features.metaTransactionsFeatureV2 = new MetaTransactionsFeatureV2(address(ZERO_EX), ZERO_EX_DEPLOYED.weth); ZERO_EX_DEPLOYED.features.erc1155OrdersFeature = new ERC1155OrdersFeature( address(ZERO_EX), ZERO_EX_DEPLOYED.weth diff --git a/contracts/zero-ex/tests/utils/TestUtils.sol b/contracts/zero-ex/tests/utils/TestUtils.sol index 1541e29ca6..c1dd2894d8 100644 --- a/contracts/zero-ex/tests/utils/TestUtils.sol +++ b/contracts/zero-ex/tests/utils/TestUtils.sol @@ -22,7 +22,7 @@ import "src/transformers/LibERC20Transformer.sol"; import "src/features/libs/LibSignature.sol"; import "src/features/libs/LibNativeOrder.sol"; import "../../contracts/test/tokens/TestMintableERC20Token.sol"; -import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol"; +import "@0x/contracts-erc20/src/IEtherToken.sol"; contract TestUtils is Test { address private constant ZERO_ADDRESS = 0x0000000000000000000000000000000000000000; @@ -54,8 +54,8 @@ contract TestUtils is Test { function makeTestRfqOrder( DeployZeroEx.ZeroExDeployed memory zeroExDeployed, - IERC20TokenV06 makerToken, - IERC20TokenV06 takerToken, + IERC20Token makerToken, + IERC20Token takerToken, address makerAddress, address takerAddress, uint256 makerKey @@ -93,8 +93,8 @@ contract TestUtils is Test { function makeTestLimitOrder( DeployZeroEx.ZeroExDeployed memory zeroExDeployed, - IERC20TokenV06 makerToken, - IERC20TokenV06 takerToken, + IERC20Token makerToken, + IERC20Token takerToken, address makerAddress, address takerAddress, uint256 makerKey @@ -133,8 +133,8 @@ contract TestUtils is Test { function transformERC20Call( DeployZeroEx.ZeroExDeployed memory zeroExDeployed, - IERC20TokenV06 makerToken, - IERC20TokenV06 takerToken, + IERC20Token makerToken, + IERC20Token takerToken, address takerAddress, uint256 transformerNonce ) public returns (bytes memory) { @@ -158,11 +158,11 @@ contract TestUtils is Test { ); } - function mintTo(IERC20TokenV06 token, address recipient, uint256 amount) public { + function mintTo(IERC20Token token, address recipient, uint256 amount) public { TestMintableERC20Token(address(token)).mint(recipient, amount); } - function mintToWETH(IEtherTokenV06 wethToken, address recipient, uint256 amount) public { + function mintToWETH(IEtherToken wethToken, address recipient, uint256 amount) public { wethToken.deposit{value: amount}(); WETH9V06(payable(address(wethToken))).transfer(recipient, amount); }