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 }