Commit 7d8e3ca8 by Paul Barclay Committed by Matt Condon

Align ERC721 Receiver with current ERC721 standard. (#1047)

* Align ERC721 Receiver with current ERC721 standard.
Adds a second address field to onERC721Received
onERC721Received(address,address,uint256,bytes)
Updates the function signature to 0x150b7a02 from 0xf0b9e5ba

* Add _operator to onERC721Received

* Fix error caused by formatOnSave

* Fixed comments on ERC721Receiver
Removed "Must use 50,000 gas or less"
Corrected the function signature
parent 6b37ba36
...@@ -8,7 +8,8 @@ contract ERC721ReceiverMock is ERC721Receiver { ...@@ -8,7 +8,8 @@ contract ERC721ReceiverMock is ERC721Receiver {
bool reverts; bool reverts;
event Received( event Received(
address _address, address _operator,
address _from,
uint256 _tokenId, uint256 _tokenId,
bytes _data, bytes _data,
uint256 _gas uint256 _gas
...@@ -20,7 +21,8 @@ contract ERC721ReceiverMock is ERC721Receiver { ...@@ -20,7 +21,8 @@ contract ERC721ReceiverMock is ERC721Receiver {
} }
function onERC721Received( function onERC721Received(
address _address, address _operator,
address _from,
uint256 _tokenId, uint256 _tokenId,
bytes _data bytes _data
) )
...@@ -29,7 +31,8 @@ contract ERC721ReceiverMock is ERC721Receiver { ...@@ -29,7 +31,8 @@ contract ERC721ReceiverMock is ERC721Receiver {
{ {
require(!reverts); require(!reverts);
emit Received( emit Received(
_address, _operator,
_from,
_tokenId, _tokenId,
_data, _data,
gasleft() // msg.gas was deprecated in solidityv0.4.21 gasleft() // msg.gas was deprecated in solidityv0.4.21
......
...@@ -36,9 +36,9 @@ contract ERC721BasicToken is SupportsInterfaceWithLookup, ERC721Basic { ...@@ -36,9 +36,9 @@ contract ERC721BasicToken is SupportsInterfaceWithLookup, ERC721Basic {
using SafeMath for uint256; using SafeMath for uint256;
using AddressUtils for address; using AddressUtils for address;
// Equals to `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))` // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
// which can be also obtained as `ERC721Receiver(0).onERC721Received.selector` // which can be also obtained as `ERC721Receiver(0).onERC721Received.selector`
bytes4 private constant ERC721_RECEIVED = 0xf0b9e5ba; bytes4 private constant ERC721_RECEIVED = 0x150b7a02;
// Mapping from token ID to owner // Mapping from token ID to owner
mapping (uint256 => address) internal tokenOwner; mapping (uint256 => address) internal tokenOwner;
...@@ -194,7 +194,7 @@ contract ERC721BasicToken is SupportsInterfaceWithLookup, ERC721Basic { ...@@ -194,7 +194,7 @@ contract ERC721BasicToken is SupportsInterfaceWithLookup, ERC721Basic {
* @dev Safely transfers the ownership of a given token ID to another address * @dev Safely transfers the ownership of a given token ID to another address
* If the target address is a contract, it must implement `onERC721Received`, * If the target address is a contract, it must implement `onERC721Received`,
* which is called upon a safe transfer, and return the magic value * which is called upon a safe transfer, and return the magic value
* `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`; otherwise, * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
* the transfer is reverted. * the transfer is reverted.
* *
* Requires the msg sender to be the owner, approved, or operator * Requires the msg sender to be the owner, approved, or operator
...@@ -218,7 +218,7 @@ contract ERC721BasicToken is SupportsInterfaceWithLookup, ERC721Basic { ...@@ -218,7 +218,7 @@ contract ERC721BasicToken is SupportsInterfaceWithLookup, ERC721Basic {
* @dev Safely transfers the ownership of a given token ID to another address * @dev Safely transfers the ownership of a given token ID to another address
* If the target address is a contract, it must implement `onERC721Received`, * If the target address is a contract, it must implement `onERC721Received`,
* which is called upon a safe transfer, and return the magic value * which is called upon a safe transfer, and return the magic value
* `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`; otherwise, * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
* the transfer is reverted. * the transfer is reverted.
* Requires the msg sender to be the owner, approved, or operator * Requires the msg sender to be the owner, approved, or operator
* @param _from current owner of the token * @param _from current owner of the token
...@@ -346,7 +346,7 @@ contract ERC721BasicToken is SupportsInterfaceWithLookup, ERC721Basic { ...@@ -346,7 +346,7 @@ contract ERC721BasicToken is SupportsInterfaceWithLookup, ERC721Basic {
return true; return true;
} }
bytes4 retval = ERC721Receiver(_to).onERC721Received( bytes4 retval = ERC721Receiver(_to).onERC721Received(
_from, _tokenId, _data); msg.sender, _from, _tokenId, _data);
return (retval == ERC721_RECEIVED); return (retval == ERC721_RECEIVED);
} }
} }
...@@ -4,7 +4,7 @@ import "./ERC721Receiver.sol"; ...@@ -4,7 +4,7 @@ import "./ERC721Receiver.sol";
contract ERC721Holder is ERC721Receiver { contract ERC721Holder is ERC721Receiver {
function onERC721Received(address, uint256, bytes) public returns(bytes4) { function onERC721Received(address, address, uint256, bytes) public returns(bytes4) {
return ERC721_RECEIVED; return ERC721_RECEIVED;
} }
} }
...@@ -9,24 +9,26 @@ pragma solidity ^0.4.24; ...@@ -9,24 +9,26 @@ pragma solidity ^0.4.24;
contract ERC721Receiver { contract ERC721Receiver {
/** /**
* @dev Magic value to be returned upon successful reception of an NFT * @dev Magic value to be returned upon successful reception of an NFT
* Equals to `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`, * Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`,
* which can be also obtained as `ERC721Receiver(0).onERC721Received.selector` * which can be also obtained as `ERC721Receiver(0).onERC721Received.selector`
*/ */
bytes4 internal constant ERC721_RECEIVED = 0xf0b9e5ba; bytes4 internal constant ERC721_RECEIVED = 0x150b7a02;
/** /**
* @notice Handle the receipt of an NFT * @notice Handle the receipt of an NFT
* @dev The ERC721 smart contract calls this function on the recipient * @dev The ERC721 smart contract calls this function on the recipient
* after a `safetransfer`. This function MAY throw to revert and reject the * after a `safetransfer`. This function MAY throw to revert and reject the
* transfer. This function MUST use 50,000 gas or less. Return of other * transfer. Return of other than the magic value MUST result in the
* than the magic value MUST result in the transaction being reverted. * transaction being reverted.
* Note: the contract address is always the message sender. * Note: the contract address is always the message sender.
* @param _from The sending address * @param _operator The address which called `safeTransferFrom` function
* @param _from The address which previously owned the token
* @param _tokenId The NFT identifier which is being transfered * @param _tokenId The NFT identifier which is being transfered
* @param _data Additional data with no specified format * @param _data Additional data with no specified format
* @return `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))` * @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
*/ */
function onERC721Received( function onERC721Received(
address _operator,
address _from, address _from,
uint256 _tokenId, uint256 _tokenId,
bytes _data bytes _data
......
...@@ -18,7 +18,7 @@ export default function shouldBehaveLikeERC721BasicToken (accounts) { ...@@ -18,7 +18,7 @@ export default function shouldBehaveLikeERC721BasicToken (accounts) {
const unknownTokenId = 3; const unknownTokenId = 3;
const creator = accounts[0]; const creator = accounts[0];
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'; const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
const RECEIVER_MAGIC_VALUE = '0xf0b9e5ba'; const RECEIVER_MAGIC_VALUE = '0x150b7a02';
describe('like an ERC721BasicToken', function () { describe('like an ERC721BasicToken', function () {
beforeEach(async function () { beforeEach(async function () {
...@@ -280,7 +280,19 @@ export default function shouldBehaveLikeERC721BasicToken (accounts) { ...@@ -280,7 +280,19 @@ export default function shouldBehaveLikeERC721BasicToken (accounts) {
result.receipt.logs.length.should.be.equal(2); result.receipt.logs.length.should.be.equal(2);
const [log] = decodeLogs([result.receipt.logs[1]], ERC721Receiver, this.receiver.address); const [log] = decodeLogs([result.receipt.logs[1]], ERC721Receiver, this.receiver.address);
log.event.should.be.eq('Received'); log.event.should.be.eq('Received');
log.args._address.should.be.equal(owner); log.args._operator.should.be.equal(owner);
log.args._from.should.be.equal(owner);
log.args._tokenId.toNumber().should.be.equal(tokenId);
log.args._data.should.be.equal(data);
});
it('should call onERC721Received from approved', async function () {
const result = await transferFun.call(this, owner, this.to, tokenId, { from: approved });
result.receipt.logs.length.should.be.equal(2);
const [log] = decodeLogs([result.receipt.logs[1]], ERC721Receiver, this.receiver.address);
log.event.should.be.eq('Received');
log.args._operator.should.be.equal(approved);
log.args._from.should.be.equal(owner);
log.args._tokenId.toNumber().should.be.equal(tokenId); log.args._tokenId.toNumber().should.be.equal(tokenId);
log.args._data.should.be.equal(data); log.args._data.should.be.equal(data);
}); });
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment