Remove redundant files, hard code function selector in dispatchTransferFrom, and modify revert reason
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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()
|
||||
|
@@ -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]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@@ -49,10 +49,6 @@ contract MixinAuthorizable is
|
||||
!authorized[target],
|
||||
"TARGET_ALREADY_AUTHORIZED"
|
||||
);
|
||||
|
||||
assembly {
|
||||
sstore(target, 1)
|
||||
}
|
||||
|
||||
authorized[target] = true;
|
||||
authorities.push(target);
|
||||
|
@@ -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"
|
||||
);
|
||||
}
|
||||
}
|
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
@@ -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()
|
||||
|
@@ -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.
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
@@ -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;
|
||||
}
|
@@ -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.
|
||||
|
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
@@ -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,
|
||||
|
Reference in New Issue
Block a user