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.
|
||||
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.
|
||||
// If there is no return data, we assume the token incorrectly
|
||||
// does not return a bool. In this case we expect it to revert
|
||||
// on failure, which was handled above.
|
||||
// If the token does return data, we require that it is a single
|
||||
// value that evaluates to true.
|
||||
bytes4 transferFromSelector = IERC20Token(token).transferFrom.selector;
|
||||
bool success;
|
||||
assembly {
|
||||
if returndatasize {
|
||||
success := 0
|
||||
if eq(returndatasize, 32) {
|
||||
// First 64 bytes of memory are reserved scratch space
|
||||
returndatacopy(0, 0, 32)
|
||||
success := mload(0)
|
||||
/////// Setup State ///////
|
||||
// `cdStart` is the start of the calldata for `token.transferFrom` (equal to free memory ptr).
|
||||
let cdStart := mload(64)
|
||||
// `cdEnd` is the end of the calldata for `token.transferFrom`.
|
||||
let cdEnd := add(cdStart, 100)
|
||||
|
||||
/////// 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(
|
||||
|
@@ -27,7 +27,7 @@ contract MixinERC721Transfer is
|
||||
LibTransferErrors
|
||||
{
|
||||
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.
|
||||
@@ -54,11 +54,76 @@ contract MixinERC721Transfer is
|
||||
bytes memory receiverData
|
||||
) = decodeERC721AssetData(assetData);
|
||||
|
||||
ERC721Token(token).safeTransferFrom(
|
||||
from,
|
||||
to,
|
||||
tokenId,
|
||||
receiverData
|
||||
// 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 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.
|
||||
if (amount > 0) {
|
||||
require(assetData.length >= 4, "ASSET_DATA_LENGTH");
|
||||
|
||||
// Lookup assetProxy
|
||||
bytes4 assetProxyId = assetData.readBytes4(0);
|
||||
bytes4 assetProxyId;
|
||||
assembly {
|
||||
assetProxyId := and(mload(add(assetData, 32)),
|
||||
0xFFFFFFFF00000000000000000000000000000000000000000000000000000000
|
||||
)
|
||||
}
|
||||
IAssetProxy assetProxy = assetProxies[assetProxyId];
|
||||
// Ensure that assetProxy exists
|
||||
require(
|
||||
assetProxy != address(0),
|
||||
ASSET_PROXY_DOES_NOT_EXIST
|
||||
);
|
||||
// transferFrom will either succeed or throw.
|
||||
|
||||
/*
|
||||
assetProxy.transferFrom(
|
||||
assetData,
|
||||
from,
|
||||
to,
|
||||
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