From 3ce90b8257ccfceecb9bbd67995cbc2ad38ee0eb Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Mon, 25 Jun 2018 18:53:12 +0200 Subject: Optimize ERC20 transferFrom --- .../protocol/AssetProxy/MixinERC20Transfer.sol | 128 ++++++++++++++++++++- 1 file changed, 126 insertions(+), 2 deletions(-) (limited to 'packages/contracts/src') diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC20Transfer.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC20Transfer.sol index a09db43bd..7cf6e1989 100644 --- a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC20Transfer.sol +++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC20Transfer.sol @@ -19,13 +19,137 @@ pragma solidity ^0.4.24; pragma experimental ABIEncoderV2; +import "./mixins/MAuthorizable.sol"; import "../../utils/LibBytes/LibBytes.sol"; import "../../tokens/ERC20Token/IERC20Token.sol"; -contract MixinERC20Transfer { - +contract MixinERC20Transfer is + MAuthorizable +{ using LibBytes for bytes; + /// @dev Internal version of `transferFrom`. + /// @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 assetData, + address from, + address to, + uint256 amount + ) + external + onlyAuthorized() + { + // `transferFrom`. + // The function is marked `external`, so no abi decodeding is done for + // us. Instead, we expect the `calldata` memory to contain the + // following: + // + // | Area | Offset | Length | Contents | + // |----------|--------|---------|-------------------------------------| + // | Header | 0 | 4 | function selector | + // | Params | | 4 * 32 | function parameters: | + // | | 4 | | 1. offset to assetData (*) | + // | | 36 | | 2. from | + // | | 68 | | 3. to | + // | | 100 | | 4. amount | + // | Data | | | assetData: | + // | | 132 | 32 | assetData Length | + // | | 164 | ** | assetData Contents | + // + // (*): offset is computed from start of function parameters, so offset + // by an additional 4 bytes in the calldata. + // + // WARNING: The ABIv2 specification allows additional padding between + // the Params and Data section. This will result in a larger + // offset to assetData. + + // Asset data itself is encoded as follows: + // + // | Area | Offset | Length | Contents | + // |----------|--------|---------|-------------------------------------| + // | Header | 0 | 4 | function selector | + // | Params | | 1 * 32 | function parameters: | + // | | 4 | 12 + 20 | 1. token address | + + // Transfer tokens. + // We do a raw call so we can check the success separate + // from the return data. + // We construct calldata for the `token.transferFrom` ABI. + // The layout of this calldata is in the table below. + // + // | Area | Offset | Length | Contents | + // |----------|--------|---------|-------------------------------------| + // | Header | 0 | 4 | function selector | + // | Params | | 3 * 32 | function parameters: | + // | | 4 | | 1. from | + // | | 36 | | 2. to | + // | | 68 | | 3. amount | + + bytes4 transferFromSelector = IERC20Token(0).transferFrom.selector; + bool success; + assembly { + /////// Token contract address /////// + // The token address is found as follows: + // * It is stored at offset 4 in `assetData` contents. + // * This is stored at offset 32 from `assetData`. + // * The offset to `assetData` from Params is stored at offset + // 4 in calldata. + // * The offset of Params in calldata is 4. + // So we read location 4 and add 32 + 4 + 4 to it. + let token := calldataload(add(calldataload(4), 40)) + + /////// Setup State /////// + // `cdStart` is the start of the calldata for `token.transferFrom` (equal to free memory ptr). + let cdStart := mload(64) + + /////// Setup Header Area /////// + // This area holds the 4-byte `transferFromSelector`. + // Any trailing data in transferFromSelector will be + // overwritten in the next `mstore` call. + mstore(cdStart, transferFromSelector) + + /////// Setup Params Area /////// + // We copy the fields `from`, `to` and `amount` in bulk + // from our own calldata to the new calldata. + calldatacopy(add(cdStart, 4), 36, 96) + + /////// Call `token.transferFrom` using the calldata /////// + success := call( + gas, // forward all gas + token, // call address of token contract + 0, // don't send any ETH + cdStart, // pointer to start of input + 100, // length of input + cdStart, // write output over input + 32 // output size should be 32 bytes + ) + + /////// Check return data. /////// + // If there is no return data, we assume the token incorrectly + // does not return a bool. In this case we expect it to revert + // on failure, which was handled above. + // If the token does return data, we require that it is a single + // nonzero 32 bytes value. + // So the transfer succeeded if the call succeeded and either + // returned nothing, or returned a non-zero 32 byte value. + success := and(success, or( + iszero(returndatasize), + and( + eq(returndatasize, 32), + gt(mload(cdStart), 0) + ) + )) + } + require( + success, + "TRANSFER_FAILED" + ); + } + + /// @dev Internal version of `transferFrom`. /// @param assetData Encoded byte array. /// @param from Address to transfer asset from. -- cgit From 6921943afffa4a8588e652caf194ce5abb3976e1 Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Mon, 25 Jun 2018 19:02:50 +0200 Subject: Inline ERC20 tranferFrom selector constant --- .../contracts/current/protocol/AssetProxy/MixinERC20Transfer.sol | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'packages/contracts/src') diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC20Transfer.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC20Transfer.sol index 7cf6e1989..8081e7dd3 100644 --- a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC20Transfer.sol +++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC20Transfer.sol @@ -88,7 +88,6 @@ contract MixinERC20Transfer is // | | 36 | | 2. to | // | | 68 | | 3. amount | - bytes4 transferFromSelector = IERC20Token(0).transferFrom.selector; bool success; assembly { /////// Token contract address /////// @@ -106,11 +105,11 @@ contract MixinERC20Transfer is let cdStart := mload(64) /////// Setup Header Area /////// - // This area holds the 4-byte `transferFromSelector`. + // This area holds the 4-byte `transferFrom` selector. // Any trailing data in transferFromSelector will be // overwritten in the next `mstore` call. - mstore(cdStart, transferFromSelector) - + mstore(cdStart, 0x23b872dd00000000000000000000000000000000000000000000000000000000) + /////// Setup Params Area /////// // We copy the fields `from`, `to` and `amount` in bulk // from our own calldata to the new calldata. -- cgit From 4caf1271e42cea743cb24a93bbf832982e656cb2 Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Mon, 25 Jun 2018 21:21:15 +0200 Subject: Golf ERC721 transferFrom --- .../protocol/AssetProxy/MixinERC721Transfer.sol | 168 ++++++++++++++++++++- .../protocol/AssetProxy/libs/LibTransferErrors.sol | 1 + 2 files changed, 166 insertions(+), 3 deletions(-) (limited to 'packages/contracts/src') diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC721Transfer.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC721Transfer.sol index c170917ea..e00e43889 100644 --- a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC721Transfer.sol +++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC721Transfer.sol @@ -19,17 +19,179 @@ pragma solidity ^0.4.24; pragma experimental ABIEncoderV2; +import "./mixins/MAuthorizable.sol"; import "../../utils/LibBytes/LibBytes.sol"; -import "../../tokens/ERC721Token/ERC721Token.sol"; import "./libs/LibTransferErrors.sol"; contract MixinERC721Transfer is - LibTransferErrors + LibTransferErrors, + MAuthorizable { using LibBytes for bytes; bytes4 constant SAFE_TRANSFER_FROM_SELECTOR = bytes4(keccak256("safeTransferFrom(address,address,uint256,bytes)")); - + /// @dev Internal version of `transferFrom`. + /// @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 assetData, + address from, + address to, + uint256 amount + ) + external + onlyAuthorized() + { + // `transferFrom`. + // The function is marked `external`, so no abi decodeding is done for + // us. Instead, we expect the `calldata` memory to contain the + // following: + // + // | Area | Offset | Length | Contents | + // |----------|--------|---------|-------------------------------------| + // | Header | 0 | 4 | function selector | + // | Params | | 4 * 32 | function parameters: | + // | | 4 | | 1. offset to assetData (*) | + // | | 36 | | 2. from | + // | | 68 | | 3. to | + // | | 100 | | 4. amount | + // | Data | | | assetData: | + // | | 132 | 32 | assetData Length | + // | | 164 | ** | assetData Contents | + // + // (*): offset is computed from start of function parameters, so offset + // by an additional 4 bytes in the calldata. + // + // WARNING: The ABIv2 specification allows additional padding between + // the Params and Data section. This will result in a larger + // offset to assetData. + + // Asset data itself is encoded as follows: + // + // | Area | Offset | Length | Contents | + // |----------|--------|---------|-------------------------------------| + // | Header | 0 | 4 | function selector | + // | Params | | 3 * 32 | function parameters: | + // | | 4 | 12 + 20 | 1. token address | + // | | 36 | | 2. tokenId | + // | | 68 | | 3. offset to receiverData (*) | + // | Data | | | receiverData: | + // | | 100 | 32 | receiverData Length | + // | | 132 | ** | receiverData Contents | + + // We construct calldata for the `token.safeTransferFrom` ABI. + // The layout of this calldata is in the table below. + // + // | Area | Offset | Length | Contents | + // |----------|--------|---------|-------------------------------------| + // | Header | 0 | 4 | function selector | + // | Params | | 4 * 32 | function parameters: | + // | | 4 | | 1. from | + // | | 36 | | 2. to | + // | | 68 | | 3. tokenId | + // | | 100 | | 4. offset to receiverData (*) | + // | Data | | | receiverData: | + // | | 132 | 32 | receiverData Length | + // | | 164 | ** | receiverData Contents | + + // bytes4 safeTransferFromSelector = SAFE_TRANSFER_FROM_SELECTOR; + // bool success; + assembly { + // There exists only 1 of each token. + // require(amount == 1, "INVALID_AMOUNT") + if sub(calldataload(100), 1) { + // Revert with `Error("INVALID_AMOUNT")` + mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) + mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) + mstore(64, 0x0000000e494e56414c49445f414d4f554e540000000000000000000000000000) + mstore(96, 0) + revert(0, 100) + } + + // Require assetData to be at least 132 bytes + let offset := calldataload(4) + if lt(calldataload(add(offset, 4)), 132) { + // Revert with `Error("ASSET_DATA_TO_SHORT")` + mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) + mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) + mstore(64, 0x0000001341535345545f444154415f544f5f53484f5254000000000000000000) + mstore(96, 0) + revert(0, 100) + } + + /////// Setup State /////// + // `cdStart` is the start of the calldata for + // `token.safeTransferFrom` (equal to free memory ptr). + let cdStart := mload(64) + // `dataAreaLength` is the total number of words + // needed to store `receiverData` + // As-per the ABI spec, this value is padded up to + // the nearest multiple of 32, + // and includes 32-bytes for length. + // It's calculated as folows: + // - Unpadded length in bytes = `mload(receiverData) + 32` + // - Add 31 to convert rounding down to rounding up. + // Combined with the previous and this is `63`. + // - Round down to nearest multiple of 32 by clearing + // bits 0x1F. This is done with `and` and a mask. + + /////// Setup Header Area /////// + // This area holds the 4-byte `transferFromSelector`. + // Any trailing data in transferFromSelector will be + // overwritten in the next `mstore` call. + mstore(cdStart, 0xb88d4fde00000000000000000000000000000000000000000000000000000000) + + /////// Setup Params Area /////// + // Each parameter is padded to 32-bytes. + // The entire Params Area is 128 bytes. + // Notes: + // 1. A 20-byte mask is applied to addresses + // to zero-out the unused bytes. + // 2. The offset to `receiverData` is the length + // of the Params Area (128 bytes). + + let length := calldataload(add(offset, 104)) + let token := calldataload(add(offset, 40)) + + // Round length up to multiple of 32 + length := and(add(length, 31), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0) + + // Copy `from` and `to` + calldatacopy(add(cdStart, 4), 36, 64) + + // TokenId + mstore(add(cdStart, 68), calldataload(add(offset, 72))) + + // Offset to receiverData + mstore(add(cdStart, 100), 128) + + // receiverData (including length) + calldatacopy(add(cdStart, 132), add(offset, 104), add(length, 32)) + + /////// Call `token.safeTransferFrom` using the calldata /////// + let success := call( + gas, // forward all gas + token, // call address of token contract + 0, // don't send any ETH + cdStart, // pointer to start of input + add(length, 164), // length of input + 0, // write output to null + 0 // output size is 0 bytes + ) + + if iszero(success) { + // Revert with `Error("TRANSFER_FAILED")` + mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) + mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) + mstore(64, 0x0000000f5452414e534645525f4641494c454400000000000000000000000000) + mstore(96, 0) + revert(0, 100) + } + } + } + /// @dev Internal version of `transferFrom`. /// @param assetData Encoded byte array. /// @param from Address to transfer asset from. diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/libs/LibTransferErrors.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/libs/LibTransferErrors.sol index 88187f196..98bca31b2 100644 --- a/packages/contracts/src/contracts/current/protocol/AssetProxy/libs/LibTransferErrors.sol +++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/libs/LibTransferErrors.sol @@ -25,4 +25,5 @@ contract LibTransferErrors { /// Transfer errors /// string constant INVALID_AMOUNT = "INVALID_AMOUNT"; // Transfer amount must equal 1. string constant TRANSFER_FAILED = "TRANSFER_FAILED"; // Transfer failed. + string constant ASSET_DATA_TO_SHORT = "ASSET_DATA_TO_SHORT"; // Asset data to short } -- cgit From dbaf1fcd43637f2acc08af5a30cc3c1890dacb24 Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Mon, 25 Jun 2018 21:30:13 +0200 Subject: ERC721 inline return --- .../current/protocol/AssetProxy/MixinERC721Transfer.sol | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'packages/contracts/src') diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC721Transfer.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC721Transfer.sol index e00e43889..2b425b15e 100644 --- a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC721Transfer.sol +++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC721Transfer.sol @@ -180,15 +180,16 @@ contract MixinERC721Transfer is 0, // write output to null 0 // output size is 0 bytes ) - - if iszero(success) { - // Revert with `Error("TRANSFER_FAILED")` - mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) - mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) - mstore(64, 0x0000000f5452414e534645525f4641494c454400000000000000000000000000) - mstore(96, 0) - revert(0, 100) + if success { + return(0, 0) } + + // Revert with `Error("TRANSFER_FAILED")` + mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) + mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) + mstore(64, 0x0000000f5452414e534645525f4641494c454400000000000000000000000000) + mstore(96, 0) + revert(0, 100) } } -- cgit From 3b46e82625fbb16ad4068851df5a2d96ff448f35 Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Mon, 25 Jun 2018 21:30:38 +0200 Subject: ERC20 inline return --- .../current/protocol/AssetProxy/MixinERC20Transfer.sol | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'packages/contracts/src') diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC20Transfer.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC20Transfer.sol index 8081e7dd3..fce8da7c7 100644 --- a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC20Transfer.sol +++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC20Transfer.sol @@ -88,7 +88,6 @@ contract MixinERC20Transfer is // | | 36 | | 2. to | // | | 68 | | 3. amount | - bool success; assembly { /////// Token contract address /////// // The token address is found as follows: @@ -116,7 +115,7 @@ contract MixinERC20Transfer is calldatacopy(add(cdStart, 4), 36, 96) /////// Call `token.transferFrom` using the calldata /////// - success := call( + let success := call( gas, // forward all gas token, // call address of token contract 0, // don't send any ETH @@ -141,11 +140,17 @@ contract MixinERC20Transfer is gt(mload(cdStart), 0) ) )) + if success { + return(0, 0) + } + + // Revert with `Error("TRANSFER_FAILED")` + mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) + mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) + mstore(64, 0x0000000f5452414e534645525f4641494c454400000000000000000000000000) + mstore(96, 0) + revert(0, 100) } - require( - success, - "TRANSFER_FAILED" - ); } -- cgit From b79588c4a0802e37f8e173846ad0b12e88901ae3 Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Mon, 25 Jun 2018 21:40:10 +0200 Subject: ERC20 manual memory layout --- .../current/protocol/AssetProxy/MixinERC20Transfer.sol | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) (limited to 'packages/contracts/src') diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC20Transfer.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC20Transfer.sol index fce8da7c7..2cb12aa6b 100644 --- a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC20Transfer.sol +++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC20Transfer.sol @@ -99,29 +99,25 @@ contract MixinERC20Transfer is // So we read location 4 and add 32 + 4 + 4 to it. let token := calldataload(add(calldataload(4), 40)) - /////// Setup State /////// - // `cdStart` is the start of the calldata for `token.transferFrom` (equal to free memory ptr). - let cdStart := mload(64) - /////// Setup Header Area /////// // This area holds the 4-byte `transferFrom` selector. // Any trailing data in transferFromSelector will be // overwritten in the next `mstore` call. - mstore(cdStart, 0x23b872dd00000000000000000000000000000000000000000000000000000000) + mstore(0, 0x23b872dd00000000000000000000000000000000000000000000000000000000) /////// Setup Params Area /////// // We copy the fields `from`, `to` and `amount` in bulk // from our own calldata to the new calldata. - calldatacopy(add(cdStart, 4), 36, 96) + calldatacopy(4, 36, 96) /////// Call `token.transferFrom` using the calldata /////// let success := call( gas, // forward all gas token, // call address of token contract 0, // don't send any ETH - cdStart, // pointer to start of input + 0, // pointer to start of input 100, // length of input - cdStart, // write output over input + 0, // write output over input 32 // output size should be 32 bytes ) @@ -137,7 +133,7 @@ contract MixinERC20Transfer is iszero(returndatasize), and( eq(returndatasize, 32), - gt(mload(cdStart), 0) + gt(mload(0), 0) ) )) if success { @@ -152,8 +148,7 @@ contract MixinERC20Transfer is revert(0, 100) } } - - + /// @dev Internal version of `transferFrom`. /// @param assetData Encoded byte array. /// @param from Address to transfer asset from. -- cgit From 394fbebfa83cd42a80bdd0b3ebe267899070f056 Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Mon, 25 Jun 2018 21:49:36 +0200 Subject: Golf the authorization check --- .../current/protocol/AssetProxy/ERC20Proxy.sol | 133 ++++++++++++++++++++- .../protocol/AssetProxy/MixinERC20Transfer.sol | 121 ------------------- 2 files changed, 132 insertions(+), 122 deletions(-) (limited to 'packages/contracts/src') diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC20Proxy.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC20Proxy.sol index b74d2c231..cb9c8c86c 100644 --- a/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC20Proxy.sol +++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC20Proxy.sol @@ -31,7 +31,7 @@ contract ERC20Proxy is { // Id of this proxy. bytes4 constant PROXY_ID = bytes4(keccak256("ERC20Token(address)")); - + /// @dev Gets the proxy id associated with the proxy address. /// @return Proxy id. function getProxyId() @@ -41,4 +41,135 @@ contract ERC20Proxy is { return PROXY_ID; } + + /// @dev Internal version of `transferFrom`. + /// @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 assetData, + address from, + address to, + uint256 amount + ) + external + { + bool auth = authorized[msg.sender]; + + // `transferFrom`. + // The function is marked `external`, so no abi decodeding is done for + // us. Instead, we expect the `calldata` memory to contain the + // following: + // + // | Area | Offset | Length | Contents | + // |----------|--------|---------|-------------------------------------| + // | Header | 0 | 4 | function selector | + // | Params | | 4 * 32 | function parameters: | + // | | 4 | | 1. offset to assetData (*) | + // | | 36 | | 2. from | + // | | 68 | | 3. to | + // | | 100 | | 4. amount | + // | Data | | | assetData: | + // | | 132 | 32 | assetData Length | + // | | 164 | ** | assetData Contents | + // + // (*): offset is computed from start of function parameters, so offset + // by an additional 4 bytes in the calldata. + // + // WARNING: The ABIv2 specification allows additional padding between + // the Params and Data section. This will result in a larger + // offset to assetData. + + // Asset data itself is encoded as follows: + // + // | Area | Offset | Length | Contents | + // |----------|--------|---------|-------------------------------------| + // | Header | 0 | 4 | function selector | + // | Params | | 1 * 32 | function parameters: | + // | | 4 | 12 + 20 | 1. token address | + + // Transfer tokens. + // We do a raw call so we can check the success separate + // from the return data. + // We construct calldata for the `token.transferFrom` ABI. + // The layout of this calldata is in the table below. + // + // | Area | Offset | Length | Contents | + // |----------|--------|---------|-------------------------------------| + // | Header | 0 | 4 | function selector | + // | Params | | 3 * 32 | function parameters: | + // | | 4 | | 1. from | + // | | 36 | | 2. to | + // | | 68 | | 3. amount | + + assembly { + if iszero(auth) { + // Revert with `Error("SENDER_NOT_AUTHORIZED")` + mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) + mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) + mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000) + mstore(96, 0) + revert(0, 100) + } + + /////// Token contract address /////// + // The token address is found as follows: + // * It is stored at offset 4 in `assetData` contents. + // * This is stored at offset 32 from `assetData`. + // * The offset to `assetData` from Params is stored at offset + // 4 in calldata. + // * The offset of Params in calldata is 4. + // So we read location 4 and add 32 + 4 + 4 to it. + let token := calldataload(add(calldataload(4), 40)) + + /////// Setup Header Area /////// + // This area holds the 4-byte `transferFrom` selector. + // Any trailing data in transferFromSelector will be + // overwritten in the next `mstore` call. + mstore(0, 0x23b872dd00000000000000000000000000000000000000000000000000000000) + + /////// Setup Params Area /////// + // We copy the fields `from`, `to` and `amount` in bulk + // from our own calldata to the new calldata. + calldatacopy(4, 36, 96) + + /////// Call `token.transferFrom` using the calldata /////// + let success := call( + gas, // forward all gas + token, // call address of token contract + 0, // don't send any ETH + 0, // pointer to start of input + 100, // length of input + 0, // write output over input + 32 // output size should be 32 bytes + ) + + /////// Check return data. /////// + // If there is no return data, we assume the token incorrectly + // does not return a bool. In this case we expect it to revert + // on failure, which was handled above. + // If the token does return data, we require that it is a single + // nonzero 32 bytes value. + // So the transfer succeeded if the call succeeded and either + // returned nothing, or returned a non-zero 32 byte value. + success := and(success, or( + iszero(returndatasize), + and( + eq(returndatasize, 32), + gt(mload(0), 0) + ) + )) + if success { + return(0, 0) + } + + // Revert with `Error("TRANSFER_FAILED")` + mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) + mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) + mstore(64, 0x0000000f5452414e534645525f4641494c454400000000000000000000000000) + mstore(96, 0) + revert(0, 100) + } + } } diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC20Transfer.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC20Transfer.sol index 2cb12aa6b..5bd98a1f4 100644 --- a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC20Transfer.sol +++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC20Transfer.sol @@ -27,127 +27,6 @@ contract MixinERC20Transfer is MAuthorizable { using LibBytes for bytes; - - /// @dev Internal version of `transferFrom`. - /// @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 assetData, - address from, - address to, - uint256 amount - ) - external - onlyAuthorized() - { - // `transferFrom`. - // The function is marked `external`, so no abi decodeding is done for - // us. Instead, we expect the `calldata` memory to contain the - // following: - // - // | Area | Offset | Length | Contents | - // |----------|--------|---------|-------------------------------------| - // | Header | 0 | 4 | function selector | - // | Params | | 4 * 32 | function parameters: | - // | | 4 | | 1. offset to assetData (*) | - // | | 36 | | 2. from | - // | | 68 | | 3. to | - // | | 100 | | 4. amount | - // | Data | | | assetData: | - // | | 132 | 32 | assetData Length | - // | | 164 | ** | assetData Contents | - // - // (*): offset is computed from start of function parameters, so offset - // by an additional 4 bytes in the calldata. - // - // WARNING: The ABIv2 specification allows additional padding between - // the Params and Data section. This will result in a larger - // offset to assetData. - - // Asset data itself is encoded as follows: - // - // | Area | Offset | Length | Contents | - // |----------|--------|---------|-------------------------------------| - // | Header | 0 | 4 | function selector | - // | Params | | 1 * 32 | function parameters: | - // | | 4 | 12 + 20 | 1. token address | - - // Transfer tokens. - // We do a raw call so we can check the success separate - // from the return data. - // We construct calldata for the `token.transferFrom` ABI. - // The layout of this calldata is in the table below. - // - // | Area | Offset | Length | Contents | - // |----------|--------|---------|-------------------------------------| - // | Header | 0 | 4 | function selector | - // | Params | | 3 * 32 | function parameters: | - // | | 4 | | 1. from | - // | | 36 | | 2. to | - // | | 68 | | 3. amount | - - assembly { - /////// Token contract address /////// - // The token address is found as follows: - // * It is stored at offset 4 in `assetData` contents. - // * This is stored at offset 32 from `assetData`. - // * The offset to `assetData` from Params is stored at offset - // 4 in calldata. - // * The offset of Params in calldata is 4. - // So we read location 4 and add 32 + 4 + 4 to it. - let token := calldataload(add(calldataload(4), 40)) - - /////// Setup Header Area /////// - // This area holds the 4-byte `transferFrom` selector. - // Any trailing data in transferFromSelector will be - // overwritten in the next `mstore` call. - mstore(0, 0x23b872dd00000000000000000000000000000000000000000000000000000000) - - /////// Setup Params Area /////// - // We copy the fields `from`, `to` and `amount` in bulk - // from our own calldata to the new calldata. - calldatacopy(4, 36, 96) - - /////// Call `token.transferFrom` using the calldata /////// - let success := call( - gas, // forward all gas - token, // call address of token contract - 0, // don't send any ETH - 0, // pointer to start of input - 100, // length of input - 0, // write output over input - 32 // output size should be 32 bytes - ) - - /////// Check return data. /////// - // If there is no return data, we assume the token incorrectly - // does not return a bool. In this case we expect it to revert - // on failure, which was handled above. - // If the token does return data, we require that it is a single - // nonzero 32 bytes value. - // So the transfer succeeded if the call succeeded and either - // returned nothing, or returned a non-zero 32 byte value. - success := and(success, or( - iszero(returndatasize), - and( - eq(returndatasize, 32), - gt(mload(0), 0) - ) - )) - if success { - return(0, 0) - } - - // Revert with `Error("TRANSFER_FAILED")` - mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) - mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) - mstore(64, 0x0000000f5452414e534645525f4641494c454400000000000000000000000000) - mstore(96, 0) - revert(0, 100) - } - } /// @dev Internal version of `transferFrom`. /// @param assetData Encoded byte array. -- cgit From 97a70d14a333ff1fc8e9f35cb7acd34075c1098e Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Mon, 25 Jun 2018 21:55:26 +0200 Subject: ERC20 Custom storage layout --- .../src/contracts/current/protocol/AssetProxy/ERC20Proxy.sol | 4 +--- .../src/contracts/current/protocol/AssetProxy/MixinAuthorizable.sol | 4 ++++ 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'packages/contracts/src') diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC20Proxy.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC20Proxy.sol index cb9c8c86c..7dcd2087b 100644 --- a/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC20Proxy.sol +++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC20Proxy.sol @@ -55,8 +55,6 @@ contract ERC20Proxy is ) external { - bool auth = authorized[msg.sender]; - // `transferFrom`. // The function is marked `external`, so no abi decodeding is done for // us. Instead, we expect the `calldata` memory to contain the @@ -104,7 +102,7 @@ contract ERC20Proxy is // | | 68 | | 3. amount | assembly { - if iszero(auth) { + if iszero(sload(caller)) { // Revert with `Error("SENDER_NOT_AUTHORIZED")` mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinAuthorizable.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinAuthorizable.sol index 3b9584a44..9aa8697ba 100644 --- a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinAuthorizable.sol +++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinAuthorizable.sol @@ -49,6 +49,10 @@ contract MixinAuthorizable is !authorized[target], "TARGET_ALREADY_AUTHORIZED" ); + + assembly { + sstore(target, 1) + } authorized[target] = true; authorities.push(target); -- cgit From 873ec898d82f24ed4d9c46ce3f62b52bab31ff90 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Mon, 25 Jun 2018 19:53:46 -0700 Subject: Updated offset of receiverData length to reflect new assetData encoding. --- .../src/contracts/current/protocol/AssetProxy/MixinERC721Transfer.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'packages/contracts/src') diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC721Transfer.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC721Transfer.sol index 2b425b15e..561c2a625 100644 --- a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC721Transfer.sol +++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC721Transfer.sol @@ -152,7 +152,7 @@ contract MixinERC721Transfer is // 2. The offset to `receiverData` is the length // of the Params Area (128 bytes). - let length := calldataload(add(offset, 104)) + let length := calldataload(add(offset, 136)) let token := calldataload(add(offset, 40)) // Round length up to multiple of 32 @@ -168,7 +168,7 @@ contract MixinERC721Transfer is mstore(add(cdStart, 100), 128) // receiverData (including length) - calldatacopy(add(cdStart, 132), add(offset, 104), add(length, 32)) + calldatacopy(add(cdStart, 132), add(offset, 136), add(length, 32)) /////// Call `token.safeTransferFrom` using the calldata /////// let success := call( -- cgit From 08ee1ab2e636099344cade1c8bff67e9b3da2bb4 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Tue, 26 Jun 2018 12:23:00 -0700 Subject: Remove redundant files, hard code function selector in dispatchTransferFrom, and modify revert reason --- .../current/protocol/AssetProxy/ERC20Proxy.sol | 42 ++- .../current/protocol/AssetProxy/ERC721Proxy.sol | 173 ++++++++++- .../protocol/AssetProxy/MixinAssetProxy.sol | 75 ----- .../protocol/AssetProxy/MixinAuthorizable.sol | 4 - .../protocol/AssetProxy/MixinERC20Transfer.sol | 115 ------- .../protocol/AssetProxy/MixinERC721Transfer.sol | 331 --------------------- .../protocol/AssetProxy/interfaces/IAssetProxy.sol | 13 - .../AssetProxy/libs/LibAssetProxyErrors.sol | 15 +- .../protocol/AssetProxy/libs/LibTransferErrors.sol | 29 -- .../protocol/AssetProxy/mixins/MAssetProxy.sol | 40 --- .../Exchange/MixinAssetProxyDispatcher.sol | 4 +- .../TestAssetDataDecoders.sol | 56 ---- packages/contracts/src/utils/artifacts.ts | 2 - 13 files changed, 198 insertions(+), 701 deletions(-) delete mode 100644 packages/contracts/src/contracts/current/protocol/AssetProxy/MixinAssetProxy.sol delete mode 100644 packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC20Transfer.sol delete mode 100644 packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC721Transfer.sol delete mode 100644 packages/contracts/src/contracts/current/protocol/AssetProxy/libs/LibTransferErrors.sol delete mode 100644 packages/contracts/src/contracts/current/protocol/AssetProxy/mixins/MAssetProxy.sol delete mode 100644 packages/contracts/src/contracts/current/test/TestAssetDataDecoders/TestAssetDataDecoders.sol (limited to 'packages/contracts/src') diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC20Proxy.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC20Proxy.sol index 7dcd2087b..b4780b52b 100644 --- a/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC20Proxy.sol +++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC20Proxy.sol @@ -20,28 +20,16 @@ pragma solidity ^0.4.24; pragma experimental ABIEncoderV2; import "../../utils/LibBytes/LibBytes.sol"; -import "./MixinAssetProxy.sol"; +import "./interfaces/IAssetProxy.sol"; import "./MixinAuthorizable.sol"; -import "./MixinERC20Transfer.sol"; contract ERC20Proxy is - MixinAssetProxy, - MixinAuthorizable, - MixinERC20Transfer + IAssetProxy, + MixinAuthorizable { // Id of this proxy. bytes4 constant PROXY_ID = bytes4(keccak256("ERC20Token(address)")); - /// @dev Gets the proxy id associated with the proxy address. - /// @return Proxy id. - function getProxyId() - external - view - returns (bytes4) - { - return PROXY_ID; - } - /// @dev Internal version of `transferFrom`. /// @param assetData Encoded byte array. /// @param from Address to transfer asset from. @@ -55,6 +43,11 @@ contract ERC20Proxy is ) external { + require( + authorized[msg.sender], + "SENDER_NOT_AUTHORIZED" + ); + // `transferFrom`. // The function is marked `external`, so no abi decodeding is done for // us. Instead, we expect the `calldata` memory to contain the @@ -102,15 +95,6 @@ contract ERC20Proxy is // | | 68 | | 3. amount | assembly { - if iszero(sload(caller)) { - // Revert with `Error("SENDER_NOT_AUTHORIZED")` - mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) - mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) - mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000) - mstore(96, 0) - revert(0, 100) - } - /////// Token contract address /////// // The token address is found as follows: // * It is stored at offset 4 in `assetData` contents. @@ -170,4 +154,14 @@ contract ERC20Proxy is revert(0, 100) } } + + /// @dev Gets the proxy id associated with the proxy address. + /// @return Proxy id. + function getProxyId() + external + view + returns (bytes4) + { + return PROXY_ID; + } } diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC721Proxy.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC721Proxy.sol index f6293f97d..8d7cea717 100644 --- a/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC721Proxy.sol +++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC721Proxy.sol @@ -20,18 +20,181 @@ pragma solidity ^0.4.24; pragma experimental ABIEncoderV2; import "../../utils/LibBytes/LibBytes.sol"; -import "./MixinAssetProxy.sol"; +import "./interfaces/IAssetProxy.sol"; import "./MixinAuthorizable.sol"; -import "./MixinERC721Transfer.sol"; contract ERC721Proxy is - MixinAssetProxy, - MixinAuthorizable, - MixinERC721Transfer + IAssetProxy, + MixinAuthorizable { // Id of this proxy. bytes4 constant PROXY_ID = bytes4(keccak256("ERC721Token(address,uint256,bytes)")); + /// @dev Internal version of `transferFrom`. + /// @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 assetData, + address from, + address to, + uint256 amount + ) + external + { + require( + authorized[msg.sender], + "SENDER_NOT_AUTHORIZED" + ); + + // `transferFrom`. + // The function is marked `external`, so no abi decodeding is done for + // us. Instead, we expect the `calldata` memory to contain the + // following: + // + // | Area | Offset | Length | Contents | + // |----------|--------|---------|-------------------------------------| + // | Header | 0 | 4 | function selector | + // | Params | | 4 * 32 | function parameters: | + // | | 4 | | 1. offset to assetData (*) | + // | | 36 | | 2. from | + // | | 68 | | 3. to | + // | | 100 | | 4. amount | + // | Data | | | assetData: | + // | | 132 | 32 | assetData Length | + // | | 164 | ** | assetData Contents | + // + // (*): offset is computed from start of function parameters, so offset + // by an additional 4 bytes in the calldata. + // + // WARNING: The ABIv2 specification allows additional padding between + // the Params and Data section. This will result in a larger + // offset to assetData. + + // Asset data itself is encoded as follows: + // + // | Area | Offset | Length | Contents | + // |----------|--------|---------|-------------------------------------| + // | Header | 0 | 4 | function selector | + // | Params | | 3 * 32 | function parameters: | + // | | 4 | 12 + 20 | 1. token address | + // | | 36 | | 2. tokenId | + // | | 68 | | 3. offset to receiverData (*) | + // | Data | | | receiverData: | + // | | 100 | 32 | receiverData Length | + // | | 132 | ** | receiverData Contents | + + // We construct calldata for the `token.safeTransferFrom` ABI. + // The layout of this calldata is in the table below. + // + // | Area | Offset | Length | Contents | + // |----------|--------|---------|-------------------------------------| + // | Header | 0 | 4 | function selector | + // | Params | | 4 * 32 | function parameters: | + // | | 4 | | 1. from | + // | | 36 | | 2. to | + // | | 68 | | 3. tokenId | + // | | 100 | | 4. offset to receiverData (*) | + // | Data | | | receiverData: | + // | | 132 | 32 | receiverData Length | + // | | 164 | ** | receiverData Contents | + + assembly { + // There exists only 1 of each token. + // require(amount == 1, "INVALID_AMOUNT") + if sub(calldataload(100), 1) { + // Revert with `Error("INVALID_AMOUNT")` + mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) + mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) + mstore(64, 0x0000000e494e56414c49445f414d4f554e540000000000000000000000000000) + mstore(96, 0) + revert(0, 100) + } + + // Require assetData to be at least 132 bytes + let offset := calldataload(4) + if lt(calldataload(add(offset, 4)), 132) { + // Revert with `Error("LENGTH_GREATER_THAN_131_REQUIRED")` + mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) + mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) + mstore(64, 0x000000204c454e4754485f475245415445525f5448414e5f3133315f52455155) + mstore(96, 0x4952454400000000000000000000000000000000000000000000000000000000) + revert(0, 100) + } + + /////// Setup State /////// + // `cdStart` is the start of the calldata for + // `token.safeTransferFrom` (equal to free memory ptr). + let cdStart := mload(64) + // `dataAreaLength` is the total number of words + // needed to store `receiverData` + // As-per the ABI spec, this value is padded up to + // the nearest multiple of 32, + // and includes 32-bytes for length. + // It's calculated as folows: + // - Unpadded length in bytes = `mload(receiverData) + 32` + // - Add 31 to convert rounding down to rounding up. + // Combined with the previous and this is `63`. + // - Round down to nearest multiple of 32 by clearing + // bits 0x1F. This is done with `and` and a mask. + + /////// Setup Header Area /////// + // This area holds the 4-byte `transferFromSelector`. + // Any trailing data in transferFromSelector will be + // overwritten in the next `mstore` call. + mstore(cdStart, 0xb88d4fde00000000000000000000000000000000000000000000000000000000) + + /////// Setup Params Area /////// + // Each parameter is padded to 32-bytes. + // The entire Params Area is 128 bytes. + // Notes: + // 1. A 20-byte mask is applied to addresses + // to zero-out the unused bytes. + // 2. The offset to `receiverData` is the length + // of the Params Area (128 bytes). + + let length := calldataload(add(offset, 136)) + let token := calldataload(add(offset, 40)) + + // Round length up to multiple of 32 + length := and(add(length, 31), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0) + + // Copy `from` and `to` + calldatacopy(add(cdStart, 4), 36, 64) + + // TokenId + mstore(add(cdStart, 68), calldataload(add(offset, 72))) + + // Offset to receiverData + mstore(add(cdStart, 100), 128) + + // receiverData (including length) + calldatacopy(add(cdStart, 132), add(offset, 136), add(length, 32)) + + /////// Call `token.safeTransferFrom` using the calldata /////// + let success := call( + gas, // forward all gas + token, // call address of token contract + 0, // don't send any ETH + cdStart, // pointer to start of input + add(length, 164), // length of input + 0, // write output to null + 0 // output size is 0 bytes + ) + if success { + return(0, 0) + } + + // Revert with `Error("TRANSFER_FAILED")` + mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) + mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) + mstore(64, 0x0000000f5452414e534645525f4641494c454400000000000000000000000000) + mstore(96, 0) + revert(0, 100) + } + } + /// @dev Gets the proxy id associated with the proxy address. /// @return Proxy id. function getProxyId() diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinAssetProxy.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinAssetProxy.sol deleted file mode 100644 index 9032658e7..000000000 --- a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinAssetProxy.sol +++ /dev/null @@ -1,75 +0,0 @@ -/* - - 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 "./mixins/MAuthorizable.sol"; -import "./mixins/MAssetProxy.sol"; - -contract MixinAssetProxy is - MAuthorizable, - MAssetProxy -{ - - /// @dev Transfers assets. Either succeeds or throws. - /// @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 assetData, - address from, - address to, - uint256 amount - ) - external - onlyAuthorized - { - transferFromInternal( - assetData, - from, - to, - amount - ); - } - - /// @dev Makes multiple transfers of assets. Either succeeds or throws. - /// @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 assetData, - address[] memory from, - address[] memory to, - uint256[] memory amounts - ) - public - onlyAuthorized - { - for (uint256 i = 0; i < assetData.length; i++) { - transferFromInternal( - assetData[i], - from[i], - to[i], - amounts[i] - ); - } - } -} diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinAuthorizable.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinAuthorizable.sol index 9aa8697ba..3b9584a44 100644 --- a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinAuthorizable.sol +++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinAuthorizable.sol @@ -49,10 +49,6 @@ contract MixinAuthorizable is !authorized[target], "TARGET_ALREADY_AUTHORIZED" ); - - assembly { - sstore(target, 1) - } authorized[target] = true; authorities.push(target); diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC20Transfer.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC20Transfer.sol deleted file mode 100644 index 5bd98a1f4..000000000 --- a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC20Transfer.sol +++ /dev/null @@ -1,115 +0,0 @@ -/* - - 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 "./mixins/MAuthorizable.sol"; -import "../../utils/LibBytes/LibBytes.sol"; -import "../../tokens/ERC20Token/IERC20Token.sol"; - -contract MixinERC20Transfer is - MAuthorizable -{ - using LibBytes for bytes; - - /// @dev Internal version of `transferFrom`. - /// @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 assetData, - address from, - address to, - uint256 amount - ) - internal - { - // Decode asset data. - address token = assetData.readAddress(16); - - // Transfer tokens. - // We do a raw call so we can check the success separate - // from the return data. - // We construct calldata for the `token.transferFrom` ABI. - // The layout of this calldata is in the table below. - // - // | Area | Offset | Length | Contents | - // |----------|--------|---------|-------------------------------------| - // | Header | 0 | 4 | function selector | - // | Params | | 3 * 32 | function parameters: | - // | | 4 | | 1. from | - // | | 36 | | 2. to | - // | | 68 | | 3. amount | - - bytes4 transferFromSelector = IERC20Token(token).transferFrom.selector; - bool success; - assembly { - /////// Setup State /////// - // `cdStart` is the start of the calldata for `token.transferFrom` (equal to free memory ptr). - let cdStart := mload(64) - - /////// Setup Header Area /////// - // This area holds the 4-byte `transferFromSelector`. - // Any trailing data in transferFromSelector will be - // overwritten in the next `mstore` call. - mstore(cdStart, transferFromSelector) - - /////// Setup Params Area /////// - // Each parameter is padded to 32-bytes. - // The entire Params Area is 96 bytes. - // A 20-byte mask is applied to addresses to - // zero-out the unused bytes. - mstore(add(cdStart, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) - mstore(add(cdStart, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) - mstore(add(cdStart, 68), amount) - - /////// Call `token.transferFrom` using the calldata /////// - success := call( - gas, // forward all gas - token, // call address of token contract - 0, // don't send any ETH - cdStart, // pointer to start of input - 100, // length of input - cdStart, // write output over input - 32 // output size should be 32 bytes - ) - - /////// Check return data. /////// - // If there is no return data, we assume the token incorrectly - // does not return a bool. In this case we expect it to revert - // on failure, which was handled above. - // If the token does return data, we require that it is a single - // nonzero 32 bytes value. - // So the transfer succeeded if the call succeeded and either - // returned nothing, or returned a non-zero 32 byte value. - success := and(success, or( - iszero(returndatasize), - and( - eq(returndatasize, 32), - gt(mload(cdStart), 0) - ) - )) - } - require( - success, - "TRANSFER_FAILED" - ); - } -} diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC721Transfer.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC721Transfer.sol deleted file mode 100644 index 561c2a625..000000000 --- a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC721Transfer.sol +++ /dev/null @@ -1,331 +0,0 @@ -/* - - 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 "./mixins/MAuthorizable.sol"; -import "../../utils/LibBytes/LibBytes.sol"; -import "./libs/LibTransferErrors.sol"; - -contract MixinERC721Transfer is - LibTransferErrors, - MAuthorizable -{ - using LibBytes for bytes; - - bytes4 constant SAFE_TRANSFER_FROM_SELECTOR = bytes4(keccak256("safeTransferFrom(address,address,uint256,bytes)")); - /// @dev Internal version of `transferFrom`. - /// @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 assetData, - address from, - address to, - uint256 amount - ) - external - onlyAuthorized() - { - // `transferFrom`. - // The function is marked `external`, so no abi decodeding is done for - // us. Instead, we expect the `calldata` memory to contain the - // following: - // - // | Area | Offset | Length | Contents | - // |----------|--------|---------|-------------------------------------| - // | Header | 0 | 4 | function selector | - // | Params | | 4 * 32 | function parameters: | - // | | 4 | | 1. offset to assetData (*) | - // | | 36 | | 2. from | - // | | 68 | | 3. to | - // | | 100 | | 4. amount | - // | Data | | | assetData: | - // | | 132 | 32 | assetData Length | - // | | 164 | ** | assetData Contents | - // - // (*): offset is computed from start of function parameters, so offset - // by an additional 4 bytes in the calldata. - // - // WARNING: The ABIv2 specification allows additional padding between - // the Params and Data section. This will result in a larger - // offset to assetData. - - // Asset data itself is encoded as follows: - // - // | Area | Offset | Length | Contents | - // |----------|--------|---------|-------------------------------------| - // | Header | 0 | 4 | function selector | - // | Params | | 3 * 32 | function parameters: | - // | | 4 | 12 + 20 | 1. token address | - // | | 36 | | 2. tokenId | - // | | 68 | | 3. offset to receiverData (*) | - // | Data | | | receiverData: | - // | | 100 | 32 | receiverData Length | - // | | 132 | ** | receiverData Contents | - - // We construct calldata for the `token.safeTransferFrom` ABI. - // The layout of this calldata is in the table below. - // - // | Area | Offset | Length | Contents | - // |----------|--------|---------|-------------------------------------| - // | Header | 0 | 4 | function selector | - // | Params | | 4 * 32 | function parameters: | - // | | 4 | | 1. from | - // | | 36 | | 2. to | - // | | 68 | | 3. tokenId | - // | | 100 | | 4. offset to receiverData (*) | - // | Data | | | receiverData: | - // | | 132 | 32 | receiverData Length | - // | | 164 | ** | receiverData Contents | - - // bytes4 safeTransferFromSelector = SAFE_TRANSFER_FROM_SELECTOR; - // bool success; - assembly { - // There exists only 1 of each token. - // require(amount == 1, "INVALID_AMOUNT") - if sub(calldataload(100), 1) { - // Revert with `Error("INVALID_AMOUNT")` - mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) - mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) - mstore(64, 0x0000000e494e56414c49445f414d4f554e540000000000000000000000000000) - mstore(96, 0) - revert(0, 100) - } - - // Require assetData to be at least 132 bytes - let offset := calldataload(4) - if lt(calldataload(add(offset, 4)), 132) { - // Revert with `Error("ASSET_DATA_TO_SHORT")` - mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) - mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) - mstore(64, 0x0000001341535345545f444154415f544f5f53484f5254000000000000000000) - mstore(96, 0) - revert(0, 100) - } - - /////// Setup State /////// - // `cdStart` is the start of the calldata for - // `token.safeTransferFrom` (equal to free memory ptr). - let cdStart := mload(64) - // `dataAreaLength` is the total number of words - // needed to store `receiverData` - // As-per the ABI spec, this value is padded up to - // the nearest multiple of 32, - // and includes 32-bytes for length. - // It's calculated as folows: - // - Unpadded length in bytes = `mload(receiverData) + 32` - // - Add 31 to convert rounding down to rounding up. - // Combined with the previous and this is `63`. - // - Round down to nearest multiple of 32 by clearing - // bits 0x1F. This is done with `and` and a mask. - - /////// Setup Header Area /////// - // This area holds the 4-byte `transferFromSelector`. - // Any trailing data in transferFromSelector will be - // overwritten in the next `mstore` call. - mstore(cdStart, 0xb88d4fde00000000000000000000000000000000000000000000000000000000) - - /////// Setup Params Area /////// - // Each parameter is padded to 32-bytes. - // The entire Params Area is 128 bytes. - // Notes: - // 1. A 20-byte mask is applied to addresses - // to zero-out the unused bytes. - // 2. The offset to `receiverData` is the length - // of the Params Area (128 bytes). - - let length := calldataload(add(offset, 136)) - let token := calldataload(add(offset, 40)) - - // Round length up to multiple of 32 - length := and(add(length, 31), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0) - - // Copy `from` and `to` - calldatacopy(add(cdStart, 4), 36, 64) - - // TokenId - mstore(add(cdStart, 68), calldataload(add(offset, 72))) - - // Offset to receiverData - mstore(add(cdStart, 100), 128) - - // receiverData (including length) - calldatacopy(add(cdStart, 132), add(offset, 136), add(length, 32)) - - /////// Call `token.safeTransferFrom` using the calldata /////// - let success := call( - gas, // forward all gas - token, // call address of token contract - 0, // don't send any ETH - cdStart, // pointer to start of input - add(length, 164), // length of input - 0, // write output to null - 0 // output size is 0 bytes - ) - if success { - return(0, 0) - } - - // Revert with `Error("TRANSFER_FAILED")` - mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) - mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) - mstore(64, 0x0000000f5452414e534645525f4641494c454400000000000000000000000000) - mstore(96, 0) - revert(0, 100) - } - } - - /// @dev Internal version of `transferFrom`. - /// @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 assetData, - address from, - address to, - uint256 amount - ) - internal - { - // There exists only 1 of each token. - require( - amount == 1, - INVALID_AMOUNT - ); - - // Decode asset data. - ( - address token, - uint256 tokenId, - bytes memory receiverData - ) = decodeERC721AssetData(assetData); - - // We construct calldata for the `token.safeTransferFrom` ABI. - // The layout of this calldata is in the table below. - // - // | Area | Offset | Length | Contents | - // |----------|--------|---------|-------------------------------------| - // | Header | 0 | 4 | function selector | - // | Params | | 4 * 32 | function parameters: | - // | | 4 | | 1. from | - // | | 36 | | 2. to | - // | | 68 | | 3. tokenId | - // | | 100 | | 4. offset to receiverData (*) | - // | Data | | | receiverData: | - // | | 132 | 32 | receiverData Length | - // | | 164 | ** | receiverData Contents | - - bytes4 safeTransferFromSelector = SAFE_TRANSFER_FROM_SELECTOR; - bool success; - assembly { - /////// Setup State /////// - // `cdStart` is the start of the calldata for - // `token.safeTransferFrom` (equal to free memory ptr). - let cdStart := mload(64) - // `dataAreaLength` is the total number of words - // needed to store `receiverData` - // As-per the ABI spec, this value is padded up to - // the nearest multiple of 32, - // and includes 32-bytes for length. - // It's calculated as folows: - // - Unpadded length in bytes = `mload(receiverData) + 32` - // - Add 31 to convert rounding down to rounding up. - // Combined with the previous and this is `63`. - // - Round down to nearest multiple of 32 by clearing - // bits 0x1F. This is done with `and` and a mask. - let dataAreaLength := and(add(mload(receiverData), 63), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0) - // `cdEnd` is the end of the calldata for `token.safeTransferFrom`. - let cdEnd := add(cdStart, add(132, dataAreaLength)) - - /////// Setup Header Area /////// - // This area holds the 4-byte `transferFromSelector`. - // Any trailing data in transferFromSelector will be - // overwritten in the next `mstore` call. - mstore(cdStart, safeTransferFromSelector) - - /////// Setup Params Area /////// - // Each parameter is padded to 32-bytes. - // The entire Params Area is 128 bytes. - // Notes: - // 1. A 20-byte mask is applied to addresses - // to zero-out the unused bytes. - // 2. The offset to `receiverData` is the length - // of the Params Area (128 bytes). - mstore(add(cdStart, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) - mstore(add(cdStart, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) - mstore(add(cdStart, 68), tokenId) - mstore(add(cdStart, 100), 128) - - /////// Setup Data Area /////// - // This area holds `receiverData`. - let dataArea := add(cdStart, 132) - for {} lt(dataArea, cdEnd) {} { - mstore(dataArea, mload(receiverData)) - dataArea := add(dataArea, 32) - receiverData := add(receiverData, 32) - } - - /////// Call `token.safeTransferFrom` using the calldata /////// - success := call( - gas, // forward all gas - token, // call address of token contract - 0, // don't send any ETH - cdStart, // pointer to start of input - sub(cdEnd, cdStart), // length of input - cdStart, // write output over input - 0 // output size is 0 bytes - ) - } - require( - success, - TRANSFER_FAILED - ); - } - - /// @dev Decodes ERC721 Asset data. - /// @param assetData Encoded byte array. - /// @return proxyId Intended ERC721 proxy id. - /// @return token ERC721 token address. - /// @return tokenId ERC721 token id. - /// @return receiverData Additional data with no specific format, which - /// is passed to the receiving contract's onERC721Received. - function decodeERC721AssetData(bytes memory assetData) - internal - pure - returns ( - address token, - uint256 tokenId, - bytes memory receiverData - ) - { - // Decode asset data. - token = assetData.readAddress(16); - tokenId = assetData.readUint256(36); - receiverData = assetData.readBytesWithLength(100); - - return ( - token, - tokenId, - receiverData - ); - } -} 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 d12d23610..ae8e195da 100644 --- a/packages/contracts/src/contracts/current/protocol/AssetProxy/interfaces/IAssetProxy.sol +++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/interfaces/IAssetProxy.sol @@ -38,19 +38,6 @@ contract IAssetProxy is ) external; - /// @dev Makes multiple transfers of assets. Either succeeds or throws. - /// @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 assetData, - address[] memory from, - address[] memory to, - uint256[] memory amounts - ) - public; - /// @dev Gets the proxy id associated with the proxy address. /// @return Proxy id. function getProxyId() 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 b0b20c044..338cb12e2 100644 --- a/packages/contracts/src/contracts/current/protocol/AssetProxy/libs/LibAssetProxyErrors.sol +++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/libs/LibAssetProxyErrors.sol @@ -23,9 +23,14 @@ pragma solidity ^0.4.24; contract LibAssetProxyErrors { /// Authorizable errors /// - string constant SENDER_NOT_AUTHORIZED = "SENDER_NOT_AUTHORIZED"; // Sender not authorized to call this method. - string constant TARGET_NOT_AUTHORIZED = "TARGET_NOT_AUTHORIZED"; // Target address not authorized to call this method. - string constant TARGET_ALREADY_AUTHORIZED = "TARGET_ALREADY_AUTHORIZED"; // Target address must not already be authorized. - string constant INDEX_OUT_OF_BOUNDS = "INDEX_OUT_OF_BOUNDS"; // Specified array index is out of bounds. - string constant AUTHORIZED_ADDRESS_MISMATCH = "AUTHORIZED_ADDRESS_MISMATCH"; // Address at index does not match given target address. + string constant SENDER_NOT_AUTHORIZED = "SENDER_NOT_AUTHORIZED"; // Sender not authorized to call this method. + string constant TARGET_NOT_AUTHORIZED = "TARGET_NOT_AUTHORIZED"; // Target address not authorized to call this method. + string constant TARGET_ALREADY_AUTHORIZED = "TARGET_ALREADY_AUTHORIZED"; // Target address must not already be authorized. + string constant INDEX_OUT_OF_BOUNDS = "INDEX_OUT_OF_BOUNDS"; // Specified array index is out of bounds. + string constant AUTHORIZED_ADDRESS_MISMATCH = "AUTHORIZED_ADDRESS_MISMATCH"; // Address at index does not match given target address. + + /// Transfer errors /// + string constant INVALID_AMOUNT = "INVALID_AMOUNT"; // Transfer amount must equal 1. + string constant TRANSFER_FAILED = "TRANSFER_FAILED"; // Transfer failed. + string constant LENGTH_GREATER_THAN_131_REQUIRED = "LENGTH_GREATER_THAN_131_REQUIRED"; // Byte array must have a length greater than 0. } diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/libs/LibTransferErrors.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/libs/LibTransferErrors.sol deleted file mode 100644 index 98bca31b2..000000000 --- a/packages/contracts/src/contracts/current/protocol/AssetProxy/libs/LibTransferErrors.sol +++ /dev/null @@ -1,29 +0,0 @@ -/* - - 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; - -/// @dev This contract documents the revert reasons used in the `transferFrom` methods of different AssetProxy contracts. -/// This contract is intended to serve as a reference, but is not actually used for efficiency reasons. -contract LibTransferErrors { - - /// Transfer errors /// - string constant INVALID_AMOUNT = "INVALID_AMOUNT"; // Transfer amount must equal 1. - string constant TRANSFER_FAILED = "TRANSFER_FAILED"; // Transfer failed. - string constant ASSET_DATA_TO_SHORT = "ASSET_DATA_TO_SHORT"; // Asset data to short -} diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/mixins/MAssetProxy.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/mixins/MAssetProxy.sol deleted file mode 100644 index a52cb56f9..000000000 --- a/packages/contracts/src/contracts/current/protocol/AssetProxy/mixins/MAssetProxy.sol +++ /dev/null @@ -1,40 +0,0 @@ -/* - - 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 "../interfaces/IAssetProxy.sol"; - -contract MAssetProxy is - IAssetProxy -{ - - /// @dev Internal version of `transferFrom`. - /// @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 assetData, - address from, - address to, - uint256 amount - ) - internal; -} diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinAssetProxyDispatcher.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinAssetProxyDispatcher.sol index 3203322aa..93e752af3 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinAssetProxyDispatcher.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinAssetProxyDispatcher.sol @@ -135,7 +135,6 @@ contract MixinAssetProxyDispatcher is // | | 132 | 32 | assetData Length | // | | 164 | ** | assetData Contents | - bytes4 transferFromSelector = IAssetProxy(assetProxy).transferFrom.selector; bool success; assembly { /////// Setup State /////// @@ -151,7 +150,8 @@ contract MixinAssetProxyDispatcher is /////// Setup Header Area /////// // This area holds the 4-byte `transferFromSelector`. - mstore(cdStart, transferFromSelector) + // bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4 + mstore(cdStart, 0xa85e59e400000000000000000000000000000000000000000000000000000000) /////// Setup Params Area /////// // Each parameter is padded to 32-bytes. The entire Params Area is 128 bytes. diff --git a/packages/contracts/src/contracts/current/test/TestAssetDataDecoders/TestAssetDataDecoders.sol b/packages/contracts/src/contracts/current/test/TestAssetDataDecoders/TestAssetDataDecoders.sol deleted file mode 100644 index 5987291d2..000000000 --- a/packages/contracts/src/contracts/current/test/TestAssetDataDecoders/TestAssetDataDecoders.sol +++ /dev/null @@ -1,56 +0,0 @@ -/* - - Copyright 2018 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.4.24; -pragma experimental ABIEncoderV2; - -import "../../protocol/AssetProxy/ERC20Proxy.sol"; -import "../../protocol/AssetProxy/ERC721Proxy.sol"; - -contract TestAssetDataDecoders is - ERC721Proxy -{ - /// @dev Decodes ERC721 Asset data. - /// @param assetData Encoded byte array. - /// @return proxyId Intended ERC721 proxy id. - /// @return token ERC721 token address. - /// @return tokenId ERC721 token id. - /// @return receiverData Additional data with no specific format, which - /// is passed to the receiving contract's onERC721Received. - function publicDecodeERC721Data(bytes memory assetData) - public - pure - returns ( - address token, - uint256 tokenId, - bytes memory receiverData - ) - { - ( - token, - tokenId, - receiverData - ) = decodeERC721AssetData(assetData); - - return ( - token, - tokenId, - receiverData - ); - } -} diff --git a/packages/contracts/src/utils/artifacts.ts b/packages/contracts/src/utils/artifacts.ts index a46bab9ae..d58e7973c 100644 --- a/packages/contracts/src/utils/artifacts.ts +++ b/packages/contracts/src/utils/artifacts.ts @@ -11,7 +11,6 @@ import * as ExchangeWrapper from '../artifacts/ExchangeWrapper.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 TestAssetProxyOwner from '../artifacts/TestAssetProxyOwner.json'; import * as TestLibBytes from '../artifacts/TestLibBytes.json'; @@ -39,7 +38,6 @@ export const artifacts = { MultiSigWalletWithTimeLock: (MultiSigWalletWithTimeLock as any) as ContractArtifact, TestAssetProxyOwner: (TestAssetProxyOwner as any) as ContractArtifact, TestAssetProxyDispatcher: (TestAssetProxyDispatcher as any) as ContractArtifact, - TestAssetDataDecoders: (TestAssetDataDecoders as any) as ContractArtifact, TestLibBytes: (TestLibBytes as any) as ContractArtifact, TestLibs: (TestLibs as any) as ContractArtifact, TestSignatureValidator: (TestSignatureValidator as any) as ContractArtifact, -- cgit From b6d8dcb6e11cfe8da6854371029fe1af3f6a68de Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Tue, 26 Jun 2018 12:59:35 -0700 Subject: Make dispatchTransferFrom revert with reason from AssetProxy on failure --- .../current/protocol/Exchange/MixinAssetProxyDispatcher.sol | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'packages/contracts/src') diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinAssetProxyDispatcher.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinAssetProxyDispatcher.sol index 93e752af3..f7086f543 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinAssetProxyDispatcher.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinAssetProxyDispatcher.sol @@ -180,13 +180,12 @@ contract MixinAssetProxyDispatcher is cdStart, // pointer to start of input sub(cdEnd, cdStart), // length of input cdStart, // write output over input - 0 // output size is 0 bytes + 512 // reserve 512 bytes for output ) + if eq(success, 0) { + revert(cdStart, returndatasize()) + } } - require( - success, - "TRANSFER_FAILED" - ); } } } -- cgit