├── .babelrc ├── .gitignore ├── README.md ├── contracts ├── Migrations.sol └── MyERC721.sol ├── migrations ├── 1_initial_migration.js └── 2_erc721_migration.js ├── package-lock.json ├── package.json ├── test └── erc721.spec.js ├── truffle-config.js └── truffle.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # A Simple ERC-721 Example 3 | 4 | What is it? 5 | 6 | ![](https://cdn-images-1.medium.com/max/2000/0*jr7S0JF8XiousKKz.png) 7 | 8 | ERC-721 tokens are a hot topic today with the advent of [crypto kitties](https://www.cryptokitties.co/) and a host of other digital collectibles spawned by its success. The [ERC-721 standard](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md) has gone through a couple iterations and is more or less in place now, so expect more and more players to enter this space. The basic premise of these [non-fungible tokens](https://en.wikipedia.org/wiki/Non-Fungible_Tokens) is that each token is unique and therefore cannot be exchanged on a 1:1 basis like an ERC20 token may be. There are many use cases where a unique tangible or digital asset may represented by these ERC-721 tokens, such as real estate, art, precious stones, etc. Actually, the digital collectible use case is probably the lowest market value use case of them all. 9 | 10 | ### **Purpose of this article** 11 | 12 | This article will attempt to create an ERC-721 in its simplest useful form using the [OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-solidity/tree/master/contracts/token/ERC721) implementation of the [ERC-21 standard](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md). I would recommend looking at the standard linked above in order to familiarize yourself with the requirements as they can be sometimes be hidden in the excellent OpenZeppelin implementations. There is also a fantastic article written [here](https://medium.com/blockchannel/walking-through-the-erc721-full-implementation-72ad72735f3c) that describes in-depth the ERC-721 spec. 13 | 14 | ### Setup the project 15 | 16 | Make sure that you have node, npm and truffle installed 17 | 18 | mkdir erc721 && cd erc721 && truffle init 19 | 20 | Replace the *truffle.js* content with the following: 21 | 22 | 23 | 24 | Install [Ganache](http://truffleframework.com/ganache/) and make sure it is running on 8545 25 | 26 | Compile and Migrate 27 | 28 | truffle compile 29 | truffle migrate 30 | 31 | Init the folder as an npm project 32 | 33 | npm init 34 | 35 | Install zeppelin dependency 36 | 37 | npm install zeppelin-solidity 38 | 39 | ### The Token 40 | 41 | Add the following to /contracts as *MyERC721.sol* 42 | 43 | 44 | 45 | Add the following migration to /migrations as *2_erc721_migration.js* 46 | 47 | 48 | 49 | Run the migrate script and verify that it deploys without errors 50 | 51 | Marks-MacBook-Pro:erc721 markmathis$ truffle migrate 52 | Using network 'development'. 53 | 54 | Running migration: 2_erc721_migration.js 55 | Deploying MyERC721... 56 | ... 57 | 0x25c26fd5b79b6328bee75bd34d78b37ff9389aad2d487600d46595adf0ee398d 58 | MyERC721: 0x6d9b92dfaf3cc3ae2e45b37b584f52f23bc03085 59 | Saving successful migration to network... 60 | ... 61 | 0x5c147afb3f4c2d225ef3b4cabcd7b9d3b5e4cbab88617fa150b061b85eb4cc0a 62 | Saving artifacts... 63 | 64 | ### Test it out 65 | 66 | Install test libraries 67 | 68 | npm install chai --save-dev 69 | npm install chai-as-promised --save-dev 70 | npm install babel-preset-es2015 --save-dev 71 | npm install babel-register --save-dev 72 | npm install babel-polyfill --save-dev 73 | 74 | Add *.babelrc *to project root 75 | 76 | { 77 | "presets": ["babel-preset-es2015"] 78 | } 79 | 80 | Add *erc721.spec.js* to /test 81 | 82 | 83 | 84 | Run the test from the project root 85 | 86 | truffle test 87 | 88 | ![](https://cdn-images-1.medium.com/max/3236/1*tZ9h1i5oaWrD8ISiGH5L8w.png) 89 | 90 | ### Addendum 91 | 92 | Pretty much all of the regular ERC20 functions are available in the ERC721 contracts. You can approve a third party to spend your token, burn tokens, etc. The functionality is the same, but the inputs and what happens under the covers is a bit more complex. There are a couple ERC721 methods that are not discussed in this article that are worth independent study as well 93 | 94 | * safeTransferFrom 95 | 96 | * isApprovedForAll 97 | 98 | * setApprovedForAll 99 | 100 | ### Summary 101 | 102 | Let’s recap what we learned in this article. First, we setup the project with truffle and imported our dependencies including the world class smart contract library, **OpenZeppelin. **Next, we coded our ERC721 token using the OpenZeppelin — *ERC721Token *template. We ran through a few checks to make sure our contract was valid and then we setup a mocha test to test our assertions. Our solidity code is deceptively simple and I would recommend a deeper dive into the ERC721 standard and the OpenZeppelin implementation. We accomplished our purpose in this article by creating a simple ERC721 token and you should be in a good place to continue your journey in learning this new token type. Even though this token was not ‘fungeable’ — I hope you still had ‘fun’ :) 103 | 104 | *Full project source is available* — [here](https://github.com/cipherzzz/erc721) 105 | -------------------------------------------------------------------------------- /contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 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 | -------------------------------------------------------------------------------- /contracts/MyERC721.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 2 | 3 | import "../node_modules/zeppelin-solidity/contracts/token/ERC721/ERC721Token.sol"; 4 | 5 | contract MyERC721 is ERC721Token { 6 | constructor (string _name, string _symbol) public 7 | ERC721Token(_name, _symbol) 8 | { 9 | } 10 | 11 | /** 12 | * Custom accessor to create a unique token 13 | */ 14 | function mintUniqueTokenTo( 15 | address _to, 16 | uint256 _tokenId, 17 | string _tokenURI 18 | ) public 19 | { 20 | super._mint(_to, _tokenId); 21 | super._setTokenURI(_tokenId, _tokenURI); 22 | } 23 | } -------------------------------------------------------------------------------- /migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require("./Migrations.sol"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /migrations/2_erc721_migration.js: -------------------------------------------------------------------------------- 1 | const MyERC721 = artifacts.require("./MyERC721.sol"); 2 | 3 | module.exports = async function(deployer) { 4 | await deployer.deploy(MyERC721, "MyERC721", "MyERC721") 5 | const erc721 = await MyERC721.deployed() 6 | }; 7 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "erc721", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "ansi-regex": { 8 | "version": "2.1.1", 9 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 10 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", 11 | "dev": true 12 | }, 13 | "ansi-styles": { 14 | "version": "2.2.1", 15 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", 16 | "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", 17 | "dev": true 18 | }, 19 | "assertion-error": { 20 | "version": "1.1.0", 21 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", 22 | "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==" 23 | }, 24 | "babel-code-frame": { 25 | "version": "6.26.0", 26 | "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", 27 | "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", 28 | "dev": true, 29 | "requires": { 30 | "chalk": "1.1.3", 31 | "esutils": "2.0.2", 32 | "js-tokens": "3.0.2" 33 | } 34 | }, 35 | "babel-core": { 36 | "version": "6.26.3", 37 | "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", 38 | "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", 39 | "dev": true, 40 | "requires": { 41 | "babel-code-frame": "6.26.0", 42 | "babel-generator": "6.26.1", 43 | "babel-helpers": "6.24.1", 44 | "babel-messages": "6.23.0", 45 | "babel-register": "6.26.0", 46 | "babel-runtime": "6.26.0", 47 | "babel-template": "6.26.0", 48 | "babel-traverse": "6.26.0", 49 | "babel-types": "6.26.0", 50 | "babylon": "6.18.0", 51 | "convert-source-map": "1.5.1", 52 | "debug": "2.6.9", 53 | "json5": "0.5.1", 54 | "lodash": "4.17.10", 55 | "minimatch": "3.0.4", 56 | "path-is-absolute": "1.0.1", 57 | "private": "0.1.8", 58 | "slash": "1.0.0", 59 | "source-map": "0.5.7" 60 | } 61 | }, 62 | "babel-generator": { 63 | "version": "6.26.1", 64 | "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", 65 | "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", 66 | "dev": true, 67 | "requires": { 68 | "babel-messages": "6.23.0", 69 | "babel-runtime": "6.26.0", 70 | "babel-types": "6.26.0", 71 | "detect-indent": "4.0.0", 72 | "jsesc": "1.3.0", 73 | "lodash": "4.17.10", 74 | "source-map": "0.5.7", 75 | "trim-right": "1.0.1" 76 | } 77 | }, 78 | "babel-helper-call-delegate": { 79 | "version": "6.24.1", 80 | "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", 81 | "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", 82 | "dev": true, 83 | "requires": { 84 | "babel-helper-hoist-variables": "6.24.1", 85 | "babel-runtime": "6.26.0", 86 | "babel-traverse": "6.26.0", 87 | "babel-types": "6.26.0" 88 | } 89 | }, 90 | "babel-helper-define-map": { 91 | "version": "6.26.0", 92 | "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", 93 | "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", 94 | "dev": true, 95 | "requires": { 96 | "babel-helper-function-name": "6.24.1", 97 | "babel-runtime": "6.26.0", 98 | "babel-types": "6.26.0", 99 | "lodash": "4.17.10" 100 | } 101 | }, 102 | "babel-helper-function-name": { 103 | "version": "6.24.1", 104 | "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", 105 | "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", 106 | "dev": true, 107 | "requires": { 108 | "babel-helper-get-function-arity": "6.24.1", 109 | "babel-runtime": "6.26.0", 110 | "babel-template": "6.26.0", 111 | "babel-traverse": "6.26.0", 112 | "babel-types": "6.26.0" 113 | } 114 | }, 115 | "babel-helper-get-function-arity": { 116 | "version": "6.24.1", 117 | "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", 118 | "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", 119 | "dev": true, 120 | "requires": { 121 | "babel-runtime": "6.26.0", 122 | "babel-types": "6.26.0" 123 | } 124 | }, 125 | "babel-helper-hoist-variables": { 126 | "version": "6.24.1", 127 | "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", 128 | "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", 129 | "dev": true, 130 | "requires": { 131 | "babel-runtime": "6.26.0", 132 | "babel-types": "6.26.0" 133 | } 134 | }, 135 | "babel-helper-optimise-call-expression": { 136 | "version": "6.24.1", 137 | "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", 138 | "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", 139 | "dev": true, 140 | "requires": { 141 | "babel-runtime": "6.26.0", 142 | "babel-types": "6.26.0" 143 | } 144 | }, 145 | "babel-helper-regex": { 146 | "version": "6.26.0", 147 | "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", 148 | "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", 149 | "dev": true, 150 | "requires": { 151 | "babel-runtime": "6.26.0", 152 | "babel-types": "6.26.0", 153 | "lodash": "4.17.10" 154 | } 155 | }, 156 | "babel-helper-replace-supers": { 157 | "version": "6.24.1", 158 | "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", 159 | "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", 160 | "dev": true, 161 | "requires": { 162 | "babel-helper-optimise-call-expression": "6.24.1", 163 | "babel-messages": "6.23.0", 164 | "babel-runtime": "6.26.0", 165 | "babel-template": "6.26.0", 166 | "babel-traverse": "6.26.0", 167 | "babel-types": "6.26.0" 168 | } 169 | }, 170 | "babel-helpers": { 171 | "version": "6.24.1", 172 | "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", 173 | "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", 174 | "dev": true, 175 | "requires": { 176 | "babel-runtime": "6.26.0", 177 | "babel-template": "6.26.0" 178 | } 179 | }, 180 | "babel-messages": { 181 | "version": "6.23.0", 182 | "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", 183 | "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", 184 | "dev": true, 185 | "requires": { 186 | "babel-runtime": "6.26.0" 187 | } 188 | }, 189 | "babel-plugin-check-es2015-constants": { 190 | "version": "6.22.0", 191 | "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", 192 | "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", 193 | "dev": true, 194 | "requires": { 195 | "babel-runtime": "6.26.0" 196 | } 197 | }, 198 | "babel-plugin-transform-es2015-arrow-functions": { 199 | "version": "6.22.0", 200 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", 201 | "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", 202 | "dev": true, 203 | "requires": { 204 | "babel-runtime": "6.26.0" 205 | } 206 | }, 207 | "babel-plugin-transform-es2015-block-scoped-functions": { 208 | "version": "6.22.0", 209 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", 210 | "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", 211 | "dev": true, 212 | "requires": { 213 | "babel-runtime": "6.26.0" 214 | } 215 | }, 216 | "babel-plugin-transform-es2015-block-scoping": { 217 | "version": "6.26.0", 218 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", 219 | "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", 220 | "dev": true, 221 | "requires": { 222 | "babel-runtime": "6.26.0", 223 | "babel-template": "6.26.0", 224 | "babel-traverse": "6.26.0", 225 | "babel-types": "6.26.0", 226 | "lodash": "4.17.10" 227 | } 228 | }, 229 | "babel-plugin-transform-es2015-classes": { 230 | "version": "6.24.1", 231 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", 232 | "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", 233 | "dev": true, 234 | "requires": { 235 | "babel-helper-define-map": "6.26.0", 236 | "babel-helper-function-name": "6.24.1", 237 | "babel-helper-optimise-call-expression": "6.24.1", 238 | "babel-helper-replace-supers": "6.24.1", 239 | "babel-messages": "6.23.0", 240 | "babel-runtime": "6.26.0", 241 | "babel-template": "6.26.0", 242 | "babel-traverse": "6.26.0", 243 | "babel-types": "6.26.0" 244 | } 245 | }, 246 | "babel-plugin-transform-es2015-computed-properties": { 247 | "version": "6.24.1", 248 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", 249 | "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", 250 | "dev": true, 251 | "requires": { 252 | "babel-runtime": "6.26.0", 253 | "babel-template": "6.26.0" 254 | } 255 | }, 256 | "babel-plugin-transform-es2015-destructuring": { 257 | "version": "6.23.0", 258 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", 259 | "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", 260 | "dev": true, 261 | "requires": { 262 | "babel-runtime": "6.26.0" 263 | } 264 | }, 265 | "babel-plugin-transform-es2015-duplicate-keys": { 266 | "version": "6.24.1", 267 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", 268 | "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", 269 | "dev": true, 270 | "requires": { 271 | "babel-runtime": "6.26.0", 272 | "babel-types": "6.26.0" 273 | } 274 | }, 275 | "babel-plugin-transform-es2015-for-of": { 276 | "version": "6.23.0", 277 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", 278 | "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", 279 | "dev": true, 280 | "requires": { 281 | "babel-runtime": "6.26.0" 282 | } 283 | }, 284 | "babel-plugin-transform-es2015-function-name": { 285 | "version": "6.24.1", 286 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", 287 | "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", 288 | "dev": true, 289 | "requires": { 290 | "babel-helper-function-name": "6.24.1", 291 | "babel-runtime": "6.26.0", 292 | "babel-types": "6.26.0" 293 | } 294 | }, 295 | "babel-plugin-transform-es2015-literals": { 296 | "version": "6.22.0", 297 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", 298 | "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", 299 | "dev": true, 300 | "requires": { 301 | "babel-runtime": "6.26.0" 302 | } 303 | }, 304 | "babel-plugin-transform-es2015-modules-amd": { 305 | "version": "6.24.1", 306 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", 307 | "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", 308 | "dev": true, 309 | "requires": { 310 | "babel-plugin-transform-es2015-modules-commonjs": "6.26.2", 311 | "babel-runtime": "6.26.0", 312 | "babel-template": "6.26.0" 313 | } 314 | }, 315 | "babel-plugin-transform-es2015-modules-commonjs": { 316 | "version": "6.26.2", 317 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", 318 | "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", 319 | "dev": true, 320 | "requires": { 321 | "babel-plugin-transform-strict-mode": "6.24.1", 322 | "babel-runtime": "6.26.0", 323 | "babel-template": "6.26.0", 324 | "babel-types": "6.26.0" 325 | } 326 | }, 327 | "babel-plugin-transform-es2015-modules-systemjs": { 328 | "version": "6.24.1", 329 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", 330 | "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", 331 | "dev": true, 332 | "requires": { 333 | "babel-helper-hoist-variables": "6.24.1", 334 | "babel-runtime": "6.26.0", 335 | "babel-template": "6.26.0" 336 | } 337 | }, 338 | "babel-plugin-transform-es2015-modules-umd": { 339 | "version": "6.24.1", 340 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", 341 | "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", 342 | "dev": true, 343 | "requires": { 344 | "babel-plugin-transform-es2015-modules-amd": "6.24.1", 345 | "babel-runtime": "6.26.0", 346 | "babel-template": "6.26.0" 347 | } 348 | }, 349 | "babel-plugin-transform-es2015-object-super": { 350 | "version": "6.24.1", 351 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", 352 | "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", 353 | "dev": true, 354 | "requires": { 355 | "babel-helper-replace-supers": "6.24.1", 356 | "babel-runtime": "6.26.0" 357 | } 358 | }, 359 | "babel-plugin-transform-es2015-parameters": { 360 | "version": "6.24.1", 361 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", 362 | "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", 363 | "dev": true, 364 | "requires": { 365 | "babel-helper-call-delegate": "6.24.1", 366 | "babel-helper-get-function-arity": "6.24.1", 367 | "babel-runtime": "6.26.0", 368 | "babel-template": "6.26.0", 369 | "babel-traverse": "6.26.0", 370 | "babel-types": "6.26.0" 371 | } 372 | }, 373 | "babel-plugin-transform-es2015-shorthand-properties": { 374 | "version": "6.24.1", 375 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", 376 | "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", 377 | "dev": true, 378 | "requires": { 379 | "babel-runtime": "6.26.0", 380 | "babel-types": "6.26.0" 381 | } 382 | }, 383 | "babel-plugin-transform-es2015-spread": { 384 | "version": "6.22.0", 385 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", 386 | "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", 387 | "dev": true, 388 | "requires": { 389 | "babel-runtime": "6.26.0" 390 | } 391 | }, 392 | "babel-plugin-transform-es2015-sticky-regex": { 393 | "version": "6.24.1", 394 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", 395 | "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", 396 | "dev": true, 397 | "requires": { 398 | "babel-helper-regex": "6.26.0", 399 | "babel-runtime": "6.26.0", 400 | "babel-types": "6.26.0" 401 | } 402 | }, 403 | "babel-plugin-transform-es2015-template-literals": { 404 | "version": "6.22.0", 405 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", 406 | "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", 407 | "dev": true, 408 | "requires": { 409 | "babel-runtime": "6.26.0" 410 | } 411 | }, 412 | "babel-plugin-transform-es2015-typeof-symbol": { 413 | "version": "6.23.0", 414 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", 415 | "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", 416 | "dev": true, 417 | "requires": { 418 | "babel-runtime": "6.26.0" 419 | } 420 | }, 421 | "babel-plugin-transform-es2015-unicode-regex": { 422 | "version": "6.24.1", 423 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", 424 | "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", 425 | "dev": true, 426 | "requires": { 427 | "babel-helper-regex": "6.26.0", 428 | "babel-runtime": "6.26.0", 429 | "regexpu-core": "2.0.0" 430 | } 431 | }, 432 | "babel-plugin-transform-regenerator": { 433 | "version": "6.26.0", 434 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", 435 | "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", 436 | "dev": true, 437 | "requires": { 438 | "regenerator-transform": "0.10.1" 439 | } 440 | }, 441 | "babel-plugin-transform-strict-mode": { 442 | "version": "6.24.1", 443 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", 444 | "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", 445 | "dev": true, 446 | "requires": { 447 | "babel-runtime": "6.26.0", 448 | "babel-types": "6.26.0" 449 | } 450 | }, 451 | "babel-polyfill": { 452 | "version": "6.26.0", 453 | "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", 454 | "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=", 455 | "dev": true, 456 | "requires": { 457 | "babel-runtime": "6.26.0", 458 | "core-js": "2.5.7", 459 | "regenerator-runtime": "0.10.5" 460 | } 461 | }, 462 | "babel-preset-es2015": { 463 | "version": "6.24.1", 464 | "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz", 465 | "integrity": "sha1-1EBQ1rwsn+6nAqrzjXJ6AhBTiTk=", 466 | "dev": true, 467 | "requires": { 468 | "babel-plugin-check-es2015-constants": "6.22.0", 469 | "babel-plugin-transform-es2015-arrow-functions": "6.22.0", 470 | "babel-plugin-transform-es2015-block-scoped-functions": "6.22.0", 471 | "babel-plugin-transform-es2015-block-scoping": "6.26.0", 472 | "babel-plugin-transform-es2015-classes": "6.24.1", 473 | "babel-plugin-transform-es2015-computed-properties": "6.24.1", 474 | "babel-plugin-transform-es2015-destructuring": "6.23.0", 475 | "babel-plugin-transform-es2015-duplicate-keys": "6.24.1", 476 | "babel-plugin-transform-es2015-for-of": "6.23.0", 477 | "babel-plugin-transform-es2015-function-name": "6.24.1", 478 | "babel-plugin-transform-es2015-literals": "6.22.0", 479 | "babel-plugin-transform-es2015-modules-amd": "6.24.1", 480 | "babel-plugin-transform-es2015-modules-commonjs": "6.26.2", 481 | "babel-plugin-transform-es2015-modules-systemjs": "6.24.1", 482 | "babel-plugin-transform-es2015-modules-umd": "6.24.1", 483 | "babel-plugin-transform-es2015-object-super": "6.24.1", 484 | "babel-plugin-transform-es2015-parameters": "6.24.1", 485 | "babel-plugin-transform-es2015-shorthand-properties": "6.24.1", 486 | "babel-plugin-transform-es2015-spread": "6.22.0", 487 | "babel-plugin-transform-es2015-sticky-regex": "6.24.1", 488 | "babel-plugin-transform-es2015-template-literals": "6.22.0", 489 | "babel-plugin-transform-es2015-typeof-symbol": "6.23.0", 490 | "babel-plugin-transform-es2015-unicode-regex": "6.24.1", 491 | "babel-plugin-transform-regenerator": "6.26.0" 492 | } 493 | }, 494 | "babel-register": { 495 | "version": "6.26.0", 496 | "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", 497 | "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", 498 | "dev": true, 499 | "requires": { 500 | "babel-core": "6.26.3", 501 | "babel-runtime": "6.26.0", 502 | "core-js": "2.5.7", 503 | "home-or-tmp": "2.0.0", 504 | "lodash": "4.17.10", 505 | "mkdirp": "0.5.1", 506 | "source-map-support": "0.4.18" 507 | } 508 | }, 509 | "babel-runtime": { 510 | "version": "6.26.0", 511 | "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", 512 | "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", 513 | "dev": true, 514 | "requires": { 515 | "core-js": "2.5.7", 516 | "regenerator-runtime": "0.11.1" 517 | }, 518 | "dependencies": { 519 | "regenerator-runtime": { 520 | "version": "0.11.1", 521 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", 522 | "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", 523 | "dev": true 524 | } 525 | } 526 | }, 527 | "babel-template": { 528 | "version": "6.26.0", 529 | "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", 530 | "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", 531 | "dev": true, 532 | "requires": { 533 | "babel-runtime": "6.26.0", 534 | "babel-traverse": "6.26.0", 535 | "babel-types": "6.26.0", 536 | "babylon": "6.18.0", 537 | "lodash": "4.17.10" 538 | } 539 | }, 540 | "babel-traverse": { 541 | "version": "6.26.0", 542 | "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", 543 | "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", 544 | "dev": true, 545 | "requires": { 546 | "babel-code-frame": "6.26.0", 547 | "babel-messages": "6.23.0", 548 | "babel-runtime": "6.26.0", 549 | "babel-types": "6.26.0", 550 | "babylon": "6.18.0", 551 | "debug": "2.6.9", 552 | "globals": "9.18.0", 553 | "invariant": "2.2.4", 554 | "lodash": "4.17.10" 555 | } 556 | }, 557 | "babel-types": { 558 | "version": "6.26.0", 559 | "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", 560 | "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", 561 | "dev": true, 562 | "requires": { 563 | "babel-runtime": "6.26.0", 564 | "esutils": "2.0.2", 565 | "lodash": "4.17.10", 566 | "to-fast-properties": "1.0.3" 567 | } 568 | }, 569 | "babylon": { 570 | "version": "6.18.0", 571 | "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", 572 | "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", 573 | "dev": true 574 | }, 575 | "balanced-match": { 576 | "version": "1.0.0", 577 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 578 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 579 | "dev": true 580 | }, 581 | "brace-expansion": { 582 | "version": "1.1.11", 583 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 584 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 585 | "dev": true, 586 | "requires": { 587 | "balanced-match": "1.0.0", 588 | "concat-map": "0.0.1" 589 | } 590 | }, 591 | "chai": { 592 | "version": "4.1.2", 593 | "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz", 594 | "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=", 595 | "requires": { 596 | "assertion-error": "1.1.0", 597 | "check-error": "1.0.2", 598 | "deep-eql": "3.0.1", 599 | "get-func-name": "2.0.0", 600 | "pathval": "1.1.0", 601 | "type-detect": "4.0.8" 602 | } 603 | }, 604 | "chai-as-promised": { 605 | "version": "7.1.1", 606 | "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", 607 | "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", 608 | "requires": { 609 | "check-error": "1.0.2" 610 | } 611 | }, 612 | "chalk": { 613 | "version": "1.1.3", 614 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", 615 | "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", 616 | "dev": true, 617 | "requires": { 618 | "ansi-styles": "2.2.1", 619 | "escape-string-regexp": "1.0.5", 620 | "has-ansi": "2.0.0", 621 | "strip-ansi": "3.0.1", 622 | "supports-color": "2.0.0" 623 | } 624 | }, 625 | "check-error": { 626 | "version": "1.0.2", 627 | "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", 628 | "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=" 629 | }, 630 | "concat-map": { 631 | "version": "0.0.1", 632 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 633 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 634 | "dev": true 635 | }, 636 | "convert-source-map": { 637 | "version": "1.5.1", 638 | "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", 639 | "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=", 640 | "dev": true 641 | }, 642 | "core-js": { 643 | "version": "2.5.7", 644 | "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", 645 | "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==", 646 | "dev": true 647 | }, 648 | "debug": { 649 | "version": "2.6.9", 650 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 651 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 652 | "dev": true, 653 | "requires": { 654 | "ms": "2.0.0" 655 | } 656 | }, 657 | "deep-eql": { 658 | "version": "3.0.1", 659 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", 660 | "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", 661 | "requires": { 662 | "type-detect": "4.0.8" 663 | } 664 | }, 665 | "detect-indent": { 666 | "version": "4.0.0", 667 | "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", 668 | "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", 669 | "dev": true, 670 | "requires": { 671 | "repeating": "2.0.1" 672 | } 673 | }, 674 | "escape-string-regexp": { 675 | "version": "1.0.5", 676 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 677 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 678 | "dev": true 679 | }, 680 | "esutils": { 681 | "version": "2.0.2", 682 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", 683 | "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", 684 | "dev": true 685 | }, 686 | "get-func-name": { 687 | "version": "2.0.0", 688 | "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", 689 | "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=" 690 | }, 691 | "globals": { 692 | "version": "9.18.0", 693 | "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", 694 | "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", 695 | "dev": true 696 | }, 697 | "has-ansi": { 698 | "version": "2.0.0", 699 | "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", 700 | "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", 701 | "dev": true, 702 | "requires": { 703 | "ansi-regex": "2.1.1" 704 | } 705 | }, 706 | "home-or-tmp": { 707 | "version": "2.0.0", 708 | "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", 709 | "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", 710 | "dev": true, 711 | "requires": { 712 | "os-homedir": "1.0.2", 713 | "os-tmpdir": "1.0.2" 714 | } 715 | }, 716 | "invariant": { 717 | "version": "2.2.4", 718 | "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", 719 | "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", 720 | "dev": true, 721 | "requires": { 722 | "loose-envify": "1.3.1" 723 | } 724 | }, 725 | "is-finite": { 726 | "version": "1.0.2", 727 | "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", 728 | "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", 729 | "dev": true, 730 | "requires": { 731 | "number-is-nan": "1.0.1" 732 | } 733 | }, 734 | "js-tokens": { 735 | "version": "3.0.2", 736 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", 737 | "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", 738 | "dev": true 739 | }, 740 | "jsesc": { 741 | "version": "1.3.0", 742 | "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", 743 | "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", 744 | "dev": true 745 | }, 746 | "json5": { 747 | "version": "0.5.1", 748 | "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", 749 | "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", 750 | "dev": true 751 | }, 752 | "lodash": { 753 | "version": "4.17.10", 754 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", 755 | "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", 756 | "dev": true 757 | }, 758 | "loose-envify": { 759 | "version": "1.3.1", 760 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", 761 | "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", 762 | "dev": true, 763 | "requires": { 764 | "js-tokens": "3.0.2" 765 | } 766 | }, 767 | "minimatch": { 768 | "version": "3.0.4", 769 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 770 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 771 | "dev": true, 772 | "requires": { 773 | "brace-expansion": "1.1.11" 774 | } 775 | }, 776 | "minimist": { 777 | "version": "0.0.8", 778 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 779 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 780 | "dev": true 781 | }, 782 | "mkdirp": { 783 | "version": "0.5.1", 784 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 785 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 786 | "dev": true, 787 | "requires": { 788 | "minimist": "0.0.8" 789 | } 790 | }, 791 | "ms": { 792 | "version": "2.0.0", 793 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 794 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 795 | "dev": true 796 | }, 797 | "number-is-nan": { 798 | "version": "1.0.1", 799 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", 800 | "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", 801 | "dev": true 802 | }, 803 | "os-homedir": { 804 | "version": "1.0.2", 805 | "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", 806 | "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", 807 | "dev": true 808 | }, 809 | "os-tmpdir": { 810 | "version": "1.0.2", 811 | "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", 812 | "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", 813 | "dev": true 814 | }, 815 | "path-is-absolute": { 816 | "version": "1.0.1", 817 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 818 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 819 | "dev": true 820 | }, 821 | "pathval": { 822 | "version": "1.1.0", 823 | "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", 824 | "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=" 825 | }, 826 | "private": { 827 | "version": "0.1.8", 828 | "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", 829 | "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", 830 | "dev": true 831 | }, 832 | "regenerate": { 833 | "version": "1.4.0", 834 | "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", 835 | "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", 836 | "dev": true 837 | }, 838 | "regenerator-runtime": { 839 | "version": "0.10.5", 840 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", 841 | "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=", 842 | "dev": true 843 | }, 844 | "regenerator-transform": { 845 | "version": "0.10.1", 846 | "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", 847 | "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", 848 | "dev": true, 849 | "requires": { 850 | "babel-runtime": "6.26.0", 851 | "babel-types": "6.26.0", 852 | "private": "0.1.8" 853 | } 854 | }, 855 | "regexpu-core": { 856 | "version": "2.0.0", 857 | "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", 858 | "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", 859 | "dev": true, 860 | "requires": { 861 | "regenerate": "1.4.0", 862 | "regjsgen": "0.2.0", 863 | "regjsparser": "0.1.5" 864 | } 865 | }, 866 | "regjsgen": { 867 | "version": "0.2.0", 868 | "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", 869 | "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", 870 | "dev": true 871 | }, 872 | "regjsparser": { 873 | "version": "0.1.5", 874 | "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", 875 | "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", 876 | "dev": true, 877 | "requires": { 878 | "jsesc": "0.5.0" 879 | }, 880 | "dependencies": { 881 | "jsesc": { 882 | "version": "0.5.0", 883 | "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", 884 | "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", 885 | "dev": true 886 | } 887 | } 888 | }, 889 | "repeating": { 890 | "version": "2.0.1", 891 | "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", 892 | "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", 893 | "dev": true, 894 | "requires": { 895 | "is-finite": "1.0.2" 896 | } 897 | }, 898 | "slash": { 899 | "version": "1.0.0", 900 | "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", 901 | "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", 902 | "dev": true 903 | }, 904 | "source-map": { 905 | "version": "0.5.7", 906 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", 907 | "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", 908 | "dev": true 909 | }, 910 | "source-map-support": { 911 | "version": "0.4.18", 912 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", 913 | "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", 914 | "dev": true, 915 | "requires": { 916 | "source-map": "0.5.7" 917 | } 918 | }, 919 | "strip-ansi": { 920 | "version": "3.0.1", 921 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 922 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 923 | "dev": true, 924 | "requires": { 925 | "ansi-regex": "2.1.1" 926 | } 927 | }, 928 | "supports-color": { 929 | "version": "2.0.0", 930 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", 931 | "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", 932 | "dev": true 933 | }, 934 | "to-fast-properties": { 935 | "version": "1.0.3", 936 | "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", 937 | "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", 938 | "dev": true 939 | }, 940 | "trim-right": { 941 | "version": "1.0.1", 942 | "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", 943 | "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", 944 | "dev": true 945 | }, 946 | "type-detect": { 947 | "version": "4.0.8", 948 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", 949 | "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" 950 | }, 951 | "zeppelin-solidity": { 952 | "version": "1.10.0", 953 | "resolved": "https://registry.npmjs.org/zeppelin-solidity/-/zeppelin-solidity-1.10.0.tgz", 954 | "integrity": "sha512-+7vO3ltfNdCJ8E8NKFlC0PKQUh9PTFm71o1u0vgZdJ0U20NTMRkYx/jepa0+B//vikFv4P6Zf74AULqsKJ/xhQ==" 955 | } 956 | } 957 | } 958 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "erc721", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "truffle-config.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "dependencies": { 10 | "chai": "^4.1.2", 11 | "chai-as-promised": "^7.1.1", 12 | "zeppelin-solidity": "^1.10.0" 13 | }, 14 | "devDependencies": { 15 | "babel-polyfill": "^6.26.0", 16 | "babel-preset-es2015": "^6.24.1", 17 | "babel-register": "^6.26.0" 18 | }, 19 | "scripts": { 20 | "test": "echo \"Error: no test specified\" && exit 1" 21 | }, 22 | "author": "", 23 | "license": "ISC" 24 | } 25 | -------------------------------------------------------------------------------- /test/erc721.spec.js: -------------------------------------------------------------------------------- 1 | import chai from 'chai' 2 | import chaiAsPromised from 'chai-as-promised' 3 | chai.use(chaiAsPromised) 4 | const { expect, assert } = chai 5 | 6 | var MyERC721 = artifacts.require("MyERC721"); 7 | 8 | contract('Testing ERC721 contract', function(accounts) { 9 | 10 | let token; 11 | const name = "BlueCat"; 12 | const symbol = "BCat" 13 | 14 | const account1 = accounts[1] 15 | const tokenId1 = 1111; 16 | const tokenUri1 = "This is data for the token 1"; // Does not have to be unique 17 | 18 | const account2 = accounts[2] 19 | const tokenId2 = 2222; 20 | const tokenUri2 = "This is data for the token 2"; // Does not have to be unique 21 | 22 | const account3 = accounts[3] 23 | 24 | it(' should be able to deploy and mint ERC721 token', async () => { 25 | token = await MyERC721.new(name, symbol) 26 | await token.mintUniqueTokenTo(account1, tokenId1, tokenUri1, {from: accounts[0]}) 27 | 28 | expect(await token.symbol()).to.equal(symbol) 29 | expect(await token.name()).to.equal(name) 30 | }) 31 | 32 | it(' should be unique', async () => { 33 | const duplicateTokenID = token.mintUniqueTokenTo(account2, tokenId1, tokenUri2, {from: accounts[0]}) //tokenId 34 | expect(duplicateTokenID).to.be.rejectedWith(/VM Exception while processing transaction: revert/) 35 | }) 36 | 37 | it(' should allow creation of multiple unique tokens and manage ownership', async () => { 38 | const additionalToken = await token.mintUniqueTokenTo(account2, tokenId2, tokenUri2, {from: accounts[0]}) 39 | expect(Number(await token.totalSupply())).to.equal(2) 40 | 41 | expect(await token.exists(tokenId1)).to.be.true 42 | expect(await token.exists(tokenId2)).to.be.true 43 | expect(await token.exists(9999)).to.be.false // Dummy tokenId 44 | 45 | expect(await token.ownerOf(tokenId1)).to.equal(account1) 46 | expect(await token.ownerOf(tokenId2)).to.equal(account2) 47 | }) 48 | 49 | it(' should allow safe transfers', async () => { 50 | const unownedTokenId = token.safeTransferFrom(account2, account3, tokenId1, {from: accounts[2]}) // tokenId 51 | expect(unownedTokenId).to.be.rejectedWith(/VM Exception while processing transaction: revert/) 52 | 53 | const wrongOwner = token.safeTransferFrom(account1, account3, tokenId2, {from: accounts[1]}) // wrong owner 54 | expect(wrongOwner).to.be.rejectedWith(/VM Exception while processing transaction: revert/) 55 | 56 | // Noticed that the from gas param needs to be the token owners or it fails 57 | const wrongFromGas = token.safeTransferFrom(account2, account3, tokenId2, {from: accounts[1]}) // wrong owner 58 | expect(wrongFromGas).to.be.rejectedWith(/VM Exception while processing transaction: revert/) 59 | 60 | await token.safeTransferFrom(account2, account3, tokenId2, {from: accounts[2]}) 61 | expect(await token.ownerOf(tokenId2)).to.equal(account3) 62 | }) 63 | }) 64 | -------------------------------------------------------------------------------- /truffle-config.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NB: since truffle-hdwallet-provider 0.0.5 you must wrap HDWallet providers in a 3 | * function when declaring them. Failure to do so will cause commands to hang. ex: 4 | * ``` 5 | * mainnet: { 6 | * provider: function() { 7 | * return new HDWalletProvider(mnemonic, 'https://mainnet.infura.io/') 8 | * }, 9 | * network_id: '1', 10 | * gas: 4500000, 11 | * gasPrice: 10000000000, 12 | * }, 13 | */ 14 | 15 | module.exports = { 16 | // See 17 | // to customize your Truffle configuration! 18 | }; 19 | -------------------------------------------------------------------------------- /truffle.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NB: since truffle-hdwallet-provider 0.0.5 you must wrap HDWallet providers in a 3 | * function when declaring them. Failure to do so will cause commands to hang. ex: 4 | * ``` 5 | * mainnet: { 6 | * provider: function() { 7 | * return new HDWalletProvider(mnemonic, 'https://mainnet.infura.io/') 8 | * }, 9 | * network_id: '1', 10 | * gas: 4500000, 11 | * gasPrice: 10000000000, 12 | * }, 13 | */ 14 | 15 | // Allows us to use ES6 in our migrations and tests. 16 | require('babel-register') 17 | require('babel-polyfill') 18 | 19 | module.exports = { 20 | networks: { 21 | development: { 22 | host: '127.0.0.1', 23 | port: 8545, 24 | network_id: '*', // Match any network id 25 | } 26 | }, 27 | }; 28 | --------------------------------------------------------------------------------