Optimize like in PR #767
This commit is contained in:
committed by
Amir Bandeali
parent
a2ff63daa5
commit
4df66a4802
@@ -43,35 +43,47 @@ contract MixinERC20Transfer is
|
|||||||
{
|
{
|
||||||
// Decode asset data.
|
// Decode asset data.
|
||||||
address token = assetData.readAddress(16);
|
address token = assetData.readAddress(16);
|
||||||
|
|
||||||
// Transfer tokens.
|
|
||||||
// We do a raw call so we can check the success separate
|
|
||||||
// from the return data.
|
|
||||||
bool success = token.call(abi.encodeWithSelector(
|
|
||||||
IERC20Token(token).transferFrom.selector,
|
|
||||||
from,
|
|
||||||
to,
|
|
||||||
amount
|
|
||||||
));
|
|
||||||
require(
|
|
||||||
success,
|
|
||||||
TRANSFER_FAILED
|
|
||||||
);
|
|
||||||
|
|
||||||
// Check return data.
|
bytes4 transferFromSelector = IERC20Token(token).transferFrom.selector;
|
||||||
// If there is no return data, we assume the token incorrectly
|
bool success;
|
||||||
// does not return a bool. In this case we expect it to revert
|
|
||||||
// on failure, which was handled above.
|
|
||||||
// If the token does return data, we require that it is a single
|
|
||||||
// value that evaluates to true.
|
|
||||||
assembly {
|
assembly {
|
||||||
if returndatasize {
|
/////// Setup State ///////
|
||||||
success := 0
|
// `cdStart` is the start of the calldata for `token.transferFrom` (equal to free memory ptr).
|
||||||
if eq(returndatasize, 32) {
|
let cdStart := mload(64)
|
||||||
// First 64 bytes of memory are reserved scratch space
|
// `cdEnd` is the end of the calldata for `token.transferFrom`.
|
||||||
returndatacopy(0, 0, 32)
|
let cdEnd := add(cdStart, 100)
|
||||||
success := mload(0)
|
|
||||||
|
/////// Setup Header Area ///////
|
||||||
|
// This area holds the 4-byte `transferFromSelector`.
|
||||||
|
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 constructed calldata ///////
|
||||||
|
success := call(
|
||||||
|
gas,
|
||||||
|
token,
|
||||||
|
0,
|
||||||
|
cdStart,
|
||||||
|
sub(cdEnd, cdStart),
|
||||||
|
cdStart,
|
||||||
|
32
|
||||||
|
)
|
||||||
|
if success {
|
||||||
|
if returndatasize {
|
||||||
|
success := 0
|
||||||
|
if eq(returndatasize, 32) {
|
||||||
|
// First 64 bytes of memory are reserved scratch space
|
||||||
|
returndatacopy(0, 0, 32)
|
||||||
|
success := mload(0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
require(
|
require(
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ contract MixinERC721Transfer is
|
|||||||
LibTransferErrors
|
LibTransferErrors
|
||||||
{
|
{
|
||||||
using LibBytes for bytes;
|
using LibBytes for bytes;
|
||||||
|
bytes4 constant SAFE_TRANSFER_FROM_SELECTOR = bytes4(keccak256("safeTransferFrom(address,address,uint256,bytes)"));
|
||||||
/// @dev Internal version of `transferFrom`.
|
/// @dev Internal version of `transferFrom`.
|
||||||
/// @param assetData Encoded byte array.
|
/// @param assetData Encoded byte array.
|
||||||
/// @param from Address to transfer asset from.
|
/// @param from Address to transfer asset from.
|
||||||
@@ -54,11 +54,76 @@ contract MixinERC721Transfer is
|
|||||||
bytes memory receiverData
|
bytes memory receiverData
|
||||||
) = decodeERC721AssetData(assetData);
|
) = decodeERC721AssetData(assetData);
|
||||||
|
|
||||||
ERC721Token(token).safeTransferFrom(
|
// We construct calldata for the `token.safeTransferFrom` ABI.
|
||||||
from,
|
// The layout of this calldata is in the table below.
|
||||||
to,
|
//
|
||||||
tokenId,
|
// | Area | Offset | Length | Contents |
|
||||||
receiverData
|
// | -------- |--------|---------|-------------------------------------------- |
|
||||||
|
// | 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 this value then divide by 32 to get the length in words.
|
||||||
|
// - Multiply this value by 32 to get the padded length in bytes.
|
||||||
|
let dataAreaLength := mul(div(add(mload(receiverData), 63), 32), 32)
|
||||||
|
// `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`.
|
||||||
|
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 constructed calldata ///////
|
||||||
|
success := call(
|
||||||
|
gas,
|
||||||
|
token,
|
||||||
|
0,
|
||||||
|
cdStart,
|
||||||
|
sub(cdEnd, cdStart),
|
||||||
|
cdStart,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
require(
|
||||||
|
success,
|
||||||
|
TRANSFER_FAILED
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -100,21 +100,67 @@ contract MixinAssetProxyDispatcher is
|
|||||||
{
|
{
|
||||||
// Do nothing if no amount should be transferred.
|
// Do nothing if no amount should be transferred.
|
||||||
if (amount > 0) {
|
if (amount > 0) {
|
||||||
|
require(assetData.length >= 4, "ASSET_DATA_LENGTH");
|
||||||
|
|
||||||
// Lookup assetProxy
|
// Lookup assetProxy
|
||||||
bytes4 assetProxyId = assetData.readBytes4(0);
|
bytes4 assetProxyId;
|
||||||
|
assembly {
|
||||||
|
assetProxyId := and(mload(add(assetData, 32)),
|
||||||
|
0xFFFFFFFF00000000000000000000000000000000000000000000000000000000
|
||||||
|
)
|
||||||
|
}
|
||||||
IAssetProxy assetProxy = assetProxies[assetProxyId];
|
IAssetProxy assetProxy = assetProxies[assetProxyId];
|
||||||
// Ensure that assetProxy exists
|
// Ensure that assetProxy exists
|
||||||
require(
|
require(
|
||||||
assetProxy != address(0),
|
assetProxy != address(0),
|
||||||
ASSET_PROXY_DOES_NOT_EXIST
|
ASSET_PROXY_DOES_NOT_EXIST
|
||||||
);
|
);
|
||||||
// transferFrom will either succeed or throw.
|
|
||||||
|
/*
|
||||||
assetProxy.transferFrom(
|
assetProxy.transferFrom(
|
||||||
assetData,
|
assetData,
|
||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
amount
|
amount
|
||||||
);
|
);
|
||||||
|
return;
|
||||||
|
*/
|
||||||
|
|
||||||
|
bytes4 transferFromSelector = IAssetProxy(assetProxy).transferFrom.selector;
|
||||||
|
bool success;
|
||||||
|
assembly {
|
||||||
|
|
||||||
|
let cdStart := mload(0x40)
|
||||||
|
let dataAreaLength := mul(div(add(mload(assetData), 63), 32), 32)
|
||||||
|
let cdEnd := add(cdStart, add(132, dataAreaLength))
|
||||||
|
|
||||||
|
mstore(cdStart, transferFromSelector)
|
||||||
|
mstore(add(cdStart, 4), 128)
|
||||||
|
mstore(add(cdStart, 36), and(from, 0xffffffffffffffffffffffffffffffffffffffff))
|
||||||
|
mstore(add(cdStart, 68), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
|
||||||
|
mstore(add(cdStart, 100), amount)
|
||||||
|
|
||||||
|
let dataArea := add(cdStart, 132)
|
||||||
|
for {} lt(dataArea, cdEnd) {} {
|
||||||
|
mstore(dataArea, mload(assetData))
|
||||||
|
dataArea := add(dataArea, 32)
|
||||||
|
assetData := add(assetData, 32)
|
||||||
|
}
|
||||||
|
|
||||||
|
success := call(
|
||||||
|
gas,
|
||||||
|
assetProxy,
|
||||||
|
0,
|
||||||
|
cdStart,
|
||||||
|
sub(cdEnd, cdStart),
|
||||||
|
cdStart,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
require(
|
||||||
|
success,
|
||||||
|
"TRANSFER_FAILED"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user