├── .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 | [](https://www.npmjs.com/package/sol-profiler)
3 | [](https://travis-ci.com/Aniket-Engg/sol-profiler)
4 | [](https://david-dm.org/aniket-engg/sol-profiler)
5 | [](https://david-dm.org/aniket-engg/sol-profiler?type=dev)
6 | [](https://www.npmjs.com/package/sol-profiler)
7 | [](https://www.npmjs.com/package/sol-profiler)
8 | [](https://github.com/Aniket-Engg/sol-profiler)
9 | [](https://github.com/Aniket-Engg/sol-profiler)
10 | [](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 | 
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 | }
--------------------------------------------------------------------------------