mzhu25 3cc639c8d0
MultiplexFeature and BatchFillNativeOrdersFeature (#140)
* WrappedFillFeature

* Address internal feedback

* create features/interfaces/ directory

* Split NativeOrdersFeature into mixins

* Rename mixins to use NativeOrders namespace

* Add BatchFillNativeOrdersFeature

* Rename WrapperFillFeature => MultiplexFeature and add natspec comments

* Emit LiquidityProviderSwap event

* post-rebase fixes

* Multiplex mainnet fork tests

* lint

* Add tests for batch fill functions

* Remove market functions

* Addres PR feedback

* Remove nested _batchFill calls from _multiHopFill

* Add BatchFillIncompleteRevertError type

* Use call{value: amount}() instead of transfer(amount)

* Remove outdated comment

* Update some comments

* Add events

* Address spot-check recommendations

* Remove-top level events, add ExpiredRfqOrder event

* Update changelog

* Change ExpiredRfqOrder event

* Update IZeroEx artifact and contract wrapper
2021-03-08 15:45:49 -08:00

244 lines
8.2 KiB
Solidity

// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2020 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.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
import "../../errors/LibNativeOrdersRichErrors.sol";
/// @dev A library for common native order operations.
library LibNativeOrder {
using LibSafeMathV06 for uint256;
using LibRichErrorsV06 for bytes;
enum OrderStatus {
INVALID,
FILLABLE,
FILLED,
CANCELLED,
EXPIRED
}
/// @dev A standard OTC or OO limit order.
struct LimitOrder {
IERC20TokenV06 makerToken;
IERC20TokenV06 takerToken;
uint128 makerAmount;
uint128 takerAmount;
uint128 takerTokenFeeAmount;
address maker;
address taker;
address sender;
address feeRecipient;
bytes32 pool;
uint64 expiry;
uint256 salt;
}
/// @dev An RFQ limit order.
struct RfqOrder {
IERC20TokenV06 makerToken;
IERC20TokenV06 takerToken;
uint128 makerAmount;
uint128 takerAmount;
address maker;
address taker;
address txOrigin;
bytes32 pool;
uint64 expiry;
uint256 salt;
}
/// @dev Info on a limit or RFQ order.
struct OrderInfo {
bytes32 orderHash;
OrderStatus status;
uint128 takerTokenFilledAmount;
}
uint256 private constant UINT_128_MASK = (1 << 128) - 1;
uint256 private constant UINT_64_MASK = (1 << 64) - 1;
uint256 private constant ADDRESS_MASK = (1 << 160) - 1;
// The type hash for limit orders, which is:
// keccak256(abi.encodePacked(
// "LimitOrder(",
// "address makerToken,",
// "address takerToken,",
// "uint128 makerAmount,",
// "uint128 takerAmount,",
// "uint128 takerTokenFeeAmount,",
// "address maker,",
// "address taker,",
// "address sender,",
// "address feeRecipient,",
// "bytes32 pool,",
// "uint64 expiry,",
// "uint256 salt"
// ")"
// ))
uint256 private constant _LIMIT_ORDER_TYPEHASH =
0xce918627cb55462ddbb85e73de69a8b322f2bc88f4507c52fcad6d4c33c29d49;
// The type hash for RFQ orders, which is:
// keccak256(abi.encodePacked(
// "RfqOrder(",
// "address makerToken,",
// "address takerToken,",
// "uint128 makerAmount,",
// "uint128 takerAmount,",
// "address maker,",
// "address taker,",
// "address txOrigin,",
// "bytes32 pool,",
// "uint64 expiry,",
// "uint256 salt"
// ")"
// ))
uint256 private constant _RFQ_ORDER_TYPEHASH =
0xe593d3fdfa8b60e5e17a1b2204662ecbe15c23f2084b9ad5bae40359540a7da9;
/// @dev Get the struct hash of a limit order.
/// @param order The limit order.
/// @return structHash The struct hash of the order.
function getLimitOrderStructHash(LimitOrder memory order)
internal
pure
returns (bytes32 structHash)
{
// The struct hash is:
// keccak256(abi.encode(
// TYPE_HASH,
// order.makerToken,
// order.takerToken,
// order.makerAmount,
// order.takerAmount,
// order.takerTokenFeeAmount,
// order.maker,
// order.taker,
// order.sender,
// order.feeRecipient,
// order.pool,
// order.expiry,
// order.salt,
// ))
assembly {
let mem := mload(0x40)
mstore(mem, _LIMIT_ORDER_TYPEHASH)
// order.makerToken;
mstore(add(mem, 0x20), and(ADDRESS_MASK, mload(order)))
// order.takerToken;
mstore(add(mem, 0x40), and(ADDRESS_MASK, mload(add(order, 0x20))))
// order.makerAmount;
mstore(add(mem, 0x60), and(UINT_128_MASK, mload(add(order, 0x40))))
// order.takerAmount;
mstore(add(mem, 0x80), and(UINT_128_MASK, mload(add(order, 0x60))))
// order.takerTokenFeeAmount;
mstore(add(mem, 0xA0), and(UINT_128_MASK, mload(add(order, 0x80))))
// order.maker;
mstore(add(mem, 0xC0), and(ADDRESS_MASK, mload(add(order, 0xA0))))
// order.taker;
mstore(add(mem, 0xE0), and(ADDRESS_MASK, mload(add(order, 0xC0))))
// order.sender;
mstore(add(mem, 0x100), and(ADDRESS_MASK, mload(add(order, 0xE0))))
// order.feeRecipient;
mstore(add(mem, 0x120), and(ADDRESS_MASK, mload(add(order, 0x100))))
// order.pool;
mstore(add(mem, 0x140), mload(add(order, 0x120)))
// order.expiry;
mstore(add(mem, 0x160), and(UINT_64_MASK, mload(add(order, 0x140))))
// order.salt;
mstore(add(mem, 0x180), mload(add(order, 0x160)))
structHash := keccak256(mem, 0x1A0)
}
}
/// @dev Get the struct hash of a RFQ order.
/// @param order The RFQ order.
/// @return structHash The struct hash of the order.
function getRfqOrderStructHash(RfqOrder memory order)
internal
pure
returns (bytes32 structHash)
{
// The struct hash is:
// keccak256(abi.encode(
// TYPE_HASH,
// order.makerToken,
// order.takerToken,
// order.makerAmount,
// order.takerAmount,
// order.maker,
// order.taker,
// order.txOrigin,
// order.pool,
// order.expiry,
// order.salt,
// ))
assembly {
let mem := mload(0x40)
mstore(mem, _RFQ_ORDER_TYPEHASH)
// order.makerToken;
mstore(add(mem, 0x20), and(ADDRESS_MASK, mload(order)))
// order.takerToken;
mstore(add(mem, 0x40), and(ADDRESS_MASK, mload(add(order, 0x20))))
// order.makerAmount;
mstore(add(mem, 0x60), and(UINT_128_MASK, mload(add(order, 0x40))))
// order.takerAmount;
mstore(add(mem, 0x80), and(UINT_128_MASK, mload(add(order, 0x60))))
// order.maker;
mstore(add(mem, 0xA0), and(ADDRESS_MASK, mload(add(order, 0x80))))
// order.taker;
mstore(add(mem, 0xC0), and(ADDRESS_MASK, mload(add(order, 0xA0))))
// order.txOrigin;
mstore(add(mem, 0xE0), and(ADDRESS_MASK, mload(add(order, 0xC0))))
// order.pool;
mstore(add(mem, 0x100), mload(add(order, 0xE0)))
// order.expiry;
mstore(add(mem, 0x120), and(UINT_64_MASK, mload(add(order, 0x100))))
// order.salt;
mstore(add(mem, 0x140), mload(add(order, 0x120)))
structHash := keccak256(mem, 0x160)
}
}
/// @dev Refund any leftover protocol fees in `msg.value` to `msg.sender`.
/// @param ethProtocolFeePaid How much ETH was paid in protocol fees.
function refundExcessProtocolFeeToSender(uint256 ethProtocolFeePaid)
internal
{
if (msg.value > ethProtocolFeePaid && msg.sender != address(this)) {
uint256 refundAmount = msg.value.safeSub(ethProtocolFeePaid);
(bool success,) = msg
.sender
.call{value: refundAmount}("");
if (!success) {
LibNativeOrdersRichErrors.ProtocolFeeRefundFailed(
msg.sender,
refundAmount
).rrevert();
}
}
}
}