Allow v2 orders to be filled if their makerAssetFeeData field uses a v2 order id
This commit is contained in:
parent
2113fb490d
commit
fe9fc6b459
@ -32,12 +32,14 @@ contract Forwarder is
|
||||
{
|
||||
constructor (
|
||||
address _exchange,
|
||||
address _exchangeV2,
|
||||
address _weth
|
||||
)
|
||||
public
|
||||
Ownable()
|
||||
LibConstants(
|
||||
_exchange,
|
||||
_exchangeV2,
|
||||
_weth
|
||||
)
|
||||
MixinForwarderCore()
|
||||
|
@ -30,6 +30,7 @@ import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||
import "./libs/LibConstants.sol";
|
||||
import "./libs/LibForwarderRichErrors.sol";
|
||||
import "./interfaces/IExchangeV2.sol";
|
||||
import "./MixinAssets.sol";
|
||||
|
||||
|
||||
@ -54,23 +55,19 @@ contract MixinExchangeWrapper is
|
||||
internal
|
||||
returns (LibFillResults.FillResults memory fillResults)
|
||||
{
|
||||
// ABI encode calldata for `fillOrder`
|
||||
bytes memory fillOrderCalldata = abi.encodeWithSelector(
|
||||
IExchange(address(0)).fillOrder.selector,
|
||||
if (order.makerFeeAssetData.readBytes4(0) == EXCHANGE_V2_ORDER_ID) {
|
||||
return _fillV2OrderNoThrow(
|
||||
order,
|
||||
takerAssetFillAmount,
|
||||
signature
|
||||
);
|
||||
|
||||
address exchange = address(EXCHANGE);
|
||||
(bool didSucceed, bytes memory returnData) = exchange.call(fillOrderCalldata);
|
||||
if (didSucceed) {
|
||||
assert(returnData.length == 160);
|
||||
fillResults = abi.decode(returnData, (LibFillResults.FillResults));
|
||||
}
|
||||
|
||||
// fillResults values will be 0 by default if call was unsuccessful
|
||||
return fillResults;
|
||||
return _fillV3OrderNoThrow(
|
||||
order,
|
||||
takerAssetFillAmount,
|
||||
signature
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Executes a single call of fillOrder according to the wethSellAmount and
|
||||
@ -370,6 +367,98 @@ contract MixinExchangeWrapper is
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Fills the input ExchangeV2 order. The `makerFeeAssetData` must be
|
||||
// equal to EXCHANGE_V2_ORDER_ID (0x770501f8).
|
||||
/// Returns false if the transaction would otherwise revert.
|
||||
/// @param order Order struct containing order specifications.
|
||||
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
|
||||
/// @param signature Proof that order has been created by maker.
|
||||
/// @return Amounts filled and fees paid by maker and taker.
|
||||
function _fillV2OrderNoThrow(
|
||||
LibOrder.Order memory order,
|
||||
uint256 takerAssetFillAmount,
|
||||
bytes memory signature
|
||||
)
|
||||
internal
|
||||
returns (LibFillResults.FillResults memory fillResults)
|
||||
{
|
||||
// Strip v3 specific fields from order
|
||||
IExchangeV2.Order memory v2Order = IExchangeV2.Order({
|
||||
makerAddress: order.makerAddress,
|
||||
takerAddress: order.takerAddress,
|
||||
feeRecipientAddress: order.feeRecipientAddress,
|
||||
senderAddress: order.senderAddress,
|
||||
makerAssetAmount: order.makerAssetAmount,
|
||||
takerAssetAmount: order.takerAssetAmount,
|
||||
makerFee: order.makerFee,
|
||||
takerFee: order.makerFee,
|
||||
expirationTimeSeconds: order.expirationTimeSeconds,
|
||||
salt: order.salt,
|
||||
makerAssetData: order.makerAssetData,
|
||||
takerAssetData: order.takerAssetData
|
||||
});
|
||||
|
||||
// ABI encode calldata for `fillOrder`
|
||||
bytes memory fillOrderCalldata = abi.encodeWithSelector(
|
||||
IExchangeV2(address(0)).fillOrder.selector,
|
||||
v2Order,
|
||||
takerAssetFillAmount,
|
||||
signature
|
||||
);
|
||||
|
||||
address exchange = address(EXCHANGE_V2);
|
||||
(bool didSucceed, bytes memory returnData) = exchange.call(fillOrderCalldata);
|
||||
if (didSucceed) {
|
||||
assert(returnData.length == 128);
|
||||
IExchangeV2.FillResults memory v2FillResults = abi.decode(returnData, (IExchangeV2.FillResults));
|
||||
|
||||
// Add `protocolFeePaid` field to v2 fill results
|
||||
fillResults = LibFillResults.FillResults({
|
||||
makerAssetFilledAmount: v2FillResults.makerAssetFilledAmount,
|
||||
takerAssetFilledAmount: v2FillResults.takerAssetFilledAmount,
|
||||
makerFeePaid: v2FillResults.makerFeePaid,
|
||||
takerFeePaid: v2FillResults.takerFeePaid,
|
||||
protocolFeePaid: 0
|
||||
});
|
||||
}
|
||||
|
||||
// fillResults values will be 0 by default if call was unsuccessful
|
||||
return fillResults;
|
||||
}
|
||||
|
||||
/// @dev Fills the input ExchangeV3 order.
|
||||
/// Returns false if the transaction would otherwise revert.
|
||||
/// @param order Order struct containing order specifications.
|
||||
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
|
||||
/// @param signature Proof that order has been created by maker.
|
||||
/// @return Amounts filled and fees paid by maker and taker.
|
||||
function _fillV3OrderNoThrow(
|
||||
LibOrder.Order memory order,
|
||||
uint256 takerAssetFillAmount,
|
||||
bytes memory signature
|
||||
)
|
||||
internal
|
||||
returns (LibFillResults.FillResults memory fillResults)
|
||||
{
|
||||
// ABI encode calldata for `fillOrder`
|
||||
bytes memory fillOrderCalldata = abi.encodeWithSelector(
|
||||
IExchange(address(0)).fillOrder.selector,
|
||||
order,
|
||||
takerAssetFillAmount,
|
||||
signature
|
||||
);
|
||||
|
||||
address exchange = address(EXCHANGE);
|
||||
(bool didSucceed, bytes memory returnData) = exchange.call(fillOrderCalldata);
|
||||
if (didSucceed) {
|
||||
assert(returnData.length == 160);
|
||||
fillResults = abi.decode(returnData, (LibFillResults.FillResults));
|
||||
}
|
||||
|
||||
// fillResults values will be 0 by default if call was unsuccessful
|
||||
return fillResults;
|
||||
}
|
||||
|
||||
/// @dev Checks whether one asset is effectively equal to another asset.
|
||||
/// This is the case if they have the same ERC20Proxy/ERC20BridgeProxy asset data, or if
|
||||
/// one is the ERC20Bridge equivalent of the other.
|
||||
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
|
||||
contract IExchangeV2 {
|
||||
|
||||
// solhint-disable max-line-length
|
||||
struct Order {
|
||||
address makerAddress; // Address that created the order.
|
||||
address takerAddress; // Address that is allowed to fill the order. If set to 0, any address is allowed to fill the order.
|
||||
address feeRecipientAddress; // Address that will recieve fees when order is filled.
|
||||
address senderAddress; // Address that is allowed to call Exchange contract methods that affect this order. If set to 0, any address is allowed to call these methods.
|
||||
uint256 makerAssetAmount; // Amount of makerAsset being offered by maker. Must be greater than 0.
|
||||
uint256 takerAssetAmount; // Amount of takerAsset being bid on by maker. Must be greater than 0.
|
||||
uint256 makerFee; // Amount of ZRX paid to feeRecipient by maker when order is filled. If set to 0, no transfer of ZRX from maker to feeRecipient will be attempted.
|
||||
uint256 takerFee; // Amount of ZRX paid to feeRecipient by taker when order is filled. If set to 0, no transfer of ZRX from taker to feeRecipient will be attempted.
|
||||
uint256 expirationTimeSeconds; // Timestamp in seconds at which order expires.
|
||||
uint256 salt; // Arbitrary number to facilitate uniqueness of the order's hash.
|
||||
bytes makerAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring makerAsset. The last byte references the id of this proxy.
|
||||
bytes takerAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring takerAsset. The last byte references the id of this proxy.
|
||||
}
|
||||
// solhint-enable max-line-length
|
||||
|
||||
struct FillResults {
|
||||
uint256 makerAssetFilledAmount; // Total amount of makerAsset(s) filled.
|
||||
uint256 takerAssetFilledAmount; // Total amount of takerAsset(s) filled.
|
||||
uint256 makerFeePaid; // Total amount of ZRX paid by maker(s) to feeRecipient(s).
|
||||
uint256 takerFeePaid; // Total amount of ZRX paid by taker to feeRecipients(s).
|
||||
}
|
||||
|
||||
/// @dev Fills the input order.
|
||||
/// @param order Order struct containing order specifications.
|
||||
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
|
||||
/// @param signature Proof that order has been created by maker.
|
||||
/// @return Amounts filled and fees paid by maker and taker.
|
||||
function fillOrder(
|
||||
Order memory order,
|
||||
uint256 takerAssetFillAmount,
|
||||
bytes memory signature
|
||||
)
|
||||
public;
|
||||
}
|
@ -18,29 +18,49 @@
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol";
|
||||
import "../interfaces/IExchangeV2.sol";
|
||||
|
||||
|
||||
contract LibConstants {
|
||||
|
||||
using LibBytes for bytes;
|
||||
uint256 constant internal MAX_UINT = uint256(-1);
|
||||
|
||||
uint256 constant internal MAX_UINT = 2**256 - 1;
|
||||
// The v2 order id is the first 4 bytes of the ExchangeV2 order shema hash.
|
||||
// bytes4(keccak256(abi.encodePacked(
|
||||
// "Order(",
|
||||
// "address makerAddress,",
|
||||
// "address takerAddress,",
|
||||
// "address feeRecipientAddress,",
|
||||
// "address senderAddress,",
|
||||
// "uint256 makerAssetAmount,",
|
||||
// "uint256 takerAssetAmount,",
|
||||
// "uint256 makerFee,",
|
||||
// "uint256 takerFee,",
|
||||
// "uint256 expirationTimeSeconds,",
|
||||
// "uint256 salt,",
|
||||
// "bytes makerAssetData,",
|
||||
// "bytes takerAssetData",
|
||||
// ")"
|
||||
// )));
|
||||
bytes4 constant public EXCHANGE_V2_ORDER_ID = 0x770501f8;
|
||||
|
||||
// solhint-disable var-name-mixedcase
|
||||
IExchange internal EXCHANGE;
|
||||
IExchangeV2 internal EXCHANGE_V2;
|
||||
IEtherToken internal ETHER_TOKEN;
|
||||
// solhint-enable var-name-mixedcase
|
||||
|
||||
constructor (
|
||||
address _exchange,
|
||||
address _exchangeV2,
|
||||
address _weth
|
||||
)
|
||||
public
|
||||
{
|
||||
EXCHANGE = IExchange(_exchange);
|
||||
EXCHANGE_V2 = IExchangeV2(_exchangeV2);
|
||||
ETHER_TOKEN = IEtherToken(_weth);
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ contract TestForwarder is
|
||||
constructor ()
|
||||
public
|
||||
LibConstants(
|
||||
address(0),
|
||||
address(0),
|
||||
address(0)
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user