├── .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 | 
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 | 
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 |
--------------------------------------------------------------------------------