From 9175b43542a8797b0eb3c24e8188ebac67242733 Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Sat, 23 Jun 2018 18:03:49 +0200 Subject: Add Greg's documentation to MixinErc721Transfer --- .../protocol/AssetProxy/MixinERC721Transfer.sol | 77 ++++++++++++---------- 1 file changed, 44 insertions(+), 33 deletions(-) (limited to 'packages/contracts') diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC721Transfer.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC721Transfer.sol index af986adfa..c170917ea 100644 --- a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC721Transfer.sol +++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC721Transfer.sol @@ -27,7 +27,9 @@ contract MixinERC721Transfer is LibTransferErrors { using LibBytes for bytes; - bytes4 constant SAFE_TRANSFER_FROM_SELECTOR = bytes4(keccak256("safeTransferFrom(address,address,uint256,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. @@ -57,44 +59,54 @@ contract MixinERC721Transfer is // 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 | + // | 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). + // `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: + // `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) + // - 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. + // 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). + // 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) @@ -108,19 +120,18 @@ contract MixinERC721Transfer is dataArea := add(dataArea, 32) receiverData := add(receiverData, 32) } - - /////// Call `token.safeTransferFrom` using the constructed calldata /////// + + /////// Call `token.safeTransferFrom` using the calldata /////// success := call( - gas, - token, - 0, - cdStart, - sub(cdEnd, cdStart), - cdStart, - 0 + 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 -- cgit v1.2.3