Skip to content

Commit 7b864b2

Browse files
committed
Merge branch 'master' of github.com:doublejumptokyo/mchplus-contracts
2 parents 47a1629 + 0928b8d commit 7b864b2

File tree

10 files changed

+439
-105
lines changed

10 files changed

+439
-105
lines changed

contracts/erc/ERC165.sol

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
pragma solidity ^0.5.0;
2+
3+
interface IERC165 {
4+
function supportsInterface(bytes4 interfaceID) external view returns (bool);
5+
}
6+
7+
contract ERC165 is IERC165 {
8+
bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
9+
mapping(bytes4 => bool) private _supportedInterfaces;
10+
11+
constructor () internal {
12+
_registerInterface(_INTERFACE_ID_ERC165);
13+
}
14+
15+
function supportsInterface(bytes4 interfaceId) external view returns (bool) {
16+
return _supportedInterfaces[interfaceId];
17+
}
18+
19+
function _registerInterface(bytes4 interfaceId) internal {
20+
require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
21+
_supportedInterfaces[interfaceId] = true;
22+
}
23+
}

contracts/erc/ERC721.sol

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
pragma solidity ^0.5.0;
2+
3+
import "mchplus-contracts/libraries/Uint256.sol";
4+
import "mchplus-contracts/libraries/Address.sol";
5+
6+
import "mchplus-contracts/interfaces/IERC721.sol";
7+
import "mchplus-contracts/interfaces/IERC721TokenReceiver.sol";
8+
import "mchplus-contracts/erc/ERC165.sol";
9+
10+
contract ERC721 is IERC721, ERC165 {
11+
using Uint256 for uint256;
12+
using Address for address;
13+
14+
bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
15+
bytes4 private constant _InterfaceId_ERC721 = 0x80ac58cd;
16+
17+
mapping (uint256 => address) private _tokenOwner;
18+
mapping (address => uint256) private _balance;
19+
mapping (uint256 => address) private _tokenApproved;
20+
mapping (address => mapping (address => bool)) private _operatorApprovals;
21+
22+
constructor () public {
23+
_registerInterface(_InterfaceId_ERC721);
24+
}
25+
26+
function balanceOf(address _owner) public view returns (uint256) {
27+
return _balance[_owner];
28+
}
29+
30+
function ownerOf(uint256 _tokenId) public view returns (address) {
31+
require(_exist(_tokenId),
32+
"`_tokenId` is not a valid NFT.");
33+
return _tokenOwner[_tokenId];
34+
}
35+
36+
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes memory _data) public payable {
37+
require(_data.length == 0, "data is not implemented");
38+
safeTransferFrom(_from, _to, _tokenId);
39+
}
40+
41+
function safeTransferFrom(address _from, address _to, uint256 _tokenId) public payable {
42+
require(_checkOnERC721Received(_from, _to, _tokenId, ""),
43+
"`_to` is a smart contract and onERC721Received is invalid");
44+
transferFrom(_from, _to, _tokenId);
45+
}
46+
47+
function transferFrom(address _from, address _to, uint256 _tokenId) public payable {
48+
require(_transferable(_from, _tokenId),
49+
"Unless `msg.sender` is the current owner, an authorized operator, or the approved address for this NFT.");
50+
require(ownerOf(_tokenId) == _from,
51+
"`_from` is not the current owner.");
52+
require(_to != address(0),
53+
"`_to` is the zero address.");
54+
require(_exist(_tokenId),
55+
"`_tokenId` is not a valid NFT.");
56+
_transfer(_from, _to, _tokenId);
57+
}
58+
59+
function approve(address _approved, uint256 _tokenId) public payable {
60+
address owner = ownerOf(_tokenId);
61+
require(msg.sender == owner || isApprovedForAll(owner, msg.sender),
62+
"Unless `msg.sender` is the current NFT owner, or an authorized operator of the current owner.");
63+
64+
_tokenApproved[_tokenId] = _approved;
65+
emit Approval(msg.sender, _approved, _tokenId);
66+
}
67+
68+
function setApprovalForAll(address _operator, bool _approved) public {
69+
_setApprovalForAll(msg.sender, _operator, _approved);
70+
}
71+
72+
function _setApprovalForAll(address _owner, address _operator, bool _approved) internal {
73+
_operatorApprovals[_owner][_operator] = _approved;
74+
emit ApprovalForAll(_owner, _operator, _approved);
75+
}
76+
77+
function getApproved(uint256 _tokenId) public view returns (address) {
78+
require(_exist(_tokenId),
79+
"`_tokenId` is not a valid NFT.");
80+
return _tokenApproved[_tokenId];
81+
}
82+
83+
function isApprovedForAll(address _owner, address _operator) public view returns (bool) {
84+
return _operatorApprovals[_owner][_operator];
85+
}
86+
87+
function _transferable(address _spender, uint256 _tokenId) internal view returns (bool){
88+
address owner = ownerOf(_tokenId);
89+
return (_spender == owner || getApproved(_tokenId) == _spender || isApprovedForAll(owner, _spender));
90+
}
91+
92+
function _transfer(address _from, address _to, uint256 _tokenId) internal {
93+
_clearApproval(_tokenId);
94+
_tokenOwner[_tokenId] = _to;
95+
_balance[_from] = _balance[_from].sub(1);
96+
_balance[_to] = _balance[_to].add(1);
97+
emit Transfer(_from, _to, _tokenId);
98+
}
99+
100+
function _mint(address _to, uint256 _tokenId) internal {
101+
require(!_exist(_tokenId), "mint token already exists");
102+
_tokenOwner[_tokenId] = _to;
103+
_balance[_to] = _balance[_to].add(1);
104+
emit Transfer(address(0), _to, _tokenId);
105+
}
106+
107+
function _burn(uint256 _tokenId) internal {
108+
require(_exist(_tokenId), "burn token does not already exists");
109+
address owner = ownerOf(_tokenId);
110+
_clearApproval(_tokenId);
111+
_tokenOwner[_tokenId] = address(0);
112+
_balance[owner] = _balance[owner].sub(1);
113+
emit Transfer(owner, address(0), _tokenId);
114+
}
115+
116+
function _exist(uint256 _tokenId) internal view returns (bool) {
117+
address owner = _tokenOwner[_tokenId];
118+
return owner != address(0);
119+
}
120+
121+
function _checkOnERC721Received(address _from, address _to, uint256 _tokenId, bytes memory _data) internal returns (bool) {
122+
if (!_to.isContract()) {
123+
return true;
124+
}
125+
bytes4 retval = IERC721TokenReceiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data);
126+
return (retval == _ERC721_RECEIVED);
127+
}
128+
129+
function _clearApproval(uint256 tokenId) internal {
130+
if (_tokenApproved[tokenId] != address(0)) {
131+
_tokenApproved[tokenId] = address(0);
132+
}
133+
}
134+
}

contracts/erc/ERC721Mintable.sol

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
pragma solidity ^0.5.0;
2+
3+
import "mchplus-contracts/erc/ERC721.sol";
4+
import "mchplus-contracts/roles/Operatable.sol";
5+
6+
interface IERC721Mintable {
7+
event MinterAdded(address indexed account);
8+
event MinterRemoved(address indexed account);
9+
function mint(address _to, uint256 _tokenId) external;
10+
function addMinter(address account) external;
11+
function removeMinter(address account) external;
12+
}
13+
14+
contract ERC721Mintable is ERC721, IERC721Mintable, Operatable {
15+
using Roles for Roles.Role;
16+
Roles.Role private minters;
17+
18+
constructor() public {
19+
addMinter(msg.sender);
20+
}
21+
22+
modifier onlyMinter() {
23+
require(isMinter(msg.sender), "Must be minter");
24+
_;
25+
}
26+
27+
function isMinter(address account) public view returns (bool) {
28+
return minters.has(account);
29+
}
30+
31+
function addMinter(address account) public onlyOperator() {
32+
minters.add(account);
33+
emit MinterAdded(account);
34+
}
35+
36+
function removeMinter(address account) public onlyOperator() {
37+
minters.remove(account);
38+
emit MinterRemoved(account);
39+
}
40+
41+
function mint(address to, uint256 tokenId) public {
42+
_mint(to, tokenId);
43+
}
44+
}

contracts/interfaces/IERC721.sol

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
pragma solidity ^0.5.0;
2+
3+
/// @title ERC-721 Non-Fungible Token Standard
4+
/// @dev See https://eips.ethereum.org/EIPS/eip-721
5+
/// Note: the ERC-165 identifier for this interface is 0x80ac58cd.
6+
interface IERC721 /* is ERC165 */ {
7+
event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
8+
event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
9+
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
10+
function balanceOf(address _owner) external view returns (uint256);
11+
function ownerOf(uint256 _tokenId) external view returns (address);
12+
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata data) external payable;
13+
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
14+
function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
15+
function approve(address _approved, uint256 _tokenId) external payable;
16+
function setApprovalForAll(address _operator, bool _approved) external;
17+
function getApproved(uint256 _tokenId) external view returns (address);
18+
function isApprovedForAll(address _owner, address _operator) external view returns (bool);
19+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
pragma solidity ^0.5.0;
2+
3+
/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
4+
/// @dev See https://eips.ethereum.org/EIPS/eip-721
5+
/// Note: the ERC-165 identifier for this interface is 0x5b5e139f.
6+
interface IERC721Metadata /* is ERC721 */ {
7+
/// @notice A descriptive name for a collection of NFTs in this contract
8+
function name() external view returns (string memory _name);
9+
10+
/// @notice An abbreviated name for NFTs in this contract
11+
function symbol() external view returns (string memory _symbol);
12+
13+
/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
14+
/// @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
15+
/// 3986. The URI may point to a JSON file that conforms to the "ERC721
16+
/// Metadata JSON Schema".
17+
function tokenURI(uint256 _tokenId) external view returns (string memory);
18+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
pragma solidity ^0.5.0;
2+
3+
/// @dev Note: the ERC-165 identifier for this interface is 0x150b7a02.
4+
interface IERC721TokenReceiver {
5+
/// @notice Handle the receipt of an NFT
6+
/// @dev The ERC721 smart contract calls this function on the recipient
7+
/// after a `transfer`. This function MAY throw to revert and reject the
8+
/// transfer. Return of other than the magic value MUST result in the
9+
/// transaction being reverted.
10+
/// Note: the contract address is always the message sender.
11+
/// @param _operator The address which called `safeTransferFrom` function
12+
/// @param _from The address which previously owned the token
13+
/// @param _tokenId The NFT identifier which is being transferred
14+
/// @param _data Additional data with no specified format
15+
/// @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
16+
/// unless throwing
17+
function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes calldata _data) external returns(bytes4);
18+
}

contracts/libraries/Address.sol

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ library Address {
88
return size > 0;
99
}
1010

11+
function toPayable(address account) internal pure returns (address payable) {
12+
return address(uint160(account));
13+
}
14+
1115
function toHex(address account) internal pure returns (string memory) {
1216
bytes32 value = bytes32(uint256(account));
1317
bytes memory alphabet = "0123456789abcdef";

contracts/roles/Operatable.sol

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
pragma solidity ^0.5.0;
2+
3+
import "mchplus-contracts/roles/Roles.sol";
4+
5+
contract Operatable {
6+
using Roles for Roles.Role;
7+
8+
event OperatorAdded(address indexed account);
9+
event OperatorRemoved(address indexed account);
10+
11+
event Paused(address account);
12+
event Unpaused(address account);
13+
14+
bool private _paused;
15+
Roles.Role private operators;
16+
17+
constructor() public {
18+
operators.add(msg.sender);
19+
_paused = false;
20+
}
21+
22+
modifier onlyOperator() {
23+
require(isOperator(msg.sender), "Must be operator");
24+
_;
25+
}
26+
27+
modifier whenNotPaused() {
28+
require(!_paused, "Pausable: paused");
29+
_;
30+
}
31+
32+
modifier whenPaused() {
33+
require(_paused, "Pausable: not paused");
34+
_;
35+
}
36+
37+
function isOperator(address account) public view returns (bool) {
38+
return operators.has(account);
39+
}
40+
41+
function addOperator(address account) public onlyOperator() {
42+
operators.add(account);
43+
emit OperatorAdded(account);
44+
}
45+
46+
function removeOperator(address account) public onlyOperator() {
47+
operators.remove(account);
48+
emit OperatorRemoved(account);
49+
}
50+
51+
function paused() public view returns (bool) {
52+
return _paused;
53+
}
54+
55+
function pause() public onlyOperator() whenNotPaused() {
56+
_paused = true;
57+
emit Paused(msg.sender);
58+
}
59+
60+
function unpause() public onlyOperator() whenPaused() {
61+
_paused = false;
62+
emit Unpaused(msg.sender);
63+
}
64+
65+
function withdrawEther() public onlyOperator() {
66+
msg.sender.transfer(address(this).balance);
67+
}
68+
69+
}

0 commit comments

Comments
 (0)