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