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 (
|
constructor (
|
||||||
address _exchange,
|
address _exchange,
|
||||||
|
address _exchangeV2,
|
||||||
address _weth
|
address _weth
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
Ownable()
|
Ownable()
|
||||||
LibConstants(
|
LibConstants(
|
||||||
_exchange,
|
_exchange,
|
||||||
|
_exchangeV2,
|
||||||
_weth
|
_weth
|
||||||
)
|
)
|
||||||
MixinForwarderCore()
|
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 "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||||
import "./libs/LibConstants.sol";
|
import "./libs/LibConstants.sol";
|
||||||
import "./libs/LibForwarderRichErrors.sol";
|
import "./libs/LibForwarderRichErrors.sol";
|
||||||
|
import "./interfaces/IExchangeV2.sol";
|
||||||
import "./MixinAssets.sol";
|
import "./MixinAssets.sol";
|
||||||
|
|
||||||
|
|
||||||
@ -54,23 +55,19 @@ contract MixinExchangeWrapper is
|
|||||||
internal
|
internal
|
||||||
returns (LibFillResults.FillResults memory fillResults)
|
returns (LibFillResults.FillResults memory fillResults)
|
||||||
{
|
{
|
||||||
// ABI encode calldata for `fillOrder`
|
if (order.makerFeeAssetData.readBytes4(0) == EXCHANGE_V2_ORDER_ID) {
|
||||||
bytes memory fillOrderCalldata = abi.encodeWithSelector(
|
return _fillV2OrderNoThrow(
|
||||||
IExchange(address(0)).fillOrder.selector,
|
order,
|
||||||
|
takerAssetFillAmount,
|
||||||
|
signature
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _fillV3OrderNoThrow(
|
||||||
order,
|
order,
|
||||||
takerAssetFillAmount,
|
takerAssetFillAmount,
|
||||||
signature
|
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 Executes a single call of fillOrder according to the wethSellAmount and
|
/// @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.
|
/// @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
|
/// This is the case if they have the same ERC20Proxy/ERC20BridgeProxy asset data, or if
|
||||||
/// one is the ERC20Bridge equivalent of the other.
|
/// 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;
|
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-exchange/contracts/src/interfaces/IExchange.sol";
|
||||||
import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol";
|
import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol";
|
||||||
|
import "../interfaces/IExchangeV2.sol";
|
||||||
|
|
||||||
|
|
||||||
contract LibConstants {
|
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
|
// solhint-disable var-name-mixedcase
|
||||||
IExchange internal EXCHANGE;
|
IExchange internal EXCHANGE;
|
||||||
|
IExchangeV2 internal EXCHANGE_V2;
|
||||||
IEtherToken internal ETHER_TOKEN;
|
IEtherToken internal ETHER_TOKEN;
|
||||||
// solhint-enable var-name-mixedcase
|
// solhint-enable var-name-mixedcase
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
address _exchange,
|
address _exchange,
|
||||||
|
address _exchangeV2,
|
||||||
address _weth
|
address _weth
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
{
|
{
|
||||||
EXCHANGE = IExchange(_exchange);
|
EXCHANGE = IExchange(_exchange);
|
||||||
|
EXCHANGE_V2 = IExchangeV2(_exchangeV2);
|
||||||
ETHER_TOKEN = IEtherToken(_weth);
|
ETHER_TOKEN = IEtherToken(_weth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ contract TestForwarder is
|
|||||||
constructor ()
|
constructor ()
|
||||||
public
|
public
|
||||||
LibConstants(
|
LibConstants(
|
||||||
|
address(0),
|
||||||
address(0),
|
address(0),
|
||||||
address(0)
|
address(0)
|
||||||
)
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user