Skip to content

Commit

Permalink
ERC721 full implementation (#803)
Browse files Browse the repository at this point in the history
* Rename current ERC721 implementation to BaseERC721

* Implement ERC721 optional & approveAll functionality

* Support for new ERC721 interface

- Tests for new features are pending
- ERC721 is abstract, since it requires metadata implementation
- Move some methods into DeprecatedERC721 contract
- Reorganise base vs full implementation
- Pending tokenByIndex

* Add more tests for ERC721

* Implement suggestions by @dekz

* Update comments in ERC721 contracts

* Implement tokensByIndex extension

- Remove restrictions from mock mint and burn calls

* Add default implementation for metadata URI

This allows token implementation to be non-abstract

* Allow operators to call approve on a token

* Remove gas stipend restriction in call to 721 receiver

* Remove deprecated implementation

We only want to keep the interface, for interacting with already deployed contracts

* Add notice to isContract helper on constract constructors

* Change natspec delimiters for consistency

* Minor linting fixes

* Add constant modifier to ERC721_RECEIVED magic value

* Use 4-params safeTransferFrom for implementing the 3-params overload

* Minor text changes in natspec comments

* Use address(0) instead of 0 or 0x0

* Use if-statements instead of boolean one-liners for clarity

:-(

* Keep ownedTokensCount state var in sync in full ERC721 implementation

* Fix incorrect comparison when burning ERC721 tokens with metadata

* Use address(0) instead of 0 in one more place in ERC721

* Throw when querying balance for the zero address

Required by the spec

* Update links to approved version of EIP721

* Use explicit size for uint

* Remove unneeded internal function in ERC721

Also rename addToken and removeToken for added clarity

* Use underscore instead of 'do' prefix for internal methods in ERC721

* Fix failing test due to events reordering in ERC721 safe transfer

* Fix bug introduced in 74db03b

* Remove do prefix for internal setTokenUri method

* Allow transfers to self in ERC721
  • Loading branch information
spalladino authored and frangio committed Mar 23, 2018
1 parent 9f52e94 commit e96164f
Show file tree
Hide file tree
Showing 16 changed files with 1,381 additions and 617 deletions.
21 changes: 21 additions & 0 deletions contracts/AddressUtils.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
pragma solidity ^0.4.18;

/**
* Utility library of inline functions on addresses
*/
library AddressUtils {

/**
* Returns whether there is code in the target address
* @dev This function will return false if invoked during the constructor of a contract,
* as the code is not actually created until after the constructor finishes.
* @param addr address address to check
* @return whether there is code in the target address
*/
function isContract(address addr) internal view returns (bool) {
uint256 size;
assembly { size := extcodesize(addr) }
return size > 0;
}

}
17 changes: 17 additions & 0 deletions contracts/mocks/ERC721BasicTokenMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
pragma solidity ^0.4.18;

import "../token/ERC721/ERC721BasicToken.sol";

/**
* @title ERC721BasicTokenMock
* This mock just provides a public mint and burn functions for testing purposes
*/
contract ERC721BasicTokenMock is ERC721BasicToken {
function mint(address _to, uint256 _tokenId) public {
super._mint(_to, _tokenId);
}

function burn(uint256 _tokenId) public {
super._burn(ownerOf(_tokenId), _tokenId);
}
}
21 changes: 21 additions & 0 deletions contracts/mocks/ERC721ReceiverMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
pragma solidity ^0.4.18;

import "../token/ERC721/ERC721Receiver.sol";

contract ERC721ReceiverMock is ERC721Receiver {
bytes4 retval;
bool reverts;

event Received(address _address, uint256 _tokenId, bytes _data, uint256 _gas);

function ERC721ReceiverMock(bytes4 _retval, bool _reverts) public {
retval = _retval;
reverts = _reverts;
}

function onERC721Received(address _address, uint256 _tokenId, bytes _data) public returns(bytes4) {
require(!reverts);
Received(_address, _tokenId, _data, msg.gas);
return retval;
}
}
15 changes: 11 additions & 4 deletions contracts/mocks/ERC721TokenMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,23 @@ import "../token/ERC721/ERC721Token.sol";

/**
* @title ERC721TokenMock
* This mock just provides a public mint and burn functions for testing purposes.
* This mock just provides a public mint and burn functions for testing purposes,
* and a public setter for metadata URI
*/
contract ERC721TokenMock is ERC721Token {
function ERC721TokenMock() ERC721Token() public { }
function ERC721TokenMock(string name, string symbol) public
ERC721Token(name, symbol)
{ }

function mint(address _to, uint256 _tokenId) public {
super._mint(_to, _tokenId);
}

function burn(uint256 _tokenId) public {
super._burn(_tokenId);
super._burn(ownerOf(_tokenId), _tokenId);
}
}

function setTokenURI(uint256 _tokenId, string _uri) public {
super._setTokenURI(_tokenId, _uri);
}
}
15 changes: 15 additions & 0 deletions contracts/token/ERC721/DeprecatedERC721.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
pragma solidity ^0.4.18;

import "./ERC721.sol";

/**
* @title ERC-721 methods shipped in OpenZeppelin v1.7.0, removed in the latest version of the standard
* @dev Only use this interface for compatibility with previously deployed contracts
* @dev Use ERC721 for interacting with new contracts which are standard-compliant
*/
contract DeprecatedERC721 is ERC721 {
function takeOwnership(uint256 _tokenId) public;
function transfer(address _to, uint256 _tokenId) public;
function tokensOf(address _owner) public view returns (uint256[]);
}

34 changes: 24 additions & 10 deletions contracts/token/ERC721/ERC721.sol
Original file line number Diff line number Diff line change
@@ -1,16 +1,30 @@
pragma solidity ^0.4.18;

import "./ERC721Basic.sol";

/**
* @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
* @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
*/
contract ERC721Enumerable is ERC721Basic {
function totalSupply() public view returns (uint256);
function tokenOfOwnerByIndex(address _owner, uint256 _index) public view returns (uint256 _tokenId);
function tokenByIndex(uint256 _index) public view returns (uint256);
}

/**
* @title ERC721 interface
* @dev see https://github.com/ethereum/eips/issues/721
* @title ERC-721 Non-Fungible Token Standard, optional metadata extension
* @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
*/
contract ERC721 {
event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
contract ERC721Metadata is ERC721Basic {
function name() public view returns (string _name);
function symbol() public view returns (string _symbol);
function tokenURI(uint256 _tokenId) public view returns (string);
}

function balanceOf(address _owner) public view returns (uint256 _balance);
function ownerOf(uint256 _tokenId) public view returns (address _owner);
function transfer(address _to, uint256 _tokenId) public;
function approve(address _to, uint256 _tokenId) public;
function takeOwnership(uint256 _tokenId) public;
/**
* @title ERC-721 Non-Fungible Token Standard, full implementation interface
* @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
*/
contract ERC721 is ERC721Basic, ERC721Enumerable, ERC721Metadata {
}
25 changes: 25 additions & 0 deletions contracts/token/ERC721/ERC721Basic.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
pragma solidity ^0.4.18;

/**
* @title ERC721 Non-Fungible Token Standard basic interface
* @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
*/
contract ERC721Basic {
event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);

function balanceOf(address _owner) public view returns (uint256 _balance);
function ownerOf(uint256 _tokenId) public view returns (address _owner);
function exists(uint256 _tokenId) public view returns (bool _exists);

function approve(address _to, uint256 _tokenId) public;
function getApproved(uint256 _tokenId) public view returns (address _operator);

function setApprovalForAll(address _operator, bool _approved) public;
function isApprovedForAll(address _owner, address _operator) public view returns (bool);

function transferFrom(address _from, address _to, uint256 _tokenId) public;
function safeTransferFrom(address _from, address _to, uint256 _tokenId) public;
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes _data) public;
}
Loading

0 comments on commit e96164f

Please sign in to comment.