-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' of github.com:doublejumptokyo/mchplus-contracts
- Loading branch information
Showing
10 changed files
with
439 additions
and
105 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
pragma solidity ^0.5.0; | ||
|
||
interface IERC165 { | ||
function supportsInterface(bytes4 interfaceID) external view returns (bool); | ||
} | ||
|
||
contract ERC165 is IERC165 { | ||
bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7; | ||
mapping(bytes4 => bool) private _supportedInterfaces; | ||
|
||
constructor () internal { | ||
_registerInterface(_INTERFACE_ID_ERC165); | ||
} | ||
|
||
function supportsInterface(bytes4 interfaceId) external view returns (bool) { | ||
return _supportedInterfaces[interfaceId]; | ||
} | ||
|
||
function _registerInterface(bytes4 interfaceId) internal { | ||
require(interfaceId != 0xffffffff, "ERC165: invalid interface id"); | ||
_supportedInterfaces[interfaceId] = true; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
pragma solidity ^0.5.0; | ||
|
||
import "mchplus-contracts/libraries/Uint256.sol"; | ||
import "mchplus-contracts/libraries/Address.sol"; | ||
|
||
import "mchplus-contracts/interfaces/IERC721.sol"; | ||
import "mchplus-contracts/interfaces/IERC721TokenReceiver.sol"; | ||
import "mchplus-contracts/erc/ERC165.sol"; | ||
|
||
contract ERC721 is IERC721, ERC165 { | ||
using Uint256 for uint256; | ||
using Address for address; | ||
|
||
bytes4 private constant _ERC721_RECEIVED = 0x150b7a02; | ||
bytes4 private constant _InterfaceId_ERC721 = 0x80ac58cd; | ||
|
||
mapping (uint256 => address) private _tokenOwner; | ||
mapping (address => uint256) private _balance; | ||
mapping (uint256 => address) private _tokenApproved; | ||
mapping (address => mapping (address => bool)) private _operatorApprovals; | ||
|
||
constructor () public { | ||
_registerInterface(_InterfaceId_ERC721); | ||
} | ||
|
||
function balanceOf(address _owner) public view returns (uint256) { | ||
return _balance[_owner]; | ||
} | ||
|
||
function ownerOf(uint256 _tokenId) public view returns (address) { | ||
require(_exist(_tokenId), | ||
"`_tokenId` is not a valid NFT."); | ||
return _tokenOwner[_tokenId]; | ||
} | ||
|
||
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes memory _data) public payable { | ||
require(_data.length == 0, "data is not implemented"); | ||
safeTransferFrom(_from, _to, _tokenId); | ||
} | ||
|
||
function safeTransferFrom(address _from, address _to, uint256 _tokenId) public payable { | ||
require(_checkOnERC721Received(_from, _to, _tokenId, ""), | ||
"`_to` is a smart contract and onERC721Received is invalid"); | ||
transferFrom(_from, _to, _tokenId); | ||
} | ||
|
||
function transferFrom(address _from, address _to, uint256 _tokenId) public payable { | ||
require(_transferable(_from, _tokenId), | ||
"Unless `msg.sender` is the current owner, an authorized operator, or the approved address for this NFT."); | ||
require(ownerOf(_tokenId) == _from, | ||
"`_from` is not the current owner."); | ||
require(_to != address(0), | ||
"`_to` is the zero address."); | ||
require(_exist(_tokenId), | ||
"`_tokenId` is not a valid NFT."); | ||
_transfer(_from, _to, _tokenId); | ||
} | ||
|
||
function approve(address _approved, uint256 _tokenId) public payable { | ||
address owner = ownerOf(_tokenId); | ||
require(msg.sender == owner || isApprovedForAll(owner, msg.sender), | ||
"Unless `msg.sender` is the current NFT owner, or an authorized operator of the current owner."); | ||
|
||
_tokenApproved[_tokenId] = _approved; | ||
emit Approval(msg.sender, _approved, _tokenId); | ||
} | ||
|
||
function setApprovalForAll(address _operator, bool _approved) public { | ||
_setApprovalForAll(msg.sender, _operator, _approved); | ||
} | ||
|
||
function _setApprovalForAll(address _owner, address _operator, bool _approved) internal { | ||
_operatorApprovals[_owner][_operator] = _approved; | ||
emit ApprovalForAll(_owner, _operator, _approved); | ||
} | ||
|
||
function getApproved(uint256 _tokenId) public view returns (address) { | ||
require(_exist(_tokenId), | ||
"`_tokenId` is not a valid NFT."); | ||
return _tokenApproved[_tokenId]; | ||
} | ||
|
||
function isApprovedForAll(address _owner, address _operator) public view returns (bool) { | ||
return _operatorApprovals[_owner][_operator]; | ||
} | ||
|
||
function _transferable(address _spender, uint256 _tokenId) internal view returns (bool){ | ||
address owner = ownerOf(_tokenId); | ||
return (_spender == owner || getApproved(_tokenId) == _spender || isApprovedForAll(owner, _spender)); | ||
} | ||
|
||
function _transfer(address _from, address _to, uint256 _tokenId) internal { | ||
_clearApproval(_tokenId); | ||
_tokenOwner[_tokenId] = _to; | ||
_balance[_from] = _balance[_from].sub(1); | ||
_balance[_to] = _balance[_to].add(1); | ||
emit Transfer(_from, _to, _tokenId); | ||
} | ||
|
||
function _mint(address _to, uint256 _tokenId) internal { | ||
require(!_exist(_tokenId), "mint token already exists"); | ||
_tokenOwner[_tokenId] = _to; | ||
_balance[_to] = _balance[_to].add(1); | ||
emit Transfer(address(0), _to, _tokenId); | ||
} | ||
|
||
function _burn(uint256 _tokenId) internal { | ||
require(_exist(_tokenId), "burn token does not already exists"); | ||
address owner = ownerOf(_tokenId); | ||
_clearApproval(_tokenId); | ||
_tokenOwner[_tokenId] = address(0); | ||
_balance[owner] = _balance[owner].sub(1); | ||
emit Transfer(owner, address(0), _tokenId); | ||
} | ||
|
||
function _exist(uint256 _tokenId) internal view returns (bool) { | ||
address owner = _tokenOwner[_tokenId]; | ||
return owner != address(0); | ||
} | ||
|
||
function _checkOnERC721Received(address _from, address _to, uint256 _tokenId, bytes memory _data) internal returns (bool) { | ||
if (!_to.isContract()) { | ||
return true; | ||
} | ||
bytes4 retval = IERC721TokenReceiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data); | ||
return (retval == _ERC721_RECEIVED); | ||
} | ||
|
||
function _clearApproval(uint256 tokenId) internal { | ||
if (_tokenApproved[tokenId] != address(0)) { | ||
_tokenApproved[tokenId] = address(0); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
pragma solidity ^0.5.0; | ||
|
||
import "mchplus-contracts/erc/ERC721.sol"; | ||
import "mchplus-contracts/roles/Operatable.sol"; | ||
|
||
interface IERC721Mintable { | ||
event MinterAdded(address indexed account); | ||
event MinterRemoved(address indexed account); | ||
function mint(address _to, uint256 _tokenId) external; | ||
function addMinter(address account) external; | ||
function removeMinter(address account) external; | ||
} | ||
|
||
contract ERC721Mintable is ERC721, IERC721Mintable, Operatable { | ||
using Roles for Roles.Role; | ||
Roles.Role private minters; | ||
|
||
constructor() public { | ||
addMinter(msg.sender); | ||
} | ||
|
||
modifier onlyMinter() { | ||
require(isMinter(msg.sender), "Must be minter"); | ||
_; | ||
} | ||
|
||
function isMinter(address account) public view returns (bool) { | ||
return minters.has(account); | ||
} | ||
|
||
function addMinter(address account) public onlyOperator() { | ||
minters.add(account); | ||
emit MinterAdded(account); | ||
} | ||
|
||
function removeMinter(address account) public onlyOperator() { | ||
minters.remove(account); | ||
emit MinterRemoved(account); | ||
} | ||
|
||
function mint(address to, uint256 tokenId) public { | ||
_mint(to, tokenId); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
pragma solidity ^0.5.0; | ||
|
||
/// @title ERC-721 Non-Fungible Token Standard | ||
/// @dev See https://eips.ethereum.org/EIPS/eip-721 | ||
/// Note: the ERC-165 identifier for this interface is 0x80ac58cd. | ||
interface IERC721 /* 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); | ||
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata data) external payable; | ||
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable; | ||
function transferFrom(address _from, address _to, uint256 _tokenId) external payable; | ||
function approve(address _approved, uint256 _tokenId) external payable; | ||
function setApprovalForAll(address _operator, bool _approved) external; | ||
function getApproved(uint256 _tokenId) external view returns (address); | ||
function isApprovedForAll(address _owner, address _operator) external view returns (bool); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
pragma solidity ^0.5.0; | ||
|
||
/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension | ||
/// @dev See https://eips.ethereum.org/EIPS/eip-721 | ||
/// Note: the ERC-165 identifier for this interface is 0x5b5e139f. | ||
interface IERC721Metadata /* is ERC721 */ { | ||
/// @notice A descriptive name for a collection of NFTs in this contract | ||
function name() external view returns (string memory _name); | ||
|
||
/// @notice An abbreviated name for NFTs in this contract | ||
function symbol() external view returns (string memory _symbol); | ||
|
||
/// @notice A distinct Uniform Resource Identifier (URI) for a given asset. | ||
/// @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC | ||
/// 3986. The URI may point to a JSON file that conforms to the "ERC721 | ||
/// Metadata JSON Schema". | ||
function tokenURI(uint256 _tokenId) external view returns (string memory); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
pragma solidity ^0.5.0; | ||
|
||
/// @dev Note: the ERC-165 identifier for this interface is 0x150b7a02. | ||
interface IERC721TokenReceiver { | ||
/// @notice Handle the receipt of an NFT | ||
/// @dev The ERC721 smart contract calls this function on the recipient | ||
/// after a `transfer`. This function MAY throw to revert and reject the | ||
/// transfer. Return of other than the magic value MUST result in the | ||
/// transaction being reverted. | ||
/// Note: the contract address is always the message sender. | ||
/// @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 transferred | ||
/// @param _data Additional data with no specified format | ||
/// @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` | ||
/// unless throwing | ||
function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes calldata _data) external returns(bytes4); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
pragma solidity ^0.5.0; | ||
|
||
import "mchplus-contracts/roles/Roles.sol"; | ||
|
||
contract Operatable { | ||
using Roles for Roles.Role; | ||
|
||
event OperatorAdded(address indexed account); | ||
event OperatorRemoved(address indexed account); | ||
|
||
event Paused(address account); | ||
event Unpaused(address account); | ||
|
||
bool private _paused; | ||
Roles.Role private operators; | ||
|
||
constructor() public { | ||
operators.add(msg.sender); | ||
_paused = false; | ||
} | ||
|
||
modifier onlyOperator() { | ||
require(isOperator(msg.sender), "Must be operator"); | ||
_; | ||
} | ||
|
||
modifier whenNotPaused() { | ||
require(!_paused, "Pausable: paused"); | ||
_; | ||
} | ||
|
||
modifier whenPaused() { | ||
require(_paused, "Pausable: not paused"); | ||
_; | ||
} | ||
|
||
function isOperator(address account) public view returns (bool) { | ||
return operators.has(account); | ||
} | ||
|
||
function addOperator(address account) public onlyOperator() { | ||
operators.add(account); | ||
emit OperatorAdded(account); | ||
} | ||
|
||
function removeOperator(address account) public onlyOperator() { | ||
operators.remove(account); | ||
emit OperatorRemoved(account); | ||
} | ||
|
||
function paused() public view returns (bool) { | ||
return _paused; | ||
} | ||
|
||
function pause() public onlyOperator() whenNotPaused() { | ||
_paused = true; | ||
emit Paused(msg.sender); | ||
} | ||
|
||
function unpause() public onlyOperator() whenPaused() { | ||
_paused = false; | ||
emit Unpaused(msg.sender); | ||
} | ||
|
||
function withdrawEther() public onlyOperator() { | ||
msg.sender.transfer(address(this).balance); | ||
} | ||
|
||
} |
Oops, something went wrong.