Compare commits

...

26 Commits

Author SHA1 Message Date
Michael Zhu
7bf1b0750e Sanity check maker address in preSign 2022-01-19 21:31:25 -08:00
Michael Zhu
2005054ec8 Remove revertIfIncomplete from batchMatch 2022-01-19 21:31:25 -08:00
Lawrence Forman
cc70ad9d3c address feedback from original PR (#391)
* address feedback from original PR

* Update contracts/zero-ex/contracts/src/features/ERC721OrdersFeature.sol

Co-authored-by: Kim Persson <kimpers@users.noreply.github.com>

* address review feedback and improve order parameter naming

* Add batchCancel function

* Emit order fields in preSign

* Fix tests

Co-authored-by: Lawrence Forman <me@merklejerk.com>
Co-authored-by: Kim Persson <kimpers@users.noreply.github.com>
Co-authored-by: Michael Zhu <mchl.zhu.96@gmail.com>
2022-01-19 21:31:25 -08:00
Michael Zhu
db07fad051 tmp 2022-01-19 21:31:25 -08:00
Michael Zhu
91bfce9145 TestMintableERC721Token 2022-01-19 21:31:25 -08:00
Michael Zhu
5fc608ed2f create zero-ex/contracts/test/integration/ and tokens/ directories 2022-01-19 21:31:25 -08:00
Michael Zhu
9b626ee37a Add ERC721Orders TS class 2022-01-19 21:31:25 -08:00
Michael Zhu
4ae56d5876 Assembly optimizations in LibERC721Order 2022-01-19 21:31:25 -08:00
Michael Zhu
fd5d549c43 Add taker callback 2022-01-19 21:31:25 -08:00
Michael Zhu
180110de50 Add IERC721OrdersFeature to IZeroEx 2022-01-19 21:31:25 -08:00
Michael Zhu
d186cddc29 Cancel using just the order nonce 2022-01-19 21:31:25 -08:00
Michael Zhu
fbabd2f264 Add presign support for ERC721 orders 2022-01-19 21:31:25 -08:00
Michael Zhu
35f375a525 Add comments 2022-01-19 21:31:25 -08:00
Michael Zhu
29892db0fd add rich errors for ERC721OrdersFeature 2022-01-19 21:31:25 -08:00
Michael Zhu
2cbb107380 Use SafeMath where necessary 2022-01-19 21:31:25 -08:00
Michael Zhu
599d590fbc Implement matching functions 2022-01-19 21:31:25 -08:00
Michael Zhu
9651b41264 Add missing @return comments 2022-01-19 21:31:25 -08:00
Michael Zhu
b6f118ef32 left/right orders -> sell/buy orders 2022-01-19 21:31:25 -08:00
Michael Zhu
2cc11c87d1 Implement batchBuyERC721s 2022-01-19 21:31:25 -08:00
Michael Zhu
7fa2eb4c2a implement onERC721Received 2022-01-19 21:31:25 -08:00
Michael Zhu
35d839c651 Add isValidERC721OrderSignature to interface 2022-01-19 21:31:25 -08:00
Michael Zhu
a009779a88 Implement basic functionality for ERC721 orders (buy, sell, cancel, etc) 2022-01-19 21:31:25 -08:00
Michael Zhu
9aa7945bc4 Storage lib for ERC721 orders feature 2022-01-19 21:31:25 -08:00
Michael Zhu
e7d198ef16 Add ERC721OrdersFeature interface 2022-01-19 21:31:25 -08:00
Michael Zhu
4e7e6eb634 Add ERC721 interface to vendor/ 2022-01-19 21:31:25 -08:00
Michael Zhu
f745023625 add LibERC721Order.sol 2022-01-19 21:31:25 -08:00
47 changed files with 5119 additions and 49 deletions

View File

@@ -34,6 +34,7 @@ import "./features/interfaces/IBatchFillNativeOrdersFeature.sol";
import "./features/interfaces/IMultiplexFeature.sol";
import "./features/interfaces/IOtcOrdersFeature.sol";
import "./features/interfaces/IFundRecoveryFeature.sol";
import "./features/interfaces/IERC721OrdersFeature.sol";
/// @dev Interface for a fully featured Exchange Proxy.
@@ -50,7 +51,8 @@ interface IZeroEx is
IBatchFillNativeOrdersFeature,
IMultiplexFeature,
IOtcOrdersFeature,
IFundRecoveryFeature
IFundRecoveryFeature,
IERC721OrdersFeature
{
// solhint-disable state-visibility

View File

@@ -0,0 +1,200 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 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;
library LibERC721OrdersRichErrors {
// solhint-disable func-name-mixedcase
function OverspentEthError(
uint256 ethSpent,
uint256 msgValue
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
bytes4(keccak256("OverspentEthError(uint256,uint256)")),
ethSpent,
msgValue
);
}
function InsufficientEthError(
uint256 ethAvailable,
uint256 orderAmount
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
bytes4(keccak256("InsufficientEthError(uint256,uint256)")),
ethAvailable,
orderAmount
);
}
function ERC721TokenMismatchError(
address token1,
address token2
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
bytes4(keccak256("ERC721TokenMismatchError(address,address)")),
token1,
token2
);
}
function ERC20TokenMismatchError(
address token1,
address token2
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
bytes4(keccak256("ERC20TokenMismatchError(address,address)")),
token1,
token2
);
}
function NegativeSpreadError(
uint256 sellOrderAmount,
uint256 buyOrderAmount
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
bytes4(keccak256("NegativeSpreadError(uint256,uint256)")),
sellOrderAmount,
buyOrderAmount
);
}
function SellOrderFeesExceedSpreadError(
uint256 sellOrderFees,
uint256 spread
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
bytes4(keccak256("SellOrderFeesExceedSpreadError(uint256,uint256)")),
sellOrderFees,
spread
);
}
function OnlyTakerError(
address sender,
address taker
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
bytes4(keccak256("OnlyTakerError(address,address)")),
sender,
taker
);
}
function InvalidSignerError(
address maker,
address signer
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
bytes4(keccak256("InvalidSignerError(address,address)")),
maker,
signer
);
}
function OrderNotFillableError(
address maker,
uint256 nonce,
uint8 orderStatus
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
bytes4(keccak256("OrderNotFillableError(address,uint256,uint8)")),
maker,
nonce,
orderStatus
);
}
function ERC721TokenIdMismatchError(
uint256 tokenId,
uint256 orderTokenId
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
bytes4(keccak256("ERC721TokenIdMismatchError(uint256,uint256)")),
tokenId,
orderTokenId
);
}
function PropertyValidationFailedError(
address propertyValidator,
address erc721Token,
uint256 erc721TokenId,
bytes memory propertyData,
bytes memory errorData
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
bytes4(keccak256("PropertyValidationFailedError(address,address,uint256,bytes,bytes)")),
propertyValidator,
erc721Token,
erc721TokenId,
propertyData,
errorData
);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -311,7 +311,7 @@ contract OtcOrdersFeature is
// Unwrap WETH
WETH.withdraw(order.makerAmount);
// Transfer ETH to taker
_transferEth(taker, order.makerAmount);
_transferEth(payable(taker), order.makerAmount);
emit OtcOrderFilled(
orderInfo.orderHash,
@@ -622,16 +622,4 @@ contract OtcOrdersFeature is
[txOrigin]
[nonceBucket];
}
function _transferEth(address recipient, uint256 amount)
private
{
// Transfer ETH to recipient
(bool success, bytes memory revertData) =
recipient.call{value: amount}("");
// Revert on failure
if (!success) {
revertData.rrevert();
}
}
}

View File

@@ -0,0 +1,282 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 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 "../libs/LibERC721Order.sol";
import "../libs/LibSignature.sol";
import "../../vendor/IERC721Token.sol";
/// @dev Feature for interacting with ERC721 orders.
interface IERC721OrdersFeature {
/// @dev Emitted whenever an `ERC721Order` is filled.
/// @param direction Whether the order is selling or
/// buying the ERC721 token.
/// @param erc20Token The address of the ERC20 token.
/// @param erc20TokenAmount The amount of ERC20 token
/// to sell or buy.
/// @param erc721Token The address of the ERC721 token.
/// @param erc721TokenId The ID of the ERC721 asset.
/// @param maker The maker of the order.
/// @param taker The taker of the order.
/// @param nonce The unique maker nonce in the order.
/// @param matcher If this order was matched with another using `matchERC721Orders()`,
/// this will be the address of the caller. If not, this will be `address(0)`.
event ERC721OrderFilled(
LibERC721Order.TradeDirection direction,
IERC20TokenV06 erc20Token,
uint256 erc20TokenAmount,
IERC721Token erc721Token,
uint256 erc721TokenId,
address maker,
address taker,
uint256 nonce,
address matcher
);
/// @dev Emitted whenever an `ERC721Order` is cancelled.
/// @param maker The maker of the order.
/// @param nonce The nonce of the order that was cancelled.
event ERC721OrderCancelled(
address maker,
uint256 nonce
);
/// @dev Emitted when an `ERC721Order` is pre-signed.
/// Contains all the fields of the order.
event ERC721OrderPreSigned(
LibERC721Order.TradeDirection direction,
IERC20TokenV06 erc20Token,
uint256 erc20TokenAmount,
IERC721Token erc721Token,
uint256 erc721TokenId,
LibERC721Order.Property[] erc721TokenProperties,
LibERC721Order.Fee[] fees,
address maker,
address taker,
uint256 expiry,
uint256 nonce
);
/// @dev Sells an ERC721 asset to fill the given order.
/// @param buyOrder The ERC721 buy order.
/// @param signature The order signature from the maker.
/// @param erc721TokenId The ID of the ERC721 asset being
/// sold. If the given order specifies properties,
/// the asset must satisfy those properties. Otherwise,
/// it must equal the tokenId in the order.
/// @param unwrapNativeToken If this parameter is true and the
/// ERC20 token of the order is e.g. WETH, unwraps the
/// token before transferring it to the taker.
/// @param callbackData If this parameter is non-zero, invokes
/// `zeroExERC721OrderCallback` on `msg.sender` after
/// the ERC20 tokens have been transferred to `msg.sender`
/// but before transferring the ERC721 asset to the buyer.
function sellERC721(
LibERC721Order.ERC721Order calldata buyOrder,
LibSignature.Signature calldata signature,
uint256 erc721TokenId,
bool unwrapNativeToken,
bytes calldata callbackData
)
external;
/// @dev Buys an ERC721 asset by filling the given order.
/// @param sellOrder The ERC721 sell order.
/// @param signature The order signature.
/// @param callbackData If this parameter is non-zero, invokes
/// `zeroExERC721OrderCallback` on `msg.sender` after
/// the ERC721 asset has been transferred to `msg.sender`
/// but before transferring the ERC20 tokens to the seller.
/// Native tokens acquired during the callback can be used
/// to fill the order.
function buyERC721(
LibERC721Order.ERC721Order calldata sellOrder,
LibSignature.Signature calldata signature,
bytes calldata callbackData
)
external
payable;
/// @dev Cancel a single ERC721 order by its nonce. The caller
/// should be the maker of the order. Silently succeeds if
/// an order with the same nonce has already been filled or
/// cancelled.
/// @param orderNonce The order nonce.
function cancelERC721Order(uint256 orderNonce)
external;
/// @dev Cancel multiple ERC721 orders by their nonces. The caller
/// should be the maker of the orders. Silently succeeds if
/// an order with the same nonce has already been filled or
/// cancelled.
/// @param orderNonces The order nonces.
function batchCancelERC721Orders(uint256[] calldata orderNonces)
external;
/// @dev Buys multiple ERC721 assets by filling the
/// given orders.
/// @param sellOrders The ERC721 sell orders.
/// @param signatures The order signatures.
/// @param revertIfIncomplete If true, reverts if this
/// function fails to fill any individual order.
/// @return successes An array of booleans corresponding to whether
/// each order in `orders` was successfully filled.
function batchBuyERC721s(
LibERC721Order.ERC721Order[] calldata sellOrders,
LibSignature.Signature[] calldata signatures,
bool revertIfIncomplete
)
external
payable
returns (bool[] memory successes);
/// @dev Matches a pair of complementary orders that have
/// a non-negative spread. Each order is filled at
/// their respective price, and the matcher receives
/// a profit denominated in the ERC20 token.
/// @param sellOrder Order selling an ERC721 asset.
/// @param buyOrder Order buying an ERC721 asset.
/// @param sellOrderSignature Signature for the sell order.
/// @param buyOrderSignature Signature for the buy order.
/// @return profit The amount of profit earned by the caller
/// of this function (denominated in the ERC20 token
/// of the matched orders).
function matchERC721Orders(
LibERC721Order.ERC721Order calldata sellOrder,
LibERC721Order.ERC721Order calldata buyOrder,
LibSignature.Signature calldata sellOrderSignature,
LibSignature.Signature calldata buyOrderSignature
)
external
returns (uint256 profit);
/// @dev Matches pairs of complementary orders that have
/// non-negative spreads. Each order is filled at
/// their respective price, and the matcher receives
/// a profit denominated in the ERC20 token.
/// @param sellOrders Orders selling ERC721 assets.
/// @param buyOrders Orders buying ERC721 assets.
/// @param sellOrderSignatures Signatures for the sell orders.
/// @param buyOrderSignatures Signatures for the buy orders.
/// @return profits The amount of profit earned by the caller
/// of this function for each pair of matched orders
/// (denominated in the ERC20 token of the order pair).
/// @return successes An array of booleans corresponding to
/// whether each pair of orders was successfully matched.
function batchMatchERC721Orders(
LibERC721Order.ERC721Order[] calldata sellOrders,
LibERC721Order.ERC721Order[] calldata buyOrders,
LibSignature.Signature[] calldata sellOrderSignatures,
LibSignature.Signature[] calldata buyOrderSignatures
)
external
returns (uint256[] memory profits, bool[] memory successes);
/// @dev Callback for the ERC721 `safeTransferFrom` function.
/// This callback can be used to sell an ERC721 asset if
/// a valid ERC721 order, signature and `unwrapNativeToken`
/// are encoded in `data`. This allows takers to sell their
/// ERC721 asset without first calling `setApprovalForAll`.
/// @param operator The address which called `safeTransferFrom`.
/// @param from The address which previously owned the token.
/// @param tokenId The ID of the asset being transferred.
/// @param data Additional data with no specified format. If a
/// valid ERC721 order, signature and `unwrapNativeToken`
/// are encoded in `data`, this function will try to fill
/// the order using the received asset.
/// @return success The selector of this function (0x150b7a02),
/// indicating that the callback succeeded.
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
)
external
returns (bytes4 success);
/// @dev Approves an ERC721 order on-chain. After pre-signing
/// the order, the `PRESIGNED` signature type will become
/// valid for that order and signer.
/// @param order An ERC721 order.
function preSignERC721Order(LibERC721Order.ERC721Order calldata order)
external;
/// @dev Checks whether the given signature is valid for the
/// the given ERC721 order. Reverts if not.
/// @param order The ERC721 order.
/// @param signature The signature to validate.
function validateERC721OrderSignature(
LibERC721Order.ERC721Order calldata order,
LibSignature.Signature calldata signature
)
external
view;
/// @dev If the given order is buying an ERC721 asset, checks
/// whether or not the given token ID satisfies the required
/// properties specified in the order. If the order does not
/// specify any properties, this function instead checks
/// whether the given token ID matches the ID in the order.
/// Reverts if any checks fail, or if the order is selling
/// an ERC721 asset.
/// @param order The ERC721 order.
/// @param erc721TokenId The ID of the ERC721 asset.
function validateERC721OrderProperties(
LibERC721Order.ERC721Order calldata order,
uint256 erc721TokenId
)
external
view;
/// @dev Get the current status of an ERC721 order.
/// @param order The ERC721 order.
/// @return status The status of the order.
function getERC721OrderStatus(LibERC721Order.ERC721Order calldata order)
external
view
returns (LibERC721Order.OrderStatus status);
/// @dev Get the canonical hash of an ERC721 order.
/// @param order The ERC721 order.
/// @return orderHash The order hash.
function getERC721OrderHash(LibERC721Order.ERC721Order calldata order)
external
view
returns (bytes32 orderHash);
/// @dev Get the order status bit vector for the given
/// maker address and nonce range.
/// @param maker The maker of the order.
/// @param nonceRange Order status bit vectors are indexed
/// by maker address and the upper 248 bits of the
/// order nonce. We define `nonceRange` to be these
/// 248 bits.
/// @return bitVector The order status bit vector for the
/// given maker and nonce range.
function getERC721OrderStatusBitVector(address maker, uint248 nonceRange)
external
view
returns (uint256 bitVector);
}

View File

@@ -0,0 +1,267 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 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 "../../vendor/IERC721Token.sol";
import "../../vendor/IPropertyValidator.sol";
/// @dev A library for common ERC721 order operations.
library LibERC721Order {
using LibSafeMathV06 for uint256;
using LibRichErrorsV06 for bytes;
enum OrderStatus {
INVALID,
FILLABLE,
UNFILLABLE,
EXPIRED
}
enum TradeDirection {
SELL_721,
BUY_721
}
struct Property {
IPropertyValidator propertyValidator;
bytes propertyData;
}
struct Fee {
address recipient;
uint256 amount;
bytes feeData;
}
/// @dev An ERC721<>ERC20 limit order.
struct ERC721Order {
TradeDirection direction;
IERC20TokenV06 erc20Token;
uint256 erc20TokenAmount;
IERC721Token erc721Token;
uint256 erc721TokenId;
Property[] erc721TokenProperties;
Fee[] fees;
address maker;
address taker;
uint256 expiry;
uint256 nonce;
}
// The type hash for ERC721 orders, which is:
// keccak256(abi.encodePacked(
// "ERC721Order(",
// "uint8 direction,",
// "address erc20Token,",
// "uint256 erc20TokenAmount,",
// "address erc721Token,",
// "uint256 erc721TokenId,",
// "Property[] erc721TokenProperties,",
// "Fee[] fees,",
// "address maker,",
// "address taker,",
// "uint256 expiry,",
// "uint256 nonce",
// ")",
// "Fee(",
// "address recipient,",
// "uint256 amount,",
// "bytes feeData",
// ")",
// "Property(",
// "address propertyValidator,",
// "bytes propertyData",
// ")"
// ))
uint256 private constant _ERC_721_ORDER_TYPEHASH =
0x7af652c2504c5c7d806f4f25edc3762dd8478099f694981f8802db656a9ba9d8;
// keccak256(abi.encodePacked(
// "Fee(",
// "address recipient,",
// "uint256 amount,",
// "bytes feeData",
// ")"
// ))
uint256 private constant _FEE_TYPEHASH =
0xe68c29f1b4e8cce0bbcac76eb1334bdc1dc1f293a517c90e9e532340e1e94115;
// keccak256(abi.encodePacked(
// "Property(",
// "address propertyValidator,",
// "bytes propertyData",
// ")"
// ))
uint256 private constant _PROPERTY_TYPEHASH =
0x6292cf854241cb36887e639065eca63b3af9f7f70270cebeda4c29b6d3bc65e8;
// keccak256("");
bytes32 private constant _EMPTY_ARRAY_KECCAK256 =
0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
// keccak256(abi.encodePacked(keccak256(abi.encode(
// _PROPERTY_TYPEHASH,
// address(0),
// keccak256("")
// ))));
bytes32 private constant _NULL_PROPERTY_STRUCT_HASH =
0x720ee400a9024f6a49768142c339bf09d2dd9056ab52d20fbe7165faba6e142d;
uint256 private constant ADDRESS_MASK = (1 << 160) - 1;
/// @dev Get the struct hash of an ERC721 order.
/// @param order The ERC721 order.
/// @return structHash The struct hash of the order.
function getERC721OrderStructHash(ERC721Order memory order)
internal
pure
returns (bytes32 structHash)
{
// We give `order.erc721TokenProperties.length == 0` and
// `order.erc721TokenProperties.length == 1` special treatment
// because we expect these to be the most common.
bytes32 propertiesHash;
if (order.erc721TokenProperties.length == 0) {
propertiesHash = _EMPTY_ARRAY_KECCAK256;
} else if (order.erc721TokenProperties.length == 1) {
Property memory property = order
.erc721TokenProperties[0];
if (
address(property.propertyValidator) == address(0) &&
property.propertyData.length == 0
) {
propertiesHash = _NULL_PROPERTY_STRUCT_HASH;
} else {
// propertiesHash = keccak256(abi.encodePacked(keccak256(abi.encode(
// _PROPERTY_TYPEHASH,
// order.erc721TokenProperties[0].propertyValidator,
// keccak256(order.erc721TokenProperties[0].propertyData)
// ))));
bytes32 dataHash = keccak256(property.propertyData);
assembly {
// Load free memory pointer
let mem := mload(64)
mstore(mem, _PROPERTY_TYPEHASH)
// property.propertyValidator
mstore(add(mem, 32), and(ADDRESS_MASK, mload(property)))
// keccak256(property.propertyData)
mstore(add(mem, 64), dataHash)
mstore(mem, keccak256(mem, 96))
propertiesHash := keccak256(mem, 32)
}
}
} else {
bytes32[] memory propertyStructHashArray = new bytes32[](
order.erc721TokenProperties.length
);
for (uint256 i = 0; i < order.erc721TokenProperties.length; i++) {
propertyStructHashArray[i] = keccak256(abi.encode(
_PROPERTY_TYPEHASH,
order.erc721TokenProperties[i].propertyValidator,
keccak256(order.erc721TokenProperties[i].propertyData)
));
}
propertiesHash = keccak256(abi.encodePacked(propertyStructHashArray));
}
// We give `order.fees.length == 0` and
// `order.fees.length == 1` special treatment
// because we expect these to be the most common.
bytes32 feesHash;
if (order.fees.length == 0) {
feesHash = _EMPTY_ARRAY_KECCAK256;
} else if (order.fees.length == 1) {
// feesHash = keccak256(abi.encodePacked(keccak256(abi.encode(
// _FEE_TYPEHASH,
// order.fees[0].recipient,
// order.fees[0].amount,
// keccak256(order.fees[0].feeData)
// ))));
Fee memory fee = order.fees[0];
bytes32 dataHash = keccak256(fee.feeData);
assembly {
// Load free memory pointer
let mem := mload(64)
mstore(mem, _FEE_TYPEHASH)
// fee.recipient
mstore(add(mem, 32), and(ADDRESS_MASK, mload(fee)))
// fee.amount
mstore(add(mem, 64), mload(add(fee, 32)))
// keccak256(fee.feeData)
mstore(add(mem, 96), dataHash)
mstore(mem, keccak256(mem, 128))
feesHash := keccak256(mem, 32)
}
} else {
bytes32[] memory feeStructHashArray = new bytes32[](order.fees.length);
for (uint256 i = 0; i < order.fees.length; i++) {
feeStructHashArray[i] = keccak256(abi.encode(
_FEE_TYPEHASH,
order.fees[i].recipient,
order.fees[i].amount,
keccak256(order.fees[i].feeData)
));
}
feesHash = keccak256(abi.encodePacked(feeStructHashArray));
}
// Hash in place, equivalent to:
// return keccak256(abi.encode(
// _ERC_721_ORDER_TYPEHASH,
// order.direction,
// order.erc20Token,
// order.erc20TokenAmount,
// order.erc721Token,
// order.erc721TokenId,
// propertiesHash,
// feesHash,
// order.maker,
// order.taker,
// order.expiry,
// order.nonce
// ));
assembly {
if lt(order, 32) { invalid() } // Don't underflow memory.
let typeHashPos := sub(order, 32) // order + (32 * -1)
let propertiesHashPos := add(order, 160) // order + (32 * 5)
let feesHashPos := add(order, 192) // order + (32 * 6)
let temp1 := mload(typeHashPos)
let temp2 := mload(propertiesHashPos)
let temp3 := mload(feesHashPos)
mstore(typeHashPos, _ERC_721_ORDER_TYPEHASH)
mstore(propertiesHashPos, propertiesHash)
mstore(feesHashPos, feesHash)
structHash := keccak256(typeHashPos, 384 /* 32 * 12 */ )
mstore(typeHashPos, temp1)
mstore(propertiesHashPos, temp2)
mstore(feesHashPos, temp3)
}
return structHash;
}
}

View File

@@ -44,7 +44,8 @@ library LibSignature {
ILLEGAL,
INVALID,
EIP712,
ETHSIGN
ETHSIGN,
PRESIGNED
}
/// @dev Encoded EC signature.
@@ -146,6 +147,15 @@ library LibSignature {
).rrevert();
}
// If a feature supports pre-signing, it wouldn't use
// `getSignerOfHash` on a pre-signed order.
if (signature.signatureType == SignatureType.PRESIGNED) {
LibSignatureRichErrors.SignatureValidationError(
LibSignatureRichErrors.SignatureValidationErrorCodes.UNSUPPORTED,
hash
).rrevert();
}
// Solidity should check that the signature type is within enum range for us
// when abi-decoding.
}

View File

@@ -622,15 +622,6 @@ contract MultiplexFeature is
_executeBatchSell(batchSellParams).boughtAmount;
}
// Transfers some amount of ETH to the given recipient and
// reverts if the transfer fails.
function _transferEth(address payable recipient, uint256 amount)
private
{
(bool success,) = recipient.call{value: amount}("");
require(success, "MultiplexFeature::_transferEth/TRANSFER_FAILED");
}
// This function computes the "target" address of hop index `i` within
// a multi-hop sell.
// If `i == 0`, the target is the address which should hold the input

View File

@@ -0,0 +1,74 @@
// 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-utils/contracts/src/v06/LibSafeMathV06.sol";
import "../vendor/IERC721Token.sol";
/// @dev Helpers for moving ERC721 assets around.
abstract contract FixinERC721Spender {
// Mask of the lower 20 bytes of a bytes32.
uint256 constant private ADDRESS_MASK = 0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff;
/// @dev Transfers an ERC721 asset from `owner` to `to`.
/// @param token The address of the ERC721 token contract.
/// @param owner The owner of the asset.
/// @param to The recipient of the asset.
/// @param tokenId The token ID of the asset to transfer.
function _transferERC721AssetFrom(
IERC721Token token,
address owner,
address to,
uint256 tokenId
)
internal
{
require(address(token) != address(this), "FixinERC721Spender/CANNOT_INVOKE_SELF");
assembly {
let ptr := mload(0x40) // free memory pointer
// selector for transferFrom(address,address,uint256)
mstore(ptr, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 0x04), and(owner, ADDRESS_MASK))
mstore(add(ptr, 0x24), and(to, ADDRESS_MASK))
mstore(add(ptr, 0x44), tokenId)
let success := call(
gas(),
and(token, ADDRESS_MASK),
0,
ptr,
0x64,
0,
0
)
if iszero(success) {
let rdsize := returndatasize()
returndatacopy(ptr, 0, rdsize)
revert(ptr, rdsize)
}
}
}
}

View File

@@ -20,7 +20,7 @@
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
@@ -141,6 +141,20 @@ abstract contract FixinTokenSpender {
}
}
/// @dev Transfers some amount of ETH to the given recipient and
/// reverts if the transfer fails.
/// @param recipient The recipient of the ETH.
/// @param amount The amount of ETH to transfer.
function _transferEth(address payable recipient, uint256 amount)
internal
{
if (amount > 0) {
(bool success,) = recipient.call{value: amount}("");
require(success, "FixinTokenSpender::_transferEth/TRANSFER_FAILED");
}
}
/// @dev Gets the maximum amount of an ERC20 token `token` that can be
/// pulled from `owner` by this address.
/// @param token The token to spend.

View File

@@ -0,0 +1,47 @@
// 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 "./LibStorage.sol";
/// @dev Storage helpers for `ERC721OrdersFeature`.
library LibERC721OrdersStorage {
/// @dev Storage bucket for this feature.
struct Storage {
// maker => nonce range => order status bit vector
mapping(address => mapping(uint248 => uint256)) orderStatusByMaker;
// order hash => maker => isSigned
mapping(bytes32 => mapping(address => bool)) preSigned;
}
/// @dev Get the storage bucket for this contract.
function getStorage() internal pure returns (Storage storage stor) {
uint256 storageSlot = LibStorage.getStorageSlot(
LibStorage.StorageId.ERC721Orders
);
// Dip into assembly to change the slot pointed to by the local
// variable `stor`.
// See https://solidity.readthedocs.io/en/v0.6.8/assembly.html?highlight=slot#access-to-external-variables-functions-and-libraries
assembly { stor_slot := storageSlot }
}
}

View File

@@ -39,7 +39,8 @@ library LibStorage {
MetaTransactions,
ReentrancyGuard,
NativeOrders,
OtcOrders
OtcOrders,
ERC721Orders
}
/// @dev Get the storage slot given a storage ID. We assign unique, well-spaced

View File

@@ -0,0 +1,34 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 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;
interface IERC721OrderCallback {
/// @dev A taker callback function invoked in the ERC721Feature between
/// the maker -> taker transfer and the taker -> maker transfer.
/// @param callbackData Arbitrary data used by this callback.
/// @return success The selector of this function (0x6d46db51),
/// indicating that the callback succeeded.
function zeroExERC721OrderCallback(bytes calldata callbackData)
external
returns (bytes4 success);
}

View File

@@ -0,0 +1,159 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 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;
interface IERC721Token {
/// @dev This emits when ownership of any NFT changes by any mechanism.
/// This event emits when NFTs are created (`from` == 0) and destroyed
/// (`to` == 0). Exception: during contract creation, any number of NFTs
/// may be created and assigned without emitting Transfer. At the time of
/// any transfer, the approved address for that NFT (if any) is reset to none.
event Transfer(
address indexed _from,
address indexed _to,
uint256 indexed _tokenId
);
/// @dev This emits when the approved address for an NFT is changed or
/// reaffirmed. The zero address indicates there is no approved address.
/// When a Transfer event emits, this also indicates that the approved
/// address for that NFT (if any) is reset to none.
event Approval(
address indexed _owner,
address indexed _approved,
uint256 indexed _tokenId
);
/// @dev This emits when an operator is enabled or disabled for an owner.
/// The operator can manage all NFTs of the owner.
event ApprovalForAll(
address indexed _owner,
address indexed _operator,
bool _approved
);
/// @notice Transfers the ownership of an NFT from one address to another address
/// @dev Throws unless `msg.sender` is the current owner, an authorized
/// perator, or the approved address for this NFT. Throws if `_from` is
/// not the current owner. Throws if `_to` is the zero address. Throws if
/// `_tokenId` is not a valid NFT. When transfer is complete, this function
/// checks if `_to` is a smart contract (code size > 0). If so, it calls
/// `onERC721Received` on `_to` and throws if the return value is not
/// `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
/// @param _data Additional data with no specified format, sent in call to `_to`
function safeTransferFrom(
address _from,
address _to,
uint256 _tokenId,
bytes calldata _data
)
external;
/// @notice Transfers the ownership of an NFT from one address to another address
/// @dev This works identically to the other function with an extra data parameter,
/// except this function just sets data to "".
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
function safeTransferFrom(
address _from,
address _to,
uint256 _tokenId
)
external;
/// @notice Change or reaffirm the approved address for an NFT
/// @dev The zero address indicates there is no approved address.
/// Throws unless `msg.sender` is the current NFT owner, or an authorized
/// operator of the current owner.
/// @param _approved The new approved NFT controller
/// @param _tokenId The NFT to approve
function approve(address _approved, uint256 _tokenId)
external;
/// @notice Enable or disable approval for a third party ("operator") to manage
/// all of `msg.sender`'s assets
/// @dev Emits the ApprovalForAll event. The contract MUST allow
/// multiple operators per owner.
/// @param _operator Address to add to the set of authorized operators
/// @param _approved True if the operator is approved, false to revoke approval
function setApprovalForAll(address _operator, bool _approved)
external;
/// @notice Count all NFTs assigned to an owner
/// @dev NFTs assigned to the zero address are considered invalid, and this
/// function throws for queries about the zero address.
/// @param _owner An address for whom to query the balance
/// @return The number of NFTs owned by `_owner`, possibly zero
function balanceOf(address _owner)
external
view
returns (uint256);
/// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
/// TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
/// THEY MAY BE PERMANENTLY LOST
/// @dev Throws unless `msg.sender` is the current owner, an authorized
/// operator, or the approved address for this NFT. Throws if `_from` is
/// not the current owner. Throws if `_to` is the zero address. Throws if
/// `_tokenId` is not a valid NFT.
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
function transferFrom(
address _from,
address _to,
uint256 _tokenId
)
external;
/// @notice Find the owner of an NFT
/// @dev NFTs assigned to zero address are considered invalid, and queries
/// about them do throw.
/// @param _tokenId The identifier for an NFT
/// @return The address of the owner of the NFT
function ownerOf(uint256 _tokenId)
external
view
returns (address);
/// @notice Get the approved address for a single NFT
/// @dev Throws if `_tokenId` is not a valid NFT.
/// @param _tokenId The NFT to find the approved address for
/// @return The approved address for this NFT, or the zero address if there is none
function getApproved(uint256 _tokenId)
external
view
returns (address);
/// @notice Query if an address is an authorized operator for another address
/// @param _owner The address that owns the NFTs
/// @param _operator The address that acts on behalf of the owner
/// @return True if `_operator` is an approved operator for `_owner`, false otherwise
function isApprovedForAll(address _owner, address _operator)
external
view
returns (bool);
}

View File

@@ -0,0 +1,44 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 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;
interface IFeeRecipient {
/// @dev A callback function invoked in the ERC721Feature for each ERC721
/// order fee that get paid. Integrators can make use of this callback
/// to implement arbitrary fee-handling logic, e.g. splitting the fee
/// between multiple parties.
/// @param tokenAddress The address of the token in which the received fee is
/// denominated. `0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE` indicates
/// that the fee was paid in the native token (e.g. ETH).
/// @param amount The amount of the given token received.
/// @param feeData Arbitrary data encoded in the `Fee` used by this callback.
/// @return success The selector of this function (0x0190805e),
/// indicating that the callback succeeded.
function receiveZeroExFeeCallback(
address tokenAddress,
uint256 amount,
bytes calldata feeData
)
external
returns (bytes4 success);
}

View File

@@ -0,0 +1,38 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 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;
interface IPropertyValidator {
/// @dev Checks that the given ERC721/ERC1155 asset satisfies the properties encoded in `propertyData`.
/// Should revert if the asset does not satisfy the specified properties.
/// @param tokenAddress The ERC721/ERC1155 token contract address.
/// @param tokenId The ERC721/ERC1155 tokenId of the asset to check.
/// @param propertyData Encoded properties or auxiliary data needed to perform the check.
function validateProperty(
address tokenAddress,
uint256 tokenId,
bytes calldata propertyData
)
external
view;
}

View File

@@ -0,0 +1,61 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 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 "../src/IZeroEx.sol";
import "../src/vendor/IERC721Token.sol";
import "../src/features/libs/LibERC721Order.sol";
contract TestERC721OrderPresigner {
IZeroEx private immutable zeroEx;
constructor(IZeroEx _zeroEx)
public
{
zeroEx = _zeroEx;
}
function approveERC721(IERC721Token token)
external
{
token.setApprovalForAll(address(zeroEx), true);
}
function approveERC20(IERC20TokenV06 token)
external
{
token.approve(address(zeroEx), uint256(-1));
}
function preSignOrder(LibERC721Order.ERC721Order calldata order)
external
{
zeroEx.preSignERC721Order(order);
}
function cancelOrder(uint256 orderNonce)
external
{
zeroEx.cancelERC721Order(orderNonce);
}
}

View File

@@ -0,0 +1,55 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 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;
contract TestFeeRecipient {
bytes4 constant private SUCCESS = this.receiveZeroExFeeCallback.selector;
bytes4 constant private FAILURE = 0xdeadbeef;
uint256 constant private TRIGGER_REVERT = 333;
uint256 constant private TRIGGER_FAILURE = 666;
event FeeReceived(
address tokenAddress,
uint256 amount
);
receive() external payable {}
function receiveZeroExFeeCallback(
address tokenAddress,
uint256 amount,
bytes calldata /* feeData */
)
external
returns (bytes4 success)
{
emit FeeReceived(tokenAddress, amount);
if (amount == TRIGGER_REVERT) {
revert("TestFeeRecipient::receiveZeroExFeeCallback/REVERT");
} else if (amount == TRIGGER_FAILURE) {
return FAILURE;
} else {
return SUCCESS;
}
}
}

View File

@@ -22,7 +22,7 @@ pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/v06/LibMathV06.sol";
import "../src/vendor/v3/IERC20Bridge.sol";
import "./TestMintableERC20Token.sol";
import "./tokens/TestMintableERC20Token.sol";
contract TestFillQuoteTransformerBridge {

View File

@@ -23,7 +23,7 @@ pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibMathV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
import "./TestMintableERC20Token.sol";
import "./tokens/TestMintableERC20Token.sol";
import "../src/features/libs/LibNativeOrder.sol";
import "../src/features/libs/LibSignature.sol";

View File

@@ -21,7 +21,7 @@ pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "../src/transformers/IERC20Transformer.sol";
import "./TestMintableERC20Token.sol";
import "./tokens/TestMintableERC20Token.sol";
import "./TestTransformerHost.sol";

View File

@@ -23,7 +23,7 @@ pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "../src/transformers/IERC20Transformer.sol";
import "../src/transformers/LibERC20Transformer.sol";
import "./TestMintableERC20Token.sol";
import "./tokens/TestMintableERC20Token.sol";
contract TestMintTokenERC20Transformer is

View File

@@ -0,0 +1,39 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 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;
contract TestPropertyValidator {
function validateProperty(
address tokenAddress,
uint256 tokenId,
bytes calldata propertyData
)
external
view
{
require(
propertyData.length > 0,
"TestPropertyValidator::validateProperty/REVERT"
);
}
}

View File

@@ -21,9 +21,9 @@ pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "../src/transformers/IERC20Transformer.sol";
import "./TestMintableERC20Token.sol";
import "./tokens/TestMintableERC20Token.sol";
import "./TestTransformerHost.sol";
import "./TestWeth.sol";
import "./tokens/TestWeth.sol";
contract TestWethTransformerHost is

View File

@@ -21,7 +21,7 @@ pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "./TestMintableERC20Token.sol";
import "../tokens/TestMintableERC20Token.sol";
contract TestCurve {

View File

@@ -21,7 +21,7 @@ pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "./TestMintableERC20Token.sol";
import "../tokens/TestMintableERC20Token.sol";
contract TestMooniswap {

View File

@@ -2,7 +2,7 @@
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "../src/vendor/IUniswapV2Pair.sol";
import "../../src/vendor/IUniswapV2Pair.sol";
interface IUniswapV2PoolDeployer {
struct CreationParameters {

View File

@@ -2,7 +2,7 @@
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "../src/vendor/IUniswapV3Pool.sol";
import "../../src/vendor/IUniswapV3Pool.sol";
interface IUniswapV3PoolDeployer {
struct CreationParameters {

View File

@@ -0,0 +1,385 @@
// 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-utils/contracts/src/v06/LibSafeMathV06.sol";
interface IERC721Receiver {
/// @notice Handle the receipt of an NFT
/// @dev The ERC721 smart contract calls this function on the recipient
/// after a `transfer`. This function MAY throw to revert and reject the
/// transfer. Return of other than the magic value MUST result in the
/// transaction being reverted.
/// Note: the contract address is always the message sender.
/// @param _operator The address which called `safeTransferFrom` function
/// @param _from The address which previously owned the token
/// @param _tokenId The NFT identifier which is being transferred
/// @param _data Additional data with no specified format
/// @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
/// unless throwing
function onERC721Received(
address _operator,
address _from,
uint256 _tokenId,
bytes calldata _data
)
external
returns (bytes4);
}
contract TestMintableERC721Token {
using LibSafeMathV06 for uint256;
/// @dev This emits when ownership of any NFT changes by any mechanism.
/// This event emits when NFTs are created (`from` == 0) and destroyed
/// (`to` == 0). Exception: during contract creation, any number of NFTs
/// may be created and assigned without emitting Transfer. At the time of
/// any transfer, the approved address for that NFT (if any) is reset to none.
event Transfer(
address _from,
address _to,
uint256 _tokenId
);
/// @dev This emits when the approved address for an NFT is changed or
/// reaffirmed. The zero address indicates there is no approved address.
/// When a Transfer event emits, this also indicates that the approved
/// address for that NFT (if any) is reset to none.
event Approval(
address indexed _owner,
address indexed _approved,
uint256 indexed _tokenId
);
/// @dev This emits when an operator is enabled or disabled for an owner.
/// The operator can manage all NFTs of the owner.
event ApprovalForAll(
address indexed _owner,
address indexed _operator,
bool _approved
);
// Function selector for ERC721Receiver.onERC721Received
// 0x150b7a02
bytes4 constant private ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));
// Mapping of tokenId => owner
mapping (uint256 => address) private owners;
// Mapping of tokenId => approved address
mapping (uint256 => address) private approvals;
// Mapping of owner => number of tokens owned
mapping (address => uint256) private balances;
// Mapping of owner => operator => approved
mapping (address => mapping (address => bool)) private operatorApprovals;
/// @dev Function to mint a new token
/// Reverts if the given token ID already exists
/// @param _to Address of the beneficiary that will own the minted token
/// @param _tokenId ID of the token to be minted by the msg.sender
function mint(address _to, uint256 _tokenId)
external
{
require(
_to != address(0),
"ERC721_ZERO_TO_ADDRESS"
);
address owner = owners[_tokenId];
require(
owner == address(0),
"ERC721_OWNER_ALREADY_EXISTS"
);
owners[_tokenId] = _to;
balances[_to] = balances[_to].safeAdd(1);
emit Transfer(
address(0),
_to,
_tokenId
);
}
/// @dev Function to burn a token
/// Reverts if the given token ID doesn't exist
/// @param _owner Owner of token with given token ID
/// @param _tokenId ID of the token to be burned by the msg.sender
function burn(address _owner, uint256 _tokenId)
external
{
require(
_owner != address(0),
"ERC721_ZERO_OWNER_ADDRESS"
);
address owner = owners[_tokenId];
require(
owner == _owner,
"ERC721_OWNER_MISMATCH"
);
owners[_tokenId] = address(0);
balances[_owner] = balances[_owner].safeSub(1);
emit Transfer(
_owner,
address(0),
_tokenId
);
}
/// @notice Transfers the ownership of an NFT from one address to another address
/// @dev Throws unless `msg.sender` is the current owner, an authorized
/// operator, or the approved address for this NFT. Throws if `_from` is
/// not the current owner. Throws if `_to` is the zero address. Throws if
/// `_tokenId` is not a valid NFT. When transfer is complete, this function
/// checks if `_to` is a smart contract (code size > 0). If so, it calls
/// `onERC721Received` on `_to` and throws if the return value is not
/// `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
/// @param _data Additional data with no specified format, sent in call to `_to`
function safeTransferFrom(
address _from,
address _to,
uint256 _tokenId,
bytes calldata _data
)
external
{
transferFrom(
_from,
_to,
_tokenId
);
uint256 receiverCodeSize;
assembly {
receiverCodeSize := extcodesize(_to)
}
if (receiverCodeSize > 0) {
bytes4 selector = IERC721Receiver(_to).onERC721Received(
msg.sender,
_from,
_tokenId,
_data
);
require(
selector == ERC721_RECEIVED,
"ERC721_INVALID_SELECTOR"
);
}
}
/// @notice Transfers the ownership of an NFT from one address to another address
/// @dev This works identically to the other function with an extra data parameter,
/// except this function just sets data to "".
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
function safeTransferFrom(
address _from,
address _to,
uint256 _tokenId
)
external
{
transferFrom(
_from,
_to,
_tokenId
);
uint256 receiverCodeSize;
assembly {
receiverCodeSize := extcodesize(_to)
}
if (receiverCodeSize > 0) {
bytes4 selector = IERC721Receiver(_to).onERC721Received(
msg.sender,
_from,
_tokenId,
""
);
require(
selector == ERC721_RECEIVED,
"ERC721_INVALID_SELECTOR"
);
}
}
/// @notice Change or reaffirm the approved address for an NFT
/// @dev The zero address indicates there is no approved address.
/// Throws unless `msg.sender` is the current NFT owner, or an authorized
/// operator of the current owner.
/// @param _approved The new approved NFT controller
/// @param _tokenId The NFT to approve
function approve(address _approved, uint256 _tokenId)
external
{
address owner = ownerOf(_tokenId);
require(
msg.sender == owner || isApprovedForAll(owner, msg.sender),
"ERC721_INVALID_SENDER"
);
approvals[_tokenId] = _approved;
emit Approval(
owner,
_approved,
_tokenId
);
}
/// @notice Enable or disable approval for a third party ("operator") to manage
/// all of `msg.sender`'s assets
/// @dev Emits the ApprovalForAll event. The contract MUST allow
/// multiple operators per owner.
/// @param _operator Address to add to the set of authorized operators
/// @param _approved True if the operator is approved, false to revoke approval
function setApprovalForAll(address _operator, bool _approved)
external
{
operatorApprovals[msg.sender][_operator] = _approved;
emit ApprovalForAll(
msg.sender,
_operator,
_approved
);
}
/// @notice Count all NFTs assigned to an owner
/// @dev NFTs assigned to the zero address are considered invalid, and this
/// function throws for queries about the zero address.
/// @param _owner An address for whom to query the balance
/// @return The number of NFTs owned by `_owner`, possibly zero
function balanceOf(address _owner)
external
view
returns (uint256)
{
require(
_owner != address(0),
"ERC721_ZERO_OWNER"
);
return balances[_owner];
}
/// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
/// TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
/// THEY MAY BE PERMANENTLY LOST
/// @dev Throws unless `msg.sender` is the current owner, an authorized
/// operator, or the approved address for this NFT. Throws if `_from` is
/// not the current owner. Throws if `_to` is the zero address. Throws if
/// `_tokenId` is not a valid NFT.
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
function transferFrom(
address _from,
address _to,
uint256 _tokenId
)
public
{
require(
_to != address(0),
"ERC721_ZERO_TO_ADDRESS"
);
address owner = ownerOf(_tokenId);
require(
_from == owner,
"ERC721_OWNER_MISMATCH"
);
address spender = msg.sender;
address approvedAddress = getApproved(_tokenId);
require(
spender == owner ||
isApprovedForAll(owner, spender) ||
approvedAddress == spender,
"ERC721_INVALID_SPENDER"
);
if (approvedAddress != address(0)) {
approvals[_tokenId] = address(0);
}
owners[_tokenId] = _to;
balances[_from] = balances[_from].safeSub(1);
balances[_to] = balances[_to].safeAdd(1);
emit Transfer(
_from,
_to,
_tokenId
);
}
/// @notice Find the owner of an NFT
/// @dev NFTs assigned to zero address are considered invalid, and queries
/// about them do throw.
/// @param _tokenId The identifier for an NFT
/// @return The address of the owner of the NFT
function ownerOf(uint256 _tokenId)
public
view
returns (address)
{
address owner = owners[_tokenId];
require(
owner != address(0),
"ERC721_ZERO_OWNER"
);
return owner;
}
/// @notice Get the approved address for a single NFT
/// @dev Throws if `_tokenId` is not a valid NFT.
/// @param _tokenId The NFT to find the approved address for
/// @return The approved address for this NFT, or the zero address if there is none
function getApproved(uint256 _tokenId)
public
view
returns (address)
{
return approvals[_tokenId];
}
/// @notice Query if an address is an authorized operator for another address
/// @param _owner The address that owns the NFTs
/// @param _operator The address that acts on behalf of the owner
/// @return True if `_operator` is an approved operator for `_owner`, false otherwise
function isApprovedForAll(address _owner, address _operator)
public
view
returns (bool)
{
return operatorApprovals[_owner][_operator];
}
}

View File

@@ -43,7 +43,7 @@
"config": {
"publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,PositiveSlippageFeeTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,AffiliateFeeTransformer,MetaTransactionsFeature,LogMetadataTransformer,BridgeAdapter,LiquidityProviderFeature,ILiquidityProviderFeature,NativeOrdersFeature,INativeOrdersFeature,FeeCollectorController,FeeCollector,CurveLiquidityProvider,BatchFillNativeOrdersFeature,IBatchFillNativeOrdersFeature,MultiplexFeature,IMultiplexFeature,OtcOrdersFeature,IOtcOrdersFeature",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeAdapter|BridgeProtocols|CurveLiquidityProvider|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|FundRecoveryFeature|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IFeature|IFlashWallet|IFundRecoveryFeature|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOtcOrdersFeature|IOwnableFeature|IPancakeSwapFeature|ISimpleFunctionRegistryFeature|IStaking|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IUniswapV3Feature|IUniswapV3Pool|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOtcOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinAaveV2|MixinBalancer|MixinBalancerV2|MixinBancor|MixinCoFiX|MixinCompound|MixinCryptoCom|MixinCurve|MixinCurveV2|MixinDodo|MixinDodoV2|MixinKyber|MixinKyberDmm|MixinLido|MixinMStable|MixinMakerPSM|MixinMooniswap|MixinNerve|MixinOasis|MixinShell|MixinUniswap|MixinUniswapV2|MixinUniswapV3|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|MultiplexLiquidityProvider|MultiplexOtc|MultiplexRfq|MultiplexTransformERC20|MultiplexUniswapV2|MultiplexUniswapV3|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OtcOrdersFeature|OwnableFeature|PancakeSwapFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestMooniswap|TestNativeOrdersFeature|TestNoEthRecipient|TestOrderSignerRegistryWithContractWallet|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestUniswapV2Factory|TestUniswapV2Pool|TestUniswapV3Factory|TestUniswapV3Feature|TestUniswapV3Pool|TestWeth|TestWethTransformerHost|TestZeroExFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|UniswapV3Feature|WethTransformer|ZeroEx|ZeroExOptimized).json"
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeAdapter|BridgeProtocols|CurveLiquidityProvider|ERC721OrdersFeature|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinERC721Spender|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|FundRecoveryFeature|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IERC721OrderCallback|IERC721OrdersFeature|IERC721Token|IFeature|IFeeRecipient|IFlashWallet|IFundRecoveryFeature|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOtcOrdersFeature|IOwnableFeature|IPancakeSwapFeature|IPropertyValidator|ISimpleFunctionRegistryFeature|IStaking|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IUniswapV3Feature|IUniswapV3Pool|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibERC721Order|LibERC721OrdersRichErrors|LibERC721OrdersStorage|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOtcOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinAaveV2|MixinBalancer|MixinBalancerV2|MixinBancor|MixinCoFiX|MixinCompound|MixinCryptoCom|MixinCurve|MixinCurveV2|MixinDodo|MixinDodoV2|MixinKyber|MixinKyberDmm|MixinLido|MixinMStable|MixinMakerPSM|MixinMooniswap|MixinNerve|MixinOasis|MixinShell|MixinUniswap|MixinUniswapV2|MixinUniswapV3|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|MultiplexLiquidityProvider|MultiplexOtc|MultiplexRfq|MultiplexTransformERC20|MultiplexUniswapV2|MultiplexUniswapV3|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OtcOrdersFeature|OwnableFeature|PancakeSwapFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestCurve|TestDelegateCaller|TestERC721OrderPresigner|TestFeeCollectorController|TestFeeRecipient|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestMintableERC721Token|TestMooniswap|TestNativeOrdersFeature|TestNoEthRecipient|TestOrderSignerRegistryWithContractWallet|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestPropertyValidator|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestUniswapV2Factory|TestUniswapV2Pool|TestUniswapV3Factory|TestUniswapV3Feature|TestUniswapV3Pool|TestWeth|TestWethTransformerHost|TestZeroExFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|UniswapV3Feature|WethTransformer|ZeroEx|ZeroExOptimized).json"
},
"repository": {
"type": "git",

View File

@@ -11,11 +11,13 @@ import * as BootstrapFeature from '../test/generated-artifacts/BootstrapFeature.
import * as BridgeAdapter from '../test/generated-artifacts/BridgeAdapter.json';
import * as BridgeProtocols from '../test/generated-artifacts/BridgeProtocols.json';
import * as CurveLiquidityProvider from '../test/generated-artifacts/CurveLiquidityProvider.json';
import * as ERC721OrdersFeature from '../test/generated-artifacts/ERC721OrdersFeature.json';
import * as FeeCollector from '../test/generated-artifacts/FeeCollector.json';
import * as FeeCollectorController from '../test/generated-artifacts/FeeCollectorController.json';
import * as FillQuoteTransformer from '../test/generated-artifacts/FillQuoteTransformer.json';
import * as FixinCommon from '../test/generated-artifacts/FixinCommon.json';
import * as FixinEIP712 from '../test/generated-artifacts/FixinEIP712.json';
import * as FixinERC721Spender from '../test/generated-artifacts/FixinERC721Spender.json';
import * as FixinProtocolFees from '../test/generated-artifacts/FixinProtocolFees.json';
import * as FixinReentrancyGuard from '../test/generated-artifacts/FixinReentrancyGuard.json';
import * as FixinTokenSpender from '../test/generated-artifacts/FixinTokenSpender.json';
@@ -27,7 +29,11 @@ import * as IBootstrapFeature from '../test/generated-artifacts/IBootstrapFeatur
import * as IBridgeAdapter from '../test/generated-artifacts/IBridgeAdapter.json';
import * as IERC20Bridge from '../test/generated-artifacts/IERC20Bridge.json';
import * as IERC20Transformer from '../test/generated-artifacts/IERC20Transformer.json';
import * as IERC721OrderCallback from '../test/generated-artifacts/IERC721OrderCallback.json';
import * as IERC721OrdersFeature from '../test/generated-artifacts/IERC721OrdersFeature.json';
import * as IERC721Token from '../test/generated-artifacts/IERC721Token.json';
import * as IFeature from '../test/generated-artifacts/IFeature.json';
import * as IFeeRecipient from '../test/generated-artifacts/IFeeRecipient.json';
import * as IFlashWallet from '../test/generated-artifacts/IFlashWallet.json';
import * as IFundRecoveryFeature from '../test/generated-artifacts/IFundRecoveryFeature.json';
import * as ILiquidityProvider from '../test/generated-artifacts/ILiquidityProvider.json';
@@ -42,6 +48,7 @@ import * as InitialMigration from '../test/generated-artifacts/InitialMigration.
import * as IOtcOrdersFeature from '../test/generated-artifacts/IOtcOrdersFeature.json';
import * as IOwnableFeature from '../test/generated-artifacts/IOwnableFeature.json';
import * as IPancakeSwapFeature from '../test/generated-artifacts/IPancakeSwapFeature.json';
import * as IPropertyValidator from '../test/generated-artifacts/IPropertyValidator.json';
import * as ISimpleFunctionRegistryFeature from '../test/generated-artifacts/ISimpleFunctionRegistryFeature.json';
import * as IStaking from '../test/generated-artifacts/IStaking.json';
import * as ITestSimpleFunctionRegistryFeature from '../test/generated-artifacts/ITestSimpleFunctionRegistryFeature.json';
@@ -55,6 +62,9 @@ import * as IZeroEx from '../test/generated-artifacts/IZeroEx.json';
import * as LibBootstrap from '../test/generated-artifacts/LibBootstrap.json';
import * as LibCommonRichErrors from '../test/generated-artifacts/LibCommonRichErrors.json';
import * as LibERC20Transformer from '../test/generated-artifacts/LibERC20Transformer.json';
import * as LibERC721Order from '../test/generated-artifacts/LibERC721Order.json';
import * as LibERC721OrdersRichErrors from '../test/generated-artifacts/LibERC721OrdersRichErrors.json';
import * as LibERC721OrdersStorage from '../test/generated-artifacts/LibERC721OrdersStorage.json';
import * as LibFeeCollector from '../test/generated-artifacts/LibFeeCollector.json';
import * as LibLiquidityProviderRichErrors from '../test/generated-artifacts/LibLiquidityProviderRichErrors.json';
import * as LibMetaTransactionsRichErrors from '../test/generated-artifacts/LibMetaTransactionsRichErrors.json';
@@ -129,7 +139,9 @@ import * as TestBridge from '../test/generated-artifacts/TestBridge.json';
import * as TestCallTarget from '../test/generated-artifacts/TestCallTarget.json';
import * as TestCurve from '../test/generated-artifacts/TestCurve.json';
import * as TestDelegateCaller from '../test/generated-artifacts/TestDelegateCaller.json';
import * as TestERC721OrderPresigner from '../test/generated-artifacts/TestERC721OrderPresigner.json';
import * as TestFeeCollectorController from '../test/generated-artifacts/TestFeeCollectorController.json';
import * as TestFeeRecipient from '../test/generated-artifacts/TestFeeRecipient.json';
import * as TestFillQuoteTransformerBridge from '../test/generated-artifacts/TestFillQuoteTransformerBridge.json';
import * as TestFillQuoteTransformerExchange from '../test/generated-artifacts/TestFillQuoteTransformerExchange.json';
import * as TestFillQuoteTransformerHost from '../test/generated-artifacts/TestFillQuoteTransformerHost.json';
@@ -144,6 +156,7 @@ import * as TestMetaTransactionsNativeOrdersFeature from '../test/generated-arti
import * as TestMetaTransactionsTransformERC20Feature from '../test/generated-artifacts/TestMetaTransactionsTransformERC20Feature.json';
import * as TestMigrator from '../test/generated-artifacts/TestMigrator.json';
import * as TestMintableERC20Token from '../test/generated-artifacts/TestMintableERC20Token.json';
import * as TestMintableERC721Token from '../test/generated-artifacts/TestMintableERC721Token.json';
import * as TestMintTokenERC20Transformer from '../test/generated-artifacts/TestMintTokenERC20Transformer.json';
import * as TestMooniswap from '../test/generated-artifacts/TestMooniswap.json';
import * as TestNativeOrdersFeature from '../test/generated-artifacts/TestNativeOrdersFeature.json';
@@ -151,6 +164,7 @@ import * as TestNoEthRecipient from '../test/generated-artifacts/TestNoEthRecipi
import * as TestOrderSignerRegistryWithContractWallet from '../test/generated-artifacts/TestOrderSignerRegistryWithContractWallet.json';
import * as TestPermissionlessTransformerDeployerSuicidal from '../test/generated-artifacts/TestPermissionlessTransformerDeployerSuicidal.json';
import * as TestPermissionlessTransformerDeployerTransformer from '../test/generated-artifacts/TestPermissionlessTransformerDeployerTransformer.json';
import * as TestPropertyValidator from '../test/generated-artifacts/TestPropertyValidator.json';
import * as TestRfqOriginRegistration from '../test/generated-artifacts/TestRfqOriginRegistration.json';
import * as TestSimpleFunctionRegistryFeatureImpl1 from '../test/generated-artifacts/TestSimpleFunctionRegistryFeatureImpl1.json';
import * as TestSimpleFunctionRegistryFeatureImpl2 from '../test/generated-artifacts/TestSimpleFunctionRegistryFeatureImpl2.json';
@@ -181,6 +195,7 @@ export const artifacts = {
ZeroEx: ZeroEx as ContractArtifact,
ZeroExOptimized: ZeroExOptimized as ContractArtifact,
LibCommonRichErrors: LibCommonRichErrors as ContractArtifact,
LibERC721OrdersRichErrors: LibERC721OrdersRichErrors as ContractArtifact,
LibLiquidityProviderRichErrors: LibLiquidityProviderRichErrors as ContractArtifact,
LibMetaTransactionsRichErrors: LibMetaTransactionsRichErrors as ContractArtifact,
LibNativeOrdersRichErrors: LibNativeOrdersRichErrors as ContractArtifact,
@@ -201,6 +216,7 @@ export const artifacts = {
TransformerDeployer: TransformerDeployer as ContractArtifact,
BatchFillNativeOrdersFeature: BatchFillNativeOrdersFeature as ContractArtifact,
BootstrapFeature: BootstrapFeature as ContractArtifact,
ERC721OrdersFeature: ERC721OrdersFeature as ContractArtifact,
FundRecoveryFeature: FundRecoveryFeature as ContractArtifact,
LiquidityProviderFeature: LiquidityProviderFeature as ContractArtifact,
MetaTransactionsFeature: MetaTransactionsFeature as ContractArtifact,
@@ -214,6 +230,7 @@ export const artifacts = {
UniswapV3Feature: UniswapV3Feature as ContractArtifact,
IBatchFillNativeOrdersFeature: IBatchFillNativeOrdersFeature as ContractArtifact,
IBootstrapFeature: IBootstrapFeature as ContractArtifact,
IERC721OrdersFeature: IERC721OrdersFeature as ContractArtifact,
IFeature: IFeature as ContractArtifact,
IFundRecoveryFeature: IFundRecoveryFeature as ContractArtifact,
ILiquidityProviderFeature: ILiquidityProviderFeature as ContractArtifact,
@@ -229,6 +246,7 @@ export const artifacts = {
ITransformERC20Feature: ITransformERC20Feature as ContractArtifact,
IUniswapFeature: IUniswapFeature as ContractArtifact,
IUniswapV3Feature: IUniswapV3Feature as ContractArtifact,
LibERC721Order: LibERC721Order as ContractArtifact,
LibNativeOrder: LibNativeOrder as ContractArtifact,
LibSignature: LibSignature as ContractArtifact,
MultiplexFeature: MultiplexFeature as ContractArtifact,
@@ -244,6 +262,7 @@ export const artifacts = {
NativeOrdersSettlement: NativeOrdersSettlement as ContractArtifact,
FixinCommon: FixinCommon as ContractArtifact,
FixinEIP712: FixinEIP712 as ContractArtifact,
FixinERC721Spender: FixinERC721Spender as ContractArtifact,
FixinProtocolFees: FixinProtocolFees as ContractArtifact,
FixinReentrancyGuard: FixinReentrancyGuard as ContractArtifact,
FixinTokenSpender: FixinTokenSpender as ContractArtifact,
@@ -253,6 +272,7 @@ export const artifacts = {
InitialMigration: InitialMigration as ContractArtifact,
LibBootstrap: LibBootstrap as ContractArtifact,
LibMigrate: LibMigrate as ContractArtifact,
LibERC721OrdersStorage: LibERC721OrdersStorage as ContractArtifact,
LibMetaTransactionsStorage: LibMetaTransactionsStorage as ContractArtifact,
LibNativeOrdersStorage: LibNativeOrdersStorage as ContractArtifact,
LibOtcOrdersStorage: LibOtcOrdersStorage as ContractArtifact,
@@ -298,8 +318,12 @@ export const artifacts = {
MixinUniswapV2: MixinUniswapV2 as ContractArtifact,
MixinUniswapV3: MixinUniswapV3 as ContractArtifact,
MixinZeroExBridge: MixinZeroExBridge as ContractArtifact,
IERC721OrderCallback: IERC721OrderCallback as ContractArtifact,
IERC721Token: IERC721Token as ContractArtifact,
IFeeRecipient: IFeeRecipient as ContractArtifact,
ILiquidityProvider: ILiquidityProvider as ContractArtifact,
IMooniswapPool: IMooniswapPool as ContractArtifact,
IPropertyValidator: IPropertyValidator as ContractArtifact,
IUniswapV2Pair: IUniswapV2Pair as ContractArtifact,
IUniswapV3Pool: IUniswapV3Pool as ContractArtifact,
IERC20Bridge: IERC20Bridge as ContractArtifact,
@@ -307,9 +331,10 @@ export const artifacts = {
ITestSimpleFunctionRegistryFeature: ITestSimpleFunctionRegistryFeature as ContractArtifact,
TestBridge: TestBridge as ContractArtifact,
TestCallTarget: TestCallTarget as ContractArtifact,
TestCurve: TestCurve as ContractArtifact,
TestDelegateCaller: TestDelegateCaller as ContractArtifact,
TestERC721OrderPresigner: TestERC721OrderPresigner as ContractArtifact,
TestFeeCollectorController: TestFeeCollectorController as ContractArtifact,
TestFeeRecipient: TestFeeRecipient as ContractArtifact,
TestFillQuoteTransformerBridge: TestFillQuoteTransformerBridge as ContractArtifact,
TestFillQuoteTransformerExchange: TestFillQuoteTransformerExchange as ContractArtifact,
TestFillQuoteTransformerHost: TestFillQuoteTransformerHost as ContractArtifact,
@@ -319,33 +344,36 @@ export const artifacts = {
TestInitialMigration: TestInitialMigration as ContractArtifact,
TestLibNativeOrder: TestLibNativeOrder as ContractArtifact,
TestLibSignature: TestLibSignature as ContractArtifact,
TestLiquidityProvider: TestLiquidityProvider as ContractArtifact,
TestMetaTransactionsNativeOrdersFeature: TestMetaTransactionsNativeOrdersFeature as ContractArtifact,
TestMetaTransactionsTransformERC20Feature: TestMetaTransactionsTransformERC20Feature as ContractArtifact,
TestMigrator: TestMigrator as ContractArtifact,
TestMintTokenERC20Transformer: TestMintTokenERC20Transformer as ContractArtifact,
TestMintableERC20Token: TestMintableERC20Token as ContractArtifact,
TestMooniswap: TestMooniswap as ContractArtifact,
TestNativeOrdersFeature: TestNativeOrdersFeature as ContractArtifact,
TestNoEthRecipient: TestNoEthRecipient as ContractArtifact,
TestOrderSignerRegistryWithContractWallet: TestOrderSignerRegistryWithContractWallet as ContractArtifact,
TestPermissionlessTransformerDeployerSuicidal: TestPermissionlessTransformerDeployerSuicidal as ContractArtifact,
TestPermissionlessTransformerDeployerTransformer: TestPermissionlessTransformerDeployerTransformer as ContractArtifact,
TestPropertyValidator: TestPropertyValidator as ContractArtifact,
TestRfqOriginRegistration: TestRfqOriginRegistration as ContractArtifact,
TestSimpleFunctionRegistryFeatureImpl1: TestSimpleFunctionRegistryFeatureImpl1 as ContractArtifact,
TestSimpleFunctionRegistryFeatureImpl2: TestSimpleFunctionRegistryFeatureImpl2 as ContractArtifact,
TestStaking: TestStaking as ContractArtifact,
TestTokenSpenderERC20Token: TestTokenSpenderERC20Token as ContractArtifact,
TestTransformERC20: TestTransformERC20 as ContractArtifact,
TestTransformerBase: TestTransformerBase as ContractArtifact,
TestTransformerDeployerTransformer: TestTransformerDeployerTransformer as ContractArtifact,
TestTransformerHost: TestTransformerHost as ContractArtifact,
TestUniswapV3Feature: TestUniswapV3Feature as ContractArtifact,
TestWethTransformerHost: TestWethTransformerHost as ContractArtifact,
TestZeroExFeature: TestZeroExFeature as ContractArtifact,
TestCurve: TestCurve as ContractArtifact,
TestLiquidityProvider: TestLiquidityProvider as ContractArtifact,
TestMooniswap: TestMooniswap as ContractArtifact,
TestUniswapV2Factory: TestUniswapV2Factory as ContractArtifact,
TestUniswapV2Pool: TestUniswapV2Pool as ContractArtifact,
TestUniswapV3Factory: TestUniswapV3Factory as ContractArtifact,
TestUniswapV3Feature: TestUniswapV3Feature as ContractArtifact,
TestUniswapV3Pool: TestUniswapV3Pool as ContractArtifact,
TestMintableERC20Token: TestMintableERC20Token as ContractArtifact,
TestMintableERC721Token: TestMintableERC721Token as ContractArtifact,
TestTokenSpenderERC20Token: TestTokenSpenderERC20Token as ContractArtifact,
TestWeth: TestWeth as ContractArtifact,
TestWethTransformerHost: TestWethTransformerHost as ContractArtifact,
TestZeroExFeature: TestZeroExFeature as ContractArtifact,
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,22 @@
import { constants, getRandomInteger, randomAddress } from '@0x/contracts-test-utils';
import { ERC721Order } from '@0x/protocol-utils';
import { BigNumber } from '@0x/utils';
/**
* Generate a random ERC721 Order
*/
export function getRandomERC721Order(fields: Partial<ERC721Order> = {}): ERC721Order {
return new ERC721Order({
erc20Token: randomAddress(),
erc20TokenAmount: getRandomInteger('1e18', '10e18'),
erc721Token: randomAddress(),
erc721TokenId: getRandomInteger(0, constants.MAX_UINT256),
maker: randomAddress(),
taker: randomAddress(),
erc721TokenProperties: [],
fees: [],
nonce: getRandomInteger(0, constants.MAX_UINT256),
expiry: new BigNumber(Math.floor(Date.now() / 1000 + 60)),
...fields,
});
}

View File

@@ -9,11 +9,13 @@ export * from '../test/generated-wrappers/bootstrap_feature';
export * from '../test/generated-wrappers/bridge_adapter';
export * from '../test/generated-wrappers/bridge_protocols';
export * from '../test/generated-wrappers/curve_liquidity_provider';
export * from '../test/generated-wrappers/erc721_orders_feature';
export * from '../test/generated-wrappers/fee_collector';
export * from '../test/generated-wrappers/fee_collector_controller';
export * from '../test/generated-wrappers/fill_quote_transformer';
export * from '../test/generated-wrappers/fixin_common';
export * from '../test/generated-wrappers/fixin_e_i_p712';
export * from '../test/generated-wrappers/fixin_erc721_spender';
export * from '../test/generated-wrappers/fixin_protocol_fees';
export * from '../test/generated-wrappers/fixin_reentrancy_guard';
export * from '../test/generated-wrappers/fixin_token_spender';
@@ -25,7 +27,11 @@ export * from '../test/generated-wrappers/i_bootstrap_feature';
export * from '../test/generated-wrappers/i_bridge_adapter';
export * from '../test/generated-wrappers/i_erc20_bridge';
export * from '../test/generated-wrappers/i_erc20_transformer';
export * from '../test/generated-wrappers/i_erc721_order_callback';
export * from '../test/generated-wrappers/i_erc721_orders_feature';
export * from '../test/generated-wrappers/i_erc721_token';
export * from '../test/generated-wrappers/i_feature';
export * from '../test/generated-wrappers/i_fee_recipient';
export * from '../test/generated-wrappers/i_flash_wallet';
export * from '../test/generated-wrappers/i_fund_recovery_feature';
export * from '../test/generated-wrappers/i_liquidity_provider';
@@ -39,6 +45,7 @@ export * from '../test/generated-wrappers/i_native_orders_feature';
export * from '../test/generated-wrappers/i_otc_orders_feature';
export * from '../test/generated-wrappers/i_ownable_feature';
export * from '../test/generated-wrappers/i_pancake_swap_feature';
export * from '../test/generated-wrappers/i_property_validator';
export * from '../test/generated-wrappers/i_simple_function_registry_feature';
export * from '../test/generated-wrappers/i_staking';
export * from '../test/generated-wrappers/i_test_simple_function_registry_feature';
@@ -53,6 +60,9 @@ export * from '../test/generated-wrappers/initial_migration';
export * from '../test/generated-wrappers/lib_bootstrap';
export * from '../test/generated-wrappers/lib_common_rich_errors';
export * from '../test/generated-wrappers/lib_erc20_transformer';
export * from '../test/generated-wrappers/lib_erc721_order';
export * from '../test/generated-wrappers/lib_erc721_orders_rich_errors';
export * from '../test/generated-wrappers/lib_erc721_orders_storage';
export * from '../test/generated-wrappers/lib_fee_collector';
export * from '../test/generated-wrappers/lib_liquidity_provider_rich_errors';
export * from '../test/generated-wrappers/lib_meta_transactions_rich_errors';
@@ -127,7 +137,9 @@ export * from '../test/generated-wrappers/test_bridge';
export * from '../test/generated-wrappers/test_call_target';
export * from '../test/generated-wrappers/test_curve';
export * from '../test/generated-wrappers/test_delegate_caller';
export * from '../test/generated-wrappers/test_erc721_order_presigner';
export * from '../test/generated-wrappers/test_fee_collector_controller';
export * from '../test/generated-wrappers/test_fee_recipient';
export * from '../test/generated-wrappers/test_fill_quote_transformer_bridge';
export * from '../test/generated-wrappers/test_fill_quote_transformer_exchange';
export * from '../test/generated-wrappers/test_fill_quote_transformer_host';
@@ -143,12 +155,14 @@ export * from '../test/generated-wrappers/test_meta_transactions_transform_erc20
export * from '../test/generated-wrappers/test_migrator';
export * from '../test/generated-wrappers/test_mint_token_erc20_transformer';
export * from '../test/generated-wrappers/test_mintable_erc20_token';
export * from '../test/generated-wrappers/test_mintable_erc721_token';
export * from '../test/generated-wrappers/test_mooniswap';
export * from '../test/generated-wrappers/test_native_orders_feature';
export * from '../test/generated-wrappers/test_no_eth_recipient';
export * from '../test/generated-wrappers/test_order_signer_registry_with_contract_wallet';
export * from '../test/generated-wrappers/test_permissionless_transformer_deployer_suicidal';
export * from '../test/generated-wrappers/test_permissionless_transformer_deployer_transformer';
export * from '../test/generated-wrappers/test_property_validator';
export * from '../test/generated-wrappers/test_rfq_origin_registration';
export * from '../test/generated-wrappers/test_simple_function_registry_feature_impl1';
export * from '../test/generated-wrappers/test_simple_function_registry_feature_impl2';

View File

@@ -42,11 +42,13 @@
"test/generated-artifacts/BridgeAdapter.json",
"test/generated-artifacts/BridgeProtocols.json",
"test/generated-artifacts/CurveLiquidityProvider.json",
"test/generated-artifacts/ERC721OrdersFeature.json",
"test/generated-artifacts/FeeCollector.json",
"test/generated-artifacts/FeeCollectorController.json",
"test/generated-artifacts/FillQuoteTransformer.json",
"test/generated-artifacts/FixinCommon.json",
"test/generated-artifacts/FixinEIP712.json",
"test/generated-artifacts/FixinERC721Spender.json",
"test/generated-artifacts/FixinProtocolFees.json",
"test/generated-artifacts/FixinReentrancyGuard.json",
"test/generated-artifacts/FixinTokenSpender.json",
@@ -58,7 +60,11 @@
"test/generated-artifacts/IBridgeAdapter.json",
"test/generated-artifacts/IERC20Bridge.json",
"test/generated-artifacts/IERC20Transformer.json",
"test/generated-artifacts/IERC721OrderCallback.json",
"test/generated-artifacts/IERC721OrdersFeature.json",
"test/generated-artifacts/IERC721Token.json",
"test/generated-artifacts/IFeature.json",
"test/generated-artifacts/IFeeRecipient.json",
"test/generated-artifacts/IFlashWallet.json",
"test/generated-artifacts/IFundRecoveryFeature.json",
"test/generated-artifacts/ILiquidityProvider.json",
@@ -72,6 +78,7 @@
"test/generated-artifacts/IOtcOrdersFeature.json",
"test/generated-artifacts/IOwnableFeature.json",
"test/generated-artifacts/IPancakeSwapFeature.json",
"test/generated-artifacts/IPropertyValidator.json",
"test/generated-artifacts/ISimpleFunctionRegistryFeature.json",
"test/generated-artifacts/IStaking.json",
"test/generated-artifacts/ITestSimpleFunctionRegistryFeature.json",
@@ -86,6 +93,9 @@
"test/generated-artifacts/LibBootstrap.json",
"test/generated-artifacts/LibCommonRichErrors.json",
"test/generated-artifacts/LibERC20Transformer.json",
"test/generated-artifacts/LibERC721Order.json",
"test/generated-artifacts/LibERC721OrdersRichErrors.json",
"test/generated-artifacts/LibERC721OrdersStorage.json",
"test/generated-artifacts/LibFeeCollector.json",
"test/generated-artifacts/LibLiquidityProviderRichErrors.json",
"test/generated-artifacts/LibMetaTransactionsRichErrors.json",
@@ -160,7 +170,9 @@
"test/generated-artifacts/TestCallTarget.json",
"test/generated-artifacts/TestCurve.json",
"test/generated-artifacts/TestDelegateCaller.json",
"test/generated-artifacts/TestERC721OrderPresigner.json",
"test/generated-artifacts/TestFeeCollectorController.json",
"test/generated-artifacts/TestFeeRecipient.json",
"test/generated-artifacts/TestFillQuoteTransformerBridge.json",
"test/generated-artifacts/TestFillQuoteTransformerExchange.json",
"test/generated-artifacts/TestFillQuoteTransformerHost.json",
@@ -176,12 +188,14 @@
"test/generated-artifacts/TestMigrator.json",
"test/generated-artifacts/TestMintTokenERC20Transformer.json",
"test/generated-artifacts/TestMintableERC20Token.json",
"test/generated-artifacts/TestMintableERC721Token.json",
"test/generated-artifacts/TestMooniswap.json",
"test/generated-artifacts/TestNativeOrdersFeature.json",
"test/generated-artifacts/TestNoEthRecipient.json",
"test/generated-artifacts/TestOrderSignerRegistryWithContractWallet.json",
"test/generated-artifacts/TestPermissionlessTransformerDeployerSuicidal.json",
"test/generated-artifacts/TestPermissionlessTransformerDeployerTransformer.json",
"test/generated-artifacts/TestPropertyValidator.json",
"test/generated-artifacts/TestRfqOriginRegistration.json",
"test/generated-artifacts/TestSimpleFunctionRegistryFeatureImpl1.json",
"test/generated-artifacts/TestSimpleFunctionRegistryFeatureImpl2.json",

View File

@@ -74,8 +74,19 @@ export function getExchangeProxyEIP712Hash(structHash: string, chainId?: number,
/**
* Compute the type hash of an EIP712 struct given its ABI.
*/
export function getTypeHash(structName: string, abi: EIP712_STRUCT_ABI): string {
return hexUtils.hash(
hexUtils.toHex(Buffer.from([`${structName}(`, abi.map(a => `${a.type} ${a.name}`).join(','), ')'].join(''))),
);
export function getTypeHash(
primaryStructName: string,
primaryStructAbi: EIP712_STRUCT_ABI,
referencedStructs: { [structName: string]: EIP712_STRUCT_ABI } = {},
): string {
const primaryStructType = encodeType(primaryStructName, primaryStructAbi);
// Referenced structs are sorted lexicographically
const referencedStructTypes = Object.entries(referencedStructs)
.sort(([nameA], [nameB]) => nameA.localeCompare(nameB))
.map(([name, abi]) => encodeType(name, abi));
return hexUtils.hash(hexUtils.toHex(Buffer.from(primaryStructType + referencedStructTypes.join(''))));
}
function encodeType(structName: string, abi: EIP712_STRUCT_ABI): string {
return [`${structName}(`, abi.map(a => `${a.type} ${a.name}`).join(','), ')'].join('');
}

View File

@@ -0,0 +1,260 @@
import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
import { SupportedProvider } from '@0x/subproviders';
import { EIP712TypedData } from '@0x/types';
import { BigNumber, hexUtils, NULL_ADDRESS } from '@0x/utils';
import { ZERO } from './constants';
import {
createExchangeProxyEIP712Domain,
EIP712_DOMAIN_PARAMETERS,
getExchangeProxyEIP712Hash,
getTypeHash,
} from './eip712_utils';
import {
eip712SignHashWithKey,
eip712SignTypedDataWithProviderAsync,
ethSignHashWithKey,
ethSignHashWithProviderAsync,
Signature,
SignatureType,
} from './signature_utils';
export class ERC721Order {
public static readonly STRUCT_NAME = 'ERC721Order';
public static readonly STRUCT_ABI = [
{ type: 'uint8', name: 'direction' },
{ type: 'address', name: 'erc20Token' },
{ type: 'uint256', name: 'erc20TokenAmount' },
{ type: 'address', name: 'erc721Token' },
{ type: 'uint256', name: 'erc721TokenId' },
{ type: 'Property[]', name: 'erc721TokenProperties' },
{ type: 'Fee[]', name: 'fees' },
{ type: 'address', name: 'maker' },
{ type: 'address', name: 'taker' },
{ type: 'uint256', name: 'expiry' },
{ type: 'uint256', name: 'nonce' },
];
public static readonly REFERENCED_STRUCT_ABIS = {
['Fee']: [
{ type: 'address', name: 'recipient' },
{ type: 'uint256', name: 'amount' },
{ type: 'bytes', name: 'feeData' },
],
['Property']: [
{ type: 'address', name: 'propertyValidator' },
{ type: 'bytes', name: 'propertyData' },
],
};
public static readonly TYPE_HASH = getTypeHash(
ERC721Order.STRUCT_NAME,
ERC721Order.STRUCT_ABI,
ERC721Order.REFERENCED_STRUCT_ABIS,
);
public static readonly FEE_TYPE_HASH = getTypeHash('Fee', ERC721Order.REFERENCED_STRUCT_ABIS.Fee);
public static readonly PROPERTY_TYPE_HASH = getTypeHash('Property', ERC721Order.REFERENCED_STRUCT_ABIS.Property);
public direction: ERC721Order.TradeDirection;
public erc20Token: string;
public erc20TokenAmount: BigNumber;
public erc721Token: string;
public erc721TokenId: BigNumber;
public erc721TokenProperties: ERC721Order.Property[];
public fees: ERC721Order.Fee[];
public maker: string;
public taker: string;
public expiry: BigNumber;
public nonce: BigNumber;
public chainId: number;
public verifyingContract: string;
constructor(fields: Partial<ERC721OrderFields> = {}) {
const _fields = { ...ERC721_ORDER_DEFAULT_VALUES, ...fields };
this.direction = _fields.direction;
this.erc20Token = _fields.erc20Token;
this.erc20TokenAmount = _fields.erc20TokenAmount;
this.erc721Token = _fields.erc721Token;
this.erc721TokenId = _fields.erc721TokenId;
this.erc721TokenProperties = _fields.erc721TokenProperties;
this.fees = _fields.fees;
this.maker = _fields.maker;
this.taker = _fields.taker;
this.expiry = _fields.expiry;
this.nonce = _fields.nonce;
this.chainId = _fields.chainId;
this.verifyingContract = _fields.verifyingContract;
}
public clone(fields: Partial<ERC721OrderFields> = {}): ERC721Order {
return new ERC721Order({
direction: this.direction,
erc20Token: this.erc20Token,
erc20TokenAmount: this.erc20TokenAmount,
erc721Token: this.erc721Token,
erc721TokenId: this.erc721TokenId,
erc721TokenProperties: this.erc721TokenProperties,
fees: this.fees,
maker: this.maker,
taker: this.taker,
expiry: this.expiry,
nonce: this.nonce,
chainId: this.chainId,
verifyingContract: this.verifyingContract,
...fields,
});
}
public getStructHash(): string {
const propertiesHash = hexUtils.hash(
hexUtils.concat(
...this.erc721TokenProperties.map(property =>
hexUtils.hash(
hexUtils.concat(
hexUtils.leftPad(ERC721Order.PROPERTY_TYPE_HASH),
hexUtils.leftPad(property.propertyValidator),
hexUtils.hash(property.propertyData),
),
),
),
),
);
const feesHash = hexUtils.hash(
hexUtils.concat(
...this.fees.map(fee =>
hexUtils.hash(
hexUtils.concat(
hexUtils.leftPad(ERC721Order.FEE_TYPE_HASH),
hexUtils.leftPad(fee.recipient),
hexUtils.leftPad(fee.amount),
hexUtils.hash(fee.feeData),
),
),
),
),
);
return hexUtils.hash(
hexUtils.concat(
hexUtils.leftPad(ERC721Order.TYPE_HASH),
hexUtils.leftPad(this.direction),
hexUtils.leftPad(this.erc20Token),
hexUtils.leftPad(this.erc20TokenAmount),
hexUtils.leftPad(this.erc721Token),
hexUtils.leftPad(this.erc721TokenId),
propertiesHash,
feesHash,
hexUtils.leftPad(this.maker),
hexUtils.leftPad(this.taker),
hexUtils.leftPad(this.expiry),
hexUtils.leftPad(this.nonce),
),
);
}
public getEIP712TypedData(): EIP712TypedData {
return {
types: {
EIP712Domain: EIP712_DOMAIN_PARAMETERS,
[ERC721Order.STRUCT_NAME]: ERC721Order.STRUCT_ABI,
...ERC721Order.REFERENCED_STRUCT_ABIS,
},
domain: createExchangeProxyEIP712Domain(this.chainId, this.verifyingContract) as any,
primaryType: ERC721Order.STRUCT_NAME,
message: {
direction: this.direction,
erc20Token: this.erc20Token,
erc20TokenAmount: this.erc20TokenAmount.toString(10),
erc721Token: this.erc721Token,
erc721TokenId: this.erc721TokenId.toString(10),
erc721TokenProperties: this.erc721TokenProperties as any,
fees: this.fees.map(fee => ({
recipient: fee.recipient,
amount: fee.amount.toString(10),
feeData: fee.feeData,
})) as any,
maker: this.maker,
taker: this.taker,
expiry: this.expiry.toString(10),
nonce: this.nonce.toString(10),
},
};
}
public willExpire(secondsFromNow: number = 0): boolean {
const millisecondsInSecond = 1000;
const currentUnixTimestampSec = new BigNumber(Date.now() / millisecondsInSecond).integerValue();
return this.expiry.isLessThan(currentUnixTimestampSec.plus(secondsFromNow));
}
public getHash(): string {
return getExchangeProxyEIP712Hash(this.getStructHash(), this.chainId, this.verifyingContract);
}
public async getSignatureWithProviderAsync(
provider: SupportedProvider,
type: SignatureType = SignatureType.EthSign,
signer: string = this.maker,
): Promise<Signature> {
switch (type) {
case SignatureType.EIP712:
return eip712SignTypedDataWithProviderAsync(this.getEIP712TypedData(), signer, provider);
case SignatureType.EthSign:
return ethSignHashWithProviderAsync(this.getHash(), signer, provider);
default:
throw new Error(`Cannot sign with signature type: ${type}`);
}
}
public getSignatureWithKey(key: string, type: SignatureType = SignatureType.EthSign): Signature {
switch (type) {
case SignatureType.EIP712:
return eip712SignHashWithKey(this.getHash(), key);
case SignatureType.EthSign:
return ethSignHashWithKey(this.getHash(), key);
default:
throw new Error(`Cannot sign with signature type: ${type}`);
}
}
}
export namespace ERC721Order {
export interface Property {
propertyValidator: string;
propertyData: string;
}
export interface Fee {
recipient: string;
amount: BigNumber;
feeData: string;
}
export enum TradeDirection {
Sell721 = 0,
Buy721 = 1,
}
export enum OrderStatus {
Invalid = 0,
Fillable = 1,
Unfillable = 2,
Expired = 3,
}
}
const ERC721_ORDER_DEFAULT_VALUES = {
direction: ERC721Order.TradeDirection.Sell721,
erc20Token: NULL_ADDRESS,
erc20TokenAmount: ZERO,
erc721Token: NULL_ADDRESS,
erc721TokenId: ZERO,
erc721TokenProperties: [] as ERC721Order.Property[],
fees: [] as ERC721Order.Fee[],
maker: NULL_ADDRESS,
taker: NULL_ADDRESS,
expiry: ZERO,
nonce: ZERO,
chainId: 1,
verifyingContract: getContractAddressesForChainOrThrow(1).exchangeProxy,
};
export type ERC721OrderFields = typeof ERC721_ORDER_DEFAULT_VALUES;

View File

@@ -10,3 +10,4 @@ export * from './transformer_utils';
export * from './constants';
export * from './vip_utils';
export * from './treasury_votes';
export * from './erc721_orders';

View File

@@ -0,0 +1,132 @@
// tslint:disable: max-classes-per-file
import { Numberish, RevertError } from '@0x/utils';
import { ERC721Order } from '../erc721_orders';
export class OverspentEthError extends RevertError {
constructor(ethSpent?: Numberish, msgValue?: Numberish) {
super('OverspentEthError', 'OverspentEthError(uint256 ethSpent, uint256 msgValue)', {
ethSpent,
msgValue,
});
}
}
export class InsufficientEthError extends RevertError {
constructor(ethAvailable?: Numberish, orderAmount?: Numberish) {
super('InsufficientEthError', 'InsufficientEthError(uint256 ethAvailable, uint256 orderAmount)', {
ethAvailable,
orderAmount,
});
}
}
export class ERC721TokenMismatchError extends RevertError {
constructor(token1?: string, token2?: string) {
super('ERC721TokenMismatchError', 'ERC721TokenMismatchError(address token1, address token2)', {
token1,
token2,
});
}
}
export class ERC20TokenMismatchError extends RevertError {
constructor(token1?: string, token2?: string) {
super('ERC20TokenMismatchError', 'ERC20TokenMismatchError(address token1, address token2)', {
token1,
token2,
});
}
}
export class NegativeSpreadError extends RevertError {
constructor(sellOrderAmount?: Numberish, buyOrderAmount?: Numberish) {
super('NegativeSpreadError', 'NegativeSpreadError(uint256 sellOrderAmount, uint256 buyOrderAmount)', {
sellOrderAmount,
buyOrderAmount,
});
}
}
export class SellOrderFeesExceedSpreadError extends RevertError {
constructor(sellOrderFees?: Numberish, spread?: Numberish) {
super(
'SellOrderFeesExceedSpreadError',
'SellOrderFeesExceedSpreadError(uint256 sellOrderFees, uint256 spread)',
{
sellOrderFees,
spread,
},
);
}
}
export class OnlyTakerError extends RevertError {
constructor(sender?: string, taker?: string) {
super('OnlyTakerError', 'OnlyTakerError(address sender, address taker)', {
sender,
taker,
});
}
}
export { InvalidSignerError } from './native_orders';
export class OrderNotFillableError extends RevertError {
constructor(maker?: string, nonce?: Numberish, orderStatus?: ERC721Order.OrderStatus) {
super('OrderNotFillableError', 'OrderNotFillableError(address maker, uint256 nonce, uint8 orderStatus)', {
maker,
nonce,
orderStatus,
});
}
}
export class ERC721TokenIdMismatchError extends RevertError {
constructor(tokenId?: Numberish, orderTokenId?: Numberish) {
super('ERC721TokenIdMismatchError', 'ERC721TokenIdMismatchError(uint256 tokenId, uint256 orderTokenId)', {
tokenId,
orderTokenId,
});
}
}
export class PropertyValidationFailedError extends RevertError {
constructor(
propertyValidator?: string,
erc721Token?: string,
erc721TokenId?: Numberish,
propertyData?: string,
errorData?: string,
) {
super(
'PropertyValidationFailedError',
'PropertyValidationFailedError(address propertyValidator, address erc721Token, uint256 erc721TokenId, bytes propertyData, bytes errorData)',
{
propertyValidator,
erc721Token,
erc721TokenId,
propertyData,
errorData,
},
);
}
}
const types = [
OverspentEthError,
InsufficientEthError,
ERC721TokenMismatchError,
ERC20TokenMismatchError,
NegativeSpreadError,
SellOrderFeesExceedSpreadError,
OnlyTakerError,
OrderNotFillableError,
ERC721TokenIdMismatchError,
PropertyValidationFailedError,
];
// Register the types we've defined.
for (const type of types) {
RevertError.registerType(type);
}

View File

@@ -14,6 +14,7 @@ import {
} from './inherited';
import * as NativeOrders from './native_orders';
import * as Signatures from './signatures';
import * as ERC721Orders from './erc721_orders';
export {
Common,
@@ -28,4 +29,5 @@ export {
LiquidityProvider,
NativeOrders,
Signatures,
ERC721Orders,
};

View File

@@ -12,6 +12,7 @@ export enum SignatureType {
Invalid = 1,
EIP712 = 2,
EthSign = 3,
PreSigned = 4,
}
/**