├── .npmignore ├── .travis.yml ├── .jshintrc ├── utils ├── index.js ├── directory.js ├── profile.js ├── file.js └── data.js ├── package.json ├── LICENSE ├── .gitignore ├── bin └── sol-profiler.js ├── lib └── generate.js ├── README.md ├── profiles ├── sample_Profile.txt └── ERC721Metadata_Profile.txt └── example ├── sample.sol └── ERC721Metadata.sol /.npmignore: -------------------------------------------------------------------------------- 1 | profiles/ 2 | example/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "node" -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esversion" : 9, 3 | "strict" : true, 4 | "node" : true 5 | } -------------------------------------------------------------------------------- /utils/index.js: -------------------------------------------------------------------------------- 1 | const data = require('./data'), 2 | file = require('./file'), 3 | directory = require('./directory'), 4 | profile = require('./profile'); 5 | 6 | module.exports = { 7 | data, 8 | file, 9 | directory, 10 | profile 11 | }; -------------------------------------------------------------------------------- /utils/directory.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const fs = require('fs'); 4 | 5 | var files = []; 6 | 7 | function deepReadDir(path){ 8 | let filesArr = fs.readdirSync(path); 9 | for(let file of filesArr){ 10 | if(fs.lstatSync(path + '/'+file).isDirectory()) 11 | deepReadDir(path + '/'+file); 12 | else if(!files.includes(path + '/'+file)) 13 | files.push(path + '/'+file); 14 | } 15 | return files; 16 | } 17 | 18 | module.exports.getAllfiles = deepReadDir; 19 | -------------------------------------------------------------------------------- /utils/profile.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const path = require('path'), 4 | fs = require('fs'); 5 | 6 | 7 | module.exports.store = (data, contractName) => { 8 | if(!fs.existsSync(path.join(process.cwd(), '/profiles'))) 9 | fs.mkdirSync(path.join(process.cwd(), '/profiles')); 10 | let fileData = data.replace( 11 | /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, ''); // clearing color formatting 12 | 13 | fs.writeFileSync(path.join(process.cwd(), 'profiles', contractName + '_Profile.txt'), fileData); 14 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sol-profiler", 3 | "version": "2.0.1", 4 | "description": "Colourful Solidity Smart Contract Profiler", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "jshint ./bin ./lib ./utils" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/Aniket-Engg/sol-profiler.git" 12 | }, 13 | "bin": { 14 | "sol-profiler": "./bin/sol-profiler.js" 15 | }, 16 | "keywords": [ 17 | "solidity", 18 | "smartcontract", 19 | "sol", 20 | "profiler", 21 | "solidity-profiler", 22 | "sol-profiler" 23 | ], 24 | "author": "Aniket", 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/Aniket-Engg/sol-profiler/issues" 28 | }, 29 | "homepage": "https://github.com/Aniket-Engg/sol-profiler#readme", 30 | "dependencies": { 31 | "cli-color": "^2.0.0", 32 | "solparse": "2.2.8", 33 | "table": "^5.2.2" 34 | }, 35 | "devDependencies": { 36 | "jshint": "^2.10.2" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Aniket 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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | -------------------------------------------------------------------------------- /bin/sol-profiler.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | "use strict"; 4 | const clc = require('cli-color'), 5 | fs = require('fs'), 6 | { generateAndStore } = require('./../lib/generate'), 7 | {directory} = require('./../utils'); 8 | 9 | if(process.argv.length < 3) { 10 | console.log(clc.redBright("Error: No file or directory provided")); 11 | process.exit(1); 12 | } 13 | 14 | let path = process.argv[2]; 15 | var profiler = async(path) => { 16 | try{ 17 | if(fs.lstatSync(path).isDirectory()){ 18 | let count = 0; 19 | let files = directory.getAllfiles(path); 20 | for(const file of files){ 21 | if(file.substr(-4) == '.sol'){ 22 | await generateAndStore(file); 23 | count++; 24 | } 25 | } 26 | if(count) 27 | console.log(clc.green('Info: Found '+ count + ' Solidity files!!! Generated profiles stored in '+ process.cwd() + '/profiles')); 28 | else 29 | console.log(clc.green('Info: No Solidity files found in directory')); 30 | } 31 | else{ 32 | if(path.substr(-4) == '.sol'){ 33 | let tableData = await generateAndStore(path); 34 | 35 | //Render profile on console 36 | console.log(tableData); 37 | } 38 | else 39 | console.warn(clc.yellow('Warning: Please provide a solidity file')); 40 | } 41 | }catch(error){ 42 | console.error(clc.redBright('Error in generating profile: ' + error.message)); 43 | } 44 | }; 45 | module.exports = profiler(path); 46 | 47 | 48 | -------------------------------------------------------------------------------- /utils/file.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const path = require('path'), 4 | {execSync} = require('child_process'), 5 | fs = require('fs'); 6 | 7 | var regEx = { 8 | pragma : /(pragma solidity (.+?);)/g, 9 | import : /import ['"](.+?)['"];/g 10 | }; 11 | 12 | var processedFiles = []; 13 | 14 | var processFile = async(file, initCall = false) => { 15 | try{ 16 | if(initCall) 17 | processedFiles = []; 18 | 19 | if(processedFiles.indexOf(file) !== -1) 20 | return; 21 | 22 | processedFiles.push(file); 23 | let result = ''; 24 | 25 | let contents = fs.readFileSync(file, { encoding: 'utf-8' }); 26 | contents = contents.replace(regEx.pragma, '').trim(); 27 | let imports = await processImports(file, contents); 28 | 29 | for (let i = 0; i < imports.length; i++) { 30 | result += imports[i] + '\n\n'; 31 | } 32 | contents = contents.replace(regEx.import, '').trim(); 33 | result += contents; 34 | return result; 35 | } 36 | catch(error){ 37 | throw error; 38 | } 39 | }; 40 | 41 | var processImports = async (file, content) => { 42 | try{ 43 | let group=''; 44 | let result = []; 45 | regEx.import.exec(''); // Resetting state of RegEx 46 | while (group = regEx.import.exec(content)) { // jshint ignore:line 47 | let _importFile = group[1]; 48 | let filePath = path.join(path.dirname(file), _importFile); 49 | if(!fs.existsSync(filePath)){ 50 | let nodeModulesPath = (await execSync('npm root', { cwd: path.dirname(file)})).toString().trim(); 51 | filePath = path.join(nodeModulesPath , _importFile); 52 | } 53 | filePath = path.normalize(filePath); 54 | let fileContents = await processFile(filePath); 55 | if (fileContents) { 56 | result.push(fileContents); 57 | } 58 | } 59 | return result; 60 | } 61 | catch(error){ 62 | throw error; 63 | } 64 | }; 65 | 66 | var getPragma = async(path) => { 67 | let contents = fs.readFileSync(path, { encoding: 'utf-8' }); 68 | let group = regEx.pragma.exec(contents); 69 | return group && group[1]; 70 | }; 71 | 72 | module.exports.process = processFile; 73 | module.exports.getPragma = getPragma; -------------------------------------------------------------------------------- /lib/generate.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const parser= require("solparse"), 4 | clc = require("cli-color"), 5 | table = require('table'), 6 | {data, file, profile} = require('./../utils'); 7 | 8 | let config = { 9 | border: { 10 | topBody: `─`, 11 | topJoin: `┬`, 12 | topLeft: `┌`, 13 | topRight: `┐`, 14 | 15 | bottomBody: `─`, 16 | bottomJoin: `┴`, 17 | bottomLeft: `└`, 18 | bottomRight: `┘`, 19 | 20 | bodyLeft: `│`, 21 | bodyRight: `│`, 22 | bodyJoin: `│`, 23 | 24 | joinBody: `─`, 25 | joinLeft: `├`, 26 | joinRight: `┤`, 27 | joinJoin: `┼` 28 | } 29 | }; 30 | 31 | 32 | module.exports.generateAndStore = async(path) => { 33 | try{ 34 | let tableRows = []; 35 | 36 | // Adding filename and solidity version 37 | let version; 38 | let pragma = await file.getPragma(path); 39 | let code = await file.process(path, true); 40 | let source = parser.parse(pragma + '\n\n' + code); 41 | if(source.body[0].type == 'PragmaStatement'){ 42 | let pragmaData = source.body[0]; 43 | version = pragmaData.start_version.operator + pragmaData.start_version.version; 44 | if(pragmaData.end_version) 45 | version += pragmaData.end_version.operator + pragmaData.end_version.version; 46 | } 47 | let fileArray = path.split('/'); 48 | let fileName = fileArray[fileArray.length -1]; 49 | let contractName = fileName.substr(0, fileName.length - 4); 50 | tableRows.push(['',clc.greenBright("File: " + fileName + ", Solidity Pragma: " + version), '','','','']); 51 | 52 | // Adding header row 53 | tableRows.push([clc.whiteBright.bold('Contract/Library/Interface'), clc.whiteBright.bold('Function(Params with Storage Location)'), clc.whiteBright.bold('Visibility'), clc.whiteBright.bold('View/Pure'), clc.whiteBright.bold('Returns'), clc.whiteBright.bold('Modifiers')]); 54 | source.body.forEach(function(contract) { 55 | if(contract.type != 'PragmaStatement'){ 56 | contract.body.forEach(function(part) { 57 | if(part.type == 'ConstructorDeclaration' || part.type == 'FunctionDeclaration') { 58 | let {contractName, functionName, visibility, viewOrPure, returns, modifiers} = data.parseData(contract, part); 59 | tableRows.push([contractName, functionName, visibility, viewOrPure, returns, modifiers]); 60 | } 61 | }); 62 | } 63 | }); 64 | var tableData = table.table(tableRows, config); 65 | // Store profile in profiles folder 66 | profile.store(tableData, contractName); 67 | return tableData; 68 | }catch(error){ 69 | throw error; 70 | } 71 | }; -------------------------------------------------------------------------------- /utils/data.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const clc = require("cli-color"); 4 | 5 | module.exports.parseData = (contract, part) => { 6 | let contractName = clc.cyanBright(contract.name); 7 | if(contract.type == 'LibraryStatement') 8 | contractName = contractName + clc.blackBright(' (library)'); 9 | else if(contract.type == 'InterfaceStatement') 10 | contractName = contractName + clc.whiteBright(' (interface)'); 11 | 12 | let funcName = null; 13 | if(part.type == 'ConstructorDeclaration') 14 | funcName = 'constructor'; 15 | else if(part.type == 'FunctionDeclaration'){ 16 | funcName = part.name || ''; 17 | 18 | } 19 | 20 | let params = []; 21 | if(part.params) { 22 | part.params.forEach(function(param) { 23 | if(param.storage_location) 24 | params.push(param.literal.literal + ' ' + clc.cyan(param.storage_location)); 25 | else 26 | params.push(param.literal.literal); 27 | }); 28 | funcName += '(' + params.join(',') + ')'; 29 | } 30 | else { 31 | //Check fallback 32 | if(!funcName && !part.name && !part.params && !part.returnParams) 33 | funcName = '()' + clc.white(' -fallback'); 34 | else 35 | funcName += '()'; 36 | } 37 | 38 | if(part.is_abstract) 39 | funcName += clc.green(' -abstract'); 40 | 41 | // Default is public 42 | let visibility = clc.magentaBright("public"); 43 | let viewOrPure = ''; 44 | let returns = []; 45 | let custom = []; 46 | 47 | if(part.modifiers) { 48 | part.modifiers.forEach(function(mod) { 49 | switch(mod.name) { 50 | case "public": 51 | break; 52 | case "private": 53 | visibility = clc.redBright("private"); 54 | break; 55 | case "internal": 56 | visibility = clc.red("internal"); 57 | break; 58 | case "external": 59 | visibility = clc.magenta("external"); 60 | break; 61 | case "view": 62 | viewOrPure = clc.yellow("view"); 63 | break; 64 | case "pure": 65 | viewOrPure = clc.yellowBright("pure"); 66 | break; 67 | default: 68 | custom.push(mod.name); 69 | } 70 | }); 71 | } 72 | if(part.returnParams) { 73 | part.returnParams.params.forEach(function(param) { 74 | if(param.storage_location) 75 | returns.push(param.literal.literal + ' ' + clc.cyan(param.storage_location)); 76 | else 77 | returns.push(param.literal.literal); 78 | }); 79 | } 80 | 81 | return { 82 | contractName: contractName, 83 | functionName: clc.blueBright(funcName), 84 | visibility : visibility, 85 | viewOrPure : viewOrPure, 86 | returns : clc.white(returns), 87 | modifiers : clc.white(custom) 88 | }; 89 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sol-profiler 2 | [![npm version](https://badge.fury.io/js/sol-profiler.svg)](https://www.npmjs.com/package/sol-profiler) 3 | [![Build status](https://travis-ci.com/Aniket-Engg/sol-profiler.svg?branch=master)](https://travis-ci.com/Aniket-Engg/sol-profiler) 4 | [![dependencies Status](https://david-dm.org/aniket-engg/sol-profiler/status.svg)](https://david-dm.org/aniket-engg/sol-profiler) 5 | [![devDependencies Status](https://david-dm.org/aniket-engg/sol-profiler/dev-status.svg)](https://david-dm.org/aniket-engg/sol-profiler?type=dev) 6 | [![npm](https://img.shields.io/npm/dw/sol-profiler.svg)](https://www.npmjs.com/package/sol-profiler) 7 | [![npm](https://img.shields.io/npm/dt/sol-profiler.svg?label=Total%20Downloads)](https://www.npmjs.com/package/sol-profiler) 8 | [![GitHub](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/Aniket-Engg/sol-profiler) 9 | [![LoC](https://tokei.rs/b1/github/Aniket-Engg/sol-profiler?category=lines)](https://github.com/Aniket-Engg/sol-profiler) 10 | [![Package Quality](https://npm.packagequality.com/shield/sol-profiler.svg)](https://packagequality.com/#?package=sol-profiler) 11 | 12 | sol-profiler lists down the properties of all the contract methods which helps to visualize and review the definition of various contract methods involved at one glance. This is easy to use and user-friendly. 13 | 14 | Note: sol-profiler does not ensure/guarantee any kind of security or correctness of any smart-contract. 15 | 16 | ## Features 17 | * Lists down attributes of contract methods 18 | * Works with file & directory both 19 | * Displays user friendly colourful profile on console for single file 20 | * Also stores generated profile in a folder names `profiles` in a `.txt` file named with suffix `_Profile` 21 | * Generates & stores profile for each available Solidity file if directory path is passed 22 | * Supports file import relatively and from `node_modules` 23 | * Explicitly marks `abstract` and `fallback` functions 24 | * Explicitly marks `library` and `interface` contracts 25 | * Since Solidity release 0.5.0, Explicit data location for all variables of struct, array or mapping types is now mandatory, so profile also include the data location of parameters defined explicitly. 26 | 27 | ## Install 28 | ``` 29 | npm install --global sol-profiler 30 | ``` 31 | or 32 | ``` 33 | npm install --save-dev sol-profiler 34 | ``` 35 | 36 | ## Application 37 | For DApp, one can provide its contract directory path and profile will be stored for each contract which can be referred in future to get the knowledge of the methods defined in various contract. 38 | ``` 39 | sol-profiler 40 | ``` 41 | 42 | It can be used for individual file as: 43 | ``` 44 | sol-profiler 45 | ``` 46 | It can also be added in the `package.json` as: 47 | ``` 48 | { 49 | "scripts": { 50 | "generateProfile": "sol-profiler ./contracts/" 51 | }, 52 | } 53 | ``` 54 | ## Example 55 | We have attached an extensive example i.e. [sample.sol](https://github.com/Aniket-Engg/sol-profiler/blob/master/example/sample.sol). For this, profiler result will be same as in below image : 56 | 57 | ![solp2](https://user-images.githubusercontent.com/30843294/55281218-4bc56a80-5357-11e9-852f-520dde666b9d.png) 58 | 59 | Generated profile which get stored in `.txt` file can be seen [here](https://github.com/Aniket-Engg/sol-profiler/blob/master/profiles/sample_Profile.txt). 60 | 61 | ## VSCode Extension 62 | sol-profiler is also available as [Visual Studio Code Extension](https://marketplace.visualstudio.com/items?itemName=Aniket-Engg.sol-profiler-vscode) 63 | 64 | ## Contribution/Suggestions 65 | Any kind of suggestion/feedback/contribution is most welcome! 66 | 67 | ## License 68 | [MIT](https://github.com/Aniket-Engg/sol-profiler/blob/master/LICENSE) 69 | -------------------------------------------------------------------------------- /profiles/sample_Profile.txt: -------------------------------------------------------------------------------- 1 | ┌────────────────────────────┬──────────────────────────────────────────────────┬────────────┬───────────┬─────────┬───────────────┐ 2 | │ │ File: sample.sol, Solidity Pragma: >=0.4.0<0.7.0 │ │ │ │ │ 3 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 4 | │ Contract/Library/Interface │ Function(Params with Storage Location) │ Visibility │ View/Pure │ Returns │ Modifiers │ 5 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 6 | │ IERC20 (interface) │ totalSupply() -abstract │ external │ view │ uint256 │ │ 7 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 8 | │ IERC20 (interface) │ balanceOf(address) -abstract │ external │ view │ uint256 │ │ 9 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 10 | │ IERC20 (interface) │ allowance(address,address) -abstract │ external │ view │ uint256 │ │ 11 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 12 | │ IERC20 (interface) │ transfer(address,uint256) -abstract │ external │ │ bool │ │ 13 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 14 | │ IERC20 (interface) │ approve(address,uint256) -abstract │ external │ │ bool │ │ 15 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 16 | │ IERC20 (interface) │ transferFrom(address,address,uint256) -abstract │ external │ │ bool │ │ 17 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 18 | │ SafeMath (library) │ mul(uint256,uint256) │ internal │ pure │ uint256 │ │ 19 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 20 | │ SafeMath (library) │ div(uint256,uint256) │ internal │ pure │ uint256 │ │ 21 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 22 | │ SafeMath (library) │ sub(uint256,uint256) │ internal │ pure │ uint256 │ │ 23 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 24 | │ SafeMath (library) │ add(uint256,uint256) │ internal │ pure │ uint256 │ │ 25 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 26 | │ SafeMath (library) │ mod(uint256,uint256) │ internal │ pure │ uint256 │ │ 27 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 28 | │ Ownable │ constructor() │ internal │ │ │ │ 29 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 30 | │ Ownable │ owner() │ public │ view │ address │ │ 31 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 32 | │ Ownable │ isOwner() │ public │ view │ bool │ │ 33 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 34 | │ Ownable │ renounceOwnership() │ public │ │ │ onlyOwner │ 35 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 36 | │ Ownable │ transferOwnership(address) │ public │ │ │ onlyOwner │ 37 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 38 | │ Ownable │ _transferOwnership(address) │ internal │ │ │ │ 39 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 40 | │ ERC20 │ totalSupply() │ public │ view │ uint256 │ │ 41 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 42 | │ ERC20 │ balanceOf(address) │ public │ view │ uint256 │ │ 43 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 44 | │ ERC20 │ allowance(address,address) │ public │ view │ uint256 │ │ 45 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 46 | │ ERC20 │ transfer(address,uint256) │ public │ │ bool │ │ 47 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 48 | │ ERC20 │ approve(address,uint256) │ public │ │ bool │ │ 49 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 50 | │ ERC20 │ transferFrom(address,address,uint256) │ public │ │ bool │ │ 51 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 52 | │ ERC20 │ increaseAllowance(address,uint256) │ public │ │ bool │ │ 53 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 54 | │ ERC20 │ decreaseAllowance(address,uint256) │ public │ │ bool │ │ 55 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 56 | │ ERC20 │ _transfer(address,address,uint256) │ internal │ │ │ │ 57 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 58 | │ ERC20 │ _mint(address,uint256) │ internal │ │ │ │ 59 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 60 | │ ERC20 │ _burn(address,uint256) │ internal │ │ │ │ 61 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 62 | │ ERC20 │ _burnFrom(address,uint256) │ internal │ │ │ │ 63 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 64 | │ ERC20Detailed │ constructor(string,string,uint8) │ public │ │ │ │ 65 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 66 | │ ERC20Detailed │ name() │ public │ view │ string │ │ 67 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 68 | │ ERC20Detailed │ symbol() │ public │ view │ string │ │ 69 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 70 | │ ERC20Detailed │ decimals() │ public │ view │ uint8 │ │ 71 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 72 | │ SimpleToken │ constructor() │ public │ │ │ ERC20Detailed │ 73 | ├────────────────────────────┼──────────────────────────────────────────────────┼────────────┼───────────┼─────────┼───────────────┤ 74 | │ SimpleToken │ () -fallback │ public │ │ │ payable │ 75 | └────────────────────────────┴──────────────────────────────────────────────────┴────────────┴───────────┴─────────┴───────────────┘ 76 | -------------------------------------------------------------------------------- /example/sample.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.0 <0.7.0; 2 | 3 | /** 4 | * @title ERC20 interface 5 | * @dev see https://github.com/ethereum/EIPs/issues/20 6 | */ 7 | interface IERC20 { 8 | function totalSupply() external view returns (uint256); 9 | 10 | function balanceOf(address who) external view returns (uint256); 11 | 12 | function allowance(address owner, address spender) 13 | external view returns (uint256); 14 | 15 | function transfer(address to, uint256 value) external returns (bool); 16 | 17 | function approve(address spender, uint256 value) 18 | external returns (bool); 19 | 20 | function transferFrom(address from, address to, uint256 value) 21 | external returns (bool); 22 | 23 | event Transfer( 24 | address indexed from, 25 | address indexed to, 26 | uint256 value 27 | ); 28 | 29 | event Approval( 30 | address indexed owner, 31 | address indexed spender, 32 | uint256 value 33 | ); 34 | } 35 | 36 | /** 37 | * @title SafeMath 38 | * @dev Math operations with safety checks that revert on error 39 | */ 40 | library SafeMath { 41 | 42 | /** 43 | * @dev Multiplies two numbers, reverts on overflow. 44 | */ 45 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 46 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 47 | // benefit is lost if 'b' is also tested. 48 | // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 49 | if (a == 0) { 50 | return 0; 51 | } 52 | 53 | uint256 c = a * b; 54 | require(c / a == b); 55 | 56 | return c; 57 | } 58 | 59 | /** 60 | * @dev Integer division of two numbers truncating the quotient, reverts on division by zero. 61 | */ 62 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 63 | require(b > 0); // Solidity only automatically asserts when dividing by 0 64 | uint256 c = a / b; 65 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 66 | 67 | return c; 68 | } 69 | 70 | /** 71 | * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend). 72 | */ 73 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 74 | require(b <= a); 75 | uint256 c = a - b; 76 | 77 | return c; 78 | } 79 | 80 | /** 81 | * @dev Adds two numbers, reverts on overflow. 82 | */ 83 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 84 | uint256 c = a + b; 85 | require(c >= a); 86 | 87 | return c; 88 | } 89 | 90 | /** 91 | * @dev Divides two numbers and returns the remainder (unsigned integer modulo), 92 | * reverts when dividing by zero. 93 | */ 94 | function mod(uint256 a, uint256 b) internal pure returns (uint256) { 95 | require(b != 0); 96 | return a % b; 97 | } 98 | } 99 | 100 | /** 101 | * @title Ownable 102 | * @dev The Ownable contract has an owner address, and provides basic authorization control 103 | * functions, this simplifies the implementation of "user permissions". 104 | */ 105 | contract Ownable { 106 | address private _owner; 107 | 108 | event OwnershipTransferred( 109 | address indexed previousOwner, 110 | address indexed newOwner 111 | ); 112 | 113 | /** 114 | * @dev The Ownable constructor sets the original `owner` of the contract to the sender 115 | * account. 116 | */ 117 | constructor() internal { 118 | _owner = msg.sender; 119 | emit OwnershipTransferred(address(0), _owner); 120 | } 121 | 122 | /** 123 | * @return the address of the owner. 124 | */ 125 | function owner() public view returns(address) { 126 | return _owner; 127 | } 128 | 129 | /** 130 | * @dev Throws if called by any account other than the owner. 131 | */ 132 | modifier onlyOwner() { 133 | require(isOwner()); 134 | _; 135 | } 136 | 137 | /** 138 | * @return true if `msg.sender` is the owner of the contract. 139 | */ 140 | function isOwner() public view returns(bool) { 141 | return msg.sender == _owner; 142 | } 143 | 144 | /** 145 | * @dev Allows the current owner to relinquish control of the contract. 146 | * @notice Renouncing to ownership will leave the contract without an owner. 147 | * It will not be possible to call the functions with the `onlyOwner` 148 | * modifier anymore. 149 | */ 150 | function renounceOwnership() public onlyOwner { 151 | emit OwnershipTransferred(_owner, address(0)); 152 | _owner = address(0); 153 | } 154 | 155 | /** 156 | * @dev Allows the current owner to transfer control of the contract to a newOwner. 157 | * @param newOwner The address to transfer ownership to. 158 | */ 159 | function transferOwnership(address newOwner) public onlyOwner { 160 | _transferOwnership(newOwner); 161 | } 162 | 163 | /** 164 | * @dev Transfers control of the contract to a newOwner. 165 | * @param newOwner The address to transfer ownership to. 166 | */ 167 | function _transferOwnership(address newOwner) internal { 168 | require(newOwner != address(0)); 169 | emit OwnershipTransferred(_owner, newOwner); 170 | _owner = newOwner; 171 | } 172 | } 173 | 174 | /** 175 | * @title Standard ERC20 token 176 | * 177 | * @dev Implementation of the basic standard token. 178 | * https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md 179 | * Originally based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol 180 | */ 181 | contract ERC20 is IERC20 { 182 | using SafeMath for uint256; 183 | 184 | mapping (address => uint256) private _balances; 185 | 186 | mapping (address => mapping (address => uint256)) private _allowed; 187 | 188 | uint256 private _totalSupply; 189 | 190 | /** 191 | * @dev Total number of tokens in existence 192 | */ 193 | function totalSupply() public view returns (uint256) { 194 | return _totalSupply; 195 | } 196 | 197 | /** 198 | * @dev Gets the balance of the specified address. 199 | * @param owner The address to query the balance of. 200 | * @return An uint256 representing the amount owned by the passed address. 201 | */ 202 | function balanceOf(address owner) public view returns (uint256) { 203 | return _balances[owner]; 204 | } 205 | 206 | /** 207 | * @dev Function to check the amount of tokens that an owner allowed to a spender. 208 | * @param owner address The address which owns the funds. 209 | * @param spender address The address which will spend the funds. 210 | * @return A uint256 specifying the amount of tokens still available for the spender. 211 | */ 212 | function allowance( 213 | address owner, 214 | address spender 215 | ) 216 | public 217 | view 218 | returns (uint256) 219 | { 220 | return _allowed[owner][spender]; 221 | } 222 | 223 | /** 224 | * @dev Transfer token for a specified address 225 | * @param to The address to transfer to. 226 | * @param value The amount to be transferred. 227 | */ 228 | function transfer(address to, uint256 value) public returns (bool) { 229 | _transfer(msg.sender, to, value); 230 | return true; 231 | } 232 | 233 | /** 234 | * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. 235 | * Beware that changing an allowance with this method brings the risk that someone may use both the old 236 | * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this 237 | * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: 238 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 239 | * @param spender The address which will spend the funds. 240 | * @param value The amount of tokens to be spent. 241 | */ 242 | function approve(address spender, uint256 value) public returns (bool) { 243 | require(spender != address(0)); 244 | 245 | _allowed[msg.sender][spender] = value; 246 | emit Approval(msg.sender, spender, value); 247 | return true; 248 | } 249 | 250 | /** 251 | * @dev Transfer tokens from one address to another 252 | * @param from address The address which you want to send tokens from 253 | * @param to address The address which you want to transfer to 254 | * @param value uint256 the amount of tokens to be transferred 255 | */ 256 | function transferFrom( 257 | address from, 258 | address to, 259 | uint256 value 260 | ) 261 | public 262 | returns (bool) 263 | { 264 | require(value <= _allowed[from][msg.sender]); 265 | 266 | _allowed[from][msg.sender] = _allowed[from][msg.sender].sub(value); 267 | _transfer(from, to, value); 268 | return true; 269 | } 270 | 271 | /** 272 | * @dev Increase the amount of tokens that an owner allowed to a spender. 273 | * approve should be called when allowed_[_spender] == 0. To increment 274 | * allowed value is better to use this function to avoid 2 calls (and wait until 275 | * the first transaction is mined) 276 | * From MonolithDAO Token.sol 277 | * @param spender The address which will spend the funds. 278 | * @param addedValue The amount of tokens to increase the allowance by. 279 | */ 280 | function increaseAllowance( 281 | address spender, 282 | uint256 addedValue 283 | ) 284 | public 285 | returns (bool) 286 | { 287 | require(spender != address(0)); 288 | 289 | _allowed[msg.sender][spender] = ( 290 | _allowed[msg.sender][spender].add(addedValue)); 291 | emit Approval(msg.sender, spender, _allowed[msg.sender][spender]); 292 | return true; 293 | } 294 | 295 | /** 296 | * @dev Decrease the amount of tokens that an owner allowed to a spender. 297 | * approve should be called when allowed_[_spender] == 0. To decrement 298 | * allowed value is better to use this function to avoid 2 calls (and wait until 299 | * the first transaction is mined) 300 | * From MonolithDAO Token.sol 301 | * @param spender The address which will spend the funds. 302 | * @param subtractedValue The amount of tokens to decrease the allowance by. 303 | */ 304 | function decreaseAllowance( 305 | address spender, 306 | uint256 subtractedValue 307 | ) 308 | public 309 | returns (bool) 310 | { 311 | require(spender != address(0)); 312 | 313 | _allowed[msg.sender][spender] = ( 314 | _allowed[msg.sender][spender].sub(subtractedValue)); 315 | emit Approval(msg.sender, spender, _allowed[msg.sender][spender]); 316 | return true; 317 | } 318 | 319 | /** 320 | * @dev Transfer token for a specified addresses 321 | * @param from The address to transfer from. 322 | * @param to The address to transfer to. 323 | * @param value The amount to be transferred. 324 | */ 325 | function _transfer(address from, address to, uint256 value) internal { 326 | require(value <= _balances[from]); 327 | require(to != address(0)); 328 | 329 | _balances[from] = _balances[from].sub(value); 330 | _balances[to] = _balances[to].add(value); 331 | emit Transfer(from, to, value); 332 | } 333 | 334 | /** 335 | * @dev Internal function that mints an amount of the token and assigns it to 336 | * an account. This encapsulates the modification of balances such that the 337 | * proper events are emitted. 338 | * @param account The account that will receive the created tokens. 339 | * @param value The amount that will be created. 340 | */ 341 | function _mint(address account, uint256 value) internal { 342 | require(account != 0); 343 | _totalSupply = _totalSupply.add(value); 344 | _balances[account] = _balances[account].add(value); 345 | emit Transfer(address(0), account, value); 346 | } 347 | 348 | /** 349 | * @dev Internal function that burns an amount of the token of a given 350 | * account. 351 | * @param account The account whose tokens will be burnt. 352 | * @param value The amount that will be burnt. 353 | */ 354 | function _burn(address account, uint256 value) internal { 355 | require(account != 0); 356 | require(value <= _balances[account]); 357 | 358 | _totalSupply = _totalSupply.sub(value); 359 | _balances[account] = _balances[account].sub(value); 360 | emit Transfer(account, address(0), value); 361 | } 362 | 363 | /** 364 | * @dev Internal function that burns an amount of the token of a given 365 | * account, deducting from the sender's allowance for said account. Uses the 366 | * internal burn function. 367 | * @param account The account whose tokens will be burnt. 368 | * @param value The amount that will be burnt. 369 | */ 370 | function _burnFrom(address account, uint256 value) internal { 371 | require(value <= _allowed[account][msg.sender]); 372 | 373 | // Should https://github.com/OpenZeppelin/zeppelin-solidity/issues/707 be accepted, 374 | // this function needs to emit an event with the updated approval. 375 | _allowed[account][msg.sender] = _allowed[account][msg.sender].sub( 376 | value); 377 | _burn(account, value); 378 | } 379 | } 380 | 381 | /** 382 | * @title ERC20Detailed token 383 | * @dev The decimals are only for visualization purposes. 384 | * All the operations are done using the smallest and indivisible token unit, 385 | * just as on Ethereum all the operations are done in wei. 386 | */ 387 | contract ERC20Detailed is IERC20 { 388 | string private _name; 389 | string private _symbol; 390 | uint8 private _decimals; 391 | 392 | constructor(string name, string symbol, uint8 decimals) public { 393 | _name = name; 394 | _symbol = symbol; 395 | _decimals = decimals; 396 | } 397 | 398 | /** 399 | * @return the name of the token. 400 | */ 401 | function name() public view returns(string) { 402 | return _name; 403 | } 404 | 405 | /** 406 | * @return the symbol of the token. 407 | */ 408 | function symbol() public view returns(string) { 409 | return _symbol; 410 | } 411 | 412 | /** 413 | * @return the number of decimals of the token. 414 | */ 415 | function decimals() public view returns(uint8) { 416 | return _decimals; 417 | } 418 | } 419 | 420 | /** 421 | * @title SimpleToken 422 | * @dev Very simple ERC20 Token example, where all tokens are pre-assigned to the creator. 423 | * Note they can later distribute these tokens as they wish using `transfer` and other 424 | * `ERC20` functions. 425 | */ 426 | contract SimpleToken is ERC20, ERC20Detailed { 427 | 428 | uint256 public constant INITIAL_SUPPLY = 10000 * (10 ** uint256(decimals())); 429 | 430 | /** 431 | * @dev Constructor that gives msg.sender all of existing tokens. 432 | */ 433 | constructor() public ERC20Detailed("SimpleToken", "SIM", 18) { 434 | _mint(msg.sender, INITIAL_SUPPLY); 435 | } 436 | 437 | /** 438 | Fallback accepts ether 439 | */ 440 | 441 | function() public payable{ 442 | 443 | } 444 | 445 | } -------------------------------------------------------------------------------- /profiles/ERC721Metadata_Profile.txt: -------------------------------------------------------------------------------- 1 | ┌────────────────────────────┬──────────────────────────────────────────────────────────────────┬────────────┬───────────┬───────────────┬───────────┐ 2 | │ │ File: ERC721Metadata.sol, Solidity Pragma: ^0.5.2 │ │ │ │ │ 3 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 4 | │ Contract/Library/Interface │ Function(Params with Storage Location) │ Visibility │ View/Pure │ Returns │ Modifiers │ 5 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 6 | │ IERC165 (interface) │ supportsInterface(bytes4) -abstract │ external │ view │ bool │ │ 7 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 8 | │ IERC721 │ balanceOf(address) -abstract │ public │ view │ uint256 │ │ 9 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 10 | │ IERC721 │ ownerOf(uint256) -abstract │ public │ view │ address │ │ 11 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 12 | │ IERC721 │ approve(address,uint256) -abstract │ public │ │ │ │ 13 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 14 | │ IERC721 │ getApproved(uint256) -abstract │ public │ view │ address │ │ 15 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 16 | │ IERC721 │ setApprovalForAll(address,bool) -abstract │ public │ │ │ │ 17 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 18 | │ IERC721 │ isApprovedForAll(address,address) -abstract │ public │ view │ bool │ │ 19 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 20 | │ IERC721 │ transferFrom(address,address,uint256) -abstract │ public │ │ │ │ 21 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 22 | │ IERC721 │ safeTransferFrom(address,address,uint256) -abstract │ public │ │ │ │ 23 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 24 | │ IERC721 │ safeTransferFrom(address,address,uint256,bytes memory) -abstract │ public │ │ │ │ 25 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 26 | │ IERC721Receiver │ onERC721Received(address,address,uint256,bytes memory) -abstract │ public │ │ bytes4 │ │ 27 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 28 | │ SafeMath (library) │ mul(uint256,uint256) │ internal │ pure │ uint256 │ │ 29 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 30 | │ SafeMath (library) │ div(uint256,uint256) │ internal │ pure │ uint256 │ │ 31 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 32 | │ SafeMath (library) │ sub(uint256,uint256) │ internal │ pure │ uint256 │ │ 33 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 34 | │ SafeMath (library) │ add(uint256,uint256) │ internal │ pure │ uint256 │ │ 35 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 36 | │ SafeMath (library) │ mod(uint256,uint256) │ internal │ pure │ uint256 │ │ 37 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 38 | │ Address (library) │ isContract(address) │ internal │ view │ bool │ │ 39 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 40 | │ Counters (library) │ current(Counter storage) │ internal │ view │ uint256 │ │ 41 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 42 | │ Counters (library) │ increment(Counter storage) │ internal │ │ │ │ 43 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 44 | │ Counters (library) │ decrement(Counter storage) │ internal │ │ │ │ 45 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 46 | │ ERC165 │ constructor() │ internal │ │ │ │ 47 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 48 | │ ERC165 │ supportsInterface(bytes4) │ external │ view │ bool │ │ 49 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 50 | │ ERC165 │ _registerInterface(bytes4) │ internal │ │ │ │ 51 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 52 | │ ERC721 │ constructor() │ public │ │ │ │ 53 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 54 | │ ERC721 │ balanceOf(address) │ public │ view │ uint256 │ │ 55 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 56 | │ ERC721 │ ownerOf(uint256) │ public │ view │ address │ │ 57 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 58 | │ ERC721 │ approve(address,uint256) │ public │ │ │ │ 59 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 60 | │ ERC721 │ getApproved(uint256) │ public │ view │ address │ │ 61 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 62 | │ ERC721 │ setApprovalForAll(address,bool) │ public │ │ │ │ 63 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 64 | │ ERC721 │ isApprovedForAll(address,address) │ public │ view │ bool │ │ 65 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 66 | │ ERC721 │ transferFrom(address,address,uint256) │ public │ │ │ │ 67 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 68 | │ ERC721 │ safeTransferFrom(address,address,uint256) │ public │ │ │ │ 69 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 70 | │ ERC721 │ safeTransferFrom(address,address,uint256,bytes memory) │ public │ │ │ │ 71 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 72 | │ ERC721 │ _exists(uint256) │ internal │ view │ bool │ │ 73 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 74 | │ ERC721 │ _isApprovedOrOwner(address,uint256) │ internal │ view │ bool │ │ 75 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 76 | │ ERC721 │ _mint(address,uint256) │ internal │ │ │ │ 77 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 78 | │ ERC721 │ _burn(address,uint256) │ internal │ │ │ │ 79 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 80 | │ ERC721 │ _burn(uint256) │ internal │ │ │ │ 81 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 82 | │ ERC721 │ _transferFrom(address,address,uint256) │ internal │ │ │ │ 83 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 84 | │ ERC721 │ _checkOnERC721Received(address,address,uint256,bytes memory) │ internal │ │ bool │ │ 85 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 86 | │ ERC721 │ _clearApproval(uint256) │ private │ │ │ │ 87 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 88 | │ IERC721Metadata │ name() -abstract │ external │ view │ string memory │ │ 89 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 90 | │ IERC721Metadata │ symbol() -abstract │ external │ view │ string memory │ │ 91 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 92 | │ IERC721Metadata │ tokenURI(uint256) -abstract │ external │ view │ string memory │ │ 93 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 94 | │ ERC721Metadata │ constructor(string memory,string memory) │ public │ │ │ │ 95 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 96 | │ ERC721Metadata │ name() │ external │ view │ string memory │ │ 97 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 98 | │ ERC721Metadata │ symbol() │ external │ view │ string memory │ │ 99 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 100 | │ ERC721Metadata │ tokenURI(uint256) │ external │ view │ string memory │ │ 101 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 102 | │ ERC721Metadata │ _setTokenURI(uint256,string memory) │ internal │ │ │ │ 103 | ├────────────────────────────┼──────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────┤ 104 | │ ERC721Metadata │ _burn(address,uint256) │ internal │ │ │ │ 105 | └────────────────────────────┴──────────────────────────────────────────────────────────────────┴────────────┴───────────┴───────────────┴───────────┘ 106 | -------------------------------------------------------------------------------- /example/ERC721Metadata.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | /** 4 | * @title IERC165 5 | * @dev https://eips.ethereum.org/EIPS/eip-165 6 | */ 7 | interface IERC165 { 8 | /** 9 | * @notice Query if a contract implements an interface 10 | * @param interfaceId The interface identifier, as specified in ERC-165 11 | * @dev Interface identification is specified in ERC-165. This function 12 | * uses less than 30,000 gas. 13 | */ 14 | function supportsInterface(bytes4 interfaceId) external view returns (bool); 15 | } 16 | 17 | /** 18 | * @title ERC721 Non-Fungible Token Standard basic interface 19 | * @dev see https://eips.ethereum.org/EIPS/eip-721 20 | */ 21 | contract IERC721 is IERC165 { 22 | event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); 23 | event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); 24 | event ApprovalForAll(address indexed owner, address indexed operator, bool approved); 25 | 26 | function balanceOf(address owner) public view returns (uint256 balance); 27 | function ownerOf(uint256 tokenId) public view returns (address owner); 28 | 29 | function approve(address to, uint256 tokenId) public; 30 | function getApproved(uint256 tokenId) public view returns (address operator); 31 | 32 | function setApprovalForAll(address operator, bool _approved) public; 33 | function isApprovedForAll(address owner, address operator) public view returns (bool); 34 | 35 | function transferFrom(address from, address to, uint256 tokenId) public; 36 | function safeTransferFrom(address from, address to, uint256 tokenId) public; 37 | 38 | function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public; 39 | } 40 | 41 | /** 42 | * @title ERC721 token receiver interface 43 | * @dev Interface for any contract that wants to support safeTransfers 44 | * from ERC721 asset contracts. 45 | */ 46 | contract IERC721Receiver { 47 | /** 48 | * @notice Handle the receipt of an NFT 49 | * @dev The ERC721 smart contract calls this function on the recipient 50 | * after a `safeTransfer`. This function MUST return the function selector, 51 | * otherwise the caller will revert the transaction. The selector to be 52 | * returned can be obtained as `this.onERC721Received.selector`. This 53 | * function MAY throw to revert and reject the transfer. 54 | * Note: the ERC721 contract address is always the message sender. 55 | * @param operator The address which called `safeTransferFrom` function 56 | * @param from The address which previously owned the token 57 | * @param tokenId The NFT identifier which is being transferred 58 | * @param data Additional data with no specified format 59 | * @return bytes4 `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` 60 | */ 61 | function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data) 62 | public returns (bytes4); 63 | } 64 | 65 | /** 66 | * @title SafeMath 67 | * @dev Unsigned math operations with safety checks that revert on error 68 | */ 69 | library SafeMath { 70 | /** 71 | * @dev Multiplies two unsigned integers, reverts on overflow. 72 | */ 73 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 74 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 75 | // benefit is lost if 'b' is also tested. 76 | // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 77 | if (a == 0) { 78 | return 0; 79 | } 80 | 81 | uint256 c = a * b; 82 | require(c / a == b); 83 | 84 | return c; 85 | } 86 | 87 | /** 88 | * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero. 89 | */ 90 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 91 | // Solidity only automatically asserts when dividing by 0 92 | require(b > 0); 93 | uint256 c = a / b; 94 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 95 | 96 | return c; 97 | } 98 | 99 | /** 100 | * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend). 101 | */ 102 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 103 | require(b <= a); 104 | uint256 c = a - b; 105 | 106 | return c; 107 | } 108 | 109 | /** 110 | * @dev Adds two unsigned integers, reverts on overflow. 111 | */ 112 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 113 | uint256 c = a + b; 114 | require(c >= a); 115 | 116 | return c; 117 | } 118 | 119 | /** 120 | * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo), 121 | * reverts when dividing by zero. 122 | */ 123 | function mod(uint256 a, uint256 b) internal pure returns (uint256) { 124 | require(b != 0); 125 | return a % b; 126 | } 127 | } 128 | 129 | /** 130 | * Utility library of inline functions on addresses 131 | */ 132 | library Address { 133 | /** 134 | * Returns whether the target address is a contract 135 | * @dev This function will return false if invoked during the constructor of a contract, 136 | * as the code is not actually created until after the constructor finishes. 137 | * @param account address of the account to check 138 | * @return whether the target address is a contract 139 | */ 140 | function isContract(address account) internal view returns (bool) { 141 | uint256 size; 142 | // XXX Currently there is no better way to check if there is a contract in an address 143 | // than to check the size of the code at that address. 144 | // See https://ethereum.stackexchange.com/a/14016/36603 145 | // for more details about how this works. 146 | // TODO Check this again before the Serenity release, because all addresses will be 147 | // contracts then. 148 | // solhint-disable-next-line no-inline-assembly 149 | assembly { size := extcodesize(account) } 150 | return size > 0; 151 | } 152 | } 153 | 154 | /** 155 | * @title Counters 156 | * @author Matt Condon (@shrugs) 157 | * @dev Provides counters that can only be incremented or decremented by one. This can be used e.g. to track the number 158 | * of elements in a mapping, issuing ERC721 ids, or counting request ids 159 | * 160 | * Include with `using Counters for Counters.Counter;` 161 | * Since it is not possible to overflow a 256 bit integer with increments of one, `increment` can skip the SafeMath 162 | * overflow check, thereby saving gas. This does assume however correct usage, in that the underlying `_value` is never 163 | * directly accessed. 164 | */ 165 | library Counters { 166 | using SafeMath for uint256; 167 | 168 | struct Counter { 169 | // This variable should never be directly accessed by users of the library: interactions must be restricted to 170 | // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add 171 | // this feature: see https://github.com/ethereum/solidity/issues/4637 172 | uint256 _value; // default: 0 173 | } 174 | 175 | function current(Counter storage counter) internal view returns (uint256) { 176 | return counter._value; 177 | } 178 | 179 | function increment(Counter storage counter) internal { 180 | counter._value += 1; 181 | } 182 | 183 | function decrement(Counter storage counter) internal { 184 | counter._value = counter._value.sub(1); 185 | } 186 | } 187 | 188 | /** 189 | * @title ERC165 190 | * @author Matt Condon (@shrugs) 191 | * @dev Implements ERC165 using a lookup table. 192 | */ 193 | contract ERC165 is IERC165 { 194 | bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7; 195 | /* 196 | * 0x01ffc9a7 === 197 | * bytes4(keccak256('supportsInterface(bytes4)')) 198 | */ 199 | 200 | /** 201 | * @dev a mapping of interface id to whether or not it's supported 202 | */ 203 | mapping(bytes4 => bool) private _supportedInterfaces; 204 | 205 | /** 206 | * @dev A contract implementing SupportsInterfaceWithLookup 207 | * implement ERC165 itself 208 | */ 209 | constructor () internal { 210 | _registerInterface(_INTERFACE_ID_ERC165); 211 | } 212 | 213 | /** 214 | * @dev implement supportsInterface(bytes4) using a lookup table 215 | */ 216 | function supportsInterface(bytes4 interfaceId) external view returns (bool) { 217 | return _supportedInterfaces[interfaceId]; 218 | } 219 | 220 | /** 221 | * @dev internal method for registering an interface 222 | */ 223 | function _registerInterface(bytes4 interfaceId) internal { 224 | require(interfaceId != 0xffffffff); 225 | _supportedInterfaces[interfaceId] = true; 226 | } 227 | } 228 | 229 | /** 230 | * @title ERC721 Non-Fungible Token Standard basic implementation 231 | * @dev see https://eips.ethereum.org/EIPS/eip-721 232 | */ 233 | contract ERC721 is ERC165, IERC721 { 234 | using SafeMath for uint256; 235 | using Address for address; 236 | using Counters for Counters.Counter; 237 | 238 | // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` 239 | // which can be also obtained as `IERC721Receiver(0).onERC721Received.selector` 240 | bytes4 private constant _ERC721_RECEIVED = 0x150b7a02; 241 | 242 | // Mapping from token ID to owner 243 | mapping (uint256 => address) private _tokenOwner; 244 | 245 | // Mapping from token ID to approved address 246 | mapping (uint256 => address) private _tokenApprovals; 247 | 248 | // Mapping from owner to number of owned token 249 | mapping (address => Counters.Counter) private _ownedTokensCount; 250 | 251 | // Mapping from owner to operator approvals 252 | mapping (address => mapping (address => bool)) private _operatorApprovals; 253 | 254 | bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd; 255 | /* 256 | * 0x80ac58cd === 257 | * bytes4(keccak256('balanceOf(address)')) ^ 258 | * bytes4(keccak256('ownerOf(uint256)')) ^ 259 | * bytes4(keccak256('approve(address,uint256)')) ^ 260 | * bytes4(keccak256('getApproved(uint256)')) ^ 261 | * bytes4(keccak256('setApprovalForAll(address,bool)')) ^ 262 | * bytes4(keccak256('isApprovedForAll(address,address)')) ^ 263 | * bytes4(keccak256('transferFrom(address,address,uint256)')) ^ 264 | * bytes4(keccak256('safeTransferFrom(address,address,uint256)')) ^ 265 | * bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) 266 | */ 267 | 268 | constructor () public { 269 | // register the supported interfaces to conform to ERC721 via ERC165 270 | _registerInterface(_INTERFACE_ID_ERC721); 271 | } 272 | 273 | /** 274 | * @dev Gets the balance of the specified address 275 | * @param owner address to query the balance of 276 | * @return uint256 representing the amount owned by the passed address 277 | */ 278 | function balanceOf(address owner) public view returns (uint256) { 279 | require(owner != address(0)); 280 | return _ownedTokensCount[owner].current(); 281 | } 282 | 283 | /** 284 | * @dev Gets the owner of the specified token ID 285 | * @param tokenId uint256 ID of the token to query the owner of 286 | * @return address currently marked as the owner of the given token ID 287 | */ 288 | function ownerOf(uint256 tokenId) public view returns (address) { 289 | address owner = _tokenOwner[tokenId]; 290 | require(owner != address(0)); 291 | return owner; 292 | } 293 | 294 | /** 295 | * @dev Approves another address to transfer the given token ID 296 | * The zero address indicates there is no approved address. 297 | * There can only be one approved address per token at a given time. 298 | * Can only be called by the token owner or an approved operator. 299 | * @param to address to be approved for the given token ID 300 | * @param tokenId uint256 ID of the token to be approved 301 | */ 302 | function approve(address to, uint256 tokenId) public { 303 | address owner = ownerOf(tokenId); 304 | require(to != owner); 305 | require(msg.sender == owner || isApprovedForAll(owner, msg.sender)); 306 | 307 | _tokenApprovals[tokenId] = to; 308 | emit Approval(owner, to, tokenId); 309 | } 310 | 311 | /** 312 | * @dev Gets the approved address for a token ID, or zero if no address set 313 | * Reverts if the token ID does not exist. 314 | * @param tokenId uint256 ID of the token to query the approval of 315 | * @return address currently approved for the given token ID 316 | */ 317 | function getApproved(uint256 tokenId) public view returns (address) { 318 | require(_exists(tokenId)); 319 | return _tokenApprovals[tokenId]; 320 | } 321 | 322 | /** 323 | * @dev Sets or unsets the approval of a given operator 324 | * An operator is allowed to transfer all tokens of the sender on their behalf 325 | * @param to operator address to set the approval 326 | * @param approved representing the status of the approval to be set 327 | */ 328 | function setApprovalForAll(address to, bool approved) public { 329 | require(to != msg.sender); 330 | _operatorApprovals[msg.sender][to] = approved; 331 | emit ApprovalForAll(msg.sender, to, approved); 332 | } 333 | 334 | /** 335 | * @dev Tells whether an operator is approved by a given owner 336 | * @param owner owner address which you want to query the approval of 337 | * @param operator operator address which you want to query the approval of 338 | * @return bool whether the given operator is approved by the given owner 339 | */ 340 | function isApprovedForAll(address owner, address operator) public view returns (bool) { 341 | return _operatorApprovals[owner][operator]; 342 | } 343 | 344 | /** 345 | * @dev Transfers the ownership of a given token ID to another address 346 | * Usage of this method is discouraged, use `safeTransferFrom` whenever possible 347 | * Requires the msg.sender to be the owner, approved, or operator 348 | * @param from current owner of the token 349 | * @param to address to receive the ownership of the given token ID 350 | * @param tokenId uint256 ID of the token to be transferred 351 | */ 352 | function transferFrom(address from, address to, uint256 tokenId) public { 353 | require(_isApprovedOrOwner(msg.sender, tokenId)); 354 | 355 | _transferFrom(from, to, tokenId); 356 | } 357 | 358 | /** 359 | * @dev Safely transfers the ownership of a given token ID to another address 360 | * If the target address is a contract, it must implement `onERC721Received`, 361 | * which is called upon a safe transfer, and return the magic value 362 | * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise, 363 | * the transfer is reverted. 364 | * Requires the msg.sender to be the owner, approved, or operator 365 | * @param from current owner of the token 366 | * @param to address to receive the ownership of the given token ID 367 | * @param tokenId uint256 ID of the token to be transferred 368 | */ 369 | function safeTransferFrom(address from, address to, uint256 tokenId) public { 370 | safeTransferFrom(from, to, tokenId, ""); 371 | } 372 | 373 | /** 374 | * @dev Safely transfers the ownership of a given token ID to another address 375 | * If the target address is a contract, it must implement `onERC721Received`, 376 | * which is called upon a safe transfer, and return the magic value 377 | * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise, 378 | * the transfer is reverted. 379 | * Requires the msg.sender to be the owner, approved, or operator 380 | * @param from current owner of the token 381 | * @param to address to receive the ownership of the given token ID 382 | * @param tokenId uint256 ID of the token to be transferred 383 | * @param _data bytes data to send along with a safe transfer check 384 | */ 385 | function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public { 386 | transferFrom(from, to, tokenId); 387 | require(_checkOnERC721Received(from, to, tokenId, _data)); 388 | } 389 | 390 | /** 391 | * @dev Returns whether the specified token exists 392 | * @param tokenId uint256 ID of the token to query the existence of 393 | * @return bool whether the token exists 394 | */ 395 | function _exists(uint256 tokenId) internal view returns (bool) { 396 | address owner = _tokenOwner[tokenId]; 397 | return owner != address(0); 398 | } 399 | 400 | /** 401 | * @dev Returns whether the given spender can transfer a given token ID 402 | * @param spender address of the spender to query 403 | * @param tokenId uint256 ID of the token to be transferred 404 | * @return bool whether the msg.sender is approved for the given token ID, 405 | * is an operator of the owner, or is the owner of the token 406 | */ 407 | function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) { 408 | address owner = ownerOf(tokenId); 409 | return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender)); 410 | } 411 | 412 | /** 413 | * @dev Internal function to mint a new token 414 | * Reverts if the given token ID already exists 415 | * @param to The address that will own the minted token 416 | * @param tokenId uint256 ID of the token to be minted 417 | */ 418 | function _mint(address to, uint256 tokenId) internal { 419 | require(to != address(0)); 420 | require(!_exists(tokenId)); 421 | 422 | _tokenOwner[tokenId] = to; 423 | _ownedTokensCount[to].increment(); 424 | 425 | emit Transfer(address(0), to, tokenId); 426 | } 427 | 428 | /** 429 | * @dev Internal function to burn a specific token 430 | * Reverts if the token does not exist 431 | * Deprecated, use _burn(uint256) instead. 432 | * @param owner owner of the token to burn 433 | * @param tokenId uint256 ID of the token being burned 434 | */ 435 | function _burn(address owner, uint256 tokenId) internal { 436 | require(ownerOf(tokenId) == owner); 437 | 438 | _clearApproval(tokenId); 439 | 440 | _ownedTokensCount[owner].decrement(); 441 | _tokenOwner[tokenId] = address(0); 442 | 443 | emit Transfer(owner, address(0), tokenId); 444 | } 445 | 446 | /** 447 | * @dev Internal function to burn a specific token 448 | * Reverts if the token does not exist 449 | * @param tokenId uint256 ID of the token being burned 450 | */ 451 | function _burn(uint256 tokenId) internal { 452 | _burn(ownerOf(tokenId), tokenId); 453 | } 454 | 455 | /** 456 | * @dev Internal function to transfer ownership of a given token ID to another address. 457 | * As opposed to transferFrom, this imposes no restrictions on msg.sender. 458 | * @param from current owner of the token 459 | * @param to address to receive the ownership of the given token ID 460 | * @param tokenId uint256 ID of the token to be transferred 461 | */ 462 | function _transferFrom(address from, address to, uint256 tokenId) internal { 463 | require(ownerOf(tokenId) == from); 464 | require(to != address(0)); 465 | 466 | _clearApproval(tokenId); 467 | 468 | _ownedTokensCount[from].decrement(); 469 | _ownedTokensCount[to].increment(); 470 | 471 | _tokenOwner[tokenId] = to; 472 | 473 | emit Transfer(from, to, tokenId); 474 | } 475 | 476 | /** 477 | * @dev Internal function to invoke `onERC721Received` on a target address 478 | * The call is not executed if the target address is not a contract 479 | * @param from address representing the previous owner of the given token ID 480 | * @param to target address that will receive the tokens 481 | * @param tokenId uint256 ID of the token to be transferred 482 | * @param _data bytes optional data to send along with the call 483 | * @return bool whether the call correctly returned the expected magic value 484 | */ 485 | function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data) 486 | internal returns (bool) 487 | { 488 | if (!to.isContract()) { 489 | return true; 490 | } 491 | 492 | bytes4 retval = IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, _data); 493 | return (retval == _ERC721_RECEIVED); 494 | } 495 | 496 | /** 497 | * @dev Private function to clear current approval of a given token ID 498 | * @param tokenId uint256 ID of the token to be transferred 499 | */ 500 | function _clearApproval(uint256 tokenId) private { 501 | if (_tokenApprovals[tokenId] != address(0)) { 502 | _tokenApprovals[tokenId] = address(0); 503 | } 504 | } 505 | } 506 | 507 | /** 508 | * @title ERC-721 Non-Fungible Token Standard, optional metadata extension 509 | * @dev See https://eips.ethereum.org/EIPS/eip-721 510 | */ 511 | contract IERC721Metadata is IERC721 { 512 | function name() external view returns (string memory); 513 | function symbol() external view returns (string memory); 514 | function tokenURI(uint256 tokenId) external view returns (string memory); 515 | } 516 | 517 | contract ERC721Metadata is ERC165, ERC721, IERC721Metadata { 518 | // Token name 519 | string private _name; 520 | 521 | // Token symbol 522 | string private _symbol; 523 | 524 | // Optional mapping for token URIs 525 | mapping(uint256 => string) private _tokenURIs; 526 | 527 | bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f; 528 | /* 529 | * 0x5b5e139f === 530 | * bytes4(keccak256('name()')) ^ 531 | * bytes4(keccak256('symbol()')) ^ 532 | * bytes4(keccak256('tokenURI(uint256)')) 533 | */ 534 | 535 | /** 536 | * @dev Constructor function 537 | */ 538 | constructor (string memory name, string memory symbol) public { 539 | _name = name; 540 | _symbol = symbol; 541 | 542 | // register the supported interfaces to conform to ERC721 via ERC165 543 | _registerInterface(_INTERFACE_ID_ERC721_METADATA); 544 | } 545 | 546 | /** 547 | * @dev Gets the token name 548 | * @return string representing the token name 549 | */ 550 | function name() external view returns (string memory) { 551 | return _name; 552 | } 553 | 554 | /** 555 | * @dev Gets the token symbol 556 | * @return string representing the token symbol 557 | */ 558 | function symbol() external view returns (string memory) { 559 | return _symbol; 560 | } 561 | 562 | /** 563 | * @dev Returns an URI for a given token ID 564 | * Throws if the token ID does not exist. May return an empty string. 565 | * @param tokenId uint256 ID of the token to query 566 | */ 567 | function tokenURI(uint256 tokenId) external view returns (string memory) { 568 | require(_exists(tokenId)); 569 | return _tokenURIs[tokenId]; 570 | } 571 | 572 | /** 573 | * @dev Internal function to set the token URI for a given token 574 | * Reverts if the token ID does not exist 575 | * @param tokenId uint256 ID of the token to set its URI 576 | * @param uri string URI to assign 577 | */ 578 | function _setTokenURI(uint256 tokenId, string memory uri) internal { 579 | require(_exists(tokenId)); 580 | _tokenURIs[tokenId] = uri; 581 | } 582 | 583 | /** 584 | * @dev Internal function to burn a specific token 585 | * Reverts if the token does not exist 586 | * Deprecated, use _burn(uint256) instead 587 | * @param owner owner of the token to burn 588 | * @param tokenId uint256 ID of the token being burned by the msg.sender 589 | */ 590 | function _burn(address owner, uint256 tokenId) internal { 591 | super._burn(owner, tokenId); 592 | 593 | // Clear metadata (if any) 594 | if (bytes(_tokenURIs[tokenId]).length != 0) { 595 | delete _tokenURIs[tokenId]; 596 | } 597 | } 598 | } --------------------------------------------------------------------------------