From d844941163ee3ed7f6cf5bcd3c7e02ee5f6a717e Mon Sep 17 00:00:00 2001 From: William Entriken Date: Mon, 2 Jul 2018 21:18:21 -0400 Subject: [PATCH] Update to ERC721 final spec and Solidity 0.4.24 --- contracts/AccessControl.sol | 6 +-- contracts/ERC165.sol | 2 +- contracts/ERC721.sol | 12 ++--- contracts/SuMain.sol | 5 +-- contracts/SuNFT.sol | 44 ++++++++++--------- contracts/SuOperation.sol | 4 +- contracts/SuPromo.sol | 4 +- contracts/SuVending.sol | 2 +- ...shInterfaces.sol => SupportsInterface.sol} | 8 ++-- 9 files changed, 44 insertions(+), 43 deletions(-) rename contracts/{PublishInterfaces.sol => SupportsInterface.sol} (82%) diff --git a/contracts/AccessControl.sol b/contracts/AccessControl.sol index 21e4325..3f8bfda 100644 --- a/contracts/AccessControl.sol +++ b/contracts/AccessControl.sol @@ -1,8 +1,8 @@ -pragma solidity ^0.4.21; +pragma solidity ^0.4.24; /// @title Reusable three-role access control inspired by CryptoKitties /// @author William Entriken (https://phor.net) -/// @dev Keep the CEO wallet stored offline, I warned you +/// @dev Keep the CEO wallet stored offline, I warned you. contract AccessControl { /// @notice The account that can only reassign executive accounts address public executiveOfficerAddress; @@ -13,7 +13,7 @@ contract AccessControl { /// @notice The account with administrative control of this contract address public operatingOfficerAddress; - function AccessControl() internal { + constructor() internal { executiveOfficerAddress = msg.sender; } diff --git a/contracts/ERC165.sol b/contracts/ERC165.sol index 2064f70..d028f3e 100644 --- a/contracts/ERC165.sol +++ b/contracts/ERC165.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.21; +pragma solidity ^0.4.24; /// @title ERC-165 Standard Interface Detection /// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md diff --git a/contracts/ERC721.sol b/contracts/ERC721.sol index 59f964d..69ba1c7 100644 --- a/contracts/ERC721.sol +++ b/contracts/ERC721.sol @@ -1,12 +1,12 @@ -pragma solidity ^0.4.21; +pragma solidity ^0.4.24; import "./ERC165.sol"; /// @title ERC-721 Non-Fungible Token Standard -/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md -contract ERC721 is ERC165 { - event Transfer(address indexed _from, address indexed _to, uint256 _tokenId); - event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId); +/// @dev Reference https://eips.ethereum.org/EIPS/eip-721 +interface ERC721 /* is ERC165 */ { + event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId); + event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId); event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved); function balanceOf(address _owner) external view returns (uint256); function ownerOf(uint256 _tokenId) external view returns (address); @@ -21,7 +21,7 @@ contract ERC721 is ERC165 { /// @title ERC-721 Non-Fungible Token Standard interface ERC721TokenReceiver { - function onERC721Received(address _from, uint256 _tokenId, bytes data) external returns(bytes4); + function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes _data) external returns(bytes4); } /// @title ERC-721 Non-Fungible Token Standard, optional metadata extension diff --git a/contracts/SuMain.sol b/contracts/SuMain.sol index 20f0ade..07556b3 100644 --- a/contracts/SuMain.sol +++ b/contracts/SuMain.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.21; +pragma solidity ^0.4.24; import "./AccessControl.sol"; import "./SuNFT.sol"; @@ -10,7 +10,6 @@ import "./SuVending.sol"; /// @author William Entriken (https://phor.net) /// @dev See SuMain contract documentation for detail on how contracts interact. contract SuMain is AccessControl, SuNFT, SuOperation, SuVending, SuPromo { - function SuMain() public { + constructor() public { } } - diff --git a/contracts/SuNFT.sol b/contracts/SuNFT.sol index b940b8f..88f1745 100644 --- a/contracts/SuNFT.sol +++ b/contracts/SuNFT.sol @@ -1,7 +1,7 @@ -pragma solidity ^0.4.21; +pragma solidity ^0.4.24; import "./ERC721.sol"; -import "./PublishInterfaces.sol"; +import "./SupportsInterface.sol"; /// @title Compliance with ERC-721 for Su Squares /// @dev This implementation assumes: @@ -10,7 +10,7 @@ import "./PublishInterfaces.sol"; /// - NFTs are initially assigned to this contract /// - This contract does not externally call its own functions /// @author William Entriken (https://phor.net) -contract SuNFT is ERC165, ERC721, ERC721Metadata, ERC721Enumerable, PublishInterfaces { +contract SuNFT is ERC165, ERC721, ERC721Metadata, ERC721Enumerable, SupportsInterface { /// @dev The authorized address for each NFT mapping (uint256 => address) internal tokenApprovals; @@ -59,13 +59,13 @@ contract SuNFT is ERC165, ERC721, ERC721Metadata, ERC721Enumerable, PublishInter /// (`to` == 0). Exception: during contract creation, any number of NFTs /// may be created and assigned without emitting Transfer. At the time of /// any transfer, the approved address for that NFT (if any) is reset to none. - event Transfer(address indexed _from, address indexed _to, uint256 _tokenId); + event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId); /// @dev This emits when the approved address for an NFT is changed or /// reaffirmed. The zero address indicates there is no approved address. /// When a Transfer event emits, this also indicates that the approved /// address for that NFT (if any) is reset to none. - event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId); + event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId); /// @dev This emits when an operator is enabled or disabled for an owner. /// The operator can manage all NFTs of the owner. @@ -106,7 +106,7 @@ contract SuNFT is ERC165, ERC721, ERC721Metadata, ERC721Enumerable, PublishInter /// `_tokenId` is not a valid NFT. When transfer is complete, this function /// checks if `_to` is a smart contract (code size > 0). If so, it calls /// `onERC721Received` on `_to` and throws if the return value is not - /// `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`. + /// `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`. /// @param _from The current owner of the NFT /// @param _to The new owner /// @param _tokenId The NFT to transfer @@ -118,7 +118,7 @@ contract SuNFT is ERC165, ERC721, ERC721Metadata, ERC721Enumerable, PublishInter /// @notice Transfers the ownership of an NFT from one address to another address /// @dev This works identically to the other function with an extra data parameter, - /// except this function just sets data to "" + /// except this function just sets data to "". /// @param _from The current owner of the NFT /// @param _to The new owner /// @param _tokenId The NFT to transfer @@ -153,9 +153,9 @@ contract SuNFT is ERC165, ERC721, ERC721Metadata, ERC721Enumerable, PublishInter _transfer(_tokenId, _to); } - /// @notice Set or reaffirm the approved address for an NFT + /// @notice Change or reaffirm the approved address for an NFT /// @dev The zero address indicates there is no approved address. - /// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized + /// Throws unless `msg.sender` is the current NFT owner, or an authorized /// operator of the current owner. /// @param _approved The new approved NFT controller /// @param _tokenId The NFT to approve @@ -174,18 +174,19 @@ contract SuNFT is ERC165, ERC721, ERC721Metadata, ERC721Enumerable, PublishInter emit Approval(_owner, _approved, _tokenId); } - /// @notice Enable or disable approval for a third party ("operator") to manage - /// all your asset. - /// @dev Emits the ApprovalForAll event - /// @param _operator Address to add to the set of authorized operators. - /// @param _approved True if the operators is approved, false to revoke approval + /// @notice Enable or disable approval for a third party ("operator") to + /// manage all of `msg.sender`'s assets + /// @dev Emits the ApprovalForAll event. The contract MUST allow + /// multiple operators per owner. + /// @param _operator Address to add to the set of authorized operators + /// @param _approved True if operator is approved, false to revoke approval function setApprovalForAll(address _operator, bool _approved) external { operatorApprovals[msg.sender][_operator] = _approved; emit ApprovalForAll(msg.sender, _operator, _approved); } /// @notice Get the approved address for a single NFT - /// @dev Throws if `_tokenId` is not a valid NFT + /// @dev Throws if `_tokenId` is not a valid NFT. /// @param _tokenId The NFT to find the approved address for /// @return The approved address for this NFT, or the zero address if there is none function getApproved(uint256 _tokenId) @@ -240,8 +241,8 @@ contract SuNFT is ERC165, ERC721, ERC721Metadata, ERC721Enumerable, PublishInter // COMPLIANCE WITH ERC721Enumerable //////////////////////////////////////// /// @notice Count NFTs tracked by this contract - /// @return A count of valid NFTs tracked by this contract, where each one of - /// them has an assigned and queryable owner not equal to the zero address + /// @return A count of valid NFTs tracked by this contract, where each one + /// has an assigned and queryable owner not equal to the zero address function totalSupply() external view returns (uint256) { return TOTAL_SUPPLY; } @@ -326,9 +327,10 @@ contract SuNFT is ERC165, ERC721, ERC721Metadata, ERC721Enumerable, PublishInter // PRIVATE STORAGE AND FUNCTIONS /////////////////////////////////////////// - uint256 private constant TOTAL_SUPPLY = 10000; // SOLIDITY ISSUE #3356 make this immutable + // See Solidity issue #3356, it would be clearer to initialize in SuMain + uint256 private constant TOTAL_SUPPLY = 10000; - bytes4 private constant ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,uint256,bytes)")); + bytes4 private constant ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)")); /// @dev The owner of each NFT /// If value == address(0), NFT is owned by address(this) @@ -359,7 +361,7 @@ contract SuNFT is ERC165, ERC721, ERC721Metadata, ERC721Enumerable, PublishInter // address[] private nftIds; // mapping (uint256 => uint256) private nftIndexOfId; - function SuNFT() internal { + constructor() internal { // Publish interfaces with ERC-165 supportedInterfaces[0x80ac58cd] = true; // ERC721 supportedInterfaces[0x5b5e139f] = true; // ERC721Metadata @@ -405,7 +407,7 @@ contract SuNFT is ERC165, ERC721, ERC721Metadata, ERC721Enumerable, PublishInter if (codeSize == 0) { return; } - bytes4 retval = ERC721TokenReceiver(_to).onERC721Received(_from, _tokenId, data); + bytes4 retval = ERC721TokenReceiver(_to).onERC721Received(msg.sender, _from, _tokenId, data); require(retval == ERC721_RECEIVED); } } diff --git a/contracts/SuOperation.sol b/contracts/SuOperation.sol index 783a8fc..a57e966 100644 --- a/contracts/SuOperation.sol +++ b/contracts/SuOperation.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.21; +pragma solidity ^0.4.24; import "./SuNFT.sol"; @@ -9,7 +9,7 @@ contract SuOperation is SuNFT { /// @dev The personalization of a square has changed event Personalized(uint256 _nftId); - /// @dev The main SuSquare struct. The owner may set these properties, subject + /// @dev The main SuSquare struct. The owner may set these properties, /// subject to certain rules. The actual 10x10 image is rendered on our /// website using this data. struct SuSquare { diff --git a/contracts/SuPromo.sol b/contracts/SuPromo.sol index 2b09abe..be4fa7d 100644 --- a/contracts/SuPromo.sol +++ b/contracts/SuPromo.sol @@ -1,9 +1,9 @@ -pragma solidity ^0.4.21; +pragma solidity ^0.4.24; import "./SuNFT.sol"; import "./AccessControl.sol"; -/// @title A limited pre-sale and promotional giveaway. +/// @title A limited pre-sale and promotional giveaway /// @author William Entriken (https://phor.net) /// @dev See SuMain contract documentation for detail on how contracts interact. contract SuPromo is AccessControl, SuNFT { diff --git a/contracts/SuVending.sol b/contracts/SuVending.sol index 9cf4e3a..9f6cfb8 100644 --- a/contracts/SuVending.sol +++ b/contracts/SuVending.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.21; +pragma solidity ^0.4.24; import "./AccessControl.sol"; import "./SuNFT.sol"; diff --git a/contracts/PublishInterfaces.sol b/contracts/SupportsInterface.sol similarity index 82% rename from contracts/PublishInterfaces.sol rename to contracts/SupportsInterface.sol index 40188fa..8a4d0b6 100644 --- a/contracts/PublishInterfaces.sol +++ b/contracts/SupportsInterface.sol @@ -1,14 +1,14 @@ -pragma solidity ^0.4.21; +pragma solidity ^0.4.24; import "./ERC165.sol"; /// @title A reusable contract to comply with ERC-165 /// @author William Entriken (https://phor.net) -contract PublishInterfaces is ERC165 { - /// @dev Every interface that we support +contract SupportsInterface is ERC165 { + /// @dev Every interface that we support, do not set 0xffffffff to true mapping(bytes4 => bool) internal supportedInterfaces; - function PublishInterfaces() internal { + constructor() internal { supportedInterfaces[0x01ffc9a7] = true; // ERC165 }