aboutsummaryrefslogtreecommitdiffstats
path: root/packages/contracts/src
diff options
context:
space:
mode:
authorJacob Evans <dekz@dekz.net>2018-06-18 19:50:35 +0800
committerGitHub <noreply@github.com>2018-06-18 19:50:35 +0800
commit190eafc30e2e444ed15b76217a6162ec04b33f73 (patch)
treeb20cbad73ff7a069dc0f0ef43ebc0373c714ad02 /packages/contracts/src
parentd4ee0e862297c16f8ee62efccd31f1193052c64e (diff)
parent0c238448fda99c4d7997901d0fe4d72cb06b79cc (diff)
downloaddexon-sol-tools-190eafc30e2e444ed15b76217a6162ec04b33f73.tar
dexon-sol-tools-190eafc30e2e444ed15b76217a6162ec04b33f73.tar.gz
dexon-sol-tools-190eafc30e2e444ed15b76217a6162ec04b33f73.tar.bz2
dexon-sol-tools-190eafc30e2e444ed15b76217a6162ec04b33f73.tar.lz
dexon-sol-tools-190eafc30e2e444ed15b76217a6162ec04b33f73.tar.xz
dexon-sol-tools-190eafc30e2e444ed15b76217a6162ec04b33f73.tar.zst
dexon-sol-tools-190eafc30e2e444ed15b76217a6162ec04b33f73.zip
Merge branch 'v2-prototype' into bug/contracts/eip712-191-prefix
Diffstat (limited to 'packages/contracts/src')
-rw-r--r--packages/contracts/src/contracts/current/protocol/AssetProxy/ERC20Proxy.sol49
-rw-r--r--packages/contracts/src/contracts/current/protocol/AssetProxy/ERC721Proxy.sol73
-rw-r--r--packages/contracts/src/contracts/current/protocol/AssetProxy/MixinAssetProxy.sol14
-rw-r--r--packages/contracts/src/contracts/current/protocol/AssetProxy/interfaces/IAssetProxy.sol8
-rw-r--r--packages/contracts/src/contracts/current/protocol/AssetProxy/libs/LibAssetProxyErrors.sol7
-rw-r--r--packages/contracts/src/contracts/current/protocol/AssetProxy/mixins/MAssetProxy.sol4
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/Exchange.sol4
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/MixinAssetProxyDispatcher.sol20
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/MixinExchangeCore.sol9
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/MixinMatchOrders.sol18
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlement.sol56
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidator.sol4
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol123
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IAssetProxyDispatcher.sol3
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/interfaces/ITransactions.sol3
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IWrapperFunctions.sol27
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/libs/LibExchangeErrors.sol1
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/mixins/MAssetProxyDispatcher.sol6
-rw-r--r--packages/contracts/src/contracts/current/test/DummyERC20Token/DummyERC20Token.sol3
-rw-r--r--packages/contracts/src/contracts/current/test/DummyERC721Receiver/DummyERC721Receiver.sol63
-rw-r--r--packages/contracts/src/contracts/current/test/DummyERC721Token/DummyERC721Token.sol3
-rw-r--r--packages/contracts/src/contracts/current/test/TestAssetDataDecoders/TestAssetDataDecoders.sol56
-rw-r--r--packages/contracts/src/contracts/current/test/TestAssetProxyDispatcher/TestAssetProxyDispatcher.sol5
-rw-r--r--packages/contracts/src/contracts/current/test/TestLibBytes/TestLibBytes.sol75
-rw-r--r--packages/contracts/src/contracts/current/test/TestLibMem/TestLibMem.sol56
-rw-r--r--packages/contracts/src/contracts/current/utils/LibBytes/LibBytes.sol195
-rw-r--r--packages/contracts/src/contracts/current/utils/LibMem/LibMem.sol140
-rw-r--r--packages/contracts/src/utils/artifacts.ts6
-rw-r--r--packages/contracts/src/utils/assertions.ts63
-rw-r--r--packages/contracts/src/utils/constants.ts13
-rw-r--r--packages/contracts/src/utils/coverage.ts4
-rw-r--r--packages/contracts/src/utils/erc20_wrapper.ts4
-rw-r--r--packages/contracts/src/utils/erc721_wrapper.ts4
-rw-r--r--packages/contracts/src/utils/exchange_wrapper.ts20
-rw-r--r--packages/contracts/src/utils/formatters.ts11
-rw-r--r--packages/contracts/src/utils/increase_time.ts31
-rw-r--r--packages/contracts/src/utils/log_decoder.ts1
-rw-r--r--packages/contracts/src/utils/match_order_tester.ts48
-rw-r--r--packages/contracts/src/utils/multi_sig_wrapper.ts6
-rw-r--r--packages/contracts/src/utils/order_factory.ts1
-rw-r--r--packages/contracts/src/utils/order_utils.ts4
-rw-r--r--packages/contracts/src/utils/profiler.ts27
-rw-r--r--packages/contracts/src/utils/revert_trace.ts21
-rw-r--r--packages/contracts/src/utils/token_registry_wrapper.ts2
-rw-r--r--packages/contracts/src/utils/types.ts7
-rw-r--r--packages/contracts/src/utils/web3_wrapper.ts69
46 files changed, 1056 insertions, 311 deletions
diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC20Proxy.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC20Proxy.sol
index 2c321e134..eeddb9707 100644
--- a/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC20Proxy.sol
+++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC20Proxy.sol
@@ -34,36 +34,51 @@ contract ERC20Proxy is
uint8 constant PROXY_ID = 1;
/// @dev Internal version of `transferFrom`.
- /// @param assetMetadata Encoded byte array.
+ /// @param assetData Encoded byte array.
/// @param from Address to transfer asset from.
/// @param to Address to transfer asset to.
/// @param amount Amount of asset to transfer.
function transferFromInternal(
- bytes memory assetMetadata,
+ bytes memory assetData,
address from,
address to,
uint256 amount
)
internal
{
- // Data must be intended for this proxy.
- uint256 length = assetMetadata.length;
+ // Decode asset data.
+ address token = readAddress(assetData, 0);
+ // Transfer tokens.
+ // We do a raw call so we can check the success separate
+ // from the return data.
+ bool success = token.call(abi.encodeWithSelector(
+ IERC20Token(token).transferFrom.selector,
+ from,
+ to,
+ amount
+ ));
require(
- length == 21,
- LENGTH_21_REQUIRED
- );
- // TODO: Is this too inflexible in the future?
- require(
- uint8(assetMetadata[length - 1]) == PROXY_ID,
- ASSET_PROXY_ID_MISMATCH
+ success,
+ TRANSFER_FAILED
);
-
- // Decode metadata.
- address token = readAddress(assetMetadata, 0);
-
- // Transfer tokens.
- bool success = IERC20Token(token).transferFrom(from, to, amount);
+
+ // Check return data.
+ // If there is no return data, we assume the token incorrectly
+ // does not return a bool. In this case we expect it to revert
+ // on failure, which was handled above.
+ // If the token does return data, we require that it is a single
+ // value that evaluates to true.
+ assembly {
+ if returndatasize {
+ success := 0
+ if eq(returndatasize, 32) {
+ // First 64 bytes of memory are reserved scratch space
+ returndatacopy(0, 0, 32)
+ success := mload(0)
+ }
+ }
+ }
require(
success,
TRANSFER_FAILED
diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC721Proxy.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC721Proxy.sol
index 07e01c774..861fac2c1 100644
--- a/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC721Proxy.sol
+++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC721Proxy.sol
@@ -34,47 +34,38 @@ contract ERC721Proxy is
uint8 constant PROXY_ID = 2;
/// @dev Internal version of `transferFrom`.
- /// @param assetMetadata Encoded byte array.
+ /// @param assetData Encoded byte array.
/// @param from Address to transfer asset from.
/// @param to Address to transfer asset to.
/// @param amount Amount of asset to transfer.
function transferFromInternal(
- bytes memory assetMetadata,
+ bytes memory assetData,
address from,
address to,
uint256 amount
)
internal
{
- // Data must be intended for this proxy.
- uint256 length = assetMetadata.length;
-
- require(
- length == 53,
- LENGTH_53_REQUIRED
- );
-
- // TODO: Is this too inflexible in the future?
- require(
- uint8(assetMetadata[length - 1]) == PROXY_ID,
- ASSET_PROXY_ID_MISMATCH
- );
-
// There exists only 1 of each token.
require(
amount == 1,
INVALID_AMOUNT
);
-
- // Decode metadata
- address token = readAddress(assetMetadata, 0);
- uint256 tokenId = readUint256(assetMetadata, 20);
-
- // Transfer token.
- // Either succeeds or throws.
- // @TODO: Call safeTransferFrom if there is additional
- // data stored in `assetMetadata`.
- ERC721Token(token).transferFrom(from, to, tokenId);
+
+ // Decode asset data.
+ (
+ address token,
+ uint256 tokenId,
+ bytes memory receiverData
+ ) = decodeERC721AssetData(assetData);
+
+ // Transfer token. Saves gas by calling safeTransferFrom only
+ // when there is receiverData present. Either succeeds or throws.
+ if (receiverData.length > 0) {
+ ERC721Token(token).safeTransferFrom(from, to, tokenId, receiverData);
+ } else {
+ ERC721Token(token).transferFrom(from, to, tokenId);
+ }
}
/// @dev Gets the proxy id associated with the proxy address.
@@ -86,4 +77,34 @@ contract ERC721Proxy is
{
return PROXY_ID;
}
+
+ /// @dev Decodes ERC721 Asset data.
+ /// @param assetData Encoded byte array.
+ /// @return proxyId Intended ERC721 proxy id.
+ /// @return token ERC721 token address.
+ /// @return tokenId ERC721 token id.
+ /// @return receiverData Additional data with no specific format, which
+ /// is passed to the receiving contract's onERC721Received.
+ function decodeERC721AssetData(bytes memory assetData)
+ internal
+ pure
+ returns (
+ address token,
+ uint256 tokenId,
+ bytes memory receiverData
+ )
+ {
+ // Decode asset data.
+ token = readAddress(assetData, 0);
+ tokenId = readUint256(assetData, 20);
+ if (assetData.length > 52) {
+ receiverData = readBytes(assetData, 52);
+ }
+
+ return (
+ token,
+ tokenId,
+ receiverData
+ );
+ }
}
diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinAssetProxy.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinAssetProxy.sol
index 5fa33cbef..9032658e7 100644
--- a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinAssetProxy.sol
+++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinAssetProxy.sol
@@ -28,12 +28,12 @@ contract MixinAssetProxy is
{
/// @dev Transfers assets. Either succeeds or throws.
- /// @param assetMetadata Encoded byte array.
+ /// @param assetData Encoded byte array.
/// @param from Address to transfer asset from.
/// @param to Address to transfer asset to.
/// @param amount Amount of asset to transfer.
function transferFrom(
- bytes assetMetadata,
+ bytes assetData,
address from,
address to,
uint256 amount
@@ -42,7 +42,7 @@ contract MixinAssetProxy is
onlyAuthorized
{
transferFromInternal(
- assetMetadata,
+ assetData,
from,
to,
amount
@@ -50,12 +50,12 @@ contract MixinAssetProxy is
}
/// @dev Makes multiple transfers of assets. Either succeeds or throws.
- /// @param assetMetadata Array of byte arrays encoded for the respective asset proxy.
+ /// @param assetData Array of byte arrays encoded for the respective asset proxy.
/// @param from Array of addresses to transfer assets from.
/// @param to Array of addresses to transfer assets to.
/// @param amounts Array of amounts of assets to transfer.
function batchTransferFrom(
- bytes[] memory assetMetadata,
+ bytes[] memory assetData,
address[] memory from,
address[] memory to,
uint256[] memory amounts
@@ -63,9 +63,9 @@ contract MixinAssetProxy is
public
onlyAuthorized
{
- for (uint256 i = 0; i < assetMetadata.length; i++) {
+ for (uint256 i = 0; i < assetData.length; i++) {
transferFromInternal(
- assetMetadata[i],
+ assetData[i],
from[i],
to[i],
amounts[i]
diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/interfaces/IAssetProxy.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/interfaces/IAssetProxy.sol
index 7e1848889..22f43b12f 100644
--- a/packages/contracts/src/contracts/current/protocol/AssetProxy/interfaces/IAssetProxy.sol
+++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/interfaces/IAssetProxy.sol
@@ -26,12 +26,12 @@ contract IAssetProxy is
{
/// @dev Transfers assets. Either succeeds or throws.
- /// @param assetMetadata Byte array encoded for the respective asset proxy.
+ /// @param assetData Byte array encoded for the respective asset proxy.
/// @param from Address to transfer asset from.
/// @param to Address to transfer asset to.
/// @param amount Amount of asset to transfer.
function transferFrom(
- bytes assetMetadata,
+ bytes assetData,
address from,
address to,
uint256 amount
@@ -39,12 +39,12 @@ contract IAssetProxy is
external;
/// @dev Makes multiple transfers of assets. Either succeeds or throws.
- /// @param assetMetadata Array of byte arrays encoded for the respective asset proxy.
+ /// @param assetData Array of byte arrays encoded for the respective asset proxy.
/// @param from Array of addresses to transfer assets from.
/// @param to Array of addresses to transfer assets to.
/// @param amounts Array of amounts of assets to transfer.
function batchTransferFrom(
- bytes[] memory assetMetadata,
+ bytes[] memory assetData,
address[] memory from,
address[] memory to,
uint256[] memory amounts
diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/libs/LibAssetProxyErrors.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/libs/LibAssetProxyErrors.sol
index e0c7fc796..65bdacdb7 100644
--- a/packages/contracts/src/contracts/current/protocol/AssetProxy/libs/LibAssetProxyErrors.sol
+++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/libs/LibAssetProxyErrors.sol
@@ -27,11 +27,6 @@ contract LibAssetProxyErrors {
string constant AUTHORIZED_ADDRESS_MISMATCH = "AUTHORIZED_ADDRESS_MISMATCH"; // Address at index does not match given target address.
/// AssetProxy errors ///
- string constant ASSET_PROXY_ID_MISMATCH = "ASSET_PROXY_ID_MISMATCH"; // Proxy id in metadata does not match this proxy id.
string constant INVALID_AMOUNT = "INVALID_AMOUNT"; // Transfer amount must equal 1.
- string constant TRANSFER_FAILED = "TRANSFER_FAILED"; // Transfer failed.
-
- /// Length validation errors ///
- string constant LENGTH_21_REQUIRED = "LENGTH_21_REQUIRED"; // Byte array must have a length of 21.
- string constant LENGTH_53_REQUIRED = "LENGTH_53_REQUIRED"; // Byte array must have a length of 53.
+ string constant TRANSFER_FAILED = "TRANSFER_FAILED"; // Transfer failed.
}
diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/mixins/MAssetProxy.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/mixins/MAssetProxy.sol
index de9d65a53..a52cb56f9 100644
--- a/packages/contracts/src/contracts/current/protocol/AssetProxy/mixins/MAssetProxy.sol
+++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/mixins/MAssetProxy.sol
@@ -26,12 +26,12 @@ contract MAssetProxy is
{
/// @dev Internal version of `transferFrom`.
- /// @param assetMetadata Encoded byte array.
+ /// @param assetData Encoded byte array.
/// @param from Address to transfer asset from.
/// @param to Address to transfer asset to.
/// @param amount Amount of asset to transfer.
function transferFromInternal(
- bytes memory assetMetadata,
+ bytes memory assetData,
address from,
address to,
uint256 amount
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/Exchange.sol b/packages/contracts/src/contracts/current/protocol/Exchange/Exchange.sol
index b7b308069..51f99bafa 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/Exchange.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/Exchange.sol
@@ -40,11 +40,11 @@ contract Exchange is
string constant public VERSION = "2.0.1-alpha";
// Mixins are instantiated in the order they are inherited
- constructor (bytes memory _zrxProxyData)
+ constructor (bytes memory _zrxAssetData)
public
MixinExchangeCore()
MixinMatchOrders()
- MixinSettlement(_zrxProxyData)
+ MixinSettlement(_zrxAssetData)
MixinSignatureValidator()
MixinTransactions()
MixinAssetProxyDispatcher()
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinAssetProxyDispatcher.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinAssetProxyDispatcher.sol
index 8f9342739..9e0246303 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinAssetProxyDispatcher.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinAssetProxyDispatcher.sol
@@ -19,12 +19,14 @@
pragma solidity ^0.4.24;
import "../../utils/Ownable/Ownable.sol";
+import "../../utils/LibBytes/LibBytes.sol";
import "./libs/LibExchangeErrors.sol";
import "./mixins/MAssetProxyDispatcher.sol";
import "../AssetProxy/interfaces/IAssetProxy.sol";
contract MixinAssetProxyDispatcher is
Ownable,
+ LibBytes,
LibExchangeErrors,
MAssetProxyDispatcher
{
@@ -80,12 +82,14 @@ contract MixinAssetProxyDispatcher is
}
/// @dev Forwards arguments to assetProxy and calls `transferFrom`. Either succeeds or throws.
- /// @param assetMetadata Byte array encoded for the respective asset proxy.
+ /// @param assetData Byte array encoded for the respective asset proxy.
+ /// @param assetProxyId Id of assetProxy to dispach to.
/// @param from Address to transfer token from.
/// @param to Address to transfer token to.
/// @param amount Amount of token to transfer.
function dispatchTransferFrom(
- bytes memory assetMetadata,
+ bytes memory assetData,
+ uint8 assetProxyId,
address from,
address to,
uint256 amount
@@ -94,18 +98,10 @@ contract MixinAssetProxyDispatcher is
{
// Do nothing if no amount should be transferred.
if (amount > 0) {
-
- // Lookup asset proxy
- uint256 length = assetMetadata.length;
- require(
- length > 0,
- LENGTH_GREATER_THAN_0_REQUIRED
- );
- uint8 assetProxyId = uint8(assetMetadata[length - 1]);
+ // Lookup assetProxy
IAssetProxy assetProxy = assetProxies[assetProxyId];
-
// transferFrom will either succeed or throw.
- assetProxy.transferFrom(assetMetadata, from, to, amount);
+ assetProxy.transferFrom(assetData, from, to, amount);
}
}
}
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinExchangeCore.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinExchangeCore.sol
index 42128c92a..0a0f0209a 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinExchangeCore.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinExchangeCore.sol
@@ -50,7 +50,7 @@ contract MixinExchangeCore is
////// Core exchange functions //////
- /// @dev Cancels all orders reated by sender with a salt less than or equal to the specified salt value.
+ /// @dev Cancels all orders created by sender with a salt less than or equal to the specified salt value.
/// @param salt Orders created with a salt less or equal to this value will be cancelled.
function cancelOrdersUpTo(uint256 salt)
external
@@ -108,9 +108,6 @@ contract MixinExchangeCore is
// Compute proportional fill amounts
fillResults = calculateFillResults(order, takerAssetFilledAmount);
- // Settle order
- settleOrder(order, takerAddress, fillResults);
-
// Update exchange internal state
updateFilledState(
order,
@@ -119,6 +116,10 @@ contract MixinExchangeCore is
orderInfo.orderTakerAssetFilledAmount,
fillResults
);
+
+ // Settle order
+ settleOrder(order, takerAddress, fillResults);
+
return fillResults;
}
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinMatchOrders.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinMatchOrders.sol
index ed76287e0..517b743fe 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinMatchOrders.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinMatchOrders.sol
@@ -14,7 +14,6 @@
pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
-import "../../utils/LibBytes/LibBytes.sol";
import "./libs/LibMath.sol";
import "./libs/LibOrder.sol";
import "./libs/LibFillResults.sol";
@@ -25,7 +24,6 @@ import "./mixins/MSettlement.sol";
import "./mixins/MTransactions.sol";
contract MixinMatchOrders is
- LibBytes,
LibMath,
LibExchangeErrors,
MExchangeCore,
@@ -94,14 +92,6 @@ contract MixinMatchOrders is
rightSignature
);
- // Settle matched orders. Succeeds or throws.
- settleMatchedOrders(
- leftOrder,
- rightOrder,
- takerAddress,
- matchedFillResults
- );
-
// Update exchange state
updateFilledState(
leftOrder,
@@ -117,6 +107,14 @@ contract MixinMatchOrders is
rightOrderInfo.orderTakerAssetFilledAmount,
matchedFillResults.right
);
+
+ // Settle matched orders. Succeeds or throws.
+ settleMatchedOrders(
+ leftOrder,
+ rightOrder,
+ takerAddress,
+ matchedFillResults
+ );
return matchedFillResults;
}
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlement.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlement.sol
index 646d3ed58..29a9c87bd 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlement.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlement.sol
@@ -19,6 +19,7 @@
pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
+import "../../utils/LibBytes/LibBytes.sol";
import "./libs/LibMath.sol";
import "./libs/LibFillResults.sol";
import "./libs/LibOrder.sol";
@@ -28,33 +29,26 @@ import "./mixins/MSettlement.sol";
import "./mixins/MAssetProxyDispatcher.sol";
contract MixinSettlement is
+ LibBytes,
LibMath,
LibExchangeErrors,
MMatchOrders,
MSettlement,
MAssetProxyDispatcher
{
- // ZRX metadata used for fee transfers.
+ // ZRX address encoded as a byte array.
// This will be constant throughout the life of the Exchange contract,
// since ZRX will always be transferred via the ERC20 AssetProxy.
- bytes internal ZRX_PROXY_DATA;
+ bytes internal ZRX_ASSET_DATA;
+ uint8 constant ZRX_PROXY_ID = 1;
- /// @dev Gets the ZRX metadata used for fee transfers.
- function zrxProxyData()
- external
- view
- returns (bytes memory)
- {
- return ZRX_PROXY_DATA;
- }
-
- /// TODO: _zrxProxyData should be a constant in production.
+ /// TODO: _zrxAssetData should be a constant in production.
/// @dev Constructor sets the metadata that will be used for paying ZRX fees.
- /// @param _zrxProxyData Byte array containing ERC20 proxy id concatenated with address of ZRX.
- constructor (bytes memory _zrxProxyData)
+ /// @param _zrxAssetData Byte array containing ERC20 proxy id concatenated with address of ZRX.
+ constructor (bytes memory _zrxAssetData)
public
{
- ZRX_PROXY_DATA = _zrxProxyData;
+ ZRX_ASSET_DATA = _zrxAssetData;
}
/// @dev Settles an order by transferring assets between counterparties.
@@ -68,26 +62,33 @@ contract MixinSettlement is
)
internal
{
+ uint8 makerAssetProxyId = uint8(popLastByte(order.makerAssetData));
+ uint8 takerAssetProxyId = uint8(popLastByte(order.takerAssetData));
+ bytes memory zrxAssetData = ZRX_ASSET_DATA;
dispatchTransferFrom(
order.makerAssetData,
+ makerAssetProxyId,
order.makerAddress,
takerAddress,
fillResults.makerAssetFilledAmount
);
dispatchTransferFrom(
order.takerAssetData,
+ takerAssetProxyId,
takerAddress,
order.makerAddress,
fillResults.takerAssetFilledAmount
);
dispatchTransferFrom(
- ZRX_PROXY_DATA,
+ zrxAssetData,
+ ZRX_PROXY_ID,
order.makerAddress,
order.feeRecipientAddress,
fillResults.makerFeePaid
);
dispatchTransferFrom(
- ZRX_PROXY_DATA,
+ zrxAssetData,
+ ZRX_PROXY_ID,
takerAddress,
order.feeRecipientAddress,
fillResults.takerFeePaid
@@ -107,21 +108,27 @@ contract MixinSettlement is
)
internal
{
+ uint8 leftMakerAssetProxyId = uint8(popLastByte(leftOrder.makerAssetData));
+ uint8 rightMakerAssetProxyId = uint8(popLastByte(rightOrder.makerAssetData));
+ bytes memory zrxAssetData = ZRX_ASSET_DATA;
// Order makers and taker
dispatchTransferFrom(
leftOrder.makerAssetData,
+ leftMakerAssetProxyId,
leftOrder.makerAddress,
rightOrder.makerAddress,
matchedFillResults.right.takerAssetFilledAmount
);
dispatchTransferFrom(
rightOrder.makerAssetData,
+ rightMakerAssetProxyId,
rightOrder.makerAddress,
leftOrder.makerAddress,
matchedFillResults.left.takerAssetFilledAmount
);
dispatchTransferFrom(
leftOrder.makerAssetData,
+ leftMakerAssetProxyId,
leftOrder.makerAddress,
takerAddress,
matchedFillResults.leftMakerAssetSpreadAmount
@@ -129,13 +136,15 @@ contract MixinSettlement is
// Maker fees
dispatchTransferFrom(
- ZRX_PROXY_DATA,
+ zrxAssetData,
+ ZRX_PROXY_ID,
leftOrder.makerAddress,
leftOrder.feeRecipientAddress,
matchedFillResults.left.makerFeePaid
);
dispatchTransferFrom(
- ZRX_PROXY_DATA,
+ zrxAssetData,
+ ZRX_PROXY_ID,
rightOrder.makerAddress,
rightOrder.feeRecipientAddress,
matchedFillResults.right.makerFeePaid
@@ -144,7 +153,8 @@ contract MixinSettlement is
// Taker fees
if (leftOrder.feeRecipientAddress == rightOrder.feeRecipientAddress) {
dispatchTransferFrom(
- ZRX_PROXY_DATA,
+ zrxAssetData,
+ ZRX_PROXY_ID,
takerAddress,
leftOrder.feeRecipientAddress,
safeAdd(
@@ -154,13 +164,15 @@ contract MixinSettlement is
);
} else {
dispatchTransferFrom(
- ZRX_PROXY_DATA,
+ zrxAssetData,
+ ZRX_PROXY_ID,
takerAddress,
leftOrder.feeRecipientAddress,
matchedFillResults.left.takerFeePaid
);
dispatchTransferFrom(
- ZRX_PROXY_DATA,
+ zrxAssetData,
+ ZRX_PROXY_ID,
takerAddress,
rightOrder.feeRecipientAddress,
matchedFillResults.right.takerFeePaid
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidator.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidator.sol
index 48a0c5552..1a556dfe2 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidator.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidator.sol
@@ -93,7 +93,7 @@ contract MixinSignatureValidator is
);
// Pop last byte off of signature byte array.
- SignatureType signatureType = SignatureType(uint8(popByte(signature)));
+ SignatureType signatureType = SignatureType(uint8(popLastByte(signature)));
// Variables are not scoped in Solidity.
uint8 v;
@@ -183,7 +183,7 @@ contract MixinSignatureValidator is
// | 0x14 + x | 1 | Signature type is always "\x06" |
} else if (signatureType == SignatureType.Validator) {
// Pop last 20 bytes off of signature byte array.
- address validator = popAddress(signature);
+ address validator = popLast20Bytes(signature);
// Ensure signer has approved validator.
if (!allowedValidators[signer][validator]) {
return false;
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol
index 0ad0710ce..a7849f4cb 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol
@@ -19,7 +19,6 @@
pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
-import "../../utils/LibBytes/LibBytes.sol";
import "./libs/LibMath.sol";
import "./libs/LibOrder.sol";
import "./libs/LibFillResults.sol";
@@ -27,7 +26,6 @@ import "./libs/LibExchangeErrors.sol";
import "./mixins/MExchangeCore.sol";
contract MixinWrapperFunctions is
- LibBytes,
LibMath,
LibFillResults,
LibExchangeErrors,
@@ -40,7 +38,8 @@ contract MixinWrapperFunctions is
function fillOrKillOrder(
LibOrder.Order memory order,
uint256 takerAssetFillAmount,
- bytes memory signature)
+ bytes memory signature
+ )
public
returns (FillResults memory fillResults)
{
@@ -65,7 +64,8 @@ contract MixinWrapperFunctions is
function fillOrderNoThrow(
LibOrder.Order memory order,
uint256 takerAssetFillAmount,
- bytes memory signature)
+ bytes memory signature
+ )
public
returns (FillResults memory fillResults)
{
@@ -91,12 +91,12 @@ contract MixinWrapperFunctions is
// | | 0x0E0 | | 8. takerFeeAmount |
// | | 0x100 | | 9. expirationTimeSeconds |
// | | 0x120 | | 10. salt |
- // | | 0x140 | | 11. Offset to makerAssetProxyMetadata (*) |
- // | | 0x160 | | 12. Offset to takerAssetProxyMetadata (*) |
- // | | 0x180 | 32 | makerAssetProxyMetadata Length |
- // | | 0x1A0 | ** | makerAssetProxyMetadata Contents |
- // | | 0x1C0 | 32 | takerAssetProxyMetadata Length |
- // | | 0x1E0 | ** | takerAssetProxyMetadata Contents |
+ // | | 0x140 | | 11. Offset to makerAssetData (*) |
+ // | | 0x160 | | 12. Offset to takerAssetData (*) |
+ // | | 0x180 | 32 | makerAssetData Length |
+ // | | 0x1A0 | ** | makerAssetData Contents |
+ // | | 0x1C0 | 32 | takerAssetData Length |
+ // | | 0x1E0 | ** | takerAssetData Contents |
// | | 0x200 | 32 | signature Length |
// | | 0x220 | ** | signature Contents |
@@ -163,43 +163,45 @@ contract MixinWrapperFunctions is
mstore(add(dataAreaEnd, 0xE0), mload(add(sourceOffset, 0xE0))) // takerFeeAmount
mstore(add(dataAreaEnd, 0x100), mload(add(sourceOffset, 0x100))) // expirationTimeSeconds
mstore(add(dataAreaEnd, 0x120), mload(add(sourceOffset, 0x120))) // salt
- mstore(add(dataAreaEnd, 0x140), mload(add(sourceOffset, 0x140))) // Offset to makerAssetProxyMetadata
- mstore(add(dataAreaEnd, 0x160), mload(add(sourceOffset, 0x160))) // Offset to takerAssetProxyMetadata
+ mstore(add(dataAreaEnd, 0x140), mload(add(sourceOffset, 0x140))) // Offset to makerAssetData
+ mstore(add(dataAreaEnd, 0x160), mload(add(sourceOffset, 0x160))) // Offset to takerAssetData
dataAreaEnd := add(dataAreaEnd, 0x180)
sourceOffset := add(sourceOffset, 0x180)
- // Write offset to <order.makerAssetProxyMetadata>
+ // Write offset to <order.makerAssetData>
mstore(add(dataAreaStart, mul(10, 0x20)), sub(dataAreaEnd, dataAreaStart))
- // Calculate length of <order.makerAssetProxyMetadata>
+ // Calculate length of <order.makerAssetData>
+ sourceOffset := mload(add(order, 0x140)) // makerAssetData
arrayLenBytes := mload(sourceOffset)
sourceOffset := add(sourceOffset, 0x20)
arrayLenWords := div(add(arrayLenBytes, 0x1F), 0x20)
- // Write length of <order.makerAssetProxyMetadata>
+ // Write length of <order.makerAssetData>
mstore(dataAreaEnd, arrayLenBytes)
dataAreaEnd := add(dataAreaEnd, 0x20)
- // Write contents of <order.makerAssetProxyMetadata>
+ // Write contents of <order.makerAssetData>
for {let i := 0} lt(i, arrayLenWords) {i := add(i, 1)} {
mstore(dataAreaEnd, mload(sourceOffset))
dataAreaEnd := add(dataAreaEnd, 0x20)
sourceOffset := add(sourceOffset, 0x20)
}
- // Write offset to <order.takerAssetProxyMetadata>
+ // Write offset to <order.takerAssetData>
mstore(add(dataAreaStart, mul(11, 0x20)), sub(dataAreaEnd, dataAreaStart))
- // Calculate length of <order.takerAssetProxyMetadata>
+ // Calculate length of <order.takerAssetData>
+ sourceOffset := mload(add(order, 0x160)) // takerAssetData
arrayLenBytes := mload(sourceOffset)
sourceOffset := add(sourceOffset, 0x20)
arrayLenWords := div(add(arrayLenBytes, 0x1F), 0x20)
- // Write length of <order.takerAssetProxyMetadata>
+ // Write length of <order.takerAssetData>
mstore(dataAreaEnd, arrayLenBytes)
dataAreaEnd := add(dataAreaEnd, 0x20)
- // Write contents of <order.takerAssetProxyMetadata>
+ // Write contents of <order.takerAssetData>
for {let i := 0} lt(i, arrayLenWords) {i := add(i, 1)} {
mstore(dataAreaEnd, mload(sourceOffset))
dataAreaEnd := add(dataAreaEnd, 0x20)
@@ -264,7 +266,8 @@ contract MixinWrapperFunctions is
function batchFillOrders(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
- bytes[] memory signatures)
+ bytes[] memory signatures
+ )
public
{
for (uint256 i = 0; i < orders.length; i++) {
@@ -283,7 +286,8 @@ contract MixinWrapperFunctions is
function batchFillOrKillOrders(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
- bytes[] memory signatures)
+ bytes[] memory signatures
+ )
public
{
for (uint256 i = 0; i < orders.length; i++) {
@@ -303,7 +307,8 @@ contract MixinWrapperFunctions is
function batchFillOrdersNoThrow(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
- bytes[] memory signatures)
+ bytes[] memory signatures
+ )
public
{
for (uint256 i = 0; i < orders.length; i++) {
@@ -323,18 +328,18 @@ contract MixinWrapperFunctions is
function marketSellOrders(
LibOrder.Order[] memory orders,
uint256 takerAssetFillAmount,
- bytes[] memory signatures)
+ bytes[] memory signatures
+ )
public
returns (FillResults memory totalFillResults)
{
+ bytes memory takerAssetData = orders[0].takerAssetData;
+
for (uint256 i = 0; i < orders.length; i++) {
- // Token being sold by taker must be the same for each order
- // TODO: optimize by only using takerAssetData for first order.
- require(
- areBytesEqual(orders[i].takerAssetData, orders[0].takerAssetData),
- ASSET_DATA_MISMATCH
- );
+ // We assume that asset being sold by taker is the same for each order.
+ // Rather than passing this in as calldata, we use the takerAssetData from the first order in all later orders.
+ orders[i].takerAssetData = takerAssetData;
// Calculate the remaining amount of takerAsset to sell
uint256 remainingTakerAssetFillAmount = safeSub(takerAssetFillAmount, totalFillResults.takerAssetFilledAmount);
@@ -346,6 +351,14 @@ contract MixinWrapperFunctions is
signatures[i]
);
+ // HACK: the proxyId is "popped" from the byte array before a fill is settled
+ // by subtracting from the length of the array. Since the popped byte is
+ // still in memory, we can "unpop" it by incrementing the length of the byte array.
+ assembly {
+ let len := mload(takerAssetData)
+ mstore(takerAssetData, add(len, 1))
+ }
+
// Update amounts filled and fees paid by maker and taker
addFillResults(totalFillResults, singleFillResults);
@@ -366,18 +379,18 @@ contract MixinWrapperFunctions is
function marketSellOrdersNoThrow(
LibOrder.Order[] memory orders,
uint256 takerAssetFillAmount,
- bytes[] memory signatures)
+ bytes[] memory signatures
+ )
public
returns (FillResults memory totalFillResults)
{
+ bytes memory takerAssetData = orders[0].takerAssetData;
+
for (uint256 i = 0; i < orders.length; i++) {
- // Token being sold by taker must be the same for each order
- // TODO: optimize by only using takerAssetData for first order.
- require(
- areBytesEqual(orders[i].takerAssetData, orders[0].takerAssetData),
- ASSET_DATA_MISMATCH
- );
+ // We assume that asset being sold by taker is the same for each order.
+ // Rather than passing this in as calldata, we use the takerAssetData from the first order in all later orders.
+ orders[i].takerAssetData = takerAssetData;
// Calculate the remaining amount of takerAsset to sell
uint256 remainingTakerAssetFillAmount = safeSub(takerAssetFillAmount, totalFillResults.takerAssetFilledAmount);
@@ -408,18 +421,18 @@ contract MixinWrapperFunctions is
function marketBuyOrders(
LibOrder.Order[] memory orders,
uint256 makerAssetFillAmount,
- bytes[] memory signatures)
+ bytes[] memory signatures
+ )
public
returns (FillResults memory totalFillResults)
{
+ bytes memory makerAssetData = orders[0].makerAssetData;
+
for (uint256 i = 0; i < orders.length; i++) {
- // Token being bought by taker must be the same for each order
- // TODO: optimize by only using makerAssetData for first order.
- require(
- areBytesEqual(orders[i].makerAssetData, orders[0].makerAssetData),
- ASSET_DATA_MISMATCH
- );
+ // We assume that asset being bought by taker is the same for each order.
+ // Rather than passing this in as calldata, we copy the makerAssetData from the first order onto all later orders.
+ orders[i].makerAssetData = makerAssetData;
// Calculate the remaining amount of makerAsset to buy
uint256 remainingMakerAssetFillAmount = safeSub(makerAssetFillAmount, totalFillResults.makerAssetFilledAmount);
@@ -439,6 +452,14 @@ contract MixinWrapperFunctions is
signatures[i]
);
+ // HACK: the proxyId is "popped" from the byte array before a fill is settled
+ // by subtracting from the length of the array. Since the popped byte is
+ // still in memory, we can "unpop" it by incrementing the length of the byte array.
+ assembly {
+ let len := mload(makerAssetData)
+ mstore(makerAssetData, add(len, 1))
+ }
+
// Update amounts filled and fees paid by maker and taker
addFillResults(totalFillResults, singleFillResults);
@@ -459,18 +480,18 @@ contract MixinWrapperFunctions is
function marketBuyOrdersNoThrow(
LibOrder.Order[] memory orders,
uint256 makerAssetFillAmount,
- bytes[] memory signatures)
+ bytes[] memory signatures
+ )
public
returns (FillResults memory totalFillResults)
{
+ bytes memory makerAssetData = orders[0].makerAssetData;
+
for (uint256 i = 0; i < orders.length; i++) {
- // Token being bought by taker must be the same for each order
- // TODO: optimize by only using makerAssetData for first order.
- require(
- areBytesEqual(orders[i].makerAssetData, orders[0].makerAssetData),
- ASSET_DATA_MISMATCH
- );
+ // We assume that asset being bought by taker is the same for each order.
+ // Rather than passing this in as calldata, we copy the makerAssetData from the first order onto all later orders.
+ orders[i].makerAssetData = makerAssetData;
// Calculate the remaining amount of makerAsset to buy
uint256 remainingMakerAssetFillAmount = safeSub(makerAssetFillAmount, totalFillResults.makerAssetFilledAmount);
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IAssetProxyDispatcher.sol b/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IAssetProxyDispatcher.sol
index 3ce5ef157..2c331dc34 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IAssetProxyDispatcher.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IAssetProxyDispatcher.sol
@@ -28,7 +28,8 @@ contract IAssetProxyDispatcher {
function registerAssetProxy(
uint8 assetProxyId,
address newAssetProxy,
- address oldAssetProxy)
+ address oldAssetProxy
+ )
external;
/// @dev Gets an asset proxy.
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/ITransactions.sol b/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/ITransactions.sol
index d973bf001..2f9a5bc7c 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/ITransactions.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/ITransactions.sol
@@ -28,6 +28,7 @@ contract ITransactions {
uint256 salt,
address signer,
bytes data,
- bytes signature)
+ bytes signature
+ )
external;
}
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IWrapperFunctions.sol b/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IWrapperFunctions.sol
index 8682b394a..acd4f359c 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IWrapperFunctions.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IWrapperFunctions.sol
@@ -33,7 +33,8 @@ contract IWrapperFunctions is
function fillOrKillOrder(
LibOrder.Order memory order,
uint256 takerAssetFillAmount,
- bytes memory signature)
+ bytes memory signature
+ )
public
returns (LibFillResults.FillResults memory fillResults);
@@ -46,7 +47,8 @@ contract IWrapperFunctions is
function fillOrderNoThrow(
LibOrder.Order memory order,
uint256 takerAssetFillAmount,
- bytes memory signature)
+ bytes memory signature
+ )
public
returns (LibFillResults.FillResults memory fillResults);
@@ -57,7 +59,8 @@ contract IWrapperFunctions is
function batchFillOrders(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
- bytes[] memory signatures)
+ bytes[] memory signatures
+ )
public;
/// @dev Synchronously executes multiple calls of fillOrKill.
@@ -67,7 +70,8 @@ contract IWrapperFunctions is
function batchFillOrKillOrders(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
- bytes[] memory signatures)
+ bytes[] memory signatures
+ )
public;
/// @dev Fills an order with specified parameters and ECDSA signature.
@@ -78,7 +82,8 @@ contract IWrapperFunctions is
function batchFillOrdersNoThrow(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
- bytes[] memory signatures)
+ bytes[] memory signatures
+ )
public;
/// @dev Synchronously executes multiple calls of fillOrder until total amount of takerAsset is sold by taker.
@@ -89,7 +94,8 @@ contract IWrapperFunctions is
function marketSellOrders(
LibOrder.Order[] memory orders,
uint256 takerAssetFillAmount,
- bytes[] memory signatures)
+ bytes[] memory signatures
+ )
public
returns (LibFillResults.FillResults memory totalFillResults);
@@ -102,7 +108,8 @@ contract IWrapperFunctions is
function marketSellOrdersNoThrow(
LibOrder.Order[] memory orders,
uint256 takerAssetFillAmount,
- bytes[] memory signatures)
+ bytes[] memory signatures
+ )
public
returns (LibFillResults.FillResults memory totalFillResults);
@@ -114,7 +121,8 @@ contract IWrapperFunctions is
function marketBuyOrders(
LibOrder.Order[] memory orders,
uint256 makerAssetFillAmount,
- bytes[] memory signatures)
+ bytes[] memory signatures
+ )
public
returns (LibFillResults.FillResults memory totalFillResults);
@@ -127,7 +135,8 @@ contract IWrapperFunctions is
function marketBuyOrdersNoThrow(
LibOrder.Order[] memory orders,
uint256 makerAssetFillAmount,
- bytes[] memory signatures)
+ bytes[] memory signatures
+ )
public
returns (LibFillResults.FillResults memory totalFillResults);
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibExchangeErrors.sol b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibExchangeErrors.sol
index 2b4bbeec4..48dd0f8be 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibExchangeErrors.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibExchangeErrors.sol
@@ -25,7 +25,6 @@ contract LibExchangeErrors {
string constant INVALID_TAKER = "INVALID_TAKER"; // Invalid takerAddress.
string constant INVALID_SENDER = "INVALID_SENDER"; // Invalid `msg.sender`.
string constant INVALID_ORDER_SIGNATURE = "INVALID_ORDER_SIGNATURE"; // Signature validation failed.
- string constant ASSET_DATA_MISMATCH = "ASSET_DATA_MISMATCH"; // Asset data must be the same for each order.
/// fillOrder validation errors ///
string constant INVALID_TAKER_AMOUNT = "INVALID_TAKER_AMOUNT"; // takerAssetFillAmount cannot equal 0.
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MAssetProxyDispatcher.sol b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MAssetProxyDispatcher.sol
index 87c5f6361..d16a830f4 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MAssetProxyDispatcher.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MAssetProxyDispatcher.sol
@@ -33,12 +33,14 @@ contract MAssetProxyDispatcher is
);
/// @dev Forwards arguments to assetProxy and calls `transferFrom`. Either succeeds or throws.
- /// @param assetMetadata Byte array encoded for the respective asset proxy.
+ /// @param assetData Byte array encoded for the respective asset proxy.
+ /// @param assetProxyId Id of assetProxy to dispach to.
/// @param from Address to transfer token from.
/// @param to Address to transfer token to.
/// @param amount Amount of token to transfer.
function dispatchTransferFrom(
- bytes memory assetMetadata,
+ bytes memory assetData,
+ uint8 assetProxyId,
address from,
address to,
uint256 amount
diff --git a/packages/contracts/src/contracts/current/test/DummyERC20Token/DummyERC20Token.sol b/packages/contracts/src/contracts/current/test/DummyERC20Token/DummyERC20Token.sol
index 0c7b18c0c..b2fe2df06 100644
--- a/packages/contracts/src/contracts/current/test/DummyERC20Token/DummyERC20Token.sol
+++ b/packages/contracts/src/contracts/current/test/DummyERC20Token/DummyERC20Token.sol
@@ -31,7 +31,8 @@ contract DummyERC20Token is Mintable, Ownable {
string _name,
string _symbol,
uint256 _decimals,
- uint256 _totalSupply)
+ uint256 _totalSupply
+ )
public
{
name = _name;
diff --git a/packages/contracts/src/contracts/current/test/DummyERC721Receiver/DummyERC721Receiver.sol b/packages/contracts/src/contracts/current/test/DummyERC721Receiver/DummyERC721Receiver.sol
new file mode 100644
index 000000000..c584d0b54
--- /dev/null
+++ b/packages/contracts/src/contracts/current/test/DummyERC721Receiver/DummyERC721Receiver.sol
@@ -0,0 +1,63 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 Smart Contract Solutions, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+pragma solidity ^0.4.24;
+
+import "../../tokens/ERC721Token/IERC721Receiver.sol";
+
+contract DummyERC721Receiver is
+ IERC721Receiver
+{
+
+ event TokenReceived(
+ address from,
+ uint256 tokenId,
+ bytes data
+ );
+
+ /**
+ * @notice Handle the receipt of an NFT
+ * @dev The ERC721 smart contract calls this function on the recipient
+ * after a `safetransfer`. This function MAY throw to revert and reject the
+ * transfer. This function MUST use 50,000 gas or less. Return of other
+ * than the magic value MUST result in the transaction being reverted.
+ * Note: the contract address is always the message sender.
+ * @param _from The sending address
+ * @param _tokenId The NFT identifier which is being transfered
+ * @param _data Additional data with no specified format
+ * @return `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`
+ */
+ function onERC721Received(
+ address _from,
+ uint256 _tokenId,
+ bytes _data
+ )
+ public
+ returns (bytes4)
+ {
+ emit TokenReceived(_from, _tokenId, _data);
+ return ERC721_RECEIVED;
+ }
+}
diff --git a/packages/contracts/src/contracts/current/test/DummyERC721Token/DummyERC721Token.sol b/packages/contracts/src/contracts/current/test/DummyERC721Token/DummyERC721Token.sol
index 369a2950d..5503eb2f2 100644
--- a/packages/contracts/src/contracts/current/test/DummyERC721Token/DummyERC721Token.sol
+++ b/packages/contracts/src/contracts/current/test/DummyERC721Token/DummyERC721Token.sol
@@ -34,7 +34,8 @@ contract DummyERC721Token is
*/
constructor (
string name,
- string symbol)
+ string symbol
+ )
public
ERC721Token(name, symbol)
{}
diff --git a/packages/contracts/src/contracts/current/test/TestAssetDataDecoders/TestAssetDataDecoders.sol b/packages/contracts/src/contracts/current/test/TestAssetDataDecoders/TestAssetDataDecoders.sol
new file mode 100644
index 000000000..5987291d2
--- /dev/null
+++ b/packages/contracts/src/contracts/current/test/TestAssetDataDecoders/TestAssetDataDecoders.sol
@@ -0,0 +1,56 @@
+/*
+
+ Copyright 2018 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.4.24;
+pragma experimental ABIEncoderV2;
+
+import "../../protocol/AssetProxy/ERC20Proxy.sol";
+import "../../protocol/AssetProxy/ERC721Proxy.sol";
+
+contract TestAssetDataDecoders is
+ ERC721Proxy
+{
+ /// @dev Decodes ERC721 Asset data.
+ /// @param assetData Encoded byte array.
+ /// @return proxyId Intended ERC721 proxy id.
+ /// @return token ERC721 token address.
+ /// @return tokenId ERC721 token id.
+ /// @return receiverData Additional data with no specific format, which
+ /// is passed to the receiving contract's onERC721Received.
+ function publicDecodeERC721Data(bytes memory assetData)
+ public
+ pure
+ returns (
+ address token,
+ uint256 tokenId,
+ bytes memory receiverData
+ )
+ {
+ (
+ token,
+ tokenId,
+ receiverData
+ ) = decodeERC721AssetData(assetData);
+
+ return (
+ token,
+ tokenId,
+ receiverData
+ );
+ }
+}
diff --git a/packages/contracts/src/contracts/current/test/TestAssetProxyDispatcher/TestAssetProxyDispatcher.sol b/packages/contracts/src/contracts/current/test/TestAssetProxyDispatcher/TestAssetProxyDispatcher.sol
index 11ca0617d..d469a07f0 100644
--- a/packages/contracts/src/contracts/current/test/TestAssetProxyDispatcher/TestAssetProxyDispatcher.sol
+++ b/packages/contracts/src/contracts/current/test/TestAssetProxyDispatcher/TestAssetProxyDispatcher.sol
@@ -23,12 +23,13 @@ import "../../protocol/Exchange/MixinAssetProxyDispatcher.sol";
contract TestAssetProxyDispatcher is MixinAssetProxyDispatcher {
function publicDispatchTransferFrom(
- bytes memory assetMetadata,
+ bytes memory assetData,
+ uint8 assetProxyId,
address from,
address to,
uint256 amount)
public
{
- dispatchTransferFrom(assetMetadata, from, to, amount);
+ dispatchTransferFrom(assetData, assetProxyId, from, to, amount);
}
}
diff --git a/packages/contracts/src/contracts/current/test/TestLibBytes/TestLibBytes.sol b/packages/contracts/src/contracts/current/test/TestLibBytes/TestLibBytes.sol
index 69554605d..6f1898acd 100644
--- a/packages/contracts/src/contracts/current/test/TestLibBytes/TestLibBytes.sol
+++ b/packages/contracts/src/contracts/current/test/TestLibBytes/TestLibBytes.sol
@@ -28,24 +28,24 @@ contract TestLibBytes is
/// @dev Pops the last byte off of a byte array by modifying its length.
/// @param b Byte array that will be modified.
/// @return The byte that was popped off.
- function publicPopByte(bytes memory b)
+ function publicPopLastByte(bytes memory b)
public
pure
returns (bytes memory, bytes1 result)
{
- result = popByte(b);
+ result = popLastByte(b);
return (b, result);
}
/// @dev Pops the last 20 bytes off of a byte array by modifying its length.
/// @param b Byte array that will be modified.
/// @return The 20 byte address that was popped off.
- function publicPopAddress(bytes memory b)
+ function publicPopLast20Bytes(bytes memory b)
public
pure
returns (bytes memory, address result)
{
- result = popAddress(b);
+ result = popLast20Bytes(b);
return (b, result);
}
@@ -62,13 +62,29 @@ contract TestLibBytes is
return equal;
}
+ /// @dev Performs a deep copy of a byte array onto another byte array of greater than or equal length.
+ /// @param dest Byte array that will be overwritten with source bytes.
+ /// @param source Byte array to copy onto dest bytes.
+ function publicDeepCopyBytes(
+ bytes memory dest,
+ bytes memory source
+ )
+ public
+ pure
+ returns (bytes memory)
+ {
+ deepCopyBytes(dest, source);
+ return dest;
+ }
+
/// @dev Reads an address from a position in a byte array.
/// @param b Byte array containing an address.
/// @param index Index in byte array of address.
/// @return address from byte array.
function publicReadAddress(
bytes memory b,
- uint256 index)
+ uint256 index
+ )
public
pure
returns (address result)
@@ -84,7 +100,8 @@ contract TestLibBytes is
function publicWriteAddress(
bytes memory b,
uint256 index,
- address input)
+ address input
+ )
public
pure
returns (bytes memory)
@@ -99,7 +116,8 @@ contract TestLibBytes is
/// @return bytes32 value from byte array.
function publicReadBytes32(
bytes memory b,
- uint256 index)
+ uint256 index
+ )
public
pure
returns (bytes32 result)
@@ -115,7 +133,8 @@ contract TestLibBytes is
function publicWriteBytes32(
bytes memory b,
uint256 index,
- bytes32 input)
+ bytes32 input
+ )
public
pure
returns (bytes memory)
@@ -130,7 +149,8 @@ contract TestLibBytes is
/// @return uint256 value from byte array.
function publicReadUint256(
bytes memory b,
- uint256 index)
+ uint256 index
+ )
public
pure
returns (uint256 result)
@@ -146,7 +166,8 @@ contract TestLibBytes is
function publicWriteUint256(
bytes memory b,
uint256 index,
- uint256 input)
+ uint256 input
+ )
public
pure
returns (bytes memory)
@@ -166,4 +187,38 @@ contract TestLibBytes is
result = readFirst4(b);
return result;
}
+
+ /// @dev Reads nested bytes from a specific position.
+ /// @param b Byte array containing nested bytes.
+ /// @param index Index of nested bytes.
+ /// @return result Nested bytes.
+ function publicReadBytes(
+ bytes memory b,
+ uint256 index
+ )
+ public
+ pure
+ returns (bytes memory result)
+ {
+ result = readBytes(b, index);
+ return result;
+ }
+
+ /// @dev Inserts bytes at a specific position in a byte array.
+ /// @param b Byte array to insert <input> into.
+ /// @param index Index in byte array of <input>.
+ /// @param input bytes to insert.
+ /// @return b Updated input byte array
+ function publicWriteBytes(
+ bytes memory b,
+ uint256 index,
+ bytes memory input
+ )
+ public
+ pure
+ returns (bytes memory)
+ {
+ writeBytes(b, index, input);
+ return b;
+ }
}
diff --git a/packages/contracts/src/contracts/current/test/TestLibMem/TestLibMem.sol b/packages/contracts/src/contracts/current/test/TestLibMem/TestLibMem.sol
new file mode 100644
index 000000000..b7e2e06b8
--- /dev/null
+++ b/packages/contracts/src/contracts/current/test/TestLibMem/TestLibMem.sol
@@ -0,0 +1,56 @@
+/*
+
+ Copyright 2018 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.4.24;
+
+import "../../utils/LibMem/LibMem.sol";
+
+contract TestLibMem is
+ LibMem
+{
+
+ /// @dev Copies a block of memory from one location to another.
+ /// @param mem Memory contents we want to apply memCopy to
+ /// @param dest Destination offset into <mem>.
+ /// @param source Source offset into <mem>.
+ /// @param length Length of bytes to copy from <source> to <dest>
+ /// @return mem Memory contents after calling memCopy.
+ function testMemcpy(
+ bytes mem,
+ uint256 dest,
+ uint256 source,
+ uint256 length
+ )
+ public // not external, we need input in memory
+ pure
+ returns (bytes)
+ {
+ // Sanity check. Overflows are not checked.
+ require(source + length <= mem.length);
+ require(dest + length <= mem.length);
+
+ // Get pointer to memory contents
+ uint256 offset = getMemAddress(mem) + 32;
+
+ // Execute memCopy adjusted for memory array location
+ memCopy(offset + dest, offset + source, length);
+
+ // Return modified memory contents
+ return mem;
+ }
+}
diff --git a/packages/contracts/src/contracts/current/utils/LibBytes/LibBytes.sol b/packages/contracts/src/contracts/current/utils/LibBytes/LibBytes.sol
index df2221c93..10d7ce41a 100644
--- a/packages/contracts/src/contracts/current/utils/LibBytes/LibBytes.sol
+++ b/packages/contracts/src/contracts/current/utils/LibBytes/LibBytes.sol
@@ -18,31 +18,36 @@
pragma solidity ^0.4.24;
-contract LibBytes {
+import "../LibMem/LibMem.sol";
+
+contract LibBytes is
+ LibMem
+{
// Revert reasons
- string constant GT_ZERO_LENGTH_REQUIRED = "Length must be greater than 0.";
- string constant GTE_4_LENGTH_REQUIRED = "Length must be greater than or equal to 4.";
- string constant GTE_20_LENGTH_REQUIRED = "Length must be greater than or equal to 20.";
- string constant GTE_32_LENGTH_REQUIRED = "Length must be greater than or equal to 32.";
- string constant INDEX_OUT_OF_BOUNDS = "Specified array index is out of bounds.";
+ string constant GREATER_THAN_ZERO_LENGTH_REQUIRED = "GREATER_THAN_ZERO_LENGTH_REQUIRED";
+ string constant GREATER_OR_EQUAL_TO_4_LENGTH_REQUIRED = "GREATER_OR_EQUAL_TO_4_LENGTH_REQUIRED";
+ string constant GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED = "GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED";
+ string constant GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED = "GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED";
+ string constant GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED = "GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED";
+ string constant GREATER_OR_EQUAL_TO_SOURCE_BYTES_LENGTH_REQUIRED = "GREATER_OR_EQUAL_TO_SOURCE_BYTES_LENGTH_REQUIRED";
/// @dev Pops the last byte off of a byte array by modifying its length.
/// @param b Byte array that will be modified.
/// @return The byte that was popped off.
- function popByte(bytes memory b)
+ function popLastByte(bytes memory b)
internal
pure
returns (bytes1 result)
{
require(
b.length > 0,
- GT_ZERO_LENGTH_REQUIRED
+ GREATER_THAN_ZERO_LENGTH_REQUIRED
);
// Store last byte.
result = b[b.length - 1];
-
+
assembly {
// Decrement length of byte array.
let newLen := sub(mload(b), 1)
@@ -54,14 +59,14 @@ contract LibBytes {
/// @dev Pops the last 20 bytes off of a byte array by modifying its length.
/// @param b Byte array that will be modified.
/// @return The 20 byte address that was popped off.
- function popAddress(bytes memory b)
+ function popLast20Bytes(bytes memory b)
internal
pure
returns (address result)
{
require(
b.length >= 20,
- GTE_20_LENGTH_REQUIRED
+ GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED
);
// Store last 20 bytes.
@@ -75,41 +80,6 @@ contract LibBytes {
return result;
}
- /// @dev Tests equality of two byte arrays.
- /// @param lhs First byte array to compare.
- /// @param rhs Second byte array to compare.
- /// @return True if arrays are the same. False otherwise.
- function areBytesEqual(
- bytes memory lhs,
- bytes memory rhs
- )
- internal
- pure
- returns (bool equal)
- {
- assembly {
- // Get the number of words occupied by <lhs>
- let lenFullWords := div(add(mload(lhs), 0x1F), 0x20)
-
- // Add 1 to the number of words, to account for the length field
- lenFullWords := add(lenFullWords, 0x1)
-
- // Test equality word-by-word.
- // Terminates early if there is a mismatch.
- for {let i := 0} lt(i, lenFullWords) {i := add(i, 1)} {
- let lhsWord := mload(add(lhs, mul(i, 0x20)))
- let rhsWord := mload(add(rhs, mul(i, 0x20)))
- equal := eq(lhsWord, rhsWord)
- if eq(equal, 0) {
- // Break
- i := lenFullWords
- }
- }
- }
-
- return equal;
- }
-
/// @dev Reads an address from a position in a byte array.
/// @param b Byte array containing an address.
/// @param index Index in byte array of address.
@@ -124,8 +94,8 @@ contract LibBytes {
{
require(
b.length >= index + 20, // 20 is length of address
- GTE_20_LENGTH_REQUIRED
- );
+ GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED
+ );
// Add offset to index:
// 1. Arrays are prefixed by 32-byte length parameter (add 32 to index)
@@ -156,8 +126,8 @@ contract LibBytes {
{
require(
b.length >= index + 20, // 20 is length of address
- GTE_20_LENGTH_REQUIRED
- );
+ GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED
+ );
// Add offset to index:
// 1. Arrays are prefixed by 32-byte length parameter (add 32 to index)
@@ -195,7 +165,7 @@ contract LibBytes {
{
require(
b.length >= index + 32,
- GTE_32_LENGTH_REQUIRED
+ GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED
);
// Arrays are prefixed by a 256 bit length parameter
@@ -222,7 +192,7 @@ contract LibBytes {
{
require(
b.length >= index + 32,
- GTE_32_LENGTH_REQUIRED
+ GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED
);
// Arrays are prefixed by a 256 bit length parameter
@@ -274,11 +244,130 @@ contract LibBytes {
{
require(
b.length >= 4,
- GTE_4_LENGTH_REQUIRED
+ GREATER_OR_EQUAL_TO_4_LENGTH_REQUIRED
);
assembly {
result := mload(add(b, 32))
}
return result;
}
+
+ /// @dev Reads nested bytes from a specific position.
+ /// @param b Byte array containing nested bytes.
+ /// @param index Index of nested bytes.
+ /// @return result Nested bytes.
+ function readBytes(
+ bytes memory b,
+ uint256 index
+ )
+ internal
+ pure
+ returns (bytes memory result)
+ {
+ // Read length of nested bytes
+ uint256 nestedBytesLength = readUint256(b, index);
+ index += 32;
+
+ // Assert length of <b> is valid, given
+ // length of nested bytes
+ require(
+ b.length >= index + nestedBytesLength,
+ GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED
+ );
+
+ // Allocate memory and copy value to result
+ result = new bytes(nestedBytesLength);
+ memCopy(
+ getMemAddress(result) + 32, // +32 skips array length
+ getMemAddress(b) + index + 32,
+ nestedBytesLength
+ );
+
+ return result;
+ }
+
+ /// @dev Inserts bytes at a specific position in a byte array.
+ /// @param b Byte array to insert <input> into.
+ /// @param index Index in byte array of <input>.
+ /// @param input bytes to insert.
+ function writeBytes(
+ bytes memory b,
+ uint256 index,
+ bytes memory input
+ )
+ internal
+ pure
+ {
+ // Assert length of <b> is valid, given
+ // length of input
+ require(
+ b.length >= index + 32 /* 32 bytes to store length */ + input.length,
+ GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED
+ );
+
+ // Copy <input> into <b>
+ memCopy(
+ getMemAddress(b) + 32 + index, // +32 to skip length of <b>
+ getMemAddress(input), // includes length of <input>
+ input.length + 32 // +32 bytes to store <input> length
+ );
+ }
+
+ /// @dev Tests equality of two byte arrays.
+ /// @param lhs First byte array to compare.
+ /// @param rhs Second byte array to compare.
+ /// @return True if arrays are the same. False otherwise.
+ function areBytesEqual(
+ bytes memory lhs,
+ bytes memory rhs
+ )
+ internal
+ pure
+ returns (bool equal)
+ {
+ assembly {
+ // Get the number of words occupied by <lhs>
+ let lenFullWords := div(add(mload(lhs), 0x1F), 0x20)
+
+ // Add 1 to the number of words, to account for the length field
+ lenFullWords := add(lenFullWords, 0x1)
+
+ // Test equality word-by-word.
+ // Terminates early if there is a mismatch.
+ for {let i := 0} lt(i, lenFullWords) {i := add(i, 1)} {
+ let lhsWord := mload(add(lhs, mul(i, 0x20)))
+ let rhsWord := mload(add(rhs, mul(i, 0x20)))
+ equal := eq(lhsWord, rhsWord)
+ if eq(equal, 0) {
+ // Break
+ i := lenFullWords
+ }
+ }
+ }
+
+ return equal;
+ }
+
+ /// @dev Performs a deep copy of a byte array onto another byte array of greater than or equal length.
+ /// @param dest Byte array that will be overwritten with source bytes.
+ /// @param source Byte array to copy onto dest bytes.
+ function deepCopyBytes(
+ bytes memory dest,
+ bytes memory source
+ )
+ internal
+ pure
+ {
+ uint256 sourceLen = source.length;
+ // Dest length must be >= source length, or some bytes would not be copied.
+ require(
+ dest.length >= sourceLen,
+ GREATER_OR_EQUAL_TO_SOURCE_BYTES_LENGTH_REQUIRED
+ );
+ memCopy(
+ getMemAddress(dest) + 32, // +32 to skip length of <dest>
+ getMemAddress(source) + 32, // +32 to skip length of <source>
+ sourceLen
+ );
+ }
}
diff --git a/packages/contracts/src/contracts/current/utils/LibMem/LibMem.sol b/packages/contracts/src/contracts/current/utils/LibMem/LibMem.sol
new file mode 100644
index 000000000..6afb9973a
--- /dev/null
+++ b/packages/contracts/src/contracts/current/utils/LibMem/LibMem.sol
@@ -0,0 +1,140 @@
+/*
+
+ Copyright 2018 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.4.24;
+
+contract LibMem
+{
+
+ /// @dev Gets the memory address for a byte array.
+ /// @param input Byte array to lookup.
+ /// @return memoryAddress Memory address of byte array.
+ function getMemAddress(bytes memory input)
+ internal
+ pure
+ returns (uint256 memoryAddress)
+ {
+ assembly {
+ memoryAddress := input
+ }
+ return memoryAddress;
+ }
+
+ /// @dev Copies `length` bytes from memory location `source` to `dest`.
+ /// @param dest memory address to copy bytes to.
+ /// @param source memory address to copy bytes from.
+ /// @param length number of bytes to copy.
+ function memCopy(
+ uint256 dest,
+ uint256 source,
+ uint256 length
+ )
+ internal
+ pure
+ {
+ if (length < 32) {
+ // Handle a partial word by reading destination and masking
+ // off the bits we are interested in.
+ // This correctly handles overlap, zero lengths and source == dest
+ assembly {
+ let mask := sub(exp(256, sub(32, length)), 1)
+ let s := and(mload(source), not(mask))
+ let d := and(mload(dest), mask)
+ mstore(dest, or(s, d))
+ }
+ } else {
+ // Skip the O(length) loop when source == dest.
+ if (source == dest) {
+ return;
+ }
+
+ // For large copies we copy whole words at a time. The final
+ // word is aligned to the end of the range (instead of after the
+ // previous) to handle partial words. So a copy will look like this:
+ //
+ // ####
+ // ####
+ // ####
+ // ####
+ //
+ // We handle overlap in the source and destination range by
+ // changing the copying direction. This prevents us from
+ // overwriting parts of source that we still need to copy.
+ //
+ // This correctly handles source == dest
+ //
+ if (source > dest) {
+ assembly {
+ // Record the total number of full words to copy
+ let nWords := div(length, 32)
+
+ // We subtract 32 from `sEnd` and `dEnd` because it
+ // is easier to compare with in the loop, and these
+ // are also the addresses we need for copying the
+ // last bytes.
+ length := sub(length, 32)
+ let sEnd := add(source, length)
+ let dEnd := add(dest, length)
+
+ // Remember the last 32 bytes of source
+ // This needs to be done here and not after the loop
+ // because we may have overwritten the last bytes in
+ // source already due to overlap.
+ let last := mload(sEnd)
+
+ // Copy whole words front to back
+ for {let i := 0} lt(i, nWords) {i := add(i, 1)} {
+ mstore(dest, mload(source))
+ source := add(source, 32)
+ dest := add(dest, 32)
+ }
+
+ // Write the last 32 bytes
+ mstore(dEnd, last)
+ }
+ } else {
+ assembly {
+ // Record the total number of full words to copy
+ let nWords := div(length, 32)
+
+ // We subtract 32 from `sEnd` and `dEnd` because those
+ // are the starting points when copying a word at the end.
+ length := sub(length, 32)
+ let sEnd := add(source, length)
+ let dEnd := add(dest, length)
+
+ // Remember the first 32 bytes of source
+ // This needs to be done here and not after the loop
+ // because we may have overwritten the first bytes in
+ // source already due to overlap.
+ let first := mload(source)
+
+ // Copy whole words back to front
+ for {let i := 0} lt(i, nWords) {i := add(i, 1)} {
+ mstore(dEnd, mload(sEnd))
+ sEnd := sub(sEnd, 32)
+ dEnd := sub(dEnd, 32)
+ }
+
+ // Write the first 32 bytes
+ mstore(dest, first)
+ }
+ }
+ }
+ }
+}
diff --git a/packages/contracts/src/utils/artifacts.ts b/packages/contracts/src/utils/artifacts.ts
index 357c66a0a..bf7221d6d 100644
--- a/packages/contracts/src/utils/artifacts.ts
+++ b/packages/contracts/src/utils/artifacts.ts
@@ -2,6 +2,7 @@ import { ContractArtifact } from '@0xproject/sol-compiler';
import * as AssetProxyOwner from '../artifacts/AssetProxyOwner.json';
import * as DummyERC20Token from '../artifacts/DummyERC20Token.json';
+import * as DummyERC721Receiver from '../artifacts/DummyERC721Receiver.json';
import * as DummyERC721Token from '../artifacts/DummyERC721Token.json';
import * as ERC20Proxy from '../artifacts/ERC20Proxy.json';
import * as ERC721Proxy from '../artifacts/ERC721Proxy.json';
@@ -9,8 +10,10 @@ import * as Exchange from '../artifacts/Exchange.json';
import * as MixinAuthorizable from '../artifacts/MixinAuthorizable.json';
import * as MultiSigWallet from '../artifacts/MultiSigWallet.json';
import * as MultiSigWalletWithTimeLock from '../artifacts/MultiSigWalletWithTimeLock.json';
+import * as TestAssetDataDecoders from '../artifacts/TestAssetDataDecoders.json';
import * as TestAssetProxyDispatcher from '../artifacts/TestAssetProxyDispatcher.json';
import * as TestLibBytes from '../artifacts/TestLibBytes.json';
+import * as TestLibMem from '../artifacts/TestLibMem.json';
import * as TestLibs from '../artifacts/TestLibs.json';
import * as TestSignatureValidator from '../artifacts/TestSignatureValidator.json';
import * as TokenRegistry from '../artifacts/TokenRegistry.json';
@@ -21,6 +24,7 @@ import * as ZRX from '../artifacts/ZRXToken.json';
export const artifacts = {
AssetProxyOwner: (AssetProxyOwner as any) as ContractArtifact,
DummyERC20Token: (DummyERC20Token as any) as ContractArtifact,
+ DummyERC721Receiver: (DummyERC721Receiver as any) as ContractArtifact,
DummyERC721Token: (DummyERC721Token as any) as ContractArtifact,
ERC20Proxy: (ERC20Proxy as any) as ContractArtifact,
ERC721Proxy: (ERC721Proxy as any) as ContractArtifact,
@@ -30,7 +34,9 @@ export const artifacts = {
MultiSigWallet: (MultiSigWallet as any) as ContractArtifact,
MultiSigWalletWithTimeLock: (MultiSigWalletWithTimeLock as any) as ContractArtifact,
TestAssetProxyDispatcher: (TestAssetProxyDispatcher as any) as ContractArtifact,
+ TestAssetDataDecoders: (TestAssetDataDecoders as any) as ContractArtifact,
TestLibBytes: (TestLibBytes as any) as ContractArtifact,
+ TestLibMem: (TestLibMem as any) as ContractArtifact,
TestLibs: (TestLibs as any) as ContractArtifact,
TestSignatureValidator: (TestSignatureValidator as any) as ContractArtifact,
TokenRegistry: (TokenRegistry as any) as ContractArtifact,
diff --git a/packages/contracts/src/utils/assertions.ts b/packages/contracts/src/utils/assertions.ts
new file mode 100644
index 000000000..615e648f3
--- /dev/null
+++ b/packages/contracts/src/utils/assertions.ts
@@ -0,0 +1,63 @@
+import * as chai from 'chai';
+import * as _ from 'lodash';
+
+import { constants } from './constants';
+
+const expect = chai.expect;
+
+function _expectEitherErrorAsync<T>(p: Promise<T>, error1: string, error2: string): PromiseLike<void> {
+ return expect(p)
+ .to.be.rejected()
+ .then(e => {
+ expect(e).to.satisfy(
+ (err: Error) => _.includes(err.message, error1) || _.includes(err.message, error2),
+ `expected promise to reject with error message that includes "${error1}" or "${error2}", but got: ` +
+ `"${e.message}"\n`,
+ );
+ });
+}
+
+/**
+ * Rejects if the given Promise does not reject with an error indicating
+ * insufficient funds.
+ * @param p the Promise which is expected to reject
+ * @returns a new Promise which will reject if the conditions are not met and
+ * otherwise resolve with no value.
+ */
+export function expectInsufficientFundsAsync<T>(p: Promise<T>): PromiseLike<void> {
+ return _expectEitherErrorAsync(p, 'insufficient funds', "sender doesn't have enough funds");
+}
+
+/**
+ * Rejects if the given Promise does not reject with a "revert" error or the
+ * given otherError.
+ * @param p the Promise which is expected to reject
+ * @param otherError the other error which is accepted as a valid reject error.
+ * @returns a new Promise which will reject if the conditions are not met and
+ * otherwise resolve with no value.
+ */
+export function expectRevertOrOtherErrorAsync<T>(p: Promise<T>, otherError: string): PromiseLike<void> {
+ return _expectEitherErrorAsync(p, constants.REVERT, otherError);
+}
+
+/**
+ * Rejects if the given Promise does not reject with a "revert" or "always
+ * failing transaction" error.
+ * @param p the Promise which is expected to reject
+ * @returns a new Promise which will reject if the conditions are not met and
+ * otherwise resolve with no value.
+ */
+export function expectRevertOrAlwaysFailingTransactionAsync<T>(p: Promise<T>): PromiseLike<void> {
+ return expectRevertOrOtherErrorAsync(p, 'always failing transaction');
+}
+
+/**
+ * Rejects if the given Promise does not reject with a "revert" or "Contract
+ * call failed" error.
+ * @param p the Promise which is expected to reject
+ * @returns a new Promise which will reject if the conditions are not met and
+ * otherwise resolve with no value.
+ */
+export function expectRevertOrContractCallFailedAsync<T>(p: Promise<T>): PromiseLike<void> {
+ return expectRevertOrOtherErrorAsync<T>(p, 'Contract call failed');
+}
diff --git a/packages/contracts/src/utils/constants.ts b/packages/contracts/src/utils/constants.ts
index 9b0b92545..ec3c8fd36 100644
--- a/packages/contracts/src/utils/constants.ts
+++ b/packages/contracts/src/utils/constants.ts
@@ -19,8 +19,19 @@ const TESTRPC_PRIVATE_KEYS_STRINGS = [
export const constants = {
INVALID_OPCODE: 'invalid opcode',
REVERT: 'revert',
+ LIB_BYTES_GREATER_THAN_ZERO_LENGTH_REQUIRED: 'GREATER_THAN_ZERO_LENGTH_REQUIRED',
+ LIB_BYTES_GREATER_OR_EQUAL_TO_4_LENGTH_REQUIRED: 'GREATER_OR_EQUAL_TO_4_LENGTH_REQUIRED',
+ LIB_BYTES_GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED: 'GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED',
+ LIB_BYTES_GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED: 'GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED',
+ LIB_BYTES_GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED: 'GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED',
+ LIB_BYTES_GREATER_OR_EQUAL_TO_SOURCE_BYTES_LENGTH_REQUIRED: 'GREATER_OR_EQUAL_TO_SOURCE_BYTES_LENGTH_REQUIRED',
+ ERC20_INSUFFICIENT_BALANCE: 'Insufficient balance to complete transfer.',
+ ERC20_INSUFFICIENT_ALLOWANCE: 'Insufficient allowance to complete transfer.',
TESTRPC_NETWORK_ID: 50,
- AWAIT_TRANSACTION_MINED_MS: 100,
+ // Note(albrow): In practice V8 and most other engines limit the minimum
+ // interval for setInterval to 10ms. We still set it to 0 here in order to
+ // ensure we always use the minimum interval.
+ AWAIT_TRANSACTION_MINED_MS: 0,
MAX_ETHERTOKEN_WITHDRAW_GAS: 43000,
MAX_TOKEN_TRANSFERFROM_GAS: 80000,
MAX_TOKEN_APPROVE_GAS: 60000,
diff --git a/packages/contracts/src/utils/coverage.ts b/packages/contracts/src/utils/coverage.ts
index a37939464..de29a3ecc 100644
--- a/packages/contracts/src/utils/coverage.ts
+++ b/packages/contracts/src/utils/coverage.ts
@@ -1,6 +1,5 @@
import { devConstants } from '@0xproject/dev-utils';
import { CoverageSubprovider, SolCompilerArtifactAdapter } from '@0xproject/sol-cov';
-import * as fs from 'fs';
import * as _ from 'lodash';
let coverageSubprovider: CoverageSubprovider;
@@ -15,7 +14,8 @@ export const coverage = {
_getCoverageSubprovider(): CoverageSubprovider {
const defaultFromAddress = devConstants.TESTRPC_FIRST_ADDRESS;
const solCompilerArtifactAdapter = new SolCompilerArtifactAdapter();
- const subprovider = new CoverageSubprovider(solCompilerArtifactAdapter, defaultFromAddress);
+ const isVerbose = true;
+ const subprovider = new CoverageSubprovider(solCompilerArtifactAdapter, defaultFromAddress, isVerbose);
return subprovider;
},
};
diff --git a/packages/contracts/src/utils/erc20_wrapper.ts b/packages/contracts/src/utils/erc20_wrapper.ts
index dceeceeea..91c9d50b7 100644
--- a/packages/contracts/src/utils/erc20_wrapper.ts
+++ b/packages/contracts/src/utils/erc20_wrapper.ts
@@ -3,8 +3,8 @@ import { Web3Wrapper } from '@0xproject/web3-wrapper';
import { Provider } from 'ethereum-types';
import * as _ from 'lodash';
-import { DummyERC20TokenContract } from '../contract_wrappers/generated/dummy_e_r_c20_token';
-import { ERC20ProxyContract } from '../contract_wrappers/generated/e_r_c20_proxy';
+import { DummyERC20TokenContract } from '../generated_contract_wrappers/dummy_e_r_c20_token';
+import { ERC20ProxyContract } from '../generated_contract_wrappers/e_r_c20_proxy';
import { artifacts } from './artifacts';
import { constants } from './constants';
diff --git a/packages/contracts/src/utils/erc721_wrapper.ts b/packages/contracts/src/utils/erc721_wrapper.ts
index 13fdf630e..b20d9acd2 100644
--- a/packages/contracts/src/utils/erc721_wrapper.ts
+++ b/packages/contracts/src/utils/erc721_wrapper.ts
@@ -4,8 +4,8 @@ import { Web3Wrapper } from '@0xproject/web3-wrapper';
import { Provider } from 'ethereum-types';
import * as _ from 'lodash';
-import { DummyERC721TokenContract } from '../contract_wrappers/generated/dummy_e_r_c721_token';
-import { ERC721ProxyContract } from '../contract_wrappers/generated/e_r_c721_proxy';
+import { DummyERC721TokenContract } from '../generated_contract_wrappers/dummy_e_r_c721_token';
+import { ERC721ProxyContract } from '../generated_contract_wrappers/e_r_c721_proxy';
import { artifacts } from './artifacts';
import { constants } from './constants';
diff --git a/packages/contracts/src/utils/exchange_wrapper.ts b/packages/contracts/src/utils/exchange_wrapper.ts
index dd278e77c..4cc8f0b89 100644
--- a/packages/contracts/src/utils/exchange_wrapper.ts
+++ b/packages/contracts/src/utils/exchange_wrapper.ts
@@ -1,10 +1,10 @@
import { AssetProxyId, SignedOrder } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
-import { LogEntry, Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
+import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import * as _ from 'lodash';
-import { ExchangeContract } from '../contract_wrappers/generated/exchange';
+import { ExchangeContract } from '../generated_contract_wrappers/exchange';
import { constants } from './constants';
import { formatters } from './formatters';
@@ -60,14 +60,14 @@ export class ExchangeWrapper {
public async fillOrderNoThrowAsync(
signedOrder: SignedOrder,
from: string,
- opts: { takerAssetFillAmount?: BigNumber } = {},
+ opts: { takerAssetFillAmount?: BigNumber; gas?: number } = {},
): Promise<TransactionReceiptWithDecodedLogs> {
const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount);
const txHash = await this._exchange.fillOrderNoThrow.sendTransactionAsync(
params.order,
params.takerAssetFillAmount,
params.signature,
- { from },
+ { from, gas: opts.gas },
);
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
return tx;
@@ -105,14 +105,14 @@ export class ExchangeWrapper {
public async batchFillOrdersNoThrowAsync(
orders: SignedOrder[],
from: string,
- opts: { takerAssetFillAmounts?: BigNumber[] } = {},
+ opts: { takerAssetFillAmounts?: BigNumber[]; gas?: number } = {},
): Promise<TransactionReceiptWithDecodedLogs> {
const params = formatters.createBatchFill(orders, opts.takerAssetFillAmounts);
const txHash = await this._exchange.batchFillOrdersNoThrow.sendTransactionAsync(
params.orders,
params.takerAssetFillAmounts,
params.signatures,
- { from },
+ { from, gas: opts.gas },
);
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
return tx;
@@ -135,14 +135,14 @@ export class ExchangeWrapper {
public async marketSellOrdersNoThrowAsync(
orders: SignedOrder[],
from: string,
- opts: { takerAssetFillAmount: BigNumber },
+ opts: { takerAssetFillAmount: BigNumber; gas?: number },
): Promise<TransactionReceiptWithDecodedLogs> {
const params = formatters.createMarketSellOrders(orders, opts.takerAssetFillAmount);
const txHash = await this._exchange.marketSellOrdersNoThrow.sendTransactionAsync(
params.orders,
params.takerAssetFillAmount,
params.signatures,
- { from },
+ { from, gas: opts.gas },
);
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
return tx;
@@ -165,14 +165,14 @@ export class ExchangeWrapper {
public async marketBuyOrdersNoThrowAsync(
orders: SignedOrder[],
from: string,
- opts: { makerAssetFillAmount: BigNumber },
+ opts: { makerAssetFillAmount: BigNumber; gas?: number },
): Promise<TransactionReceiptWithDecodedLogs> {
const params = formatters.createMarketBuyOrders(orders, opts.makerAssetFillAmount);
const txHash = await this._exchange.marketBuyOrdersNoThrow.sendTransactionAsync(
params.orders,
params.makerAssetFillAmount,
params.signatures,
- { from },
+ { from, gas: opts.gas },
);
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
return tx;
diff --git a/packages/contracts/src/utils/formatters.ts b/packages/contracts/src/utils/formatters.ts
index 1035f2d7c..32e4787d6 100644
--- a/packages/contracts/src/utils/formatters.ts
+++ b/packages/contracts/src/utils/formatters.ts
@@ -2,6 +2,7 @@ import { SignedOrder } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
import * as _ from 'lodash';
+import { constants } from './constants';
import { orderUtils } from './order_utils';
import { BatchCancelOrders, BatchFillOrders, MarketBuyOrders, MarketSellOrders } from './types';
@@ -28,8 +29,11 @@ export const formatters = {
signatures: [],
takerAssetFillAmount,
};
- _.forEach(signedOrders, signedOrder => {
+ _.forEach(signedOrders, (signedOrder, i) => {
const orderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress(signedOrder);
+ if (i !== 0) {
+ orderWithoutExchangeAddress.takerAssetData = constants.NULL_BYTES;
+ }
marketSellOrders.orders.push(orderWithoutExchangeAddress);
marketSellOrders.signatures.push(signedOrder.signature);
});
@@ -41,8 +45,11 @@ export const formatters = {
signatures: [],
makerAssetFillAmount,
};
- _.forEach(signedOrders, signedOrder => {
+ _.forEach(signedOrders, (signedOrder, i) => {
const orderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress(signedOrder);
+ if (i !== 0) {
+ orderWithoutExchangeAddress.makerAssetData = constants.NULL_BYTES;
+ }
marketBuyOrders.orders.push(orderWithoutExchangeAddress);
marketBuyOrders.signatures.push(signedOrder.signature);
});
diff --git a/packages/contracts/src/utils/increase_time.ts b/packages/contracts/src/utils/increase_time.ts
new file mode 100644
index 000000000..4565d8dbc
--- /dev/null
+++ b/packages/contracts/src/utils/increase_time.ts
@@ -0,0 +1,31 @@
+import * as _ from 'lodash';
+
+import { constants } from './constants';
+import { web3Wrapper } from './web3_wrapper';
+
+let firstAccount: string | undefined;
+
+/**
+ * Increases time by the given number of seconds and then mines a block so that
+ * the current block timestamp has the offset applied.
+ * @param seconds the number of seconds by which to incrase the time offset.
+ * @returns a new Promise which will resolve with the new total time offset or
+ * reject if the time could not be increased.
+ */
+export async function increaseTimeAndMineBlockAsync(seconds: number): Promise<number> {
+ if (_.isUndefined(firstAccount)) {
+ const accounts = await web3Wrapper.getAvailableAddressesAsync();
+ firstAccount = accounts[0];
+ }
+
+ const offset = await web3Wrapper.increaseTimeAsync(seconds);
+ // Note: we need to send a transaction after increasing time so
+ // that a block is actually mined. The contract looks at the
+ // last mined block for the timestamp.
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await web3Wrapper.sendTransactionAsync({ from: firstAccount, to: firstAccount, value: 0 }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+
+ return offset;
+}
diff --git a/packages/contracts/src/utils/log_decoder.ts b/packages/contracts/src/utils/log_decoder.ts
index 07d10e69d..07127ba79 100644
--- a/packages/contracts/src/utils/log_decoder.ts
+++ b/packages/contracts/src/utils/log_decoder.ts
@@ -39,6 +39,7 @@ export class LogDecoder {
}
public decodeLogOrThrow<ArgsType extends DecodedLogArgs>(log: LogEntry): LogWithDecodedArgs<ArgsType> | RawLog {
const logWithDecodedArgsOrLog = this._abiDecoder.tryToDecodeLogOrNoop(log);
+ // tslint:disable-next-line:no-unnecessary-type-assertion
if (_.isUndefined((logWithDecodedArgsOrLog as LogWithDecodedArgs<ArgsType>).args)) {
throw new Error(`Unable to decode log: ${JSON.stringify(log)}`);
}
diff --git a/packages/contracts/src/utils/match_order_tester.ts b/packages/contracts/src/utils/match_order_tester.ts
index 6170188bc..fbb1b99db 100644
--- a/packages/contracts/src/utils/match_order_tester.ts
+++ b/packages/contracts/src/utils/match_order_tester.ts
@@ -1,38 +1,21 @@
-import { BlockchainLifecycle } from '@0xproject/dev-utils';
-import { assetProxyUtils, crypto, orderHashUtils } from '@0xproject/order-utils';
+import { assetProxyUtils, orderHashUtils } from '@0xproject/order-utils';
import { AssetProxyId, SignedOrder } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
import * as chai from 'chai';
-import { LogWithDecodedArgs } from 'ethereum-types';
-import ethUtil = require('ethereumjs-util');
import * as _ from 'lodash';
-import { DummyERC20TokenContract } from '../contract_wrappers/generated/dummy_e_r_c20_token';
-import { DummyERC721TokenContract } from '../contract_wrappers/generated/dummy_e_r_c721_token';
-import { ERC20ProxyContract } from '../contract_wrappers/generated/e_r_c20_proxy';
-import { ERC721ProxyContract } from '../contract_wrappers/generated/e_r_c721_proxy';
-import {
- CancelContractEventArgs,
- ExchangeContract,
- FillContractEventArgs,
-} from '../contract_wrappers/generated/exchange';
import { chaiSetup } from '../utils/chai_setup';
-import { constants } from '../utils/constants';
import { ERC20Wrapper } from '../utils/erc20_wrapper';
import { ERC721Wrapper } from '../utils/erc721_wrapper';
import { ExchangeWrapper } from '../utils/exchange_wrapper';
-import { OrderFactory } from '../utils/order_factory';
import {
- ContractName,
ERC20BalancesByOwner,
ERC721TokenIdsByOwner,
TransferAmountsByMatchOrders as TransferAmounts,
} from '../utils/types';
-import { provider, web3Wrapper } from '../utils/web3_wrapper';
chaiSetup.configure();
const expect = chai.expect;
-const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
export class MatchOrderTester {
private _exchangeWrapper: ExchangeWrapper;
@@ -112,11 +95,6 @@ export class MatchOrderTester {
initialTakerAssetFilledAmountLeft?: BigNumber,
initialTakerAssetFilledAmountRight?: BigNumber,
): Promise<[ERC20BalancesByOwner, ERC721TokenIdsByOwner]> {
- // Test setup & verify preconditions
- const makerAddressLeft = signedOrderLeft.makerAddress;
- const makerAddressRight = signedOrderRight.makerAddress;
- const feeRecipientAddressLeft = signedOrderLeft.feeRecipientAddress;
- const feeRecipientAddressRight = signedOrderRight.feeRecipientAddress;
// Verify Left order preconditions
const orderTakerAssetFilledAmountLeft = await this._exchangeWrapper.getTakerAssetFilledAmountAsync(
orderHashUtils.getOrderHashHex(signedOrderLeft),
@@ -259,11 +237,11 @@ export class MatchOrderTester {
const expectedNewERC20BalancesByOwner = _.cloneDeep(erc20BalancesByOwner);
const expectedNewERC721TokenIdsByOwner = _.cloneDeep(erc721TokenIdsByOwner);
// Left Maker Asset (Right Taker Asset)
- const makerAssetProxyIdLeft = assetProxyUtils.decodeProxyDataId(signedOrderLeft.makerAssetData);
+ const makerAssetProxyIdLeft = assetProxyUtils.decodeAssetDataId(signedOrderLeft.makerAssetData);
if (makerAssetProxyIdLeft === AssetProxyId.ERC20) {
// Decode asset data
- const erc20ProxyData = assetProxyUtils.decodeERC20ProxyData(signedOrderLeft.makerAssetData);
- const makerAssetAddressLeft = erc20ProxyData.tokenAddress;
+ const erc20AssetData = assetProxyUtils.decodeERC20AssetData(signedOrderLeft.makerAssetData);
+ const makerAssetAddressLeft = erc20AssetData.tokenAddress;
const takerAssetAddressRight = makerAssetAddressLeft;
// Left Maker
expectedNewERC20BalancesByOwner[makerAddressLeft][makerAssetAddressLeft] = expectedNewERC20BalancesByOwner[
@@ -281,9 +259,9 @@ export class MatchOrderTester {
][makerAssetAddressLeft].add(expectedTransferAmounts.amountReceivedByTaker);
} else if (makerAssetProxyIdLeft === AssetProxyId.ERC721) {
// Decode asset data
- const erc721ProxyData = assetProxyUtils.decodeERC721ProxyData(signedOrderLeft.makerAssetData);
- const makerAssetAddressLeft = erc721ProxyData.tokenAddress;
- const makerAssetIdLeft = erc721ProxyData.tokenId;
+ const erc721AssetData = assetProxyUtils.decodeERC721AssetData(signedOrderLeft.makerAssetData);
+ const makerAssetAddressLeft = erc721AssetData.tokenAddress;
+ const makerAssetIdLeft = erc721AssetData.tokenId;
const takerAssetAddressRight = makerAssetAddressLeft;
const takerAssetIdRight = makerAssetIdLeft;
// Left Maker
@@ -294,11 +272,11 @@ export class MatchOrderTester {
}
// Left Taker Asset (Right Maker Asset)
// Note: This exchange is only between the order makers: the Taker does not receive any of the left taker asset.
- const takerAssetProxyIdLeft = assetProxyUtils.decodeProxyDataId(signedOrderLeft.takerAssetData);
+ const takerAssetProxyIdLeft = assetProxyUtils.decodeAssetDataId(signedOrderLeft.takerAssetData);
if (takerAssetProxyIdLeft === AssetProxyId.ERC20) {
// Decode asset data
- const erc20ProxyData = assetProxyUtils.decodeERC20ProxyData(signedOrderLeft.takerAssetData);
- const takerAssetAddressLeft = erc20ProxyData.tokenAddress;
+ const erc20AssetData = assetProxyUtils.decodeERC20AssetData(signedOrderLeft.takerAssetData);
+ const takerAssetAddressLeft = erc20AssetData.tokenAddress;
const makerAssetAddressRight = takerAssetAddressLeft;
// Left Maker
expectedNewERC20BalancesByOwner[makerAddressLeft][takerAssetAddressLeft] = expectedNewERC20BalancesByOwner[
@@ -312,9 +290,9 @@ export class MatchOrderTester {
);
} else if (takerAssetProxyIdLeft === AssetProxyId.ERC721) {
// Decode asset data
- const erc721ProxyData = assetProxyUtils.decodeERC721ProxyData(signedOrderRight.makerAssetData);
- const makerAssetAddressRight = erc721ProxyData.tokenAddress;
- const makerAssetIdRight = erc721ProxyData.tokenId;
+ const erc721AssetData = assetProxyUtils.decodeERC721AssetData(signedOrderRight.makerAssetData);
+ const makerAssetAddressRight = erc721AssetData.tokenAddress;
+ const makerAssetIdRight = erc721AssetData.tokenId;
const takerAssetAddressLeft = makerAssetAddressRight;
const takerAssetIdLeft = makerAssetIdRight;
// Right Maker
diff --git a/packages/contracts/src/utils/multi_sig_wrapper.ts b/packages/contracts/src/utils/multi_sig_wrapper.ts
index 9971e8f6e..f0098bd5e 100644
--- a/packages/contracts/src/utils/multi_sig_wrapper.ts
+++ b/packages/contracts/src/utils/multi_sig_wrapper.ts
@@ -3,10 +3,9 @@ import { Web3Wrapper } from '@0xproject/web3-wrapper';
import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import * as _ from 'lodash';
-import { AssetProxyOwnerContract } from '../contract_wrappers/generated/asset_proxy_owner';
-import { MultiSigWalletContract } from '../contract_wrappers/generated/multi_sig_wallet';
+import { AssetProxyOwnerContract } from '../generated_contract_wrappers/asset_proxy_owner';
+import { MultiSigWalletContract } from '../generated_contract_wrappers/multi_sig_wallet';
-import { constants } from './constants';
import { LogDecoder } from './log_decoder';
export class MultiSigWrapper {
@@ -45,6 +44,7 @@ export class MultiSigWrapper {
txId: BigNumber,
from: string,
): Promise<TransactionReceiptWithDecodedLogs> {
+ // tslint:disable-next-line:no-unnecessary-type-assertion
const txHash = await (this
._multiSig as AssetProxyOwnerContract).executeRemoveAuthorizedAddress.sendTransactionAsync(txId, { from });
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
diff --git a/packages/contracts/src/utils/order_factory.ts b/packages/contracts/src/utils/order_factory.ts
index dd02e1f5c..009dbc396 100644
--- a/packages/contracts/src/utils/order_factory.ts
+++ b/packages/contracts/src/utils/order_factory.ts
@@ -1,7 +1,6 @@
import { generatePseudoRandomSalt, orderHashUtils } from '@0xproject/order-utils';
import { Order, SignatureType, SignedOrder } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
-import * as _ from 'lodash';
import { constants } from './constants';
import { signingUtils } from './signing_utils';
diff --git a/packages/contracts/src/utils/order_utils.ts b/packages/contracts/src/utils/order_utils.ts
index 180761f2c..a9f994d80 100644
--- a/packages/contracts/src/utils/order_utils.ts
+++ b/packages/contracts/src/utils/order_utils.ts
@@ -1,7 +1,7 @@
import { OrderWithoutExchangeAddress, SignedOrder } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
-import ethUtil = require('ethereumjs-util');
+import { constants } from './constants';
import { CancelOrder, MatchOrder } from './types';
export const orderUtils = {
@@ -44,6 +44,8 @@ export const orderUtils = {
leftSignature: signedOrderLeft.signature,
rightSignature: signedOrderRight.signature,
};
+ fill.right.makerAssetData = constants.NULL_BYTES;
+ fill.right.takerAssetData = constants.NULL_BYTES;
return fill;
},
};
diff --git a/packages/contracts/src/utils/profiler.ts b/packages/contracts/src/utils/profiler.ts
new file mode 100644
index 000000000..85ee24f22
--- /dev/null
+++ b/packages/contracts/src/utils/profiler.ts
@@ -0,0 +1,27 @@
+import { devConstants } from '@0xproject/dev-utils';
+import { ProfilerSubprovider, SolCompilerArtifactAdapter } from '@0xproject/sol-cov';
+import * as _ from 'lodash';
+
+let profilerSubprovider: ProfilerSubprovider;
+
+export const profiler = {
+ start(): void {
+ profiler.getProfilerSubproviderSingleton().start();
+ },
+ stop(): void {
+ profiler.getProfilerSubproviderSingleton().stop();
+ },
+ getProfilerSubproviderSingleton(): ProfilerSubprovider {
+ if (_.isUndefined(profilerSubprovider)) {
+ profilerSubprovider = profiler._getProfilerSubprovider();
+ }
+ return profilerSubprovider;
+ },
+ _getProfilerSubprovider(): ProfilerSubprovider {
+ const defaultFromAddress = devConstants.TESTRPC_FIRST_ADDRESS;
+ const solCompilerArtifactAdapter = new SolCompilerArtifactAdapter();
+ const isVerbose = true;
+ const subprovider = new ProfilerSubprovider(solCompilerArtifactAdapter, defaultFromAddress, isVerbose);
+ return subprovider;
+ },
+};
diff --git a/packages/contracts/src/utils/revert_trace.ts b/packages/contracts/src/utils/revert_trace.ts
new file mode 100644
index 000000000..0bf8384bc
--- /dev/null
+++ b/packages/contracts/src/utils/revert_trace.ts
@@ -0,0 +1,21 @@
+import { devConstants } from '@0xproject/dev-utils';
+import { RevertTraceSubprovider, SolCompilerArtifactAdapter } from '@0xproject/sol-cov';
+import * as _ from 'lodash';
+
+let revertTraceSubprovider: RevertTraceSubprovider;
+
+export const revertTrace = {
+ getRevertTraceSubproviderSingleton(): RevertTraceSubprovider {
+ if (_.isUndefined(revertTraceSubprovider)) {
+ revertTraceSubprovider = revertTrace._getRevertTraceSubprovider();
+ }
+ return revertTraceSubprovider;
+ },
+ _getRevertTraceSubprovider(): RevertTraceSubprovider {
+ const defaultFromAddress = devConstants.TESTRPC_FIRST_ADDRESS;
+ const solCompilerArtifactAdapter = new SolCompilerArtifactAdapter();
+ const isVerbose = true;
+ const subprovider = new RevertTraceSubprovider(solCompilerArtifactAdapter, defaultFromAddress, isVerbose);
+ return subprovider;
+ },
+};
diff --git a/packages/contracts/src/utils/token_registry_wrapper.ts b/packages/contracts/src/utils/token_registry_wrapper.ts
index 240c06fdc..91895aa59 100644
--- a/packages/contracts/src/utils/token_registry_wrapper.ts
+++ b/packages/contracts/src/utils/token_registry_wrapper.ts
@@ -1,7 +1,7 @@
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import { Provider } from 'ethereum-types';
-import { TokenRegistryContract } from '../contract_wrappers/generated/token_registry';
+import { TokenRegistryContract } from '../generated_contract_wrappers/token_registry';
import { Token } from './types';
diff --git a/packages/contracts/src/utils/types.ts b/packages/contracts/src/utils/types.ts
index 360e1fdbc..bb8c12088 100644
--- a/packages/contracts/src/utils/types.ts
+++ b/packages/contracts/src/utils/types.ts
@@ -1,6 +1,6 @@
-import { Order, OrderWithoutExchangeAddress } from '@0xproject/types';
+import { OrderWithoutExchangeAddress } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
-import { AbiDefinition, ContractAbi } from 'ethereum-types';
+import { AbiDefinition } from 'ethereum-types';
export interface ERC20BalancesByOwner {
[ownerAddress: string]: {
@@ -90,11 +90,14 @@ export enum ContractName {
AccountLevels = 'AccountLevels',
EtherDelta = 'EtherDelta',
Arbitrage = 'Arbitrage',
+ TestAssetDataDecoders = 'TestAssetDataDecoders',
TestAssetProxyDispatcher = 'TestAssetProxyDispatcher',
+ TestLibMem = 'TestLibMem',
TestLibs = 'TestLibs',
TestSignatureValidator = 'TestSignatureValidator',
ERC20Proxy = 'ERC20Proxy',
ERC721Proxy = 'ERC721Proxy',
+ DummyERC721Receiver = 'DummyERC721Receiver',
DummyERC721Token = 'DummyERC721Token',
TestLibBytes = 'TestLibBytes',
Authorizable = 'Authorizable',
diff --git a/packages/contracts/src/utils/web3_wrapper.ts b/packages/contracts/src/utils/web3_wrapper.ts
index 1049ab967..c9d83a02d 100644
--- a/packages/contracts/src/utils/web3_wrapper.ts
+++ b/packages/contracts/src/utils/web3_wrapper.ts
@@ -1,19 +1,82 @@
import { devConstants, env, EnvVars, web3Factory } from '@0xproject/dev-utils';
import { prependSubprovider } from '@0xproject/subproviders';
+import { logUtils } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
-import { Provider } from 'ethereum-types';
+import * as _ from 'lodash';
import { coverage } from './coverage';
+import { profiler } from './profiler';
+import { revertTrace } from './revert_trace';
-export const txDefaults = {
+enum ProviderType {
+ Ganache = 'ganache',
+ Geth = 'geth',
+}
+
+let testProvider: ProviderType;
+switch (process.env.TEST_PROVIDER) {
+ case undefined:
+ testProvider = ProviderType.Ganache;
+ break;
+ case 'ganache':
+ testProvider = ProviderType.Ganache;
+ break;
+ case 'geth':
+ testProvider = ProviderType.Geth;
+ break;
+ default:
+ throw new Error(`Unknown TEST_PROVIDER: ${process.env.TEST_PROVIDER}`);
+}
+
+const ganacheTxDefaults = {
from: devConstants.TESTRPC_FIRST_ADDRESS,
gas: devConstants.GAS_LIMIT,
};
-const providerConfigs = { shouldUseInProcessGanache: true };
+const gethTxDefaults = {
+ from: devConstants.TESTRPC_FIRST_ADDRESS,
+};
+export const txDefaults = testProvider === ProviderType.Ganache ? ganacheTxDefaults : gethTxDefaults;
+
+const gethConfigs = {
+ shouldUseInProcessGanache: false,
+ rpcUrl: 'http://localhost:8501',
+ shouldUseFakeGasEstimate: false,
+};
+const ganacheConfigs = {
+ shouldUseInProcessGanache: true,
+};
+const providerConfigs = testProvider === ProviderType.Ganache ? ganacheConfigs : gethConfigs;
+
export const provider = web3Factory.getRpcProvider(providerConfigs);
const isCoverageEnabled = env.parseBoolean(EnvVars.SolidityCoverage);
+const isProfilerEnabled = env.parseBoolean(EnvVars.SolidityProfiler);
+const isRevertTraceEnabled = env.parseBoolean(EnvVars.SolidityRevertTrace);
+const enabledSubproviderCount = _.filter([isCoverageEnabled, isProfilerEnabled, isRevertTraceEnabled], _.identity)
+ .length;
+if (enabledSubproviderCount > 1) {
+ throw new Error(`Only one of coverage, profiler, or revert trace subproviders can be enabled at a time`);
+}
if (isCoverageEnabled) {
const coverageSubprovider = coverage.getCoverageSubproviderSingleton();
prependSubprovider(provider, coverageSubprovider);
}
+if (isProfilerEnabled) {
+ if (testProvider === ProviderType.Ganache) {
+ logUtils.warn(
+ "Gas costs in Ganache traces are incorrect and we don't recommend using it for profiling. Please switch to Geth",
+ );
+ process.exit(1);
+ }
+ const profilerSubprovider = profiler.getProfilerSubproviderSingleton();
+ logUtils.log(
+ "By default profilerSubprovider is stopped so that you don't get noise from setup code. Don't forget to start it before the code you want to profile and stop it afterwards",
+ );
+ profilerSubprovider.stop();
+ prependSubprovider(provider, profilerSubprovider);
+}
+if (isRevertTraceEnabled) {
+ const revertTraceSubprovider = revertTrace.getRevertTraceSubproviderSingleton();
+ prependSubprovider(provider, revertTraceSubprovider);
+}
+
export const web3Wrapper = new Web3Wrapper(provider);