Skip to content

Commit

Permalink
Merge branch 'master' of github.com:doublejumptokyo/mchplus-contracts
Browse files Browse the repository at this point in the history
  • Loading branch information
rmanzoku committed Nov 29, 2019
2 parents 47a1629 + 0928b8d commit 7b864b2
Show file tree
Hide file tree
Showing 10 changed files with 439 additions and 105 deletions.
23 changes: 23 additions & 0 deletions contracts/erc/ERC165.sol
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;
}
}
134 changes: 134 additions & 0 deletions contracts/erc/ERC721.sol
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);
}
}
}
44 changes: 44 additions & 0 deletions contracts/erc/ERC721Mintable.sol
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);
}
}
19 changes: 19 additions & 0 deletions contracts/interfaces/IERC721.sol
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);
}
18 changes: 18 additions & 0 deletions contracts/interfaces/IERC721Metadata.sol
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);
}
18 changes: 18 additions & 0 deletions contracts/interfaces/IERC721TokenReceiver.sol
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);
}
4 changes: 4 additions & 0 deletions contracts/libraries/Address.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ library Address {
return size > 0;
}

function toPayable(address account) internal pure returns (address payable) {
return address(uint160(account));
}

function toHex(address account) internal pure returns (string memory) {
bytes32 value = bytes32(uint256(account));
bytes memory alphabet = "0123456789abcdef";
Expand Down
69 changes: 69 additions & 0 deletions contracts/roles/Operatable.sol
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);
}

}
Loading

0 comments on commit 7b864b2

Please sign in to comment.