├── images ├── newBasicUI.png └── originalUI.png ├── smart_contracts ├── public │ ├── images │ │ ├── radar.gif │ │ ├── star.jpg │ │ ├── background.png │ │ ├── starIcon.png │ │ ├── undefined.png │ │ ├── background1.jpg │ │ ├── background2.jpg │ │ ├── background3.jpeg │ │ ├── background4.jpeg │ │ └── background5.jpeg │ ├── style.css │ ├── index.html │ ├── functionality.js │ └── ABI.js ├── migrations │ ├── 1_initial_migration.js │ └── 2_deploy_contracts.js ├── .gitignore ├── package.json ├── contracts │ ├── Migrations.sol │ └── StarNotary.sol ├── server.js ├── truffle-config.js └── test │ └── StarNotaryTest.js ├── .gitattributes ├── .whitesource ├── greenkeeper.json ├── LICENSE └── README.md /images/newBasicUI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alpersonalwebsite/erc721-smart-contract/HEAD/images/newBasicUI.png -------------------------------------------------------------------------------- /images/originalUI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alpersonalwebsite/erc721-smart-contract/HEAD/images/originalUI.png -------------------------------------------------------------------------------- /smart_contracts/public/images/radar.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alpersonalwebsite/erc721-smart-contract/HEAD/smart_contracts/public/images/radar.gif -------------------------------------------------------------------------------- /smart_contracts/public/images/star.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alpersonalwebsite/erc721-smart-contract/HEAD/smart_contracts/public/images/star.jpg -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | *.html linguist-detectable=false 3 | *.css linguist-detectable=false 4 | *.js linguist-detectable=false 5 | -------------------------------------------------------------------------------- /smart_contracts/public/images/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alpersonalwebsite/erc721-smart-contract/HEAD/smart_contracts/public/images/background.png -------------------------------------------------------------------------------- /smart_contracts/public/images/starIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alpersonalwebsite/erc721-smart-contract/HEAD/smart_contracts/public/images/starIcon.png -------------------------------------------------------------------------------- /smart_contracts/public/images/undefined.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alpersonalwebsite/erc721-smart-contract/HEAD/smart_contracts/public/images/undefined.png -------------------------------------------------------------------------------- /smart_contracts/public/images/background1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alpersonalwebsite/erc721-smart-contract/HEAD/smart_contracts/public/images/background1.jpg -------------------------------------------------------------------------------- /smart_contracts/public/images/background2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alpersonalwebsite/erc721-smart-contract/HEAD/smart_contracts/public/images/background2.jpg -------------------------------------------------------------------------------- /smart_contracts/public/images/background3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alpersonalwebsite/erc721-smart-contract/HEAD/smart_contracts/public/images/background3.jpeg -------------------------------------------------------------------------------- /smart_contracts/public/images/background4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alpersonalwebsite/erc721-smart-contract/HEAD/smart_contracts/public/images/background4.jpeg -------------------------------------------------------------------------------- /smart_contracts/public/images/background5.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alpersonalwebsite/erc721-smart-contract/HEAD/smart_contracts/public/images/background5.jpeg -------------------------------------------------------------------------------- /.whitesource: -------------------------------------------------------------------------------- 1 | { 2 | "generalSettings": { 3 | "shouldScanRepo": true 4 | }, 5 | "checkRunSettings": { 6 | "vulnerableCheckRunConclusionLevel": "failure" 7 | } 8 | } -------------------------------------------------------------------------------- /greenkeeper.json: -------------------------------------------------------------------------------- 1 | { 2 | "groups": { 3 | "default": { 4 | "packages": [ 5 | "smart_contracts/package.json" 6 | ] 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /smart_contracts/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require("./Migrations.sol"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /smart_contracts/migrations/2_deploy_contracts.js: -------------------------------------------------------------------------------- 1 | var StarNotary = artifacts.require('./StarNotary.sol'); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(StarNotary); 5 | }; 6 | -------------------------------------------------------------------------------- /smart_contracts/.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | 4 | # contracts > build 5 | build/contracts/* 6 | 7 | # truffle config 8 | truffle-config-withCredentials.js 9 | 10 | # misc 11 | .DS_Store 12 | .env.local 13 | .env.development.local 14 | .env.test.local 15 | .env.production.local 16 | 17 | npm-debug.log* 18 | yarn-debug.log* 19 | yarn-error.log* 20 | 21 | # we dont want to commit the image files (0-9) 22 | public/images/[0-9]* 23 | -------------------------------------------------------------------------------- /smart_contracts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "smart_contracts", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "body-parser": "^1.18.3", 14 | "express": "^4.16.4", 15 | "node-server-screenshot": "^0.2.1", 16 | "openzeppelin-solidity": "^2.1.1", 17 | "serve-static": "^1.13.2", 18 | "truffle-hdwallet-provider": "^1.0.2" 19 | }, 20 | "devDependencies": { 21 | "nodemon": "^1.18.9" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /smart_contracts/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.6.0; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | constructor() public { 8 | owner = msg.sender; 9 | } 10 | 11 | modifier restricted() { 12 | if (msg.sender == owner) _; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 DApp with custom implementation of ERC-721 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 | -------------------------------------------------------------------------------- /smart_contracts/public/style.css: -------------------------------------------------------------------------------- 1 | body, 2 | html { 3 | height: 100%; 4 | margin: 0; 5 | font: 400 15px/1.8 'Lato', sans-serif; 6 | } 7 | 8 | .screenBackground { 9 | position: relative; 10 | opacity: 1; 11 | background-position: center; 12 | background-repeat: no-repeat; 13 | background-size: cover; 14 | background-image: url('images/background3.jpeg'); 15 | height: 100%; 16 | } 17 | 18 | #screen { 19 | position: absolute; 20 | left: 0; 21 | top: 0; 22 | width: 100%; 23 | text-align: center; 24 | color: #000; 25 | } 26 | 27 | .modal-background { 28 | align-items: center; 29 | display: flex; 30 | } 31 | 32 | h1, 33 | h2 { 34 | color: #fff !important; 35 | text-shadow: 1px 1px #000; 36 | } 37 | 38 | h2 { 39 | margin-bottom: 15px !important; 40 | } 41 | 42 | article.media { 43 | background: #fff; 44 | border-radius: 4px; 45 | padding: 10px; 46 | } 47 | 48 | .image img { 49 | min-height: 100%; 50 | } 51 | 52 | .bigImage { 53 | border: 4px solid white; 54 | margin: 0 auto; 55 | display: block; 56 | max-width: 80%; 57 | } 58 | 59 | #star-show .content > p { 60 | margin-bottom: 5px; 61 | } 62 | #star-show .buttons .button, 63 | #star-show .content, 64 | #star-show .buttons:not(:last-child) { 65 | margin-bottom: 0; 66 | } 67 | 68 | #star-show .button { 69 | font-size: 10px; 70 | } 71 | 72 | .corrdinatesValidationWrap { 73 | display: none; 74 | } 75 | 76 | /* Ext helper to show */ 77 | .show { 78 | display: block; 79 | } 80 | -------------------------------------------------------------------------------- /smart_contracts/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const bodyParser = require('body-parser'); 3 | const serveStatic = require('serve-static'); 4 | const nodeServerSS = require('node-server-screenshot'); 5 | 6 | const app = express(); 7 | 8 | app.use( 9 | bodyParser.urlencoded({ 10 | extended: true 11 | }) 12 | ); 13 | app.use(bodyParser.json()); 14 | 15 | app.post('/star', async function(req, res) { 16 | if (req.get('host') !== '127.0.0.1:3000') { 17 | res.send({ status: 'domain error' }); 18 | return; 19 | } 20 | 21 | // Replace maybe with PhantomJS and some npm package 22 | // And check: https://www.npmjs.com/package/express-session 23 | // We dont want to store all the images 24 | // User closes tab, closes session and deletes image 25 | 26 | // Configuration api http://server7.wikisky.org/api? 27 | let ra = req.body.starInfo.ra.replace(/ /g, '%20'); 28 | let dec = req.body.starInfo.dec.replace(/ /g, '%20'); 29 | 30 | // Example: 'http://server1.sky-map.org/skywindow?ra=1%2003%2033.35&de=-49%2031%2038.1&zoom=10&show_box=1' 31 | const skyUrl = 32 | 'http://server1.sky-map.org/skywindow?ra=' + 33 | ra + 34 | '&de=' + 35 | dec + 36 | '&zoom=10&show_box=1'; 37 | 38 | const imagePath = 'images/' + req.body.starInfo.tokenId + '.png'; 39 | nodeServerSS.fromURL( 40 | skyUrl, 41 | 'public/' + imagePath, 42 | { 43 | clip: { 44 | x: 30, 45 | y: 30, 46 | width: req.body.userInfo.windoWidth, 47 | height: req.body.userInfo.windowHeight 48 | } 49 | }, 50 | await function() { 51 | // done! 52 | res.send({ imagePath }); 53 | } 54 | ); 55 | 56 | //res.send({ status: 'great!' }); 57 | }); 58 | 59 | app.use(serveStatic('public', { index: ['index.html'] })); 60 | 61 | app.listen(3000); 62 | -------------------------------------------------------------------------------- /smart_contracts/contracts/StarNotary.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | // For remix 4 | //import "https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-solidity/master/contracts/token/ERC721/ERC721.sol"; 5 | import '../node_modules/openzeppelin-solidity/contracts/token/ERC721/ERC721.sol'; 6 | import '../node_modules/openzeppelin-solidity/contracts/token/ERC721/ERC721Metadata.sol'; 7 | 8 | contract StarNotary is ERC721, ERC721Metadata { 9 | 10 | constructor() ERC721Metadata("UdaTokenName", "USYMB") public { 11 | } 12 | 13 | struct Star { 14 | string starName; 15 | string starStory; 16 | string ra; 17 | string dec; 18 | string mag; 19 | bytes32 coordsHash; 20 | } 21 | 22 | mapping(uint256 => Star) public tokenIdToStarInfo; 23 | mapping(bytes32 => bool) public unique; 24 | mapping(uint256 => uint256) public starsForSale; 25 | 26 | uint256 public tokenAt; 27 | 28 | function createStar(string starName, string starStory, string ra, string dec, string mag) public { 29 | tokenAt++; 30 | 31 | bytes32 coordinates; 32 | 33 | // It should be greater than 3 34 | require(bytes(starName).length > 3); 35 | 36 | // coordinates = keccak256(abi.encodePacked(ra, dec, mag)); 37 | coordinates = coordinatesToHash(ra, dec, mag); 38 | 39 | require(!checkIfStarExist(coordinates), "We have that Star!"); 40 | 41 | Star memory newStar = Star(starName, starStory, ra, dec, mag, coordinates); 42 | 43 | uint256 tokenId = tokenAt; 44 | tokenIdToStarInfo[tokenId] = newStar; 45 | unique[coordinates] = true; 46 | 47 | _mint(msg.sender, tokenId); 48 | } 49 | 50 | function tokenIdToStarInfo(uint256 tokenId) public view returns(string, string, string, string, string) { 51 | return (tokenIdToStarInfo[tokenId].starName, tokenIdToStarInfo[tokenId].starStory, tokenIdToStarInfo[tokenId].ra, tokenIdToStarInfo[tokenId].dec, tokenIdToStarInfo[tokenId].mag); 52 | } 53 | 54 | function checkIfStarExist(bytes32 coordinates) public view returns(bool) { 55 | return unique[coordinates]; 56 | } 57 | 58 | // To avoid: Warning: Function state mutability can be restricted to pure 59 | function coordinatesToHash(string ra, string dec, string mag) public pure returns(bytes32) { 60 | return keccak256(abi.encodePacked(ra, dec, mag)); 61 | } 62 | 63 | function putStarUpForSale(uint256 tokenId, uint256 price) public { 64 | require(this.ownerOf(tokenId) == msg.sender, "You are not the owner of that Star!"); 65 | starsForSale[tokenId] = price; 66 | } 67 | 68 | function buyStar(uint256 tokenId) public payable { 69 | // If it has a price, it is up for sale 70 | require(starsForSale[tokenId] > 0); 71 | 72 | uint256 starCost = starsForSale[tokenId]; 73 | address starOwner = this.ownerOf(tokenId); 74 | require(msg.value >= starCost); 75 | 76 | _removeTokenFrom(starOwner, tokenId); 77 | 78 | _addTokenTo(msg.sender, tokenId); 79 | 80 | starOwner.transfer(starCost); 81 | 82 | // If the value sent is more than the value of the star, we send the remaining back 83 | if(msg.value > starCost) { 84 | msg.sender.transfer(msg.value - starCost); 85 | } 86 | 87 | // And since it was sold, we remove it from the mapping 88 | starsForSale[tokenId] = 0; 89 | } 90 | 91 | // https://medium.com/coinmonks/exploring-non-fungible-token-with-zeppelin-library-erc721-399cb180cfaf 92 | function mint(uint256 tokenId) public { 93 | super._mint(msg.sender, tokenId); 94 | } 95 | 96 | function transferStar(address starOwner, address to, uint256 tokenId) public { 97 | safeTransferFrom(starOwner, to, tokenId); 98 | } 99 | 100 | function exchangeStars(address user1, uint256 user1TokenId, address user2, uint256 user2TokenId) public { 101 | 102 | require(this.ownerOf(user1TokenId) == user1); 103 | require(this.ownerOf(user2TokenId) == user2); 104 | 105 | _removeTokenFrom(user1, user1TokenId); 106 | _addTokenTo(user2, user1TokenId); 107 | 108 | _removeTokenFrom(user2, user2TokenId); 109 | _addTokenTo(user1, user2TokenId); 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /smart_contracts/truffle-config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Use this file to configure your truffle project. It's seeded with some 3 | * common settings for different networks and features like migrations, 4 | * compilation and testing. Uncomment the ones you need or modify 5 | * them to suit your project as necessary. 6 | * 7 | * More information about configuration can be found at: 8 | * 9 | * truffleframework.com/docs/advanced/configuration 10 | * 11 | * To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider) 12 | * to sign your transactions before they're sent to a remote public node. Infura API 13 | * keys are available for free at: infura.io/register 14 | * 15 | * > > Using Truffle V5 or later? Make sure you install the `web3-one` version. 16 | * 17 | * > > $ npm install truffle-hdwallet-provider@web3-one 18 | * 19 | * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate 20 | * public/private key pairs. If you're publishing your code to GitHub make sure you load this 21 | * phrase from a file you've .gitignored so it doesn't accidentally become public. 22 | * 23 | */ 24 | 25 | // const HDWallet = require('truffle-hdwallet-provider'); 26 | // const infuraKey = "fj4jll3k....."; 27 | // 28 | // const fs = require('fs'); 29 | // const mnemonic = fs.readFileSync(".secret").toString().trim(); 30 | 31 | // UsefuL: https://medium.com/coinmonks/the-many-ways-to-deploy-your-smart-contract-to-rinkeby-network-38cadf7b20be 32 | const HDWalletProvider = require('truffle-hdwallet-provider'); 33 | module.exports = { 34 | /** 35 | * Networks define how you connect to your ethereum client and let you set the 36 | * defaults web3 uses to send transactions. If you don't specify one truffle 37 | * will spin up a development blockchain for you on port 9545 when you 38 | * run `develop` or `test`. You can ask a truffle command to use a specific 39 | * network from the command line, e.g 40 | * 41 | * $ truffle test --network 42 | */ 43 | 44 | networks: { 45 | // Useful for testing. The `development` name is special - truffle uses it by default 46 | // if it's defined here and no other network is specified at the command line. 47 | // You should run a client (like ganache-cli, geth or parity) in a separate terminal 48 | // tab if you use this network and you must also set the `host`, `port` and `network_id` 49 | // options below to some value. 50 | // 51 | development: { 52 | host: '127.0.0.1', // Localhost (default: none) 53 | port: 8545, // Standard Ethereum port (default: none) 54 | network_id: '*' // Any network (default: none) 55 | }, 56 | rinkeby: { 57 | provider: function() { 58 | return new HDWalletProvider( 59 | 'xxx xx xxxx xxxx', 60 | 'https://rinkeby.infura.io/v3/xxxxxx' 61 | ); 62 | }, 63 | network_id: '4', 64 | gas: 4500000, 65 | gasPrice: 10000000000 66 | } 67 | // Another network with more advanced options... 68 | // advanced: { 69 | // port: 8777, // Custom port 70 | // network_id: 1342, // Custom network 71 | // gas: 8500000, // Gas sent with each transaction (default: ~6700000) 72 | // gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei) 73 | // from:
, // Account to send txs from (default: accounts[0]) 74 | // websockets: true // Enable EventEmitter interface for web3 (default: false) 75 | // }, 76 | // Useful for deploying to a public network. 77 | // NB: It's important to wrap the provider as a function. 78 | // ropsten: { 79 | // provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/${infuraKey}`), 80 | // network_id: 3, // Ropsten's id 81 | // gas: 5500000, // Ropsten has a lower block limit than mainnet 82 | // confirmations: 2, // # of confs to wait between deployments. (default: 0) 83 | // timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50) 84 | // skipDryRun: true // Skip dry run before migrations? (default: false for public nets ) 85 | // }, 86 | // Useful for private networks 87 | // private: { 88 | // provider: () => new HDWalletProvider(mnemonic, `https://network.io`), 89 | // network_id: 2111, // This network is yours, in the cloud. 90 | // production: true // Treats this network as if it was a public net. (default: false) 91 | // } 92 | }, 93 | 94 | // Set default mocha options here, use special reporters etc. 95 | mocha: { 96 | // timeout: 100000 97 | }, 98 | 99 | // Configure your compilers 100 | compilers: { 101 | solc: { 102 | version: '0.4.24' // Fetch exact version from solc-bin (default: truffle's version) 103 | // docker: true, // Use "0.5.1" you've installed locally with docker (default: false) 104 | // settings: { // See the solidity docs for advice about optimization and evmVersion 105 | // optimizer: { 106 | // enabled: false, 107 | // runs: 200 108 | // }, 109 | // evmVersion: "byzantium" 110 | // } 111 | } 112 | } 113 | }; 114 | -------------------------------------------------------------------------------- /smart_contracts/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Star Notary 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |
21 |
22 |

Star Notary

23 |

Register your Star

24 |
25 |
26 |
27 |
28 |
29 |
30 |

31 | 32 | 33 | 34 | 35 |

36 |
37 |
38 |
39 |
40 |
41 |

42 | 43 | 44 | 45 | 46 |

47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |

55 | 56 | 57 | 58 | 59 |

60 |
61 |
62 |
63 |
64 |
65 |

66 | 67 | 68 | 69 | 70 |

71 |
72 |
73 |
74 |
75 |

76 | 77 | 78 | 79 | 80 |

81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | 89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |

97 | 98 | Submit 99 | 100 |

101 |
102 |
103 |
104 |
105 |
106 |
107 |

Retrieve Star

108 |
109 |
110 |
111 |
112 |
113 |
114 |

115 | 116 | 117 | 118 | 119 |

120 |
121 |

122 | 123 | Retrieve Star Data 124 | 125 |

126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 | 137 |
138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /smart_contracts/test/StarNotaryTest.js: -------------------------------------------------------------------------------- 1 | // Contract abstraction, JSON representation 2 | const StarNotary = artifacts.require('StarNotary'); 3 | 4 | contract('StarNotary', accounts => { 5 | let user0 = accounts[0]; 6 | let user1 = accounts[1]; 7 | let user2 = accounts[2]; 8 | //let randomMaliciousUser = accounts[3]; 9 | 10 | let starName = 'My Star'; 11 | let starStory = 'I found it when I was...'; 12 | let ra = '1'; 13 | let dec = '1'; 14 | let mag = '1'; 15 | 16 | let tokenId = 1; 17 | 18 | let weiQt = 0.01; 19 | 20 | const tokenName = 'UdaTokenName'; 21 | const tokenSymbol = 'USYMB'; 22 | 23 | // web3 object is available automatically 24 | let starPrice = web3.utils.toWei(weiQt.toString(), 'ether'); 25 | // We can convert to BN web3.utils.toBN() 26 | 27 | beforeEach(async function() { 28 | this.contract = await StarNotary.new({ 29 | from: user0 30 | }); 31 | }); 32 | 33 | describe('Token information', () => { 34 | it('displays the right token name', async function() { 35 | assert.equal(await this.contract.name.call(), tokenName); 36 | }); 37 | it('displays the right token symbol', async function() { 38 | assert.equal(await this.contract.symbol.call(), tokenSymbol); 39 | }); 40 | }); 41 | 42 | describe('Star Creation', () => { 43 | it('can create a star and retrieve its name', async function() { 44 | await this.contract.createStar(starName, starStory, ra, dec, mag, { 45 | from: user1 46 | }); 47 | let starInfo = await this.contract.tokenIdToStarInfo.call(tokenId); 48 | assert.equal(starInfo[0], starName); 49 | }); 50 | 51 | describe('transfering Stars', () => { 52 | beforeEach(async function() { 53 | //await this.contract.createStar('awesome star!', starId, { 54 | await this.contract.createStar(starName, starStory, ra, dec, mag, { 55 | from: user1 56 | }); 57 | }); 58 | it('can transfer a star', async function() { 59 | assert.equal(await this.contract.ownerOf.call(tokenId), user1); 60 | await this.contract.transferStar(user1, user2, tokenId, { 61 | from: user1 62 | }); 63 | assert.equal(await this.contract.ownerOf.call(tokenId), user2); 64 | }); 65 | }); 66 | 67 | describe('buying and selling stars', () => { 68 | beforeEach(async function() { 69 | //await this.contract.createStar('awesome star!', starId, { 70 | await this.contract.createStar(starName, starStory, ra, dec, mag, { 71 | from: user1 72 | }); 73 | }); 74 | 75 | it('user1 can put up their star for sale', async function() { 76 | assert.equal(await this.contract.ownerOf(tokenId), user1); 77 | await this.contract.putStarUpForSale(tokenId, starPrice, { 78 | from: user1 79 | }); 80 | 81 | assert.equal(await this.contract.starsForSale.call(tokenId), starPrice); 82 | }); 83 | 84 | describe('user2 can buy a star that was put up for sale', () => { 85 | beforeEach(async function() { 86 | await this.contract.putStarUpForSale(tokenId, starPrice, { 87 | from: user1 88 | }); 89 | }); 90 | 91 | it('user2 is the owner of the star after they buy it', async function() { 92 | await this.contract.buyStar(tokenId, { 93 | from: user2, 94 | value: starPrice, 95 | gasPrice: 0 96 | }); 97 | assert.equal(await this.contract.ownerOf.call(tokenId), user2); 98 | }); 99 | 100 | it('user2 ether balance changed correctly', async function() { 101 | let overpaidAmount = web3.utils.toWei((0.05).toString(), 'ether'); 102 | const balanceBeforeTransaction = await web3.eth.getBalance(user2); 103 | await this.contract.buyStar(tokenId, { 104 | from: user2, 105 | value: overpaidAmount, 106 | gasPrice: 0 107 | }); 108 | const balanceAfterTransaction = await web3.eth.getBalance(user2); 109 | 110 | assert.equal( 111 | balanceBeforeTransaction - balanceAfterTransaction, 112 | starPrice 113 | ); 114 | }); 115 | }); 116 | }); 117 | 118 | ///////////////////////////////////////// 119 | 120 | describe('transfering tokens simultaneously between 2 owners', () => { 121 | const starUser1info = ['star1', 'starStory1', 'ra1', 'dec1', 'mag1']; 122 | const starUser2info = ['star2', 'starStory2', 'ra2', 'dec2', 'mag2']; 123 | let starUser1TokenId, starUser2TokenId; 124 | const regex = /[0-9]+/gm; 125 | 126 | beforeEach(async function() { 127 | // User 1 128 | let starUser1 = await this.contract.createStar(...starUser1info, { 129 | from: user1 130 | }); 131 | let tokenId1 = starUser1.logs[0].args.tokenId.toString(); 132 | starUser1TokenId = tokenId1.match(regex); 133 | 134 | // User 2 135 | let starUser2 = await this.contract.createStar(...starUser2info, { 136 | from: user2 137 | }); 138 | let tokenId2 = starUser2.logs[0].args.tokenId.toString(); 139 | starUser2TokenId = tokenId2.match(regex); 140 | }); 141 | 142 | it('changes token from user1 to user 2 and viceversa', async function() { 143 | await this.contract.exchangeStars( 144 | user1, 145 | starUser1TokenId[0], 146 | user2, 147 | starUser2TokenId[0] 148 | ); 149 | 150 | assert.equal(await this.contract.ownerOf(starUser1TokenId[0]), user2); 151 | assert.equal(await this.contract.ownerOf(starUser2TokenId[0]), user1); 152 | }); 153 | }); 154 | }); 155 | 156 | describe('Other functions tests...', () => { 157 | beforeEach(async function() { 158 | // { from: user0 } is a special object that always can be passed as the last parameter of any function 159 | await this.contract.createStar(starName, starStory, ra, dec, mag, { 160 | from: user0 161 | }); 162 | }); 163 | 164 | describe('checkIfStarExist()', () => { 165 | it('verifies if a Star was registered', async function() { 166 | let coordinates = await this.contract.coordinatesToHash(ra, dec, mag); 167 | assert.equal( 168 | await this.contract.checkIfStarExist.call(coordinates), 169 | true 170 | ); 171 | }); 172 | }); 173 | 174 | describe('mint() and ownerOf()', () => { 175 | it('verifies ownership after mint', async function() { 176 | await this.contract.mint('3'); 177 | assert.equal(await this.contract.ownerOf.call(tokenId), user0); 178 | }); 179 | }); 180 | 181 | describe('approve() and getApproved()', () => { 182 | it('approves and get approved.', async function() { 183 | await this.contract.approve(user2, tokenId, { from: user0 }); 184 | assert.equal( 185 | await this.contract.getApproved.call(tokenId, { from: user0 }), 186 | user2 187 | ); 188 | }); 189 | }); 190 | 191 | describe('setApprovalForAll()', () => { 192 | it('sets approval for all', async function() { 193 | await this.contract.setApprovalForAll(user2, tokenId); 194 | assert.equal( 195 | await this.contract.isApprovedForAll.call(user0, user2, { 196 | from: user0 197 | }), 198 | true 199 | ); 200 | }); 201 | }); 202 | 203 | describe('safeTransferFrom()', () => { 204 | it('can be transferred', async function() { 205 | await this.contract.safeTransferFrom(user0, user2, tokenId); 206 | assert.equal(await this.contract.ownerOf.call(tokenId), user2); 207 | }); 208 | }); 209 | }); 210 | }); 211 | -------------------------------------------------------------------------------- /smart_contracts/public/functionality.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Constants 3 | * 4 | */ 5 | 6 | const modalWindow = document.querySelector('body > .modal'); 7 | 8 | // The interface definition for your smart contract (the ABI) >> build/contracts/StarNotary.json > abi property 9 | const StarNotary = web3.eth.contract(ABI); 10 | // Grab the contract at specified deployed address with the >> interface defined by the ABI 11 | const starNotary = StarNotary.at('0xF7c71e77b4E0670019D4e4C89Be877428A25489d'); 12 | 13 | const starImage = 'images/starIcon.png'; 14 | const radar = 'images/radar.gif'; // For loading 15 | 16 | /* 17 | * Templates 18 | * 19 | */ 20 | // Modal template 1: no MetaMask 21 | const noMetamaskAcCtemplate = 22 | ''; 23 | 24 | //Modal template 2: zoom image 25 | let showFullImage = function(imagePath) { 26 | return ( 27 | '' 30 | ); 31 | }; 32 | 33 | let name, particularSection; 34 | 35 | let basicTemplateCard = (starImageClass, starImage, name, particularSection) => 36 | '

' + 41 | name + 42 | '' + 43 | particularSection + 44 | '

'; 45 | 46 | /* 47 | * Helpers 48 | * 49 | */ 50 | 51 | // We can expand it for multiple elements and multiple classes 52 | function addClassToElement(element, theClass) { 53 | // We dont care about IE9 54 | return element.classList.add(theClass); 55 | } 56 | 57 | function removeClassToElement(element, theClass) { 58 | console.log(element, theClass); 59 | element.classList.remove(theClass); 60 | } 61 | 62 | function openModal(element, theClass) { 63 | addClassToElement(element, theClass); 64 | } 65 | 66 | function modalContent(type, imagePath) { 67 | if (imagePath) { 68 | document.querySelector('.modal').innerHTML = type(imagePath); 69 | return; 70 | } 71 | document.querySelector('.modal').innerHTML = type; 72 | } 73 | 74 | function closeModal(element, theClass) { 75 | removeClassToElement(element, theClass); 76 | } 77 | 78 | function modalEventsForClosing(element, theClass) { 79 | document.querySelectorAll('.close').forEach(function(elem) { 80 | elem.addEventListener('click', function() { 81 | closeModal(element, theClass); 82 | }); 83 | }); 84 | } 85 | 86 | /* 87 | * Web3 init conf 88 | * 89 | */ 90 | if (typeof web3 !== 'undefined') { 91 | web3 = new Web3(web3.currentProvider); 92 | } else { 93 | // set the provider you want from Web3.providers 94 | web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545')); 95 | } 96 | 97 | // The default (top) wallet account from a list of test accounts 98 | // web3.eth.defaultAccount = web3.eth.accounts[0]; 99 | web3.eth.getAccounts(function(error, accounts) { 100 | web3.eth.defaultAccount = web3.eth.accounts[0]; 101 | }); 102 | 103 | if (!web3.eth.defaultAccount) { 104 | console.log('UHHHHHHHHHHHHHHHHHHHHHH'); 105 | openModal(modalWindow, 'is-active'); 106 | modalContent(noMetamaskAcCtemplate); 107 | modalEventsForClosing(modalWindow, 'is-active'); 108 | } 109 | 110 | /* 111 | * Methods 112 | * 113 | */ 114 | 115 | function claimButtonClicked() { 116 | name = document.querySelector('#star-name').value; 117 | const story = document.querySelector('#star-story').value; 118 | const ra = document.querySelector('#star-ra').value; 119 | const dec = document.querySelector('#star-dec').value; 120 | const mag = document.querySelector('#star-mag').value; 121 | 122 | // Verify ra/dec/mag just number 123 | let coordinatesArr = []; 124 | const errorNumberCoordinatesTemplate = 125 | 'can only be numbers (0 to 9), dot (.) or negative sign (-) and spaces( ).
Examples:
RA 13h 03m 33.35sec should be 13 03 33.35
Dec -49° 31’ 38.1” should be -49 31 38.1'; 126 | 127 | coordinatesArr = [ra, dec, mag]; 128 | 129 | console.log(coordinatesArr); 130 | 131 | const pattern = /[^0-9 .-]/g; 132 | 133 | let errorsCorrdinates = 0; 134 | for (let i = 0; i < coordinatesArr.length; i++) { 135 | console.log('corriendo'); 136 | if (pattern.test(Number(coordinatesArr[i].replace(/[. ]/gi, '')))) { 137 | errorsCorrdinates++; 138 | } 139 | } 140 | 141 | document.querySelector('.corrdinatesValidation').innerHTML = 142 | 'You have ' + 143 | errorsCorrdinates + 144 | ' errors.
Please, check your coordinates and submit again.
Message: ra/dec/mag ' + 145 | errorNumberCoordinatesTemplate; 146 | 147 | document.querySelector('.corrdinatesValidationWrap').classList.add('show'); 148 | 149 | console.log(errorsCorrdinates); 150 | 151 | if (errorsCorrdinates > 0) { 152 | return; 153 | } else { 154 | document 155 | .querySelector('.corrdinatesValidationWrap') 156 | .classList.remove('show'); 157 | } 158 | 159 | web3.eth.getAccounts(function(error, accounts) { 160 | if (error) { 161 | console.log(error); 162 | return; 163 | } 164 | 165 | const account = accounts[0]; 166 | 167 | starNotary.createStar.sendTransaction(name, story, ra, dec, mag, function( 168 | error, 169 | result 170 | ) { 171 | document.querySelector('#star-answer').innerHTML = 172 | 'Loading... Please wait...!'; 173 | 174 | shortTx = result.substring(0, 4); 175 | 176 | if (!error) { 177 | particularSection = 178 | '
Great! Check your transaction here ' + 181 | shortTx + 182 | '...'; 183 | document.querySelector('#star-answer').innerHTML = basicTemplateCard( 184 | 'register', 185 | starImage, 186 | name, 187 | particularSection 188 | ); 189 | 190 | const starClaimedEvent = starNotary.Transfer({ from: account }); 191 | starClaimedEvent.watch(function(error, result) { 192 | console.log(result); 193 | if (!error) { 194 | console.log('If I see this Tx should be done!'); 195 | //location.reload(); 196 | } else { 197 | console.log('watching for star claimed event is failing'); 198 | } 199 | }); 200 | } else { 201 | console.log(error); 202 | } 203 | }); 204 | }); 205 | } 206 | 207 | function checkStarByTx() { 208 | web3.eth.getAccounts(function(error, accounts) { 209 | if (error) { 210 | console.log(error); 211 | return; 212 | } 213 | const account = accounts[0]; 214 | const tokenId = document.querySelector('#star-token').value; 215 | if (tokenId == '' || tokenId == ' ') { 216 | alert('Token should be something...!'); 217 | return; 218 | } 219 | if (!tokenId.match(/^[0-9]*$/gm)) { 220 | alert('Token should be a number...!'); 221 | return; 222 | } 223 | 224 | // We pass thecurrent window´s size 225 | const windoWidth = Math.max( 226 | document.documentElement.clientWidth, 227 | window.innerWidth || 0 228 | ); 229 | const windowHeight = Math.max( 230 | document.documentElement.clientHeight, 231 | window.innerHeight || 0 232 | ); 233 | 234 | // We are also going to pass starInfo: tokenId, ra, dec, mag 235 | starNotary.tokenIdToStarInfo.call(tokenId, function(error, result) { 236 | if (!error) { 237 | if (!result[0]) { 238 | document.querySelector('#star-show').innerHTML = 239 | 'Please, provide a valid token'; 240 | return; 241 | } 242 | 243 | console.log(result); 244 | 245 | const ra = result[2]; 246 | const dec = result[3]; 247 | const mag = result[4]; 248 | 249 | const secStarStory = 250 | '' + 251 | result[1] + 252 | ''; 253 | 254 | const secStarInfo = 255 | '
RA: ' + 256 | result[2] + 257 | ' DEC: ' + 258 | result[3] + 259 | ' MAG: ' + 260 | result[4] + 261 | '
'; 262 | 263 | particularSection = secStarStory; 264 | particularSection += secStarInfo; 265 | 266 | const yourStarImage = 'images/' + tokenId + '.png'; 267 | 268 | let starResponse = basicTemplateCard( 269 | 'retrieve', 270 | radar, 271 | result[0], 272 | particularSection 273 | ); 274 | 275 | document.querySelector('#star-show').innerHTML = starResponse; 276 | 277 | const url = '/star'; 278 | const data = { 279 | starInfo: { tokenId, ra, dec, mag }, 280 | userInfo: { windoWidth, windowHeight } 281 | }; 282 | 283 | fetch(url, { 284 | method: 'POST', // or 'PUT' 285 | body: JSON.stringify(data), // data can be `string` or {object}! 286 | headers: { 287 | 'Content-Type': 'application/json' 288 | } 289 | }) 290 | .then(res => res.json()) 291 | .then(response => { 292 | // It should retrieve something like: {"imagePath":"public/images/2.png"} 293 | //alert(JSON.stringify(response)); 294 | document.querySelector('.retrieve').src = response.imagePath; 295 | document.querySelector('.retrieve').onclick = function() { 296 | modalWindow.classList.add('is-active'); 297 | modalContent(showFullImage, response.imagePath); 298 | modalEventsForClosing(modalWindow, 'is-active'); 299 | }; 300 | }) 301 | //.then(response => console.log('Success:', JSON.stringify(response))) 302 | .catch(error => console.error('Error:', error)); 303 | } else { 304 | console.log(error); 305 | } 306 | }); 307 | 308 | /* 309 | document.querySelector('#star-show').innerHTML = 310 | 'Loading... Please wait...!'; 311 | */ 312 | }); 313 | } 314 | -------------------------------------------------------------------------------- /smart_contracts/public/ABI.js: -------------------------------------------------------------------------------- 1 | const ABI = [ 2 | { 3 | constant: true, 4 | inputs: [ 5 | { 6 | name: 'interfaceId', 7 | type: 'bytes4' 8 | } 9 | ], 10 | name: 'supportsInterface', 11 | outputs: [ 12 | { 13 | name: '', 14 | type: 'bool' 15 | } 16 | ], 17 | payable: false, 18 | stateMutability: 'view', 19 | type: 'function', 20 | signature: '0x01ffc9a7' 21 | }, 22 | { 23 | constant: true, 24 | inputs: [ 25 | { 26 | name: '', 27 | type: 'uint256' 28 | } 29 | ], 30 | name: 'starsForSale', 31 | outputs: [ 32 | { 33 | name: '', 34 | type: 'uint256' 35 | } 36 | ], 37 | payable: false, 38 | stateMutability: 'view', 39 | type: 'function', 40 | signature: '0x0564b130' 41 | }, 42 | { 43 | constant: true, 44 | inputs: [], 45 | name: 'tokenAt', 46 | outputs: [ 47 | { 48 | name: '', 49 | type: 'uint256' 50 | } 51 | ], 52 | payable: false, 53 | stateMutability: 'view', 54 | type: 'function', 55 | signature: '0x058df07b' 56 | }, 57 | { 58 | constant: true, 59 | inputs: [], 60 | name: 'name', 61 | outputs: [ 62 | { 63 | name: '', 64 | type: 'string' 65 | } 66 | ], 67 | payable: false, 68 | stateMutability: 'view', 69 | type: 'function', 70 | signature: '0x06fdde03' 71 | }, 72 | { 73 | constant: true, 74 | inputs: [ 75 | { 76 | name: 'tokenId', 77 | type: 'uint256' 78 | } 79 | ], 80 | name: 'getApproved', 81 | outputs: [ 82 | { 83 | name: '', 84 | type: 'address' 85 | } 86 | ], 87 | payable: false, 88 | stateMutability: 'view', 89 | type: 'function', 90 | signature: '0x081812fc' 91 | }, 92 | { 93 | constant: false, 94 | inputs: [ 95 | { 96 | name: 'to', 97 | type: 'address' 98 | }, 99 | { 100 | name: 'tokenId', 101 | type: 'uint256' 102 | } 103 | ], 104 | name: 'approve', 105 | outputs: [], 106 | payable: false, 107 | stateMutability: 'nonpayable', 108 | type: 'function', 109 | signature: '0x095ea7b3' 110 | }, 111 | { 112 | constant: false, 113 | inputs: [ 114 | { 115 | name: 'from', 116 | type: 'address' 117 | }, 118 | { 119 | name: 'to', 120 | type: 'address' 121 | }, 122 | { 123 | name: 'tokenId', 124 | type: 'uint256' 125 | } 126 | ], 127 | name: 'transferFrom', 128 | outputs: [], 129 | payable: false, 130 | stateMutability: 'nonpayable', 131 | type: 'function', 132 | signature: '0x23b872dd' 133 | }, 134 | { 135 | constant: false, 136 | inputs: [ 137 | { 138 | name: 'from', 139 | type: 'address' 140 | }, 141 | { 142 | name: 'to', 143 | type: 'address' 144 | }, 145 | { 146 | name: 'tokenId', 147 | type: 'uint256' 148 | } 149 | ], 150 | name: 'safeTransferFrom', 151 | outputs: [], 152 | payable: false, 153 | stateMutability: 'nonpayable', 154 | type: 'function', 155 | signature: '0x42842e0e' 156 | }, 157 | { 158 | constant: true, 159 | inputs: [ 160 | { 161 | name: 'tokenId', 162 | type: 'uint256' 163 | } 164 | ], 165 | name: 'ownerOf', 166 | outputs: [ 167 | { 168 | name: '', 169 | type: 'address' 170 | } 171 | ], 172 | payable: false, 173 | stateMutability: 'view', 174 | type: 'function', 175 | signature: '0x6352211e' 176 | }, 177 | { 178 | constant: true, 179 | inputs: [ 180 | { 181 | name: 'owner', 182 | type: 'address' 183 | } 184 | ], 185 | name: 'balanceOf', 186 | outputs: [ 187 | { 188 | name: '', 189 | type: 'uint256' 190 | } 191 | ], 192 | payable: false, 193 | stateMutability: 'view', 194 | type: 'function', 195 | signature: '0x70a08231' 196 | }, 197 | { 198 | constant: true, 199 | inputs: [], 200 | name: 'symbol', 201 | outputs: [ 202 | { 203 | name: '', 204 | type: 'string' 205 | } 206 | ], 207 | payable: false, 208 | stateMutability: 'view', 209 | type: 'function', 210 | signature: '0x95d89b41' 211 | }, 212 | { 213 | constant: false, 214 | inputs: [ 215 | { 216 | name: 'to', 217 | type: 'address' 218 | }, 219 | { 220 | name: 'approved', 221 | type: 'bool' 222 | } 223 | ], 224 | name: 'setApprovalForAll', 225 | outputs: [], 226 | payable: false, 227 | stateMutability: 'nonpayable', 228 | type: 'function', 229 | signature: '0xa22cb465' 230 | }, 231 | { 232 | constant: true, 233 | inputs: [ 234 | { 235 | name: '', 236 | type: 'bytes32' 237 | } 238 | ], 239 | name: 'unique', 240 | outputs: [ 241 | { 242 | name: '', 243 | type: 'bool' 244 | } 245 | ], 246 | payable: false, 247 | stateMutability: 'view', 248 | type: 'function', 249 | signature: '0xa98d03b2' 250 | }, 251 | { 252 | constant: false, 253 | inputs: [ 254 | { 255 | name: 'from', 256 | type: 'address' 257 | }, 258 | { 259 | name: 'to', 260 | type: 'address' 261 | }, 262 | { 263 | name: 'tokenId', 264 | type: 'uint256' 265 | }, 266 | { 267 | name: '_data', 268 | type: 'bytes' 269 | } 270 | ], 271 | name: 'safeTransferFrom', 272 | outputs: [], 273 | payable: false, 274 | stateMutability: 'nonpayable', 275 | type: 'function', 276 | signature: '0xb88d4fde' 277 | }, 278 | { 279 | constant: true, 280 | inputs: [ 281 | { 282 | name: 'tokenId', 283 | type: 'uint256' 284 | } 285 | ], 286 | name: 'tokenURI', 287 | outputs: [ 288 | { 289 | name: '', 290 | type: 'string' 291 | } 292 | ], 293 | payable: false, 294 | stateMutability: 'view', 295 | type: 'function', 296 | signature: '0xc87b56dd' 297 | }, 298 | { 299 | constant: true, 300 | inputs: [ 301 | { 302 | name: 'owner', 303 | type: 'address' 304 | }, 305 | { 306 | name: 'operator', 307 | type: 'address' 308 | } 309 | ], 310 | name: 'isApprovedForAll', 311 | outputs: [ 312 | { 313 | name: '', 314 | type: 'bool' 315 | } 316 | ], 317 | payable: false, 318 | stateMutability: 'view', 319 | type: 'function', 320 | signature: '0xe985e9c5' 321 | }, 322 | { 323 | inputs: [], 324 | payable: false, 325 | stateMutability: 'nonpayable', 326 | type: 'constructor', 327 | signature: 'constructor' 328 | }, 329 | { 330 | anonymous: false, 331 | inputs: [ 332 | { 333 | indexed: true, 334 | name: 'from', 335 | type: 'address' 336 | }, 337 | { 338 | indexed: true, 339 | name: 'to', 340 | type: 'address' 341 | }, 342 | { 343 | indexed: true, 344 | name: 'tokenId', 345 | type: 'uint256' 346 | } 347 | ], 348 | name: 'Transfer', 349 | type: 'event', 350 | signature: 351 | '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef' 352 | }, 353 | { 354 | anonymous: false, 355 | inputs: [ 356 | { 357 | indexed: true, 358 | name: 'owner', 359 | type: 'address' 360 | }, 361 | { 362 | indexed: true, 363 | name: 'approved', 364 | type: 'address' 365 | }, 366 | { 367 | indexed: true, 368 | name: 'tokenId', 369 | type: 'uint256' 370 | } 371 | ], 372 | name: 'Approval', 373 | type: 'event', 374 | signature: 375 | '0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925' 376 | }, 377 | { 378 | anonymous: false, 379 | inputs: [ 380 | { 381 | indexed: true, 382 | name: 'owner', 383 | type: 'address' 384 | }, 385 | { 386 | indexed: true, 387 | name: 'operator', 388 | type: 'address' 389 | }, 390 | { 391 | indexed: false, 392 | name: 'approved', 393 | type: 'bool' 394 | } 395 | ], 396 | name: 'ApprovalForAll', 397 | type: 'event', 398 | signature: 399 | '0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31' 400 | }, 401 | { 402 | constant: false, 403 | inputs: [ 404 | { 405 | name: 'starName', 406 | type: 'string' 407 | }, 408 | { 409 | name: 'starStory', 410 | type: 'string' 411 | }, 412 | { 413 | name: 'ra', 414 | type: 'string' 415 | }, 416 | { 417 | name: 'dec', 418 | type: 'string' 419 | }, 420 | { 421 | name: 'mag', 422 | type: 'string' 423 | } 424 | ], 425 | name: 'createStar', 426 | outputs: [], 427 | payable: false, 428 | stateMutability: 'nonpayable', 429 | type: 'function', 430 | signature: '0x5f3ff33c' 431 | }, 432 | { 433 | constant: true, 434 | inputs: [ 435 | { 436 | name: 'tokenId', 437 | type: 'uint256' 438 | } 439 | ], 440 | name: 'tokenIdToStarInfo', 441 | outputs: [ 442 | { 443 | name: '', 444 | type: 'string' 445 | }, 446 | { 447 | name: '', 448 | type: 'string' 449 | }, 450 | { 451 | name: '', 452 | type: 'string' 453 | }, 454 | { 455 | name: '', 456 | type: 'string' 457 | }, 458 | { 459 | name: '', 460 | type: 'string' 461 | } 462 | ], 463 | payable: false, 464 | stateMutability: 'view', 465 | type: 'function', 466 | signature: '0x1967fd98' 467 | }, 468 | { 469 | constant: true, 470 | inputs: [ 471 | { 472 | name: 'coordinates', 473 | type: 'bytes32' 474 | } 475 | ], 476 | name: 'checkIfStarExist', 477 | outputs: [ 478 | { 479 | name: '', 480 | type: 'bool' 481 | } 482 | ], 483 | payable: false, 484 | stateMutability: 'view', 485 | type: 'function', 486 | signature: '0x253d6390' 487 | }, 488 | { 489 | constant: true, 490 | inputs: [ 491 | { 492 | name: 'ra', 493 | type: 'string' 494 | }, 495 | { 496 | name: 'dec', 497 | type: 'string' 498 | }, 499 | { 500 | name: 'mag', 501 | type: 'string' 502 | } 503 | ], 504 | name: 'coordinatesToHash', 505 | outputs: [ 506 | { 507 | name: '', 508 | type: 'bytes32' 509 | } 510 | ], 511 | payable: false, 512 | stateMutability: 'pure', 513 | type: 'function', 514 | signature: '0xa2d1d2d1' 515 | }, 516 | { 517 | constant: false, 518 | inputs: [ 519 | { 520 | name: 'tokenId', 521 | type: 'uint256' 522 | }, 523 | { 524 | name: 'price', 525 | type: 'uint256' 526 | } 527 | ], 528 | name: 'putStarUpForSale', 529 | outputs: [], 530 | payable: false, 531 | stateMutability: 'nonpayable', 532 | type: 'function', 533 | signature: '0x316a4361' 534 | }, 535 | { 536 | constant: false, 537 | inputs: [ 538 | { 539 | name: 'tokenId', 540 | type: 'uint256' 541 | } 542 | ], 543 | name: 'buyStar', 544 | outputs: [], 545 | payable: true, 546 | stateMutability: 'payable', 547 | type: 'function', 548 | signature: '0x2f1c34ef' 549 | }, 550 | { 551 | constant: false, 552 | inputs: [ 553 | { 554 | name: 'tokenId', 555 | type: 'uint256' 556 | } 557 | ], 558 | name: 'mint', 559 | outputs: [], 560 | payable: false, 561 | stateMutability: 'nonpayable', 562 | type: 'function', 563 | signature: '0xa0712d68' 564 | }, 565 | { 566 | constant: false, 567 | inputs: [ 568 | { 569 | name: 'starOwner', 570 | type: 'address' 571 | }, 572 | { 573 | name: 'to', 574 | type: 'address' 575 | }, 576 | { 577 | name: 'tokenId', 578 | type: 'uint256' 579 | } 580 | ], 581 | name: 'transferStar', 582 | outputs: [], 583 | payable: false, 584 | stateMutability: 'nonpayable', 585 | type: 'function', 586 | signature: '0x37706d0c' 587 | }, 588 | { 589 | constant: false, 590 | inputs: [ 591 | { 592 | name: 'user1', 593 | type: 'address' 594 | }, 595 | { 596 | name: 'user1TokenId', 597 | type: 'uint256' 598 | }, 599 | { 600 | name: 'user2', 601 | type: 'address' 602 | }, 603 | { 604 | name: 'user2TokenId', 605 | type: 'uint256' 606 | } 607 | ], 608 | name: 'exchangeStars', 609 | outputs: [], 610 | payable: false, 611 | stateMutability: 'nonpayable', 612 | type: 'function', 613 | signature: '0x40f5ec88' 614 | } 615 | ]; 616 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Decentralized Star Notary 2 | 3 | [![Greenkeeper badge](https://badges.greenkeeper.io/alpersonalwebsite/erc721-smart-contract.svg)](https://greenkeeper.io/) 4 | [![License: MIT](https://img.shields.io/badge/License-MIT-brightgreen.svg)](https://opensource.org/licenses/MIT) 5 | 6 | # Objective 7 | 8 | Create and implement an [ERC-721](http://erc721.org/) smart contract with its proper UT and a basic UI to "Register" (Add) and "Retrieve" a "Digital Asset" (in this case, a Star) by its "Token ID". 9 | 10 | The contract should cover the following functionality: 11 | 12 | * Creation 13 | * Existence 14 | * Token to Asset (Star) 15 | * Transaction: Selling and Buying 16 | * Transferring 17 | * Exchanging 18 | 19 | # Installation 20 | 21 | ``` 22 | cd erc721-smart-contract 23 | cd smart_contracts 24 | npm install 25 | ``` 26 | 27 | _Note:_ [electron](https://github.com/electron) will be downloaded and installed. 28 | 29 | ## Deploying locally 30 | 31 | 1. Install `truffle` and `ganache-cli` globally 32 | 33 | ``` 34 | npm install --global truffle ganache-cli 35 | ``` 36 | 37 | 2. Open one terminal and execute 38 | 39 | ``` 40 | ganache-cli 41 | ``` 42 | 43 | Example result: 44 | 45 | ``` 46 | Ganache CLI v6.2.4 (ganache-core: 2.3.2) 47 | 48 | Available Accounts 49 | ================== 50 | (0) 0xdf224df7a6922a153c22f523a26e942703e7dc79 (~100 ETH) 51 | (1) 0x328be691a2ad0a713d8d59602e694fc9d01a852e (~100 ETH) 52 | (2) 0x5e96adbbada2e30f5857c4d35490c540790c6193 (~100 ETH) 53 | (3) 0x4d56d91a73de76189268dd082a2d6393dc981999 (~100 ETH) 54 | (4) 0x0949c6244925d6c77982ae5092f491f4d474454d (~100 ETH) 55 | (5) 0x6d35ab7ae32e86324a328823c3627dfdabca95c1 (~100 ETH) 56 | (6) 0xb0583f897ab701ac9f5671306c9eca297e7763f5 (~100 ETH) 57 | (7) 0x2f7ae937adc457a6eb15f7a30d040f51df270de2 (~100 ETH) 58 | (8) 0xec0208919d5c82cffe00c58228108af077564493 (~100 ETH) 59 | (9) 0x1f6bc2f37a5d70b737575fcf53b3aad7de09e77a (~100 ETH) 60 | 61 | Private Keys 62 | ================== 63 | (0) ...? 64 | (1) ...? 65 | (2) ...? 66 | (3) ...? 67 | (4) ...? 68 | (5) ...? 69 | (6) ...? 70 | (7) ...? 71 | (8) ...? 72 | (9) ...? 73 | 74 | HD Wallet 75 | ================== 76 | Mnemonic: ...? 77 | Base HD Path: m/44'/60'/0'/0/{account_index} 78 | 79 | Gas Price 80 | ================== 81 | 20000000000 82 | 83 | Gas Limit 84 | ================== 85 | 6721975 86 | 87 | Listening on 127.0.0.1:8545 88 | ``` 89 | 90 | 3. Open another terminal and execute 91 | 92 | ``` 93 | truffle develop 94 | ``` 95 | 96 | Example result: 97 | 98 | ``` 99 | Truffle Develop started at http://127.0.0.1:9545/ 100 | 101 | Accounts: 102 | (0) 0x931d4b4d410d9ceb89b35132df8b9da8d39eba98 103 | (1) 0xaf7aa7c665885743ba4e77e7e3dd591d737691ce 104 | (2) 0x7e62bab71d131ad7df42bf01293818347127fd18 105 | (3) 0xf65ecebee0e5978ddd1cbdda77b9b93d1b793218 106 | (4) 0xc472dfc3f7a2c5f27c5c7cf6ba8f2724c8c7e8b2 107 | (5) 0x168ecc20dac109c611abc1ff6a224a0d90996763 108 | (6) 0x04b9ba43bd087ca726b94f5f3e26982fcc654694 109 | (7) 0x6b5629613c795683fa1f88bcdd40b61ebcbbafb6 110 | (8) 0xfe0679a313ef4350c91e5077012b7bd306855519 111 | (9) 0x43c7e124bc58cf3f71e259c65ad4b44c4dea7cc8 112 | 113 | Private Keys: 114 | (0) ...? 115 | (1) ...? 116 | (2) ...? 117 | (3) ...? 118 | (4) ...? 119 | (5) ...? 120 | (6) ...? 121 | (7) ...? 122 | (8) ...? 123 | (9) ...? 124 | 125 | Mnemonic: ...? 126 | 127 | ⚠️ Important ⚠️ : This mnemonic was created for you by Truffle. It is not secure. 128 | Ensure you do not use it on production blockchains, or else you risk losing funds. 129 | 130 | truffle(develop)> 131 | ``` 132 | 133 | 4. In truffle´s terminal, run the tests 134 | 135 | ``` 136 | test 137 | ``` 138 | 139 | Expected output: 140 | 141 | ``` 142 | Using network 'develop'. 143 | 144 | Compiling .\contracts\StarNotary.sol... 145 | 146 | 147 | Contract: StarNotary 148 | Token information 149 | √ displays the right token name 150 | √ displays the right token symbol (833ms) 151 | Star Creation 152 | √ can create a star and retrieve its name (149ms) 153 | transfering Stars 154 | √ can transfer a star (109ms) 155 | buying and selling stars 156 | √ user1 can put up their star for sale (108ms) 157 | user2 can buy a star that was put up for sale 158 | √ user2 is the owner of the star after they buy it (90ms) 159 | √ user2 ether balance changed correctly (81ms) 160 | transfering tokens simultaneously between 2 owners 161 | √ changes token from user1 to user 2 and viceversa (117ms) 162 | Other functions tests... 163 | checkIfStarExist() 164 | √ verifies if a Star was registered 165 | mint() and ownerOf() 166 | √ verifies ownership after mint (68ms) 167 | approve() and getApproved() 168 | √ approves and get approved. (71ms) 169 | setApprovalForAll() 170 | √ sets approval for all (84ms) 171 | safeTransferFrom() 172 | √ can be transferred (91ms) 173 | 174 | 175 | 13 passing (4s) 176 | ``` 177 | 178 | 5. Compile executing 179 | 180 | ``` 181 | compile 182 | ``` 183 | 184 | Example output: 185 | 186 | ``` 187 | Compiling .\contracts\StarNotary.sol... 188 | Writing artifacts to .\build\contracts 189 | ``` 190 | 191 | 6. Migrate or deploy 192 | 193 | ``` 194 | migrate 195 | ``` 196 | 197 | Example output: 198 | 199 | ``` 200 | Starting migrations... 201 | ====================== 202 | > Network name: 'development' 203 | > Network id: 1546555279160 204 | > Block gas limit: 6721975 205 | 206 | 207 | 1_initial_migration.js 208 | ====================== 209 | 210 | Deploying 'Migrations' 211 | ---------------------- 212 | > transaction hash: 0xdac32349b8284c9cc1aa90804838c6c71a2e2430b3e1cb2e3158e6e9eaf2ecec 213 | - Blocks: 0 Seconds: 0 214 | > Blocks: 0 Seconds: 0 215 | > contract address: 0xfc9C8010288d57e7921F6Bd19458dBf6cdFa5Cbd 216 | > account: 0x1F5403BFd20157547f2A29FA2149dAE46195997B 217 | > balance: 99.99445076 218 | > gas used: 277462 219 | > gas price: 20 gwei 220 | > value sent: 0 ETH 221 | > total cost: 0.00554924 ETH 222 | 223 | 224 | - Saving migration to chain. 225 | > Saving migration to chain. 226 | > Saving artifacts 227 | ------------------------------------- 228 | > Total cost: 0.00554924 ETH 229 | 230 | 231 | 2_deploy_contracts.js 232 | ===================== 233 | 234 | Deploying 'StarNotary' 235 | ---------------------- 236 | > transaction hash: 0x55404c9144cba9d6db3f8df175a887f246c2332fa9546f41d54e5706a5f0fb45 237 | - Blocks: 0 Seconds: 0 238 | > Blocks: 0 Seconds: 0 239 | > contract address: 0x44810F8ABe4829bc59cE9a2E9018Dc2c9FEE4FE2 240 | > account: 0x1F5403BFd20157547f2A29FA2149dAE46195997B 241 | > balance: 99.92991134 242 | > gas used: 3184963 243 | > gas price: 20 gwei 244 | > value sent: 0 ETH 245 | > total cost: 0.06369926 ETH 246 | 247 | 248 | - Saving migration to chain. 249 | > Saving migration to chain. 250 | > Saving artifacts 251 | ------------------------------------- 252 | > Total cost: 0.06369926 ETH 253 | 254 | 255 | Summary 256 | ======= 257 | > Total deployments: 2 258 | > Final cost: 0.0692485 ETH 259 | ``` 260 | 261 | ## Deploying to Rinkeby 262 | 263 | In truffle´s terminal, execute: 264 | 265 | ``` 266 | migrate --reset --network rinkeby 267 | ``` 268 | 269 | Output: 270 | 271 | ``` 272 | Starting migrations... 273 | ====================== 274 | > Network name: 'rinkeby' 275 | > Network id: 4 276 | > Block gas limit: 7000000 277 | 278 | 279 | 1_initial_migration.js 280 | ====================== 281 | 282 | Deploying 'Migrations' 283 | ---------------------- 284 | > transaction hash: 0x4fda5a7d1a31b0c1c1039df7af5979f5d32cd11283773bd3339bd413ad54f045 285 | - Blocks: 0 Seconds: 0 286 | > Blocks: 0 Seconds: 4 287 | > contract address: 0x2eE13C495BE37d1A1aa3651B429c4be31f504cb4 288 | > account: 0x7f04D59435Eaf5059F038Cfb5448d8f8De0B8F56 289 | > balance: 18.468876334 290 | > gas used: 277462 291 | > gas price: 10 gwei 292 | > value sent: 0 ETH 293 | > total cost: 0.00277462 ETH 294 | 295 | 296 | - Saving migration to chain. 297 | > Saving migration to chain. 298 | > Saving artifacts 299 | ------------------------------------- 300 | > Total cost: 0.00277462 ETH 301 | 302 | 303 | 2_deploy_contracts.js 304 | ===================== 305 | 306 | Deploying 'StarNotary' 307 | ---------------------- 308 | > transaction hash: 0x3041fce7bdc905e64c86c7f3df8c1370dfe328e8a437a5eff0903070e14e9e73 309 | - Blocks: 0 Seconds: 0 310 | > Blocks: 0 Seconds: 12 311 | > contract address: 0xF7c71e77b4E0670019D4e4C89Be877428A25489d 312 | > account: 0x7f04D59435Eaf5059F038Cfb5448d8f8De0B8F56 313 | > balance: 18.436606624 314 | > gas used: 3184963 315 | > gas price: 10 gwei 316 | > value sent: 0 ETH 317 | > total cost: 0.03184963 ETH 318 | 319 | 320 | - Saving migration to chain. 321 | > Saving migration to chain. 322 | > Saving artifacts 323 | ------------------------------------- 324 | > Total cost: 0.03184963 ETH 325 | 326 | 327 | Summary 328 | ======= 329 | > Total deployments: 2 330 | > Final cost: 0.03462425 ETH 331 | ``` 332 | 333 | ### Contract address 334 | 335 | https://rinkeby.etherscan.io/address/0xF7c71e77b4E0670019D4e4C89Be877428A25489d 336 | 337 | (TxHash): `0x3041fce7bdc905e64c86c7f3df8c1370dfe328e8a437a5eff0903070e14e9e73` 338 | 339 | ### Open the Truffle´s console with rinkeby network... 340 | 341 | ``` 342 | truffle console --network rinkeby 343 | ``` 344 | 345 | ### Create Star 346 | 347 | ``` 348 | StarNotary.deployed().then(function(instance){myDapp = instance;}); 349 | ``` 350 | 351 | ``` 352 | myDapp.createStar("Star power 103!", "I love my wonderful star", "ra_032.155", "dec_121.874", "mag_245.978"); 353 | ``` 354 | 355 | Output: 356 | 357 | ``` 358 | { tx: '0xb02eed37b65a3eb19761847279a33bc0c8bca2f00288cf7a938d8cc29fba92ca', 359 | receipt: 360 | { blockHash: '0x104e71fab0766c23ea57585d789b0bee30947178835064c171cf826ccfad6d2f', 361 | blockNumber: 3628448, 362 | contractAddress: null, 363 | cumulativeGasUsed: 513284, 364 | from: '0x7f04d59435eaf5059f038cfb5448d8f8de0b8f56', 365 | gasUsed: 238860, 366 | logs: [ [Object] ], 367 | logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000002040000000000000000000000000008000000000000000000040000000000000000000000000000020000000000000000000800000000000000000000000010000000100000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000060000000000000000001000000000000000000000000000000000000000000400000', 368 | status: true, 369 | to: '0xf7c71e77b4e0670019d4e4c89be877428a25489d', 370 | transactionHash: '0xb02eed37b65a3eb19761847279a33bc0c8bca2f00288cf7a938d8cc29fba92ca', 371 | transactionIndex: 1, 372 | rawLogs: [ [Object] ] }, 373 | logs: 374 | [ { address: '0xF7c71e77b4E0670019D4e4C89Be877428A25489d', 375 | blockHash: '0x104e71fab0766c23ea57585d789b0bee30947178835064c171cf826ccfad6d2f', 376 | blockNumber: 3628448, 377 | logIndex: 1, 378 | removed: false, 379 | transactionHash: '0xb02eed37b65a3eb19761847279a33bc0c8bca2f00288cf7a938d8cc29fba92ca', 380 | transactionIndex: 1, 381 | id: 'log_b4013ce1', 382 | event: 'Transfer', 383 | args: [Object] } ] } 384 | ``` 385 | 386 | In Etherscan... 387 | https://rinkeby.etherscan.io/tx/0xb02eed37b65a3eb19761847279a33bc0c8bca2f00288cf7a938d8cc29fba92ca 388 | 389 | ### Put Star on Sale 390 | 391 | ``` 392 | myDapp.putStarUpForSale(1, 10000000000); 393 | ``` 394 | 395 | Output: 396 | 397 | ``` 398 | { tx: '0x57e5634cb70b31a60e1a5c559e38b417c12727c6e8b4a1492c1e491f51423f80', 399 | receipt: 400 | { blockHash: '0x63d503ea49e7b0cd55804c4a0de202637959daa32ce2164706ed67e6f26dfeac', 401 | blockNumber: 3628670, 402 | contractAddress: null, 403 | cumulativeGasUsed: 72449, 404 | from: '0x7f04d59435eaf5059f038cfb5448d8f8de0b8f56', 405 | gasUsed: 45043, 406 | logs: [], 407 | logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', 408 | status: true, 409 | to: '0xf7c71e77b4e0670019d4e4c89be877428a25489d', 410 | transactionHash: '0x57e5634cb70b31a60e1a5c559e38b417c12727c6e8b4a1492c1e491f51423f80', 411 | transactionIndex: 1, 412 | rawLogs: [] }, 413 | logs: [] } 414 | ``` 415 | 416 | In Etherscan... 417 | https://rinkeby.etherscan.io/tx/0x57e5634cb70b31a60e1a5c559e38b417c12727c6e8b4a1492c1e491f51423f80 418 | 419 | ### Retrieve token name 420 | 421 | ``` 422 | myDapp.name(); 423 | ``` 424 | 425 | Output: 426 | 427 | ``` 428 | 'UdaTokenName' 429 | ``` 430 | 431 | ### Retrieve token symbol 432 | 433 | ``` 434 | myDapp.symbol(); 435 | ``` 436 | 437 | Output: 438 | 439 | ``` 440 | 'USYMB' 441 | ``` 442 | 443 | ### Exchange stars 444 | 445 | I decided to go with one caller approach: we see user1 as a "source of trust". 446 | In a real environment, logic would be applied to verify that both users are transferring their assets before crediting one to either of them. 447 | First, be sure that both accounts have an Star. 448 | In my case, account1 has the Star with name "Star power 103!" and token 1. 449 | And, account2 has the Star with name "SuperStar" and token 3. 450 | 451 | ``` 452 | myDapp.exchangeStars('0x7f04D59435Eaf5059F038Cfb5448d8f8De0B8F56', 1, '0xb74E0d06B4C917A10E0744A14498047b1D1B7267', 3); 453 | ``` 454 | 455 | Output: 456 | 457 | ``` 458 | { tx: '0xaee0bb570cf51bbde91777705654830637e4503569445809da303e223d7f9bb7', 459 | receipt: 460 | { blockHash: '0xd812fe3312799c4230e966af31a283c143828544840adcb5728626613c313f3b', 461 | blockNumber: 3650085, 462 | contractAddress: null, 463 | cumulativeGasUsed: 149327, 464 | from: '0x7f04d59435eaf5059f038cfb5448d8f8de0b8f56', 465 | gasUsed: 75513, 466 | logs: [], 467 | logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', 468 | status: true, 469 | to: '0xf7c71e77b4e0670019d4e4c89be877428a25489d', 470 | transactionHash: '0xaee0bb570cf51bbde91777705654830637e4503569445809da303e223d7f9bb7', 471 | transactionIndex: 2, 472 | rawLogs: [] }, 473 | logs: [] } 474 | ``` 475 | 476 | ## Interact through the WebApp 477 | 478 | ### Launch the local server 479 | 480 | ``` 481 | node server 482 | ``` 483 | 484 | Alternatively, for development, you can run: `nodemon server` 485 | 486 | Then go to http://127.0.0.1:3000/ 487 | 488 | ### Check a Star by Token 489 | 490 | In Retrieve Star section, put your token. Example: **1** and click the button _Retrieve Star Data_. 491 | 492 | Output (if everything went well): 493 | 494 | ``` 495 | Name: Star power 103! 496 | Story: I love my wonderful star 497 | Coordinates 498 | RA: ra_032.155 499 | DEC: dec_121.874 500 | MAG: mag_245.978 501 | ``` 502 | 503 | ### Register Star 504 | 505 | Fill the inputs with your data. 506 | Example: 507 | 508 | ``` 509 | Name: Star 2 510 | Story: Great Star 511 | Coordinates 512 | RA: ra_2 513 | DEC: dec_2 514 | MAG: mag_2 515 | ``` 516 | 517 | Then, click in _Add Star_. 518 | 519 | MetaMask will ask to CONFIRM the operation. Please, click in _CONFIRM_ in the pop-up. 520 | After 15 seconds (+/-), you transaction should be confirmed. 521 | 522 | Output (if everything went well): 523 | https://rinkeby.etherscan.io/tx/0xc4c692d5ffadc29d7c061d1801bd9f7321a9a8bc39af9dec4f77ef5a0822a96d 524 | 525 | In `Tokens Transfered: From 0x0000000000000000000000000000000000000000To 0x7f04d59435eaf5059f038cfb5448d8f8de0b8f56For ERC-721 TokenID [5] ERC-721 Token` you can see the **TokedID**; in this case, **5**. You can use that token to retrieve the Star info. 526 | 527 | ## Exchanging tokens 528 | 529 | I tried to exchange tokens through MetaMask, failing both times: 530 | 531 | * https://rinkeby.etherscan.io/tx/0x77663844eb161a66bac70594b14316469dad1c47e22019bf5670486c53df2ed9 532 | * https://rinkeby.etherscan.io/tx/0x7e9ad711950fc9635f4cb68e7745e7159702b55366d3d2c9819e7d732f4964a9 533 | However, since `MetaMask` can only process `transfers` for `ERC-720` contracts, I ended using `MyEtherWallet`: https://www.myetherwallet.com/#contracts 534 | 535 | ## Author Notes 536 | 537 | For privacy... 538 | 539 | I´m deleting... 540 | 541 | 1. Personal configuration in `truffle-config.js` (infura link and seed) 542 | 2. The artifact or build output `build/contracts/*` since some of those files explicitly expose my OS username 543 | 544 | And excluding... 545 | 546 | 3. `node_modules/` I´m using openzeppelin-solidity so you can install the dependencies or check them in their repo. 547 | Example for ERC-721: https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-solidity/master/contracts/token/ERC721/ERC721.sol 548 | 549 | ## Useful resources 550 | 551 | * https://medium.com/coinmonks/the-many-ways-to-deploy-your-smart-contract-to-rinkeby-network-38cadf7b20be 552 | * https://medium.com/coinmonks/exploring-non-fungible-token-with-zeppelin-library-erc721-399cb180cfaf 553 | * https://github.com/trufflesuite/truffle-contract 554 | 555 | ## Credits 556 | 557 | * Background image: [Scenic View Of Rocky Mountain During Evening](https://www.pexels.com/photo/scenic-view-of-rocky-mountain-during-evening-1624438/) - By: Eberhard Grossgasteiger 558 | * Sky-map > Site Linker: http://server7.wikisky.org/SiteLinker_V1.0.html 559 | --------------------------------------------------------------------------------