├── LICENSE ├── contracts ├── V2 │ └── Certification.sol └── V1 │ └── StandardNFT.sol └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 lexDAO 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /contracts/V2/Certification.sol: -------------------------------------------------------------------------------- 1 | /// SPDX-License-Identifier: MIT 2 | /// Presented by LexDAO LLC 3 | /// @notice Minimal Certification NFT. 4 | pragma solidity 0.8.4; 5 | 6 | contract Certification { 7 | address public governance; 8 | uint256 public totalSupply; 9 | string public baseURI; 10 | string public details; 11 | string public name; 12 | string public symbol; 13 | 14 | mapping(address => uint256) public balanceOf; 15 | mapping(uint256 => address) public ownerOf; 16 | mapping(uint256 => string) public tokenURI; 17 | mapping(bytes4 => bool) public supportsInterface; // ERC-165 18 | 19 | event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); 20 | event GovTokenURI(uint256 indexed tokenId, string tokenURI); 21 | event TransferGovernance(address indexed governance); 22 | event UpdateBaseURI(string baseURI); 23 | 24 | constructor( 25 | address _governance, 26 | string memory _baseURI, 27 | string memory _details, 28 | string memory _name, 29 | string memory _symbol 30 | ) { 31 | governance = _governance; 32 | baseURI = _baseURI; 33 | details = _details; 34 | name = _name; 35 | symbol = _symbol; 36 | supportsInterface[0x80ac58cd] = true; // ERC-721 37 | supportsInterface[0x5b5e139f] = true; // METADATA 38 | } 39 | 40 | modifier onlyGovernance { 41 | require(msg.sender == governance, '!governance'); 42 | _; 43 | } 44 | 45 | function burn(address from, uint256 tokenId) external { 46 | require(from == ownerOf[tokenId] || from == governance, '!owner||!governance'); 47 | balanceOf[from]--; 48 | ownerOf[tokenId] = address(0); 49 | tokenURI[tokenId] = ""; 50 | emit Transfer(from, address(0), tokenId); 51 | } 52 | 53 | function mint(address to, string calldata customURI) external onlyGovernance { 54 | string memory _tokenURI; 55 | bytes(customURI).length > 0 ? _tokenURI = customURI : _tokenURI = baseURI; 56 | totalSupply++; 57 | uint256 tokenId = totalSupply; 58 | balanceOf[to]++; 59 | ownerOf[tokenId] = to; 60 | tokenURI[tokenId] = _tokenURI; 61 | emit Transfer(address(0), to, tokenId); 62 | } 63 | 64 | function govTokenURI(uint256 tokenId, string calldata _tokenURI) external onlyGovernance { 65 | require(tokenId <= totalSupply, '!exist'); 66 | tokenURI[tokenId] = _tokenURI; 67 | emit GovTokenURI(tokenId, _tokenURI); 68 | } 69 | 70 | function govTransferFrom(address from, address to, uint256 tokenId) external onlyGovernance { 71 | require(from == ownerOf[tokenId], 'from!=owner'); 72 | balanceOf[from]--; 73 | balanceOf[to]++; 74 | ownerOf[tokenId] = to; 75 | emit Transfer(from, to, tokenId); 76 | } 77 | 78 | function transferGovernance(address _governance) external onlyGovernance { 79 | governance = _governance; 80 | emit TransferGovernance(_governance); 81 | } 82 | 83 | function updateBaseURI(string calldata _baseURI) external onlyGovernance { 84 | baseURI = _baseURI; 85 | emit UpdateBaseURI(_baseURI); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Legal Engineers 2 | 3 | ## What is Legal Engineering? 4 | 5 | There are many definitions (see [background](https://www.legalbusinessworld.com/post/2020/02/17/an-essay-on-legal-engineering-from-confusion-to-clarity)) but for LexDAO purposes: 6 | 7 | `the design of computational law applications` 8 | 9 | Legal Engineering Certification by [LexDAO vote](https://gnosis-safe.io/app/#/safes/0x5B620676E28693fC14876b035b08CbB1B657dF38/transactions) of your peers requires demonstrated: 10 | - contributions in the field of computational law (developing H-shaped skills in code+law); 11 | - aptitude in web3 applications and design (including understanding wet-ink contracts); 12 | - support of LexDAO initiatives to further the same (eg mentoring, thought leadership). 13 | 14 | What does this mean in practice? Check out some projects being built by LexDAO engineers: 15 | - [KALI DAO Incorporator](https://app.kalidao.xyz/) 16 | - [Real Property Tokenization](https://lexdao.substack.com/p/when-daos-get-real-managing-real?s=r) 17 | - [Open Source Law](https://github.com/ErichDylus/Open-Source-Law) 18 | - [AI powered crypto wallet assistant](https://nani.ooo/) 19 | 20 | ## Is there a path to certification? 21 | 22 | Joining the LexDAO squabbles in discord, contributing to research and working group discussions, publishing articles, and similar, are great ways to demonstrate qualifications. A more linear path based on skill-trees would involve: 23 | 1. 🧙 writing and deploying a smart contract with legal applications or high regulatory compliance, 24 | 2. 🏹 designing a legal framework for people using smart contract instruments/mechanisms, and 25 | 3. 🏴‍☠️ working on a LexDAO engineering project (see above) via hackathons or sprints as l'expert. 26 | 27 | ## Why apply for LexDAO certification? 28 | 29 | LexDAO Engineers (see roster below) have established reputation and work history among law firms and web3 protocols. Joining our ranks lets the public know you support our mission and have been vetted. You can append "| LexDAO" as your discord handle, add "LexDAO Engineer" in your twitter or law firm bio, or unlock additional powers with affiliated partners. Being a LexDAO engineer also makes you eligible to serve in more advanced legal engineering roles within our network, like working on digital arbitration panels or being on editorial board of our journals. Plus you'd be getting a NFTy dynamic badge once we get our collective trustmark registered. 30 | 31 | ## Eligibility to join LexDAO Legal Engineering 32 | 33 | We have a dual path where apprentices who have established legal training (J.D., paralegal experience, etc.) gains practice in coding value, dispute and other legal engineering protocols --or-- skillup [developers](https://www.lexology.com/library/detail.aspx?g=f8d9bb92-3779-4bc2-9f1b-7354d416acb1) or project managers in cyberlaw, RegTech and compliance. We point out that Legal Enginner **CANNOT** give legal advice (unless admitted to the bar) but would have a working knowledge at least at the paralegal level. 34 | 35 | Join our [discord chat](https://discord.com/invite/M4jxXmk) and check out our `Member` channel to learn more (ask for guest pass in `introductions` if you can't see that channel). 36 | 37 | ```mermaid 38 | journey 39 | title Journey to becoming L'expert in Legal Engineering 40 | section Apprentice 41 | Discord guest: 3: Me 42 | Can't afford membership: 1: Me 43 | Work as l'extern: 4: Me, Mentor 44 | DeWork task: 2: Me, Squab 45 | Hackathon voted MVP: 7: Squab 46 | section Journey(wo)man e'LEETH 47 | Micro-Practice: 1: Me, Cat 48 | Side-Project: 3: Me 49 | Join Raid: 5: Squab 50 | Thought leader: 7: Me 51 | Startup fails: 2: Cat 52 | Side project Acquire: 6: Me, Squab 53 | section L'expert 54 | Emeritus LEETH: 9: Me 55 | ``` 56 | 57 | ## LexDAO Legal Engineers 🕶️ 1Q2024 58 | 59 | | Name | Ethereum Address | Notes | 60 | |----------|:-------------:|:-----:| 61 | | Ross Campbell | [`0x1C0Aa8cCD568d90d61659F060D1bFb1e6f855A20`](https://etherscan.io/address/0x1c0aa8ccd568d90d61659f060d1bfb1e6f855a20) | $${\color{white}Lvl 5 Cryptowizard}$$ | 62 | | Robert Leonhard | [`0xcC4Dc8e92A6E30b6F5F6E65156b121D9f83Ca18F`](https://etherscan.io/address/0xcc4dc8e92a6e30b6f5f6e65156b121d9f83ca18f) |$${\color{white}Lvl 4 MetaMagician}$$ | 63 | | Joshua Ma | [`0xf290f3d843826d00f8176182fd76550535f6dbb4`](https://etherscan.io/address/0xf290f3d843826d00f8176182fd76550535f6dbb4) | 64 | | Alex Fagella | [`0x756CE9C56B02f05b5FaFfaFc707B552bEDce83eE`](https://etherscan.io/address/0x756ce9c56b02f05b5faffafc707b552bedce83ee) | $${\color{white}Lvl 3 Sourceror}$$ | 65 | | James McCall | [`0xd5B3988eD0AB5ec375E51bB6fd10e205cEC16A2E`](https://etherscan.io/address/0xd5B3988eD0AB5ec375E51bB6fd10e205cEC16A2E) | $${\color{white}Lvl 3 Serjeant}$$ | 66 | | Scott Stevenson | [`0x130093A5aEbc07e78e16f0EcEF09d1c45AfD8178`](https://etherscan.io/address/0x130093A5aEbc07e78e16f0EcEF09d1c45AfD8178) | $${\color{white}Lvl 4- Ranger}$$ | 67 | | Adam Kerpelman | [`0xa3564677FC4907A15c9A7EAe1DBc1ae9aC57b8E1`](https://etherscan.io/address/0xa3564677FC4907A15c9A7EAe1DBc1ae9aC57b8E1) | $${\color{white}Lvl 3 Serjeant}$$ | 68 | | Stamford Hwang | [`0x4744cda32bE7b3e75b9334001da9ED21789d4c0d`](https://etherscan.io/address/0x4744cda32bE7b3e75b9334001da9ED21789d4c0d) | $${\color{white}Lvl 4- MetaMagician}$$ | 69 | | Bill Warren | [`0x7136fbDdD4DFfa2369A9283B6E90A040318011Ca`](https://etherscan.io/address/0x7136fbDdD4DFfa2369A9283B6E90A040318011Ca) | $${\color{white}Lvl 4- Evoker}$$ | 70 | | Erich Dylus | [`0xb7f49e02552751b249cae86959fd50d887708b1d`](https://etherscan.io/address/0xb7f49e02552751b249cae86959fd50d887708b1d) | $${\color{white}Lvl 4- Metamagician}$$ | 71 | | Nick Rishwain | [`0xCbbd18d3aC27ab0FFfD04BCCd091B2802c92e0ca`](https://etherscan.io/address/0xCbbd18d3aC27ab0FFfD04BCCd091B2802c92e0ca) | $${\color{white}Lvl 3 Herald}$$ | 72 | | Shivanshi Tyagi | [`0xcb0592589602b841be035e1e64c2a5b1ef006aa2`](https://etherscan.io/address/0xcb0592589602b841be035e1e64c2a5b1ef006aa2) | $${\color{white}Lvl 3 Summoner}$$ | 73 | | Lawrence Lau | [`0x85384c23d8ae724a094b5592c53ad4015bc94574`](https://etherscan.io/address/0x85384c23d8ae724a094b5592c53ad4015bc94574) | $${\color{white}Lvl 4+ DAOsigner}$$ | 74 | | Jordan Teague | [`0xE3bbFD7dbd338a2C1c4F28F8e06aC00589118c4B`](https://etherscan.io/address/0xE3bbFD7dbd338a2C1c4F28F8e06aC00589118c4B) | $${\color{white}Lvl 3 Enchantress}$$ | 75 | | Larry Florio | [`0xc04aFCeEA0BE5A8CeCe930c4e6208eb03BeCd066`](https://etherscan.io/address/0xc04aFCeEA0BE5A8CeCe930c4e6208eb03BeCd066) | $${\color{white}Lvl 4- Sniper}$$ | 76 | | Geoff Costeloe | [`0x82fab1819986492A0799f65B4DdfB6B570F881E0`](https://etherscan.io/address/0x82fab1819986492A0799f65B4DdfB6B570F881E0) | $${\color{white}Lvl 3 Slinger}$$ | 77 | | Gaurav Gopinath | [`0xdf01f0c6152253f45d492176c9f69ba5058b6b13`](https://etherscan.io/address/0xdf01f0c6152253f45d492176c9f69ba5058b6b13) | 78 | | Jared Cohen | [`0xb42Ce7ce6eE1Dd2B7959DD6c120eFAE8Ec288E03`](https://etherscan.io/address/0xb42Ce7ce6eE1Dd2B7959DD6c120eFAE8Ec288E03) | $${\color{white}Lvl 3 Sourceror}$$ | 79 | | Kyle Smith | [`0x0D89421D6eec0A4385F95f410732186A2Ab45077`](https://etherscan.io/address/0x0D89421D6eec0A4385F95f410732186A2Ab45077) | $${\color{white}Lvl 3 Slinger}$$ | 80 | | Arvind Alexander | [`0x48A64aDbb34837F3Bf4d40BCc40C85bBCa1A3a84`](https://etherscan.io/address/0x48A64aDbb34837F3Bf4d40BCc40C85bBCa1A3a84) | 81 | | Matt Blaine | [`0xECa5e3CE0774372e71cE9b3675CB07c6677C2d52`](https://etherscan.io/address/0xECa5e3CE0774372e71cE9b3675CB07c6677C2d52) | 82 | | Mark Dittrich | [`0x509Bed05fA643cc4023620652b5c03d15d7E911a`](https://etherscan.io/address/0x509Bed05fA643cc4023620652b5c03d15d7E911a) | 83 | | Gianluca Cattin | [`0xC316f415096223561e2077c30A26043499d579aD`](https://etherscan.io/address/0xC316f415096223561e2077c30A26043499d579aD) | 84 | | Nick Viavant | [`0xCC976F37b22B67660D78D9BA9157ABC2131c6F5d`](https://etherscan.io/address/0xCC976F37b22B67660D78D9BA9157ABC2131c6F5d) | $${\color{white}Lvl 3 Mage}$$ | 85 | | ??? | [`0xbAEE49c72026bC7C4Baa6b446E288535Ec22F020`](https://etherscan.io/address/0xbAEE49c72026bC7C4Baa6b446E288535Ec22F020) | 86 | | ??? | [`0x82ab35094Fa962646a0D1baAB2866F7350B98D79`](https://etherscan.io/address/0x82ab35094Fa962646a0D1baAB2866F7350B98D79) | 87 | 88 | [Legal Engineer NFT Registry](https://opensea.io/collection/lexdao-engineering) but not for sale.✨ 89 | -------------------------------------------------------------------------------- /contracts/V1/StandardNFT.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.26; 2 | 3 | /** 4 | * @title SafeMath 5 | * @dev Unsigned math operations with safety checks that revert on error 6 | */ 7 | library SafeMath { 8 | /** 9 | * @dev Multiplies two unsigned integers, reverts on overflow. 10 | */ 11 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 12 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 13 | // benefit is lost if 'b' is also tested. 14 | // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 15 | if (a == 0) { 16 | return 0; 17 | } 18 | 19 | uint256 c = a * b; 20 | require(c / a == b); 21 | 22 | return c; 23 | } 24 | 25 | /** 26 | * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero. 27 | */ 28 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 29 | // Solidity only automatically asserts when dividing by 0 30 | require(b > 0); 31 | uint256 c = a / b; 32 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 33 | 34 | return c; 35 | } 36 | 37 | /** 38 | * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend). 39 | */ 40 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 41 | require(b <= a); 42 | uint256 c = a - b; 43 | 44 | return c; 45 | } 46 | 47 | /** 48 | * @dev Adds two unsigned integers, reverts on overflow. 49 | */ 50 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 51 | uint256 c = a + b; 52 | require(c >= a); 53 | 54 | return c; 55 | } 56 | 57 | /** 58 | * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo), 59 | * reverts when dividing by zero. 60 | */ 61 | function mod(uint256 a, uint256 b) internal pure returns (uint256) { 62 | require(b != 0); 63 | return a % b; 64 | } 65 | } 66 | 67 | /** 68 | * Utility library of inline functions on addresses 69 | */ 70 | library Address { 71 | /** 72 | * Returns whether the target address is a contract 73 | * @dev This function will return false if invoked during the constructor of a contract, 74 | * as the code is not actually created until after the constructor finishes. 75 | * @param account address of the account to check 76 | * @return whether the target address is a contract 77 | */ 78 | function isContract(address account) internal view returns (bool) { 79 | uint256 size; 80 | // XXX Currently there is no better way to check if there is a contract in an address 81 | // than to check the size of the code at that address. 82 | // See https://ethereum.stackexchange.com/a/14016/36603 83 | // for more details about how this works. 84 | // TODO Check this again before the Serenity release, because all addresses will be 85 | // contracts then. 86 | // solhint-disable-next-line no-inline-assembly 87 | assembly { size := extcodesize(account) } 88 | return size > 0; 89 | } 90 | } 91 | 92 | /** 93 | * @title Roles 94 | * @dev Library for managing addresses assigned to a Role. 95 | */ 96 | library Roles { 97 | struct Role { 98 | mapping (address => bool) bearer; 99 | } 100 | 101 | /** 102 | * @dev give an account access to this role 103 | */ 104 | function add(Role storage role, address account) internal { 105 | require(account != address(0)); 106 | require(!has(role, account)); 107 | 108 | role.bearer[account] = true; 109 | } 110 | 111 | /** 112 | * @dev remove an account's access to this role 113 | */ 114 | function remove(Role storage role, address account) internal { 115 | require(account != address(0)); 116 | require(has(role, account)); 117 | 118 | role.bearer[account] = false; 119 | } 120 | 121 | /** 122 | * @dev check if an account has this role 123 | * @return bool 124 | */ 125 | function has(Role storage role, address account) internal view returns (bool) { 126 | require(account != address(0)); 127 | return role.bearer[account]; 128 | } 129 | } 130 | 131 | contract MinterRole { 132 | using Roles for Roles.Role; 133 | 134 | event MinterAdded(address indexed account); 135 | event MinterRemoved(address indexed account); 136 | 137 | Roles.Role private _minters; 138 | 139 | modifier onlyMinter() { 140 | require(isMinter(msg.sender)); 141 | _; 142 | } 143 | 144 | function isMinter(address account) public view returns (bool) { 145 | return _minters.has(account); 146 | } 147 | 148 | function addMinter(address account) public onlyMinter { 149 | _addMinter(account); 150 | } 151 | 152 | function renounceMinter() public { 153 | _removeMinter(msg.sender); 154 | } 155 | 156 | function _addMinter(address account) internal { 157 | _minters.add(account); 158 | emit MinterAdded(account); 159 | } 160 | 161 | function _removeMinter(address account) internal { 162 | _minters.remove(account); 163 | emit MinterRemoved(account); 164 | } 165 | } 166 | 167 | contract PauserRole { 168 | using Roles for Roles.Role; 169 | 170 | event PauserAdded(address indexed account); 171 | event PauserRemoved(address indexed account); 172 | 173 | Roles.Role private _pausers; 174 | 175 | modifier onlyPauser() { 176 | require(isPauser(msg.sender), "PauserRole: caller does not have the Pauser role"); 177 | _; 178 | } 179 | 180 | function isPauser(address account) public view returns (bool) { 181 | return _pausers.has(account); 182 | } 183 | 184 | function addPauser(address account) public onlyPauser { 185 | _addPauser(account); 186 | } 187 | 188 | function renouncePauser() public { 189 | _removePauser(msg.sender); 190 | } 191 | 192 | function _addPauser(address account) internal { 193 | _pausers.add(account); 194 | emit PauserAdded(account); 195 | } 196 | 197 | function _removePauser(address account) internal { 198 | _pausers.remove(account); 199 | emit PauserRemoved(account); 200 | } 201 | } 202 | 203 | /** 204 | * @dev Contract module which allows children to implement an emergency stop 205 | * mechanism that can be triggered by an authorized account. 206 | * 207 | * This module is used through inheritance. It will make available the 208 | * modifiers `whenNotPaused` and `whenPaused`, which can be applied to 209 | * the functions of your contract. Note that they will not be pausable by 210 | * simply including this module, only once the modifiers are put in place. 211 | */ 212 | contract Pausable is PauserRole { 213 | /** 214 | * @dev Emitted when the pause is triggered by a pauser (`account`). 215 | */ 216 | event Paused(address account); 217 | 218 | /** 219 | * @dev Emitted when the pause is lifted by a pauser (`account`). 220 | */ 221 | event Unpaused(address account); 222 | 223 | bool private _paused; 224 | 225 | /** 226 | * @dev Initializes the contract in unpaused state. Assigns the Pauser role 227 | * to the deployer. 228 | */ 229 | constructor () internal { 230 | _paused = true; 231 | } 232 | 233 | /** 234 | * @dev Returns true if the contract is paused, and false otherwise. 235 | */ 236 | function paused() public view returns (bool) { 237 | return _paused; 238 | } 239 | 240 | /** 241 | * @dev Modifier to make a function callable only when the contract is not paused. 242 | */ 243 | modifier whenNotPaused() { 244 | require(!_paused, "Pausable: paused"); 245 | _; 246 | } 247 | 248 | /** 249 | * @dev Modifier to make a function callable only when the contract is paused. 250 | */ 251 | modifier whenPaused() { 252 | require(_paused, "Pausable: not paused"); 253 | _; 254 | } 255 | 256 | /** 257 | * @dev Called by a pauser to pause, triggers stopped state. 258 | */ 259 | function pause() public onlyPauser whenNotPaused { 260 | _paused = true; 261 | emit Paused(msg.sender); 262 | } 263 | 264 | /** 265 | * @dev Called by a pauser to unpause, returns to normal state. 266 | */ 267 | function unpause() public onlyPauser whenPaused { 268 | _paused = false; 269 | emit Unpaused(msg.sender); 270 | } 271 | } 272 | 273 | /** 274 | * @title IERC165 275 | * @dev https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md 276 | */ 277 | interface IERC165 { 278 | /** 279 | * @notice Query if a contract implements an interface 280 | * @param interfaceId The interface identifier, as specified in ERC-165 281 | * @dev Interface identification is specified in ERC-165. This function 282 | * uses less than 30,000 gas. 283 | */ 284 | function supportsInterface(bytes4 interfaceId) external view returns (bool); 285 | } 286 | 287 | /** 288 | * @title ERC721 Non-Fungible Token Standard basic interface 289 | * @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md 290 | */ 291 | contract IERC721 is Pausable, IERC165 { 292 | event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); 293 | event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); 294 | event ApprovalForAll(address indexed owner, address indexed operator, bool approved); 295 | 296 | function balanceOf(address owner) public view returns (uint256 balance); 297 | function ownerOf(uint256 tokenId) public view returns (address owner); 298 | 299 | function approve(address to, uint256 tokenId) public; 300 | function getApproved(uint256 tokenId) public view returns (address operator); 301 | 302 | function setApprovalForAll(address operator, bool _approved) public; 303 | function isApprovedForAll(address owner, address operator) public view returns (bool); 304 | 305 | function transferFrom(address from, address to, uint256 tokenId) public whenNotPaused returns (bool); 306 | function safeTransferFrom(address from, address to, uint256 tokenId) public whenNotPaused returns (bool); 307 | 308 | function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public whenNotPaused returns (bool); 309 | } 310 | 311 | /** 312 | * @title ERC721 token receiver interface 313 | * @dev Interface for any contract that wants to support safeTransfers 314 | * from ERC721 asset contracts. 315 | */ 316 | contract IERC721Receiver { 317 | /** 318 | * @notice Handle the receipt of an NFT 319 | * @dev The ERC721 smart contract calls this function on the recipient 320 | * after a `safeTransfer`. This function MUST return the function selector, 321 | * otherwise the caller will revert the transaction. The selector to be 322 | * returned can be obtained as `this.onERC721Received.selector`. This 323 | * function MAY throw to revert and reject the transfer. 324 | * Note: the ERC721 contract address is always the message sender. 325 | * @param operator The address which called `safeTransferFrom` function 326 | * @param from The address which previously owned the token 327 | * @param tokenId The NFT identifier which is being transferred 328 | * @param data Additional data with no specified format 329 | * @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` 330 | */ 331 | function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data) 332 | public returns (bytes4); 333 | } 334 | 335 | /** 336 | * @title ERC165 337 | * @author Matt Condon (@shrugs) 338 | * @dev Implements ERC165 using a lookup table. 339 | */ 340 | contract ERC165 is IERC165 { 341 | bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7; 342 | /** 343 | * 0x01ffc9a7 === 344 | * bytes4(keccak256('supportsInterface(bytes4)')) 345 | */ 346 | 347 | /** 348 | * @dev a mapping of interface id to whether or not it's supported 349 | */ 350 | mapping(bytes4 => bool) private _supportedInterfaces; 351 | 352 | /** 353 | * @dev A contract implementing SupportsInterfaceWithLookup 354 | * implement ERC165 itself 355 | */ 356 | constructor () internal { 357 | _registerInterface(_INTERFACE_ID_ERC165); 358 | } 359 | 360 | /** 361 | * @dev implement supportsInterface(bytes4) using a lookup table 362 | */ 363 | function supportsInterface(bytes4 interfaceId) external view returns (bool) { 364 | return _supportedInterfaces[interfaceId]; 365 | } 366 | 367 | /** 368 | * @dev internal method for registering an interface 369 | */ 370 | function _registerInterface(bytes4 interfaceId) internal { 371 | require(interfaceId != 0xffffffff); 372 | _supportedInterfaces[interfaceId] = true; 373 | } 374 | } 375 | 376 | /** 377 | * @title ERC721 Non-Fungible Token Standard basic implementation 378 | * @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md 379 | */ 380 | contract ERC721 is ERC165, IERC721 { 381 | using SafeMath for uint256; 382 | using Address for address; 383 | 384 | // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` 385 | // which can be also obtained as `IERC721Receiver(0).onERC721Received.selector` 386 | bytes4 private constant _ERC721_RECEIVED = 0x150b7a02; 387 | 388 | // Mapping from token ID to owner 389 | mapping (uint256 => address) private _tokenOwner; 390 | 391 | // Mapping from token ID to approved address 392 | mapping (uint256 => address) private _tokenApprovals; 393 | 394 | // Mapping from owner to number of owned token 395 | mapping (address => uint256) private _ownedTokensCount; 396 | 397 | // Mapping from owner to operator approvals 398 | mapping (address => mapping (address => bool)) private _operatorApprovals; 399 | 400 | bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd; 401 | /* 402 | * 0x80ac58cd === 403 | * bytes4(keccak256('balanceOf(address)')) ^ 404 | * bytes4(keccak256('ownerOf(uint256)')) ^ 405 | * bytes4(keccak256('approve(address,uint256)')) ^ 406 | * bytes4(keccak256('getApproved(uint256)')) ^ 407 | * bytes4(keccak256('setApprovalForAll(address,bool)')) ^ 408 | * bytes4(keccak256('isApprovedForAll(address,address)')) ^ 409 | * bytes4(keccak256('transferFrom(address,address,uint256)')) ^ 410 | * bytes4(keccak256('safeTransferFrom(address,address,uint256)')) ^ 411 | * bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) 412 | */ 413 | 414 | constructor () public { 415 | // register the supported interfaces to conform to ERC721 via ERC165 416 | _registerInterface(_INTERFACE_ID_ERC721); 417 | } 418 | 419 | /** 420 | * @dev Gets the balance of the specified address 421 | * @param owner address to query the balance of 422 | * @return uint256 representing the amount owned by the passed address 423 | */ 424 | function balanceOf(address owner) public view returns (uint256) { 425 | require(owner != address(0)); 426 | return _ownedTokensCount[owner]; 427 | } 428 | 429 | /** 430 | * @dev Gets the owner of the specified token ID 431 | * @param tokenId uint256 ID of the token to query the owner of 432 | * @return owner address currently marked as the owner of the given token ID 433 | */ 434 | function ownerOf(uint256 tokenId) public view returns (address) { 435 | address owner = _tokenOwner[tokenId]; 436 | require(owner != address(0)); 437 | return owner; 438 | } 439 | 440 | /** 441 | * @dev Approves another address to transfer the given token ID 442 | * The zero address indicates there is no approved address. 443 | * There can only be one approved address per token at a given time. 444 | * Can only be called by the token owner or an approved operator. 445 | * @param to address to be approved for the given token ID 446 | * @param tokenId uint256 ID of the token to be approved 447 | */ 448 | function approve(address to, uint256 tokenId) public { 449 | address owner = ownerOf(tokenId); 450 | require(to != owner); 451 | require(msg.sender == owner || isApprovedForAll(owner, msg.sender)); 452 | 453 | _tokenApprovals[tokenId] = to; 454 | emit Approval(owner, to, tokenId); 455 | } 456 | 457 | /** 458 | * @dev Gets the approved address for a token ID, or zero if no address set 459 | * Reverts if the token ID does not exist. 460 | * @param tokenId uint256 ID of the token to query the approval of 461 | * @return address currently approved for the given token ID 462 | */ 463 | function getApproved(uint256 tokenId) public view returns (address) { 464 | require(_exists(tokenId)); 465 | return _tokenApprovals[tokenId]; 466 | } 467 | 468 | /** 469 | * @dev Sets or unsets the approval of a given operator 470 | * An operator is allowed to transfer all tokens of the sender on their behalf 471 | * @param to operator address to set the approval 472 | * @param approved representing the status of the approval to be set 473 | */ 474 | function setApprovalForAll(address to, bool approved) public { 475 | require(to != msg.sender); 476 | _operatorApprovals[msg.sender][to] = approved; 477 | emit ApprovalForAll(msg.sender, to, approved); 478 | } 479 | 480 | /** 481 | * @dev Tells whether an operator is approved by a given owner 482 | * @param owner owner address which you want to query the approval of 483 | * @param operator operator address which you want to query the approval of 484 | * @return bool whether the given operator is approved by the given owner 485 | */ 486 | function isApprovedForAll(address owner, address operator) public view returns (bool) { 487 | return _operatorApprovals[owner][operator]; 488 | } 489 | 490 | /** 491 | * @dev Transfers the ownership of a given token ID to another address 492 | * Usage of this method is discouraged, use `safeTransferFrom` whenever possible 493 | * Requires the msg sender to be the owner, approved, or operator 494 | * @param from current owner of the token 495 | * @param to address to receive the ownership of the given token ID 496 | * @param tokenId uint256 ID of the token to be transferred 497 | */ 498 | function transferFrom(address from, address to, uint256 tokenId) public whenNotPaused returns (bool) { 499 | require(_isApprovedOrOwner(msg.sender, tokenId)); 500 | 501 | _transferFrom(from, to, tokenId); 502 | } 503 | 504 | /** 505 | * @dev Safely transfers the ownership of a given token ID to another address 506 | * If the target address is a contract, it must implement `onERC721Received`, 507 | * which is called upon a safe transfer, and return the magic value 508 | * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise, 509 | * the transfer is reverted. 510 | * 511 | * Requires the msg sender to be the owner, approved, or operator 512 | * @param from current owner of the token 513 | * @param to address to receive the ownership of the given token ID 514 | * @param tokenId uint256 ID of the token to be transferred 515 | */ 516 | function safeTransferFrom(address from, address to, uint256 tokenId) public whenNotPaused returns (bool) { 517 | safeTransferFrom(from, to, tokenId, ""); 518 | } 519 | 520 | /** 521 | * @dev Safely transfers the ownership of a given token ID to another address 522 | * If the target address is a contract, it must implement `onERC721Received`, 523 | * which is called upon a safe transfer, and return the magic value 524 | * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise, 525 | * the transfer is reverted. 526 | * Requires the msg sender to be the owner, approved, or operator 527 | * @param from current owner of the token 528 | * @param to address to receive the ownership of the given token ID 529 | * @param tokenId uint256 ID of the token to be transferred 530 | * @param _data bytes data to send along with a safe transfer check 531 | */ 532 | function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public whenNotPaused returns (bool) { 533 | transferFrom(from, to, tokenId); 534 | require(_checkOnERC721Received(from, to, tokenId, _data)); 535 | } 536 | 537 | /** 538 | * @dev Returns whether the specified token exists 539 | * @param tokenId uint256 ID of the token to query the existence of 540 | * @return whether the token exists 541 | */ 542 | function _exists(uint256 tokenId) internal view returns (bool) { 543 | address owner = _tokenOwner[tokenId]; 544 | return owner != address(0); 545 | } 546 | 547 | /** 548 | * @dev Returns whether the given spender can transfer a given token ID 549 | * @param spender address of the spender to query 550 | * @param tokenId uint256 ID of the token to be transferred 551 | * @return bool whether the msg.sender is approved for the given token ID, 552 | * is an operator of the owner, or is the owner of the token 553 | */ 554 | function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) { 555 | address owner = ownerOf(tokenId); 556 | return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender)); 557 | } 558 | 559 | /** 560 | * @dev Internal function to mint a new token 561 | * Reverts if the given token ID already exists 562 | * @param to The address that will own the minted token 563 | * @param tokenId uint256 ID of the token to be minted 564 | */ 565 | function _mint(address to, uint256 tokenId) internal { 566 | require(to != address(0)); 567 | require(!_exists(tokenId)); 568 | 569 | _tokenOwner[tokenId] = to; 570 | _ownedTokensCount[to] = _ownedTokensCount[to].add(1); 571 | 572 | emit Transfer(address(0), to, tokenId); 573 | } 574 | 575 | /** 576 | * @dev Internal function to burn a specific token 577 | * Reverts if the token does not exist 578 | * Deprecated, use _burn(uint256) instead. 579 | * @param owner owner of the token to burn 580 | * @param tokenId uint256 ID of the token being burned 581 | */ 582 | function _burn(address owner, uint256 tokenId) internal { 583 | require(ownerOf(tokenId) == owner); 584 | 585 | _clearApproval(tokenId); 586 | 587 | _ownedTokensCount[owner] = _ownedTokensCount[owner].sub(1); 588 | _tokenOwner[tokenId] = address(0); 589 | 590 | emit Transfer(owner, address(0), tokenId); 591 | } 592 | 593 | /** 594 | * @dev Internal function to burn a specific token 595 | * Reverts if the token does not exist 596 | * @param tokenId uint256 ID of the token being burned 597 | */ 598 | function _burn(uint256 tokenId) internal { 599 | _burn(ownerOf(tokenId), tokenId); 600 | } 601 | 602 | /** 603 | * @dev Internal function to transfer ownership of a given token ID to another address. 604 | * As opposed to transferFrom, this imposes no restrictions on msg.sender. 605 | * @param from current owner of the token 606 | * @param to address to receive the ownership of the given token ID 607 | * @param tokenId uint256 ID of the token to be transferred 608 | */ 609 | function _transferFrom(address from, address to, uint256 tokenId) internal { 610 | require(ownerOf(tokenId) == from); 611 | require(to != address(0)); 612 | 613 | _clearApproval(tokenId); 614 | 615 | _ownedTokensCount[from] = _ownedTokensCount[from].sub(1); 616 | _ownedTokensCount[to] = _ownedTokensCount[to].add(1); 617 | 618 | _tokenOwner[tokenId] = to; 619 | 620 | emit Transfer(from, to, tokenId); 621 | } 622 | 623 | /** 624 | * @dev Internal function to invoke `onERC721Received` on a target address 625 | * The call is not executed if the target address is not a contract 626 | * @param from address representing the previous owner of the given token ID 627 | * @param to target address that will receive the tokens 628 | * @param tokenId uint256 ID of the token to be transferred 629 | * @param _data bytes optional data to send along with the call 630 | * @return whether the call correctly returned the expected magic value 631 | */ 632 | function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data) 633 | internal returns (bool) 634 | { 635 | if (!to.isContract()) { 636 | return true; 637 | } 638 | 639 | bytes4 retval = IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, _data); 640 | return (retval == _ERC721_RECEIVED); 641 | } 642 | 643 | /** 644 | * @dev Private function to clear current approval of a given token ID 645 | * @param tokenId uint256 ID of the token to be transferred 646 | */ 647 | function _clearApproval(uint256 tokenId) private { 648 | if (_tokenApprovals[tokenId] != address(0)) { 649 | _tokenApprovals[tokenId] = address(0); 650 | } 651 | } 652 | } 653 | 654 | /** 655 | * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension 656 | * @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md 657 | */ 658 | contract IERC721Enumerable is IERC721 { 659 | function totalSupply() public view returns (uint256); 660 | function tokenOfOwnerByIndex(address owner, uint256 index) public view returns (uint256 tokenId); 661 | 662 | function tokenByIndex(uint256 index) public view returns (uint256); 663 | } 664 | 665 | /** 666 | * @title ERC-721 Non-Fungible Token with optional enumeration extension logic 667 | * @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md 668 | */ 669 | contract ERC721Enumerable is ERC165, ERC721, IERC721Enumerable { 670 | // Mapping from owner to list of owned token IDs 671 | mapping(address => uint256[]) private _ownedTokens; 672 | 673 | // Mapping from token ID to index of the owner tokens list 674 | mapping(uint256 => uint256) private _ownedTokensIndex; 675 | 676 | // Array with all token ids, used for enumeration 677 | uint256[] private _allTokens; 678 | 679 | // Mapping from token id to position in the allTokens array 680 | mapping(uint256 => uint256) private _allTokensIndex; 681 | 682 | bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63; 683 | /** 684 | * 0x780e9d63 === 685 | * bytes4(keccak256('totalSupply()')) ^ 686 | * bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) ^ 687 | * bytes4(keccak256('tokenByIndex(uint256)')) 688 | */ 689 | 690 | /** 691 | * @dev Constructor function 692 | */ 693 | constructor () public { 694 | // register the supported interface to conform to ERC721 via ERC165 695 | _registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE); 696 | } 697 | 698 | /** 699 | * @dev Gets the token ID at a given index of the tokens list of the requested owner 700 | * @param owner address owning the tokens list to be accessed 701 | * @param index uint256 representing the index to be accessed of the requested tokens list 702 | * @return uint256 token ID at the given index of the tokens list owned by the requested address 703 | */ 704 | function tokenOfOwnerByIndex(address owner, uint256 index) public view returns (uint256) { 705 | require(index < balanceOf(owner)); 706 | return _ownedTokens[owner][index]; 707 | } 708 | 709 | /** 710 | * @dev Gets the total amount of tokens stored by the contract 711 | * @return uint256 representing the total amount of tokens 712 | */ 713 | function totalSupply() public view returns (uint256) { 714 | return _allTokens.length; 715 | } 716 | 717 | /** 718 | * @dev Gets the token ID at a given index of all the tokens in this contract 719 | * Reverts if the index is greater or equal to the total number of tokens 720 | * @param index uint256 representing the index to be accessed of the tokens list 721 | * @return uint256 token ID at the given index of the tokens list 722 | */ 723 | function tokenByIndex(uint256 index) public view returns (uint256) { 724 | require(index < totalSupply()); 725 | return _allTokens[index]; 726 | } 727 | 728 | /** 729 | * @dev Internal function to transfer ownership of a given token ID to another address. 730 | * As opposed to transferFrom, this imposes no restrictions on msg.sender. 731 | * @param from current owner of the token 732 | * @param to address to receive the ownership of the given token ID 733 | * @param tokenId uint256 ID of the token to be transferred 734 | */ 735 | function _transferFrom(address from, address to, uint256 tokenId) internal { 736 | super._transferFrom(from, to, tokenId); 737 | 738 | _removeTokenFromOwnerEnumeration(from, tokenId); 739 | 740 | _addTokenToOwnerEnumeration(to, tokenId); 741 | } 742 | 743 | /** 744 | * @dev Internal function to mint a new token 745 | * Reverts if the given token ID already exists 746 | * @param to address the beneficiary that will own the minted token 747 | * @param tokenId uint256 ID of the token to be minted 748 | */ 749 | function _mint(address to, uint256 tokenId) internal { 750 | super._mint(to, tokenId); 751 | 752 | _addTokenToOwnerEnumeration(to, tokenId); 753 | 754 | _addTokenToAllTokensEnumeration(tokenId); 755 | } 756 | 757 | /** 758 | * @dev Internal function to burn a specific token 759 | * Reverts if the token does not exist 760 | * Deprecated, use _burn(uint256) instead 761 | * @param owner owner of the token to burn 762 | * @param tokenId uint256 ID of the token being burned 763 | */ 764 | function _burn(address owner, uint256 tokenId) internal { 765 | super._burn(owner, tokenId); 766 | 767 | _removeTokenFromOwnerEnumeration(owner, tokenId); 768 | // Since tokenId will be deleted, we can clear its slot in _ownedTokensIndex to trigger a gas refund 769 | _ownedTokensIndex[tokenId] = 0; 770 | 771 | _removeTokenFromAllTokensEnumeration(tokenId); 772 | } 773 | 774 | /** 775 | * @dev Gets the list of token IDs of the requested owner 776 | * @param owner address owning the tokens 777 | * @return uint256[] List of token IDs owned by the requested address 778 | */ 779 | function _tokensOfOwner(address owner) internal view returns (uint256[] storage) { 780 | return _ownedTokens[owner]; 781 | } 782 | 783 | /** 784 | * @dev Private function to add a token to this extension's ownership-tracking data structures. 785 | * @param to address representing the new owner of the given token ID 786 | * @param tokenId uint256 ID of the token to be added to the tokens list of the given address 787 | */ 788 | function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private { 789 | _ownedTokensIndex[tokenId] = _ownedTokens[to].length; 790 | _ownedTokens[to].push(tokenId); 791 | } 792 | 793 | /** 794 | * @dev Private function to add a token to this extension's token tracking data structures. 795 | * @param tokenId uint256 ID of the token to be added to the tokens list 796 | */ 797 | function _addTokenToAllTokensEnumeration(uint256 tokenId) private { 798 | _allTokensIndex[tokenId] = _allTokens.length; 799 | _allTokens.push(tokenId); 800 | } 801 | 802 | /** 803 | * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that 804 | * while the token is not assigned a new owner, the _ownedTokensIndex mapping is _not_ updated: this allows for 805 | * gas optimizations e.g. when performing a transfer operation (avoiding double writes). 806 | * This has O(1) time complexity, but alters the order of the _ownedTokens array. 807 | * @param from address representing the previous owner of the given token ID 808 | * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address 809 | */ 810 | function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private { 811 | // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and 812 | // then delete the last slot (swap and pop). 813 | 814 | uint256 lastTokenIndex = _ownedTokens[from].length.sub(1); 815 | uint256 tokenIndex = _ownedTokensIndex[tokenId]; 816 | 817 | // When the token to delete is the last token, the swap operation is unnecessary 818 | if (tokenIndex != lastTokenIndex) { 819 | uint256 lastTokenId = _ownedTokens[from][lastTokenIndex]; 820 | 821 | _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token 822 | _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index 823 | } 824 | 825 | // This also deletes the contents at the last position of the array 826 | _ownedTokens[from].length--; 827 | 828 | // Note that _ownedTokensIndex[tokenId] hasn't been cleared: it still points to the old slot (now occcupied by 829 | // lasTokenId, or just over the end of the array if the token was the last one). 830 | } 831 | 832 | /** 833 | * @dev Private function to remove a token from this extension's token tracking data structures. 834 | * This has O(1) time complexity, but alters the order of the _allTokens array. 835 | * @param tokenId uint256 ID of the token to be removed from the tokens list 836 | */ 837 | function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private { 838 | // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and 839 | // then delete the last slot (swap and pop). 840 | 841 | uint256 lastTokenIndex = _allTokens.length.sub(1); 842 | uint256 tokenIndex = _allTokensIndex[tokenId]; 843 | 844 | // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so 845 | // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding 846 | // an 'if' statement (like in _removeTokenFromOwnerEnumeration) 847 | uint256 lastTokenId = _allTokens[lastTokenIndex]; 848 | 849 | _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token 850 | _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index 851 | 852 | // This also deletes the contents at the last position of the array 853 | _allTokens.length--; 854 | _allTokensIndex[tokenId] = 0; 855 | } 856 | } 857 | 858 | /** 859 | * @title ERC-721 Non-Fungible Token Standard, optional metadata extension 860 | * @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md 861 | */ 862 | contract IERC721Metadata is IERC721 { 863 | function name() external view returns (string memory); 864 | function symbol() external view returns (string memory); 865 | function tokenURI(uint256 tokenId) external view returns (string memory); 866 | } 867 | 868 | contract ERC721Metadata is ERC165, ERC721, IERC721Metadata { 869 | // Token name 870 | string private _name; 871 | 872 | // Token symbol 873 | string private _symbol; 874 | 875 | // Token owner 876 | address private _owner; 877 | 878 | // Optional mapping for token URIs 879 | mapping(uint256 => string) public _tokenURIs; 880 | 881 | bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f; 882 | /** 883 | * 0x5b5e139f === 884 | * bytes4(keccak256('name()')) ^ 885 | * bytes4(keccak256('symbol()')) ^ 886 | * bytes4(keccak256('tokenURI(uint256)')) 887 | */ 888 | 889 | /** 890 | * @dev Constructor function 891 | */ 892 | constructor (string memory name, string memory symbol) public { 893 | _name = name; 894 | _symbol = symbol; 895 | 896 | 897 | // register the supported interfaces to conform to ERC721 via ERC165 898 | _registerInterface(_INTERFACE_ID_ERC721_METADATA); 899 | } 900 | 901 | /** 902 | * @dev Gets the token name 903 | * @return string representing the token name 904 | */ 905 | function name() external view returns (string memory) { 906 | return _name; 907 | } 908 | 909 | /** 910 | * @dev Gets the token symbol 911 | * @return string representing the token symbol 912 | */ 913 | function symbol() external view returns (string memory) { 914 | return _symbol; 915 | } 916 | 917 | /** 918 | * @dev Returns an URI for a given token ID 919 | * Throws if the token ID does not exist. May return an empty string. 920 | * @param tokenId uint256 ID of the token to query 921 | */ 922 | function tokenURI(uint256 tokenId) external view returns (string memory) { 923 | require(_exists(tokenId)); 924 | return _tokenURIs[tokenId]; 925 | } 926 | 927 | /** 928 | * @dev Internal function to set the token URI for a given token 929 | * Reverts if the token ID does not exist 930 | * @param tokenId uint256 ID of the token to set its URI 931 | * @param uri string URI to assign 932 | */ 933 | function _setTokenURI(uint256 tokenId, string memory uri) internal { 934 | require(_exists(tokenId)); 935 | _tokenURIs[tokenId] = uri; 936 | } 937 | 938 | /** 939 | * @dev Internal function to burn a specific token 940 | * Reverts if the token does not exist 941 | * Deprecated, use _burn(uint256) instead 942 | * @param owner owner of the token to burn 943 | * @param tokenId uint256 ID of the token being burned by the msg.sender 944 | */ 945 | function _burn(address owner, uint256 tokenId) internal { 946 | super._burn(owner, tokenId); 947 | 948 | // Clear metadata (if any) 949 | if (bytes(_tokenURIs[tokenId]).length != 0) { 950 | delete _tokenURIs[tokenId]; 951 | } 952 | } 953 | } 954 | 955 | /** 956 | * @title ERC721Mintable 957 | * @dev ERC721 minting logic 958 | */ 959 | contract ERC721Mintable is MinterRole, ERC721 { 960 | /** 961 | * @dev Function to mint tokens 962 | * @param to The address that will receive the minted tokens. 963 | * @param tokenId The token id to mint. 964 | * @return A boolean that indicates if the operation was successful. 965 | */ 966 | function mint(address to, uint256 tokenId) public onlyMinter returns (bool) { 967 | _mint(to, tokenId); 968 | return true; 969 | } 970 | } 971 | 972 | /** 973 | * @title ERC721MetadataMintable 974 | * @dev ERC721 minting logic with metadata 975 | */ 976 | contract ERC721MetadataMintable is ERC721Metadata, ERC721Mintable { 977 | /** 978 | * @dev Function to mint tokens 979 | * @param to The address that will receive the minted tokens. 980 | * @param tokenId The token id to mint. 981 | * @param tokenURI The token URI of the minted token. 982 | * @return A boolean that indicates if the operation was successful. 983 | */ 984 | function mintWithTokenURI(address to, uint256 tokenId, string memory tokenURI) public onlyMinter returns (bool) { 985 | _mint(to, tokenId); 986 | _setTokenURI(tokenId, tokenURI); 987 | return true; 988 | } 989 | } 990 | 991 | /** 992 | * @title ERC721 Burnable Token 993 | * @dev ERC721 Token that can be irreversibly burned (destroyed). 994 | */ 995 | contract ERC721Burnable is ERC721 { 996 | /** 997 | * @dev Burns a specific ERC721 token. 998 | * @param tokenId uint256 id of the ERC721 token to be burned. 999 | */ 1000 | function burn(uint256 tokenId) public { 1001 | require(_isApprovedOrOwner(msg.sender, tokenId)); 1002 | _burn(tokenId); 1003 | } 1004 | } 1005 | 1006 | contract StandardNFT is ERC721Enumerable, ERC721MetadataMintable, ERC721Burnable { 1007 | using SafeMath for uint256; 1008 | 1009 | constructor (string memory name, string memory symbol, address catalyst) ERC721Metadata(name, symbol) public { 1010 | _addMinter(catalyst); 1011 | mintWithTokenURI(catalyst, 0, "Catalyst"); 1012 | _addPauser(catalyst); 1013 | } 1014 | 1015 | // Basic NFT transfers 1016 | 1017 | function transfer(address _to, uint256 _tokenId) public whenNotPaused returns (bool) { 1018 | safeTransferFrom(msg.sender, _to, _tokenId); 1019 | } 1020 | 1021 | function transferAll(address _to, uint256[] memory _tokenId) public whenNotPaused returns (bool) { 1022 | for (uint i = 0; i < _tokenId.length; i++) { 1023 | safeTransferFrom(msg.sender, _to, _tokenId[i]); 1024 | } 1025 | } 1026 | } 1027 | --------------------------------------------------------------------------------