* 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
244 lines
8.2 KiB
Solidity
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();
|
|
}
|
|
}
|
|
}
|
|
}
|