diff options
author | Greg Hysen <hysz@users.noreply.github.com> | 2018-06-09 02:58:23 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-06-09 02:58:23 +0800 |
commit | 817c332d11835f02726f0609374d1c25c9ab39b5 (patch) | |
tree | d84c9a96465b5e862629be3dd04325dcccdbc780 /packages/contracts | |
parent | add9a9db9ba4294cf12489bf589c527b0921ed1e (diff) | |
parent | 05fbc8e6b061e5cef5bb8b8078176f9b375c2ec5 (diff) | |
download | dexon-sol-tools-817c332d11835f02726f0609374d1c25c9ab39b5.tar.gz dexon-sol-tools-817c332d11835f02726f0609374d1c25c9ab39b5.tar.zst dexon-sol-tools-817c332d11835f02726f0609374d1c25c9ab39b5.zip |
Merge pull request #627 from 0xProject/feature/contracts/erc721SafeTransferFrom
On-Chain AssetData Decoding Lib + safeTransferFrom for ERC721 + Memcpy
Diffstat (limited to 'packages/contracts')
40 files changed, 1429 insertions, 362 deletions
diff --git a/packages/contracts/compiler.json b/packages/contracts/compiler.json index 48ba4ffcd..6ef8e6a95 100644 --- a/packages/contracts/compiler.json +++ b/packages/contracts/compiler.json @@ -21,6 +21,7 @@ "contracts": [ "AssetProxyOwner", "DummyERC20Token", + "DummyERC721Receiver", "DummyERC721Token", "ERC20Proxy", "ERC721Proxy", @@ -28,8 +29,10 @@ "MixinAuthorizable", "MultiSigWallet", "MultiSigWalletWithTimeLock", + "TestAssetDataDecoders", "TestAssetProxyDispatcher", "TestLibBytes", + "TestLibMem", "TestLibs", "TestSignatureValidator", "TokenRegistry", diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 99c507197..b533a22ce 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -30,7 +30,7 @@ "test:circleci": "yarn test" }, "config": { - "abis": "../migrations/artifacts/2.0.0/@(AssetProxyOwner|DummyERC20Token|DummyERC721Token|ERC20Proxy|ERC721Proxy|Exchange|MixinAuthorizable|MultiSigWallet|MultiSigWalletWithTimeLock|TestAssetProxyDispatcher|TestLibBytes|TestLibs|TestSignatureValidator|TokenRegistry|Whitelist|WETH9|ZRXToken).json" + "abis": "../migrations/artifacts/2.0.0/@(AssetProxyOwner|DummyERC20Token|DummyERC721Receiver|DummyERC721Token|ERC20Proxy|ERC721Proxy|Exchange|MixinAuthorizable|MultiSigWallet|MultiSigWalletWithTimeLock|TestAssetDataDecoders|TestAssetProxyDispatcher|TestLibBytes|TestLibMem|TestLibs|TestSignatureValidator|TokenRegistry|Whitelist|WETH9|ZRXToken).json" }, "repository": { "type": "git", diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC20Proxy.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC20Proxy.sol index 2c321e134..dd25bf41a 100644 --- a/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC20Proxy.sol +++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC20Proxy.sol @@ -34,34 +34,30 @@ 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. + ( + uint8 proxyId, + address token + ) = decodeERC20AssetData(assetData); + // Data must be intended for this proxy. require( - length == 21, - LENGTH_21_REQUIRED - ); - // TODO: Is this too inflexible in the future? - require( - uint8(assetMetadata[length - 1]) == PROXY_ID, + proxyId == PROXY_ID, ASSET_PROXY_ID_MISMATCH ); - // Decode metadata. - address token = readAddress(assetMetadata, 0); - // Transfer tokens. bool success = IERC20Token(token).transferFrom(from, to, amount); require( @@ -79,4 +75,30 @@ contract ERC20Proxy is { return PROXY_ID; } + + /// @dev Decodes ERC20 Asset data. + /// @param assetData Encoded byte array. + /// @return proxyId Intended ERC20 proxy id. + /// @return token ERC20 token address. + function decodeERC20AssetData(bytes memory assetData) + internal + pure + returns ( + uint8 proxyId, + address token + ) + { + // Validate encoded data length + uint256 length = assetData.length; + require( + length == 21, + LENGTH_21_REQUIRED + ); + + // Decode data + token = readAddress(assetData, 0); + proxyId = uint8(assetData[length - 1]); + + return (proxyId, token); + } } diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC721Proxy.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC721Proxy.sol index 07e01c774..25136133d 100644 --- a/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC721Proxy.sol +++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC721Proxy.sol @@ -34,29 +34,30 @@ 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; + // Decode asset data. + ( + uint8 proxyId, + address token, + uint256 tokenId, + bytes memory receiverData + ) = decodeERC721AssetData(assetData); - require( - length == 53, - LENGTH_53_REQUIRED - ); - // TODO: Is this too inflexible in the future? + // Data must be intended for this proxy. require( - uint8(assetMetadata[length - 1]) == PROXY_ID, + proxyId == PROXY_ID, ASSET_PROXY_ID_MISMATCH ); @@ -66,15 +67,13 @@ contract ERC721Proxy is 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); + // 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 +85,44 @@ 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 ( + uint8 proxyId, + address token, + uint256 tokenId, + bytes memory receiverData + ) + { + // Validate encoded data length + uint256 length = assetData.length; + require( + length >= 53, + LENGTH_AT_LEAST_53_REQUIRED + ); + + // Decode asset data. + token = readAddress(assetData, 0); + tokenId = readUint256(assetData, 20); + if (length > 53) { + receiverData = readBytes(assetData, 52); + } + proxyId = uint8(assetData[length - 1]); + + return ( + proxyId, + 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..80180a0d9 100644 --- a/packages/contracts/src/contracts/current/protocol/AssetProxy/libs/LibAssetProxyErrors.sol +++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/libs/LibAssetProxyErrors.sol @@ -29,9 +29,9 @@ contract LibAssetProxyErrors { /// 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. + 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 LENGTH_AT_LEAST_53_REQUIRED = "LENGTH_AT_LEAST_53_REQUIRED"; // Byte array must have a length of at least 53. } 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..e77d81c06 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinAssetProxyDispatcher.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinAssetProxyDispatcher.sol @@ -80,12 +80,12 @@ 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 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, address from, address to, uint256 amount @@ -96,16 +96,16 @@ contract MixinAssetProxyDispatcher is if (amount > 0) { // Lookup asset proxy - uint256 length = assetMetadata.length; + uint256 length = assetData.length; require( length > 0, LENGTH_GREATER_THAN_0_REQUIRED ); - uint8 assetProxyId = uint8(assetMetadata[length - 1]); + uint8 assetProxyId = uint8(assetData[length - 1]); 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/MixinSettlement.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlement.sol index 646d3ed58..83e9dfdf4 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlement.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlement.sol @@ -40,7 +40,7 @@ contract MixinSettlement is bytes internal ZRX_PROXY_DATA; /// @dev Gets the ZRX metadata used for fee transfers. - function zrxProxyData() + function zrxAssetData() external view returns (bytes memory) @@ -48,13 +48,13 @@ contract MixinSettlement is 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_PROXY_DATA = _zrxAssetData; } /// @dev Settles an order by transferring assets between counterparties. diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol index 0ad0710ce..e09f80bcc 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol @@ -40,7 +40,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 +66,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 +93,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 +165,43 @@ 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> 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> 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,7 +328,8 @@ contract MixinWrapperFunctions is function marketSellOrders( LibOrder.Order[] memory orders, uint256 takerAssetFillAmount, - bytes[] memory signatures) + bytes[] memory signatures + ) public returns (FillResults memory totalFillResults) { @@ -366,7 +372,8 @@ contract MixinWrapperFunctions is function marketSellOrdersNoThrow( LibOrder.Order[] memory orders, uint256 takerAssetFillAmount, - bytes[] memory signatures) + bytes[] memory signatures + ) public returns (FillResults memory totalFillResults) { @@ -408,7 +415,8 @@ contract MixinWrapperFunctions is function marketBuyOrders( LibOrder.Order[] memory orders, uint256 makerAssetFillAmount, - bytes[] memory signatures) + bytes[] memory signatures + ) public returns (FillResults memory totalFillResults) { @@ -459,7 +467,8 @@ contract MixinWrapperFunctions is function marketBuyOrdersNoThrow( LibOrder.Order[] memory orders, uint256 makerAssetFillAmount, - bytes[] memory signatures) + bytes[] memory signatures + ) public returns (FillResults memory totalFillResults) { 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/mixins/MAssetProxyDispatcher.sol b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MAssetProxyDispatcher.sol index 87c5f6361..82eafb529 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,12 @@ 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 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, 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..2c6a8fdb0 --- /dev/null +++ b/packages/contracts/src/contracts/current/test/TestAssetDataDecoders/TestAssetDataDecoders.sol @@ -0,0 +1,77 @@ +/* + + 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 + ERC20Proxy, + ERC721Proxy +{ + + /// @dev Decodes ERC20 Asset data. + /// @param assetData Encoded byte array. + /// @return proxyId Intended ERC20 proxy id. + /// @return token ERC20 token address. + function publicDecodeERC20Data(bytes memory assetData) + public + pure + returns ( + uint8 proxyId, + address token + ) + { + (proxyId, token) = decodeERC20AssetData(assetData); + return (proxyId, token); + } + + /// @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 ( + uint8 proxyId, + address token, + uint256 tokenId, + bytes memory receiverData + ) + { + ( + proxyId, + token, + tokenId, + receiverData + ) = decodeERC721AssetData(assetData); + + return ( + proxyId, + 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..2ae69e0ef 100644 --- a/packages/contracts/src/contracts/current/test/TestAssetProxyDispatcher/TestAssetProxyDispatcher.sol +++ b/packages/contracts/src/contracts/current/test/TestAssetProxyDispatcher/TestAssetProxyDispatcher.sol @@ -23,12 +23,12 @@ import "../../protocol/Exchange/MixinAssetProxyDispatcher.sol"; contract TestAssetProxyDispatcher is MixinAssetProxyDispatcher { function publicDispatchTransferFrom( - bytes memory assetMetadata, + bytes memory assetData, address from, address to, uint256 amount) public { - dispatchTransferFrom(assetMetadata, from, to, amount); + dispatchTransferFrom(assetData, 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..22c84504c 100644 --- a/packages/contracts/src/contracts/current/test/TestLibBytes/TestLibBytes.sol +++ b/packages/contracts/src/contracts/current/test/TestLibBytes/TestLibBytes.sol @@ -68,7 +68,8 @@ contract TestLibBytes is /// @return address from byte array. function publicReadAddress( bytes memory b, - uint256 index) + uint256 index + ) public pure returns (address result) @@ -84,7 +85,8 @@ contract TestLibBytes is function publicWriteAddress( bytes memory b, uint256 index, - address input) + address input + ) public pure returns (bytes memory) @@ -99,7 +101,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 +118,8 @@ contract TestLibBytes is function publicWriteBytes32( bytes memory b, uint256 index, - bytes32 input) + bytes32 input + ) public pure returns (bytes memory) @@ -130,7 +134,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 +151,8 @@ contract TestLibBytes is function publicWriteUint256( bytes memory b, uint256 index, - uint256 input) + uint256 input + ) public pure returns (bytes memory) @@ -166,4 +172,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..d1d10476f 100644 --- a/packages/contracts/src/contracts/current/utils/LibBytes/LibBytes.sol +++ b/packages/contracts/src/contracts/current/utils/LibBytes/LibBytes.sol @@ -18,14 +18,18 @@ 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"; /// @dev Pops the last byte off of a byte array by modifying its length. /// @param b Byte array that will be modified. @@ -37,12 +41,12 @@ contract LibBytes { { 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) @@ -61,7 +65,7 @@ contract LibBytes { { require( b.length >= 20, - GTE_20_LENGTH_REQUIRED + GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED ); // Store last 20 bytes. @@ -124,8 +128,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 +160,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 +199,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 +226,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 +278,72 @@ 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 + ); + } } 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/constants.ts b/packages/contracts/src/utils/constants.ts index fa2a4af3c..af3f26d82 100644 --- a/packages/contracts/src/utils/constants.ts +++ b/packages/contracts/src/utils/constants.ts @@ -19,11 +19,11 @@ const TESTRPC_PRIVATE_KEYS_STRINGS = [ export const constants = { INVALID_OPCODE: 'invalid opcode', REVERT: 'revert', - LIB_BYTES_GT_ZERO_LENGTH_REQUIRED: 'Length must be greater than 0.', - LIB_BYTES_GTE_4_LENGTH_REQUIRED: 'Length must be greater than or equal to 4.', - LIB_BYTES_GTE_20_LENGTH_REQUIRED: 'Length must be greater than or equal to 20.', - LIB_BYTES_GTE_32_LENGTH_REQUIRED: 'Length must be greater than or equal to 32.', - LIB_BYTES_INDEX_OUT_OF_BOUNDS: 'Specified array index is out of bounds.', + 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', ERC20_INSUFFICIENT_BALANCE: 'Insufficient balance to complete transfer.', ERC20_INSUFFICIENT_ALLOWANCE: 'Insufficient allowance to complete transfer.', TESTRPC_NETWORK_ID: 50, diff --git a/packages/contracts/src/utils/match_order_tester.ts b/packages/contracts/src/utils/match_order_tester.ts index f4f7f965b..fbb1b99db 100644 --- a/packages/contracts/src/utils/match_order_tester.ts +++ b/packages/contracts/src/utils/match_order_tester.ts @@ -237,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[ @@ -259,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 @@ -272,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[ @@ -290,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/types.ts b/packages/contracts/src/utils/types.ts index 491890fa1..bb8c12088 100644 --- a/packages/contracts/src/utils/types.ts +++ b/packages/contracts/src/utils/types.ts @@ -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/test/asset_proxy/decoder.ts b/packages/contracts/test/asset_proxy/decoder.ts new file mode 100644 index 000000000..d4fae1601 --- /dev/null +++ b/packages/contracts/test/asset_proxy/decoder.ts @@ -0,0 +1,103 @@ +import { BlockchainLifecycle } from '@0xproject/dev-utils'; +import { assetProxyUtils, generatePseudoRandomSalt } from '@0xproject/order-utils'; +import { BigNumber } from '@0xproject/utils'; +import * as chai from 'chai'; +import ethUtil = require('ethereumjs-util'); + +import { TestAssetDataDecodersContract } from '../../src/generated_contract_wrappers/test_asset_data_decoders'; +import { artifacts } from '../../src/utils/artifacts'; +import { chaiSetup } from '../../src/utils/chai_setup'; +import { provider, txDefaults, web3Wrapper } from '../../src/utils/web3_wrapper'; + +chaiSetup.configure(); +const expect = chai.expect; +const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); + +describe('TestAssetDataDecoders', () => { + let testAssetProxyDecoder: TestAssetDataDecodersContract; + let testAddress: string; + + before(async () => { + await blockchainLifecycle.startAsync(); + }); + after(async () => { + await blockchainLifecycle.revertAsync(); + }); + before(async () => { + // Setup accounts & addresses + const accounts = await web3Wrapper.getAvailableAddressesAsync(); + testAddress = accounts[0]; + // Deploy TestLibMem + testAssetProxyDecoder = await TestAssetDataDecodersContract.deployFrom0xArtifactAsync( + artifacts.TestAssetDataDecoders, + provider, + txDefaults, + ); + }); + beforeEach(async () => { + await blockchainLifecycle.startAsync(); + }); + afterEach(async () => { + await blockchainLifecycle.revertAsync(); + }); + + describe('Asset Data Decoders', () => { + it('should correctly decode ERC20 asset data)', async () => { + const encodedAssetData = assetProxyUtils.encodeERC20AssetData(testAddress); + const expectedDecodedAssetData = assetProxyUtils.decodeERC20AssetData(encodedAssetData); + let decodedAssetProxyId: number; + let decodedTokenAddress: string; + [decodedAssetProxyId, decodedTokenAddress] = await testAssetProxyDecoder.publicDecodeERC20Data.callAsync( + encodedAssetData, + ); + expect(decodedAssetProxyId).to.be.equal(expectedDecodedAssetData.assetProxyId); + expect(decodedTokenAddress).to.be.equal(expectedDecodedAssetData.tokenAddress); + }); + + it('should correctly decode ERC721 asset data', async () => { + const tokenId = generatePseudoRandomSalt(); + const encodedAssetData = assetProxyUtils.encodeERC721AssetData(testAddress, tokenId); + const expectedDecodedAssetData = assetProxyUtils.decodeERC721AssetData(encodedAssetData); + let decodedAssetProxyId: number; + let decodedTokenAddress: string; + let decodedTokenId: BigNumber; + let decodedData: string; + [ + decodedAssetProxyId, + decodedTokenAddress, + decodedTokenId, + decodedData, + ] = await testAssetProxyDecoder.publicDecodeERC721Data.callAsync(encodedAssetData); + expect(decodedAssetProxyId).to.be.equal(expectedDecodedAssetData.assetProxyId); + expect(decodedTokenAddress).to.be.equal(expectedDecodedAssetData.tokenAddress); + expect(decodedTokenId).to.be.bignumber.equal(expectedDecodedAssetData.tokenId); + expect(decodedData).to.be.equal(expectedDecodedAssetData.receiverData); + }); + + it('should correctly decode ERC721 asset data with receiver data', async () => { + const tokenId = generatePseudoRandomSalt(); + const receiverDataFirst32Bytes = ethUtil.bufferToHex( + assetProxyUtils.encodeUint256(generatePseudoRandomSalt()), + ); + const receiverDataExtraBytes = 'FFFF'; + // We add extra bytes to generate a value that doesn't fit perfectly into one word + const receiverData = receiverDataFirst32Bytes + receiverDataExtraBytes; + const encodedAssetData = assetProxyUtils.encodeERC721AssetData(testAddress, tokenId, receiverData); + const expectedDecodedAssetData = assetProxyUtils.decodeERC721AssetData(encodedAssetData); + let decodedAssetProxyId: number; + let decodedTokenAddress: string; + let decodedTokenId: BigNumber; + let decodedReceiverData: string; + [ + decodedAssetProxyId, + decodedTokenAddress, + decodedTokenId, + decodedReceiverData, + ] = await testAssetProxyDecoder.publicDecodeERC721Data.callAsync(encodedAssetData); + expect(decodedAssetProxyId).to.be.equal(expectedDecodedAssetData.assetProxyId); + expect(decodedTokenAddress).to.be.equal(expectedDecodedAssetData.tokenAddress); + expect(decodedTokenId).to.be.bignumber.equal(expectedDecodedAssetData.tokenId); + expect(decodedReceiverData).to.be.equal(expectedDecodedAssetData.receiverData); + }); + }); +}); diff --git a/packages/contracts/test/asset_proxy/proxies.ts b/packages/contracts/test/asset_proxy/proxies.ts index d14280c5f..08376ccfb 100644 --- a/packages/contracts/test/asset_proxy/proxies.ts +++ b/packages/contracts/test/asset_proxy/proxies.ts @@ -1,24 +1,33 @@ import { BlockchainLifecycle } from '@0xproject/dev-utils'; -import { assetProxyUtils } from '@0xproject/order-utils'; +import { assetProxyUtils, generatePseudoRandomSalt } from '@0xproject/order-utils'; 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 '../../src/generated_contract_wrappers/dummy_e_r_c20_token'; +import { + DummyERC721ReceiverContract, + TokenReceivedContractEventArgs, +} from '../../src/generated_contract_wrappers/dummy_e_r_c721_receiver'; import { DummyERC721TokenContract } from '../../src/generated_contract_wrappers/dummy_e_r_c721_token'; import { ERC20ProxyContract } from '../../src/generated_contract_wrappers/e_r_c20_proxy'; import { ERC721ProxyContract } from '../../src/generated_contract_wrappers/e_r_c721_proxy'; +import { artifacts } from '../../src/utils/artifacts'; import { expectRevertOrAlwaysFailingTransactionAsync } from '../../src/utils/assertions'; import { chaiSetup } from '../../src/utils/chai_setup'; import { constants } from '../../src/utils/constants'; import { ERC20Wrapper } from '../../src/utils/erc20_wrapper'; import { ERC721Wrapper } from '../../src/utils/erc721_wrapper'; -import { provider, web3Wrapper } from '../../src/utils/web3_wrapper'; +import { LogDecoder } from '../../src/utils/log_decoder'; +import { provider, txDefaults, web3Wrapper } from '../../src/utils/web3_wrapper'; chaiSetup.configure(); const expect = chai.expect; const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); +// tslint:disable:no-unnecessary-type-assertion describe('Asset Transfer Proxies', () => { let owner: string; let notAuthorized: string; @@ -28,6 +37,7 @@ describe('Asset Transfer Proxies', () => { let zrxToken: DummyERC20TokenContract; let erc721Token: DummyERC721TokenContract; + let erc721Receiver: DummyERC721ReceiverContract; let erc20Proxy: ERC20ProxyContract; let erc721Proxy: ERC721ProxyContract; @@ -69,6 +79,11 @@ describe('Asset Transfer Proxies', () => { }), constants.AWAIT_TRANSACTION_MINED_MS, ); + erc721Receiver = await DummyERC721ReceiverContract.deployFrom0xArtifactAsync( + artifacts.DummyERC721Receiver, + provider, + txDefaults, + ); }); beforeEach(async () => { await blockchainLifecycle.startAsync(); @@ -79,14 +94,14 @@ describe('Asset Transfer Proxies', () => { describe('Transfer Proxy - ERC20', () => { describe('transferFrom', () => { it('should successfully transfer tokens', async () => { - // Construct metadata for ERC20 proxy - const encodedProxyMetadata = assetProxyUtils.encodeERC20ProxyData(zrxToken.address); + // Construct ERC20 asset data + const encodedAssetData = assetProxyUtils.encodeERC20AssetData(zrxToken.address); // Perform a transfer from makerAddress to takerAddress const erc20Balances = await erc20Wrapper.getBalancesAsync(); const amount = new BigNumber(10); await web3Wrapper.awaitTransactionSuccessAsync( await erc20Proxy.transferFrom.sendTransactionAsync( - encodedProxyMetadata, + encodedAssetData, makerAddress, takerAddress, amount, @@ -105,14 +120,14 @@ describe('Asset Transfer Proxies', () => { }); it('should do nothing if transferring 0 amount of a token', async () => { - // Construct metadata for ERC20 proxy - const encodedProxyMetadata = assetProxyUtils.encodeERC20ProxyData(zrxToken.address); + // Construct ERC20 asset data + const encodedAssetData = assetProxyUtils.encodeERC20AssetData(zrxToken.address); // Perform a transfer from makerAddress to takerAddress const erc20Balances = await erc20Wrapper.getBalancesAsync(); const amount = new BigNumber(0); await web3Wrapper.awaitTransactionSuccessAsync( await erc20Proxy.transferFrom.sendTransactionAsync( - encodedProxyMetadata, + encodedAssetData, makerAddress, takerAddress, amount, @@ -131,8 +146,8 @@ describe('Asset Transfer Proxies', () => { }); it('should throw if allowances are too low', async () => { - // Construct metadata for ERC20 proxy - const encodedProxyMetadata = assetProxyUtils.encodeERC20ProxyData(zrxToken.address); + // Construct ERC20 asset data + const encodedAssetData = assetProxyUtils.encodeERC20AssetData(zrxToken.address); // Create allowance less than transfer amount. Set allowance on proxy. const allowance = new BigNumber(0); const transferAmount = new BigNumber(10); @@ -145,7 +160,7 @@ describe('Asset Transfer Proxies', () => { // Perform a transfer; expect this to fail. return expectRevertOrAlwaysFailingTransactionAsync( erc20Proxy.transferFrom.sendTransactionAsync( - encodedProxyMetadata, + encodedAssetData, makerAddress, takerAddress, transferAmount, @@ -155,20 +170,14 @@ describe('Asset Transfer Proxies', () => { }); it('should throw if requesting address is not authorized', async () => { - // Construct metadata for ERC20 proxy - const encodedProxyMetadata = assetProxyUtils.encodeERC20ProxyData(zrxToken.address); + // Construct ERC20 asset data + const encodedAssetData = assetProxyUtils.encodeERC20AssetData(zrxToken.address); // Perform a transfer from makerAddress to takerAddress const amount = new BigNumber(10); return expectRevertOrAlwaysFailingTransactionAsync( - erc20Proxy.transferFrom.sendTransactionAsync( - encodedProxyMetadata, - makerAddress, - takerAddress, - amount, - { - from: notAuthorized, - }, - ), + erc20Proxy.transferFrom.sendTransactionAsync(encodedAssetData, makerAddress, takerAddress, amount, { + from: notAuthorized, + }), ); }); }); @@ -177,16 +186,16 @@ describe('Asset Transfer Proxies', () => { it('should succesfully make multiple token transfers', async () => { const erc20Balances = await erc20Wrapper.getBalancesAsync(); - const encodedProxyMetadata = assetProxyUtils.encodeERC20ProxyData(zrxToken.address); + const encodedAssetData = assetProxyUtils.encodeERC20AssetData(zrxToken.address); const amount = new BigNumber(10); const numTransfers = 2; - const assetMetadata = _.times(numTransfers, () => encodedProxyMetadata); + const assetData = _.times(numTransfers, () => encodedAssetData); const fromAddresses = _.times(numTransfers, () => makerAddress); const toAddresses = _.times(numTransfers, () => takerAddress); const amounts = _.times(numTransfers, () => amount); const txHash = await erc20Proxy.batchTransferFrom.sendTransactionAsync( - assetMetadata, + assetData, fromAddresses, toAddresses, amounts, @@ -208,22 +217,18 @@ describe('Asset Transfer Proxies', () => { }); it('should throw if not called by an authorized address', async () => { - const encodedProxyMetadata = assetProxyUtils.encodeERC20ProxyData(zrxToken.address); + const encodedAssetData = assetProxyUtils.encodeERC20AssetData(zrxToken.address); const amount = new BigNumber(10); const numTransfers = 2; - const assetMetadata = _.times(numTransfers, () => encodedProxyMetadata); + const assetData = _.times(numTransfers, () => encodedAssetData); const fromAddresses = _.times(numTransfers, () => makerAddress); const toAddresses = _.times(numTransfers, () => takerAddress); const amounts = _.times(numTransfers, () => amount); return expectRevertOrAlwaysFailingTransactionAsync( - erc20Proxy.batchTransferFrom.sendTransactionAsync( - assetMetadata, - fromAddresses, - toAddresses, - amounts, - { from: notAuthorized }, - ), + erc20Proxy.batchTransferFrom.sendTransactionAsync(assetData, fromAddresses, toAddresses, amounts, { + from: notAuthorized, + }), ); }); }); @@ -237,11 +242,8 @@ describe('Asset Transfer Proxies', () => { describe('Transfer Proxy - ERC721', () => { describe('transferFrom', () => { it('should successfully transfer tokens', async () => { - // Construct metadata for ERC721 proxy - const encodedProxyMetadata = assetProxyUtils.encodeERC721ProxyData( - erc721Token.address, - erc721MakerTokenId, - ); + // Construct ERC721 asset data + const encodedAssetData = assetProxyUtils.encodeERC721AssetData(erc721Token.address, erc721MakerTokenId); // Verify pre-condition const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); expect(ownerMakerAsset).to.be.bignumber.equal(makerAddress); @@ -249,7 +251,7 @@ describe('Asset Transfer Proxies', () => { const amount = new BigNumber(1); await web3Wrapper.awaitTransactionSuccessAsync( await erc721Proxy.transferFrom.sendTransactionAsync( - encodedProxyMetadata, + encodedAssetData, makerAddress, takerAddress, amount, @@ -262,12 +264,94 @@ describe('Asset Transfer Proxies', () => { expect(newOwnerMakerAsset).to.be.bignumber.equal(takerAddress); }); - it('should throw if transferring 0 amount of a token', async () => { - // Construct metadata for ERC721 proxy - const encodedProxyMetadata = assetProxyUtils.encodeERC721ProxyData( + it('should not call onERC721Received when transferring to a smart contract without receiver data', async () => { + // Construct ERC721 asset data + const encodedAssetData = assetProxyUtils.encodeERC721AssetData(erc721Token.address, erc721MakerTokenId); + // Verify pre-condition + const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); + expect(ownerMakerAsset).to.be.bignumber.equal(makerAddress); + // Perform a transfer from makerAddress to takerAddress + const amount = new BigNumber(1); + const txHash = await erc721Proxy.transferFrom.sendTransactionAsync( + encodedAssetData, + makerAddress, + erc721Receiver.address, + amount, + { from: exchangeAddress }, + ); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + // Parse transaction logs + const logDecoder = new LogDecoder(web3Wrapper, erc721Receiver.address); + const tx = await logDecoder.getTxWithDecodedLogsAsync(txHash); + // Verify that no log was emitted by erc721 receiver + expect(tx.logs.length).to.be.equal(0); + // Verify transfer was successful + const newOwnerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); + expect(newOwnerMakerAsset).to.be.bignumber.equal(erc721Receiver.address); + }); + + it('should call onERC721Received when transferring to a smart contract with receiver data', async () => { + // Construct ERC721 asset data + const receiverData = ethUtil.bufferToHex(assetProxyUtils.encodeUint256(generatePseudoRandomSalt())); + const encodedAssetData = assetProxyUtils.encodeERC721AssetData( + erc721Token.address, + erc721MakerTokenId, + receiverData, + ); + // Verify pre-condition + const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); + expect(ownerMakerAsset).to.be.bignumber.equal(makerAddress); + // Perform a transfer from makerAddress to takerAddress + const amount = new BigNumber(1); + const txHash = await erc721Proxy.transferFrom.sendTransactionAsync( + encodedAssetData, + makerAddress, + erc721Receiver.address, + amount, + { from: exchangeAddress }, + ); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + // Parse transaction logs + const logDecoder = new LogDecoder(web3Wrapper, erc721Receiver.address); + const tx = await logDecoder.getTxWithDecodedLogsAsync(txHash); + // Validate log emitted by erc721 receiver + expect(tx.logs.length).to.be.equal(1); + const tokenReceivedLog = tx.logs[0] as LogWithDecodedArgs<TokenReceivedContractEventArgs>; + expect(tokenReceivedLog.args.from).to.be.equal(makerAddress); + expect(tokenReceivedLog.args.tokenId).to.be.bignumber.equal(erc721MakerTokenId); + expect(tokenReceivedLog.args.data).to.be.equal(receiverData); + // Verify transfer was successful + const newOwnerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); + expect(newOwnerMakerAsset).to.be.bignumber.equal(erc721Receiver.address); + }); + + it('should throw if there is receiver data but contract does not have onERC721Received', async () => { + // Construct ERC721 asset data + const receiverData = ethUtil.bufferToHex(assetProxyUtils.encodeUint256(generatePseudoRandomSalt())); + const encodedAssetData = assetProxyUtils.encodeERC721AssetData( erc721Token.address, erc721MakerTokenId, + receiverData, + ); + // Verify pre-condition + const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); + expect(ownerMakerAsset).to.be.bignumber.equal(makerAddress); + // Perform a transfer from makerAddress to takerAddress + const amount = new BigNumber(1); + return expectRevertOrAlwaysFailingTransactionAsync( + erc721Proxy.transferFrom.sendTransactionAsync( + encodedAssetData, + makerAddress, + erc20Proxy.address, // the ERC20 proxy does not have an ERC721 receiver + amount, + { from: exchangeAddress }, + ), ); + }); + + it('should throw if transferring 0 amount of a token', async () => { + // Construct ERC721 asset data + const encodedAssetData = assetProxyUtils.encodeERC721AssetData(erc721Token.address, erc721MakerTokenId); // Verify pre-condition const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); expect(ownerMakerAsset).to.be.bignumber.equal(makerAddress); @@ -275,7 +359,7 @@ describe('Asset Transfer Proxies', () => { const amount = new BigNumber(0); return expectRevertOrAlwaysFailingTransactionAsync( erc721Proxy.transferFrom.sendTransactionAsync( - encodedProxyMetadata, + encodedAssetData, makerAddress, takerAddress, amount, @@ -285,11 +369,8 @@ describe('Asset Transfer Proxies', () => { }); it('should throw if transferring > 1 amount of a token', async () => { - // Construct metadata for ERC721 proxy - const encodedProxyMetadata = assetProxyUtils.encodeERC721ProxyData( - erc721Token.address, - erc721MakerTokenId, - ); + // Construct ERC721 asset data + const encodedAssetData = assetProxyUtils.encodeERC721AssetData(erc721Token.address, erc721MakerTokenId); // Verify pre-condition const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); expect(ownerMakerAsset).to.be.bignumber.equal(makerAddress); @@ -297,7 +378,7 @@ describe('Asset Transfer Proxies', () => { const amount = new BigNumber(500); return expectRevertOrAlwaysFailingTransactionAsync( erc721Proxy.transferFrom.sendTransactionAsync( - encodedProxyMetadata, + encodedAssetData, makerAddress, takerAddress, amount, @@ -307,11 +388,8 @@ describe('Asset Transfer Proxies', () => { }); it('should throw if allowances are too low', async () => { - // Construct metadata for ERC721 proxy - const encodedProxyMetadata = assetProxyUtils.encodeERC721ProxyData( - erc721Token.address, - erc721MakerTokenId, - ); + // Construct ERC721 asset data + const encodedAssetData = assetProxyUtils.encodeERC721AssetData(erc721Token.address, erc721MakerTokenId); // Remove transfer approval for makerAddress. await web3Wrapper.awaitTransactionSuccessAsync( await erc721Token.setApprovalForAll.sendTransactionAsync(erc721Proxy.address, false, { @@ -322,29 +400,20 @@ describe('Asset Transfer Proxies', () => { // Perform a transfer; expect this to fail. const amount = new BigNumber(1); return expectRevertOrAlwaysFailingTransactionAsync( - erc20Proxy.transferFrom.sendTransactionAsync( - encodedProxyMetadata, - makerAddress, - takerAddress, - amount, - { - from: notAuthorized, - }, - ), + erc20Proxy.transferFrom.sendTransactionAsync(encodedAssetData, makerAddress, takerAddress, amount, { + from: notAuthorized, + }), ); }); it('should throw if requesting address is not authorized', async () => { - // Construct metadata for ERC721 proxy - const encodedProxyMetadata = assetProxyUtils.encodeERC721ProxyData( - erc721Token.address, - erc721MakerTokenId, - ); + // Construct ERC721 asset data + const encodedAssetData = assetProxyUtils.encodeERC721AssetData(erc721Token.address, erc721MakerTokenId); // Perform a transfer from makerAddress to takerAddress const amount = new BigNumber(1); return expectRevertOrAlwaysFailingTransactionAsync( erc721Proxy.transferFrom.sendTransactionAsync( - encodedProxyMetadata, + encodedAssetData, makerAddress, takerAddress, amount, @@ -360,16 +429,16 @@ describe('Asset Transfer Proxies', () => { const [makerTokenIdA, makerTokenIdB] = erc721TokensById[makerAddress][erc721Token.address]; const numTransfers = 2; - const assetMetadata = [ - assetProxyUtils.encodeERC721ProxyData(erc721Token.address, makerTokenIdA), - assetProxyUtils.encodeERC721ProxyData(erc721Token.address, makerTokenIdB), + const assetData = [ + assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerTokenIdA), + assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerTokenIdB), ]; const fromAddresses = _.times(numTransfers, () => makerAddress); const toAddresses = _.times(numTransfers, () => takerAddress); const amounts = _.times(numTransfers, () => new BigNumber(1)); const txHash = await erc721Proxy.batchTransferFrom.sendTransactionAsync( - assetMetadata, + assetData, fromAddresses, toAddresses, amounts, @@ -392,22 +461,18 @@ describe('Asset Transfer Proxies', () => { const [makerTokenIdA, makerTokenIdB] = erc721TokensById[makerAddress][erc721Token.address]; const numTransfers = 2; - const assetMetadata = [ - assetProxyUtils.encodeERC721ProxyData(erc721Token.address, makerTokenIdA), - assetProxyUtils.encodeERC721ProxyData(erc721Token.address, makerTokenIdB), + const assetData = [ + assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerTokenIdA), + assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerTokenIdB), ]; const fromAddresses = _.times(numTransfers, () => makerAddress); const toAddresses = _.times(numTransfers, () => takerAddress); const amounts = _.times(numTransfers, () => new BigNumber(1)); return expectRevertOrAlwaysFailingTransactionAsync( - erc721Proxy.batchTransferFrom.sendTransactionAsync( - assetMetadata, - fromAddresses, - toAddresses, - amounts, - { from: notAuthorized }, - ), + erc721Proxy.batchTransferFrom.sendTransactionAsync(assetData, fromAddresses, toAddresses, amounts, { + from: notAuthorized, + }), ); }); }); @@ -418,3 +483,4 @@ describe('Asset Transfer Proxies', () => { }); }); }); +// tslint:enable:no-unnecessary-type-assertion diff --git a/packages/contracts/test/exchange/core.ts b/packages/contracts/test/exchange/core.ts index bee74cca8..53b98c755 100644 --- a/packages/contracts/test/exchange/core.ts +++ b/packages/contracts/test/exchange/core.ts @@ -87,7 +87,7 @@ describe('Exchange core', () => { artifacts.Exchange, provider, txDefaults, - assetProxyUtils.encodeERC20ProxyData(zrxToken.address), + assetProxyUtils.encodeERC20AssetData(zrxToken.address), ); exchangeWrapper = new ExchangeWrapper(exchange, provider); await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC20, erc20Proxy.address, owner); @@ -114,8 +114,8 @@ describe('Exchange core', () => { exchangeAddress: exchange.address, makerAddress, feeRecipientAddress, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultMakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultTakerAssetAddress), + makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultMakerAssetAddress), + takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultTakerAssetAddress), }; const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)]; orderFactory = new OrderFactory(privateKey, defaultOrderParams); @@ -711,8 +711,8 @@ describe('Exchange core', () => { signedOrder = orderFactory.newSignedOrder({ makerAssetAmount: new BigNumber(1), takerAssetAmount: new BigNumber(1), - makerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, makerAssetId), - takerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, takerAssetId), + makerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerAssetId), + takerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, takerAssetId), }); // Verify pre-conditions const initialOwnerMakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId); @@ -737,8 +737,8 @@ describe('Exchange core', () => { signedOrder = orderFactory.newSignedOrder({ makerAssetAmount: new BigNumber(1), takerAssetAmount: new BigNumber(1), - makerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, makerAssetId), - takerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, takerAssetId), + makerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerAssetId), + takerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, takerAssetId), }); // Verify pre-conditions const initialOwnerMakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId); @@ -759,8 +759,8 @@ describe('Exchange core', () => { signedOrder = orderFactory.newSignedOrder({ makerAssetAmount: new BigNumber(1), takerAssetAmount: new BigNumber(1), - makerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, makerAssetId), - takerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, takerAssetId), + makerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerAssetId), + takerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, takerAssetId), }); // Verify pre-conditions const initialOwnerMakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId); @@ -781,8 +781,8 @@ describe('Exchange core', () => { signedOrder = orderFactory.newSignedOrder({ makerAssetAmount: new BigNumber(2), takerAssetAmount: new BigNumber(1), - makerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, makerAssetId), - takerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, takerAssetId), + makerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerAssetId), + takerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, takerAssetId), }); // Verify pre-conditions const initialOwnerMakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId); @@ -803,8 +803,8 @@ describe('Exchange core', () => { signedOrder = orderFactory.newSignedOrder({ makerAssetAmount: new BigNumber(1), takerAssetAmount: new BigNumber(500), - makerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, makerAssetId), - takerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, takerAssetId), + makerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerAssetId), + takerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, takerAssetId), }); // Verify pre-conditions const initialOwnerMakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId); @@ -825,8 +825,8 @@ describe('Exchange core', () => { signedOrder = orderFactory.newSignedOrder({ makerAssetAmount: new BigNumber(1), takerAssetAmount: new BigNumber(0), - makerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, makerAssetId), - takerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, takerAssetId), + makerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerAssetId), + takerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, takerAssetId), }); // Verify pre-conditions const initialOwnerMakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId); @@ -846,8 +846,8 @@ describe('Exchange core', () => { signedOrder = orderFactory.newSignedOrder({ makerAssetAmount: new BigNumber(1), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), - makerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, makerAssetId), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultTakerAssetAddress), + makerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerAssetId), + takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultTakerAssetAddress), }); // Verify pre-conditions const initialOwnerMakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId); @@ -886,8 +886,8 @@ describe('Exchange core', () => { signedOrder = orderFactory.newSignedOrder({ takerAssetAmount: new BigNumber(1), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), - takerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, takerAssetId), - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultMakerAssetAddress), + takerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, takerAssetId), + makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultMakerAssetAddress), }); // Verify pre-conditions const initialOwnerTakerAsset = await erc721Token.ownerOf.callAsync(takerAssetId); diff --git a/packages/contracts/test/exchange/dispatcher.ts b/packages/contracts/test/exchange/dispatcher.ts index 02d43a132..9e113e47d 100644 --- a/packages/contracts/test/exchange/dispatcher.ts +++ b/packages/contracts/test/exchange/dispatcher.ts @@ -275,13 +275,13 @@ describe('AssetProxyDispatcher', () => { constants.AWAIT_TRANSACTION_MINED_MS, ); // Construct metadata for ERC20 proxy - const encodedProxyMetadata = assetProxyUtils.encodeERC20ProxyData(zrxToken.address); + const encodedAssetData = assetProxyUtils.encodeERC20AssetData(zrxToken.address); // Perform a transfer from makerAddress to takerAddress const erc20Balances = await erc20Wrapper.getBalancesAsync(); const amount = new BigNumber(10); await web3Wrapper.awaitTransactionSuccessAsync( await assetProxyDispatcher.publicDispatchTransferFrom.sendTransactionAsync( - encodedProxyMetadata, + encodedAssetData, makerAddress, takerAddress, amount, @@ -301,12 +301,12 @@ describe('AssetProxyDispatcher', () => { it('should throw if dispatching to unregistered proxy', async () => { // Construct metadata for ERC20 proxy - const encodedProxyMetadata = assetProxyUtils.encodeERC20ProxyData(zrxToken.address); + const encodedAssetData = assetProxyUtils.encodeERC20AssetData(zrxToken.address); // Perform a transfer from makerAddress to takerAddress const amount = new BigNumber(10); return expectRevertOrAlwaysFailingTransactionAsync( assetProxyDispatcher.publicDispatchTransferFrom.sendTransactionAsync( - encodedProxyMetadata, + encodedAssetData, makerAddress, takerAddress, amount, diff --git a/packages/contracts/test/exchange/libs.ts b/packages/contracts/test/exchange/libs.ts index b12934014..eff05981d 100644 --- a/packages/contracts/test/exchange/libs.ts +++ b/packages/contracts/test/exchange/libs.ts @@ -38,8 +38,8 @@ describe('Exchange libs', () => { exchangeAddress: libs.address, makerAddress, feeRecipientAddress: addressUtils.generatePseudoRandomAddress(), - makerAssetData: assetProxyUtils.encodeERC20ProxyData(addressUtils.generatePseudoRandomAddress()), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(addressUtils.generatePseudoRandomAddress()), + makerAssetData: assetProxyUtils.encodeERC20AssetData(addressUtils.generatePseudoRandomAddress()), + takerAssetData: assetProxyUtils.encodeERC20AssetData(addressUtils.generatePseudoRandomAddress()), }; const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)]; orderFactory = new OrderFactory(privateKey, defaultOrderParams); diff --git a/packages/contracts/test/exchange/match_orders.ts b/packages/contracts/test/exchange/match_orders.ts index aab3308ae..18a46187f 100644 --- a/packages/contracts/test/exchange/match_orders.ts +++ b/packages/contracts/test/exchange/match_orders.ts @@ -96,7 +96,7 @@ describe('matchOrders', () => { artifacts.Exchange, provider, txDefaults, - assetProxyUtils.encodeERC20ProxyData(zrxToken.address), + assetProxyUtils.encodeERC20AssetData(zrxToken.address), ); exchangeWrapper = new ExchangeWrapper(exchange, provider); await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC20, erc20Proxy.address, owner); @@ -122,8 +122,8 @@ describe('matchOrders', () => { const defaultOrderParams = { ...constants.STATIC_ORDER_PARAMS, exchangeAddress: exchange.address, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), + makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), + takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), }; const privateKeyLeft = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddressLeft)]; orderFactoryLeft = new OrderFactory(privateKeyLeft, defaultOrderParams); @@ -148,16 +148,14 @@ describe('matchOrders', () => { // Create orders to match const signedOrderLeft = orderFactoryLeft.newSignedOrder({ makerAddress: makerAddressLeft, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = orderFactoryRight.newSignedOrder({ makerAddress: makerAddressRight, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), + makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), + takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), feeRecipientAddress: feeRecipientAddressRight, @@ -182,16 +180,14 @@ describe('matchOrders', () => { // Create orders to match const signedOrderLeft = orderFactoryLeft.newSignedOrder({ makerAddress: makerAddressLeft, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = orderFactoryRight.newSignedOrder({ makerAddress: makerAddressRight, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), + makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), + takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), feeRecipientAddress: feeRecipientAddressRight, @@ -227,16 +223,14 @@ describe('matchOrders', () => { // Create orders to match const signedOrderLeft = orderFactoryLeft.newSignedOrder({ makerAddress: makerAddressLeft, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = orderFactoryRight.newSignedOrder({ makerAddress: makerAddressRight, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), + makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), + takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(20), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 18), feeRecipientAddress: feeRecipientAddressRight, @@ -261,16 +255,14 @@ describe('matchOrders', () => { // Create orders to match const signedOrderLeft = orderFactoryLeft.newSignedOrder({ makerAddress: makerAddressLeft, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = orderFactoryRight.newSignedOrder({ makerAddress: makerAddressRight, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), + makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), + takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), feeRecipientAddress: feeRecipientAddressRight, @@ -295,16 +287,14 @@ describe('matchOrders', () => { // Create orders to match const signedOrderLeft = orderFactoryLeft.newSignedOrder({ makerAddress: makerAddressLeft, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = orderFactoryRight.newSignedOrder({ makerAddress: makerAddressRight, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), + makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), + takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), feeRecipientAddress: feeRecipientAddressRight, @@ -334,8 +324,8 @@ describe('matchOrders', () => { // branch in the contract twice for this test. const signedOrderRight2 = orderFactoryRight.newSignedOrder({ makerAddress: makerAddressRight, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), + makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), + takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18), feeRecipientAddress: feeRecipientAddressRight, @@ -364,8 +354,6 @@ describe('matchOrders', () => { // Create orders to match const signedOrderLeft = orderFactoryLeft.newSignedOrder({ makerAddress: makerAddressLeft, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), feeRecipientAddress: feeRecipientAddressLeft, @@ -373,8 +361,8 @@ describe('matchOrders', () => { const signedOrderRight = orderFactoryRight.newSignedOrder({ makerAddress: makerAddressRight, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), + makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), + takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), feeRecipientAddress: feeRecipientAddressRight, @@ -404,8 +392,6 @@ describe('matchOrders', () => { // branch in the contract twice for this test. const signedOrderLeft2 = orderFactoryLeft.newSignedOrder({ makerAddress: makerAddressLeft, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18), feeRecipientAddress: feeRecipientAddressLeft, @@ -437,16 +423,14 @@ describe('matchOrders', () => { const feeRecipientAddress = feeRecipientAddressLeft; const signedOrderLeft = orderFactoryLeft.newSignedOrder({ makerAddress: makerAddressLeft, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), feeRecipientAddress, }); const signedOrderRight = orderFactoryRight.newSignedOrder({ makerAddress: makerAddressRight, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), + makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), + takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), feeRecipientAddress, @@ -465,16 +449,14 @@ describe('matchOrders', () => { // Create orders to match const signedOrderLeft = orderFactoryLeft.newSignedOrder({ makerAddress: makerAddressLeft, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = orderFactoryRight.newSignedOrder({ makerAddress: makerAddressRight, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), + makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), + takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), feeRecipientAddress: feeRecipientAddressRight, @@ -494,16 +476,14 @@ describe('matchOrders', () => { // Create orders to match const signedOrderLeft = orderFactoryLeft.newSignedOrder({ makerAddress: makerAddressLeft, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = orderFactoryRight.newSignedOrder({ makerAddress: makerAddressRight, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), + makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), + takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), feeRecipientAddress: feeRecipientAddressRight, @@ -523,16 +503,14 @@ describe('matchOrders', () => { // Create orders to match const signedOrderLeft = orderFactoryLeft.newSignedOrder({ makerAddress: makerAddressLeft, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = orderFactoryRight.newSignedOrder({ makerAddress: makerAddressRight, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), + makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), + takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), feeRecipientAddress: feeRecipientAddressRight, @@ -552,16 +530,14 @@ describe('matchOrders', () => { // Create orders to match const signedOrderLeft = orderFactoryLeft.newSignedOrder({ makerAddress: makerAddressLeft, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = orderFactoryRight.newSignedOrder({ makerAddress: makerAddressRight, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), + makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), + takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), feeRecipientAddress: feeRecipientAddressRight, @@ -581,16 +557,14 @@ describe('matchOrders', () => { // Create orders to match const signedOrderLeft = orderFactoryLeft.newSignedOrder({ makerAddress: makerAddressLeft, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), feeRecipientAddress: makerAddressLeft, }); const signedOrderRight = orderFactoryRight.newSignedOrder({ makerAddress: makerAddressRight, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), + makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), + takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), feeRecipientAddress: makerAddressRight, @@ -609,16 +583,14 @@ describe('matchOrders', () => { // Create orders to match const signedOrderLeft = orderFactoryLeft.newSignedOrder({ makerAddress: makerAddressLeft, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = orderFactoryRight.newSignedOrder({ makerAddress: makerAddressRight, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), + makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), + takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), feeRecipientAddress: feeRecipientAddressRight, @@ -635,16 +607,14 @@ describe('matchOrders', () => { // Create orders to match const signedOrderLeft = orderFactoryLeft.newSignedOrder({ makerAddress: makerAddressLeft, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = orderFactoryRight.newSignedOrder({ makerAddress: makerAddressRight, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), + makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), + takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), feeRecipientAddress: feeRecipientAddressRight, @@ -661,16 +631,14 @@ describe('matchOrders', () => { // Create orders to match const signedOrderLeft = orderFactoryLeft.newSignedOrder({ makerAddress: makerAddressLeft, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = orderFactoryRight.newSignedOrder({ makerAddress: makerAddressRight, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), + makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), + takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), 18), feeRecipientAddress: feeRecipientAddressRight, @@ -691,16 +659,14 @@ describe('matchOrders', () => { // Create orders to match const signedOrderLeft = orderFactoryLeft.newSignedOrder({ makerAddress: makerAddressLeft, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = orderFactoryRight.newSignedOrder({ makerAddress: makerAddressRight, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), + makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), + takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), feeRecipientAddress: feeRecipientAddressRight, @@ -721,16 +687,16 @@ describe('matchOrders', () => { // Create orders to match const signedOrderLeft = orderFactoryLeft.newSignedOrder({ makerAddress: makerAddressLeft, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), + makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), + takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = orderFactoryRight.newSignedOrder({ makerAddress: makerAddressRight, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), + makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), + takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), feeRecipientAddress: feeRecipientAddressRight, @@ -752,16 +718,16 @@ describe('matchOrders', () => { const erc721TokenToTransfer = erc721LeftMakerAssetIds[0]; const signedOrderLeft = orderFactoryLeft.newSignedOrder({ makerAddress: makerAddressLeft, - makerAssetData: assetProxyUtils.encodeERC721ProxyData(defaultERC721AssetAddress, erc721TokenToTransfer), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), + makerAssetData: assetProxyUtils.encodeERC721AssetData(defaultERC721AssetAddress, erc721TokenToTransfer), + takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), makerAssetAmount: new BigNumber(1), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = orderFactoryRight.newSignedOrder({ makerAddress: makerAddressRight, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC721ProxyData(defaultERC721AssetAddress, erc721TokenToTransfer), + makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), + takerAssetData: assetProxyUtils.encodeERC721AssetData(defaultERC721AssetAddress, erc721TokenToTransfer), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: new BigNumber(1), feeRecipientAddress: feeRecipientAddressRight, @@ -787,16 +753,16 @@ describe('matchOrders', () => { const erc721TokenToTransfer = erc721RightMakerAssetIds[0]; const signedOrderLeft = orderFactoryLeft.newSignedOrder({ makerAddress: makerAddressLeft, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC721ProxyData(defaultERC721AssetAddress, erc721TokenToTransfer), + makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), + takerAssetData: assetProxyUtils.encodeERC721AssetData(defaultERC721AssetAddress, erc721TokenToTransfer), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: new BigNumber(1), feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = orderFactoryRight.newSignedOrder({ makerAddress: makerAddressRight, - makerAssetData: assetProxyUtils.encodeERC721ProxyData(defaultERC721AssetAddress, erc721TokenToTransfer), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress), + makerAssetData: assetProxyUtils.encodeERC721AssetData(defaultERC721AssetAddress, erc721TokenToTransfer), + takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: new BigNumber(1), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), feeRecipientAddress: feeRecipientAddressRight, diff --git a/packages/contracts/test/exchange/signature_validator.ts b/packages/contracts/test/exchange/signature_validator.ts index 79db620b4..c39fd6ee4 100644 --- a/packages/contracts/test/exchange/signature_validator.ts +++ b/packages/contracts/test/exchange/signature_validator.ts @@ -42,8 +42,8 @@ describe('MixinSignatureValidator', () => { exchangeAddress: signatureValidator.address, makerAddress, feeRecipientAddress: addressUtils.generatePseudoRandomAddress(), - makerAssetData: assetProxyUtils.encodeERC20ProxyData(addressUtils.generatePseudoRandomAddress()), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(addressUtils.generatePseudoRandomAddress()), + makerAssetData: assetProxyUtils.encodeERC20AssetData(addressUtils.generatePseudoRandomAddress()), + takerAssetData: assetProxyUtils.encodeERC20AssetData(addressUtils.generatePseudoRandomAddress()), }; const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)]; orderFactory = new OrderFactory(privateKey, defaultOrderParams); diff --git a/packages/contracts/test/exchange/transactions.ts b/packages/contracts/test/exchange/transactions.ts index 7897fa558..12390ce01 100644 --- a/packages/contracts/test/exchange/transactions.ts +++ b/packages/contracts/test/exchange/transactions.ts @@ -72,7 +72,7 @@ describe('Exchange transactions', () => { artifacts.Exchange, provider, txDefaults, - assetProxyUtils.encodeERC20ProxyData(zrxToken.address), + assetProxyUtils.encodeERC20AssetData(zrxToken.address), ); exchangeWrapper = new ExchangeWrapper(exchange, provider); await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC20, erc20Proxy.address, owner); @@ -91,8 +91,8 @@ describe('Exchange transactions', () => { exchangeAddress: exchange.address, makerAddress, feeRecipientAddress, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultMakerTokenAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultTakerTokenAddress), + makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultMakerTokenAddress), + takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultTakerTokenAddress), }; makerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)]; takerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(takerAddress)]; @@ -224,8 +224,8 @@ describe('Exchange transactions', () => { exchangeAddress: exchange.address, makerAddress, feeRecipientAddress, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultMakerTokenAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultTakerTokenAddress), + makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultMakerTokenAddress), + takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultTakerTokenAddress), }; whitelistOrderFactory = new OrderFactory(makerPrivateKey, defaultOrderParams); }); diff --git a/packages/contracts/test/exchange/wrapper.ts b/packages/contracts/test/exchange/wrapper.ts index 258e1ff81..b66cff90a 100644 --- a/packages/contracts/test/exchange/wrapper.ts +++ b/packages/contracts/test/exchange/wrapper.ts @@ -81,7 +81,7 @@ describe('Exchange wrappers', () => { artifacts.Exchange, provider, txDefaults, - assetProxyUtils.encodeERC20ProxyData(zrxToken.address), + assetProxyUtils.encodeERC20AssetData(zrxToken.address), ); exchangeWrapper = new ExchangeWrapper(exchange, provider); await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC20, erc20Proxy.address, owner); @@ -108,8 +108,8 @@ describe('Exchange wrappers', () => { exchangeAddress: exchange.address, makerAddress, feeRecipientAddress, - makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultMakerAssetAddress), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultTakerAssetAddress), + makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultMakerAssetAddress), + takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultTakerAssetAddress), }; const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)]; orderFactory = new OrderFactory(privateKey, defaultOrderParams); @@ -304,7 +304,7 @@ describe('Exchange wrappers', () => { const signedOrder = orderFactory.newSignedOrder({ makerAssetAmount: makerZRXBalance, makerFee: new BigNumber(1), - makerAssetData: assetProxyUtils.encodeERC20ProxyData(zrxToken.address), + makerAssetData: assetProxyUtils.encodeERC20AssetData(zrxToken.address), }); await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress); const newBalances = await erc20Wrapper.getBalancesAsync(); @@ -316,7 +316,7 @@ describe('Exchange wrappers', () => { const signedOrder = orderFactory.newSignedOrder({ makerAssetAmount: new BigNumber(makerZRXAllowance), makerFee: new BigNumber(1), - makerAssetData: assetProxyUtils.encodeERC20ProxyData(zrxToken.address), + makerAssetData: assetProxyUtils.encodeERC20AssetData(zrxToken.address), }); await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress); const newBalances = await erc20Wrapper.getBalancesAsync(); @@ -328,7 +328,7 @@ describe('Exchange wrappers', () => { const signedOrder = orderFactory.newSignedOrder({ takerAssetAmount: takerZRXBalance, takerFee: new BigNumber(1), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(zrxToken.address), + takerAssetData: assetProxyUtils.encodeERC20AssetData(zrxToken.address), }); await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress); const newBalances = await erc20Wrapper.getBalancesAsync(); @@ -340,7 +340,7 @@ describe('Exchange wrappers', () => { const signedOrder = orderFactory.newSignedOrder({ takerAssetAmount: new BigNumber(takerZRXAllowance), takerFee: new BigNumber(1), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(zrxToken.address), + takerAssetData: assetProxyUtils.encodeERC20AssetData(zrxToken.address), }); await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress); const newBalances = await erc20Wrapper.getBalancesAsync(); @@ -354,8 +354,8 @@ describe('Exchange wrappers', () => { const signedOrder = orderFactory.newSignedOrder({ makerAssetAmount: new BigNumber(1), takerAssetAmount: new BigNumber(1), - makerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, makerAssetId), - takerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, takerAssetId), + makerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerAssetId), + takerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, takerAssetId), }); // Verify pre-conditions const initialOwnerMakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId); @@ -692,7 +692,7 @@ describe('Exchange wrappers', () => { signedOrders = [ orderFactory.newSignedOrder(), orderFactory.newSignedOrder({ - takerAssetData: assetProxyUtils.encodeERC20ProxyData(zrxToken.address), + takerAssetData: assetProxyUtils.encodeERC20AssetData(zrxToken.address), }), orderFactory.newSignedOrder(), ]; @@ -785,7 +785,7 @@ describe('Exchange wrappers', () => { signedOrders = [ orderFactory.newSignedOrder(), orderFactory.newSignedOrder({ - takerAssetData: assetProxyUtils.encodeERC20ProxyData(zrxToken.address), + takerAssetData: assetProxyUtils.encodeERC20AssetData(zrxToken.address), }), orderFactory.newSignedOrder(), ]; @@ -874,7 +874,7 @@ describe('Exchange wrappers', () => { signedOrders = [ orderFactory.newSignedOrder(), orderFactory.newSignedOrder({ - makerAssetData: assetProxyUtils.encodeERC20ProxyData(zrxToken.address), + makerAssetData: assetProxyUtils.encodeERC20AssetData(zrxToken.address), }), orderFactory.newSignedOrder(), ]; @@ -967,7 +967,7 @@ describe('Exchange wrappers', () => { signedOrders = [ orderFactory.newSignedOrder(), orderFactory.newSignedOrder({ - makerAssetData: assetProxyUtils.encodeERC20ProxyData(zrxToken.address), + makerAssetData: assetProxyUtils.encodeERC20AssetData(zrxToken.address), }), orderFactory.newSignedOrder(), ]; diff --git a/packages/contracts/test/libraries/lib_bytes.ts b/packages/contracts/test/libraries/lib_bytes.ts index 0996cdc84..2fefb7aeb 100644 --- a/packages/contracts/test/libraries/lib_bytes.ts +++ b/packages/contracts/test/libraries/lib_bytes.ts @@ -1,4 +1,5 @@ import { BlockchainLifecycle } from '@0xproject/dev-utils'; +import { assetProxyUtils, generatePseudoRandomSalt } from '@0xproject/order-utils'; import { BigNumber } from '@0xproject/utils'; import BN = require('bn.js'); import * as chai from 'chai'; @@ -28,6 +29,15 @@ describe('LibBytes', () => { let testAddress: string; const testBytes32 = '0x102030405060708090a0b0c0d0e0f0102030405060708090a0b0c0d0e0f01020'; const testUint256 = new BigNumber(testBytes32, 16); + let shortData: string; + let shortTestBytes: string; + let shortTestBytesAsBuffer: Buffer; + let wordOfData: string; + let wordOfTestBytes: string; + let wordOfTestBytesAsBuffer: Buffer; + let longData: string; + let longTestBytes: string; + let longTestBytesAsBuffer: Buffer; before(async () => { await blockchainLifecycle.startAsync(); @@ -48,6 +58,26 @@ describe('LibBytes', () => { expect(byteArrayLongerThan32BytesLength).to.be.greaterThan(32); const testBytes32Length = ethUtil.toBuffer(testBytes32).byteLength; expect(testBytes32Length).to.be.equal(32); + // Create short test bytes + shortData = '0xffffaa'; + const encodedShortData = ethUtil.toBuffer(shortData); + const shortDataLength = new BigNumber(encodedShortData.byteLength); + const encodedShortDataLength = assetProxyUtils.encodeUint256(shortDataLength); + shortTestBytesAsBuffer = Buffer.concat([encodedShortDataLength, encodedShortData]); + shortTestBytes = ethUtil.bufferToHex(shortTestBytesAsBuffer); + // Create test bytes one word in length + wordOfData = ethUtil.bufferToHex(assetProxyUtils.encodeUint256(generatePseudoRandomSalt())); + const encodedWordOfData = ethUtil.toBuffer(wordOfData); + const wordOfDataLength = new BigNumber(encodedWordOfData.byteLength); + const encodedWordOfDataLength = assetProxyUtils.encodeUint256(wordOfDataLength); + wordOfTestBytesAsBuffer = Buffer.concat([encodedWordOfDataLength, encodedWordOfData]); + wordOfTestBytes = ethUtil.bufferToHex(wordOfTestBytesAsBuffer); + // Create long test bytes (combines short test bytes with word of test bytes) + longData = ethUtil.bufferToHex(Buffer.concat([encodedShortData, encodedWordOfData])); + const longDataLength = new BigNumber(encodedShortData.byteLength + encodedWordOfData.byteLength); + const encodedLongDataLength = assetProxyUtils.encodeUint256(longDataLength); + longTestBytesAsBuffer = Buffer.concat([encodedLongDataLength, encodedShortData, encodedWordOfData]); + longTestBytes = ethUtil.bufferToHex(longTestBytesAsBuffer); }); beforeEach(async () => { await blockchainLifecycle.startAsync(); @@ -60,7 +90,7 @@ describe('LibBytes', () => { it('should revert if length is 0', async () => { return expectRevertOrOtherErrorAsync( libBytes.publicPopByte.callAsync(constants.NULL_BYTES), - constants.LIB_BYTES_GT_ZERO_LENGTH_REQUIRED, + constants.LIB_BYTES_GREATER_THAN_ZERO_LENGTH_REQUIRED, ); }); @@ -77,7 +107,7 @@ describe('LibBytes', () => { it('should revert if length is less than 20', async () => { return expectRevertOrOtherErrorAsync( libBytes.publicPopAddress.callAsync(byteArrayShorterThan20Bytes), - constants.LIB_BYTES_GTE_20_LENGTH_REQUIRED, + constants.LIB_BYTES_GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED, ); }); @@ -163,7 +193,7 @@ describe('LibBytes', () => { const offset = new BigNumber(0); return expectRevertOrOtherErrorAsync( libBytes.publicReadAddress.callAsync(shortByteArray, offset), - constants.LIB_BYTES_GTE_20_LENGTH_REQUIRED, + constants.LIB_BYTES_GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED, ); }); @@ -172,7 +202,7 @@ describe('LibBytes', () => { const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength); return expectRevertOrOtherErrorAsync( libBytes.publicReadAddress.callAsync(byteArray, badOffset), - constants.LIB_BYTES_GTE_20_LENGTH_REQUIRED, + constants.LIB_BYTES_GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED, ); }); }); @@ -209,7 +239,7 @@ describe('LibBytes', () => { const offset = new BigNumber(0); return expectRevertOrOtherErrorAsync( libBytes.publicReadBytes32.callAsync(byteArrayShorterThan32Bytes, offset), - constants.LIB_BYTES_GTE_32_LENGTH_REQUIRED, + constants.LIB_BYTES_GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED, ); }); @@ -217,7 +247,7 @@ describe('LibBytes', () => { const badOffset = new BigNumber(ethUtil.toBuffer(testBytes32).byteLength); return expectRevertOrOtherErrorAsync( libBytes.publicReadBytes32.callAsync(testBytes32, badOffset), - constants.LIB_BYTES_GTE_32_LENGTH_REQUIRED, + constants.LIB_BYTES_GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED, ); }); }); @@ -258,7 +288,7 @@ describe('LibBytes', () => { const offset = new BigNumber(0); return expectRevertOrOtherErrorAsync( libBytes.publicReadUint256.callAsync(byteArrayShorterThan32Bytes, offset), - constants.LIB_BYTES_GTE_32_LENGTH_REQUIRED, + constants.LIB_BYTES_GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED, ); }); @@ -269,7 +299,7 @@ describe('LibBytes', () => { const badOffset = new BigNumber(testUint256AsBuffer.byteLength); return expectRevertOrOtherErrorAsync( libBytes.publicReadUint256.callAsync(byteArray, badOffset), - constants.LIB_BYTES_GTE_32_LENGTH_REQUIRED, + constants.LIB_BYTES_GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED, ); }); }); @@ -291,7 +321,7 @@ describe('LibBytes', () => { const byteArrayLessThan4Bytes = '0x010101'; return expectRevertOrOtherErrorAsync( libBytes.publicReadFirst4.callAsync(byteArrayLessThan4Bytes), - constants.LIB_BYTES_GTE_4_LENGTH_REQUIRED, + constants.LIB_BYTES_GREATER_OR_EQUAL_TO_4_LENGTH_REQUIRED, ); }); it('should return the first 4 bytes of a byte array of arbitrary length', async () => { @@ -300,4 +330,180 @@ describe('LibBytes', () => { expect(first4Bytes).to.equal(expectedFirst4Bytes); }); }); + + describe('readBytes', () => { + it('should successfully read short, nested array of bytes when it takes up the whole array', async () => { + const testBytesOffset = new BigNumber(0); + const bytes = await libBytes.publicReadBytes.callAsync(shortTestBytes, testBytesOffset); + return expect(bytes).to.be.equal(shortData); + }); + + it('should successfully read short, nested array of bytes when it is offset in the array', async () => { + const prefixByteArrayBuffer = ethUtil.toBuffer('0xabcdef'); + const combinedByteArrayBuffer = Buffer.concat([prefixByteArrayBuffer, shortTestBytesAsBuffer]); + const combinedByteArray = ethUtil.bufferToHex(combinedByteArrayBuffer); + const testUint256Offset = new BigNumber(prefixByteArrayBuffer.byteLength); + const bytes = await libBytes.publicReadBytes.callAsync(combinedByteArray, testUint256Offset); + return expect(bytes).to.be.equal(shortData); + }); + + it('should successfully read a nested array of bytes - one word in length - when it takes up the whole array', async () => { + const testBytesOffset = new BigNumber(0); + const bytes = await libBytes.publicReadBytes.callAsync(wordOfTestBytes, testBytesOffset); + return expect(bytes).to.be.equal(wordOfData); + }); + + it('should successfully read a nested array of bytes - one word in length - when it is offset in the array', async () => { + const prefixByteArrayBuffer = ethUtil.toBuffer('0xabcdef'); + const combinedByteArrayBuffer = Buffer.concat([prefixByteArrayBuffer, wordOfTestBytesAsBuffer]); + const combinedByteArray = ethUtil.bufferToHex(combinedByteArrayBuffer); + const testUint256Offset = new BigNumber(prefixByteArrayBuffer.byteLength); + const bytes = await libBytes.publicReadBytes.callAsync(combinedByteArray, testUint256Offset); + return expect(bytes).to.be.equal(wordOfData); + }); + + it('should successfully read long, nested array of bytes when it takes up the whole array', async () => { + const testBytesOffset = new BigNumber(0); + const bytes = await libBytes.publicReadBytes.callAsync(longTestBytes, testBytesOffset); + return expect(bytes).to.be.equal(longData); + }); + + it('should successfully read long, nested array of bytes when it is offset in the array', async () => { + const prefixByteArrayBuffer = ethUtil.toBuffer('0xabcdef'); + const combinedByteArrayBuffer = Buffer.concat([prefixByteArrayBuffer, longTestBytesAsBuffer]); + const combinedByteArray = ethUtil.bufferToHex(combinedByteArrayBuffer); + const testUint256Offset = new BigNumber(prefixByteArrayBuffer.byteLength); + const bytes = await libBytes.publicReadBytes.callAsync(combinedByteArray, testUint256Offset); + return expect(bytes).to.be.equal(longData); + }); + + it('should fail if the byte array is too short to hold the length of a nested byte array', async () => { + // The length of the nested array is 32 bytes. By storing less than 32 bytes, a length cannot be read. + const offset = new BigNumber(0); + return expectRevertOrOtherErrorAsync( + libBytes.publicReadBytes.callAsync(byteArrayShorterThan32Bytes, offset), + constants.LIB_BYTES_GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED, + ); + }); + + it('should fail if we store a nested byte array length, without a nested byte array', async () => { + const offset = new BigNumber(0); + return expectRevertOrOtherErrorAsync( + libBytes.publicReadBytes.callAsync(testBytes32, offset), + constants.LIB_BYTES_GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED, + ); + }); + + it('should fail if the length between the offset and end of the byte array is too short to hold the length of a nested byte array', async () => { + const badOffset = new BigNumber(ethUtil.toBuffer(byteArrayShorterThan32Bytes).byteLength); + return expectRevertOrOtherErrorAsync( + libBytes.publicReadBytes.callAsync(byteArrayShorterThan32Bytes, badOffset), + constants.LIB_BYTES_GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED, + ); + }); + + it('should fail if the length between the offset and end of the byte array is too short to hold the nested byte array', async () => { + const badOffset = new BigNumber(ethUtil.toBuffer(testBytes32).byteLength); + return expectRevertOrOtherErrorAsync( + libBytes.publicReadBytes.callAsync(testBytes32, badOffset), + constants.LIB_BYTES_GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED, + ); + }); + }); + + describe('writeBytes', () => { + it('should successfully write short, nested array of bytes when it takes up the whole array)', async () => { + const testBytesOffset = new BigNumber(0); + const emptyByteArray = ethUtil.bufferToHex(new Buffer(shortTestBytesAsBuffer.byteLength)); + const bytesWritten = await libBytes.publicWriteBytes.callAsync(emptyByteArray, testBytesOffset, shortData); + const bytesRead = await libBytes.publicReadBytes.callAsync(bytesWritten, testBytesOffset); + return expect(bytesRead).to.be.equal(shortData); + }); + + it('should successfully write short, nested array of bytes when it is offset in the array', async () => { + // Write a prefix to the array + const prefixData = '0xabcdef'; + const prefixDataAsBuffer = ethUtil.toBuffer(prefixData); + const prefixOffset = new BigNumber(0); + const emptyByteArray = ethUtil.bufferToHex( + new Buffer(prefixDataAsBuffer.byteLength + shortTestBytesAsBuffer.byteLength), + ); + let bytesWritten = await libBytes.publicWriteBytes.callAsync(emptyByteArray, prefixOffset, prefixData); + // Write data after prefix + const testBytesOffset = new BigNumber(prefixDataAsBuffer.byteLength); + bytesWritten = await libBytes.publicWriteBytes.callAsync(bytesWritten, testBytesOffset, shortData); + // Read data after prefix and validate + const bytes = await libBytes.publicReadBytes.callAsync(bytesWritten, testBytesOffset); + return expect(bytes).to.be.equal(shortData); + }); + + it('should successfully write a nested array of bytes - one word in length - when it takes up the whole array', async () => { + const testBytesOffset = new BigNumber(0); + const emptyByteArray = ethUtil.bufferToHex(new Buffer(wordOfTestBytesAsBuffer.byteLength)); + const bytesWritten = await libBytes.publicWriteBytes.callAsync(emptyByteArray, testBytesOffset, wordOfData); + const bytesRead = await libBytes.publicReadBytes.callAsync(bytesWritten, testBytesOffset); + return expect(bytesRead).to.be.equal(wordOfData); + }); + + it('should successfully write a nested array of bytes - one word in length - when it is offset in the array', async () => { + // Write a prefix to the array + const prefixData = '0xabcdef'; + const prefixDataAsBuffer = ethUtil.toBuffer(prefixData); + const prefixOffset = new BigNumber(0); + const emptyByteArray = ethUtil.bufferToHex( + new Buffer(prefixDataAsBuffer.byteLength + wordOfTestBytesAsBuffer.byteLength), + ); + let bytesWritten = await libBytes.publicWriteBytes.callAsync(emptyByteArray, prefixOffset, prefixData); + // Write data after prefix + const testBytesOffset = new BigNumber(prefixDataAsBuffer.byteLength); + bytesWritten = await libBytes.publicWriteBytes.callAsync(bytesWritten, testBytesOffset, wordOfData); + // Read data after prefix and validate + const bytes = await libBytes.publicReadBytes.callAsync(bytesWritten, testBytesOffset); + return expect(bytes).to.be.equal(wordOfData); + }); + + it('should successfully write a long, nested bytes when it takes up the whole array', async () => { + const testBytesOffset = new BigNumber(0); + const emptyByteArray = ethUtil.bufferToHex(new Buffer(longTestBytesAsBuffer.byteLength)); + const bytesWritten = await libBytes.publicWriteBytes.callAsync(emptyByteArray, testBytesOffset, longData); + const bytesRead = await libBytes.publicReadBytes.callAsync(bytesWritten, testBytesOffset); + return expect(bytesRead).to.be.equal(longData); + }); + + it('should successfully write long, nested array of bytes when it is offset in the array', async () => { + // Write a prefix to the array + const prefixData = '0xabcdef'; + const prefixDataAsBuffer = ethUtil.toBuffer(prefixData); + const prefixOffset = new BigNumber(0); + const emptyByteArray = ethUtil.bufferToHex( + new Buffer(prefixDataAsBuffer.byteLength + longTestBytesAsBuffer.byteLength), + ); + let bytesWritten = await libBytes.publicWriteBytes.callAsync(emptyByteArray, prefixOffset, prefixData); + // Write data after prefix + const testBytesOffset = new BigNumber(prefixDataAsBuffer.byteLength); + bytesWritten = await libBytes.publicWriteBytes.callAsync(bytesWritten, testBytesOffset, longData); + // Read data after prefix and validate + const bytes = await libBytes.publicReadBytes.callAsync(bytesWritten, testBytesOffset); + return expect(bytes).to.be.equal(longData); + }); + + it('should fail if the byte array is too short to hold the length of a nested byte array', async () => { + const offset = new BigNumber(0); + const emptyByteArray = ethUtil.bufferToHex(new Buffer(1)); + return expectRevertOrOtherErrorAsync( + libBytes.publicWriteBytes.callAsync(emptyByteArray, offset, longData), + constants.LIB_BYTES_GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED, + ); + }); + + it('should fail if the length between the offset and end of the byte array is too short to hold the length of a nested byte array)', async () => { + const emptyByteArray = ethUtil.bufferToHex(new Buffer(shortTestBytesAsBuffer.byteLength)); + const badOffset = new BigNumber(ethUtil.toBuffer(shortTestBytesAsBuffer).byteLength); + return expectRevertOrOtherErrorAsync( + libBytes.publicWriteBytes.callAsync(emptyByteArray, badOffset, shortData), + constants.LIB_BYTES_GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED, + ); + }); + }); }); +// tslint:disable:max-file-line-count diff --git a/packages/contracts/test/libraries/lib_mem.ts b/packages/contracts/test/libraries/lib_mem.ts new file mode 100644 index 000000000..90d54edcb --- /dev/null +++ b/packages/contracts/test/libraries/lib_mem.ts @@ -0,0 +1,190 @@ +import { BigNumber } from '@0xproject/utils'; +import * as chai from 'chai'; + +import { TestLibMemContract } from '../../src/generated_contract_wrappers/test_lib_mem'; +import { artifacts } from '../../src/utils/artifacts'; +import { chaiSetup } from '../../src/utils/chai_setup'; +import { provider, txDefaults } from '../../src/utils/web3_wrapper'; + +chaiSetup.configure(); +const expect = chai.expect; + +// BUG: Ideally we would use Buffer.from(memory).toString('hex') +// https://github.com/Microsoft/TypeScript/issues/23155 +const toHex = (buf: Uint8Array): string => buf.reduce((a, v) => a + ('00' + v.toString(16)).slice(-2), '0x'); + +const fromHex = (str: string): Uint8Array => Uint8Array.from(Buffer.from(str.slice(2), 'hex')); + +describe('LibMem', () => { + let testLibMem: TestLibMemContract; + + before(async () => { + // Deploy TestLibMem + testLibMem = await TestLibMemContract.deployFrom0xArtifactAsync(artifacts.TestLibMem, provider, txDefaults); + }); + + describe('memCopy', () => { + // Create memory 0x000102...FF + const memSize = 256; + const memory = new Uint8Array(memSize).map((_, i) => i); + const memHex = toHex(memory); + + // Reference implementation to test against + const refMemcpy = (mem: Uint8Array, dest: number, source: number, length: number): Uint8Array => + Uint8Array.from(memory).copyWithin(dest, source, source + length); + + // Test vectors: destination, source, length, job description + type Tests = Array<[number, number, number, string]>; + + const test = (tests: Tests) => + tests.forEach(([dest, source, length, job]) => + it(job, async () => { + const expected = refMemcpy(memory, dest, source, length); + const resultStr = await testLibMem.testMemcpy.callAsync( + memHex, + new BigNumber(dest), + new BigNumber(source), + new BigNumber(length), + ); + const result = fromHex(resultStr); + expect(result).to.deep.equal(expected); + }), + ); + + test([[0, 0, 0, 'copies zero bytes with overlap']]); + + describe('copies forward', () => + test([ + [128, 0, 0, 'zero bytes'], + [128, 0, 1, 'one byte'], + [128, 0, 11, 'eleven bytes'], + [128, 0, 31, 'thirty-one bytes'], + [128, 0, 32, 'one word'], + [128, 0, 64, 'two words'], + [128, 0, 96, 'three words'], + [128, 0, 33, 'one word and one byte'], + [128, 0, 72, 'two words and eight bytes'], + [128, 0, 100, 'three words and four bytes'], + ])); + + describe('copies forward within one word', () => + test([ + [16, 0, 0, 'zero bytes'], + [16, 0, 1, 'one byte'], + [16, 0, 11, 'eleven bytes'], + [16, 0, 16, 'sixteen bytes'], + ])); + + describe('copies forward with one byte overlap', () => + test([ + [0, 0, 1, 'one byte'], + [10, 0, 11, 'eleven bytes'], + [30, 0, 31, 'thirty-one bytes'], + [31, 0, 32, 'one word'], + [32, 0, 33, 'one word and one byte'], + [71, 0, 72, 'two words and eight bytes'], + [99, 0, 100, 'three words and four bytes'], + ])); + + describe('copies forward with thirty-one bytes overlap', () => + test([ + [0, 0, 31, 'thirty-one bytes'], + [1, 0, 32, 'one word'], + [2, 0, 33, 'one word and one byte'], + [41, 0, 72, 'two words and eight bytes'], + [69, 0, 100, 'three words and four bytes'], + ])); + + describe('copies forward with one word overlap', () => + test([ + [0, 0, 32, 'one word'], + [1, 0, 33, 'one word and one byte'], + [41, 0, 72, 'two words and eight bytes'], + [69, 0, 100, 'three words and four bytes'], + ])); + + describe('copies forward with one word and one byte overlap', () => + test([ + [0, 0, 33, 'one word and one byte'], + [40, 0, 72, 'two words and eight bytes'], + [68, 0, 100, 'three words and four bytes'], + ])); + + describe('copies forward with two words overlap', () => + test([ + [0, 0, 64, 'two words'], + [8, 0, 72, 'two words and eight bytes'], + [36, 0, 100, 'three words and four bytes'], + ])); + + describe('copies forward within one word and one byte overlap', () => + test([[0, 0, 1, 'one byte'], [10, 0, 11, 'eleven bytes'], [15, 0, 16, 'sixteen bytes']])); + + describe('copies backward', () => + test([ + [0, 128, 0, 'zero bytes'], + [0, 128, 1, 'one byte'], + [0, 128, 11, 'eleven bytes'], + [0, 128, 31, 'thirty-one bytes'], + [0, 128, 32, 'one word'], + [0, 128, 64, 'two words'], + [0, 128, 96, 'three words'], + [0, 128, 33, 'one word and one byte'], + [0, 128, 72, 'two words and eight bytes'], + [0, 128, 100, 'three words and four bytes'], + ])); + + describe('copies backward within one word', () => + test([ + [0, 16, 0, 'zero bytes'], + [0, 16, 1, 'one byte'], + [0, 16, 11, 'eleven bytes'], + [0, 16, 16, 'sixteen bytes'], + ])); + + describe('copies backward with one byte overlap', () => + test([ + [0, 0, 1, 'one byte'], + [0, 10, 11, 'eleven bytes'], + [0, 30, 31, 'thirty-one bytes'], + [0, 31, 32, 'one word'], + [0, 32, 33, 'one word and one byte'], + [0, 71, 72, 'two words and eight bytes'], + [0, 99, 100, 'three words and four bytes'], + ])); + + describe('copies backward with thirty-one bytes overlap', () => + test([ + [0, 0, 31, 'thirty-one bytes'], + [0, 1, 32, 'one word'], + [0, 2, 33, 'one word and one byte'], + [0, 41, 72, 'two words and eight bytes'], + [0, 69, 100, 'three words and four bytes'], + ])); + + describe('copies backward with one word overlap', () => + test([ + [0, 0, 32, 'one word'], + [0, 1, 33, 'one word and one byte'], + [0, 41, 72, 'two words and eight bytes'], + [0, 69, 100, 'three words and four bytes'], + ])); + + describe('copies backward with one word and one byte overlap', () => + test([ + [0, 0, 33, 'one word and one byte'], + [0, 40, 72, 'two words and eight bytes'], + [0, 68, 100, 'three words and four bytes'], + ])); + + describe('copies backward with two words overlap', () => + test([ + [0, 0, 64, 'two words'], + [0, 8, 72, 'two words and eight bytes'], + [0, 36, 100, 'three words and four bytes'], + ])); + + describe('copies forward within one word and one byte overlap', () => + test([[0, 0, 1, 'one byte'], [0, 10, 11, 'eleven bytes'], [0, 15, 16, 'sixteen bytes']])); + }); +}); |