eth-erc721

ERC-721 'NFT' token interface with example developer badge token contract
Info | Log | Files | Refs | LICENSE

BadgeToken.sol (8689B)


      1 pragma solidity >=0.8.0;
      2 
      3 // SPDX-License-Identifier: AGPL-3.0-or-later
      4 
      5 contract BadgeToken {
      6 	// EIP 173
      7 	address public owner;
      8 
      9 	uint256[] token; // token item registry
     10 	uint256[] tokenMintedAt; // block height mapping to token array
     11 	
     12 	mapping(uint256 => uint256) tokenIndex; // tokenId to token array index
     13 	mapping(uint256 => address) tokenOwner; // tokenId to owner address
     14 	mapping(address => uint256[]) tokenOwnerIndex; // index of owned tokens by owner address
     15 	mapping(uint256 => uint256) tokenOwnerIdIndex; // index of owned token ids in tokenOwnerIndex
     16 	mapping(address => uint256) tokenOwnerCount; // end of token owner index array
     17 
     18 	mapping(uint256 => address) tokenAllowance; // backend for approve
     19 	mapping(address => address) tokenOperator; // backend for setApprovalForAll
     20 
     21 	mapping(uint256 => bytes32[]) tokenData; // store optional data submitted with safeTransferFrom
     22 
     23 	// Implements ERC721Metadata
     24 	string public name;
     25 
     26 	// Implements ERC721Metadata
     27 	string public symbol;
     28 
     29 	// Implements ERC5007
     30 	int64 constant public endTime = 9223372036854775807; // max int64
     31 
     32 	// Implements ERC721
     33 	event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
     34 	// Implements ERC721
     35 	event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
     36 	// Implements ERC721
     37 	event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
     38 
     39 	// Implements ERC173
     40 	event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
     41 
     42 	event TransferWithData(address indexed _from, address indexed _to, uint256 indexed _tokenId, bytes32 _data);
     43 
     44 	// Implements Minter
     45 	event Mint(address indexed _minter, address indexed _beneficiary, uint256 value);
     46 
     47 	constructor(string memory _name, string memory _symbol) {
     48 		owner = msg.sender;
     49 		name = _name;
     50 		symbol = _symbol;
     51 	}
     52 	
     53 	function withdraw(uint256 _amount) public returns(bool) {
     54 		require(msg.sender == owner, 'ERR_ACCESS');
     55 		payable(msg.sender).transfer(_amount);
     56 		return true;
     57 	}
     58 
     59 	// Implements ERC721
     60 	function balanceOf(address _owner) external view returns (uint256) {
     61 		return tokenOwnerCount[_owner];
     62 	}
     63 
     64 	// Implements ERC721
     65 	function ownerOf(uint256 _tokenId) external view returns (address) {
     66 		return tokenOwner[_tokenId];
     67 	}
     68 
     69 	// shared function for transfer methods
     70 	function transferCore(address _from, address _to, uint256 _tokenId, bytes memory _data) internal {
     71 		address currentTokenOwner;
     72 
     73 		currentTokenOwner = tokenOwner[_tokenId];
     74 		require(tokenOwner[_tokenId] == _from);
     75 		if (_from != msg.sender) {
     76 			require(tokenAllowance[_tokenId] == msg.sender || tokenOperator[currentTokenOwner] == msg.sender);
     77 		}
     78 		
     79 		tokenAllowance[_tokenId] = address(0);
     80 
     81 		tokenOwnerIndex[_from][tokenOwnerIdIndex[_tokenId]] = tokenOwnerIndex[_from][tokenOwnerIndex[_from].length-1];
     82 		tokenOwnerCount[_from]--;
     83 
     84 		tokenOwnerIndex[_to].push(_tokenId);
     85 		tokenOwnerCount[_to]++;
     86 
     87 		tokenOwner[_tokenId] = _to;
     88 
     89 		for (uint256 i = 0; i < _data.length; i++) {
     90 			tokenData[_tokenId][i % 32] = _data[i];
     91 		}
     92 	}
     93 
     94 	// Implements ERC721
     95 	function transferFrom(address _from, address _to, uint256 _tokenId) external payable {
     96 		bytes memory _data;
     97 
     98 		_data = new bytes(0);
     99 		transferCore(_from, _to, _tokenId, _data);
    100 		emit Transfer(_from, _to, _tokenId);
    101 	}
    102 
    103 	// Implements ERC721
    104 	function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes memory _data) external payable {
    105 		transferCore(_from, _to, _tokenId, _data);
    106 		emit Transfer(_from, _to, _tokenId);
    107 		emit TransferWithData(_from, _to, _tokenId, tokenData[_tokenId][tokenData[_tokenId].length-1]);
    108 	}
    109 
    110 	// Implements ERC721
    111 	function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable {
    112 		bytes memory _data;
    113 
    114 		_data = new bytes(0);
    115 		transferCore(_from, _to, _tokenId, _data);
    116 		emit Transfer(_from, _to, _tokenId);
    117 	}
    118 
    119 	// Implements ERC721
    120 	function approve(address _approved, uint256 _tokenId) external payable {
    121 		require(tokenOwner[_tokenId] == msg.sender);
    122 
    123 		tokenAllowance[_tokenId] = _approved;
    124 
    125 		emit Approval(msg.sender, _approved, _tokenId);
    126 	}
    127 
    128 	// Implements ERC721
    129 	function setApprovalForAll(address _operator, bool _approved) external {
    130 		if (_approved) {
    131 			require(tokenOperator[msg.sender] == address(0)); // save a few bucks in gas if fail
    132 			tokenOperator[msg.sender] = _operator;
    133 		} else {
    134 			require(tokenOperator[msg.sender] != address(0));
    135 			tokenOperator[msg.sender] = address(0);
    136 		}
    137 		emit ApprovalForAll(msg.sender, _operator, _approved);
    138 	}
    139 
    140 	// Implements ERC721
    141 	function getApproved(uint256 _tokenId) external view returns (address) {
    142 		return tokenAllowance[_tokenId];
    143 	}
    144 
    145 	// Implements ERC721
    146 	function isApprovedForAll(address _owner, address _operator) external view returns (bool) {
    147 		return tokenOperator[_owner] == _operator;
    148 	}
    149 
    150 	// Implements ERC721Enumerable
    151 	function totalSupply() external view returns (uint256) {
    152 		return token.length;
    153 	}
    154 
    155 	// Implements ERC721Enumerable
    156 	function tokenByIndex(uint256 _index) external view returns (uint256) {
    157 		return token[_index];
    158 	}
    159 
    160 	// Implements ERC721Enumerable
    161 	function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256) {
    162 		require(_index < tokenOwnerCount[_owner]);
    163 
    164 		return tokenOwnerIndex[_owner][_index];
    165 	}
    166 
    167 	// TODO: Implement Locator
    168 	// Create sha256 uri from data
    169 	function toURI(bytes32 _data) public pure returns(string memory) {
    170 		bytes memory out;
    171 		uint8 t;
    172 		uint256 c;
    173 
    174 		out = new bytes(64 + 7);
    175 		out[0] = "s";
    176 		out[1] = "h";
    177 		out[2] = "a";
    178 		out[3] = "2";
    179 		out[4] = "5";
    180 		out[5] = "6";
    181 		out[6] = ":";
    182 		
    183 		c = 7;	
    184 		for (uint256 i = 0; i < 32; i++) {
    185 			t = (uint8(_data[i]) & 0xf0) >> 4;
    186 			if (t < 10) {
    187 				out[c] = bytes1(t + 0x30);
    188 			} else {
    189 				out[c] = bytes1(t + 0x57);
    190 			}
    191 			t = uint8(_data[i]) & 0x0f;
    192 			if (t < 10) {
    193 				out[c+1] = bytes1(t + 0x30);
    194 			} else {
    195 				out[c+1] = bytes1(t + 0x57);
    196 			}
    197 			c += 2;
    198 		}
    199 		return string(out);
    200 	}
    201 
    202 	// Implements ERC721Metadata
    203 	function tokenURI(uint256 _tokenId) public view returns (string memory) {
    204 		return toURI(bytes32(token[tokenIndex[_tokenId]]));
    205 	}
    206 
    207 	// Implements Minter
    208 	function mintTo(address _beneficiary, uint256 _tokenId) public returns (bool) {
    209 		require(owner == msg.sender);
    210 		require(tokenIndex[_tokenId] == 0x0 || token.length == 0);
    211 
    212 		uint256 newTokenIndex;
    213 		uint256 newTokenId;
    214 
    215 		newTokenIndex = token.length;
    216 		newTokenId = uint256(_tokenId);
    217 
    218 		token.push(newTokenId);
    219 		tokenIndex[newTokenId] = newTokenIndex;
    220 		tokenMintedAt.push(block.number);
    221 		tokenOwner[newTokenId] = _beneficiary;
    222 		tokenOwnerIdIndex[tokenOwnerIndex[_beneficiary].length] = _tokenId;
    223 	       	tokenOwnerIndex[_beneficiary].push(_tokenId);	
    224 		tokenOwnerCount[_beneficiary]++;
    225 
    226 		emit Mint(msg.sender, _beneficiary, _tokenId);
    227 
    228 		return true;
    229 	}
    230 
    231 	// Implements Minter
    232 	function mint(address _beneficiary, uint256 _tokenId, bytes calldata _data) public {
    233 		_data;
    234 		mintTo(_beneficiary, _tokenId);
    235 	}
    236 
    237 	// Implements Minter
    238 	function safeMint(address _beneficiary, uint256 _tokenId, bytes calldata _data) public {
    239 		_data;
    240 		mintTo(_beneficiary, _tokenId);
    241 	}
    242 
    243 	// Implements Chrono
    244 	function createTime(uint256 _idx) public view returns (int64) {
    245 		uint256 _tokenIndex;
    246 
    247 		_tokenIndex = tokenIndex[_idx];
    248 
    249 		return int64(uint64(tokenMintedAt[_tokenIndex]));
    250 	}
    251 
    252 	// Implements ERC5007
    253 	function startTime(uint256 _tokenId) public view returns (int64) {
    254 		return createTime(_tokenId);
    255 	}
    256 
    257 	// Implements ERC173
    258 	function transferOwnership(address _newOwner) external returns (bool) {
    259 		require(msg.sender == owner);
    260 
    261 		bytes memory zeroData;
    262 		address previousOwner;
    263 		uint256[] storage currentTokenOwnerIndex;
    264 
    265 		previousOwner = owner;
    266 		currentTokenOwnerIndex = tokenOwnerIndex[previousOwner];
    267 
    268 		// TODO: Dangerous, may run out of gas
    269 		zeroData = new bytes(0);
    270 		for (uint256 i = 0; i < currentTokenOwnerIndex.length; i++) {
    271 			transferCore(previousOwner, _newOwner, currentTokenOwnerIndex[i], zeroData);
    272 		}
    273 
    274 		owner = _newOwner;
    275 
    276 		emit OwnershipTransferred(previousOwner, _newOwner);
    277 		return true;
    278 	}
    279 
    280 
    281 	// Implements ERC165
    282 	function supportsInterface(bytes4 interfaceID) external pure returns (bool) {
    283 		if (interfaceID == 0xc22876c3) { // ERC721
    284 			return true;
    285 		}
    286 		if (interfaceID == 0xd283ef1d) { // ERC721Metadata 
    287 			return true;
    288 		}
    289 		if (interfaceID == 0xdd9d2087) { // ERC721Enumerable
    290 			return true;
    291 		}
    292 		if (interfaceID == 0x5878bcf4) { // Minter
    293 			return true;
    294 		}
    295 		if (interfaceID == 0x01ffc9a7) { // ERC165
    296 			return true;
    297 		}
    298 		if (interfaceID == 0x9493f8b2) { // ERC173
    299 			return true;
    300 		}
    301 		if (interfaceID == 0x7a0cdf92) { // ERC5007
    302 			return true;
    303 		}
    304 		return false;
    305 	}
    306 }