forked from tornado-packages/ethers.js
Compare commits
704 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
92c978e5c2 | ||
|
|
34397fa2aa | ||
|
|
19587eea3f | ||
|
|
4a9373e773 | ||
|
|
2997bae935 | ||
|
|
b0bd9ee162 | ||
|
|
7075c8c235 | ||
|
|
a4a532fe8e | ||
|
|
77c771bf76 | ||
|
|
c93b48920e | ||
|
|
04c92bb8d5 | ||
|
|
16c9745326 | ||
|
|
05648177aa | ||
|
|
f318fd9cf1 | ||
|
|
c15a89832b | ||
|
|
94b0abc240 | ||
|
|
72edcd054f | ||
|
|
e4a2f8ac6c | ||
|
|
c6d7b31a84 | ||
|
|
0ed983a264 | ||
|
|
8fab48a380 | ||
|
|
86e815999d | ||
|
|
a2306f7870 | ||
|
|
60b75c10d7 | ||
|
|
41c2c8a729 | ||
|
|
9785eed8dd | ||
|
|
527de7ba5e | ||
|
|
14484e566e | ||
|
|
429af2c40d | ||
|
|
11fffd1690 | ||
|
|
af3aed4580 | ||
|
|
3a3764bdb4 | ||
|
|
18ee2c518c | ||
|
|
36172f7f7b | ||
|
|
908258f8d4 | ||
|
|
774b2d5fee | ||
|
|
b5f720ace6 | ||
|
|
9f201c386e | ||
|
|
700dd34137 | ||
|
|
f2dd977de4 | ||
|
|
5f013216c5 | ||
|
|
eac0805435 | ||
|
|
e5bee7e5a3 | ||
|
|
442553620a | ||
|
|
6d08968b87 | ||
|
|
c2ce59f95e | ||
|
|
f3ec27b95f | ||
|
|
c88cb5ea90 | ||
|
|
99a21660ab | ||
|
|
4bc62a1e8a | ||
|
|
26eb6cc01a | ||
|
|
bcba17a9e7 | ||
|
|
918b66bc2e | ||
|
|
152d672278 | ||
|
|
51fb472809 | ||
|
|
66440b8542 | ||
|
|
fefdd51084 | ||
|
|
6ca1d77298 | ||
|
|
4f6748ec4c | ||
|
|
f56fc572f1 | ||
|
|
16fdf6b621 | ||
|
|
a863037ca3 | ||
|
|
2d854bd94c | ||
|
|
9565c28a91 | ||
|
|
bc457bb3bd | ||
|
|
db383a3121 | ||
|
|
3f76f603d9 | ||
|
|
68304848ae | ||
|
|
bb6bc4cac3 | ||
|
|
ef8b9c36ef | ||
|
|
e6c943d01f | ||
|
|
31d3ee899f | ||
|
|
98143a845b | ||
|
|
bffc557be1 | ||
|
|
09208fa8fe | ||
|
|
048c571d3d | ||
|
|
24757f1064 | ||
|
|
88f2f51266 | ||
|
|
93152ef863 | ||
|
|
09b698b0a9 | ||
|
|
478aaf9619 | ||
|
|
fad902b438 | ||
|
|
7bfaf292db | ||
|
|
be0488a1a0 | ||
|
|
28a52cd485 | ||
|
|
3a19f43844 | ||
|
|
4852e837d2 | ||
|
|
fa68385cfe | ||
|
|
d54609a458 | ||
|
|
f682861e0b | ||
|
|
023a20ff47 | ||
|
|
e39cd84923 | ||
|
|
5020897f10 | ||
|
|
6ac2d923b7 | ||
|
|
6996dd86f4 | ||
|
|
493273d698 | ||
|
|
84344ac4c2 | ||
|
|
9b118af304 | ||
|
|
e39e2ed718 | ||
|
|
71f781d542 | ||
|
|
b2db10e216 | ||
|
|
3736a15714 | ||
|
|
248158130e | ||
|
|
f5c7ccbb80 | ||
|
|
24335d0dd7 | ||
|
|
908c2c1096 | ||
|
|
9797b36186 | ||
|
|
731f189010 | ||
|
|
cc5b157231 | ||
|
|
99fed75202 | ||
|
|
cb5f9f576a | ||
|
|
aeac2cdb86 | ||
|
|
0dafd83033 | ||
|
|
f6d946cf68 | ||
|
|
b9c07b549c | ||
|
|
c34a1f73c6 | ||
|
|
281bd0613d | ||
|
|
d936b4cd09 | ||
|
|
5a0ebf84ef | ||
|
|
c9c5b4d641 | ||
|
|
5e86e02304 | ||
|
|
15cd0d8e58 | ||
|
|
061b0eae1d | ||
|
|
d3876303f2 | ||
|
|
2ad110548d | ||
|
|
ef5d19e5c9 | ||
|
|
5f6bc405dd | ||
|
|
1ca203ca38 | ||
|
|
c8a75ab01d | ||
|
|
8c6eb555de | ||
|
|
cb68403387 | ||
|
|
7b5ce86c5b | ||
|
|
f4e247fc92 | ||
|
|
e582bd73ee | ||
|
|
543e12c6ca | ||
|
|
57439af25d | ||
|
|
f4dd5d19e7 | ||
|
|
05e30f4680 | ||
|
|
9c87e569cf | ||
|
|
15761027df | ||
|
|
65fa0b7b22 | ||
|
|
8b2e57276f | ||
|
|
25337d4543 | ||
|
|
892f30a051 | ||
|
|
829a56cc79 | ||
|
|
9d04f2c1eb | ||
|
|
f02f4bc0c0 | ||
|
|
b9829f205f | ||
|
|
eef07e5a4f | ||
|
|
13e50ec6db | ||
|
|
9dcf6f3253 | ||
|
|
e0cefb0398 | ||
|
|
6018bf63f9 | ||
|
|
7aebe53e5b | ||
|
|
32a070d909 | ||
|
|
b5408bcbd0 | ||
|
|
284ffdfd1f | ||
|
|
ae4cd90441 | ||
|
|
5efd0c1f8a | ||
|
|
962f75038b | ||
|
|
ea5a56f0f0 | ||
|
|
afae5cd2f9 | ||
|
|
280dc56a74 | ||
|
|
2b9d8d6d6a | ||
|
|
2d35e5e9d7 | ||
|
|
102a4e8bf4 | ||
|
|
ce837e307e | ||
|
|
810e9e743e | ||
|
|
e092746165 | ||
|
|
388b658bfa | ||
|
|
0358e8afb7 | ||
|
|
2b88efea65 | ||
|
|
f4c3073304 | ||
|
|
e4d5786861 | ||
|
|
46a55a1d99 | ||
|
|
2c1022cde1 | ||
|
|
f9723c42cb | ||
|
|
3635a9bb40 | ||
|
|
9e0bfa733b | ||
|
|
a80a8a8ced | ||
|
|
b44a80800c | ||
|
|
e3b04bb3fc | ||
|
|
fcee62a3d9 | ||
|
|
7f1fbfc203 | ||
|
|
4f63b82bbb | ||
|
|
f63c844c42 | ||
|
|
694c0b3f7d | ||
|
|
1c45840148 | ||
|
|
421b2c857b | ||
|
|
bacf42a22f | ||
|
|
5c8703fb30 | ||
|
|
30a84d8577 | ||
|
|
3198bc27fd | ||
|
|
c7555516ff | ||
|
|
3cd0e8bf88 | ||
|
|
957ccd2eaf | ||
|
|
558ef2c00d | ||
|
|
b73a964bac | ||
|
|
3e091a2938 | ||
|
|
cb6d18035f | ||
|
|
4b15229832 | ||
|
|
f0dfa50848 | ||
|
|
22f007498d | ||
|
|
cc6f658ef1 | ||
|
|
9b885296d0 | ||
|
|
c6c5ba0118 | ||
|
|
a966a60656 | ||
|
|
d05da1e487 | ||
|
|
294d0fda61 | ||
|
|
29f3d2dea8 | ||
|
|
d2b6570c39 | ||
|
|
a1a765f6e4 | ||
|
|
1d98928475 | ||
|
|
73486d6f39 | ||
|
|
640ed5dc33 | ||
|
|
dda957c8e5 | ||
|
|
97b6d8a797 | ||
|
|
00bb20546b | ||
|
|
7cf8596906 | ||
|
|
5a0607d11b | ||
|
|
26207e7bb8 | ||
|
|
ae8d75fe6a | ||
|
|
4db19a1799 | ||
|
|
543ca19361 | ||
|
|
7014afaab6 | ||
|
|
62987ac3a8 | ||
|
|
3a5ec9b4ca | ||
|
|
3169cf2f60 | ||
|
|
bceefc7197 | ||
|
|
bfff3ea4cc | ||
|
|
7d4caa05ca | ||
|
|
004fd7d37d | ||
|
|
e3da92d1ee | ||
|
|
0bc72c5ed4 | ||
|
|
d0cf0c8f43 | ||
|
|
4d4fe60a55 | ||
|
|
81903e99d3 | ||
|
|
fd40f66766 | ||
|
|
2aa2a38e37 | ||
|
|
d701995bc0 | ||
|
|
c4084fe625 | ||
|
|
8c7c592bd1 | ||
|
|
b9aca357a9 | ||
|
|
26f5a558d6 | ||
|
|
960919d00a | ||
|
|
b712c88088 | ||
|
|
a062f75d38 | ||
|
|
a67e3d1d65 | ||
|
|
28927a6f1a | ||
|
|
ce864fa1b2 | ||
|
|
7e1e760b95 | ||
|
|
d19df0dcae | ||
|
|
b61b84dfc8 | ||
|
|
922de67a8b | ||
|
|
e8c657ba70 | ||
|
|
1ce4f75b0d | ||
|
|
175966674d | ||
|
|
d1ce9f4964 | ||
|
|
0f9d0ef533 | ||
|
|
e4df325e22 | ||
|
|
3b843a1782 | ||
|
|
614b2d0325 | ||
|
|
46e4db0b24 | ||
|
|
94171f6c57 | ||
|
|
e0391ee1bc | ||
|
|
7004cd0b92 | ||
|
|
b61643fecb | ||
|
|
8d6fa3dc93 | ||
|
|
e6c8db88bd | ||
|
|
937194b5ee | ||
|
|
5f3ceec6f9 | ||
|
|
3f9f0e02e5 | ||
|
|
d6260ae11a | ||
|
|
0ecfe4bafd | ||
|
|
df2a00a2fc | ||
|
|
dfdbb9bb28 | ||
|
|
979e374270 | ||
|
|
b7e143b4f3 | ||
|
|
272265f085 | ||
|
|
27402fafe6 | ||
|
|
ac4211d0c6 | ||
|
|
ee0faba708 | ||
|
|
e5d5871b95 | ||
|
|
4514229f27 | ||
|
|
7ac8cb63c8 | ||
|
|
8e83ceaedc | ||
|
|
b1d026b800 | ||
|
|
ebf42dc9e0 | ||
|
|
a8283ea99f | ||
|
|
564c5fa364 | ||
|
|
6faa978767 | ||
|
|
8a340c8ff3 | ||
|
|
0b35f1959a | ||
|
|
98ea4b59a0 | ||
|
|
ac2ad5a886 | ||
|
|
8fe19de3ce | ||
|
|
40559b7044 | ||
|
|
0b8b682fee | ||
|
|
083aca4693 | ||
|
|
19475a06f5 | ||
|
|
5fa014e01d | ||
|
|
df591cfc4c | ||
|
|
fcd57f9756 | ||
|
|
c348c60d5c | ||
|
|
1d2fe8993b | ||
|
|
18aa885aef | ||
|
|
a693576941 | ||
|
|
5c0475ce8c | ||
|
|
380c17a0a5 | ||
|
|
bd4ca0d4d3 | ||
|
|
7949444612 | ||
|
|
cf16b0ffa9 | ||
|
|
ec40c67c50 | ||
|
|
55ac3fef88 | ||
|
|
c4edd957b4 | ||
|
|
a19cc91cd4 | ||
|
|
c83596455b | ||
|
|
41a91c3c2d | ||
|
|
b77937ec4c | ||
|
|
1082105eea | ||
|
|
45923ad5dd | ||
|
|
aa67537097 | ||
|
|
63e5ad1d95 | ||
|
|
b2be7c807f | ||
|
|
dde33aa5a5 | ||
|
|
91f6e8ede6 | ||
|
|
7d8abf4463 | ||
|
|
6abab3b974 | ||
|
|
970613fad9 | ||
|
|
edbf8e319c | ||
|
|
28cd3cace2 | ||
|
|
45f8827c6e | ||
|
|
342be430cb | ||
|
|
622f02c369 | ||
|
|
263bf8047a | ||
|
|
2feced5937 | ||
|
|
6de0a765c9 | ||
|
|
4fd35224b3 | ||
|
|
52dc35bf0e | ||
|
|
2a111ab6ce | ||
|
|
5631f071c3 | ||
|
|
a0f92954c8 | ||
|
|
64b312ef6e | ||
|
|
baf92d4ec8 | ||
|
|
1d282c544a | ||
|
|
5b18a63ebc | ||
|
|
336df72e04 | ||
|
|
cf79190175 | ||
|
|
5da4917c27 | ||
|
|
c1b24e818c | ||
|
|
af893e79a4 | ||
|
|
aa48dfcdf4 | ||
|
|
efbfed0d40 | ||
|
|
059b03e090 | ||
|
|
7391cf8d19 | ||
|
|
be7a128bf4 | ||
|
|
b6794b96a5 | ||
|
|
4df78764a2 | ||
|
|
c411d9744d | ||
|
|
f7bfa50f15 | ||
|
|
8c0308dc58 | ||
|
|
4758b30cd3 | ||
|
|
6a8ca9c318 | ||
|
|
884593ab76 | ||
|
|
0517f70946 | ||
|
|
514aab7ee3 | ||
|
|
eaaa689f2f | ||
|
|
cf7872a498 | ||
|
|
f779b6e3cc | ||
|
|
a6cced81d6 | ||
|
|
62e51861b8 | ||
|
|
6c6e031254 | ||
|
|
48c07f6ef6 | ||
|
|
1b6c20341e | ||
|
|
75eb7ed507 | ||
|
|
cd360a4f8c | ||
|
|
fd27d3e2ce | ||
|
|
6737128b84 | ||
|
|
63b2eae4f5 | ||
|
|
0b34aea23a | ||
|
|
1290fa0073 | ||
|
|
da74e95f88 | ||
|
|
ac82a5cb83 | ||
|
|
ba21348c0f | ||
|
|
27207037a8 | ||
|
|
a612e1e8fa | ||
|
|
08d3547d4e | ||
|
|
7702d4ce7c | ||
|
|
3bf39b3bee | ||
|
|
417065bad6 | ||
|
|
3cb21dd6c7 | ||
|
|
cebf2aab29 | ||
|
|
72bf73f931 | ||
|
|
65caec2ea5 | ||
|
|
ea7c74d48d | ||
|
|
86df2306c4 | ||
|
|
6c12d8f30c | ||
|
|
e649a22ccf | ||
|
|
eddf9de8f9 | ||
|
|
7e91091cc3 | ||
|
|
9745a6ecbc | ||
|
|
3936b15afc | ||
|
|
efb7dce524 | ||
|
|
ac4b5d9199 | ||
|
|
fdb7114511 | ||
|
|
03f8c1db86 | ||
|
|
12b68b0242 | ||
|
|
28ddf485ed | ||
|
|
96d420e832 | ||
|
|
0f98bb5ac5 | ||
|
|
296473299c | ||
|
|
ce7718c87e | ||
|
|
1ec8f9cf85 | ||
|
|
46fd2deb8b | ||
|
|
5a9f440c8f | ||
|
|
1d67aa3e29 | ||
|
|
bd7e8e708f | ||
|
|
8a842af999 | ||
|
|
224c17a9b9 | ||
|
|
89e1cb02a4 | ||
|
|
3cdb6aad25 | ||
|
|
8a26f38579 | ||
|
|
424cce9224 | ||
|
|
96abf66333 | ||
|
|
b2ada0cda9 | ||
|
|
e67a66f900 | ||
|
|
20293fbcfb | ||
|
|
03f20f023f | ||
|
|
dfdf4ef317 | ||
|
|
f10c595ad3 | ||
|
|
f611dba0ad | ||
|
|
da852d0012 | ||
|
|
6ade579542 | ||
|
|
e17947930c | ||
|
|
74c71e6677 | ||
|
|
7635708c21 | ||
|
|
4ee9a4d191 | ||
|
|
bb24fce859 | ||
|
|
21d53f190b | ||
|
|
5fc59dd1cf | ||
|
|
34fd2882d8 | ||
|
|
4a23b0ddc7 | ||
|
|
c4690f9e7b | ||
|
|
36c6d53757 | ||
|
|
c550f2eb07 | ||
|
|
67bb0b7f93 | ||
|
|
c5b411811a | ||
|
|
eddf93b200 | ||
|
|
9aeb309d9d | ||
|
|
bea0ea3aaf | ||
|
|
df930103e7 | ||
|
|
d6cf970ae9 | ||
|
|
e778ab6f30 | ||
|
|
b7fc74a99d | ||
|
|
b38a36b2f5 | ||
|
|
046a4a4dc0 | ||
|
|
357d49ecee | ||
|
|
a568be9df3 | ||
|
|
43061c691b | ||
|
|
a4e768e2b3 | ||
|
|
2d4fb371c2 | ||
|
|
9b54b53081 | ||
|
|
3512922bef | ||
|
|
60334b66ba | ||
|
|
d38bf992c2 | ||
|
|
ce95391804 | ||
|
|
511fff1390 | ||
|
|
178980c34a | ||
|
|
132e56e65e | ||
|
|
ff63e99c86 | ||
|
|
a3fab18a93 | ||
|
|
a9cc1bf641 | ||
|
|
fa4db2cecc | ||
|
|
ca249054e5 | ||
|
|
f6ace914e9 | ||
|
|
1d38633e69 | ||
|
|
8129f0cb8b | ||
|
|
b06002a2a8 | ||
|
|
464584265a | ||
|
|
184839a57a | ||
|
|
3ed66f5c16 | ||
|
|
0d106d01af | ||
|
|
807c8133ea | ||
|
|
f6cc207ae5 | ||
|
|
388a77258e | ||
|
|
424abc3395 | ||
|
|
83b5e4b4a3 | ||
|
|
03c10086e7 | ||
|
|
dae3a5b8b1 | ||
|
|
67a29eb92e | ||
|
|
5a837e1b0a | ||
|
|
a1f2c84d03 | ||
|
|
628c99936e | ||
|
|
e8b23c29a5 | ||
|
|
6e19e6eca9 | ||
|
|
ef71271db6 | ||
|
|
11f0434bdd | ||
|
|
46ff8e891e | ||
|
|
4df288e244 | ||
|
|
a9bc2b5ea8 | ||
|
|
d49beb8374 | ||
|
|
f88b3523c0 | ||
|
|
04f0a33489 | ||
|
|
b4bc01f2c3 | ||
|
|
7eaf22f573 | ||
|
|
255b7e0a0f | ||
|
|
9adcbbb0fe | ||
|
|
31b5650489 | ||
|
|
2e90718d6a | ||
|
|
fe3ac0e94d | ||
|
|
50422b6060 | ||
|
|
4351a3ac86 | ||
|
|
ccffc46f67 | ||
|
|
e8bb624560 | ||
|
|
21c6fdce3a | ||
|
|
18cd678b71 | ||
|
|
9cac6b06f5 | ||
|
|
73ee7664a1 | ||
|
|
8835225655 | ||
|
|
54c19dfb5a | ||
|
|
e4c455bdb6 | ||
|
|
3c9f6f6ab9 | ||
|
|
fb65772dd6 | ||
|
|
aeec6d6d52 | ||
|
|
9be0053d62 | ||
|
|
466fbcb8da | ||
|
|
f43a03a151 | ||
|
|
b3134642a2 | ||
|
|
4d00215098 | ||
|
|
6926e5f423 | ||
|
|
a5e1f40c30 | ||
|
|
857688f36c | ||
|
|
0ac2bfd1cf | ||
|
|
0adedcab50 | ||
|
|
74406cc0b7 | ||
|
|
8754bb80e0 | ||
|
|
67ac227fec | ||
|
|
666210a061 | ||
|
|
2403a98219 | ||
|
|
f140fa6017 | ||
|
|
a5df551689 | ||
|
|
ea6177d19e | ||
|
|
5c776a83ff | ||
|
|
bdcdfdabec | ||
|
|
264a7da9f7 | ||
|
|
f5f2bf85c5 | ||
|
|
cb7e40a892 | ||
|
|
121d104d46 | ||
|
|
44729ffba9 | ||
|
|
4a3d68eada | ||
|
|
afb35ec307 | ||
|
|
bbd3c94ec3 | ||
|
|
394761a6ca | ||
|
|
c566f00567 | ||
|
|
03b967c99a | ||
|
|
966267edf9 | ||
|
|
a53595eea5 | ||
|
|
eec9799706 | ||
|
|
811710c8f0 | ||
|
|
1c0d0157ad | ||
|
|
b4d622b02b | ||
|
|
1b3b117b06 | ||
|
|
33ca1fc670 | ||
|
|
91f9a47afa | ||
|
|
70f10cd23e | ||
|
|
0a52beb539 | ||
|
|
0d36f839c3 | ||
|
|
c815166eb5 | ||
|
|
9616720abb | ||
|
|
01f909fc05 | ||
|
|
b69d3de3e3 | ||
|
|
7b1856aacb | ||
|
|
d5402d017a | ||
|
|
c59414b74b | ||
|
|
0aee62df4d | ||
|
|
1531793084 | ||
|
|
4b41c0e1a2 | ||
|
|
12eb60d2f7 | ||
|
|
00b628c6cc | ||
|
|
9e80c55275 | ||
|
|
301d803fc5 | ||
|
|
a07dfa9572 | ||
|
|
b528131937 | ||
|
|
169347d0c4 | ||
|
|
e4dcec7de7 | ||
|
|
4407c7a4e1 | ||
|
|
cafd16b960 | ||
|
|
3d56251d15 | ||
|
|
a90ab3dbb1 | ||
|
|
c137f5fe9f | ||
|
|
f7bdaadd7e | ||
|
|
36a5b13a0c | ||
|
|
42f4ff398c | ||
|
|
d65a1f120e | ||
|
|
ef2be2e86d | ||
|
|
14841641c1 | ||
|
|
cde70b1494 | ||
|
|
79a7f3dd64 | ||
|
|
ef2f0add35 | ||
|
|
2ff0b10084 | ||
|
|
40634da90b | ||
|
|
afea5e81f9 | ||
|
|
29cc0795b4 | ||
|
|
f77d5ff909 | ||
|
|
88b48738cc | ||
|
|
617f7a1ce6 | ||
|
|
2ab382a355 | ||
|
|
28f40287ce | ||
|
|
7250e96850 | ||
|
|
92bef64979 | ||
|
|
1c2d7c466e | ||
|
|
85bce0582e | ||
|
|
e746891ae0 | ||
|
|
9fa2c878b9 | ||
|
|
6c963f6404 | ||
|
|
bb8f06c248 | ||
|
|
cb9bfccb9a | ||
|
|
3069fa759b | ||
|
|
ecd9d31371 | ||
|
|
6c0723bc43 | ||
|
|
dc4f2dcee7 | ||
|
|
f772ce111e | ||
|
|
d686374e05 | ||
|
|
73eef741b6 | ||
|
|
4075f330e2 | ||
|
|
c10c3b937c | ||
|
|
8603c836e1 | ||
|
|
27bdceaf32 | ||
|
|
9672b9d3b8 | ||
|
|
4f05bf87ac | ||
|
|
c88d796f0e | ||
|
|
0b8cf42fcf | ||
|
|
807b27048b | ||
|
|
6a01b622a2 | ||
|
|
7e61b6b730 | ||
|
|
156534e2ba | ||
|
|
5f650572ce | ||
|
|
9bed4096b8 | ||
|
|
b097e48efc | ||
|
|
1a3aa4fd1a | ||
|
|
83d099b37c | ||
|
|
637a6d7f40 | ||
|
|
78c4af5e4b | ||
|
|
730ccd36b2 | ||
|
|
33a1459505 | ||
|
|
30cf4a1702 | ||
|
|
16529909f9 | ||
|
|
adc1d93ac2 | ||
|
|
94ba940382 | ||
|
|
cf83d92cc1 | ||
|
|
f6990b6e1f | ||
|
|
da1526b92f | ||
|
|
4cd710f84e | ||
|
|
55833fcad0 | ||
|
|
f1d35ba18a | ||
|
|
54685bd660 | ||
|
|
b0f0e8841e | ||
|
|
a073968597 | ||
|
|
5ecba670a5 | ||
|
|
45c1108ca8 | ||
|
|
e115b2d8a8 | ||
|
|
cf20071189 | ||
|
|
b4b60b64aa | ||
|
|
8111ef74b2 | ||
|
|
b4c498f088 | ||
|
|
8286c63ddc | ||
|
|
7394e83eeb | ||
|
|
ebd9bbde2e | ||
|
|
90f27da293 | ||
|
|
7d11687f15 | ||
|
|
3234f04b27 | ||
|
|
5c16c14fed | ||
|
|
6aca737405 | ||
|
|
c884627e21 | ||
|
|
8fa9b34e87 | ||
|
|
b1190e6e0b | ||
|
|
0c1615aef5 | ||
|
|
e31f20e441 | ||
|
|
735e492f8c | ||
|
|
a107a7c3e2 | ||
|
|
b5f4d3b113 | ||
|
|
005c0d2cdc | ||
|
|
0db4b352b6 | ||
|
|
0694a5db0e | ||
|
|
73c7efffb0 | ||
|
|
729170ec97 | ||
|
|
2b0c40feb4 | ||
|
|
91543a0029 | ||
|
|
9625745f4c | ||
|
|
89c4c75e3c | ||
|
|
6aceee8486 | ||
|
|
26958d74d8 | ||
|
|
38d578a2c6 | ||
|
|
96aaed7395 | ||
|
|
7027935ff7 | ||
|
|
5d692f4908 | ||
|
|
9def4a6170 | ||
|
|
3a0d79b1e4 | ||
|
|
048ef96ad0 | ||
|
|
51f1fa50dd | ||
|
|
d8a88c18d3 | ||
|
|
db7cff9582 | ||
|
|
dc2583ddd8 | ||
|
|
3be962f09d |
31
.eslintrc.js
Normal file
31
.eslintrc.js
Normal file
@@ -0,0 +1,31 @@
|
||||
module.exports = {
|
||||
"env": {
|
||||
"browser": true,
|
||||
"commonjs": true,
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
// "eslint:recommended",
|
||||
// "plugin:promise/recommended"
|
||||
],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 5
|
||||
},
|
||||
"plugins": [
|
||||
"promise"
|
||||
],
|
||||
"rules": {
|
||||
"promise/always-return": "error",
|
||||
"promise/no-return-wrap": "error",
|
||||
"promise/param-names": "error",
|
||||
"promise/catch-or-return": "error",
|
||||
"promise/no-native": "off",
|
||||
// "promise/no-nesting": "warn",
|
||||
"promise/no-promise-in-callback": "warn",
|
||||
"promise/no-callback-in-promise": "warn",
|
||||
// "promise/avoid-new": "warn",
|
||||
"promise/no-new-statics": "error",
|
||||
"promise/no-return-in-finally": "warn",
|
||||
"promise/valid-params": "warn"
|
||||
}
|
||||
};
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -1 +1,10 @@
|
||||
node_modules/
|
||||
obsolete/
|
||||
.test-account.key
|
||||
.account.key
|
||||
.DS_Store
|
||||
.tmp/
|
||||
docs/build/doctrees/
|
||||
docs/build/html/_sources/
|
||||
dist/types/shims/
|
||||
shims/*.d.ts
|
||||
|
||||
13
.npmignore
Normal file
13
.npmignore
Normal file
@@ -0,0 +1,13 @@
|
||||
# Config files for testing and linting
|
||||
.eslintrc.js
|
||||
.travis.yml
|
||||
|
||||
# This is only needed for building dist and library files
|
||||
gulpfile.js
|
||||
|
||||
# We do not need the TypeScipt source in deployments
|
||||
tsconfig.json
|
||||
src.ts/
|
||||
|
||||
# To run tests, checkout GitHub
|
||||
tests/
|
||||
17
.travis.yml
Normal file
17
.travis.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
language: node_js
|
||||
|
||||
node_js:
|
||||
- "6"
|
||||
- "8"
|
||||
- "10"
|
||||
|
||||
env:
|
||||
- RUN_PHANTOMJS=0
|
||||
- RUN_PHANTOMJS=1
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
- node_js: "6"
|
||||
env: RUN_PHANTOMJS=1
|
||||
- node_js: "8"
|
||||
env: RUN_PHANTOMJS=1
|
||||
46
CODE_OF_CONDUCT.md
Normal file
46
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at support@ethers.io. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
28
Gruntfile.js
28
Gruntfile.js
@@ -1,28 +0,0 @@
|
||||
module.exports = function(grunt) {
|
||||
grunt.initConfig({
|
||||
browserify: {
|
||||
dist: {
|
||||
files: {
|
||||
'dist/ethers-wallet.js': [ 'index.js' ]
|
||||
},
|
||||
options: {
|
||||
browserifyOptions: {
|
||||
standalone: 'Wallet'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
uglify: {
|
||||
dist: {
|
||||
files: {
|
||||
'dist/ethers-wallet.min.js' : [ 'dist/ethers-wallet.js' ]
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
grunt.loadNpmTasks('grunt-browserify');
|
||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||
|
||||
grunt.registerTask('dist', ['browserify', 'uglify']);
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2014 Richard Moore
|
||||
Copyright (c) 2017 Richard Moore
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -9,14 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
417
README.md
417
README.md
@@ -1,20 +1,30 @@
|
||||
ethers-wallet
|
||||
=============
|
||||
ethers.js
|
||||
=========
|
||||
|
||||
[](https://badge.fury.io/js/ethers-wallet)
|
||||
[](https://badge.fury.io/js/ethers)
|
||||
|
||||
Complete Ethereum wallet implementation and utilities in JavaScript.
|
||||
Complete Ethereum wallet implementation and utilities in JavaScript (and TypeScript).
|
||||
|
||||
**Features:**
|
||||
|
||||
- Keep your private keys in your browser
|
||||
- Import and export JSON wallets (Geth and crowdsale) and brain wallets
|
||||
- Meta-classes create JavaScript objects from any contract ABI
|
||||
- Connect to Ethereum nodes over RPC, injected Web3 or [Etherscan](https://etherscan.io)
|
||||
- Small (~100kb compressed; 290kb uncompressed)
|
||||
- MIT licensed (with one exception, which we are migrating off of; see below)
|
||||
- Keep your private keys in your client, **safe** and sound
|
||||
- Import and export **JSON wallets** (Geth, Parity and crowdsale)
|
||||
- Import and export BIP 39 **mnemonic phrases** (12 word backup phrases) and **HD Wallets** (English, French, Italian, Japanese, Korean, Simplified Chinese, Spanish, Traditional Chinese)
|
||||
- Meta-classes create JavaScript objects from any contract ABI, including **ABIv2** and **Human-Readable ABI**
|
||||
- Connect to Ethereum nodes over [JSON-RPC](https://github.com/ethereum/wiki/wiki/JSON-RPC), [INFURA](https://infura.io), [Etherscan](https://etherscan.io), or [MetaMask](https://metamask.io)
|
||||
- **ENS names** are first-class citizens; they can be used anywhere an Ethereum addresses can be used
|
||||
- **Tiny** (~84kb compressed; 270kb uncompressed)
|
||||
- **Complete** functionality for all your Ethereum needs
|
||||
- Extensive [documentation](https://docs.ethers.io/ethers.js/html/)
|
||||
- Large collection of **test cases** which are maintained and added to
|
||||
- Fully **TypeScript** ready, with definition files and full TypeScript source
|
||||
- **MIT License** (including ALL dependencies); completely open source to do with as you please
|
||||
|
||||
**NOTE: This is still very beta; please only use it on the testnet for now, or with VERY small amounts of ether on the livenet that you are willing to lose due to bugs.**
|
||||
|
||||
Keep Updated
|
||||
------------
|
||||
|
||||
For the latest news and advisories, please follow [@ethersproject](https://twitter.com/ethersproject) on Twitter as well as this GitHub project.
|
||||
|
||||
|
||||
Installing
|
||||
@@ -23,368 +33,73 @@ Installing
|
||||
To use in a browser:
|
||||
|
||||
```html
|
||||
<script type="text/javascript" src="https://rawgit.com/ethers-io/ethers-wallet/master/dist/ethers-wallet.min.js"></script>
|
||||
<script charset="utf-8"
|
||||
src="https://cdn.ethers.io/scripts/ethers-v4.min.js"
|
||||
type="text/javascript">
|
||||
</script>
|
||||
```
|
||||
|
||||
To use in [node.js](https://nodejs.org/):
|
||||
|
||||
```
|
||||
npm install ethers-wallet
|
||||
/Users/ethers/my-app> npm install --save ethers
|
||||
```
|
||||
|
||||
|
||||
API
|
||||
---
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
Browse the [API Documentation](https://docs.ethers.io/ethers.js/html/) online.
|
||||
|
||||
### Wallet API
|
||||
To fork and submit pull requests to the documentation, please see the
|
||||
[documentation repository](https://github.com/ethers-io/documentation).
|
||||
|
||||
An *Ethereum* wallet wraps a cryptographic private key, which is used to sign transactions and control the ether located at the wallet's address. These transactions can then be broadcast to the *Ethereum* network.
|
||||
|
||||
```javascript
|
||||
// A private key can be specified as a 32 byte buffer or hexidecimal string
|
||||
var privateKey = new Wallet.utils.Buffer([
|
||||
0x31, 0x41, 0x59, 0x26, 0x53, 0x58, 0x97, 0x93,
|
||||
0x23, 0x84, 0x62, 0x64, 0x33, 0x83, 0x27, 0x95,
|
||||
0x02, 0x88, 0x41, 0x97, 0x16, 0x93, 0x99, 0x37,
|
||||
0x51, 0x05, 0x82, 0x09, 0x74, 0x94, 0x45, 0x92
|
||||
])
|
||||
Related Libraries
|
||||
---------------
|
||||
|
||||
// or equivalently:
|
||||
var privateKey = '0x3141592653589793238462643383279502884197169399375105820974944592'
|
||||
- [Command Line Interface](https://github.com/ethers-io/ethers-cli) - Command Line Tools for ethers
|
||||
- [CryptoKitties](https://github.com/ricmoo/ethers-meow) - CryptoKitties utility libraries
|
||||
- [ENS](https://github.com/ethers-io/ethers-ens) - ENS utility libraries for managing names
|
||||
- [LedgerSigner](https://github.com/ethers-io/ethers-ledger) - Use a Ledger Hardware Wallet as an ethers Signer (supports HID (node.js) and U2F (browser); or specify your own transport)
|
||||
- [Web3 Bridge](https://github.com/ethers-io/ethers-web3-bridge) - Use ethers as the backend for a Web3 front-end
|
||||
|
||||
// Create a wallet object
|
||||
var wallet = new Wallet(privateKey)
|
||||
|
||||
// Wallet privateKey
|
||||
console.log(wallet.privateKey)
|
||||
/// "0x3141592653589793238462643383279502884197169399375105820974944592"
|
||||
Hacking and Contributing
|
||||
------------------------
|
||||
|
||||
// Wallet address
|
||||
console.log(wallet.address)
|
||||
/// "0x7357589f8e367c2C31F51242fB77B350A11830F3"
|
||||
|
||||
// Sign transactions
|
||||
wallet.sign({
|
||||
to: "0x06B5955A67D827CDF91823E3bB8F069e6c89c1D6",
|
||||
gasLimit: 3000000,
|
||||
gasPrice: "0x1000",
|
||||
value: "0x1000"
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
### Converting addresses
|
||||
|
||||
Addresses come in various forms, and it is often useful to convert between them. You can pass any valid address into any function, and the library will convert it internally as needed. The address types are:
|
||||
- **hexidecimal** - 0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef (all letters must be either lower case or uppercase; no mixed case, as this implies a checksum address)
|
||||
- **ICAP** - XE49Q0EPSW7XTS5PRIE9226HRPOO69XRVU7 (uses the International Bank Account Number format)
|
||||
- **checksum** - 0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF (notice the case is adjusted encoding checkum information)
|
||||
|
||||
```javascript
|
||||
// ICAP Addresses
|
||||
Wallet.getIcapAddress(wallet.address)
|
||||
/// "XE39DH16QOXYG5JY9BYY6JGZW8ORUPBX71V"
|
||||
|
||||
// Get checksummed address (from ICAP)
|
||||
Wallet.getAddress("XE39DH16QOXYG5JY9BYY6JGZW8ORUPBX71V")
|
||||
/// "0x7357589f8e367c2C31F51242fB77B350A11830F3"
|
||||
|
||||
// Get checksummed addresses (from unchecksumed)
|
||||
Wallet.getAddress("0x7357589f8e367c2c31f51242fb77b350a11830f3")
|
||||
/// "0x7357589f8e367c2C31F51242fB77B350A11830F3"
|
||||
|
||||
// Detect address checksum errors (notice the last "f" should be lowercase)
|
||||
Wallet.getAddress('0x7357589f8e367c2c31f51242fb77b350a11830F3')
|
||||
/// throws Error: invalid checksum address
|
||||
```
|
||||
|
||||
|
||||
### Crowdsale JSON Wallets
|
||||
|
||||
During the crowdsale, the Ethereum Project sold ether by generating *crowdsale JSON wallet*. These functions allow you to decrypt those files and retreive the private key.
|
||||
|
||||
```javascript
|
||||
|
||||
// See the test-wallets directory samples (the variable should be a string)
|
||||
var json = "... see the test-wallets directory for samples ...";
|
||||
|
||||
// Detect crowdsale JSON wallets
|
||||
Wallet.isCrowdsaleWallet(json)
|
||||
|
||||
// Get a wallet from a crowdsale JSON wallet
|
||||
var wallet = Wallet.decryptCrowdsaleWallet(json, password);
|
||||
console.log(wallet.address)
|
||||
console.log(wallet.privateKey)
|
||||
```
|
||||
|
||||
|
||||
### Secret Storage JSON Wallet
|
||||
|
||||
This API allows you to decrypt and encrypt the [Secret Storage](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) format used by *Geth* and many other wallet platforms (such as *ethers.io*).
|
||||
|
||||
The Secret Storage JSON Wallet format uses an algorithm called *scrypt*, which is intentionally CPU-intensive, which ensures that an attacker would need to tie up considerable resources to attempt to brute-force guess your password. It aslo means it may take some time (10-30 seconds) to decrypt or encrypt a wallet. So, these API calls use a callback to provide progress feedback as well as the opportunity to cancel the process.
|
||||
|
||||
The callback should look like `function(progress)` where progress is a Number between 0 and 1 (inclusive) and if the function returns `true`, then the process will be cancelled, calling the callback once more with `callback(new Error('cancelled'))`.
|
||||
|
||||
#### Wallet.decrypt(json, password[, callback]);
|
||||
|
||||
```javascript
|
||||
|
||||
// See the test-wallets directory samples (the variable should be a string)
|
||||
var json = "... see the test-wallets directory for samples ...";
|
||||
|
||||
// Decrypt a Secret Storage JSON wallet
|
||||
var shouldCancelDecrypt = false;
|
||||
|
||||
function updateInterface(progress) {
|
||||
console.log('The wallet is ' + parseInt(100 * progress) + '% decrypted');
|
||||
|
||||
// Optionally return true to stop this decryption; this callback will get
|
||||
// called once more with callback(new Error("cancelled"))
|
||||
return shouldCancelDecrypt;
|
||||
}
|
||||
|
||||
Wallet.decrypt(json, password, updateInterface).then(function(wallet) {
|
||||
// The wallet was successfully decrypted
|
||||
|
||||
}, function(error) {
|
||||
if (error.message === 'invalid password') {
|
||||
// Wrong password
|
||||
|
||||
} else if (error.message === 'cancelled') {
|
||||
// The decryption was cancelled
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
#### Wallet.prototype.encrypt(password[, options][, callback]);
|
||||
|
||||
```javascript
|
||||
|
||||
// Encrypt a wallet into a Secret Storage JSON Wallet (all options are optional)
|
||||
var bytes16 = '0xdeadbeef1deadbeef2deadbeef301234';
|
||||
var options = {
|
||||
salt: bytes16, // hex string or Buffer, any length
|
||||
iv: bytes16, // hex string or Buffer, 16 bytes
|
||||
uuid: bytes16, // hex string or Buffer, 16 bytes
|
||||
scrypt: {
|
||||
N: (1 << 17), // Number, power of 2 greater than 2
|
||||
p: 8, // Number
|
||||
r: 1 // Number
|
||||
}
|
||||
}
|
||||
|
||||
var wallet = new Wallet(privateKey);
|
||||
|
||||
var shouldCancelEncrypt = false;
|
||||
|
||||
function updateInterface(progress) {
|
||||
console.log('The wallet is ' + parseInt(100 * progress) + '% encrypted');
|
||||
|
||||
// Optionally return true to stop this encryption; this callback will get
|
||||
// called once more with callback(new Error("cancelled"))
|
||||
return shouldCancelEncrypt;
|
||||
}
|
||||
|
||||
wallet.encrypt(password, options, updateInterface).then(function(json) {
|
||||
// The wallet was successfully encrypted as a json string
|
||||
|
||||
}, function(error) {
|
||||
if (error.message === 'cancelled') {
|
||||
// Cancelled
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
### Brain Wallets
|
||||
|
||||
Brain wallets should not be considered a secure way to store large amounts of ether; anyone who knows your username/password can steal your funds.
|
||||
|
||||
```javascript
|
||||
|
||||
// Username and passwords must be buffers; see scrypt-js library for summary
|
||||
// of UTF-8 gotchas (@TOOD: include a link)
|
||||
var email = new Wallet.utils.Buffer('github@ricmoo.com', 'utf8');
|
||||
var password = new Wallet.utils.Buffer('password', 'utf8');
|
||||
|
||||
var shouldCancelSummon = false;
|
||||
|
||||
function updateInterface(progress) {
|
||||
console.log('The wallet is ' + parseInt(100 * progress) + '% generated');
|
||||
|
||||
// Optionally return true to stop this generation; this callback will get
|
||||
// called once more with callback(new Error("cancelled"))
|
||||
return shouldCancelSummon;
|
||||
}
|
||||
|
||||
Wallet.summonBrainWallet(email, password, updateInterface).then(function(wallet) {
|
||||
// The wallet was successfully generated
|
||||
|
||||
}, function(error) {
|
||||
if (error.message === 'cancelled') {
|
||||
// Cancelled
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
Provider API
|
||||
------------
|
||||
|
||||
Connect to standard *Ethereum* nodes via RPC (if you have a local [parity](https://ethcore.io/parity.html) or [geth](https://github.com/ethereum/go-ethereum/wiki/Geth) instance running), or via [Etherscan](https://etherscan.io):
|
||||
|
||||
```javascript
|
||||
|
||||
// The Web3 library is NOT required, but if you have one (for example from Metamask)
|
||||
var web3Provider = new Web3.providers.HttpProvider('http://localhost:8545');
|
||||
var web3 = new Web3(web3Provider);
|
||||
|
||||
// All these are equivalent
|
||||
var wallet = new Wallet(privateKey, 'http://localhost:8545');
|
||||
var wallet = new Wallet(privateKey, web3Provider);
|
||||
var wallet = new Wallet(privateKey, web3);
|
||||
|
||||
// Or use Etherscan:
|
||||
var wallet = new Wallet(privateKey, new Wallet.providers.EtherscanProvider({testnet: true}));
|
||||
|
||||
|
||||
// With a provider attached, you can call additional methods on the wallet
|
||||
|
||||
// Get the wallet's balance
|
||||
wallet.getBalance().then(function(balance) {
|
||||
console.log(balance);
|
||||
});
|
||||
|
||||
// Get the current nonce for this wallet
|
||||
wallet.getTransactionCount().then(function(transactionCount) {
|
||||
console.log(transactionCount);
|
||||
})
|
||||
|
||||
// Send ether to another account or contract
|
||||
wallet.send(targetAddress, Wallet.parseEther('1.0')).then(function(txid) {
|
||||
console.log(txid);
|
||||
})
|
||||
```
|
||||
|
||||
Contract API
|
||||
------------
|
||||
|
||||
```javascript
|
||||
|
||||
var privateKey = '0x3141592653589793238462643383279502884197169399375105820974944592';
|
||||
|
||||
// Create your wallet with any method from the above Provider API
|
||||
var wallet = new Wallet(privateKey, 'http://localhost:8545')
|
||||
|
||||
console.log(wallet.address);
|
||||
/// "0x7357589f8e367c2C31F51242fB77B350A11830F3"
|
||||
|
||||
// Find an existing contract address and ABI
|
||||
// See: https://gist.github.com/ricmoo/e78709e075ff8082a86c875ac062c3c3
|
||||
var simpleStorageAddress = '0xdfaf84077cF4bCECA4F79d167F47041Ed3006D5b'
|
||||
var simpleStorageAbi = [
|
||||
{
|
||||
"constant":true,
|
||||
"inputs":[],
|
||||
"name":"getValue",
|
||||
"outputs":[{"name":"","type":"string"}],
|
||||
"type":"function"
|
||||
}, {
|
||||
"constant":false,
|
||||
"inputs":[{"name":"value","type":"string"}],
|
||||
"name":"setValue",
|
||||
"outputs":[],
|
||||
"type":"function"
|
||||
}, {
|
||||
"anonymous":false,
|
||||
"inputs":[
|
||||
{"indexed":false,"name":"oldValue","type":"string"},
|
||||
{"indexed":false,"name":"newValue","type":"string"}
|
||||
],
|
||||
"name":"valueChanged",
|
||||
"type":"event"
|
||||
}
|
||||
];
|
||||
|
||||
// Get the contract
|
||||
var contract = wallet.getContract(simpleStorageAddress, simpleStorageAbi)
|
||||
|
||||
// Set up events
|
||||
contract.onvaluechanged = function(oldValue, newValue) {
|
||||
console.log('Value Changed from "' + oldValue + '" to "' + newValue + '".')
|
||||
}
|
||||
|
||||
// Call constant methods, which don't alter state (free).
|
||||
// Returns a promise.
|
||||
contract.getValue().then(function(value) {
|
||||
console.log('Value is "' + value + '".')
|
||||
})
|
||||
|
||||
// Call state-changing methods (which will cost you ether, so use testnet to test!)
|
||||
// Returns a promise.
|
||||
contract.setValue("Hello World").then(function(txid) {
|
||||
console.log('txid: ' + txid);
|
||||
});
|
||||
|
||||
// Include custom parameters with a state-changing call
|
||||
var options = {
|
||||
gasPrice: 1000 // in wei (default: from network)
|
||||
gasLimit: 3000000, // is gas (default: 3000000)
|
||||
value: 1000 // in wei (default: 0)
|
||||
}
|
||||
contract.setValue("Hello World", options).then(function(txid) {
|
||||
console.log('txid: ' + txid);
|
||||
});
|
||||
|
||||
// Estimate the gas cost of calling a state-changing method (returns a BN.js)
|
||||
contract.estimate.setValue("Hello World").then(function(gasCost) {
|
||||
console.log(gasCost.toString(10));
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
Testing
|
||||
-------
|
||||
|
||||
A lot of the test cases are performed by comparing against known working implementations of many of the features of this library. To run the test suite, you must use `npm install` (without the `--production` flag, which would skip the development dependencies.)
|
||||
|
||||
To run the test suite,
|
||||
The JavaScript code is now generated from TypeScript, so make sure you modify the
|
||||
TypeScript and compile it, rather than modifying the JavaScript directly. To start
|
||||
auto-compiling the TypeScript code, you may use:
|
||||
|
||||
```
|
||||
/Users/ethers> npm test
|
||||
|
||||
> ethers-wallet@0.0.9 test /Users/ethers/ethers-wallet
|
||||
> nodeunit test.js
|
||||
|
||||
Running test cases... (this can take a long time, please be patient)
|
||||
|
||||
index.js
|
||||
+ testPrivateKeyToAddress
|
||||
+ testChecksumAddress
|
||||
+ testIcapAddress
|
||||
+ testEtherFormat
|
||||
+ testTrasactions
|
||||
+ testSolidityCoder
|
||||
+ testContracts
|
||||
+ testSecretStorage
|
||||
+ testBrainWallet
|
||||
+ testContractAddress
|
||||
+ testProviders
|
||||
|
||||
OK: 52178 assertions (147788ms)
|
||||
/home/ethers> npm run auto-build
|
||||
```
|
||||
|
||||
There are also some test JSON wallets available in the [test-wallets](https://github.com/ethers-io/ethers-wallet/tree/master/test-wallets) directory.
|
||||
A very important part of ethers is its exhaustive test cases, so before making any
|
||||
bug fix, please add a test case that fails prior to the fix, and succeeds after the
|
||||
fix. All regression tests must pass.
|
||||
|
||||
Pull requests are always welcome, but please keep a few points in mind:
|
||||
|
||||
- Compatibility-breaking changes will not be accepted; they may be considered for the next major version
|
||||
- Security is important; adding dependencies require fairly convincing arguments
|
||||
- The library aims to be lean, so keep an eye on the `dist/ethers.min.js` file size before and after your changes
|
||||
- Add test cases for both expected and unexpected input
|
||||
- Any new features need to be supported by us (issues, documentation, testing), so anything that is overly complicated or specific may not be accepted
|
||||
|
||||
In general, **please start an issue before beginning a pull request**, so we can have a public discussion. :)
|
||||
|
||||
|
||||
Donations
|
||||
---------
|
||||
|
||||
I do this because I love it, but if you want to buy me a coffee, I won't say no. **:o)**
|
||||
|
||||
Ethereum: `0xEA517D5a070e6705Cc5467858681Ed953d285Eb9`
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
MIT Licensed, with the exceptions:
|
||||
- RLP (MPL-2.0)
|
||||
|
||||
We are working on our own implementations and will have the library 100% MIT in the near future.
|
||||
|
||||
Stay tuned!
|
||||
Completely MIT Licensed. Including ALL dependencies.
|
||||
|
||||
1
_version.d.ts
vendored
Normal file
1
_version.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export declare const version = "4.0.29";
|
||||
3
_version.js
Normal file
3
_version.js
Normal file
@@ -0,0 +1,3 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.version = "4.0.29";
|
||||
11
abstract-signer.d.ts
vendored
Normal file
11
abstract-signer.d.ts
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Provider } from './providers/abstract-provider';
|
||||
import { Arrayish } from './utils/bytes';
|
||||
import { TransactionRequest, TransactionResponse } from './providers/abstract-provider';
|
||||
export declare abstract class Signer {
|
||||
readonly provider?: Provider;
|
||||
abstract getAddress(): Promise<string>;
|
||||
abstract signMessage(message: Arrayish | string): Promise<string>;
|
||||
abstract sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse>;
|
||||
constructor();
|
||||
static isSigner(value: any): value is Signer;
|
||||
}
|
||||
14
abstract-signer.js
Normal file
14
abstract-signer.js
Normal file
@@ -0,0 +1,14 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var properties_1 = require("./utils/properties");
|
||||
var Signer = /** @class */ (function () {
|
||||
function Signer() {
|
||||
properties_1.setType(this, 'Signer');
|
||||
}
|
||||
Signer.isSigner = function (value) {
|
||||
return properties_1.isType(value, 'Signer');
|
||||
};
|
||||
return Signer;
|
||||
}());
|
||||
exports.Signer = Signer;
|
||||
//defineReadOnly(Signer, 'inherits', inheritable(Signer));
|
||||
11
constants.d.ts
vendored
Normal file
11
constants.d.ts
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { BigNumber } from './utils/bignumber';
|
||||
declare const AddressZero = "0x0000000000000000000000000000000000000000";
|
||||
declare const HashZero = "0x0000000000000000000000000000000000000000000000000000000000000000";
|
||||
declare const EtherSymbol = "\u039E";
|
||||
declare const NegativeOne: BigNumber;
|
||||
declare const Zero: BigNumber;
|
||||
declare const One: BigNumber;
|
||||
declare const Two: BigNumber;
|
||||
declare const WeiPerEther: BigNumber;
|
||||
declare const MaxUint256: BigNumber;
|
||||
export { AddressZero, HashZero, EtherSymbol, NegativeOne, Zero, One, Two, WeiPerEther, MaxUint256 };
|
||||
24
constants.js
Normal file
24
constants.js
Normal file
@@ -0,0 +1,24 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var bignumber_1 = require("./utils/bignumber");
|
||||
var AddressZero = '0x0000000000000000000000000000000000000000';
|
||||
exports.AddressZero = AddressZero;
|
||||
var HashZero = '0x0000000000000000000000000000000000000000000000000000000000000000';
|
||||
exports.HashZero = HashZero;
|
||||
// NFKD (decomposed)
|
||||
//const EtherSymbol = '\uD835\uDF63';
|
||||
// NFKC (composed)
|
||||
var EtherSymbol = '\u039e';
|
||||
exports.EtherSymbol = EtherSymbol;
|
||||
var NegativeOne = bignumber_1.bigNumberify(-1);
|
||||
exports.NegativeOne = NegativeOne;
|
||||
var Zero = bignumber_1.bigNumberify(0);
|
||||
exports.Zero = Zero;
|
||||
var One = bignumber_1.bigNumberify(1);
|
||||
exports.One = One;
|
||||
var Two = bignumber_1.bigNumberify(2);
|
||||
exports.Two = Two;
|
||||
var WeiPerEther = bignumber_1.bigNumberify('1000000000000000000');
|
||||
exports.WeiPerEther = WeiPerEther;
|
||||
var MaxUint256 = bignumber_1.bigNumberify('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
|
||||
exports.MaxUint256 = MaxUint256;
|
||||
86
contract.d.ts
vendored
Normal file
86
contract.d.ts
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
import { BigNumber } from './utils/bignumber';
|
||||
import { Indexed, Interface } from './utils/interface';
|
||||
import { UnsignedTransaction } from './utils/transaction';
|
||||
import { BlockTag, Provider } from './providers/abstract-provider';
|
||||
import { Signer } from './abstract-signer';
|
||||
import { Arrayish } from './utils/bytes';
|
||||
import { ParamType } from './utils/abi-coder';
|
||||
import { Block, Listener, Log, TransactionReceipt, TransactionRequest, TransactionResponse } from './providers/abstract-provider';
|
||||
export declare type ContractFunction = (...params: Array<any>) => Promise<any>;
|
||||
export declare type EventFilter = {
|
||||
address?: string;
|
||||
topics?: Array<string>;
|
||||
};
|
||||
export interface Event extends Log {
|
||||
args?: Array<any>;
|
||||
decode?: (data: string, topics?: Array<string>) => any;
|
||||
event?: string;
|
||||
eventSignature?: string;
|
||||
removeListener: () => void;
|
||||
getBlock: () => Promise<Block>;
|
||||
getTransaction: () => Promise<TransactionResponse>;
|
||||
getTransactionReceipt: () => Promise<TransactionReceipt>;
|
||||
}
|
||||
export interface ContractReceipt extends TransactionReceipt {
|
||||
events?: Array<Event>;
|
||||
}
|
||||
export interface ContractTransaction extends TransactionResponse {
|
||||
wait(confirmations?: number): Promise<ContractReceipt>;
|
||||
}
|
||||
export declare class VoidSigner extends Signer {
|
||||
readonly address: string;
|
||||
constructor(address: string, provider: Provider);
|
||||
getAddress(): Promise<string>;
|
||||
_fail(message: string, operation: string): Promise<any>;
|
||||
signMessage(message: Arrayish | string): Promise<string>;
|
||||
sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse>;
|
||||
connect(provider: Provider): VoidSigner;
|
||||
}
|
||||
interface Bucket<T> {
|
||||
[name: string]: T;
|
||||
}
|
||||
export declare class Contract {
|
||||
readonly address: string;
|
||||
readonly interface: Interface;
|
||||
readonly signer: Signer;
|
||||
readonly provider: Provider;
|
||||
readonly estimate: Bucket<(...params: Array<any>) => Promise<BigNumber>>;
|
||||
readonly functions: Bucket<ContractFunction>;
|
||||
readonly filters: Bucket<(...params: Array<any>) => EventFilter>;
|
||||
readonly [name: string]: ContractFunction | any;
|
||||
readonly addressPromise: Promise<string>;
|
||||
readonly deployTransaction: TransactionResponse;
|
||||
private _deployedPromise;
|
||||
constructor(addressOrName: string, contractInterface: Array<string | ParamType> | string | Interface, signerOrProvider: Signer | Provider);
|
||||
deployed(): Promise<Contract>;
|
||||
_deployed(blockTag?: BlockTag): Promise<Contract>;
|
||||
fallback(overrides?: TransactionRequest): Promise<TransactionResponse>;
|
||||
connect(signerOrProvider: Signer | Provider | string): Contract;
|
||||
attach(addressOrName: string): Contract;
|
||||
static isIndexed(value: any): value is Indexed;
|
||||
private _events;
|
||||
private _getEventFilter;
|
||||
private _addEventListener;
|
||||
on(event: EventFilter | string, listener: Listener): Contract;
|
||||
once(event: EventFilter | string, listener: Listener): Contract;
|
||||
addListener(eventName: EventFilter | string, listener: Listener): Contract;
|
||||
emit(eventName: EventFilter | string, ...args: Array<any>): boolean;
|
||||
listenerCount(eventName?: EventFilter | string): number;
|
||||
listeners(eventName: EventFilter | string): Array<Listener>;
|
||||
removeAllListeners(eventName: EventFilter | string): Contract;
|
||||
removeListener(eventName: any, listener: Listener): Contract;
|
||||
}
|
||||
export declare class ContractFactory {
|
||||
readonly interface: Interface;
|
||||
readonly bytecode: string;
|
||||
readonly signer: Signer;
|
||||
constructor(contractInterface: Array<string | ParamType> | string | Interface, bytecode: Arrayish | string | {
|
||||
object: string;
|
||||
}, signer?: Signer);
|
||||
getDeployTransaction(...args: Array<any>): UnsignedTransaction;
|
||||
deploy(...args: Array<any>): Promise<Contract>;
|
||||
attach(address: string): Contract;
|
||||
connect(signer: Signer): ContractFactory;
|
||||
static fromSolidity(compilerOutput: any, signer?: Signer): ContractFactory;
|
||||
}
|
||||
export {};
|
||||
710
contract.js
Normal file
710
contract.js
Normal file
@@ -0,0 +1,710 @@
|
||||
'use strict';
|
||||
var __extends = (this && this.__extends) || (function () {
|
||||
var extendStatics = Object.setPrototypeOf ||
|
||||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
||||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
|
||||
return function (d, b) {
|
||||
extendStatics(d, b);
|
||||
function __() { this.constructor = d; }
|
||||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
||||
};
|
||||
})();
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
||||
result["default"] = mod;
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var constants_1 = require("./constants");
|
||||
var errors = __importStar(require("./errors"));
|
||||
var abi_coder_1 = require("./utils/abi-coder");
|
||||
var address_1 = require("./utils/address");
|
||||
var bignumber_1 = require("./utils/bignumber");
|
||||
var bytes_1 = require("./utils/bytes");
|
||||
var interface_1 = require("./utils/interface");
|
||||
var properties_1 = require("./utils/properties");
|
||||
///////////////////////////////
|
||||
// Imported Abstracts
|
||||
var abstract_provider_1 = require("./providers/abstract-provider");
|
||||
var abstract_signer_1 = require("./abstract-signer");
|
||||
///////////////////////////////
|
||||
var VoidSigner = /** @class */ (function (_super) {
|
||||
__extends(VoidSigner, _super);
|
||||
function VoidSigner(address, provider) {
|
||||
var _this = _super.call(this) || this;
|
||||
properties_1.defineReadOnly(_this, 'address', address);
|
||||
properties_1.defineReadOnly(_this, 'provider', provider);
|
||||
return _this;
|
||||
}
|
||||
VoidSigner.prototype.getAddress = function () {
|
||||
return Promise.resolve(this.address);
|
||||
};
|
||||
VoidSigner.prototype._fail = function (message, operation) {
|
||||
return Promise.resolve().then(function () {
|
||||
errors.throwError(message, errors.UNSUPPORTED_OPERATION, { operation: operation });
|
||||
});
|
||||
};
|
||||
VoidSigner.prototype.signMessage = function (message) {
|
||||
return this._fail('VoidSigner cannot sign messages', 'signMessage');
|
||||
};
|
||||
VoidSigner.prototype.sendTransaction = function (transaction) {
|
||||
return this._fail('VoidSigner cannot sign transactions', 'sendTransaction');
|
||||
};
|
||||
VoidSigner.prototype.connect = function (provider) {
|
||||
return new VoidSigner(this.address, provider);
|
||||
};
|
||||
return VoidSigner;
|
||||
}(abstract_signer_1.Signer));
|
||||
exports.VoidSigner = VoidSigner;
|
||||
var allowedTransactionKeys = {
|
||||
chainId: true, data: true, from: true, gasLimit: true, gasPrice: true, nonce: true, to: true, value: true
|
||||
};
|
||||
// Recursively replaces ENS names with promises to resolve the name and
|
||||
// stalls until all promises have returned
|
||||
// @TODO: Expand this to resolve any promises too
|
||||
function resolveAddresses(provider, value, paramType) {
|
||||
if (Array.isArray(paramType)) {
|
||||
var promises_1 = [];
|
||||
paramType.forEach(function (paramType, index) {
|
||||
var v = null;
|
||||
if (Array.isArray(value)) {
|
||||
v = value[index];
|
||||
}
|
||||
else {
|
||||
v = value[paramType.name];
|
||||
}
|
||||
promises_1.push(resolveAddresses(provider, v, paramType));
|
||||
});
|
||||
return Promise.all(promises_1);
|
||||
}
|
||||
if (paramType.type === 'address') {
|
||||
return provider.resolveName(value);
|
||||
}
|
||||
if (paramType.type === 'tuple') {
|
||||
return resolveAddresses(provider, value, paramType.components);
|
||||
}
|
||||
// Strips one level of array indexing off the end to recuse into
|
||||
var isArrayMatch = paramType.type.match(/(.*)(\[[0-9]*\]$)/);
|
||||
if (isArrayMatch) {
|
||||
if (!Array.isArray(value)) {
|
||||
throw new Error('invalid value for array');
|
||||
}
|
||||
var promises = [];
|
||||
var subParamType = {
|
||||
components: paramType.components,
|
||||
type: isArrayMatch[1],
|
||||
};
|
||||
value.forEach(function (v) {
|
||||
promises.push(resolveAddresses(provider, v, subParamType));
|
||||
});
|
||||
return Promise.all(promises);
|
||||
}
|
||||
return Promise.resolve(value);
|
||||
}
|
||||
function runMethod(contract, functionName, estimateOnly) {
|
||||
var method = contract.interface.functions[functionName];
|
||||
return function () {
|
||||
var params = [];
|
||||
for (var _i = 0; _i < arguments.length; _i++) {
|
||||
params[_i] = arguments[_i];
|
||||
}
|
||||
var tx = {};
|
||||
var blockTag = null;
|
||||
// If 1 extra parameter was passed in, it contains overrides
|
||||
if (params.length === method.inputs.length + 1 && typeof (params[params.length - 1]) === 'object') {
|
||||
tx = properties_1.shallowCopy(params.pop());
|
||||
if (tx.blockTag != null) {
|
||||
blockTag = tx.blockTag;
|
||||
}
|
||||
delete tx.blockTag;
|
||||
// Check for unexpected keys (e.g. using "gas" instead of "gasLimit")
|
||||
for (var key in tx) {
|
||||
if (!allowedTransactionKeys[key]) {
|
||||
throw new Error('unknown transaction override ' + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (params.length != method.inputs.length) {
|
||||
throw new Error('incorrect number of arguments');
|
||||
}
|
||||
// Check overrides make sense
|
||||
['data', 'to'].forEach(function (key) {
|
||||
if (tx[key] != null) {
|
||||
errors.throwError('cannot override ' + key, errors.UNSUPPORTED_OPERATION, { operation: key });
|
||||
}
|
||||
});
|
||||
tx.to = contract._deployed(blockTag).then(function () {
|
||||
return contract.addressPromise;
|
||||
});
|
||||
return resolveAddresses(contract.provider, params, method.inputs).then(function (params) {
|
||||
tx.data = method.encode(params);
|
||||
if (method.type === 'call') {
|
||||
// Call (constant functions) always cost 0 ether
|
||||
if (estimateOnly) {
|
||||
return Promise.resolve(constants_1.Zero);
|
||||
}
|
||||
if (!contract.provider) {
|
||||
errors.throwError('call (constant functions) require a provider or a signer with a provider', errors.UNSUPPORTED_OPERATION, { operation: 'call' });
|
||||
}
|
||||
// Check overrides make sense
|
||||
['gasLimit', 'gasPrice', 'value'].forEach(function (key) {
|
||||
if (tx[key] != null) {
|
||||
throw new Error('call cannot override ' + key);
|
||||
}
|
||||
});
|
||||
if (tx.from == null && contract.signer) {
|
||||
tx.from = contract.signer.getAddress();
|
||||
}
|
||||
return contract.provider.call(tx, blockTag).then(function (value) {
|
||||
if ((bytes_1.hexDataLength(value) % 32) === 4 && bytes_1.hexDataSlice(value, 0, 4) === '0x08c379a0') {
|
||||
var reason = abi_coder_1.defaultAbiCoder.decode(['string'], bytes_1.hexDataSlice(value, 4));
|
||||
errors.throwError('call revert exception', errors.CALL_EXCEPTION, {
|
||||
address: contract.address,
|
||||
args: params,
|
||||
method: method.signature,
|
||||
errorSignature: 'Error(string)',
|
||||
errorArgs: [reason],
|
||||
reason: reason,
|
||||
transaction: tx
|
||||
});
|
||||
}
|
||||
try {
|
||||
var result = method.decode(value);
|
||||
if (method.outputs.length === 1) {
|
||||
result = result[0];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (error) {
|
||||
if (value === '0x' && method.outputs.length > 0) {
|
||||
errors.throwError('call exception', errors.CALL_EXCEPTION, {
|
||||
address: contract.address,
|
||||
method: method.signature,
|
||||
args: params
|
||||
});
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (method.type === 'transaction') {
|
||||
// Only computing the transaction estimate
|
||||
if (estimateOnly) {
|
||||
if (!contract.provider) {
|
||||
errors.throwError('estimate gas require a provider or a signer with a provider', errors.UNSUPPORTED_OPERATION, { operation: 'estimateGas' });
|
||||
}
|
||||
if (tx.from == null && contract.signer) {
|
||||
tx.from = contract.signer.getAddress();
|
||||
}
|
||||
return contract.provider.estimateGas(tx);
|
||||
}
|
||||
if (tx.gasLimit == null && method.gas != null) {
|
||||
tx.gasLimit = bignumber_1.bigNumberify(method.gas).add(21000);
|
||||
}
|
||||
if (!contract.signer) {
|
||||
errors.throwError('sending a transaction require a signer', errors.UNSUPPORTED_OPERATION, { operation: 'sendTransaction' });
|
||||
}
|
||||
// Make sure they aren't overriding something they shouldn't
|
||||
if (tx.from != null) {
|
||||
errors.throwError('cannot override from in a transaction', errors.UNSUPPORTED_OPERATION, { operation: 'sendTransaction' });
|
||||
}
|
||||
return contract.signer.sendTransaction(tx).then(function (tx) {
|
||||
var wait = tx.wait.bind(tx);
|
||||
tx.wait = function (confirmations) {
|
||||
return wait(confirmations).then(function (receipt) {
|
||||
receipt.events = receipt.logs.map(function (log) {
|
||||
var event = properties_1.deepCopy(log);
|
||||
var parsed = contract.interface.parseLog(log);
|
||||
if (parsed) {
|
||||
event.args = parsed.values;
|
||||
event.decode = parsed.decode;
|
||||
event.event = parsed.name;
|
||||
event.eventSignature = parsed.signature;
|
||||
}
|
||||
event.removeListener = function () { return contract.provider; };
|
||||
event.getBlock = function () {
|
||||
return contract.provider.getBlock(receipt.blockHash);
|
||||
};
|
||||
event.getTransaction = function () {
|
||||
return contract.provider.getTransaction(receipt.transactionHash);
|
||||
};
|
||||
event.getTransactionReceipt = function () {
|
||||
return Promise.resolve(receipt);
|
||||
};
|
||||
return event;
|
||||
});
|
||||
return receipt;
|
||||
});
|
||||
};
|
||||
return tx;
|
||||
});
|
||||
}
|
||||
throw new Error('invalid type - ' + method.type);
|
||||
return null;
|
||||
});
|
||||
};
|
||||
}
|
||||
function getEventTag(filter) {
|
||||
if (filter.address && (filter.topics == null || filter.topics.length === 0)) {
|
||||
return '*';
|
||||
}
|
||||
return (filter.address || '*') + '@' + (filter.topics ? filter.topics.join(':') : '');
|
||||
}
|
||||
var Contract = /** @class */ (function () {
|
||||
// https://github.com/Microsoft/TypeScript/issues/5453
|
||||
// Once this issue is resolved (there are open PR) we can do this nicer
|
||||
// by making addressOrName default to null for 2 operand calls. :)
|
||||
function Contract(addressOrName, contractInterface, signerOrProvider) {
|
||||
var _this = this;
|
||||
errors.checkNew(this, Contract);
|
||||
// @TODO: Maybe still check the addressOrName looks like a valid address or name?
|
||||
//address = getAddress(address);
|
||||
if (interface_1.Interface.isInterface(contractInterface)) {
|
||||
properties_1.defineReadOnly(this, 'interface', contractInterface);
|
||||
}
|
||||
else {
|
||||
properties_1.defineReadOnly(this, 'interface', new interface_1.Interface(contractInterface));
|
||||
}
|
||||
if (abstract_signer_1.Signer.isSigner(signerOrProvider)) {
|
||||
properties_1.defineReadOnly(this, 'provider', signerOrProvider.provider);
|
||||
properties_1.defineReadOnly(this, 'signer', signerOrProvider);
|
||||
}
|
||||
else if (abstract_provider_1.Provider.isProvider(signerOrProvider)) {
|
||||
properties_1.defineReadOnly(this, 'provider', signerOrProvider);
|
||||
properties_1.defineReadOnly(this, 'signer', null);
|
||||
}
|
||||
else {
|
||||
errors.throwError('invalid signer or provider', errors.INVALID_ARGUMENT, { arg: 'signerOrProvider', value: signerOrProvider });
|
||||
}
|
||||
properties_1.defineReadOnly(this, 'estimate', {});
|
||||
properties_1.defineReadOnly(this, 'functions', {});
|
||||
properties_1.defineReadOnly(this, 'filters', {});
|
||||
Object.keys(this.interface.events).forEach(function (eventName) {
|
||||
var event = _this.interface.events[eventName];
|
||||
properties_1.defineReadOnly(_this.filters, eventName, function () {
|
||||
var args = [];
|
||||
for (var _i = 0; _i < arguments.length; _i++) {
|
||||
args[_i] = arguments[_i];
|
||||
}
|
||||
return {
|
||||
address: _this.address,
|
||||
topics: event.encodeTopics(args)
|
||||
};
|
||||
});
|
||||
});
|
||||
this._events = [];
|
||||
properties_1.defineReadOnly(this, 'address', addressOrName);
|
||||
if (this.provider) {
|
||||
properties_1.defineReadOnly(this, 'addressPromise', this.provider.resolveName(addressOrName).then(function (address) {
|
||||
if (address == null) {
|
||||
throw new Error('name not found');
|
||||
}
|
||||
return address;
|
||||
}).catch(function (error) {
|
||||
throw error;
|
||||
}));
|
||||
}
|
||||
else {
|
||||
try {
|
||||
properties_1.defineReadOnly(this, 'addressPromise', Promise.resolve(address_1.getAddress(addressOrName)));
|
||||
}
|
||||
catch (error) {
|
||||
// Without a provider, we cannot use ENS names
|
||||
errors.throwError('provider is required to use non-address contract address', errors.INVALID_ARGUMENT, { argument: 'addressOrName', value: addressOrName });
|
||||
}
|
||||
}
|
||||
Object.keys(this.interface.functions).forEach(function (name) {
|
||||
var run = runMethod(_this, name, false);
|
||||
if (_this[name] == null) {
|
||||
properties_1.defineReadOnly(_this, name, run);
|
||||
}
|
||||
else {
|
||||
errors.warn('WARNING: Multiple definitions for ' + name);
|
||||
}
|
||||
if (_this.functions[name] == null) {
|
||||
properties_1.defineReadOnly(_this.functions, name, run);
|
||||
properties_1.defineReadOnly(_this.estimate, name, runMethod(_this, name, true));
|
||||
}
|
||||
});
|
||||
}
|
||||
// @TODO: Allow timeout?
|
||||
Contract.prototype.deployed = function () {
|
||||
return this._deployed();
|
||||
};
|
||||
Contract.prototype._deployed = function (blockTag) {
|
||||
var _this = this;
|
||||
if (!this._deployedPromise) {
|
||||
// If we were just deployed, we know the transaction we should occur in
|
||||
if (this.deployTransaction) {
|
||||
this._deployedPromise = this.deployTransaction.wait().then(function () {
|
||||
return _this;
|
||||
});
|
||||
}
|
||||
else {
|
||||
// @TODO: Once we allow a timeout to be passed in, we will wait
|
||||
// up to that many blocks for getCode
|
||||
// Otherwise, poll for our code to be deployed
|
||||
this._deployedPromise = this.provider.getCode(this.address, blockTag).then(function (code) {
|
||||
if (code === '0x') {
|
||||
errors.throwError('contract not deployed', errors.UNSUPPORTED_OPERATION, {
|
||||
contractAddress: _this.address,
|
||||
operation: 'getDeployed'
|
||||
});
|
||||
}
|
||||
return _this;
|
||||
});
|
||||
}
|
||||
}
|
||||
return this._deployedPromise;
|
||||
};
|
||||
// @TODO:
|
||||
// estimateFallback(overrides?: TransactionRequest): Promise<BigNumber>
|
||||
// @TODO:
|
||||
// estimateDeploy(bytecode: string, ...args): Promise<BigNumber>
|
||||
Contract.prototype.fallback = function (overrides) {
|
||||
var _this = this;
|
||||
if (!this.signer) {
|
||||
errors.throwError('sending a transaction require a signer', errors.UNSUPPORTED_OPERATION, { operation: 'sendTransaction(fallback)' });
|
||||
}
|
||||
var tx = properties_1.shallowCopy(overrides || {});
|
||||
['from', 'to'].forEach(function (key) {
|
||||
if (tx[key] == null) {
|
||||
return;
|
||||
}
|
||||
errors.throwError('cannot override ' + key, errors.UNSUPPORTED_OPERATION, { operation: key });
|
||||
});
|
||||
tx.to = this.addressPromise;
|
||||
return this.deployed().then(function () {
|
||||
return _this.signer.sendTransaction(tx);
|
||||
});
|
||||
};
|
||||
// Reconnect to a different signer or provider
|
||||
Contract.prototype.connect = function (signerOrProvider) {
|
||||
if (typeof (signerOrProvider) === 'string') {
|
||||
signerOrProvider = new VoidSigner(signerOrProvider, this.provider);
|
||||
}
|
||||
var contract = new Contract(this.address, this.interface, signerOrProvider);
|
||||
if (this.deployTransaction) {
|
||||
properties_1.defineReadOnly(contract, 'deployTransaction', this.deployTransaction);
|
||||
}
|
||||
return contract;
|
||||
};
|
||||
// Re-attach to a different on=chain instance of this contract
|
||||
Contract.prototype.attach = function (addressOrName) {
|
||||
return new Contract(addressOrName, this.interface, this.signer || this.provider);
|
||||
};
|
||||
Contract.isIndexed = function (value) {
|
||||
return interface_1.Interface.isIndexed(value);
|
||||
};
|
||||
Contract.prototype._getEventFilter = function (eventName) {
|
||||
var _this = this;
|
||||
if (typeof (eventName) === 'string') {
|
||||
// Listen for any event
|
||||
if (eventName === '*') {
|
||||
return {
|
||||
prepareEvent: function (e) {
|
||||
var parsed = _this.interface.parseLog(e);
|
||||
if (parsed) {
|
||||
e.args = parsed.values;
|
||||
e.decode = parsed.decode;
|
||||
e.event = parsed.name;
|
||||
e.eventSignature = parsed.signature;
|
||||
}
|
||||
return [e];
|
||||
},
|
||||
eventTag: '*',
|
||||
filter: { address: this.address },
|
||||
};
|
||||
}
|
||||
// Normalize the eventName
|
||||
if (eventName.indexOf('(') !== -1) {
|
||||
eventName = abi_coder_1.formatSignature(abi_coder_1.parseSignature('event ' + eventName));
|
||||
}
|
||||
var event_1 = this.interface.events[eventName];
|
||||
if (!event_1) {
|
||||
errors.throwError('unknown event - ' + eventName, errors.INVALID_ARGUMENT, { argumnet: 'eventName', value: eventName });
|
||||
}
|
||||
var filter_1 = {
|
||||
address: this.address,
|
||||
topics: [event_1.topic]
|
||||
};
|
||||
return {
|
||||
prepareEvent: function (e) {
|
||||
var args = event_1.decode(e.data, e.topics);
|
||||
e.args = args;
|
||||
var result = Array.prototype.slice.call(args);
|
||||
result.push(e);
|
||||
return result;
|
||||
},
|
||||
event: event_1,
|
||||
eventTag: getEventTag(filter_1),
|
||||
filter: filter_1
|
||||
};
|
||||
}
|
||||
var filter = {
|
||||
address: this.address
|
||||
};
|
||||
// Find the matching event in the ABI; if none, we still allow filtering
|
||||
// since it may be a filter for an otherwise unknown event
|
||||
var event = null;
|
||||
if (eventName.topics && eventName.topics[0]) {
|
||||
filter.topics = eventName.topics;
|
||||
for (var name_1 in this.interface.events) {
|
||||
if (name_1.indexOf('(') === -1) {
|
||||
continue;
|
||||
}
|
||||
var e = this.interface.events[name_1];
|
||||
if (e.topic === eventName.topics[0].toLowerCase()) {
|
||||
event = e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
prepareEvent: function (e) {
|
||||
if (!event) {
|
||||
return [e];
|
||||
}
|
||||
var args = event.decode(e.data, e.topics);
|
||||
e.args = args;
|
||||
var result = Array.prototype.slice.call(args);
|
||||
result.push(e);
|
||||
return result;
|
||||
},
|
||||
event: event,
|
||||
eventTag: getEventTag(filter),
|
||||
filter: filter
|
||||
};
|
||||
};
|
||||
Contract.prototype._addEventListener = function (eventFilter, listener, once) {
|
||||
var _this = this;
|
||||
if (!this.provider) {
|
||||
errors.throwError('events require a provider or a signer with a provider', errors.UNSUPPORTED_OPERATION, { operation: 'once' });
|
||||
}
|
||||
var wrappedListener = function (log) {
|
||||
var event = properties_1.deepCopy(log);
|
||||
var args = eventFilter.prepareEvent(event);
|
||||
if (eventFilter.event) {
|
||||
event.decode = eventFilter.event.decode;
|
||||
event.event = eventFilter.event.name;
|
||||
event.eventSignature = eventFilter.event.signature;
|
||||
}
|
||||
event.removeListener = function () { _this.removeListener(eventFilter.filter, listener); };
|
||||
event.getBlock = function () { return _this.provider.getBlock(log.blockHash); };
|
||||
event.getTransaction = function () { return _this.provider.getTransaction(log.transactionHash); };
|
||||
event.getTransactionReceipt = function () { return _this.provider.getTransactionReceipt(log.transactionHash); };
|
||||
_this.emit.apply(_this, [eventFilter.filter].concat(args));
|
||||
};
|
||||
this.provider.on(eventFilter.filter, wrappedListener);
|
||||
this._events.push({ eventFilter: eventFilter, listener: listener, wrappedListener: wrappedListener, once: once });
|
||||
};
|
||||
Contract.prototype.on = function (event, listener) {
|
||||
this._addEventListener(this._getEventFilter(event), listener, false);
|
||||
return this;
|
||||
};
|
||||
Contract.prototype.once = function (event, listener) {
|
||||
this._addEventListener(this._getEventFilter(event), listener, true);
|
||||
return this;
|
||||
};
|
||||
Contract.prototype.addListener = function (eventName, listener) {
|
||||
return this.on(eventName, listener);
|
||||
};
|
||||
Contract.prototype.emit = function (eventName) {
|
||||
var _this = this;
|
||||
var args = [];
|
||||
for (var _i = 1; _i < arguments.length; _i++) {
|
||||
args[_i - 1] = arguments[_i];
|
||||
}
|
||||
if (!this.provider) {
|
||||
return false;
|
||||
}
|
||||
var result = false;
|
||||
var eventFilter = this._getEventFilter(eventName);
|
||||
this._events = this._events.filter(function (event) {
|
||||
// Not this event (keep it for later)
|
||||
if (event.eventFilter.eventTag !== eventFilter.eventTag) {
|
||||
return true;
|
||||
}
|
||||
// Call the callback in the next event loop
|
||||
setTimeout(function () {
|
||||
event.listener.apply(_this, args);
|
||||
}, 0);
|
||||
result = true;
|
||||
// Reschedule it if it not "once"
|
||||
return !(event.once);
|
||||
});
|
||||
return result;
|
||||
};
|
||||
Contract.prototype.listenerCount = function (eventName) {
|
||||
if (!this.provider) {
|
||||
return 0;
|
||||
}
|
||||
var eventFilter = this._getEventFilter(eventName);
|
||||
return this._events.filter(function (event) {
|
||||
return event.eventFilter.eventTag === eventFilter.eventTag;
|
||||
}).length;
|
||||
};
|
||||
Contract.prototype.listeners = function (eventName) {
|
||||
if (!this.provider) {
|
||||
return [];
|
||||
}
|
||||
var eventFilter = this._getEventFilter(eventName);
|
||||
return this._events.filter(function (event) {
|
||||
return event.eventFilter.eventTag === eventFilter.eventTag;
|
||||
}).map(function (event) { return event.listener; });
|
||||
};
|
||||
Contract.prototype.removeAllListeners = function (eventName) {
|
||||
var _this = this;
|
||||
if (!this.provider) {
|
||||
return this;
|
||||
}
|
||||
var eventFilter = this._getEventFilter(eventName);
|
||||
this._events = this._events.filter(function (event) {
|
||||
// Keep all other events
|
||||
if (event.eventFilter.eventTag !== eventFilter.eventTag) {
|
||||
return true;
|
||||
}
|
||||
// Deregister this event from the provider and filter it out
|
||||
_this.provider.removeListener(event.eventFilter.filter, event.wrappedListener);
|
||||
return false;
|
||||
});
|
||||
return this;
|
||||
};
|
||||
Contract.prototype.removeListener = function (eventName, listener) {
|
||||
var _this = this;
|
||||
if (!this.provider) {
|
||||
return this;
|
||||
}
|
||||
var found = false;
|
||||
var eventFilter = this._getEventFilter(eventName);
|
||||
this._events = this._events.filter(function (event) {
|
||||
// Make sure this event and listener match
|
||||
if (event.eventFilter.eventTag !== eventFilter.eventTag) {
|
||||
return true;
|
||||
}
|
||||
if (event.listener !== listener) {
|
||||
return true;
|
||||
}
|
||||
_this.provider.removeListener(event.eventFilter.filter, event.wrappedListener);
|
||||
// Already found a matching event in a previous loop
|
||||
if (found) {
|
||||
return true;
|
||||
}
|
||||
// REmove this event (returning false filters us out)
|
||||
found = true;
|
||||
return false;
|
||||
});
|
||||
return this;
|
||||
};
|
||||
return Contract;
|
||||
}());
|
||||
exports.Contract = Contract;
|
||||
var ContractFactory = /** @class */ (function () {
|
||||
function ContractFactory(contractInterface, bytecode, signer) {
|
||||
var bytecodeHex = null;
|
||||
// Allow the bytecode object from the Solidity compiler
|
||||
if (typeof (bytecode) === 'string') {
|
||||
bytecodeHex = bytecode;
|
||||
}
|
||||
else if (bytes_1.isArrayish(bytecode)) {
|
||||
bytecodeHex = bytes_1.hexlify(bytecode);
|
||||
}
|
||||
else if (typeof (bytecode.object) === 'string') {
|
||||
bytecodeHex = bytecode.object;
|
||||
}
|
||||
else {
|
||||
errors.throwError('bytecode must be a valid hex string', errors.INVALID_ARGUMENT, { arg: 'bytecode', value: bytecode });
|
||||
}
|
||||
// Make sure it is 0x prefixed
|
||||
if (bytecodeHex.substring(0, 2) !== '0x') {
|
||||
bytecodeHex = '0x' + bytecodeHex;
|
||||
}
|
||||
if (!bytes_1.isHexString(bytecodeHex)) {
|
||||
errors.throwError('bytecode must be a valid hex string', errors.INVALID_ARGUMENT, { arg: 'bytecode', value: bytecode });
|
||||
}
|
||||
if ((bytecodeHex.length % 2) !== 0) {
|
||||
errors.throwError('bytecode must be valid data (even length)', errors.INVALID_ARGUMENT, { arg: 'bytecode', value: bytecode });
|
||||
}
|
||||
properties_1.defineReadOnly(this, 'bytecode', bytecodeHex);
|
||||
if (interface_1.Interface.isInterface(contractInterface)) {
|
||||
properties_1.defineReadOnly(this, 'interface', contractInterface);
|
||||
}
|
||||
else {
|
||||
properties_1.defineReadOnly(this, 'interface', new interface_1.Interface(contractInterface));
|
||||
}
|
||||
if (signer && !abstract_signer_1.Signer.isSigner(signer)) {
|
||||
errors.throwError('invalid signer', errors.INVALID_ARGUMENT, { arg: 'signer', value: null });
|
||||
}
|
||||
properties_1.defineReadOnly(this, 'signer', signer || null);
|
||||
}
|
||||
ContractFactory.prototype.getDeployTransaction = function () {
|
||||
var args = [];
|
||||
for (var _i = 0; _i < arguments.length; _i++) {
|
||||
args[_i] = arguments[_i];
|
||||
}
|
||||
var tx = {};
|
||||
// If we have 1 additional argument, we allow transaction overrides
|
||||
if (args.length === this.interface.deployFunction.inputs.length + 1) {
|
||||
tx = properties_1.shallowCopy(args.pop());
|
||||
for (var key in tx) {
|
||||
if (!allowedTransactionKeys[key]) {
|
||||
throw new Error('unknown transaction override ' + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Do not allow these to be overridden in a deployment transaction
|
||||
['data', 'from', 'to'].forEach(function (key) {
|
||||
if (tx[key] == null) {
|
||||
return;
|
||||
}
|
||||
errors.throwError('cannot override ' + key, errors.UNSUPPORTED_OPERATION, { operation: key });
|
||||
});
|
||||
// Make sure the call matches the constructor signature
|
||||
errors.checkArgumentCount(args.length, this.interface.deployFunction.inputs.length, ' in Contract constructor');
|
||||
// Set the data to the bytecode + the encoded constructor arguments
|
||||
tx.data = this.interface.deployFunction.encode(this.bytecode, args);
|
||||
return tx;
|
||||
};
|
||||
ContractFactory.prototype.deploy = function () {
|
||||
var _this = this;
|
||||
var args = [];
|
||||
for (var _i = 0; _i < arguments.length; _i++) {
|
||||
args[_i] = arguments[_i];
|
||||
}
|
||||
// Get the deployment transaction (with optional overrides)
|
||||
var tx = this.getDeployTransaction.apply(this, args);
|
||||
// Send the deployment transaction
|
||||
return this.signer.sendTransaction(tx).then(function (tx) {
|
||||
var contract = new Contract(address_1.getContractAddress(tx), _this.interface, _this.signer);
|
||||
properties_1.defineReadOnly(contract, 'deployTransaction', tx);
|
||||
return contract;
|
||||
});
|
||||
};
|
||||
ContractFactory.prototype.attach = function (address) {
|
||||
return new Contract(address, this.interface, this.signer);
|
||||
};
|
||||
ContractFactory.prototype.connect = function (signer) {
|
||||
return new ContractFactory(this.interface, this.bytecode, signer);
|
||||
};
|
||||
ContractFactory.fromSolidity = function (compilerOutput, signer) {
|
||||
if (compilerOutput == null) {
|
||||
errors.throwError('missing compiler output', errors.MISSING_ARGUMENT, { argument: 'compilerOutput' });
|
||||
}
|
||||
if (typeof (compilerOutput) === 'string') {
|
||||
compilerOutput = JSON.parse(compilerOutput);
|
||||
}
|
||||
var abi = compilerOutput.abi;
|
||||
var bytecode = null;
|
||||
if (compilerOutput.bytecode) {
|
||||
bytecode = compilerOutput.bytecode;
|
||||
}
|
||||
else if (compilerOutput.evm && compilerOutput.evm.bytecode) {
|
||||
bytecode = compilerOutput.evm.bytecode;
|
||||
}
|
||||
return new ContractFactory(abi, bytecode, signer);
|
||||
};
|
||||
return ContractFactory;
|
||||
}());
|
||||
exports.ContractFactory = ContractFactory;
|
||||
274
examples/wallet/index.html → dist/demo/index.html
vendored
274
examples/wallet/index.html → dist/demo/index.html
vendored
@@ -1,42 +1,13 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Ethereum Wallet</title>
|
||||
<link rel="stylesheet" type="text/css" href="../style.css">
|
||||
<link rel="stylesheet" type="text/css" href="./style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="centerer" id="screen-select">
|
||||
<div class="centered">
|
||||
<h1>Ethereum Wallet Tool</h1>
|
||||
<hr />
|
||||
<h2>Summon Brain Wallet</h2>
|
||||
<p>
|
||||
A brain wallet generates an <i>Ethereum</i> wallet from a username and a password
|
||||
without using any servers to store your information. If you lose your username
|
||||
or password, <b>no one</b> can help you recover them. Anyone who can guess your
|
||||
username and password can steal your funds. Brain wallets should <b>not</b> be
|
||||
considered a safe way to store large amounts of ether nor for long periods of time.
|
||||
</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>E-mail Address:</th>
|
||||
<td><input type="text" placeholder="(e-mail address)" id="select-brainwallet-username" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Password:</th>
|
||||
<td><input type="password" placeholder="(password)" id="select-brainwallet-password" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Confirm Password:</th>
|
||||
<td><input type="password" placeholder="(same password)" id="select-brainwallet-confirm-password" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>
|
||||
<div id="select-submit-brainwallet" class="submit disable">Summon Brain Wallet</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr />
|
||||
<h2>Load JSON Wallet</h2>
|
||||
<p>
|
||||
If you have a JSON wallet file from <i>geth</i> or from the initial <i>Ethereum</i>
|
||||
@@ -60,6 +31,29 @@
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr />
|
||||
<h2>Mnemonic Phrase Wallet</h2>
|
||||
<p>
|
||||
If you have a 12 word mnemonic phrase, you can generate your wallet here.
|
||||
No information is shared with <b>any</b> server.
|
||||
</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Mnemonic Phrase:</th>
|
||||
<td><input type="text" placeholder="(mnemonic phrase)" id="select-mnemonic-phrase" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Path:</th>
|
||||
<td><input type="text" placeholder="(path)" id="select-mnemonic-path" value="m/44'/60'/0'/0/0" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>
|
||||
<div id="select-submit-mnemonic" class="submit disable">Derive Wallet</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr />
|
||||
<h2>Raw Private Key</h2>
|
||||
<p>
|
||||
@@ -72,7 +66,7 @@
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>
|
||||
<div id="select-submit-privatekey" class="submit disable">Unlock JSON Wallet</div>
|
||||
<div id="select-submit-privatekey" class="submit disable">Load Raw Private Key</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -126,8 +120,12 @@
|
||||
<tr>
|
||||
<th>Network:</th>
|
||||
<td>
|
||||
<div class="option left" id="option-morden">Morden (testnet)</div>
|
||||
<div class="option right selected" id="option-homestead">Homestead (mainnet)</div>
|
||||
<div class="clearfix"></div>
|
||||
<div class="network option" data-network="ropsten">Ropsten<br /><span>testnet</span></div>
|
||||
<div class="network option" data-network="rinkeby">Rinkeby<br /><span>testnet</span></div>
|
||||
<div class="network option" data-network="kovan">Kovan<br /><span>testnet</span></div>
|
||||
<div class="network option last selected" data-network="homestead">Homestead<br /><span>mainnet</span></div>
|
||||
<div class="clearfix"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -177,8 +175,16 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="../../dist/ethers-wallet.js"></script>
|
||||
<script type="text/javascript" src="../../dist/ethers.js"></script>
|
||||
<script type="text/javascript">
|
||||
function query(el, selector) {
|
||||
if (!selector) {
|
||||
selector = el;
|
||||
el = document;
|
||||
}
|
||||
return Array.prototype.slice.call(el.querySelectorAll(selector));
|
||||
}
|
||||
|
||||
function setEnter(source, target) {
|
||||
source.onkeyup = function(e) {
|
||||
if (e.which === 13) { target.click(); }
|
||||
@@ -190,58 +196,15 @@
|
||||
cancelScrypt = true;
|
||||
};
|
||||
|
||||
(function() {
|
||||
var inputUsername = document.getElementById('select-brainwallet-username');
|
||||
var inputPassword = document.getElementById('select-brainwallet-password');
|
||||
var inputConfirmPassword = document.getElementById('select-brainwallet-confirm-password');
|
||||
var submit = document.getElementById('select-submit-brainwallet');
|
||||
|
||||
function checkBrainwallet() {
|
||||
if (inputUsername.value && inputPassword.value && inputPassword.value === inputConfirmPassword.value) {
|
||||
submit.classList.remove('disable');
|
||||
} else {
|
||||
submit.classList.add('disable');
|
||||
}
|
||||
}
|
||||
inputUsername.oninput = checkBrainwallet;
|
||||
inputPassword.oninput = checkBrainwallet;
|
||||
inputConfirmPassword.oninput = checkBrainwallet;
|
||||
|
||||
setEnter(inputUsername, submit);
|
||||
setEnter(inputPassword, submit);
|
||||
setEnter(inputConfirmPassword, submit);
|
||||
|
||||
submit.onclick = function() {
|
||||
if (submit.classList.contains('disable')) { return; }
|
||||
|
||||
var username = new Wallet.utils.Buffer(inputUsername.value, 'utf8');
|
||||
var password = new Wallet.utils.Buffer(inputPassword.value, 'utf8');
|
||||
|
||||
showLoading('Summoning Brain Wallet...');
|
||||
|
||||
cancelScrypt = false;
|
||||
|
||||
Wallet.summonBrainWallet(username, password, function(error, wallet, progress) {
|
||||
if (error) {
|
||||
if (error.message !== 'cancelled') {
|
||||
alert('Unknown error');
|
||||
}
|
||||
showSelect();
|
||||
|
||||
} else if (wallet) {
|
||||
showWallet(wallet);
|
||||
document.getElementById('wallet-username').textContent = inputUsername.value;
|
||||
|
||||
} else {
|
||||
updateLoading(progress);
|
||||
}
|
||||
|
||||
return cancelScrypt;
|
||||
});
|
||||
};
|
||||
var updateLoading = (function() {
|
||||
var loadingStatus = document.getElementById('loading-status');
|
||||
return (function(progress) {
|
||||
loadingStatus.value = (parseInt(progress * 100)) + '%';
|
||||
return cancelScrypt;
|
||||
});
|
||||
})();
|
||||
|
||||
|
||||
// JSON Wallet
|
||||
(function() {
|
||||
var inputFile = document.getElementById('select-wallet-file');
|
||||
var targetDrop = document.getElementById('select-wallet-drop');
|
||||
@@ -278,33 +241,22 @@
|
||||
fileReader.onload = function(e) {
|
||||
var json = e.target.result;
|
||||
|
||||
if (Wallet.isCrowdsaleWallet(json)) {
|
||||
showWallet(Wallet.decryptCrowdsale(json, password));
|
||||
|
||||
} else if (Wallet.isValidWallet(json)) {
|
||||
if (ethers.utils.getJsonWalletAddress(json)) {
|
||||
showLoading('Decrypting Wallet...');
|
||||
var password = new Wallet.utils.Buffer(inputPassword.value);
|
||||
|
||||
cancelScrypt = false;
|
||||
|
||||
Wallet.decrypt(json, password, function(error, wallet, progress) {
|
||||
if (error) {
|
||||
if (error.message === 'invalid password') {
|
||||
alert('Wrong Password');
|
||||
} else {
|
||||
console.log(error);
|
||||
alert('Error Decrypting Wallet');
|
||||
}
|
||||
showSelect();
|
||||
|
||||
} else if (wallet) {
|
||||
showWallet(wallet);
|
||||
ethers.Wallet.fromEncryptedJson(json, inputPassword.value, updateLoading).then(function(wallet) {
|
||||
showWallet(wallet);
|
||||
|
||||
}, function(error) {
|
||||
if (error.message === 'invalid password') {
|
||||
alert('Wrong Password');
|
||||
} else {
|
||||
updateLoading(progress);
|
||||
console.log(error);
|
||||
alert('Error Decrypting Wallet');
|
||||
}
|
||||
|
||||
return cancelScrypt;
|
||||
showSelect();
|
||||
});
|
||||
} else {
|
||||
alert('Unknown JSON wallet format');
|
||||
@@ -315,6 +267,7 @@
|
||||
|
||||
})();
|
||||
|
||||
// Raw Private Key
|
||||
(function() {
|
||||
var inputPrivatekey = document.getElementById('select-privatekey');
|
||||
var submit = document.getElementById('select-submit-privatekey');
|
||||
@@ -334,7 +287,32 @@
|
||||
if (submit.classList.contains('disable')) { return; }
|
||||
var privateKey = inputPrivatekey.value;
|
||||
if (privateKey.substring(0, 2) !== '0x') { privateKey = '0x' + privateKey; }
|
||||
showWallet(new Wallet(privateKey));
|
||||
showWallet(new ethers.Wallet(privateKey));
|
||||
}
|
||||
})();
|
||||
|
||||
// Mnemonic Phrase
|
||||
(function() {
|
||||
var inputPhrase = document.getElementById('select-mnemonic-phrase');
|
||||
var inputPath = document.getElementById('select-mnemonic-path');
|
||||
var submit = document.getElementById('select-submit-mnemonic');
|
||||
|
||||
function check() {
|
||||
if (ethers.HDNode.isValidMnemonic(inputPhrase.value)) {
|
||||
submit.classList.remove('disable');
|
||||
} else {
|
||||
submit.classList.add('disable');
|
||||
}
|
||||
}
|
||||
inputPhrase.oninput = check;
|
||||
inputPath.oninput = check;
|
||||
|
||||
setEnter(inputPhrase, submit);
|
||||
setEnter(inputPath, submit);
|
||||
|
||||
submit.onclick = function() {
|
||||
if (submit.classList.contains('disable')) { return; }
|
||||
showWallet(ethers.Wallet.fromMnemonic(inputPhrase.value, inputPath.value));
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -355,7 +333,7 @@
|
||||
addActivity('> Refreshing details...');
|
||||
activeWallet.getBalance('pending').then(function(balance) {
|
||||
addActivity('< Balance: ' + balance.toString(10));
|
||||
inputBalance.value = Wallet.formatEther(balance, {commify: true});
|
||||
inputBalance.value = ethers.utils.formatEther(balance, { commify: true });
|
||||
}, function(error) {
|
||||
showError(error);
|
||||
});
|
||||
@@ -376,7 +354,10 @@
|
||||
return function(message, url) {
|
||||
var line = document.createElement('a');
|
||||
line.textContent = message;
|
||||
if (url) { line.setAttribute('href', url); }
|
||||
if (url) {
|
||||
line.setAttribute('href', url);
|
||||
line.setAttribute('target', '_blank');
|
||||
}
|
||||
activity.appendChild(line);
|
||||
}
|
||||
})();
|
||||
@@ -391,8 +372,8 @@
|
||||
// Validate the address and value (to enable the send button)
|
||||
function check() {
|
||||
try {
|
||||
Wallet.getAddress(inputTargetAddress.value);
|
||||
Wallet.parseEther(inputAmount.value);
|
||||
ethers.utils.getAddress(inputTargetAddress.value);
|
||||
ethers.utils.parseEther(inputAmount.value);
|
||||
} catch (error) {
|
||||
submit.classList.add('disable');
|
||||
return;
|
||||
@@ -402,45 +383,43 @@
|
||||
inputTargetAddress.oninput = check;
|
||||
inputAmount.oninput = check;
|
||||
|
||||
var optionMorden = document.getElementById('option-morden');
|
||||
var optionHomestead = document.getElementById('option-homestead');
|
||||
|
||||
// Select the morden network
|
||||
optionMorden.onclick = function() {
|
||||
if (optionMorden.classList.contains('selected')) { return; }
|
||||
addActivity('! Switched network: Morden');
|
||||
activeWallet.provider = new Wallet.providers.EtherscanProvider({testnet: true});
|
||||
optionMorden.classList.add('selected');
|
||||
optionHomestead.classList.remove('selected');
|
||||
refresh();
|
||||
}
|
||||
|
||||
// Select the homestead network
|
||||
optionHomestead.onclick = function() {
|
||||
if (optionHomestead.classList.contains('selected')) { return; }
|
||||
addActivity('! Switched network: Homestead');
|
||||
activeWallet.provider = new Wallet.providers.EtherscanProvider({testnet: false});
|
||||
optionMorden.classList.remove('selected');
|
||||
optionHomestead.classList.add('selected');
|
||||
refresh();
|
||||
}
|
||||
query('.network.option').forEach(function(el) {
|
||||
var network = el.getAttribute('data-network');
|
||||
el.onclick = function() {
|
||||
addActivity('! Switched network: ' + network);
|
||||
activeWallet = activeWallet.connect(ethers.providers.getDefaultProvider(network));
|
||||
query('.network.option.selected').forEach(function(el) {
|
||||
el.classList.remove('selected');
|
||||
});
|
||||
el.classList.add('selected');
|
||||
refresh();
|
||||
};
|
||||
});
|
||||
|
||||
// Send ether
|
||||
submit.onclick = function() {
|
||||
|
||||
// Matt (from Etherscan) is working on a gasPrice API call, which
|
||||
// should be done within a week or so.
|
||||
var gasPrice = (activeWallet.provider.testnet ? 0x4a817c800: 0xba43b7400);
|
||||
console.log('GasPrice: ' + gasPrice);
|
||||
// @TODO
|
||||
//var gasPrice = (activeWallet.provider.testnet ? 0x4a817c800: 0xba43b7400);
|
||||
//console.log('GasPrice: ' + gasPrice);
|
||||
|
||||
var targetAddress = Wallet.getAddress(inputTargetAddress.value);
|
||||
var amountWei = Wallet.parseEther(inputAmount.value);
|
||||
activeWallet.send(targetAddress, amountWei, {
|
||||
gasPrice: gasPrice,
|
||||
gasLimit: 21000,
|
||||
}).then(function(txid) {
|
||||
var url = (activeWallet.provider.testnet ? 'https://testnet.etherscan.io/tx/': 'https://etherscan.io/tx/') + txid;
|
||||
addActivity('< Transaction sent: ' + txid.substring(0, 20) + '...', url);
|
||||
var targetAddress = ethers.utils.getAddress(inputTargetAddress.value);
|
||||
var amountWei = ethers.utils.parseEther(inputAmount.value);
|
||||
activeWallet.sendTransaction({
|
||||
to: targetAddress,
|
||||
value: amountWei,
|
||||
//gasPrice: activeWallet.provider.getGasPrice(),
|
||||
//gasLimit: 21000,
|
||||
}).then(function(tx) {
|
||||
console.log(tx);
|
||||
|
||||
// Since we only use standard networks, network will always be known
|
||||
var tag = activeWallet.provider.network.name + '.';
|
||||
if (tag === 'homestead.') { tag = ''; }
|
||||
var url = 'https://' + tag + 'etherscan.io/tx/' + tx.hash;
|
||||
addActivity('< Transaction sent: ' + tx.hash.substring(0, 20) + '...', url);
|
||||
alert('Success!');
|
||||
|
||||
inputTargetAddress.value = '';
|
||||
@@ -449,6 +428,7 @@
|
||||
|
||||
refresh();
|
||||
}, function(error) {
|
||||
console.log(error);
|
||||
showError(error);
|
||||
});
|
||||
}
|
||||
@@ -468,15 +448,9 @@
|
||||
document.getElementById('loading-header').textContent = title;
|
||||
}
|
||||
|
||||
var loadingStatus = document.getElementById('loading-status');
|
||||
function updateLoading(progress) {
|
||||
loadingStatus.value = (parseInt(progress * 100)) + '%';
|
||||
}
|
||||
|
||||
function showWallet(wallet) {
|
||||
var testnet = document.getElementById('option-morden').classList.contains('selected');
|
||||
activeWallet = wallet;
|
||||
activeWallet.provider = new Wallet.providers.EtherscanProvider({testnet: testnet});
|
||||
var network = document.querySelector('.network.option.selected').getAttribute('data-network');
|
||||
activeWallet = wallet.connect(new ethers.providers.getDefaultProvider(network));
|
||||
|
||||
document.getElementById('screen-select').style.display = 'none';
|
||||
document.getElementById('screen-loading').style.display = 'none';
|
||||
@@ -492,7 +466,7 @@
|
||||
}
|
||||
|
||||
//var privateKey = '0x3141592653589793238462643383279502884197169399375105820974944592';
|
||||
//showWallet(new Wallet(privateKey));
|
||||
//showWallet(new ethers.Wallet(privateKey));
|
||||
|
||||
</script>
|
||||
</body>
|
||||
20
examples/style.css → dist/demo/style.css
vendored
20
examples/style.css → dist/demo/style.css
vendored
@@ -62,14 +62,14 @@ input[type=text] {
|
||||
border: 1px solid #555;
|
||||
font-size: 16px;
|
||||
padding: 10px;
|
||||
width: 500px;
|
||||
width: 501px;
|
||||
}
|
||||
|
||||
input[type=password] {
|
||||
border: 1px solid #555;
|
||||
font-size: 16px;
|
||||
padding: 10px;
|
||||
width: 500px;
|
||||
width: 501px;
|
||||
}
|
||||
|
||||
input[type=file] {
|
||||
@@ -81,7 +81,7 @@ input[type=file] {
|
||||
padding: 10px;
|
||||
position: relative;
|
||||
dvisibility: hidden;
|
||||
width: 500px;
|
||||
width: 501px;
|
||||
}
|
||||
|
||||
input[type=text].readonly {
|
||||
@@ -105,15 +105,27 @@ input[type=text].readonly {
|
||||
box-shadow: 0px 0px 5px #888;
|
||||
}
|
||||
|
||||
.clearfix {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.option {
|
||||
border: 1px solid #999;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
float: left;
|
||||
font-size: 16px;
|
||||
opacity: 0.3;
|
||||
padding: 10px;
|
||||
margin-right: 20px;
|
||||
text-align: center;
|
||||
transition: opacity 0.1s linear;
|
||||
width: 200px;
|
||||
width: 110px;
|
||||
}
|
||||
|
||||
.option span {
|
||||
font-size: 0.8em;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.option.selected {
|
||||
20201
dist/ethers-wallet.js
vendored
20201
dist/ethers-wallet.js
vendored
File diff suppressed because it is too large
Load Diff
10
dist/ethers-wallet.min.js
vendored
10
dist/ethers-wallet.min.js
vendored
File diff suppressed because one or more lines are too long
17282
dist/ethers.js
vendored
Normal file
17282
dist/ethers.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
dist/ethers.min.js
vendored
Normal file
2
dist/ethers.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/ethers.min.js.map
vendored
Normal file
1
dist/ethers.min.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
1059
dist/ethers.types.txt
vendored
Normal file
1059
dist/ethers.types.txt
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
dist/shims.js
vendored
Normal file
1
dist/shims.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/wordlist-es.js
vendored
Normal file
1
dist/wordlist-es.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/wordlist-fr.js
vendored
Normal file
1
dist/wordlist-fr.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/wordlist-it.js
vendored
Normal file
1
dist/wordlist-it.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/wordlist-ja.js
vendored
Normal file
1
dist/wordlist-ja.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/wordlist-ko.js
vendored
Normal file
1
dist/wordlist-ko.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/wordlist-zh.js
vendored
Normal file
1
dist/wordlist-zh.js
vendored
Normal file
File diff suppressed because one or more lines are too long
20
errors.d.ts
vendored
Normal file
20
errors.d.ts
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
export declare const UNKNOWN_ERROR = "UNKNOWN_ERROR";
|
||||
export declare const NOT_IMPLEMENTED = "NOT_IMPLEMENTED";
|
||||
export declare const MISSING_NEW = "MISSING_NEW";
|
||||
export declare const CALL_EXCEPTION = "CALL_EXCEPTION";
|
||||
export declare const INVALID_ARGUMENT = "INVALID_ARGUMENT";
|
||||
export declare const MISSING_ARGUMENT = "MISSING_ARGUMENT";
|
||||
export declare const UNEXPECTED_ARGUMENT = "UNEXPECTED_ARGUMENT";
|
||||
export declare const NUMERIC_FAULT = "NUMERIC_FAULT";
|
||||
export declare const INSUFFICIENT_FUNDS = "INSUFFICIENT_FUNDS";
|
||||
export declare const NONCE_EXPIRED = "NONCE_EXPIRED";
|
||||
export declare const REPLACEMENT_UNDERPRICED = "REPLACEMENT_UNDERPRICED";
|
||||
export declare const UNSUPPORTED_OPERATION = "UNSUPPORTED_OPERATION";
|
||||
export declare function throwError(message: string, code: string, params: any): never;
|
||||
export declare function checkNew(self: any, kind: any): void;
|
||||
export declare function checkArgumentCount(count: number, expectedCount: number, suffix?: string): void;
|
||||
export declare function setCensorship(censorship: boolean, permanent?: boolean): void;
|
||||
export declare function checkNormalize(): void;
|
||||
export declare function setLogLevel(logLevel: string): void;
|
||||
export declare function warn(...args: Array<any>): void;
|
||||
export declare function info(...args: Array<any>): void;
|
||||
163
errors.js
Normal file
163
errors.js
Normal file
@@ -0,0 +1,163 @@
|
||||
'use strict';
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var _version_1 = require("./_version");
|
||||
// Unknown Error
|
||||
exports.UNKNOWN_ERROR = 'UNKNOWN_ERROR';
|
||||
// Not implemented
|
||||
exports.NOT_IMPLEMENTED = 'NOT_IMPLEMENTED';
|
||||
// Missing new operator to an object
|
||||
// - name: The name of the class
|
||||
exports.MISSING_NEW = 'MISSING_NEW';
|
||||
// Call exception
|
||||
// - transaction: the transaction
|
||||
// - address?: the contract address
|
||||
// - args?: The arguments passed into the function
|
||||
// - method?: The Solidity method signature
|
||||
// - errorSignature?: The EIP848 error signature
|
||||
// - errorArgs?: The EIP848 error parameters
|
||||
// - reason: The reason (only for EIP848 "Error(string)")
|
||||
exports.CALL_EXCEPTION = 'CALL_EXCEPTION';
|
||||
// Invalid argument (e.g. value is incompatible with type) to a function:
|
||||
// - argument: The argument name that was invalid
|
||||
// - value: The value of the argument
|
||||
exports.INVALID_ARGUMENT = 'INVALID_ARGUMENT';
|
||||
// Missing argument to a function:
|
||||
// - count: The number of arguments received
|
||||
// - expectedCount: The number of arguments expected
|
||||
exports.MISSING_ARGUMENT = 'MISSING_ARGUMENT';
|
||||
// Too many arguments
|
||||
// - count: The number of arguments received
|
||||
// - expectedCount: The number of arguments expected
|
||||
exports.UNEXPECTED_ARGUMENT = 'UNEXPECTED_ARGUMENT';
|
||||
// Numeric Fault
|
||||
// - operation: the operation being executed
|
||||
// - fault: the reason this faulted
|
||||
exports.NUMERIC_FAULT = 'NUMERIC_FAULT';
|
||||
// Insufficien funds (< value + gasLimit * gasPrice)
|
||||
// - transaction: the transaction attempted
|
||||
exports.INSUFFICIENT_FUNDS = 'INSUFFICIENT_FUNDS';
|
||||
// Nonce has already been used
|
||||
// - transaction: the transaction attempted
|
||||
exports.NONCE_EXPIRED = 'NONCE_EXPIRED';
|
||||
// The replacement fee for the transaction is too low
|
||||
// - transaction: the transaction attempted
|
||||
exports.REPLACEMENT_UNDERPRICED = 'REPLACEMENT_UNDERPRICED';
|
||||
// Unsupported operation
|
||||
// - operation
|
||||
exports.UNSUPPORTED_OPERATION = 'UNSUPPORTED_OPERATION';
|
||||
var _permanentCensorErrors = false;
|
||||
var _censorErrors = false;
|
||||
// @TODO: Enum
|
||||
function throwError(message, code, params) {
|
||||
if (_censorErrors) {
|
||||
throw new Error('unknown error');
|
||||
}
|
||||
if (!code) {
|
||||
code = exports.UNKNOWN_ERROR;
|
||||
}
|
||||
if (!params) {
|
||||
params = {};
|
||||
}
|
||||
var messageDetails = [];
|
||||
Object.keys(params).forEach(function (key) {
|
||||
try {
|
||||
messageDetails.push(key + '=' + JSON.stringify(params[key]));
|
||||
}
|
||||
catch (error) {
|
||||
messageDetails.push(key + '=' + JSON.stringify(params[key].toString()));
|
||||
}
|
||||
});
|
||||
messageDetails.push("version=" + _version_1.version);
|
||||
var reason = message;
|
||||
if (messageDetails.length) {
|
||||
message += ' (' + messageDetails.join(', ') + ')';
|
||||
}
|
||||
// @TODO: Any??
|
||||
var error = new Error(message);
|
||||
error.reason = reason;
|
||||
error.code = code;
|
||||
Object.keys(params).forEach(function (key) {
|
||||
error[key] = params[key];
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
exports.throwError = throwError;
|
||||
function checkNew(self, kind) {
|
||||
if (!(self instanceof kind)) {
|
||||
throwError('missing new', exports.MISSING_NEW, { name: kind.name });
|
||||
}
|
||||
}
|
||||
exports.checkNew = checkNew;
|
||||
function checkArgumentCount(count, expectedCount, suffix) {
|
||||
if (!suffix) {
|
||||
suffix = '';
|
||||
}
|
||||
if (count < expectedCount) {
|
||||
throwError('missing argument' + suffix, exports.MISSING_ARGUMENT, { count: count, expectedCount: expectedCount });
|
||||
}
|
||||
if (count > expectedCount) {
|
||||
throwError('too many arguments' + suffix, exports.UNEXPECTED_ARGUMENT, { count: count, expectedCount: expectedCount });
|
||||
}
|
||||
}
|
||||
exports.checkArgumentCount = checkArgumentCount;
|
||||
function setCensorship(censorship, permanent) {
|
||||
if (_permanentCensorErrors) {
|
||||
throwError('error censorship permanent', exports.UNSUPPORTED_OPERATION, { operation: 'setCensorship' });
|
||||
}
|
||||
_censorErrors = !!censorship;
|
||||
_permanentCensorErrors = !!permanent;
|
||||
}
|
||||
exports.setCensorship = setCensorship;
|
||||
function checkNormalize() {
|
||||
try {
|
||||
// Make sure all forms of normalization are supported
|
||||
["NFD", "NFC", "NFKD", "NFKC"].forEach(function (form) {
|
||||
try {
|
||||
"test".normalize(form);
|
||||
}
|
||||
catch (error) {
|
||||
throw new Error('missing ' + form);
|
||||
}
|
||||
});
|
||||
if (String.fromCharCode(0xe9).normalize('NFD') !== String.fromCharCode(0x65, 0x0301)) {
|
||||
throw new Error('broken implementation');
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
throwError('platform missing String.prototype.normalize', exports.UNSUPPORTED_OPERATION, { operation: 'String.prototype.normalize', form: error.message });
|
||||
}
|
||||
}
|
||||
exports.checkNormalize = checkNormalize;
|
||||
var LogLevels = { debug: 1, "default": 2, info: 2, warn: 3, error: 4, off: 5 };
|
||||
var LogLevel = LogLevels["default"];
|
||||
function setLogLevel(logLevel) {
|
||||
var level = LogLevels[logLevel];
|
||||
if (level == null) {
|
||||
warn("invliad log level - " + logLevel);
|
||||
return;
|
||||
}
|
||||
LogLevel = level;
|
||||
}
|
||||
exports.setLogLevel = setLogLevel;
|
||||
function log(logLevel, args) {
|
||||
if (LogLevel > LogLevels[logLevel]) {
|
||||
return;
|
||||
}
|
||||
console.log.apply(console, args);
|
||||
}
|
||||
function warn() {
|
||||
var args = [];
|
||||
for (var _i = 0; _i < arguments.length; _i++) {
|
||||
args[_i] = arguments[_i];
|
||||
}
|
||||
log("warn", args);
|
||||
}
|
||||
exports.warn = warn;
|
||||
function info() {
|
||||
var args = [];
|
||||
for (var _i = 0; _i < arguments.length; _i++) {
|
||||
args[_i] = arguments[_i];
|
||||
}
|
||||
log("info", args);
|
||||
}
|
||||
exports.info = info;
|
||||
13
ethers.d.ts
vendored
Normal file
13
ethers.d.ts
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Contract, ContractFactory, VoidSigner } from './contract';
|
||||
import { Signer } from './abstract-signer';
|
||||
import { Wallet } from './wallet';
|
||||
import * as constants from './constants';
|
||||
import * as errors from './errors';
|
||||
import * as providers from './providers';
|
||||
import * as utils from './utils';
|
||||
import * as wordlists from './wordlists';
|
||||
import { platform } from './utils/shims';
|
||||
import { version } from './_version';
|
||||
import { ContractFunction, ContractTransaction, Event, EventFilter } from './contract';
|
||||
declare function getDefaultProvider(network?: utils.Network | string): providers.BaseProvider;
|
||||
export { Signer, Wallet, VoidSigner, getDefaultProvider, providers, Contract, ContractFactory, constants, errors, utils, wordlists, platform, version, ContractFunction, ContractTransaction, Event, EventFilter };
|
||||
51
ethers.js
Normal file
51
ethers.js
Normal file
@@ -0,0 +1,51 @@
|
||||
'use strict';
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
||||
result["default"] = mod;
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var contract_1 = require("./contract");
|
||||
exports.Contract = contract_1.Contract;
|
||||
exports.ContractFactory = contract_1.ContractFactory;
|
||||
exports.VoidSigner = contract_1.VoidSigner;
|
||||
var abstract_signer_1 = require("./abstract-signer");
|
||||
exports.Signer = abstract_signer_1.Signer;
|
||||
var wallet_1 = require("./wallet");
|
||||
exports.Wallet = wallet_1.Wallet;
|
||||
var constants = __importStar(require("./constants"));
|
||||
exports.constants = constants;
|
||||
var errors = __importStar(require("./errors"));
|
||||
exports.errors = errors;
|
||||
var providers = __importStar(require("./providers"));
|
||||
exports.providers = providers;
|
||||
var utils = __importStar(require("./utils"));
|
||||
exports.utils = utils;
|
||||
var wordlists = __importStar(require("./wordlists"));
|
||||
exports.wordlists = wordlists;
|
||||
////////////////////////
|
||||
// Compile-Time Constants
|
||||
// This is empty in node, and used by browserify to inject extra goodies
|
||||
var shims_1 = require("./utils/shims");
|
||||
exports.platform = shims_1.platform;
|
||||
// This is generated by "npm run dist"
|
||||
var _version_1 = require("./_version");
|
||||
exports.version = _version_1.version;
|
||||
////////////////////////
|
||||
// Helper Functions
|
||||
function getDefaultProvider(network) {
|
||||
if (network == null) {
|
||||
network = 'homestead';
|
||||
}
|
||||
var n = utils.getNetwork(network);
|
||||
if (!n || !n._defaultProvider) {
|
||||
errors.throwError('unsupported getDefaultProvider network', errors.UNSUPPORTED_OPERATION, {
|
||||
operation: 'getDefaultProvider',
|
||||
network: network
|
||||
});
|
||||
}
|
||||
return n._defaultProvider(providers);
|
||||
}
|
||||
exports.getDefaultProvider = getDefaultProvider;
|
||||
@@ -1,284 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Ethereum Classic Split Tool</title>
|
||||
<link rel="stylesheet" type="text/css" href="../style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="centerer">
|
||||
<div class="centered">
|
||||
<h1>Split Ether Classic</h1>
|
||||
<hr />
|
||||
<h2>What does this tool do?</h2>
|
||||
<p>
|
||||
This tool will take a <i>geth</i> (or crowdsale) JSON wallet, decrypt it and
|
||||
send all its funds to <a href="http://etherscan.io/address/0x3474627d4f63a678266bc17171d87f8570936622#code">this contract</a>,
|
||||
which will:
|
||||
</p>
|
||||
<ul>
|
||||
<li>On the ETH branch — send the funds back to the original address</li>
|
||||
<li>On the ETC branch — send the funds to the provided target address (for example, a <a href="https://www.poloniex.com">Poloniex</a> deposit address)</li>
|
||||
</ul>
|
||||
<br />
|
||||
<h3>Disclaimer:</h3>
|
||||
<p>
|
||||
I threw this together in couple of hours, mainly to split my own ether
|
||||
and test my <i>ethers-wallet</i> library (which is still missing features
|
||||
and is itself not ready for production use). Testing has been fairly minimal
|
||||
beyond trying it on a few wallets. <b>Use this at your own risk.</b>
|
||||
</p>
|
||||
<hr />
|
||||
<h2>Check Current ETC Balance</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<th>ETC Address:</th>
|
||||
<td><input type="text" id="checkAddress" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>
|
||||
<div id="submitCheck" class="submit disable">Check Classic Ether Balance</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr />
|
||||
<h2>Split ETC/ETH</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<th>JSON Wallet:</th>
|
||||
<td><div class="file" id="drop">Drop JSON wallet file here</div><input type="file" id="json" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Password:</th>
|
||||
<td><input type="password" id="password" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Target ETC Address:</th>
|
||||
<td><input type="text" id="targetAddress" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>
|
||||
<div id="submitSplit" class="submit disable">Split Classic Ether</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="../../dist/ethers-wallet.js"></script>
|
||||
<script type="text/javascript">
|
||||
var submitCheck = document.getElementById('submitCheck');
|
||||
var submitSplit = document.getElementById('submitSplit');
|
||||
|
||||
var inputJson = document.getElementById('json');
|
||||
var inputCheckAddress = document.getElementById('checkAddress')
|
||||
var inputTargetAddress = document.getElementById('targetAddress')
|
||||
var inputPassword = document.getElementById('password');
|
||||
|
||||
var targetDrop = document.getElementById('drop');
|
||||
|
||||
var provider = new Wallet.providers.HttpProvider('https://linode-newark.ethers.ws:8002');
|
||||
|
||||
var contractAddress = '0x3474627d4f63a678266bc17171d87f8570936622';
|
||||
var contractAbi = JSON.parse('[{"constant":false,"inputs":[{"name":"balance","type":"uint256"}],"name":"claimDonations","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"isClassic","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"classicAddress","type":"address"}],"name":"split","outputs":[],"type":"function"},{"inputs":[],"type":"constructor"}]');
|
||||
|
||||
submitCheck.onclick = function() {
|
||||
if (submitCheck.classList.contains('disable')) { return; }
|
||||
|
||||
var address = document.getElementById('checkAddress').value;
|
||||
try {
|
||||
address = Wallet.getAddress(address);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
alert('invalid address');
|
||||
return;
|
||||
}
|
||||
provider.getBalance(address, 'latest').then(function(balance) {
|
||||
alert('Balance: ' + Wallet.formatEther(balance, {commify: true}) + ' ' + Wallet.etherSymbol);
|
||||
}, function(error) {
|
||||
if (error) {
|
||||
console.log(error);
|
||||
alert('Faied to get balance');
|
||||
}
|
||||
});
|
||||
|
||||
console.log('check', address);
|
||||
}
|
||||
|
||||
submitSplit.onclick = function() {
|
||||
if (submitSplit.classList.contains('disable')) { return; }
|
||||
|
||||
function done() {
|
||||
submitSplit.textContent = 'Split Classic Ether';
|
||||
inputJson.readOnly = false;
|
||||
inputPassword.readOnly = false;
|
||||
inputTargetAddress.readOnly = false;
|
||||
checkSplit();
|
||||
}
|
||||
|
||||
inputJson.readOnly = true;
|
||||
inputPassword.readOnly = true;
|
||||
inputTargetAddress.readOnly = true;
|
||||
|
||||
submitSplit.classList.add('disable');
|
||||
|
||||
var files = inputJson.files;
|
||||
if (files.length !== 1) {
|
||||
alert('No wallet found');
|
||||
return done();
|
||||
}
|
||||
|
||||
var password = new Wallet.utils.Buffer(inputPassword.value, 'utf8');
|
||||
|
||||
var targetAddress = document.getElementById('targetAddress').value;
|
||||
try {
|
||||
targetAddress = Wallet.getAddress(targetAddress);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
alert('invalid target address');
|
||||
return done();
|
||||
}
|
||||
|
||||
function processWallet(wallet) {
|
||||
console.log(wallet);
|
||||
if (wallet.address === targetAddress) {
|
||||
alert('Wallet address and target address cannot be the same.');
|
||||
return done();
|
||||
}
|
||||
|
||||
submitSplit.textContent = 'Decrypted \u2014 Processing (please wait)';
|
||||
|
||||
var contract = wallet.getContract(contractAddress, contractAbi);
|
||||
var data = contract.interface.split(targetAddress).data;
|
||||
|
||||
var transaction = {
|
||||
to: contractAddress,
|
||||
data: data
|
||||
};
|
||||
|
||||
Promise.all([
|
||||
provider.getBalance(wallet.address, 'latest'),
|
||||
provider.getTransactionCount(wallet.address, 'latest'),
|
||||
provider.getGasPrice(),
|
||||
// provider.estimateGas(transaction)
|
||||
]).then(function(results) {
|
||||
var balance = results[0];
|
||||
var transactionCount = results[1];
|
||||
var gasPrice = results[2];
|
||||
var gasEstimate = new Wallet.utils.BN('29735');
|
||||
//results[3].add(new Wallet.utils.BN(21000));
|
||||
|
||||
transaction.gasPrice = '0x' + gasPrice.toString(16);
|
||||
transaction.nonce = transactionCount;
|
||||
transaction.gasLimit = gasEstimate;
|
||||
|
||||
var toSend = balance.sub(gasPrice.mul(gasEstimate));
|
||||
transaction.value = toSend;
|
||||
|
||||
console.log(transaction);
|
||||
|
||||
var accept = confirm('Balance: ' + Wallet.formatEther(balance, {commify: true}) + ' ' + Wallet.etherSymbol + '. Are you sure you want to split Classic Ether to ' + targetAddress + '?');
|
||||
if (accept) {
|
||||
var signedTransaction = wallet.sign(transaction);
|
||||
function showError(error) {
|
||||
console.log(error);
|
||||
done();
|
||||
alert('Error sending transaction');
|
||||
}
|
||||
provider.sendTransaction(signedTransaction).then(function(txid) {
|
||||
if (txid.match(/^0x0+$/)) {
|
||||
showError(new Error('txid was zero'));
|
||||
return;
|
||||
}
|
||||
done();
|
||||
console.log('txid:' + txid);
|
||||
alert('Success \u2014 ' + txid);
|
||||
}, function(error) {
|
||||
showError(error);
|
||||
});
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
}, function(error) {
|
||||
done();
|
||||
console.log(error);
|
||||
alert('Error: Faied to fetch info');
|
||||
});
|
||||
}
|
||||
|
||||
var fileReader = new FileReader();
|
||||
fileReader.onload = function(e) {
|
||||
var json = e.target.result;
|
||||
if (Wallet.isCrowdsaleWallet(json)) {
|
||||
var wallet = Wallet.decryptCrowdsale(json, password);
|
||||
processWallet(wallet);
|
||||
} else if (Wallet.isValidWallet(json)) {
|
||||
Wallet.decrypt(json, password, function(error, wallet, progress) {
|
||||
if (error) {
|
||||
done();
|
||||
console.log(error);
|
||||
if (error.message === 'invalid password') {
|
||||
alert('Wrong Password');
|
||||
} else {
|
||||
alert('Error Decrypting Wallet');
|
||||
}
|
||||
} else if (wallet) {
|
||||
processWallet(wallet);
|
||||
} else {
|
||||
submitSplit.textContent = 'Decrypting \u2014 ' + (parseInt(progress * 100) + '%');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
alert('unknown walet format');
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
fileReader.readAsText(files[0]);
|
||||
}
|
||||
|
||||
inputCheckAddress.oninput = function() {
|
||||
try {
|
||||
Wallet.getAddress(inputCheckAddress.value);
|
||||
submitCheck.classList.remove('disable');
|
||||
} catch (error) {
|
||||
submitCheck.classList.add('disable');
|
||||
}
|
||||
}
|
||||
|
||||
function checkSplit() {
|
||||
if (!inputJson.files || inputJson.files.length !== 1) {
|
||||
submitSplit.classList.add('disable');
|
||||
return;
|
||||
}
|
||||
targetDrop.textContent = inputJson.files[0].name;
|
||||
|
||||
try {
|
||||
Wallet.getAddress(inputTargetAddress.value);
|
||||
} catch (error) {
|
||||
submitSplit.classList.add('disable');
|
||||
return;
|
||||
}
|
||||
|
||||
submitSplit.classList.remove('disable');
|
||||
}
|
||||
|
||||
inputTargetAddress.oninput = checkSplit;
|
||||
inputJson.onchange = checkSplit;
|
||||
|
||||
inputJson.addEventListener('dragover', function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
targetDrop.classList.add('highlight');
|
||||
}, true);
|
||||
|
||||
inputJson.addEventListener('drop', function(event) {
|
||||
targetDrop.classList.remove('highlight');
|
||||
}, true);
|
||||
|
||||
|
||||
var check
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
356
gulpfile.js
Normal file
356
gulpfile.js
Normal file
@@ -0,0 +1,356 @@
|
||||
'use strict';
|
||||
|
||||
var fs = require('fs');
|
||||
var through = require('through');
|
||||
|
||||
var gulp = require("gulp");
|
||||
var ts = require("gulp-typescript");
|
||||
var tsProject = ts.createProject("tsconfig.json");
|
||||
|
||||
var browserify = require("browserify");
|
||||
var source = require('vinyl-source-stream');
|
||||
var tsify = require("tsify");
|
||||
|
||||
var sourcemaps = require('gulp-sourcemaps');
|
||||
var uglify = require('gulp-uglify');
|
||||
var buffer = require('vinyl-buffer');
|
||||
|
||||
function createTransform(transforms, show) {
|
||||
if (!show) { show = { }; }
|
||||
|
||||
function padding(length) {
|
||||
let pad = '';
|
||||
while (pad.length < length) { pad += ' '; }
|
||||
return pad;
|
||||
}
|
||||
|
||||
function transformFile(path) {
|
||||
for (var pattern in transforms) {
|
||||
if (path.match(new RegExp('/' + pattern + '$'))) {
|
||||
return transforms[pattern];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return function(path, options) {
|
||||
var data = '';
|
||||
|
||||
return through(function(chunk) {
|
||||
data += chunk;
|
||||
}, function () {
|
||||
var transformed = transformFile(path);
|
||||
var shortPath = path;
|
||||
if (shortPath.substring(0, __dirname.length) == __dirname) {
|
||||
shortPath = shortPath.substring(__dirname.length);
|
||||
}
|
||||
var size = fs.readFileSync(path).length;
|
||||
if (transformed != null) {
|
||||
if (show.transformed) {
|
||||
console.log('Transformed:', shortPath, padding(70 - shortPath.length), size, padding(6 - String(size).length), '=>', transformed.length);
|
||||
}
|
||||
data = transformed;
|
||||
} else if (shortPath === '/src.ts/utils/wordlist.ts') {
|
||||
data += '\n\nexportWordlist = true;'
|
||||
if (show.transformed) {
|
||||
console.log('Transformed:', shortPath, padding(70 - shortPath.length), size, padding(6 - String(size).length), '=>', data.length);
|
||||
}
|
||||
} else {
|
||||
if (show.preserved) {
|
||||
console.log('Preserved: ', shortPath, padding(70 - shortPath.length), size);
|
||||
}
|
||||
}
|
||||
this.queue(data);
|
||||
this.queue(null);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bundled Library (browser)
|
||||
*
|
||||
* Source: src.ts/index.ts src.ts/{contracts,providers,utils,wallet}/*.ts src.ts/wordlists/lang-en.ts
|
||||
* Target: dist/ethers{.min,}.js
|
||||
*/
|
||||
function taskBundle(name, options) {
|
||||
var show = options.show || { };
|
||||
|
||||
// The elliptic package.json is only used for its version
|
||||
var ellipticPackage = require('elliptic/package.json');
|
||||
ellipticPackage = JSON.stringify({ version: ellipticPackage.version });
|
||||
|
||||
var version = require('./package.json').version;
|
||||
|
||||
var undef = "module.exports = undefined;";
|
||||
var empty = "module.exports = {};";
|
||||
|
||||
// This is only used in getKeyPair, which we do not use; but we'll
|
||||
// leave it in tact using the browser crypto functions
|
||||
var brorand = "module.exports = function(length) { var result = new Uint8Array(length); (global.crypto || global.msCrypto).getRandomValues(result); return result; }";
|
||||
|
||||
// setImmediate is installed globally by our src.browser/shims.ts, loaded from src.ts/index.ts
|
||||
var process = "module.exports = { browser: true };";
|
||||
var timers = "module.exports = { setImmediate: global.setImmediate }; ";
|
||||
|
||||
function readShim(filename) {
|
||||
return fs.readFileSync('./shims/' + filename + '.js').toString();
|
||||
}
|
||||
|
||||
|
||||
var transforms = {
|
||||
|
||||
// Remove the precomputed secp256k1 points
|
||||
"elliptic/lib/elliptic/precomputed/secp256k1.js": undef,
|
||||
|
||||
// Remove curves we don't care about
|
||||
"elliptic/curve/edwards.js": empty,
|
||||
"elliptic/curve/mont.js": empty,
|
||||
"elliptic/lib/elliptic/eddsa/.*": empty,
|
||||
|
||||
// We only use the version from this JSON package
|
||||
"elliptic/package.json" : ellipticPackage,
|
||||
|
||||
// Remove RIPEMD160 and unneeded hashing algorithms
|
||||
//"hash.js/lib/hash/ripemd.js": "module.exports = {ripemd160: null}",
|
||||
"hash.js/lib/hash/sha/1.js": empty,
|
||||
"hash.js/lib/hash/sha/224.js": empty,
|
||||
"hash.js/lib/hash/sha/384.js": empty,
|
||||
|
||||
// Swap out borland for the random bytes we already have
|
||||
"brorand/index.js": brorand,
|
||||
|
||||
"xmlhttprequest/lib/XMLHttpRequest.js": readShim("xmlhttprequest"),
|
||||
|
||||
// Used by sha3 if it exists; (so make it no exist)
|
||||
"process/browser.js": process,
|
||||
"timers-browserify/main.js": timers,
|
||||
|
||||
"ethers.js/utils/base64.js": readShim("base64"),
|
||||
"ethers.js/providers/ipc-provider.js": readShim("empty"),
|
||||
"ethers.js/utils/hmac.js": readShim("hmac"),
|
||||
"ethers.js/utils/pbkdf2.js": readShim("pbkdf2"),
|
||||
"ethers.js/utils/random-bytes.js": readShim("random-bytes"),
|
||||
"ethers.js/utils/shims.js": readShim("shims"),
|
||||
"ethers.js/wordlists/index.js": readShim("wordlists"),
|
||||
|
||||
};
|
||||
|
||||
gulp.task(name, function () {
|
||||
|
||||
var result = browserify({
|
||||
basedir: '.',
|
||||
debug: false,
|
||||
entries: [ './index.js' ],
|
||||
cache: { },
|
||||
packageCache: {},
|
||||
standalone: "ethers",
|
||||
transform: [ [ createTransform(transforms, show), { global: true } ] ],
|
||||
})
|
||||
.bundle()
|
||||
.pipe(source(options.filename))
|
||||
|
||||
if (options.minify) {
|
||||
result = result.pipe(buffer())
|
||||
.pipe(sourcemaps.init({ loadMaps: true }))
|
||||
.pipe(uglify({
|
||||
output: { ascii_only: true }
|
||||
}))
|
||||
.pipe(sourcemaps.write('./'))
|
||||
}
|
||||
|
||||
result = result.pipe(gulp.dest(options.dest));
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
// Creates dist/ethers.js
|
||||
taskBundle("default", { filename: "ethers.js", dest: 'dist', show: { transformed: true, preserved: true }, minify: false });
|
||||
|
||||
// Creates dist/ethers.js
|
||||
taskBundle("default-test", { filename: "ethers.js", dest: 'tests/dist', show: { transformed: true }, minify: false });
|
||||
|
||||
// Creates dist/ethers.min.js
|
||||
taskBundle("minified", { filename: "ethers.min.js", dest: 'dist', minify: true });
|
||||
|
||||
// Creates dist/ethers.min.js
|
||||
taskBundle("minified-test", { filename: "ethers.min.js", dest: 'tests/dist', minify: true });
|
||||
|
||||
gulp.task('shims', function () {
|
||||
|
||||
var result = browserify({
|
||||
basedir: '.',
|
||||
debug: false,
|
||||
entries: [ './tests/shims/index.js' ],
|
||||
cache: { },
|
||||
packageCache: {},
|
||||
standalone: "_shims",
|
||||
insertGlobalVars: {
|
||||
process: function() { return; },
|
||||
}
|
||||
})
|
||||
.bundle()
|
||||
.pipe(source('shims.js'))
|
||||
.pipe(buffer())
|
||||
.pipe(uglify({
|
||||
output: { ascii_only: true }
|
||||
}))
|
||||
.pipe(gulp.dest('dist'));
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
/*
|
||||
// Dump the TypeScript definitions to dist/types/
|
||||
gulp.task("types", function() {
|
||||
return gulp.src(['./src.ts/index.ts', './src.ts / * * / * . ts'])
|
||||
.pipe(ts({
|
||||
declaration: true,
|
||||
esModuleInterop: true,
|
||||
moduleResolution: "node",
|
||||
lib: [ "es2015", "es5", "dom" ],
|
||||
module: "commonjs",
|
||||
outDir: './dist/types',
|
||||
target: "es5",
|
||||
}))
|
||||
.dts
|
||||
.pipe(gulp.dest("dist/types/"))
|
||||
});
|
||||
*/
|
||||
|
||||
/**
|
||||
* Browser Friendly BIP39 Wordlists
|
||||
*
|
||||
* source: src.ts/wordlist/lang-*.ts
|
||||
* target: dist/wordlist-*.js
|
||||
*
|
||||
* Since all of the functions these wordlists use is already
|
||||
* available from the global ethers library, we use this to
|
||||
* target the global ethers functions directly, rather than
|
||||
* re-include them.
|
||||
*/
|
||||
function taskLang(locale) {
|
||||
function transformBip39(path, options) {
|
||||
var data = '';
|
||||
|
||||
return through(function(chunk) {
|
||||
data += chunk;
|
||||
}, function () {
|
||||
var shortPath = path;
|
||||
if (shortPath.substring(0, __dirname.length) == __dirname) {
|
||||
shortPath = shortPath.substring(__dirname.length);
|
||||
}
|
||||
|
||||
// Word list files...
|
||||
if (shortPath.match(/^\/src\.ts\/wordlists\//)) {
|
||||
shortPath = '/';
|
||||
}
|
||||
|
||||
switch (shortPath) {
|
||||
// Use the existing "ethers.errors"
|
||||
case '/src.ts/errors.ts':
|
||||
data = "module.exports = global.ethers.errors";
|
||||
break;
|
||||
|
||||
// Use the existing "ethers.utils"
|
||||
case '/src.ts/utils/bytes.ts':
|
||||
case '/src.ts/utils/hash.ts':
|
||||
case '/src.ts/utils/properties.ts':
|
||||
case '/src.ts/utils/utf8.ts':
|
||||
data = "module.exports = global.ethers.utils";
|
||||
break;
|
||||
|
||||
// If it is the Wordlist class, register should export the wordlist
|
||||
case '/src.ts/utils/wordlist.ts':
|
||||
data += '\n\nexportWordlist = true;'
|
||||
break;
|
||||
|
||||
// Do nothing
|
||||
case '/':
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error('unhandled file: ' + shortPath);
|
||||
}
|
||||
|
||||
this.queue(data);
|
||||
this.queue(null);
|
||||
});
|
||||
}
|
||||
|
||||
gulp.task("bip39-" + locale, function() {
|
||||
return browserify({
|
||||
basedir: '.',
|
||||
debug: false,
|
||||
entries: [ 'src.ts/wordlists/lang-' + locale + ".ts" ],
|
||||
cache: {},
|
||||
packageCache: {},
|
||||
transform: [ [ transformBip39, { global: true } ] ],
|
||||
})
|
||||
.plugin(tsify)
|
||||
.bundle()
|
||||
.pipe(source("wordlist-" + locale + ".js"))
|
||||
.pipe(buffer())
|
||||
.pipe(uglify({
|
||||
output: { ascii_only: true }
|
||||
}))
|
||||
.pipe(gulp.dest("dist"));
|
||||
});
|
||||
}
|
||||
|
||||
taskLang("es");
|
||||
taskLang("fr");
|
||||
taskLang("it");
|
||||
taskLang("ja");
|
||||
taskLang("ko");
|
||||
taskLang("zh");
|
||||
|
||||
// Package up all the test cases into tests/dist/tests.json
|
||||
gulp.task("tests", function() {
|
||||
|
||||
function readShim(filename) {
|
||||
return fs.readFileSync('./tests/' + filename + '.js').toString();
|
||||
}
|
||||
|
||||
var transforms = {
|
||||
"tests/utils-ethers.js": readShim('utils-ethers-browser')
|
||||
}
|
||||
|
||||
// Create a mock-fs module that can load our gzipped test cases
|
||||
var data = {};
|
||||
|
||||
fs.readdirSync('tests/tests').forEach(function(filename) {
|
||||
if (!filename.match(/\.json\.gz$/)) { return; }
|
||||
filename = 'tests/tests/' + filename;
|
||||
data['/' + filename] = fs.readFileSync(filename).toString('base64');
|
||||
});
|
||||
|
||||
fs.readdirSync('tests/tests/easyseed-bip39').forEach(function(filename) {
|
||||
if (!filename.match(/\.json$/)) { return; }
|
||||
filename = 'tests/tests/easyseed-bip39/' + filename;
|
||||
data['/' + filename] = fs.readFileSync(filename).toString('base64');
|
||||
});
|
||||
|
||||
fs.readdirSync('tests/wordlist-generation').forEach(function(filename) {
|
||||
if (!filename.match(/\.txt$/)) { return; }
|
||||
filename = 'tests/wordlist-generation/' + filename;
|
||||
data['/' + filename] = fs.readFileSync(filename).toString('base64');
|
||||
});
|
||||
|
||||
fs.writeFileSync('./tests/dist/tests.json', JSON.stringify(data));
|
||||
|
||||
return browserify({
|
||||
basedir: './',
|
||||
debug: false,
|
||||
entries: [ "./tests/browser.js" ],
|
||||
cache: {},
|
||||
packageCache: {},
|
||||
standalone: "tests",
|
||||
transform: [ [ createTransform(transforms), { global: true } ] ],
|
||||
})
|
||||
.bundle()
|
||||
.pipe(source("tests.js"))
|
||||
.pipe(gulp.dest("tests/dist/"));
|
||||
});
|
||||
|
||||
|
||||
3
index.d.ts
vendored
Normal file
3
index.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import * as ethers from './ethers';
|
||||
export { ethers };
|
||||
export * from './ethers';
|
||||
111
index.js
111
index.js
@@ -1,96 +1,15 @@
|
||||
'use strict';
|
||||
|
||||
var scrypt = require('scrypt-js');
|
||||
|
||||
var Contract = require('./lib/contract.js');
|
||||
var providers = require('./lib/providers.js');
|
||||
var secretStorage = require('./lib/secret-storage.js');
|
||||
var Randomish = require('./lib/randomish.js');
|
||||
var SigningKey = require('./lib/signing-key.js');
|
||||
var Wallet = require('./lib/wallet.js');
|
||||
var units = require('./lib/units.js');
|
||||
|
||||
var utils = require('./lib/utils.js');
|
||||
var BN = utils.BN;
|
||||
|
||||
|
||||
var exportUtils = {};
|
||||
utils.defineProperty(Wallet, 'utils', exportUtils);
|
||||
|
||||
utils.defineProperty(exportUtils, 'BN', BN);
|
||||
utils.defineProperty(exportUtils, 'Buffer', Buffer);
|
||||
|
||||
utils.defineProperty(exportUtils, 'sha3', utils.sha3);
|
||||
utils.defineProperty(exportUtils, 'sha256', utils.sha256);
|
||||
|
||||
utils.defineProperty(exportUtils, 'getContractAddress', utils.getContractAddress);
|
||||
|
||||
module.exports = Wallet;
|
||||
|
||||
|
||||
utils.defineProperty(Wallet, 'etherSymbol', '\uD835\uDF63');
|
||||
|
||||
utils.defineProperty(Wallet, 'formatEther', units.formatEther);
|
||||
utils.defineProperty(Wallet, 'parseEther', units.parseEther);
|
||||
|
||||
utils.defineProperty(Wallet, 'getAddress', utils.getAddress);
|
||||
utils.defineProperty(Wallet, 'getIcapAddress', utils.getIcapAddress);
|
||||
|
||||
utils.defineProperty(Wallet, 'isCrowdsaleWallet', secretStorage.isCrowdsaleWallet);
|
||||
utils.defineProperty(Wallet, 'isValidWallet', secretStorage.isValidWallet);
|
||||
|
||||
utils.defineProperty(Wallet, 'decryptCrowdsale', function(json, password) {
|
||||
return new Wallet(secretStorage.decryptCrowdsale(json, password));
|
||||
});
|
||||
|
||||
utils.defineProperty(Wallet, 'decrypt', function(json, password, progressCallback) {
|
||||
if (progressCallback && typeof(progressCallback) !== 'function') {
|
||||
throw new Error('invalid callback');
|
||||
}
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
secretStorage.decrypt(json, password, progressCallback).then(function(signingKey) {
|
||||
resolve(new Wallet(signingKey));
|
||||
}, function(error) {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
utils.defineProperty(Wallet.prototype, 'encrypt', function(password, options, progressCallback) {
|
||||
if (typeof(options) === 'function' && !progressCallback) {
|
||||
progressCallback = options;
|
||||
options = {};
|
||||
}
|
||||
if (progressCallback && typeof(progressCallback) !== 'function') {
|
||||
throw new Error('invalid callback');
|
||||
}
|
||||
|
||||
return secretStorage.encrypt(this.privateKey, password, options, progressCallback);
|
||||
});
|
||||
|
||||
utils.defineProperty(Wallet, 'summonBrainWallet', function(username, password, progressCallback) {
|
||||
if (progressCallback && typeof(progressCallback) !== 'function') {
|
||||
throw new Error('invalid callback');
|
||||
}
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
scrypt(password, username, (1 << 18), 8, 1, 32, function(error, progress, key) {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else if (key) {
|
||||
resolve(new Wallet(new Buffer(key)));
|
||||
} else if (progressCallback) {
|
||||
progressCallback(progress);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
utils.defineProperty(Wallet, 'providers', providers);
|
||||
|
||||
|
||||
utils.defineProperty(Wallet, 'randomish', new Randomish());
|
||||
|
||||
module.exports = Wallet;
|
||||
"use strict";
|
||||
function __export(m) {
|
||||
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
|
||||
}
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
||||
result["default"] = mod;
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var ethers = __importStar(require("./ethers"));
|
||||
exports.ethers = ethers;
|
||||
__export(require("./ethers"));
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var utils = require('./utils.js');
|
||||
|
||||
var crypto = global.crypto || global.msCrypto;
|
||||
if (!crypto || !crypto.getRandomValues) {
|
||||
console.log('WARNING: Missing strong random number source; using weak randomBytes');
|
||||
crypto = {
|
||||
getRandomValues: function(length) {
|
||||
|
||||
for (var i = 0; i < buffer.length; i++) {
|
||||
buffer[i] = parseInt(256 * Math.random());
|
||||
}
|
||||
|
||||
return buffer;
|
||||
},
|
||||
_weakCrypto: true
|
||||
};
|
||||
} else {
|
||||
console.log('Found strong random number source');
|
||||
}
|
||||
|
||||
function randomBytes(length) {
|
||||
if (length <= 0 || length > 1024 || parseInt(length) != length) {
|
||||
throw new Error('invalid length');
|
||||
}
|
||||
|
||||
var buffer = new Buffer(length);
|
||||
crypto.getRandomValues(buffer);
|
||||
return buffer;
|
||||
};
|
||||
|
||||
if (crypto._weakCrypto === true) {
|
||||
utils.defineProperty(randomBytes, '_weakCrypto', true);
|
||||
}
|
||||
|
||||
module.exports = randomBytes;
|
||||
658
lib/contract.js
658
lib/contract.js
@@ -1,658 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var utils = require('./utils.js');
|
||||
|
||||
// Creates property that is immutable
|
||||
function defineFrozen(object, name, value) {
|
||||
var frozen = JSON.stringify(value);
|
||||
Object.defineProperty(object, name, {
|
||||
enumerable: true,
|
||||
get: function() { return JSON.parse(frozen); }
|
||||
});
|
||||
}
|
||||
|
||||
// getKeys([{a: 1, b: 2}, {a: 3, b: 4}], 'a') => [1, 3]
|
||||
function getKeys(params, key) {
|
||||
if (!Array.isArray(params)) { throw new Error('invalid params'); }
|
||||
|
||||
var result = [];
|
||||
|
||||
for (var i = 0; i < params.length; i++) {
|
||||
if (typeof(params[i][key]) !== 'string') { throw new Error('invalid abi'); }
|
||||
result.push(params[i][key]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Convert the value from a Number to a BN (if necessary)
|
||||
function numberOrBN(value) {
|
||||
if (!value.eq) {
|
||||
if (typeof(value) !== 'number') {
|
||||
throw new Error('invalid number');
|
||||
}
|
||||
value = new utils.BN(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function zpad(buffer, length) {
|
||||
var zero = new Buffer([0]);
|
||||
while (buffer.length < length) {
|
||||
buffer = Buffer.concat([zero, buffer]);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// There seems to be a but in maskn, so we are doing this for now.
|
||||
var bitmasks = [];
|
||||
(function() {
|
||||
var mask = '';
|
||||
for (var i = 0; i < 33; i++) {
|
||||
bitmasks.push(new utils.BN(mask, 16));
|
||||
mask += 'ff';
|
||||
}
|
||||
})();
|
||||
|
||||
function coderNumber(size, signed) {
|
||||
return {
|
||||
encode: function(value) {
|
||||
value = numberOrBN(value)
|
||||
value = value.toTwos(size * 8).and(bitmasks[size]);
|
||||
if (signed) {
|
||||
value = value.fromTwos(size * 8).toTwos(256);
|
||||
}
|
||||
return value.toArrayLike(Buffer, 'be', 32);
|
||||
},
|
||||
decode: function(data, offset) {
|
||||
var junkLength = 32 - size;
|
||||
var value = new utils.BN(data.slice(offset + junkLength, offset + 32));
|
||||
if (signed) {
|
||||
value = value.fromTwos(size * 8);
|
||||
} else {
|
||||
value = value.and(bitmasks[size]);
|
||||
}
|
||||
return {
|
||||
consumed: 32,
|
||||
value: value,
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
var uint256Coder = coderNumber(32, false);
|
||||
|
||||
var coderBoolean = {
|
||||
encode: function(value) {
|
||||
return uint256Coder.encode(value ? 1: 0);
|
||||
},
|
||||
decode: function(data, offset) {
|
||||
var result = uint256Coder.decode(data, offset);
|
||||
return {
|
||||
consumed: result.consumed,
|
||||
value: !result.value.isZero()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function coderFixedBytes(length) {
|
||||
return {
|
||||
encode: function(value) {
|
||||
value = utils.hexOrBuffer(value);
|
||||
if (length === 32) { return value; }
|
||||
|
||||
var result = new Buffer(32);
|
||||
result.fill(0);
|
||||
value.copy(result);
|
||||
return result;
|
||||
},
|
||||
decode: function(data, offset) {
|
||||
if (data.length < offset + 32) { throw new Error('invalid bytes' + length); }
|
||||
|
||||
return {
|
||||
consumed: 32,
|
||||
value: '0x' + data.slice(offset, offset + length).toString('hex')
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var coderAddress = {
|
||||
encode: function(value) {
|
||||
if (!utils.isHexString(value, 20)) { throw new Error('invalid address'); }
|
||||
value = utils.hexOrBuffer(value);
|
||||
var result = new Buffer(32);
|
||||
result.fill(0);
|
||||
value.copy(result, 12);
|
||||
return result;
|
||||
},
|
||||
decode: function(data, offset) {
|
||||
if (data.length < offset + 32) { throw new Error('invalid address'); }
|
||||
return {
|
||||
consumed: 32,
|
||||
value: '0x' + data.slice(offset + 12, offset + 32).toString('hex')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _encodeDynamicBytes(value) {
|
||||
var dataLength = parseInt(32 * Math.ceil(value.length / 32));
|
||||
var padding = new Buffer(dataLength - value.length);
|
||||
padding.fill(0);
|
||||
|
||||
return Buffer.concat([
|
||||
uint256Coder.encode(value.length),
|
||||
value,
|
||||
padding
|
||||
]);
|
||||
}
|
||||
|
||||
function _decodeDynamicBytes(data, offset) {
|
||||
if (data.length < offset + 32) { throw new Error('invalid bytes'); }
|
||||
|
||||
var length = uint256Coder.decode(data, offset).value;
|
||||
length = length.toNumber();
|
||||
if (data.length < offset + 32 + length) { throw new Error('invalid bytes'); }
|
||||
|
||||
return {
|
||||
consumed: parseInt(32 + 32 * Math.ceil(length / 32)),
|
||||
value: data.slice(offset + 32, offset + 32 + length),
|
||||
}
|
||||
}
|
||||
|
||||
var coderDynamicBytes = {
|
||||
encode: function(value) {
|
||||
return _encodeDynamicBytes(utils.hexOrBuffer(value));
|
||||
},
|
||||
decode: function(data, offset) {
|
||||
var result = _decodeDynamicBytes(data, offset);
|
||||
result.value = '0x' + result.value.toString('hex');
|
||||
return result;
|
||||
},
|
||||
dynamic: true
|
||||
};
|
||||
|
||||
var coderString = {
|
||||
encode: function(value) {
|
||||
return _encodeDynamicBytes(new Buffer(value, 'utf8'));
|
||||
},
|
||||
decode: function(data, offset) {
|
||||
var result = _decodeDynamicBytes(data, offset);
|
||||
result.value = result.value.toString('utf8');
|
||||
return result;
|
||||
},
|
||||
dynamic: true
|
||||
};
|
||||
|
||||
function coderArray(coder, length) {
|
||||
return {
|
||||
encode: function(value) {
|
||||
if (!Array.isArray(value)) { throw new Error('invalid array'); }
|
||||
|
||||
var result = new Buffer(0);
|
||||
if (length === -1) {
|
||||
length = value.length;
|
||||
result = uint256Coder.encode(length);
|
||||
}
|
||||
|
||||
if (length !== value.length) { throw new Error('size mismatch'); }
|
||||
|
||||
value.forEach(function(value) {
|
||||
result = Buffer.concat([
|
||||
result,
|
||||
coder.encode(value)
|
||||
]);
|
||||
});
|
||||
|
||||
return result;
|
||||
},
|
||||
decode: function(data, offset) {
|
||||
// @TODO:
|
||||
//if (data.length < offset + length * 32) { throw new Error('invalid array'); }
|
||||
|
||||
var consumed = 0;
|
||||
|
||||
var result;
|
||||
if (length === -1) {
|
||||
result = uint256Coder.decode(data, offset);
|
||||
length = result.value.toNumber();
|
||||
consumed += result.consumed;
|
||||
offset += result.consumed;
|
||||
}
|
||||
|
||||
var value = [];
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
var result = coder.decode(data, offset);
|
||||
consumed += result.consumed;
|
||||
offset += result.consumed;
|
||||
value.push(result.value);
|
||||
}
|
||||
|
||||
return {
|
||||
consumed: consumed,
|
||||
value: value,
|
||||
}
|
||||
},
|
||||
dynamic: (length === -1)
|
||||
}
|
||||
}
|
||||
|
||||
// Break the type up into [staticType][staticArray]*[dynamicArray]? | [dynamicType] and
|
||||
// build the coder up from its parts
|
||||
var paramTypePart = new RegExp(/^((u?int|bytes)([0-9]*)|(address|bool|string)|(\[([0-9]*)\]))/);
|
||||
function getParamCoder(type) {
|
||||
var coder = null;
|
||||
while (type) {
|
||||
var part = type.match(paramTypePart);
|
||||
if (!part) { throw new Error('invalid type: ' + type); }
|
||||
type = type.substring(part[0].length);
|
||||
|
||||
var prefix = (part[2] || part[4] || part[5]);
|
||||
switch (prefix) {
|
||||
case 'int': case 'uint':
|
||||
if (coder) { throw new Error('invalid type ' + type); }
|
||||
var size = parseInt(part[3] || 256);
|
||||
if (size === 0 || size > 256 || (size % 8) !== 0) {
|
||||
throw new Error('invalid type ' + type);
|
||||
}
|
||||
coder = coderNumber(size / 8, (prefix === 'int'));
|
||||
break;
|
||||
|
||||
case 'bool':
|
||||
if (coder) { throw new Error('invalid type ' + type); }
|
||||
coder = coderBoolean;
|
||||
break;
|
||||
|
||||
case 'string':
|
||||
if (coder) { throw new Error('invalid type ' + type); }
|
||||
coder = coderString;
|
||||
break;
|
||||
|
||||
case 'bytes':
|
||||
if (coder) { throw new Error('invalid type ' + type); }
|
||||
if (part[3]) {
|
||||
var size = parseInt(part[3]);
|
||||
if (size === 0 || size > 32) {
|
||||
throw new Error('invalid type ' + type);
|
||||
}
|
||||
coder = coderFixedBytes(size);
|
||||
} else {
|
||||
coder = coderDynamicBytes;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'address':
|
||||
if (coder) { throw new Error('invalid type ' + type); }
|
||||
coder = coderAddress;
|
||||
break;
|
||||
|
||||
case '[]':
|
||||
if (!coder || coder.dynamic) { throw new Error('invalid type ' + type); }
|
||||
coder = coderArray(coder, -1);
|
||||
break;
|
||||
|
||||
// "[0-9+]"
|
||||
default:
|
||||
if (!coder || coder.dynamic) { throw new Error('invalid type ' + type); }
|
||||
var size = parseInt(part[6]);
|
||||
coder = coderArray(coder, size);
|
||||
}
|
||||
}
|
||||
|
||||
if (!coder) { throw new Error('invalid type'); }
|
||||
return coder;
|
||||
}
|
||||
|
||||
function Interface(abi) {
|
||||
if (!(this instanceof Interface)) { throw new Error('missing new'); }
|
||||
|
||||
//defineProperty(this, 'address', address);
|
||||
|
||||
// Wrap this up as JSON so we can return a "copy" and avoid mutation
|
||||
defineFrozen(this, 'abi', abi);
|
||||
|
||||
var methods = [], events = [];
|
||||
abi.forEach(function(method) {
|
||||
|
||||
var func = null;
|
||||
|
||||
switch (method.type) {
|
||||
case 'function':
|
||||
methods.push(method.name);
|
||||
func = (function() {
|
||||
var inputTypes = getKeys(method.inputs, 'type');
|
||||
var outputTypes = getKeys(method.outputs, 'type');
|
||||
|
||||
var func = function() {
|
||||
var signature = method.name + '(' + getKeys(method.inputs, 'type').join(',') + ')';
|
||||
var result = {
|
||||
name: method.name,
|
||||
signature: signature,
|
||||
};
|
||||
|
||||
var params = Array.prototype.slice.call(arguments, 0);
|
||||
|
||||
if (params.length < inputTypes.length) {
|
||||
throw new Error('missing parameter');
|
||||
} else if (params.length > inputTypes.length) {
|
||||
throw new Error('too many parameters');
|
||||
}
|
||||
|
||||
signature = '0x' + utils.sha3(signature).slice(0, 4).toString('hex');
|
||||
|
||||
result.data = signature + Interface.encodeParams(inputTypes, params).substring(2);
|
||||
if (method.constant) {
|
||||
result.type = 'call';
|
||||
result.parse = function(data) {
|
||||
return Interface.decodeParams(
|
||||
outputTypes,
|
||||
utils.hexOrBuffer(data)
|
||||
);
|
||||
};
|
||||
} else {
|
||||
result.type = 'transaction';
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
defineFrozen(func, 'inputs', getKeys(method.inputs, 'name'));
|
||||
defineFrozen(func, 'outputs', getKeys(method.outputs, 'name'));
|
||||
|
||||
return func;
|
||||
})();
|
||||
break;
|
||||
|
||||
case 'event':
|
||||
events.push(method.name);
|
||||
func = (function() {
|
||||
var inputTypes = getKeys(method.inputs, 'type');
|
||||
var func = function() {
|
||||
var signature = method.name + '(' + getKeys(method.inputs, 'type').join(',') + ')';
|
||||
var result = {
|
||||
inputs: method.inputs,
|
||||
name: method.name,
|
||||
type: 'filter',
|
||||
signature: signature,
|
||||
topics: ['0x' + utils.sha3(signature).toString('hex')],
|
||||
};
|
||||
result.parse = function(data) {
|
||||
return Interface.decodeParams(
|
||||
inputTypes,
|
||||
utils.hexOrBuffer(data)
|
||||
);
|
||||
};
|
||||
return result;
|
||||
}
|
||||
defineFrozen(func, 'inputs', getKeys(method.inputs, 'name'));
|
||||
return func;
|
||||
})();
|
||||
break;
|
||||
|
||||
default:
|
||||
func = (function() {
|
||||
return function() {
|
||||
return {type: 'unknown'}
|
||||
}
|
||||
})();
|
||||
break;
|
||||
}
|
||||
|
||||
utils.defineProperty(this, method.name, func);
|
||||
}, this);
|
||||
|
||||
defineFrozen(this, 'methods', methods);
|
||||
defineFrozen(this, 'events', events);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
utils.defineProperty(Interface, 'encodeParams', function(types, values) {
|
||||
if (types.length !== values.length) { throw new Error('types/values mismatch'); }
|
||||
|
||||
var parts = [];
|
||||
|
||||
types.forEach(function(type, index) {
|
||||
var coder = getParamCoder(type);
|
||||
parts.push({dynamic: coder.dynamic, value: coder.encode(values[index])});
|
||||
})
|
||||
|
||||
function alignSize(size) {
|
||||
return parseInt(32 * Math.ceil(size / 32));
|
||||
}
|
||||
|
||||
var staticSize = 0, dynamicSize = 0;
|
||||
parts.forEach(function(part) {
|
||||
if (part.dynamic) {
|
||||
staticSize += 32;
|
||||
dynamicSize += alignSize(part.value.length);
|
||||
} else {
|
||||
staticSize += alignSize(part.value.length);
|
||||
}
|
||||
});
|
||||
|
||||
var offset = 0, dynamicOffset = staticSize;
|
||||
var data = new Buffer(staticSize + dynamicSize);
|
||||
|
||||
parts.forEach(function(part, index) {
|
||||
if (part.dynamic) {
|
||||
uint256Coder.encode(dynamicOffset).copy(data, offset);
|
||||
offset += 32;
|
||||
|
||||
part.value.copy(data, dynamicOffset);
|
||||
dynamicOffset += alignSize(part.value.length);
|
||||
} else {
|
||||
part.value.copy(data, offset);
|
||||
offset += alignSize(part.value.length);
|
||||
}
|
||||
});
|
||||
|
||||
return '0x' + data.toString('hex');
|
||||
});
|
||||
|
||||
utils.defineProperty(Interface, 'decodeParams', function(types, data) {
|
||||
data = utils.hexOrBuffer(data);
|
||||
|
||||
var values = [];
|
||||
var offset = 0;
|
||||
types.forEach(function(type) {
|
||||
var coder = getParamCoder(type);
|
||||
if (coder.dynamic) {
|
||||
var dynamicOffset = uint256Coder.decode(data, offset);
|
||||
var result = coder.decode(data, dynamicOffset.value.toNumber());
|
||||
offset += dynamicOffset.consumed;
|
||||
} else {
|
||||
var result = coder.decode(data, offset);
|
||||
offset += result.consumed;
|
||||
}
|
||||
values.push(result.value);
|
||||
});
|
||||
return values;
|
||||
});
|
||||
|
||||
|
||||
var allowedTransactionKeys = {
|
||||
data: true, from: true, gasLimit: true, gasPrice:true, to: true, value: true
|
||||
}
|
||||
|
||||
function Contract(wallet, contractAddress, contractInterface) {
|
||||
utils.defineProperty(this, 'wallet', wallet);
|
||||
|
||||
utils.defineProperty(this, 'contractAddress', contractAddress);
|
||||
utils.defineProperty(this, 'interface', contractInterface);
|
||||
|
||||
var self = this;
|
||||
|
||||
var filters = {};
|
||||
function setupFilter(call, callback) {
|
||||
var info = filters[call.name];
|
||||
|
||||
// Stop and remove the filter
|
||||
if (!callback) {
|
||||
if (info) { info.filter.stopWatching(); }
|
||||
delete filters[call.name];
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof(callback) !== 'function') {
|
||||
throw new Error('invalid callback');
|
||||
}
|
||||
|
||||
// Already have a filter, just update the callback
|
||||
if (info) {
|
||||
info.callback = callback;
|
||||
return;
|
||||
}
|
||||
|
||||
info = {callback: callback};
|
||||
filters[call.name] = info;
|
||||
|
||||
// Start a new filter
|
||||
/*
|
||||
info.filter = web3.eth.filter({
|
||||
address: contractAddress,
|
||||
topics: call.topics
|
||||
}, function(error, result) {
|
||||
// @TODO: Emit errors to .onerror? Maybe?
|
||||
if (error) {
|
||||
console.log(error);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
info.callback.apply(self, call.parse(result.data));
|
||||
} catch(error) {
|
||||
console.log(error);
|
||||
}
|
||||
});
|
||||
*/
|
||||
}
|
||||
function runMethod(method, estimateOnly) {
|
||||
return function() {
|
||||
var provider = wallet._provider;
|
||||
|
||||
var transaction = {}
|
||||
|
||||
var params = Array.prototype.slice.call(arguments);
|
||||
if (params.length == contractInterface[method].inputs.length + 1) {
|
||||
transaction = params.pop();
|
||||
if (typeof(transaction) !== 'object') {
|
||||
throw new Error('invalid transaction overrides');
|
||||
}
|
||||
for (var key in transaction) {
|
||||
if (!allowedTransactionKeys[key]) {
|
||||
throw new Error('unknown transaction override ' + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var call = contractInterface[method].apply(contractInterface, params);
|
||||
switch (call.type) {
|
||||
case 'call':
|
||||
['data', 'gasLimit', 'gasPrice', 'to', 'value'].forEach(function(key) {
|
||||
if (transaction[key] != null) {
|
||||
throw new Error('call cannot override ' + key) ;
|
||||
}
|
||||
});
|
||||
transaction.data = call.data;
|
||||
if (transaction.from == null) {
|
||||
transaction.from = wallet.address;
|
||||
}
|
||||
transaction.to = contractAddress;
|
||||
|
||||
if (estimateOnly) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
resolve(new utils.BN(0));
|
||||
});
|
||||
}
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
provider.call(transaction).then(function(value) {
|
||||
resolve(call.parse(value));
|
||||
}, function(error) {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
|
||||
case 'transaction':
|
||||
['data', 'from', 'to'].forEach(function(key) {
|
||||
if (transaction[key] != null) {
|
||||
throw new Error('transaction cannot override ' + key) ;
|
||||
}
|
||||
});
|
||||
transaction.data = call.data;
|
||||
transaction.to = contractAddress;
|
||||
if (transaction.gasLimit == null) {
|
||||
transaction.gasLimit = 3000000;
|
||||
}
|
||||
|
||||
if (estimateOnly) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
provider.estimateGas(transaction).then(function(gasEstimate) {
|
||||
resolve(gasEstimate);
|
||||
}, function(error) {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
Promise.all([
|
||||
provider.getTransactionCount(wallet.address, 'pending'),
|
||||
provider.getGasPrice(),
|
||||
]).then(function(results) {
|
||||
if (transaction.nonce == null) {
|
||||
transaction.nonce = results[0];
|
||||
} else if (console.warn) {
|
||||
console.warn('Overriding suggested nonce: ' + results[0]);
|
||||
}
|
||||
if (transaction.gasPrice == null) {
|
||||
transaction.gasPrice = results[1];
|
||||
} else if (console.warn) {
|
||||
console.warn('Overriding suggested gasPrice: ' + utils.hexlify(results[1]));
|
||||
}
|
||||
|
||||
var signedTransaction = wallet.sign(transaction);
|
||||
provider.sendTransaction(signedTransaction).then(function(txid) {
|
||||
resolve(txid);
|
||||
}, function(error) {
|
||||
reject(error);
|
||||
});
|
||||
}, function(error) {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var estimate = {};
|
||||
utils.defineProperty(this, 'estimate', estimate);
|
||||
|
||||
contractInterface.methods.forEach(function(method) {
|
||||
utils.defineProperty(this, method, runMethod(method, false));
|
||||
utils.defineProperty(estimate, method, runMethod(method, true));
|
||||
}, this);
|
||||
|
||||
contractInterface.events.forEach(function(method) {
|
||||
var call = contractInterface[method].apply(contractInterface, []);
|
||||
Object.defineProperty(self, 'on' + call.name.toLowerCase(), {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
//console.log('get');
|
||||
var info = filters[call.name];
|
||||
if (!info || !info[call.name]) { return null; }
|
||||
return info.callback;
|
||||
},
|
||||
set: function(value) {
|
||||
//console.log('set');
|
||||
setupFilter(call, value);
|
||||
}
|
||||
});
|
||||
}, this);
|
||||
}
|
||||
utils.defineProperty(Contract, 'Interface', Interface);
|
||||
|
||||
module.exports = Contract;
|
||||
331
lib/providers.js
331
lib/providers.js
@@ -1,331 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var inherits = require('inherits');
|
||||
var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest;
|
||||
|
||||
var utils = require('./utils.js');
|
||||
|
||||
// The required methods a provider must support
|
||||
var methods = [
|
||||
'getBalance',
|
||||
'getTransactionCount',
|
||||
'getGasPrice',
|
||||
'sendTransaction',
|
||||
'call',
|
||||
'estimateGas'
|
||||
];
|
||||
|
||||
|
||||
// Manages JSON-RPC to an Ethereum node
|
||||
function Web3Connector(provider) {
|
||||
if (!(this instanceof Web3Connector)) { throw new Error('missing new'); }
|
||||
|
||||
var nextMessageId = 1;
|
||||
utils.defineProperty(this, 'sendMessage', function(method, params) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
provider.sendAsync({
|
||||
id: (nextMessageId++),
|
||||
jsonrpc: '2.0',
|
||||
method: method,
|
||||
params: params
|
||||
}, function(error, result) {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
if (result.error) {
|
||||
var error = new Error(result.error.message);
|
||||
error.code = result.error.code;
|
||||
error.data = result.error.data;
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(result.result);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Mimics Web3 interface
|
||||
function rpcSendAsync(url) {
|
||||
return {
|
||||
sendAsync: function(payload, callback) {
|
||||
|
||||
var request = new XMLHttpRequest();
|
||||
request.open('POST', url, true);
|
||||
request.setRequestHeader('Content-Type','application/json');
|
||||
request.onreadystatechange = function() {
|
||||
if (request.readyState !== 4) { return; }
|
||||
|
||||
if (typeof(callback) !== 'function') { return; }
|
||||
|
||||
var result = request.responseText;
|
||||
try {
|
||||
callback(null, JSON.parse(result));
|
||||
} catch (error) {
|
||||
var responseError = new Error('invalid response');
|
||||
responseError.orginialError = error;
|
||||
responseError.data = result;
|
||||
callback(responseError);
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
request.send(JSON.stringify(payload));
|
||||
} catch (error) {
|
||||
var connectionError = new Error('connection error');
|
||||
connectionError.error = error;
|
||||
callback(connectionError);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function SendAsyncProvider(sendAsync) {
|
||||
if (!(this instanceof SendAsyncProvider)) { throw new Error('missing new'); }
|
||||
utils.defineProperty(this, 'client', new Web3Connector(sendAsync));
|
||||
}
|
||||
|
||||
|
||||
function validBlock(value) {
|
||||
if (value == null) { return 'latest'; }
|
||||
if (value === 'latest' || value === 'pending') { return value; }
|
||||
|
||||
if (typeof(value) === 'number' && value == parseInt(value)) {
|
||||
return parseInt(value);
|
||||
}
|
||||
|
||||
throw new Error('invalid blockNumber');
|
||||
}
|
||||
|
||||
function postProcess(client, method, params, makeBN) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
client.sendMessage(method, params).then(function (result) {
|
||||
if (!utils.isHexString(result)) {
|
||||
reject(new Error('invalid server response'));
|
||||
} else {
|
||||
result = result.substring(2);
|
||||
if (makeBN) {
|
||||
result = new utils.BN(result, 16);
|
||||
} else {
|
||||
result = parseInt(result, 16);
|
||||
}
|
||||
resolve(result);
|
||||
}
|
||||
}, function(error) {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
utils.defineProperty(SendAsyncProvider.prototype, 'getBalance', function(address, blockNumber) {
|
||||
return postProcess(this.client, 'eth_getBalance', [
|
||||
utils.getAddress(address),
|
||||
validBlock(blockNumber)
|
||||
], true);
|
||||
});
|
||||
|
||||
utils.defineProperty(SendAsyncProvider.prototype, 'getTransactionCount', function(address, blockNumber) {
|
||||
return postProcess(this.client, 'eth_getTransactionCount', [
|
||||
utils.getAddress(address),
|
||||
validBlock(blockNumber)
|
||||
], false);
|
||||
});
|
||||
|
||||
utils.defineProperty(SendAsyncProvider.prototype, 'getGasPrice', function() {
|
||||
return postProcess(this.client, 'eth_gasPrice', [], true);
|
||||
});
|
||||
|
||||
utils.defineProperty(SendAsyncProvider.prototype, 'sendTransaction', function(signedTransaction) {
|
||||
if (!utils.isHexString(signedTransaction)) { throw new Error('invalid transaction'); }
|
||||
return this.client.sendMessage('eth_sendRawTransaction', [signedTransaction]);
|
||||
});
|
||||
|
||||
utils.defineProperty(SendAsyncProvider.prototype, 'call', function(transaction) {
|
||||
// @TODO: check validTransaction?
|
||||
return this.client.sendMessage('eth_call', [transaction]);
|
||||
});
|
||||
|
||||
utils.defineProperty(SendAsyncProvider.prototype, 'estimateGas', function(transaction) {
|
||||
// @TODO: check validTransaction?
|
||||
return postProcess(this.client, 'eth_estimateGas', [transaction], true);
|
||||
});
|
||||
|
||||
|
||||
var providers = {};
|
||||
|
||||
|
||||
function HttpProvider(url) {
|
||||
if (!(this instanceof HttpProvider)) { throw new Error('missing new'); }
|
||||
SendAsyncProvider.call(this, rpcSendAsync(url));
|
||||
}
|
||||
inherits(HttpProvider, SendAsyncProvider);
|
||||
utils.defineProperty(providers, 'HttpProvider', HttpProvider);
|
||||
|
||||
|
||||
function Web3Provider(provider) {
|
||||
if (!(this instanceof Web3Provider)) { throw new Error('missing new'); }
|
||||
if (provider.currentProvider) { provider = provider.currentProvider; }
|
||||
if (!provider.sendAsync) { throw new Error('invalid provider'); }
|
||||
SendAsyncProvider.call(this, provider);
|
||||
}
|
||||
inherits(Web3Provider, SendAsyncProvider);
|
||||
utils.defineProperty(providers, 'Web3Provider', Web3Provider);
|
||||
|
||||
|
||||
function base10ToBN(value) {
|
||||
return new utils.BN(value);
|
||||
}
|
||||
|
||||
function hexToBN(value) {
|
||||
return new utils.BN(ensureHex(value).substring(2), 16);
|
||||
}
|
||||
|
||||
function hexToNumber(value) {
|
||||
if (!utils.isHexString(value)) { throw new Error('invalid hex string'); }
|
||||
return parseInt(value.substring(2), 16);
|
||||
}
|
||||
|
||||
function ensureHex(value) {
|
||||
if (!utils.isHexString(value)) { throw new Error('invalid hex string'); }
|
||||
return value;
|
||||
}
|
||||
|
||||
function ensureTxid(value) {
|
||||
if (!utils.isHexString(value, 32)) { throw new Error('invalid hex string'); }
|
||||
return value;
|
||||
}
|
||||
|
||||
function getGasPrice(value) {
|
||||
if (!value || !value.transactions || value.transactions.length === 0) {
|
||||
throw new Error('invalid response');
|
||||
}
|
||||
return hexToBN(value.transactions[0].gasPrice);
|
||||
}
|
||||
|
||||
function EtherscanProvider(options) {
|
||||
if (!(this instanceof EtherscanProvider)) { throw new Error('missing new'); }
|
||||
if (!options) { options = {}; }
|
||||
|
||||
var testnet = options.testnet;
|
||||
var apiKey = options.apiKey;
|
||||
|
||||
utils.defineProperty(this, 'testnet', testnet);
|
||||
utils.defineProperty(this, 'apiKey', apiKey);
|
||||
|
||||
utils.defineProperty(this, '_send', function(query, check) {
|
||||
var url = (testnet ? 'https://testnet.etherscan.io/api?': 'https://api.etherscan.io/api?');
|
||||
url += query;
|
||||
if (apiKey) { url += 'apikey=' + apiKey; }
|
||||
//console.log('URL', url);
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
var request = new XMLHttpRequest();
|
||||
request.open('GET', url, true);
|
||||
request.onreadystatechange = function() {
|
||||
if (request.readyState !== 4) { return; }
|
||||
|
||||
var result = request.responseText;
|
||||
//console.log(result);
|
||||
try {
|
||||
result = JSON.parse(result);
|
||||
if (result.message) {
|
||||
if (result.message === 'OK') {
|
||||
resolve(check(result.result));
|
||||
} else {
|
||||
reject(new Error('invalid response'));
|
||||
}
|
||||
} else {
|
||||
if (result.error) {
|
||||
console.log(result.error);
|
||||
reject(new Error('invalid response'));
|
||||
} else {
|
||||
resolve(check(result.result));
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
reject(new Error('invalid response'));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
request.send();
|
||||
} catch (error) {
|
||||
var connectionError = new Error('connection error');
|
||||
connectionError.error = error;
|
||||
reject(connectionError);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
utils.defineProperty(providers, 'EtherscanProvider', EtherscanProvider);
|
||||
|
||||
utils.defineProperty(EtherscanProvider.prototype, 'getBalance', function(address, blockNumber) {
|
||||
address = utils.getAddress(address);
|
||||
blockNumber = validBlock(blockNumber);
|
||||
var query = ('module=account&action=balance&address=' + address + '&tag=' + blockNumber);
|
||||
return this._send(query, base10ToBN);
|
||||
});
|
||||
|
||||
utils.defineProperty(EtherscanProvider.prototype, 'getTransactionCount', function(address, blockNumber) {
|
||||
address = utils.getAddress(address);
|
||||
blockNumber = validBlock(blockNumber);
|
||||
var query = ('module=proxy&action=eth_getTransactionCount&address=' + address + '&tag=' + blockNumber);
|
||||
return this._send(query, hexToNumber);
|
||||
});
|
||||
|
||||
utils.defineProperty(EtherscanProvider.prototype, 'getGasPrice', function() {
|
||||
var query = ('module=proxy&action=eth_gasPrice');
|
||||
return this._send(query, hexToBN);
|
||||
});
|
||||
|
||||
utils.defineProperty(EtherscanProvider.prototype, 'sendTransaction', function(signedTransaction) {
|
||||
if (!utils.isHexString(signedTransaction)) { throw new Error('invalid transaction'); }
|
||||
var query = ('module=proxy&action=eth_sendRawTransaction&hex=' + signedTransaction);
|
||||
return this._send(query, ensureTxid);
|
||||
});
|
||||
|
||||
utils.defineProperty(EtherscanProvider.prototype, 'call', function(transaction) {
|
||||
var address = utils.getAddress(transaction.to);
|
||||
var data = transaction.data;
|
||||
if (!utils.isHexString(data)) { throw new Error('invalid data'); }
|
||||
var query = ('module=proxy&action=eth_call&to=' + address + '&data=' + data);
|
||||
return this._send(query, ensureHex);
|
||||
});
|
||||
|
||||
utils.defineProperty(EtherscanProvider.prototype, 'estimateGas', function(transaction) {
|
||||
var address = utils.getAddress(transaction.to);
|
||||
|
||||
var query = 'module=proxy&action=eth_estimateGas&to=' + address;
|
||||
if (transaction.gasPrice) {
|
||||
query += '&gasPrice=' + utils.hexlify(transaction.gasPrice);
|
||||
}
|
||||
if (transaction.gasLimit) {
|
||||
query += '&gas=' + utils.hexlify(transaction.gasLimit);
|
||||
}
|
||||
if (transaction.from) {
|
||||
query += '&from=' + utils.getAddress(transaction.from);
|
||||
}
|
||||
if (transaction.data) {
|
||||
query += '&data=' + ensureHex(transaction.data);
|
||||
}
|
||||
if (transaction.value) {
|
||||
query += '&value=' + utils.hexlify(transaction.value);
|
||||
}
|
||||
return this._send(query, hexToBN);
|
||||
});
|
||||
|
||||
|
||||
utils.defineProperty(providers, 'isProvider', function(provider) {
|
||||
if (!provider) { return false; }
|
||||
for (var i = 0; i < methods; i++) {
|
||||
if (typeof(provider[methods[i]]) !== 'function') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
module.exports = providers;
|
||||
@@ -1,4 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = require('crypto').randomBytes;
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var aes = require('aes-js');
|
||||
var randomBytes = require('./random-bytes.js');
|
||||
|
||||
var utils = require('./utils.js');
|
||||
|
||||
function Randomish() {
|
||||
if (!(this instanceof Randomish)) { throw new Error('missing new'); }
|
||||
|
||||
var weak = (randomBytes._weakCrypto || false);
|
||||
|
||||
var entropyBits = (weak ? 0: ((32 + 16) * 8));
|
||||
Object.defineProperty(this, 'entropy', {
|
||||
enumerable: true,
|
||||
get: function() { return entropyBits; }
|
||||
});
|
||||
|
||||
var entropy = new aes.ModeOfOperation.cbc(
|
||||
Randomish.randomishBytes(32),
|
||||
Randomish.randomishBytes(16)
|
||||
);
|
||||
|
||||
utils.defineProperty(this, 'feedEntropy', function(data, expectedEntropyBits) {
|
||||
if (!data) { data = ''; }
|
||||
if (!expectedEntropyBits) { expectedEntropyBits = 0; }
|
||||
|
||||
if (parseInt(expectedEntropyBits) != expectedEntropyBits) {
|
||||
throw new Error('invalid expectedEntropyBits');
|
||||
}
|
||||
|
||||
data = (new Date()).getTime() + '-' + JSON.stringify(data) + '-' + data.toString();
|
||||
var hashed = utils.sha3(new Buffer(data, 'utf8'));
|
||||
|
||||
entropyBits += expectedEntropyBits + (weak ? 0: ((32) * 8));
|
||||
|
||||
// Feed the hashed data and random data to the mode of operation
|
||||
entropy.encrypt(hashed.slice(0, 16));
|
||||
entropy.encrypt(randomBytes(16));
|
||||
entropy.encrypt(hashed.slice(0, 16));
|
||||
return new Buffer(entropy.encrypt(randomBytes(16)));
|
||||
});
|
||||
|
||||
utils.defineProperty(this, 'randomBytes', function(length, key) {
|
||||
if (parseInt(length) != length || length <= 0 || length > 1024) {
|
||||
throw new Error('invalid length');
|
||||
}
|
||||
|
||||
// If we don't have a key, create one
|
||||
if (!key) {
|
||||
key = Buffer.concat([this.feedEntropy(), this.feedEntropy()]);
|
||||
}
|
||||
|
||||
if (!Buffer.isBuffer(key) || key.length !== 32) {
|
||||
throw new Error('invalid key');
|
||||
}
|
||||
|
||||
var aesCbc = new aes.ModeOfOperation.cbc(key, this.feedEntropy());
|
||||
var result = new Buffer(0);
|
||||
while (result.length < length) {
|
||||
result = Buffer.concat([result, this.feedEntropy()]);
|
||||
}
|
||||
|
||||
return result.slice(0, length);
|
||||
});
|
||||
|
||||
this.feedEntropy();
|
||||
}
|
||||
|
||||
utils.defineProperty(Randomish, 'randomishBytes', function(length) {
|
||||
return randomBytes(length);
|
||||
});
|
||||
|
||||
module.exports = Randomish
|
||||
@@ -1,345 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var aes = require('aes-js');
|
||||
var pbkdf2 = require('pbkdf2');
|
||||
var scrypt = require('scrypt-js');
|
||||
var uuid = require('uuid');
|
||||
|
||||
var Randomish = require('./randomish.js');
|
||||
var SigningKey = require('./signing-key.js');
|
||||
var utils = require('./utils.js')
|
||||
|
||||
|
||||
// Search an Object and its children recursively, caselessly.
|
||||
function searchPath(object, path) {
|
||||
var currentChild = object;
|
||||
|
||||
var comps = path.toLowerCase().split('/');
|
||||
for (var i = 0; i < comps.length; i++) {
|
||||
|
||||
// Search for a child object with a case-insensitive matching key
|
||||
var matchingChild = null;
|
||||
for (var key in currentChild) {
|
||||
if (key.toLowerCase() === comps[i]) {
|
||||
matchingChild = currentChild[key];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Didn't find one. :'(
|
||||
if (matchingChild === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Now check this child...
|
||||
currentChild = matchingChild;
|
||||
}
|
||||
|
||||
return currentChild;
|
||||
}
|
||||
|
||||
/*
|
||||
function SecretStorage(json, signingKey) {
|
||||
if (!(this instanceof SecretStorage)) { throw new Error('missing new'); }
|
||||
|
||||
utils.defineProperty(this, 'json', json);
|
||||
|
||||
Object.defineProperty(this, 'data', {
|
||||
enumerable: true,
|
||||
get: function() { return JSON.parse(json); }
|
||||
});
|
||||
|
||||
utils.defineProperty(this, 'address', signingKey.privateKey);
|
||||
utils.defineProperty(this, 'signingKey', signingKey);
|
||||
}
|
||||
*/
|
||||
var secretStorage = {};
|
||||
|
||||
|
||||
utils.defineProperty(secretStorage, 'isCrowdsaleWallet', function(json) {
|
||||
try {
|
||||
var data = JSON.parse(json);
|
||||
} catch (error) { return false; }
|
||||
|
||||
return (data.encseed && data.ethaddr);
|
||||
});
|
||||
|
||||
utils.defineProperty(secretStorage, 'isValidWallet', function(json) {
|
||||
try {
|
||||
var data = JSON.parse(json);
|
||||
} catch (error) { return false; }
|
||||
|
||||
if (!data.version || parseInt(data.version) !== data.version || parseInt(data.version) !== 3) {
|
||||
return false;
|
||||
}
|
||||
// @TODO: Put more checks to make sure it has kdf, iv and all that good stuff
|
||||
return true;
|
||||
});
|
||||
|
||||
|
||||
// See: https://github.com/ethereum/pyethsaletool
|
||||
utils.defineProperty(secretStorage, 'decryptCrowdsale', function(json, password) {
|
||||
var data = JSON.parse(json);
|
||||
|
||||
// Ethereum Address
|
||||
var ethaddr = utils.getAddress(searchPath(data, 'ethaddr'));
|
||||
|
||||
// Encrypted Seed
|
||||
var encseed = new Buffer(searchPath(data, 'encseed'), 'hex');
|
||||
if (!encseed || (encseed.length % 16) !== 0) {
|
||||
throw new Error('invalid encseed');
|
||||
}
|
||||
|
||||
var key = pbkdf2.pbkdf2Sync(password, password, 2000, 32, 'sha256').slice(0, 16);
|
||||
|
||||
var iv = encseed.slice(0, 16);
|
||||
var encryptedSeed = encseed.slice(16);
|
||||
|
||||
// Decrypt the seed
|
||||
var seed = new Buffer(0);
|
||||
var aesCbc = new aes.ModeOfOperation.cbc(key, iv);
|
||||
for (var i = 0; i < encryptedSeed.length; i += 16) {
|
||||
seed = Buffer.concat([seed, new Buffer(aesCbc.decrypt(encryptedSeed.slice(i, i + 16)))]);
|
||||
}
|
||||
|
||||
// Check PKCS#7 padding is valid
|
||||
var pad = seed[seed.length - 1];
|
||||
if (pad > 16 || pad > seed.length) {
|
||||
throw new Error('invalid password');
|
||||
}
|
||||
for (var i = seed.length - pad; i < seed.length; i++) {
|
||||
if (seed[i] !== pad) {
|
||||
throw new Error('invalid password');
|
||||
}
|
||||
}
|
||||
|
||||
// Strip the padding
|
||||
seed = seed.slice(0, seed.length - pad);
|
||||
|
||||
// This wallet format is weird... Convert the binary encoded hex to a string.
|
||||
var seedHex = '';
|
||||
for (var i = 0; i < seed.length; i++) {
|
||||
seedHex += String.fromCharCode(seed[i]);
|
||||
}
|
||||
|
||||
var signingKey = new SigningKey(utils.sha3(new Buffer(seedHex)));
|
||||
|
||||
if (signingKey.address !== ethaddr) {
|
||||
throw new Error('corrupt crowdsale wallet');
|
||||
}
|
||||
|
||||
return signingKey;
|
||||
});
|
||||
|
||||
|
||||
utils.defineProperty(secretStorage, 'decrypt', function(json, password, progressCallback) {
|
||||
if (!Buffer.isBuffer(password)) { throw new Error('password must be a buffer'); }
|
||||
|
||||
var data = JSON.parse(json);
|
||||
|
||||
var decrypt = function(key, ciphertext) {
|
||||
var cipher = searchPath(data, 'crypto/cipher');
|
||||
if (cipher === 'aes-128-ctr') {
|
||||
var iv = new Buffer(searchPath(data, 'crypto/cipherparams/iv'), 'hex')
|
||||
var counter = new aes.Counter(iv);
|
||||
|
||||
var aesCtr = new aes.ModeOfOperation.ctr(key, counter);
|
||||
|
||||
return new Buffer(aesCtr.decrypt(ciphertext));
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
var computeMAC = function(derivedHalf, ciphertext) {
|
||||
return utils.sha3(Buffer.concat([derivedHalf, ciphertext]));
|
||||
}
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
var kdf = searchPath(data, 'crypto/kdf');
|
||||
if (kdf && kdf.toLowerCase() === 'scrypt') {
|
||||
var salt = new Buffer(searchPath(data, 'crypto/kdfparams/salt'), 'hex');
|
||||
var N = parseInt(searchPath(data, 'crypto/kdfparams/n'));
|
||||
var r = parseInt(searchPath(data, 'crypto/kdfparams/r'));
|
||||
var p = parseInt(searchPath(data, 'crypto/kdfparams/p'));
|
||||
if (!N || !r || !p) {
|
||||
reject(new Error('unsupported key-derivation function parameters'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure N is a power of 2
|
||||
if ((N & (N - 1)) !== 0) {
|
||||
reject(new Error('unsupported key-derivation function parameter value for N'));
|
||||
return;
|
||||
}
|
||||
|
||||
var dkLen = searchPath(data, 'crypto/kdfparams/dklen');
|
||||
if (dkLen !== 32) {
|
||||
reject( new Error('unsupported key-derivation derived-key length'));
|
||||
return;
|
||||
}
|
||||
|
||||
scrypt(password, salt, N, r, p, dkLen, function(error, progress, key) {
|
||||
if (error) {
|
||||
error.progress = progress;
|
||||
reject(error);
|
||||
|
||||
} else if (key) {
|
||||
key = new Buffer(key);
|
||||
|
||||
var ciphertext = new Buffer(searchPath(data, 'crypto/ciphertext'), 'hex');
|
||||
|
||||
var computedMAC = computeMAC(key.slice(16, 32), ciphertext).toString('hex').toLowerCase();
|
||||
if (computedMAC !== searchPath(data, 'crypto/mac').toLowerCase()) {
|
||||
reject(new Error('invalid password'));
|
||||
return;
|
||||
}
|
||||
|
||||
var privateKey = decrypt(key.slice(0, 16), ciphertext);
|
||||
|
||||
if (!privateKey) {
|
||||
reject(new Error('unsupported cipher'));
|
||||
return;
|
||||
}
|
||||
|
||||
var signingKey = new SigningKey(privateKey);
|
||||
if (signingKey.address !== utils.getAddress(data.address)) {
|
||||
reject(new Error('address mismatch'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (progressCallback) { progressCallback(1); }
|
||||
resolve(signingKey);
|
||||
|
||||
} else if (progressCallback) {
|
||||
return progressCallback(progress);
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
// @TOOD: Support pbkdf2 kdf
|
||||
reject(new Error('unsupported key-derivation function'));
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
utils.defineProperty(secretStorage, 'encrypt', function(privateKey, password, options, progressCallback) {
|
||||
|
||||
// the options are optional, so adjust the call as needed
|
||||
if (typeof(options) === 'function' && !progressCallback) {
|
||||
progressCallback = options;
|
||||
options = {};
|
||||
}
|
||||
if (!options) { options = {}; }
|
||||
|
||||
// Check the private key
|
||||
if (privateKey instanceof SigningKey) {
|
||||
privateKey = privateKey.privateKey;
|
||||
}
|
||||
privateKey = utils.hexOrBuffer(privateKey, 'private key');
|
||||
if (privateKey.length !== 32) { throw new Erro('invalid private key'); }
|
||||
|
||||
// Check the password
|
||||
if (!Buffer.isBuffer(password)) { throw new Error('password must be a buffer'); }
|
||||
|
||||
// Check/generate the salt
|
||||
var salt = options.salt;
|
||||
if (salt) {
|
||||
salt = utils.hexOrBuffer(salt, 'salt');
|
||||
} else {
|
||||
salt = (new Randomish()).randomBytes(32);;
|
||||
}
|
||||
|
||||
// Override initialization vector
|
||||
var iv = null;
|
||||
if (options.iv) {
|
||||
iv = utils.hexOrBuffer(options.iv, 'iv');
|
||||
if (iv.length !== 16) { throw new Error('invalid iv'); }
|
||||
}
|
||||
|
||||
// Override the uuid
|
||||
var uuidRandom = options.uuid;
|
||||
if (uuidRandom) {
|
||||
uuidRandom = utils.hexOrBuffer(uuidRandom, 'uuid');
|
||||
if (uuidRandom.length !== 16) { throw new Error('invalid uuid'); }
|
||||
}
|
||||
|
||||
// Override the scrypt password-based key derivation function parameters
|
||||
var N = (1 << 17), r = 8, p = 1;
|
||||
if (options.scrypt) {
|
||||
if (options.scrypt.N) { N = options.scrypt.N; }
|
||||
if (options.scrypt.r) { r = options.scrypt.r; }
|
||||
if (options.scrypt.p) { p = options.scrypt.p; }
|
||||
}
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
|
||||
// We take 64 bytes:
|
||||
// - 32 bytes As normal for the Web3 secret storage (derivedKey, macPrefix)
|
||||
// - 16 bytes The initialization vector
|
||||
// - 16 bytes The UUID random bytes
|
||||
scrypt(password, salt, N, r, p, 64, function(error, progress, key) {
|
||||
if (error) {
|
||||
error.progress = progress;
|
||||
reject(error);
|
||||
|
||||
} else if (key) {
|
||||
// Convert the array-like to a Buffer
|
||||
key = new Buffer(key);
|
||||
|
||||
// These will be used to encrypt the wallet (as per Web3 secret storage)
|
||||
var derivedKey = key.slice(0, 16);
|
||||
var macPrefix = key.slice(16, 32);
|
||||
|
||||
// Get the initialization vector
|
||||
if (!iv) { iv = key.slice(32, 48); }
|
||||
|
||||
// Get the UUID random data
|
||||
if (!uuidRandom) { uuidRandom = key.slice(48, 64); }
|
||||
|
||||
// Get the address for this private key
|
||||
var address = (new SigningKey(privateKey)).address;
|
||||
|
||||
// Encrypt the private key
|
||||
var counter = new aes.Counter(iv);
|
||||
var aesCtr = new aes.ModeOfOperation.ctr(derivedKey, counter);
|
||||
var ciphertext = new Buffer(aesCtr.encrypt(privateKey));
|
||||
|
||||
// Compute the message authentication code, used to check the password
|
||||
var mac = utils.sha3(Buffer.concat([macPrefix, ciphertext]))
|
||||
|
||||
// See: https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition
|
||||
var data = {
|
||||
address: address,
|
||||
id: uuid.v4({random: uuidRandom}),
|
||||
version: 3,
|
||||
Crypto: {
|
||||
cipher: 'aes-128-ctr',
|
||||
cipherparams: {
|
||||
iv: iv.toString('hex')
|
||||
},
|
||||
ciphertext: ciphertext.toString('hex'),
|
||||
kdf: 'scrypt',
|
||||
kdfparams: {
|
||||
salt: salt.toString('hex'),
|
||||
n: N,
|
||||
dklen: 32,
|
||||
p: p,
|
||||
r: r
|
||||
},
|
||||
mac: mac.toString('hex')
|
||||
}
|
||||
};
|
||||
|
||||
if (progressCallback) { progressCallback(1); }
|
||||
resolve(JSON.stringify(data));
|
||||
|
||||
} else if (progressCallback) {
|
||||
return progressCallback(progress);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = secretStorage;
|
||||
@@ -1,38 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var elliptic = require('elliptic');
|
||||
|
||||
var utils = require('./utils.js');
|
||||
|
||||
var secp256k1 = new (elliptic.ec)('secp256k1');
|
||||
|
||||
|
||||
|
||||
function SigningKey(privateKey) {
|
||||
if (!(this instanceof SigningKey)) { throw new Error('missing new'); }
|
||||
|
||||
if (utils.isHexString(privateKey, 32)) {
|
||||
privateKey = utils.hexOrBuffer(privateKey);
|
||||
} else if (!Buffer.isBuffer(privateKey) || privateKey.length !== 32) {
|
||||
throw new Error('invalid private key');
|
||||
}
|
||||
utils.defineProperty(this, 'privateKey', '0x' + privateKey.toString('hex'))
|
||||
|
||||
var keyPair = secp256k1.keyFromPrivate(privateKey);
|
||||
var publicKey = (new Buffer(keyPair.getPublic(false, 'hex'), 'hex')).slice(1);
|
||||
|
||||
var address = utils.getAddress(utils.sha3(publicKey).slice(12).toString('hex'));
|
||||
utils.defineProperty(this, 'address', address)
|
||||
|
||||
utils.defineProperty(this, 'signDigest', function(digest) {
|
||||
return keyPair.sign(digest, {canonical: true});
|
||||
});
|
||||
}
|
||||
|
||||
utils.defineProperty(SigningKey, 'recover', function(digest, r, s, recoveryParam) {
|
||||
var publicKey = secp256k1.recoverPubKey(digest, {r: r, s: s}, recoveryParam);
|
||||
publicKey = (new Buffer(publicKey.encode('hex', false), 'hex')).slice(1);
|
||||
return utils.getAddress(utils.sha3(publicKey).slice(12).toString('hex'));
|
||||
});
|
||||
|
||||
module.exports = SigningKey;
|
||||
78
lib/units.js
78
lib/units.js
@@ -1,78 +0,0 @@
|
||||
var utils = require('./utils.js');
|
||||
|
||||
var zero = new utils.BN(0);
|
||||
var negative1 = new utils.BN(-1);
|
||||
var tenPower18 = new utils.BN('1000000000000000000');
|
||||
|
||||
function formatEther(wei, options) {
|
||||
|
||||
if (typeof(wei) === 'number') {
|
||||
// @TODO: Warn if truncation will occur?
|
||||
wei = new utils.BN(wei);
|
||||
} else if (utils.isHexString(wei)) {
|
||||
wei = new utils.BN(wei.substring(2), 16);
|
||||
}
|
||||
|
||||
if (!options) { options = {}; }
|
||||
|
||||
if (!(wei instanceof utils.BN)) { throw new Error('invalid wei'); }
|
||||
|
||||
var negative = wei.lt(zero);
|
||||
if (negative) { wei = wei.mul(negative1); }
|
||||
|
||||
var fraction = wei.mod(tenPower18).toString(10);
|
||||
while (fraction.length < 18) { fraction = '0' + fraction; }
|
||||
|
||||
if (!options.pad) {
|
||||
fraction = fraction.match(/^([0-9]*[1-9]|0)(0*)/)[1];
|
||||
}
|
||||
|
||||
var whole = wei.div(tenPower18).toString(10);
|
||||
|
||||
if (options.commify) {
|
||||
whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, ",")
|
||||
}
|
||||
|
||||
var value = whole + '.' + fraction;
|
||||
|
||||
if (negative) { value = '-' + value; }
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
function parseEther(ether) {
|
||||
if (typeof(ether) !== 'string' || !ether.match(/^-?[0-9.]+$/)) {
|
||||
throw new Error('invalid value');
|
||||
}
|
||||
|
||||
// Is it negative?
|
||||
var negative = (ether.substring(0, 1) === '-');
|
||||
if (negative) { ether = ether.substring(1); }
|
||||
|
||||
if (ether === '.') { throw new Error('invalid value'); }
|
||||
|
||||
// Split it into a whole and fractional part
|
||||
var comps = ether.split('.');
|
||||
if (comps.length > 2) { throw new Error('too many decimal points'); }
|
||||
|
||||
var whole = comps[0], fraction = comps[1];
|
||||
if (!whole) { whole = '0'; }
|
||||
if (!fraction) { fraction = '0'; }
|
||||
if (fraction.length > 18) { throw new Error('too many decimal places'); }
|
||||
|
||||
while (fraction.length < 18) { fraction += '0'; }
|
||||
|
||||
whole = new utils.BN(whole);
|
||||
fraction = new utils.BN(fraction);
|
||||
|
||||
var wei = (whole.mul(tenPower18)).add(fraction);
|
||||
|
||||
if (negative) { wei = wei.mul(negative1); }
|
||||
|
||||
return wei;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
formatEther: formatEther,
|
||||
parseEther: parseEther,
|
||||
}
|
||||
402
lib/utils.js
402
lib/utils.js
@@ -1,402 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var rlp = require('rlp');
|
||||
|
||||
var BN = require('../node_modules/elliptic/node_modules/bn.js/lib/bn.js');
|
||||
var hash = require('../node_modules/elliptic/node_modules/hash.js/lib/hash.js');
|
||||
|
||||
|
||||
// See: https://github.com/emn178/js-sha3
|
||||
var padding = [1, 256, 65536, 16777216];
|
||||
var HEX_CHARS = '0123456789abcdef'.split('');
|
||||
var SHIFT = [0, 8, 16, 24];
|
||||
var RC = [1, 0, 32898, 0, 32906, 2147483648, 2147516416, 2147483648, 32907, 0, 2147483649,
|
||||
0, 2147516545, 2147483648, 32777, 2147483648, 138, 0, 136, 0, 2147516425, 0,
|
||||
2147483658, 0, 2147516555, 0, 139, 2147483648, 32905, 2147483648, 32771,
|
||||
2147483648, 32770, 2147483648, 128, 2147483648, 32778, 0, 2147483658, 2147483648,
|
||||
2147516545, 2147483648, 32896, 2147483648, 2147483649, 0, 2147516424, 2147483648];
|
||||
|
||||
var blocks = [], s = [];
|
||||
|
||||
function keccak(message) {
|
||||
|
||||
var block, code, end = false, index = 0, start = 0, length = message.length,
|
||||
n, i, h, l, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9,
|
||||
b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17,
|
||||
b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32, b33,
|
||||
b34, b35, b36, b37, b38, b39, b40, b41, b42, b43, b44, b45, b46, b47, b48, b49;
|
||||
var blockCount = 34;
|
||||
var byteCount = blockCount * 4;
|
||||
|
||||
for (i = 0; i < 50; ++i) { s[i] = 0; }
|
||||
|
||||
block = 0;
|
||||
do {
|
||||
blocks[0] = block;
|
||||
for (i = 1; i < blockCount + 1; ++i) { blocks[i] = 0; }
|
||||
|
||||
for (i = start; index < length && i < byteCount; ++index) {
|
||||
blocks[i >> 2] |= message[index] << SHIFT[i++ & 3];
|
||||
}
|
||||
|
||||
start = i - byteCount;
|
||||
if (index == length) {
|
||||
blocks[i >> 2] |= padding[i & 3];
|
||||
++index;
|
||||
}
|
||||
|
||||
block = blocks[blockCount];
|
||||
if (index > length && i < byteCount) {
|
||||
blocks[blockCount - 1] |= 0x80000000;
|
||||
end = true;
|
||||
}
|
||||
|
||||
for (i = 0;i < blockCount;++i) { s[i] ^= blocks[i]; }
|
||||
|
||||
for (n = 0; n < 48; n += 2) {
|
||||
c0 = s[0] ^ s[10] ^ s[20] ^ s[30] ^ s[40];
|
||||
c1 = s[1] ^ s[11] ^ s[21] ^ s[31] ^ s[41];
|
||||
c2 = s[2] ^ s[12] ^ s[22] ^ s[32] ^ s[42];
|
||||
c3 = s[3] ^ s[13] ^ s[23] ^ s[33] ^ s[43];
|
||||
c4 = s[4] ^ s[14] ^ s[24] ^ s[34] ^ s[44];
|
||||
c5 = s[5] ^ s[15] ^ s[25] ^ s[35] ^ s[45];
|
||||
c6 = s[6] ^ s[16] ^ s[26] ^ s[36] ^ s[46];
|
||||
c7 = s[7] ^ s[17] ^ s[27] ^ s[37] ^ s[47];
|
||||
c8 = s[8] ^ s[18] ^ s[28] ^ s[38] ^ s[48];
|
||||
c9 = s[9] ^ s[19] ^ s[29] ^ s[39] ^ s[49];
|
||||
|
||||
h = c8 ^ ((c2 << 1) | (c3 >>> 31));
|
||||
l = c9 ^ ((c3 << 1) | (c2 >>> 31));
|
||||
s[0] ^= h; s[1] ^= l; s[10] ^= h; s[11] ^= l;
|
||||
s[20] ^= h; s[21] ^= l; s[30] ^= h; s[31] ^= l;
|
||||
s[40] ^= h; s[41] ^= l;
|
||||
h = c0 ^ ((c4 << 1) | (c5 >>> 31));
|
||||
l = c1 ^ ((c5 << 1) | (c4 >>> 31));
|
||||
s[2] ^= h; s[3] ^= l; s[12] ^= h; s[13] ^= l;
|
||||
s[22] ^= h; s[23] ^= l; s[32] ^= h; s[33] ^= l;
|
||||
s[42] ^= h; s[43] ^= l;
|
||||
h = c2 ^ ((c6 << 1) | (c7 >>> 31));
|
||||
l = c3 ^ ((c7 << 1) | (c6 >>> 31));
|
||||
s[4] ^= h; s[5] ^= l; s[14] ^= h; s[15] ^= l;
|
||||
s[24] ^= h; s[25] ^= l; s[34] ^= h; s[35] ^= l;
|
||||
s[44] ^= h; s[45] ^= l;
|
||||
h = c4 ^ ((c8 << 1) | (c9 >>> 31));
|
||||
l = c5 ^ ((c9 << 1) | (c8 >>> 31));
|
||||
s[6] ^= h; s[7] ^= l; s[16] ^= h; s[17] ^= l;
|
||||
s[26] ^= h; s[27] ^= l; s[36] ^= h; s[37] ^= l;
|
||||
s[46] ^= h; s[47] ^= l;
|
||||
h = c6 ^ ((c0 << 1) | (c1 >>> 31));
|
||||
l = c7 ^ ((c1 << 1) | (c0 >>> 31));
|
||||
s[8] ^= h; s[9] ^= l; s[18] ^= h; s[19] ^= l;
|
||||
s[28] ^= h; s[29] ^= l; s[38] ^= h; s[39] ^= l;
|
||||
s[48] ^= h; s[49] ^= l;
|
||||
|
||||
b0 = s[0]; b1 = s[1];
|
||||
b32 = (s[11] << 4) | (s[10] >>> 28);
|
||||
b33 = (s[10] << 4) | (s[11] >>> 28);
|
||||
b14 = (s[20] << 3) | (s[21] >>> 29);
|
||||
b15 = (s[21] << 3) | (s[20] >>> 29);
|
||||
b46 = (s[31] << 9) | (s[30] >>> 23);
|
||||
b47 = (s[30] << 9) | (s[31] >>> 23);
|
||||
b28 = (s[40] << 18) | (s[41] >>> 14);
|
||||
b29 = (s[41] << 18) | (s[40] >>> 14);
|
||||
b20 = (s[2] << 1) | (s[3] >>> 31);
|
||||
b21 = (s[3] << 1) | (s[2] >>> 31);
|
||||
b2 = (s[13] << 12) | (s[12] >>> 20);
|
||||
b3 = (s[12] << 12) | (s[13] >>> 20);
|
||||
b34 = (s[22] << 10) | (s[23] >>> 22);
|
||||
b35 = (s[23] << 10) | (s[22] >>> 22);
|
||||
b16 = (s[33] << 13) | (s[32] >>> 19);
|
||||
b17 = (s[32] << 13) | (s[33] >>> 19);
|
||||
b48 = (s[42] << 2) | (s[43] >>> 30);
|
||||
b49 = (s[43] << 2) | (s[42] >>> 30);
|
||||
b40 = (s[5] << 30) | (s[4] >>> 2);
|
||||
b41 = (s[4] << 30) | (s[5] >>> 2);
|
||||
b22 = (s[14] << 6) | (s[15] >>> 26);
|
||||
b23 = (s[15] << 6) | (s[14] >>> 26);
|
||||
b4 = (s[25] << 11) | (s[24] >>> 21);
|
||||
b5 = (s[24] << 11) | (s[25] >>> 21);
|
||||
b36 = (s[34] << 15) | (s[35] >>> 17);
|
||||
b37 = (s[35] << 15) | (s[34] >>> 17);
|
||||
b18 = (s[45] << 29) | (s[44] >>> 3);
|
||||
b19 = (s[44] << 29) | (s[45] >>> 3);
|
||||
b10 = (s[6] << 28) | (s[7] >>> 4);
|
||||
b11 = (s[7] << 28) | (s[6] >>> 4);
|
||||
b42 = (s[17] << 23) | (s[16] >>> 9);
|
||||
b43 = (s[16] << 23) | (s[17] >>> 9);
|
||||
b24 = (s[26] << 25) | (s[27] >>> 7);
|
||||
b25 = (s[27] << 25) | (s[26] >>> 7);
|
||||
b6 = (s[36] << 21) | (s[37] >>> 11);
|
||||
b7 = (s[37] << 21) | (s[36] >>> 11);
|
||||
b38 = (s[47] << 24) | (s[46] >>> 8);
|
||||
b39 = (s[46] << 24) | (s[47] >>> 8);
|
||||
b30 = (s[8] << 27) | (s[9] >>> 5);
|
||||
b31 = (s[9] << 27) | (s[8] >>> 5);
|
||||
b12 = (s[18] << 20) | (s[19] >>> 12);
|
||||
b13 = (s[19] << 20) | (s[18] >>> 12);
|
||||
b44 = (s[29] << 7) | (s[28] >>> 25);
|
||||
b45 = (s[28] << 7) | (s[29] >>> 25);
|
||||
b26 = (s[38] << 8) | (s[39] >>> 24);
|
||||
b27 = (s[39] << 8) | (s[38] >>> 24);
|
||||
b8 = (s[48] << 14) | (s[49] >>> 18);
|
||||
b9 = (s[49] << 14) | (s[48] >>> 18);
|
||||
|
||||
s[0] = b0 ^ (~b2 & b4); s[1] = b1 ^ (~b3 & b5);
|
||||
s[10] = b10 ^ (~b12 & b14); s[11] = b11 ^ (~b13 & b15);
|
||||
s[20] = b20 ^ (~b22 & b24); s[21] = b21 ^ (~b23 & b25);
|
||||
s[30] = b30 ^ (~b32 & b34); s[31] = b31 ^ (~b33 & b35);
|
||||
s[40] = b40 ^ (~b42 & b44); s[41] = b41 ^ (~b43 & b45);
|
||||
s[2] = b2 ^ (~b4 & b6); s[3] = b3 ^ (~b5 & b7);
|
||||
s[12] = b12 ^ (~b14 & b16); s[13] = b13 ^ (~b15 & b17);
|
||||
s[22] = b22 ^ (~b24 & b26); s[23] = b23 ^ (~b25 & b27);
|
||||
s[32] = b32 ^ (~b34 & b36); s[33] = b33 ^ (~b35 & b37);
|
||||
s[42] = b42 ^ (~b44 & b46); s[43] = b43 ^ (~b45 & b47);
|
||||
s[4] = b4 ^ (~b6 & b8); s[5] = b5 ^ (~b7 & b9);
|
||||
s[14] = b14 ^ (~b16 & b18); s[15] = b15 ^ (~b17 & b19);
|
||||
s[24] = b24 ^ (~b26 & b28); s[25] = b25 ^ (~b27 & b29);
|
||||
s[34] = b34 ^ (~b36 & b38); s[35] = b35 ^ (~b37 & b39);
|
||||
s[44] = b44 ^ (~b46 & b48); s[45] = b45 ^ (~b47 & b49);
|
||||
s[6] = b6 ^ (~b8 & b0); s[7] = b7 ^ (~b9 & b1);
|
||||
s[16] = b16 ^ (~b18 & b10); s[17] = b17 ^ (~b19 & b11);
|
||||
s[26] = b26 ^ (~b28 & b20); s[27] = b27 ^ (~b29 & b21);
|
||||
s[36] = b36 ^ (~b38 & b30); s[37] = b37 ^ (~b39 & b31);
|
||||
s[46] = b46 ^ (~b48 & b40); s[47] = b47 ^ (~b49 & b41);
|
||||
s[8] = b8 ^ (~b0 & b2); s[9] = b9 ^ (~b1 & b3);
|
||||
s[18] = b18 ^ (~b10 & b12); s[19] = b19 ^ (~b11 & b13);
|
||||
s[28] = b28 ^ (~b20 & b22); s[29] = b29 ^ (~b21 & b23);
|
||||
s[38] = b38 ^ (~b30 & b32); s[39] = b39 ^ (~b31 & b33);
|
||||
s[48] = b48 ^ (~b40 & b42); s[49] = b49 ^ (~b41 & b43);
|
||||
|
||||
s[0] ^= RC[n]; s[1] ^= RC[n + 1];
|
||||
}
|
||||
} while(!end);
|
||||
|
||||
var hex = '';
|
||||
for (i = 0, n = 8; i < n; ++i) {
|
||||
h = s[i];
|
||||
hex += HEX_CHARS[(h >> 4) & 0x0F] + HEX_CHARS[h & 0x0F] +
|
||||
HEX_CHARS[(h >> 12) & 0x0F] + HEX_CHARS[(h >> 8) & 0x0F] +
|
||||
HEX_CHARS[(h >> 20) & 0x0F] + HEX_CHARS[(h >> 16) & 0x0F] +
|
||||
HEX_CHARS[(h >> 28) & 0x0F] + HEX_CHARS[(h >> 24) & 0x0F];
|
||||
}
|
||||
|
||||
return hex;
|
||||
};
|
||||
|
||||
// Quick sanity check
|
||||
//if (keccak(new Buffer('ricmoo')) !== 'b05e424817fb90aa7a79e9da5c5f94070a316219c6ebb863a9ff7ca357dc9fa9') {
|
||||
// throw new Error('problem with sh3?!');
|
||||
//}
|
||||
|
||||
function sha256(data) {
|
||||
if (typeof(data) === 'string') {
|
||||
data = new Buffer(data, 'utf8');
|
||||
} else if (!Buffer.isBuffer(data)) {
|
||||
throw new Error('must be a sting');
|
||||
}
|
||||
|
||||
return (new Buffer(hash.sha256().update(data).digest('hex'), 'hex'));
|
||||
}
|
||||
|
||||
function sha3(data) {
|
||||
if (typeof(data) === 'string') {
|
||||
data = new Buffer(data, 'utf8');
|
||||
} else if (!Buffer.isBuffer(data)) {
|
||||
throw new Error('must be a sting');
|
||||
}
|
||||
|
||||
return new Buffer(keccak(data), 'hex');
|
||||
}
|
||||
|
||||
function defineProperty(object, name, value) {
|
||||
Object.defineProperty(object, name, {
|
||||
enumerable: true,
|
||||
value: value
|
||||
});
|
||||
}
|
||||
|
||||
function getChecksumAddress(address) {
|
||||
if (typeof(address) !== 'string' || !address.match(/^0x[0-9A-Fa-f]{40}$/)) {
|
||||
throw new Error('invalid address');
|
||||
}
|
||||
|
||||
address = address.substring(2).toLowerCase();
|
||||
var hashed = sha3(address);
|
||||
|
||||
address = address.split('');
|
||||
for (var i = 0; i < 40; i += 2) {
|
||||
if ((hashed[i >> 1] >> 4) >= 8) {
|
||||
address[i] = address[i].toUpperCase();
|
||||
}
|
||||
if ((hashed[i >> 1] & 0x0f) >= 8) {
|
||||
address[i + 1] = address[i + 1].toUpperCase();
|
||||
}
|
||||
}
|
||||
|
||||
return '0x' + address.join('');
|
||||
}
|
||||
|
||||
function getAddress(address) {
|
||||
var result = null;
|
||||
|
||||
if (typeof(address) !== 'string') { throw new Error('invalid address'); }
|
||||
|
||||
if (address.match(/^(0x)?[0-9a-fA-F]{40}$/)) {
|
||||
|
||||
// Missing the 0x prefix
|
||||
if (address.substring(0, 2) !== '0x') { address = '0x' + address; }
|
||||
|
||||
result = getChecksumAddress(address);
|
||||
|
||||
// It is a checksummed address with a bad checksum
|
||||
if (address.match(/([A-F].*[a-f])|([a-f].*[A-F])/) && result !== address) {
|
||||
throw new Error('invalid address checksum');
|
||||
}
|
||||
|
||||
// Maybe ICAP? (we only support direct mode)
|
||||
} else if (address.match(/^XE[0-9]{2}[0-9A-Za-z]{30,31}$/)) {
|
||||
|
||||
// It is an ICAP address with a bad checksum
|
||||
if (address.substring(2, 4) !== ibanChecksum(address)) {
|
||||
throw new Error('invalid address icap checksum');
|
||||
}
|
||||
|
||||
result = (new BN(address.substring(4), 36)).toString(16);
|
||||
while (result.length < 40) { result = '0' + result; }
|
||||
result = getChecksumAddress('0x' + result);
|
||||
|
||||
} else {
|
||||
throw new Error('invalid address');
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// See: https://en.wikipedia.org/wiki/International_Bank_Account_Number
|
||||
var ibanChecksum = (function() {
|
||||
|
||||
// Create lookup table
|
||||
var ibanLookup = {};
|
||||
for (var i = 0; i < 10; i++) { ibanLookup[String(i)] = String(i); }
|
||||
for (var i = 0; i < 26; i++) { ibanLookup[String.fromCharCode(65 + i)] = String(10 + i); }
|
||||
|
||||
// How many decimal digits can we process? (for 64-bit float, this is 15)
|
||||
var safeDigits = Math.floor(Math.log10(Number.MAX_SAFE_INTEGER));
|
||||
|
||||
return function(address) {
|
||||
address = address.toUpperCase();
|
||||
address = address.substring(4) + address.substring(0, 2) + '00';
|
||||
|
||||
var expanded = address.split('');
|
||||
for (var i = 0; i < expanded.length; i++) {
|
||||
expanded[i] = ibanLookup[expanded[i]];
|
||||
}
|
||||
expanded = expanded.join('');
|
||||
|
||||
// Javascript can handle integers safely up to 15 (decimal) digits
|
||||
while (expanded.length >= safeDigits){
|
||||
var block = expanded.substring(0, safeDigits);
|
||||
expanded = parseInt(block, 10) % 97 + expanded.substring(block.length);
|
||||
}
|
||||
|
||||
var checksum = String(98 - (parseInt(expanded, 10) % 97));
|
||||
while (checksum.length < 2) { checksum = '0' + checksum; }
|
||||
|
||||
return checksum;
|
||||
};
|
||||
})();
|
||||
|
||||
|
||||
function getIcapAddress(address) {
|
||||
address = getAddress(address).substring(2);
|
||||
var base36 = (new BN(address, 16)).toString(36).toUpperCase();
|
||||
while (base36.length < 30) { base36 = '0' + base36; }
|
||||
return 'XE' + ibanChecksum('XE00' + base36) + base36;
|
||||
}
|
||||
|
||||
// http://ethereum.stackexchange.com/questions/760/how-is-the-address-of-an-ethereum-contract-computed
|
||||
function getContractAddress(transaction) {
|
||||
return getAddress('0x' + sha3(rlp.encode([
|
||||
hexOrBuffer(getAddress(transaction.from)),
|
||||
hexOrBuffer(hexlify(transaction.nonce, 'nonce'))
|
||||
])).slice(12).toString('hex'));
|
||||
}
|
||||
|
||||
function cloneObject(object) {
|
||||
var clone = {};
|
||||
for (var key in object) { clone[key] = object[key]; }
|
||||
return clone;
|
||||
}
|
||||
|
||||
function stripZeros(buffer) {
|
||||
var i = 0;
|
||||
for (i = 0; i < buffer.length; i++) {
|
||||
if (buffer[i] !== 0) { break; }
|
||||
}
|
||||
return (i > 0) ? buffer.slice(i): buffer;
|
||||
}
|
||||
|
||||
function bnToBuffer(bn) {
|
||||
var hex = bn.toString(16);
|
||||
if (hex.length % 2) { hex = '0' + hex; }
|
||||
return stripZeros(new Buffer(hex, 'hex'));
|
||||
}
|
||||
|
||||
function isHexString(value, length) {
|
||||
if (typeof(value) !== 'string' || !value.match(/^0x[0-9A-Fa-f]*$/)) {
|
||||
return false
|
||||
}
|
||||
if (length && value.length !== 2 + 2 * length) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
function hexOrBuffer(value, name) {
|
||||
if (!Buffer.isBuffer(value)) {
|
||||
if (!isHexString(value)) {
|
||||
var error = new Error(name ? ('invalid ' + name) : 'invalid hex or buffer');
|
||||
error.reason = 'invalid hex string';
|
||||
error.value = value;
|
||||
throw error;
|
||||
}
|
||||
|
||||
value = value.substring(2);
|
||||
if (value.length % 2) { value = '0' + value; }
|
||||
value = new Buffer(value, 'hex');
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
function hexlify(value, name) {
|
||||
if (typeof(value) === 'number') {
|
||||
return '0x' + bnToBuffer(new BN(value)).toString('hex');
|
||||
} else if (value.mod || value.modulo) {
|
||||
return '0x' + bnToBuffer(value).toString('hex')
|
||||
} else {
|
||||
return '0x' + hexOrBuffer(value).toString('hex');
|
||||
}
|
||||
throw new Error('invalid value');
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
BN: BN,
|
||||
|
||||
defineProperty: defineProperty,
|
||||
|
||||
getAddress: getAddress,
|
||||
getIcapAddress: getIcapAddress,
|
||||
|
||||
getContractAddress: getContractAddress,
|
||||
|
||||
cloneObject: cloneObject,
|
||||
|
||||
bnToBuffer: bnToBuffer,
|
||||
isHexString: isHexString,
|
||||
hexOrBuffer: hexOrBuffer,
|
||||
hexlify: hexlify,
|
||||
stripZeros: stripZeros,
|
||||
|
||||
sha256: sha256,
|
||||
sha3: sha3,
|
||||
}
|
||||
283
lib/wallet.js
283
lib/wallet.js
@@ -1,283 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var rlp = require('rlp');
|
||||
|
||||
var Contract = require('./contract.js');
|
||||
var providers = require('./providers.js');
|
||||
var SigningKey = require('./signing-key.js');
|
||||
|
||||
var utils = require('./utils.js');
|
||||
|
||||
// This ensures we inject a setImmediate into the global space, which
|
||||
// dramatically improves the performance of the scrypt PBKDF.
|
||||
require('setimmediate');
|
||||
|
||||
var transactionFields = [
|
||||
{name: 'nonce', maxLength: 32, },
|
||||
{name: 'gasPrice', maxLength: 32, },
|
||||
{name: 'gasLimit', maxLength: 32, },
|
||||
{name: 'to', length: 20, },
|
||||
{name: 'value', maxLength: 32, },
|
||||
{name: 'data'},
|
||||
];
|
||||
|
||||
function Wallet(privateKey, provider) {
|
||||
if (!(this instanceof Wallet)) { throw new Error('missing new'); }
|
||||
|
||||
// Make sure we have a valid signing key
|
||||
var signingKey = privateKey;
|
||||
if (!(privateKey instanceof SigningKey)) {
|
||||
signingKey = new SigningKey(privateKey);
|
||||
}
|
||||
utils.defineProperty(this, 'privateKey', signingKey.privateKey);
|
||||
|
||||
Object.defineProperty(this, 'provider', {
|
||||
enumerable: true,
|
||||
get: function() { return provider; },
|
||||
set: function(value) {
|
||||
if (value !== null && !providers.isProvider(value)) {
|
||||
throw new Error('invalid provider');
|
||||
}
|
||||
provider = value;
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(this, '_provider', {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
if (!provider) { throw new Error('missing provider'); }
|
||||
return provider;
|
||||
},
|
||||
});
|
||||
|
||||
if (provider !== null) {
|
||||
|
||||
// If no provider was provided, check for metamask or ilk
|
||||
if (provider === undefined) {
|
||||
if (global.web3 && global.web3.currentProvider && global.web3.currentProvider.sendAsync) {
|
||||
this.provider = new providers.Web3Provider(global.web3.currentProvider);
|
||||
}
|
||||
|
||||
// An Ethereum RPC node
|
||||
} else if (typeof(provider) === 'string' && provider.match(/^https?:\/\//)) {
|
||||
this.provider = new providers.HttpProvider(provider);
|
||||
|
||||
} else {
|
||||
this.provider = provider;
|
||||
}
|
||||
}
|
||||
|
||||
utils.defineProperty(this, 'address', signingKey.address);
|
||||
|
||||
utils.defineProperty(this, 'sign', function(transaction) {
|
||||
var raw = [];
|
||||
transactionFields.forEach(function(fieldInfo) {
|
||||
var value = transaction[fieldInfo.name] || (new Buffer(0));
|
||||
value = utils.hexOrBuffer(utils.hexlify(value), fieldInfo.name);
|
||||
|
||||
// Fixed-width field
|
||||
if (fieldInfo.length && value.length !== fieldInfo.length && value.length > 0) {
|
||||
var error = new Error('invalid ' + fieldInfo.name);
|
||||
error.reason = 'wrong length';
|
||||
error.value = value;
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Variable-width (with a maximum)
|
||||
if (fieldInfo.maxLength) {
|
||||
value = utils.stripZeros(value);
|
||||
if (value.length > fieldInfo.maxLength) {
|
||||
var error = new Error('invalid ' + fieldInfo.name);
|
||||
error.reason = 'too long';
|
||||
error.value = value;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
raw.push(value);
|
||||
});
|
||||
|
||||
var digest = utils.sha3(rlp.encode(raw));
|
||||
|
||||
var signature = signingKey.signDigest(digest);
|
||||
var s = signature.s;
|
||||
var v = signature.recoveryParam;
|
||||
|
||||
raw.push(new Buffer([27 + v]));
|
||||
raw.push(utils.bnToBuffer(signature.r));
|
||||
raw.push(utils.bnToBuffer(s));
|
||||
|
||||
return ('0x' + rlp.encode(raw).toString('hex'));
|
||||
});
|
||||
}
|
||||
|
||||
utils.defineProperty(Wallet, 'parseTransaction', function(rawTransaction) {
|
||||
rawTransaction = utils.hexOrBuffer(rawTransaction, 'rawTransaction');
|
||||
var signedTransaction = rlp.decode(rawTransaction);
|
||||
|
||||
var raw = [];
|
||||
|
||||
var transaction = {};
|
||||
transactionFields.forEach(function(fieldInfo, index) {
|
||||
transaction[fieldInfo.name] = signedTransaction[index];
|
||||
raw.push(signedTransaction[index]);
|
||||
});
|
||||
|
||||
if (transaction.to) {
|
||||
if (transaction.to.length === 0) {
|
||||
delete transaction.to;
|
||||
} else {
|
||||
transaction.to = utils.getAddress('0x' + transaction.to.toString('hex'));
|
||||
}
|
||||
}
|
||||
|
||||
['gasPrice', 'gasLimit', 'nonce', 'value'].forEach(function(name) {
|
||||
if (!transaction[name]) { return; }
|
||||
if (transaction[name].length === 0) {
|
||||
transaction[name] = new utils.BN(0);
|
||||
} else {
|
||||
transaction[name] = new utils.BN(transaction[name].toString('hex'), 16);
|
||||
}
|
||||
});
|
||||
|
||||
/* @TODO: Maybe? In the future, all nonces stored as numbers? (obviously, major version change)
|
||||
if (transaction.nonce) {
|
||||
transaction.nonce = transaction.nonce.toNumber()
|
||||
}
|
||||
*/
|
||||
|
||||
if (signedTransaction.length > 6 && signedTransaction[6].length === 1 &&
|
||||
signedTransaction[7].length >= 1 && signedTransaction[7].length <= 32 &&
|
||||
signedTransaction[8].length >= 1 && signedTransaction[7].length <= 32) {
|
||||
|
||||
transaction.v = signedTransaction[6][0];
|
||||
transaction.r = signedTransaction[7];
|
||||
transaction.s = signedTransaction[8];
|
||||
|
||||
var digest = utils.sha3(rlp.encode(raw));
|
||||
try {
|
||||
transaction.from = SigningKey.recover(digest, transaction.r, transaction.s, transaction.v - 27);
|
||||
} catch (error) { }
|
||||
}
|
||||
|
||||
return transaction;
|
||||
});
|
||||
|
||||
utils.defineProperty(Wallet.prototype, 'getBalance', function(blockNumber) {
|
||||
var provider = this._provider;
|
||||
|
||||
var self = this;
|
||||
return new Promise(function(resolve, reject) {
|
||||
provider.getBalance(self.address, blockNumber).then(function(balance) {
|
||||
resolve(balance);
|
||||
}, function(error) {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
utils.defineProperty(Wallet.prototype, 'getTransactionCount', function(blockNumber) {
|
||||
var provider = this._provider;
|
||||
|
||||
var self = this;
|
||||
return new Promise(function(resolve, reject) {
|
||||
provider.getTransactionCount(self.address, blockNumber).then(function(transactionCount) {
|
||||
resolve(transactionCount);
|
||||
}, function(error) {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
utils.defineProperty(Wallet.prototype, 'estimateGas', function(transaction) {
|
||||
var provider = this._provider;
|
||||
|
||||
transaction = utils.cloneObject(transaction);
|
||||
if (transaction.from == null) { transaction.from = this.address; }
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
provider.estimateGas(transaction).then(function(gasEstimate) {
|
||||
resolve(gasEstimate);
|
||||
}, function(error) {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
utils.defineProperty(Wallet.prototype, 'sendTransaction', function(transaction) {
|
||||
var gasLimit = transaction.gasLimit;
|
||||
if (gasLimit == null) { gasLimit = 3000000; }
|
||||
|
||||
var self = this;
|
||||
|
||||
var provider = this._provider;
|
||||
|
||||
var gasPrice = new Promise(function(resolve, reject) {
|
||||
if (transaction.gasPrice) {
|
||||
return resolve(transaction.gasPrice);
|
||||
}
|
||||
provider.getGasPrice().then(function(gasPrice) {
|
||||
resolve(gasPrice);
|
||||
}, function(error) {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
|
||||
var nonce = new Promise(function(resolve, reject) {
|
||||
if (transaction.nonce) {
|
||||
return resolve(transaction.nonce);
|
||||
}
|
||||
provider.getTransactionCount(self.address, 'pending').then(function(transactionCount) {
|
||||
resolve(transactionCount);
|
||||
}, function(error) {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
Promise.all([gasPrice, nonce]).then(function(results) {
|
||||
var signedTransaction = self.sign({
|
||||
to: transaction.to,
|
||||
gasLimit: gasLimit,
|
||||
gasPrice: results[0],
|
||||
nonce: results[1],
|
||||
value: transaction.value
|
||||
});
|
||||
|
||||
provider.sendTransaction(signedTransaction).then(function(txid) {
|
||||
resolve(txid);
|
||||
}, function(error) {
|
||||
reject(error);
|
||||
});
|
||||
|
||||
}, function(error) {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
utils.defineProperty(Wallet.prototype, 'send', function(address, amountWei, options) {
|
||||
address = utils.getAddress(address);
|
||||
if (utils.BN.isBN(amountWei)) {
|
||||
amountWei = '0x' + utils.bnToBuffer(amountWei).toString('hex');
|
||||
}
|
||||
if (!utils.isHexString(amountWei)) { throw new Error('invalid amountWei'); }
|
||||
|
||||
if (!options) { options = {}; }
|
||||
|
||||
return this.sendTransaction({
|
||||
to: address,
|
||||
gasLimit: options.gasLimit,
|
||||
gasPrice: options.gasPrice,
|
||||
nonce: options.nonce,
|
||||
value: amountWei,
|
||||
});
|
||||
});
|
||||
|
||||
utils.defineProperty(Wallet.prototype, 'getContract', function(address, abi) {
|
||||
return new Contract(this, address, new Contract.Interface(abi));
|
||||
});
|
||||
|
||||
utils.defineProperty(Wallet, '_Contract', Contract);
|
||||
|
||||
module.exports = Wallet;
|
||||
7603
package-lock.json
generated
Normal file
7603
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
74
package.json
74
package.json
@@ -1,48 +1,68 @@
|
||||
{
|
||||
"name": "ethers-wallet",
|
||||
"version": "1.0.4",
|
||||
"name": "ethers",
|
||||
"version": "4.0.29",
|
||||
"description": "Ethereum wallet library.",
|
||||
"main": "index.js",
|
||||
"main": "./index.js",
|
||||
"types": "./index.d.ts",
|
||||
"scripts": {
|
||||
"test": "./node_modules/.bin/nodeunit tests/index.js",
|
||||
"version": "grunt dist"
|
||||
"build": "npm run dist-version && tsc -p ./tsconfig.json",
|
||||
"auto-build": "npm run build -- -w",
|
||||
"dist": "npm run dist-version && npm run build && gulp default minified shims && npm run dist-types",
|
||||
"dist-test": "gulp default-test minified-test shims",
|
||||
"dist-bip39": "gulp bip39-es bip39-fr bip39-it bip39-ja bip39-ko bip39-zh",
|
||||
"dist-types": "dts-bundle --name ethers --main ./index.d.ts --out ./dist/ethers.types.txt",
|
||||
"dist-version": "node -e \"let v = require('./package.json').version; require('fs').writeFileSync('./src.ts/_version.ts', 'export const version = \\\"' + v +'\\\";\\n')\"",
|
||||
"eslint": "eslint index.js contracts/*.js providers/*.js utils/*.js wallet/*.js wordlists/*.js",
|
||||
"test": "if [ \"$RUN_PHANTOMJS\" = \"1\" ]; then npm run-script test-phantomjs; else npm run-script test-node; fi",
|
||||
"test-node": "npm run dist-test && mocha --no-colors --reporter tests/reporter tests/test-*.js",
|
||||
"test-phantomjs": "npm run dist-test && gulp tests && phantomjs --web-security=false ./node_modules/mocha-phantomjs-core/mocha-phantomjs-core.js ./tests/test.html ./tests/reporter.js",
|
||||
"version": "npm dist"
|
||||
},
|
||||
"dependencies": {
|
||||
"aes-js": "2.0.0",
|
||||
"elliptic": "6.3.1",
|
||||
"inherits": "2.0.1",
|
||||
"pbkdf2": "3.0.4",
|
||||
"rlp": "2.0.0",
|
||||
"@types/node": "^10.3.2",
|
||||
"aes-js": "3.0.0",
|
||||
"bn.js": "^4.4.0",
|
||||
"elliptic": "6.3.3",
|
||||
"hash.js": "1.1.3",
|
||||
"js-sha3": "0.5.7",
|
||||
"scrypt-js": "2.0.4",
|
||||
"setimmediate": "1.0.4",
|
||||
"scrypt-js": "2.0.3",
|
||||
"uuid": "2.0.1",
|
||||
"xmlhttprequest": "1.8.0"
|
||||
},
|
||||
"browser": {
|
||||
"./lib/random-bytes.js": "./lib/browser-random-bytes.js",
|
||||
"xmlhttprequest": "./lib/browser-xmlhttprequest.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ethereumjs-abi": "0.6.2",
|
||||
"ethereumjs-tx": "1.1.1",
|
||||
"ethereumjs-util": "4.3.0",
|
||||
"grunt": "^0.4.5",
|
||||
"grunt-browserify": "^5.0.0",
|
||||
"grunt-contrib-uglify": "^1.0.1",
|
||||
"nodeunit": "0.9.1",
|
||||
"web3": "0.15.3",
|
||||
|
||||
"ethereumjs-vm": "1.4.0",
|
||||
"solc": "0.3.5"
|
||||
"browserify": "^16.2.3",
|
||||
"browserify-zlib": "^0.2.0",
|
||||
"dts-bundle": "^0.7.3",
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-plugin-promise": "^3.8.0",
|
||||
"ethereumjs-tx": "^1.3.5",
|
||||
"ethereumjs-util": "^5.2.0",
|
||||
"gulp": "^4.0.0",
|
||||
"gulp-cli": "^2.0.1",
|
||||
"gulp-sourcemaps": "^2.6.4",
|
||||
"gulp-typescript": "^5.0.0-alpha.1",
|
||||
"gulp-uglify": "^3.0.0",
|
||||
"mocha": "^5.2.0",
|
||||
"mocha-phantomjs-core": "2.1.2",
|
||||
"solc": "0.4.20",
|
||||
"tsify": "^4.0.0",
|
||||
"tslint": "^5.10.0",
|
||||
"typescript": "^2.9.1",
|
||||
"vinyl-buffer": "^1.0.1",
|
||||
"vinyl-source-stream": "^2.0.0",
|
||||
"web3-providers-http": "1.0.0-beta.35"
|
||||
},
|
||||
"browser": "./dist/ethers.min.js",
|
||||
"keywords": [
|
||||
"ethereum",
|
||||
"library",
|
||||
"wallet"
|
||||
],
|
||||
"author": "Richard Moore <me@ricmoo.com>",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/ethers-io/ethers-wallet.git"
|
||||
"url": "git://github.com/ethers-io/ethers.js.git"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
|
||||
108
providers/abstract-provider.d.ts
vendored
Normal file
108
providers/abstract-provider.d.ts
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
import { BigNumber } from '../utils/bignumber';
|
||||
import { Arrayish } from '../utils/bytes';
|
||||
import { BigNumberish } from '../utils/bignumber';
|
||||
import { Network } from '../utils/networks';
|
||||
import { OnceBlockable } from '../utils/web';
|
||||
import { Transaction } from '../utils/transaction';
|
||||
export interface Block {
|
||||
hash: string;
|
||||
parentHash: string;
|
||||
number: number;
|
||||
timestamp: number;
|
||||
nonce: string;
|
||||
difficulty: number;
|
||||
gasLimit: BigNumber;
|
||||
gasUsed: BigNumber;
|
||||
miner: string;
|
||||
extraData: string;
|
||||
transactions: Array<string>;
|
||||
}
|
||||
export declare type BlockTag = string | number;
|
||||
export declare type Filter = {
|
||||
fromBlock?: BlockTag;
|
||||
toBlock?: BlockTag;
|
||||
address?: string;
|
||||
topics?: Array<string | Array<string>>;
|
||||
};
|
||||
export declare type FilterByBlock = {
|
||||
blockHash?: string;
|
||||
address?: string;
|
||||
topics?: Array<string | Array<string>>;
|
||||
};
|
||||
export interface Log {
|
||||
blockNumber?: number;
|
||||
blockHash?: string;
|
||||
transactionIndex?: number;
|
||||
removed?: boolean;
|
||||
transactionLogIndex?: number;
|
||||
address: string;
|
||||
data: string;
|
||||
topics: Array<string>;
|
||||
transactionHash?: string;
|
||||
logIndex?: number;
|
||||
}
|
||||
export interface TransactionReceipt {
|
||||
to?: string;
|
||||
from?: string;
|
||||
contractAddress?: string;
|
||||
transactionIndex?: number;
|
||||
root?: string;
|
||||
gasUsed?: BigNumber;
|
||||
logsBloom?: string;
|
||||
blockHash?: string;
|
||||
transactionHash?: string;
|
||||
logs?: Array<Log>;
|
||||
blockNumber?: number;
|
||||
confirmations?: number;
|
||||
cumulativeGasUsed?: BigNumber;
|
||||
byzantium: boolean;
|
||||
status?: number;
|
||||
}
|
||||
export declare type TransactionRequest = {
|
||||
to?: string | Promise<string>;
|
||||
from?: string | Promise<string>;
|
||||
nonce?: BigNumberish | Promise<BigNumberish>;
|
||||
gasLimit?: BigNumberish | Promise<BigNumberish>;
|
||||
gasPrice?: BigNumberish | Promise<BigNumberish>;
|
||||
data?: Arrayish | Promise<Arrayish>;
|
||||
value?: BigNumberish | Promise<BigNumberish>;
|
||||
chainId?: number | Promise<number>;
|
||||
};
|
||||
export interface TransactionResponse extends Transaction {
|
||||
blockNumber?: number;
|
||||
blockHash?: string;
|
||||
timestamp?: number;
|
||||
confirmations: number;
|
||||
from: string;
|
||||
raw?: string;
|
||||
wait: (confirmations?: number) => Promise<TransactionReceipt>;
|
||||
}
|
||||
export declare type EventType = string | Array<string> | Filter;
|
||||
export declare type Listener = (...args: Array<any>) => void;
|
||||
export declare abstract class Provider implements OnceBlockable {
|
||||
abstract getNetwork(): Promise<Network>;
|
||||
abstract getBlockNumber(): Promise<number>;
|
||||
abstract getGasPrice(): Promise<BigNumber>;
|
||||
abstract getBalance(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<BigNumber>;
|
||||
abstract getTransactionCount(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<number>;
|
||||
abstract getCode(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
|
||||
abstract getStorageAt(addressOrName: string | Promise<string>, position: BigNumberish | Promise<BigNumberish>, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
|
||||
abstract sendTransaction(signedTransaction: string | Promise<string>): Promise<TransactionResponse>;
|
||||
abstract call(transaction: TransactionRequest, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
|
||||
abstract estimateGas(transaction: TransactionRequest): Promise<BigNumber>;
|
||||
abstract getBlock(blockHashOrBlockTag: BlockTag | string | Promise<BlockTag | string>, includeTransactions?: boolean): Promise<Block>;
|
||||
abstract getTransaction(transactionHash: string): Promise<TransactionResponse>;
|
||||
abstract getTransactionReceipt(transactionHash: string): Promise<TransactionReceipt>;
|
||||
abstract getLogs(filter: Filter | FilterByBlock): Promise<Array<Log>>;
|
||||
abstract resolveName(name: string | Promise<string>): Promise<string>;
|
||||
abstract lookupAddress(address: string | Promise<string>): Promise<string>;
|
||||
abstract on(eventName: EventType, listener: Listener): Provider;
|
||||
abstract once(eventName: EventType, listener: Listener): Provider;
|
||||
abstract listenerCount(eventName?: EventType): number;
|
||||
abstract listeners(eventName: EventType): Array<Listener>;
|
||||
abstract removeAllListeners(eventName: EventType): Provider;
|
||||
abstract removeListener(eventName: EventType, listener: Listener): Provider;
|
||||
abstract waitForTransaction(transactionHash: string, timeout?: number): Promise<TransactionReceipt>;
|
||||
constructor();
|
||||
static isProvider(value: any): value is Provider;
|
||||
}
|
||||
18
providers/abstract-provider.js
Normal file
18
providers/abstract-provider.js
Normal file
@@ -0,0 +1,18 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var properties_1 = require("../utils/properties");
|
||||
;
|
||||
;
|
||||
///////////////////////////////
|
||||
// Exported Abstracts
|
||||
var Provider = /** @class */ (function () {
|
||||
function Provider() {
|
||||
properties_1.setType(this, 'Provider');
|
||||
}
|
||||
Provider.isProvider = function (value) {
|
||||
return properties_1.isType(value, 'Provider');
|
||||
};
|
||||
return Provider;
|
||||
}());
|
||||
exports.Provider = Provider;
|
||||
//defineReadOnly(Signer, 'inherits', inheritable(Abstract));
|
||||
75
providers/base-provider.d.ts
vendored
Normal file
75
providers/base-provider.d.ts
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
import { BigNumber } from '../utils/bignumber';
|
||||
import { Provider } from './abstract-provider';
|
||||
import { Block, BlockTag, EventType, Filter, FilterByBlock, Listener, Log, TransactionReceipt, TransactionRequest, TransactionResponse } from './abstract-provider';
|
||||
import { BigNumberish } from '../utils/bignumber';
|
||||
import { Transaction } from '../utils/transaction';
|
||||
import { Network, Networkish } from '../utils/networks';
|
||||
export declare class BaseProvider extends Provider {
|
||||
private _network;
|
||||
private _events;
|
||||
protected _emitted: {
|
||||
[eventName: string]: number | 'pending';
|
||||
};
|
||||
private _pollingInterval;
|
||||
private _poller;
|
||||
private _lastBlockNumber;
|
||||
private _balances;
|
||||
private _fastBlockNumber;
|
||||
private _fastBlockNumberPromise;
|
||||
private _fastQueryDate;
|
||||
/**
|
||||
* ready
|
||||
*
|
||||
* A Promise<Network> that resolves only once the provider is ready.
|
||||
*
|
||||
* Sub-classes that call the super with a network without a chainId
|
||||
* MUST set this. Standard named networks have a known chainId.
|
||||
*
|
||||
*/
|
||||
protected ready: Promise<Network>;
|
||||
constructor(network: Networkish | Promise<Network>);
|
||||
private _doPoll;
|
||||
resetEventsBlock(blockNumber: number): void;
|
||||
readonly network: Network;
|
||||
getNetwork(): Promise<Network>;
|
||||
readonly blockNumber: number;
|
||||
polling: boolean;
|
||||
pollingInterval: number;
|
||||
_getFastBlockNumber(): Promise<number>;
|
||||
_setFastBlockNumber(blockNumber: number): void;
|
||||
waitForTransaction(transactionHash: string, confirmations?: number): Promise<TransactionReceipt>;
|
||||
getBlockNumber(): Promise<number>;
|
||||
getGasPrice(): Promise<BigNumber>;
|
||||
getBalance(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<BigNumber>;
|
||||
getTransactionCount(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<number>;
|
||||
getCode(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
|
||||
getStorageAt(addressOrName: string | Promise<string>, position: BigNumberish | Promise<BigNumberish>, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
|
||||
sendTransaction(signedTransaction: string | Promise<string>): Promise<TransactionResponse>;
|
||||
_wrapTransaction(tx: Transaction, hash?: string): TransactionResponse;
|
||||
call(transaction: TransactionRequest, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
|
||||
estimateGas(transaction: TransactionRequest): Promise<BigNumber>;
|
||||
getBlock(blockHashOrBlockTag: BlockTag | string | Promise<BlockTag | string>, includeTransactions?: boolean): Promise<Block>;
|
||||
getTransaction(transactionHash: string): Promise<TransactionResponse>;
|
||||
getTransactionReceipt(transactionHash: string): Promise<TransactionReceipt>;
|
||||
getLogs(filter: Filter | FilterByBlock): Promise<Array<Log>>;
|
||||
getEtherPrice(): Promise<number>;
|
||||
_getAddress(addressOrName: string): Promise<string>;
|
||||
private _resolveNames;
|
||||
private _getResolver;
|
||||
resolveName(name: string | Promise<string>): Promise<string>;
|
||||
lookupAddress(address: string | Promise<string>): Promise<string>;
|
||||
static checkTransactionResponse(transaction: any): TransactionResponse;
|
||||
doPoll(): void;
|
||||
perform(method: string, params: any): Promise<any>;
|
||||
protected _startPending(): void;
|
||||
protected _stopPending(): void;
|
||||
private _addEventListener;
|
||||
on(eventName: EventType, listener: Listener): Provider;
|
||||
once(eventName: EventType, listener: Listener): Provider;
|
||||
addEventListener(eventName: EventType, listener: Listener): Provider;
|
||||
emit(eventName: EventType, ...args: Array<any>): boolean;
|
||||
listenerCount(eventName?: EventType): number;
|
||||
listeners(eventName: EventType): Array<Listener>;
|
||||
removeAllListeners(eventName?: EventType): Provider;
|
||||
removeListener(eventName: EventType, listener: Listener): Provider;
|
||||
}
|
||||
1234
providers/base-provider.js
Normal file
1234
providers/base-provider.js
Normal file
File diff suppressed because it is too large
Load Diff
10
providers/etherscan-provider.d.ts
vendored
Normal file
10
providers/etherscan-provider.d.ts
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
import { BaseProvider } from './base-provider';
|
||||
import { BlockTag, TransactionResponse } from './abstract-provider';
|
||||
import { Networkish } from '../utils/networks';
|
||||
export declare class EtherscanProvider extends BaseProvider {
|
||||
readonly baseUrl: string;
|
||||
readonly apiKey: string;
|
||||
constructor(network?: Networkish, apiKey?: string);
|
||||
perform(method: string, params: any): Promise<any>;
|
||||
getHistory(addressOrName: string | Promise<string>, startBlock?: BlockTag, endBlock?: BlockTag): Promise<Array<TransactionResponse>>;
|
||||
}
|
||||
347
providers/etherscan-provider.js
Normal file
347
providers/etherscan-provider.js
Normal file
@@ -0,0 +1,347 @@
|
||||
"use strict";
|
||||
var __extends = (this && this.__extends) || (function () {
|
||||
var extendStatics = Object.setPrototypeOf ||
|
||||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
||||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
|
||||
return function (d, b) {
|
||||
extendStatics(d, b);
|
||||
function __() { this.constructor = d; }
|
||||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
||||
};
|
||||
})();
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
||||
result["default"] = mod;
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var base_provider_1 = require("./base-provider");
|
||||
var bytes_1 = require("../utils/bytes");
|
||||
var properties_1 = require("../utils/properties");
|
||||
var web_1 = require("../utils/web");
|
||||
var errors = __importStar(require("../errors"));
|
||||
///////////////////////////////
|
||||
// The transaction has already been sanitized by the calls in Provider
|
||||
function getTransactionString(transaction) {
|
||||
var result = [];
|
||||
for (var key in transaction) {
|
||||
if (transaction[key] == null) {
|
||||
continue;
|
||||
}
|
||||
var value = bytes_1.hexlify(transaction[key]);
|
||||
if ({ gasLimit: true, gasPrice: true, nonce: true, value: true }[key]) {
|
||||
value = bytes_1.hexStripZeros(value);
|
||||
}
|
||||
result.push(key + '=' + value);
|
||||
}
|
||||
return result.join('&');
|
||||
}
|
||||
function getResult(result) {
|
||||
// getLogs, getHistory have weird success responses
|
||||
if (result.status == 0 && (result.message === 'No records found' || result.message === 'No transactions found')) {
|
||||
return result.result;
|
||||
}
|
||||
if (result.status != 1 || result.message != 'OK') {
|
||||
// @TODO: not any
|
||||
var error = new Error('invalid response');
|
||||
error.result = JSON.stringify(result);
|
||||
throw error;
|
||||
}
|
||||
return result.result;
|
||||
}
|
||||
function getJsonResult(result) {
|
||||
if (result.jsonrpc != '2.0') {
|
||||
// @TODO: not any
|
||||
var error = new Error('invalid response');
|
||||
error.result = JSON.stringify(result);
|
||||
throw error;
|
||||
}
|
||||
if (result.error) {
|
||||
// @TODO: not any
|
||||
var error = new Error(result.error.message || 'unknown error');
|
||||
if (result.error.code) {
|
||||
error.code = result.error.code;
|
||||
}
|
||||
if (result.error.data) {
|
||||
error.data = result.error.data;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
return result.result;
|
||||
}
|
||||
// The blockTag was normalized as a string by the Provider pre-perform operations
|
||||
function checkLogTag(blockTag) {
|
||||
if (blockTag === 'pending') {
|
||||
throw new Error('pending not supported');
|
||||
}
|
||||
if (blockTag === 'latest') {
|
||||
return blockTag;
|
||||
}
|
||||
return parseInt(blockTag.substring(2), 16);
|
||||
}
|
||||
var EtherscanProvider = /** @class */ (function (_super) {
|
||||
__extends(EtherscanProvider, _super);
|
||||
function EtherscanProvider(network, apiKey) {
|
||||
var _this = _super.call(this, network) || this;
|
||||
errors.checkNew(_this, EtherscanProvider);
|
||||
var name = 'invalid';
|
||||
if (_this.network) {
|
||||
name = _this.network.name;
|
||||
}
|
||||
var baseUrl = null;
|
||||
switch (name) {
|
||||
case 'homestead':
|
||||
baseUrl = 'https://api.etherscan.io';
|
||||
break;
|
||||
case 'ropsten':
|
||||
baseUrl = 'https://api-ropsten.etherscan.io';
|
||||
break;
|
||||
case 'rinkeby':
|
||||
baseUrl = 'https://api-rinkeby.etherscan.io';
|
||||
break;
|
||||
case 'kovan':
|
||||
baseUrl = 'https://api-kovan.etherscan.io';
|
||||
break;
|
||||
case 'goerli':
|
||||
baseUrl = 'https://api-goerli.etherscan.io';
|
||||
break;
|
||||
default:
|
||||
throw new Error('unsupported network');
|
||||
}
|
||||
properties_1.defineReadOnly(_this, 'baseUrl', baseUrl);
|
||||
properties_1.defineReadOnly(_this, 'apiKey', apiKey);
|
||||
return _this;
|
||||
}
|
||||
EtherscanProvider.prototype.perform = function (method, params) {
|
||||
var _this = this;
|
||||
var url = this.baseUrl;
|
||||
var apiKey = '';
|
||||
if (this.apiKey) {
|
||||
apiKey += '&apikey=' + this.apiKey;
|
||||
}
|
||||
var get = function (url, procFunc) {
|
||||
return web_1.fetchJson(url, null, procFunc || getJsonResult).then(function (result) {
|
||||
_this.emit('debug', {
|
||||
action: 'perform',
|
||||
request: url,
|
||||
response: result,
|
||||
provider: _this
|
||||
});
|
||||
return result;
|
||||
});
|
||||
};
|
||||
switch (method) {
|
||||
case 'getBlockNumber':
|
||||
url += '/api?module=proxy&action=eth_blockNumber' + apiKey;
|
||||
return get(url);
|
||||
case 'getGasPrice':
|
||||
url += '/api?module=proxy&action=eth_gasPrice' + apiKey;
|
||||
return get(url);
|
||||
case 'getBalance':
|
||||
// Returns base-10 result
|
||||
url += '/api?module=account&action=balance&address=' + params.address;
|
||||
url += '&tag=' + params.blockTag + apiKey;
|
||||
return get(url, getResult);
|
||||
case 'getTransactionCount':
|
||||
url += '/api?module=proxy&action=eth_getTransactionCount&address=' + params.address;
|
||||
url += '&tag=' + params.blockTag + apiKey;
|
||||
return get(url);
|
||||
case 'getCode':
|
||||
url += '/api?module=proxy&action=eth_getCode&address=' + params.address;
|
||||
url += '&tag=' + params.blockTag + apiKey;
|
||||
return get(url, getJsonResult);
|
||||
case 'getStorageAt':
|
||||
url += '/api?module=proxy&action=eth_getStorageAt&address=' + params.address;
|
||||
url += '&position=' + params.position;
|
||||
url += '&tag=' + params.blockTag + apiKey;
|
||||
return get(url, getJsonResult);
|
||||
case 'sendTransaction':
|
||||
url += '/api?module=proxy&action=eth_sendRawTransaction&hex=' + params.signedTransaction;
|
||||
url += apiKey;
|
||||
return get(url).catch(function (error) {
|
||||
if (error.responseText) {
|
||||
// "Insufficient funds. The account you tried to send transaction from does not have enough funds. Required 21464000000000 and got: 0"
|
||||
if (error.responseText.toLowerCase().indexOf('insufficient funds') >= 0) {
|
||||
errors.throwError('insufficient funds', errors.INSUFFICIENT_FUNDS, {});
|
||||
}
|
||||
// "Transaction with the same hash was already imported."
|
||||
if (error.responseText.indexOf('same hash was already imported') >= 0) {
|
||||
errors.throwError('nonce has already been used', errors.NONCE_EXPIRED, {});
|
||||
}
|
||||
// "Transaction gas price is too low. There is another transaction with same nonce in the queue. Try increasing the gas price or incrementing the nonce."
|
||||
if (error.responseText.indexOf('another transaction with same nonce') >= 0) {
|
||||
errors.throwError('replacement fee too low', errors.REPLACEMENT_UNDERPRICED, {});
|
||||
}
|
||||
}
|
||||
throw error;
|
||||
});
|
||||
case 'getBlock':
|
||||
if (params.blockTag) {
|
||||
url += '/api?module=proxy&action=eth_getBlockByNumber&tag=' + params.blockTag;
|
||||
if (params.includeTransactions) {
|
||||
url += '&boolean=true';
|
||||
}
|
||||
else {
|
||||
url += '&boolean=false';
|
||||
}
|
||||
url += apiKey;
|
||||
return get(url);
|
||||
}
|
||||
throw new Error('getBlock by blockHash not implmeneted');
|
||||
case 'getTransaction':
|
||||
url += '/api?module=proxy&action=eth_getTransactionByHash&txhash=' + params.transactionHash;
|
||||
url += apiKey;
|
||||
return get(url);
|
||||
case 'getTransactionReceipt':
|
||||
url += '/api?module=proxy&action=eth_getTransactionReceipt&txhash=' + params.transactionHash;
|
||||
url += apiKey;
|
||||
return get(url);
|
||||
case 'call': {
|
||||
var transaction = getTransactionString(params.transaction);
|
||||
if (transaction) {
|
||||
transaction = '&' + transaction;
|
||||
}
|
||||
url += '/api?module=proxy&action=eth_call' + transaction;
|
||||
//url += '&tag=' + params.blockTag + apiKey;
|
||||
if (params.blockTag !== 'latest') {
|
||||
throw new Error('EtherscanProvider does not support blockTag for call');
|
||||
}
|
||||
url += apiKey;
|
||||
return get(url);
|
||||
}
|
||||
case 'estimateGas': {
|
||||
var transaction = getTransactionString(params.transaction);
|
||||
if (transaction) {
|
||||
transaction = '&' + transaction;
|
||||
}
|
||||
url += '/api?module=proxy&action=eth_estimateGas&' + transaction;
|
||||
url += apiKey;
|
||||
return get(url);
|
||||
}
|
||||
case 'getLogs':
|
||||
url += '/api?module=logs&action=getLogs';
|
||||
try {
|
||||
if (params.filter.fromBlock) {
|
||||
url += '&fromBlock=' + checkLogTag(params.filter.fromBlock);
|
||||
}
|
||||
if (params.filter.toBlock) {
|
||||
url += '&toBlock=' + checkLogTag(params.filter.toBlock);
|
||||
}
|
||||
if (params.filter.blockHash) {
|
||||
try {
|
||||
errors.throwError("Etherscan does not support blockHash filters", errors.UNSUPPORTED_OPERATION, {
|
||||
operation: "getLogs(blockHash)"
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
}
|
||||
if (params.filter.address) {
|
||||
url += '&address=' + params.filter.address;
|
||||
}
|
||||
// @TODO: We can handle slightly more complicated logs using the logs API
|
||||
if (params.filter.topics && params.filter.topics.length > 0) {
|
||||
if (params.filter.topics.length > 1) {
|
||||
throw new Error('unsupported topic format');
|
||||
}
|
||||
var topic0 = params.filter.topics[0];
|
||||
if (typeof (topic0) !== 'string' || topic0.length !== 66) {
|
||||
throw new Error('unsupported topic0 format');
|
||||
}
|
||||
url += '&topic0=' + topic0;
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
url += apiKey;
|
||||
var self = this;
|
||||
return get(url, getResult).then(function (logs) {
|
||||
var txs = {};
|
||||
var seq = Promise.resolve();
|
||||
logs.forEach(function (log) {
|
||||
seq = seq.then(function () {
|
||||
if (log.blockHash != null) {
|
||||
return null;
|
||||
}
|
||||
log.blockHash = txs[log.transactionHash];
|
||||
if (log.blockHash == null) {
|
||||
return self.getTransaction(log.transactionHash).then(function (tx) {
|
||||
txs[log.transactionHash] = tx.blockHash;
|
||||
log.blockHash = tx.blockHash;
|
||||
return null;
|
||||
});
|
||||
}
|
||||
return null;
|
||||
});
|
||||
});
|
||||
return seq.then(function () {
|
||||
return logs;
|
||||
});
|
||||
});
|
||||
case 'getEtherPrice':
|
||||
if (this.network.name !== 'homestead') {
|
||||
return Promise.resolve(0.0);
|
||||
}
|
||||
url += '/api?module=stats&action=ethprice';
|
||||
url += apiKey;
|
||||
return get(url, getResult).then(function (result) {
|
||||
return parseFloat(result.ethusd);
|
||||
});
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return _super.prototype.perform.call(this, method, params);
|
||||
};
|
||||
// @TODO: Allow startBlock and endBlock to be Promises
|
||||
EtherscanProvider.prototype.getHistory = function (addressOrName, startBlock, endBlock) {
|
||||
var _this = this;
|
||||
var url = this.baseUrl;
|
||||
var apiKey = '';
|
||||
if (this.apiKey) {
|
||||
apiKey += '&apikey=' + this.apiKey;
|
||||
}
|
||||
if (startBlock == null) {
|
||||
startBlock = 0;
|
||||
}
|
||||
if (endBlock == null) {
|
||||
endBlock = 99999999;
|
||||
}
|
||||
return this.resolveName(addressOrName).then(function (address) {
|
||||
url += '/api?module=account&action=txlist&address=' + address;
|
||||
url += '&startblock=' + startBlock;
|
||||
url += '&endblock=' + endBlock;
|
||||
url += '&sort=asc' + apiKey;
|
||||
return web_1.fetchJson(url, null, getResult).then(function (result) {
|
||||
_this.emit('debug', {
|
||||
action: 'getHistory',
|
||||
request: url,
|
||||
response: result,
|
||||
provider: _this
|
||||
});
|
||||
var output = [];
|
||||
result.forEach(function (tx) {
|
||||
['contractAddress', 'to'].forEach(function (key) {
|
||||
if (tx[key] == '') {
|
||||
delete tx[key];
|
||||
}
|
||||
});
|
||||
if (tx.creates == null && tx.contractAddress != null) {
|
||||
tx.creates = tx.contractAddress;
|
||||
}
|
||||
var item = base_provider_1.BaseProvider.checkTransactionResponse(tx);
|
||||
if (tx.timeStamp) {
|
||||
item.timestamp = parseInt(tx.timeStamp);
|
||||
}
|
||||
output.push(item);
|
||||
});
|
||||
return output;
|
||||
});
|
||||
});
|
||||
};
|
||||
return EtherscanProvider;
|
||||
}(base_provider_1.BaseProvider));
|
||||
exports.EtherscanProvider = EtherscanProvider;
|
||||
9
providers/fallback-provider.d.ts
vendored
Normal file
9
providers/fallback-provider.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import { BaseProvider } from './base-provider';
|
||||
export declare class FallbackProvider extends BaseProvider {
|
||||
private _providers;
|
||||
constructor(providers: Array<BaseProvider>);
|
||||
readonly providers: Array<BaseProvider>;
|
||||
perform(method: string, params: {
|
||||
[name: string]: any;
|
||||
}): any;
|
||||
}
|
||||
111
providers/fallback-provider.js
Normal file
111
providers/fallback-provider.js
Normal file
@@ -0,0 +1,111 @@
|
||||
'use strict';
|
||||
var __extends = (this && this.__extends) || (function () {
|
||||
var extendStatics = Object.setPrototypeOf ||
|
||||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
||||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
|
||||
return function (d, b) {
|
||||
extendStatics(d, b);
|
||||
function __() { this.constructor = d; }
|
||||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
||||
};
|
||||
})();
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
||||
result["default"] = mod;
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var base_provider_1 = require("./base-provider");
|
||||
var errors = __importStar(require("../errors"));
|
||||
// Returns:
|
||||
// - true is all networks match
|
||||
// - false if any network is null
|
||||
// - throws if any 2 networks do not match
|
||||
function checkNetworks(networks) {
|
||||
var result = true;
|
||||
var check = null;
|
||||
networks.forEach(function (network) {
|
||||
// Null
|
||||
if (network == null) {
|
||||
result = false;
|
||||
return;
|
||||
}
|
||||
// Have nothing to compre to yet
|
||||
if (check == null) {
|
||||
check = network;
|
||||
return;
|
||||
}
|
||||
// Matches!
|
||||
if (check.name === network.name &&
|
||||
check.chainId === network.chainId &&
|
||||
((check.ensAddress === network.ensAddress) ||
|
||||
(check.ensAddress == null && network.ensAddress == null))) {
|
||||
return;
|
||||
}
|
||||
errors.throwError('provider mismatch', errors.INVALID_ARGUMENT, { arg: 'networks', value: networks });
|
||||
});
|
||||
return result;
|
||||
}
|
||||
var FallbackProvider = /** @class */ (function (_super) {
|
||||
__extends(FallbackProvider, _super);
|
||||
function FallbackProvider(providers) {
|
||||
var _this = this;
|
||||
if (providers.length === 0) {
|
||||
throw new Error('no providers');
|
||||
}
|
||||
// All networks are ready, we can know the network for certain
|
||||
var ready = checkNetworks(providers.map(function (p) { return p.network; }));
|
||||
if (ready) {
|
||||
_this = _super.call(this, providers[0].network) || this;
|
||||
}
|
||||
else {
|
||||
// The network won't be known until all child providers know
|
||||
var ready_1 = Promise.all(providers.map(function (p) { return p.getNetwork(); })).then(function (networks) {
|
||||
if (!checkNetworks(networks)) {
|
||||
errors.throwError('getNetwork returned null', errors.UNKNOWN_ERROR, {});
|
||||
}
|
||||
return networks[0];
|
||||
});
|
||||
_this = _super.call(this, ready_1) || this;
|
||||
}
|
||||
errors.checkNew(_this, FallbackProvider);
|
||||
// Preserve a copy, so we don't get mutated
|
||||
_this._providers = providers.slice(0);
|
||||
return _this;
|
||||
}
|
||||
Object.defineProperty(FallbackProvider.prototype, "providers", {
|
||||
get: function () {
|
||||
// Return a copy, so we don't get mutated
|
||||
return this._providers.slice(0);
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
FallbackProvider.prototype.perform = function (method, params) {
|
||||
// Creates a copy of the providers array
|
||||
var providers = this.providers;
|
||||
return new Promise(function (resolve, reject) {
|
||||
var firstError = null;
|
||||
function next() {
|
||||
if (!providers.length) {
|
||||
reject(firstError);
|
||||
return;
|
||||
}
|
||||
var provider = providers.shift();
|
||||
provider.perform(method, params).then(function (result) {
|
||||
return resolve(result);
|
||||
}).catch(function (error) {
|
||||
if (!firstError) {
|
||||
firstError = error;
|
||||
}
|
||||
setTimeout(next, 0);
|
||||
});
|
||||
}
|
||||
next();
|
||||
});
|
||||
};
|
||||
return FallbackProvider;
|
||||
}(base_provider_1.BaseProvider));
|
||||
exports.FallbackProvider = FallbackProvider;
|
||||
11
providers/index.d.ts
vendored
Normal file
11
providers/index.d.ts
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Provider } from './abstract-provider';
|
||||
import { BaseProvider } from './base-provider';
|
||||
import { EtherscanProvider } from './etherscan-provider';
|
||||
import { FallbackProvider } from './fallback-provider';
|
||||
import { IpcProvider } from './ipc-provider';
|
||||
import { InfuraProvider } from './infura-provider';
|
||||
import { JsonRpcProvider, JsonRpcSigner } from './json-rpc-provider';
|
||||
import { Web3Provider } from './web3-provider';
|
||||
import { Block, BlockTag, EventType, Filter, Log, Listener, TransactionReceipt, TransactionRequest, TransactionResponse } from './abstract-provider';
|
||||
import { AsyncSendable } from './web3-provider';
|
||||
export { Provider, BaseProvider, FallbackProvider, EtherscanProvider, InfuraProvider, JsonRpcProvider, Web3Provider, IpcProvider, JsonRpcSigner, Block, BlockTag, EventType, Filter, Log, Listener, TransactionReceipt, TransactionRequest, TransactionResponse, AsyncSendable };
|
||||
19
providers/index.js
Normal file
19
providers/index.js
Normal file
@@ -0,0 +1,19 @@
|
||||
'use strict';
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var abstract_provider_1 = require("./abstract-provider");
|
||||
exports.Provider = abstract_provider_1.Provider;
|
||||
var base_provider_1 = require("./base-provider");
|
||||
exports.BaseProvider = base_provider_1.BaseProvider;
|
||||
var etherscan_provider_1 = require("./etherscan-provider");
|
||||
exports.EtherscanProvider = etherscan_provider_1.EtherscanProvider;
|
||||
var fallback_provider_1 = require("./fallback-provider");
|
||||
exports.FallbackProvider = fallback_provider_1.FallbackProvider;
|
||||
var ipc_provider_1 = require("./ipc-provider");
|
||||
exports.IpcProvider = ipc_provider_1.IpcProvider;
|
||||
var infura_provider_1 = require("./infura-provider");
|
||||
exports.InfuraProvider = infura_provider_1.InfuraProvider;
|
||||
var json_rpc_provider_1 = require("./json-rpc-provider");
|
||||
exports.JsonRpcProvider = json_rpc_provider_1.JsonRpcProvider;
|
||||
exports.JsonRpcSigner = json_rpc_provider_1.JsonRpcSigner;
|
||||
var web3_provider_1 = require("./web3-provider");
|
||||
exports.Web3Provider = web3_provider_1.Web3Provider;
|
||||
10
providers/infura-provider.d.ts
vendored
Normal file
10
providers/infura-provider.d.ts
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
import { JsonRpcProvider, JsonRpcSigner } from './json-rpc-provider';
|
||||
import { Networkish } from '../utils/networks';
|
||||
export declare class InfuraProvider extends JsonRpcProvider {
|
||||
readonly apiAccessToken: string;
|
||||
readonly projectId: string;
|
||||
constructor(network?: Networkish, projectId?: string);
|
||||
protected _startPending(): void;
|
||||
getSigner(address?: string): JsonRpcSigner;
|
||||
listAccounts(): Promise<Array<string>>;
|
||||
}
|
||||
84
providers/infura-provider.js
Normal file
84
providers/infura-provider.js
Normal file
@@ -0,0 +1,84 @@
|
||||
'use strict';
|
||||
var __extends = (this && this.__extends) || (function () {
|
||||
var extendStatics = Object.setPrototypeOf ||
|
||||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
||||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
|
||||
return function (d, b) {
|
||||
extendStatics(d, b);
|
||||
function __() { this.constructor = d; }
|
||||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
||||
};
|
||||
})();
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
||||
result["default"] = mod;
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var json_rpc_provider_1 = require("./json-rpc-provider");
|
||||
var bytes_1 = require("../utils/bytes");
|
||||
var networks_1 = require("../utils/networks");
|
||||
var properties_1 = require("../utils/properties");
|
||||
var errors = __importStar(require("../errors"));
|
||||
var defaultProjectId = "7d0d81d0919f4f05b9ab6634be01ee73";
|
||||
var InfuraProvider = /** @class */ (function (_super) {
|
||||
__extends(InfuraProvider, _super);
|
||||
function InfuraProvider(network, projectId) {
|
||||
var _this = this;
|
||||
var standard = networks_1.getNetwork((network == null) ? 'homestead' : network);
|
||||
if (projectId == null) {
|
||||
projectId = defaultProjectId;
|
||||
}
|
||||
var host = null;
|
||||
switch (standard.name) {
|
||||
case 'homestead':
|
||||
host = 'mainnet.infura.io';
|
||||
break;
|
||||
case 'ropsten':
|
||||
host = 'ropsten.infura.io';
|
||||
break;
|
||||
case 'rinkeby':
|
||||
host = 'rinkeby.infura.io';
|
||||
break;
|
||||
case 'goerli':
|
||||
host = 'goerli.infura.io';
|
||||
break;
|
||||
case 'kovan':
|
||||
host = 'kovan.infura.io';
|
||||
break;
|
||||
default:
|
||||
errors.throwError('unsupported network', errors.INVALID_ARGUMENT, {
|
||||
argument: "network",
|
||||
value: network
|
||||
});
|
||||
}
|
||||
// New-style Project ID
|
||||
if (bytes_1.isHexString("0x" + projectId, 16)) {
|
||||
_this = _super.call(this, 'https://' + host + '/v3/' + projectId, standard) || this;
|
||||
properties_1.defineReadOnly(_this, 'apiAccessToken', null);
|
||||
properties_1.defineReadOnly(_this, 'projectId', projectId);
|
||||
// Legacy API Access Token
|
||||
}
|
||||
else {
|
||||
errors.warn("The legacy INFURA apiAccesToken API is deprecated; please upgrade to a Project ID instead (see INFURA dshboard; https://infura.io)");
|
||||
_this = _super.call(this, 'https://' + host + '/' + projectId, standard) || this;
|
||||
properties_1.defineReadOnly(_this, 'apiAccessToken', projectId);
|
||||
properties_1.defineReadOnly(_this, 'projectId', null);
|
||||
}
|
||||
errors.checkNew(_this, InfuraProvider);
|
||||
return _this;
|
||||
}
|
||||
InfuraProvider.prototype._startPending = function () {
|
||||
errors.warn('WARNING: INFURA does not support pending filters');
|
||||
};
|
||||
InfuraProvider.prototype.getSigner = function (address) {
|
||||
return errors.throwError('INFURA does not support signing', errors.UNSUPPORTED_OPERATION, { operation: 'getSigner' });
|
||||
};
|
||||
InfuraProvider.prototype.listAccounts = function () {
|
||||
return Promise.resolve([]);
|
||||
};
|
||||
return InfuraProvider;
|
||||
}(json_rpc_provider_1.JsonRpcProvider));
|
||||
exports.InfuraProvider = InfuraProvider;
|
||||
7
providers/ipc-provider.d.ts
vendored
Normal file
7
providers/ipc-provider.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
import { JsonRpcProvider } from './json-rpc-provider';
|
||||
import { Networkish } from '../utils/networks';
|
||||
export declare class IpcProvider extends JsonRpcProvider {
|
||||
readonly path: string;
|
||||
constructor(path: string, network?: Networkish);
|
||||
send(method: string, params: any): Promise<any>;
|
||||
}
|
||||
81
providers/ipc-provider.js
Normal file
81
providers/ipc-provider.js
Normal file
@@ -0,0 +1,81 @@
|
||||
"use strict";
|
||||
var __extends = (this && this.__extends) || (function () {
|
||||
var extendStatics = Object.setPrototypeOf ||
|
||||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
||||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
|
||||
return function (d, b) {
|
||||
extendStatics(d, b);
|
||||
function __() { this.constructor = d; }
|
||||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
||||
};
|
||||
})();
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
||||
result["default"] = mod;
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var net_1 = __importDefault(require("net"));
|
||||
var json_rpc_provider_1 = require("./json-rpc-provider");
|
||||
var properties_1 = require("../utils/properties");
|
||||
var errors = __importStar(require("../errors"));
|
||||
var IpcProvider = /** @class */ (function (_super) {
|
||||
__extends(IpcProvider, _super);
|
||||
function IpcProvider(path, network) {
|
||||
var _this = this;
|
||||
if (path == null) {
|
||||
errors.throwError('missing path', errors.MISSING_ARGUMENT, {
|
||||
argument: 'path'
|
||||
});
|
||||
}
|
||||
_this = _super.call(this, 'ipc://' + path, network) || this;
|
||||
errors.checkNew(_this, IpcProvider);
|
||||
properties_1.defineReadOnly(_this, 'path', path);
|
||||
return _this;
|
||||
}
|
||||
// @TODO: Create a connection to the IPC path and use filters instead of polling for block
|
||||
IpcProvider.prototype.send = function (method, params) {
|
||||
// This method is very simple right now. We create a new socket
|
||||
// connection each time, which may be slower, but the main
|
||||
// advantage we are aiming for now is security. This simplifies
|
||||
// multiplexing requests (since we do not need to multiplex).
|
||||
var _this = this;
|
||||
var payload = JSON.stringify({
|
||||
method: method,
|
||||
params: params,
|
||||
id: 42,
|
||||
jsonrpc: "2.0"
|
||||
});
|
||||
return new Promise(function (resolve, reject) {
|
||||
var response = Buffer.alloc(0);
|
||||
var stream = net_1.default.connect(_this.path);
|
||||
stream.on('data', function (data) {
|
||||
response = Buffer.concat([response, data]);
|
||||
});
|
||||
stream.on("end", function () {
|
||||
try {
|
||||
resolve(JSON.parse(response.toString('utf8')).result);
|
||||
// @TODO: Better pull apart the error
|
||||
stream.destroy();
|
||||
}
|
||||
catch (error) {
|
||||
reject(error);
|
||||
stream.destroy();
|
||||
}
|
||||
});
|
||||
stream.on('error', function (error) {
|
||||
reject(error);
|
||||
stream.destroy();
|
||||
});
|
||||
stream.write(payload);
|
||||
stream.end();
|
||||
});
|
||||
};
|
||||
return IpcProvider;
|
||||
}(json_rpc_provider_1.JsonRpcProvider));
|
||||
exports.IpcProvider = IpcProvider;
|
||||
36
providers/json-rpc-provider.d.ts
vendored
Normal file
36
providers/json-rpc-provider.d.ts
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
import { BaseProvider } from './base-provider';
|
||||
import { Signer } from '../abstract-signer';
|
||||
import { BigNumber } from '../utils/bignumber';
|
||||
import { Arrayish } from '../utils/bytes';
|
||||
import { Networkish } from '../utils/networks';
|
||||
import { ConnectionInfo } from '../utils/web';
|
||||
import { BlockTag, TransactionRequest, TransactionResponse } from '../providers/abstract-provider';
|
||||
export declare class JsonRpcSigner extends Signer {
|
||||
readonly provider: JsonRpcProvider;
|
||||
private _index;
|
||||
private _address;
|
||||
constructor(constructorGuard: any, provider: JsonRpcProvider, addressOrIndex?: string | number);
|
||||
getAddress(): Promise<string>;
|
||||
getBalance(blockTag?: BlockTag): Promise<BigNumber>;
|
||||
getTransactionCount(blockTag?: BlockTag): Promise<number>;
|
||||
sendUncheckedTransaction(transaction: TransactionRequest): Promise<string>;
|
||||
sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse>;
|
||||
signMessage(message: Arrayish | string): Promise<string>;
|
||||
unlock(password: string): Promise<boolean>;
|
||||
}
|
||||
export declare class JsonRpcProvider extends BaseProvider {
|
||||
readonly connection: ConnectionInfo;
|
||||
private _pendingFilter;
|
||||
constructor(url?: ConnectionInfo | string, network?: Networkish);
|
||||
getSigner(addressOrIndex?: string | number): JsonRpcSigner;
|
||||
listAccounts(): Promise<Array<string>>;
|
||||
send(method: string, params: any): Promise<any>;
|
||||
perform(method: string, params: any): Promise<any>;
|
||||
protected _startPending(): void;
|
||||
protected _stopPending(): void;
|
||||
static hexlifyTransaction(transaction: TransactionRequest, allowExtra?: {
|
||||
[key: string]: boolean;
|
||||
}): {
|
||||
[key: string]: string;
|
||||
};
|
||||
}
|
||||
398
providers/json-rpc-provider.js
Normal file
398
providers/json-rpc-provider.js
Normal file
@@ -0,0 +1,398 @@
|
||||
'use strict';
|
||||
var __extends = (this && this.__extends) || (function () {
|
||||
var extendStatics = Object.setPrototypeOf ||
|
||||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
||||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
|
||||
return function (d, b) {
|
||||
extendStatics(d, b);
|
||||
function __() { this.constructor = d; }
|
||||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
||||
};
|
||||
})();
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
||||
result["default"] = mod;
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
// See: https://github.com/ethereum/wiki/wiki/JSON-RPC
|
||||
var base_provider_1 = require("./base-provider");
|
||||
var abstract_signer_1 = require("../abstract-signer");
|
||||
var errors = __importStar(require("../errors"));
|
||||
var address_1 = require("../utils/address");
|
||||
var bytes_1 = require("../utils/bytes");
|
||||
var networks_1 = require("../utils/networks");
|
||||
var properties_1 = require("../utils/properties");
|
||||
var utf8_1 = require("../utils/utf8");
|
||||
var web_1 = require("../utils/web");
|
||||
function timer(timeout) {
|
||||
return new Promise(function (resolve) {
|
||||
setTimeout(function () {
|
||||
resolve();
|
||||
}, timeout);
|
||||
});
|
||||
}
|
||||
function getResult(payload) {
|
||||
if (payload.error) {
|
||||
// @TODO: not any
|
||||
var error = new Error(payload.error.message);
|
||||
error.code = payload.error.code;
|
||||
error.data = payload.error.data;
|
||||
throw error;
|
||||
}
|
||||
return payload.result;
|
||||
}
|
||||
function getLowerCase(value) {
|
||||
if (value) {
|
||||
return value.toLowerCase();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
var _constructorGuard = {};
|
||||
// Some environments (Trust Wallet and company) use a global map
|
||||
// to track JSON-RPC ID, so we try to keep IDs unique across all
|
||||
// connections. See #489.
|
||||
var _nextId = 42;
|
||||
var JsonRpcSigner = /** @class */ (function (_super) {
|
||||
__extends(JsonRpcSigner, _super);
|
||||
function JsonRpcSigner(constructorGuard, provider, addressOrIndex) {
|
||||
var _this = _super.call(this) || this;
|
||||
errors.checkNew(_this, JsonRpcSigner);
|
||||
if (constructorGuard !== _constructorGuard) {
|
||||
throw new Error('do not call the JsonRpcSigner constructor directly; use provider.getSigner');
|
||||
}
|
||||
properties_1.defineReadOnly(_this, 'provider', provider);
|
||||
// Statically attach to a given address
|
||||
if (addressOrIndex) {
|
||||
if (typeof (addressOrIndex) === 'string') {
|
||||
properties_1.defineReadOnly(_this, '_address', address_1.getAddress(addressOrIndex));
|
||||
}
|
||||
else if (typeof (addressOrIndex) === 'number') {
|
||||
properties_1.defineReadOnly(_this, '_index', addressOrIndex);
|
||||
}
|
||||
else {
|
||||
errors.throwError('invalid address or index', errors.INVALID_ARGUMENT, { argument: 'addressOrIndex', value: addressOrIndex });
|
||||
}
|
||||
}
|
||||
else {
|
||||
properties_1.defineReadOnly(_this, '_index', 0);
|
||||
}
|
||||
return _this;
|
||||
}
|
||||
JsonRpcSigner.prototype.getAddress = function () {
|
||||
var _this = this;
|
||||
if (this._address) {
|
||||
return Promise.resolve(this._address);
|
||||
}
|
||||
return this.provider.send('eth_accounts', []).then(function (accounts) {
|
||||
if (accounts.length <= _this._index) {
|
||||
errors.throwError('unknown account #' + _this._index, errors.UNSUPPORTED_OPERATION, { operation: 'getAddress' });
|
||||
}
|
||||
_this._address = address_1.getAddress(accounts[_this._index]);
|
||||
return _this._address;
|
||||
});
|
||||
};
|
||||
JsonRpcSigner.prototype.getBalance = function (blockTag) {
|
||||
return this.provider.getBalance(this.getAddress(), blockTag);
|
||||
};
|
||||
JsonRpcSigner.prototype.getTransactionCount = function (blockTag) {
|
||||
return this.provider.getTransactionCount(this.getAddress(), blockTag);
|
||||
};
|
||||
JsonRpcSigner.prototype.sendUncheckedTransaction = function (transaction) {
|
||||
var _this = this;
|
||||
transaction = properties_1.shallowCopy(transaction);
|
||||
var fromAddress = this.getAddress().then(function (address) {
|
||||
if (address) {
|
||||
address = address.toLowerCase();
|
||||
}
|
||||
return address;
|
||||
});
|
||||
// The JSON-RPC for eth_sendTransaction uses 90000 gas; if the user
|
||||
// wishes to use this, it is easy to specify explicitly, otherwise
|
||||
// we look it up for them.
|
||||
if (transaction.gasLimit == null) {
|
||||
var estimate = properties_1.shallowCopy(transaction);
|
||||
estimate.from = fromAddress;
|
||||
transaction.gasLimit = this.provider.estimateGas(estimate);
|
||||
}
|
||||
return Promise.all([
|
||||
properties_1.resolveProperties(transaction),
|
||||
fromAddress
|
||||
]).then(function (results) {
|
||||
var tx = results[0];
|
||||
var hexTx = JsonRpcProvider.hexlifyTransaction(tx);
|
||||
hexTx.from = results[1];
|
||||
return _this.provider.send('eth_sendTransaction', [hexTx]).then(function (hash) {
|
||||
return hash;
|
||||
}, function (error) {
|
||||
if (error.responseText) {
|
||||
// See: JsonRpcProvider.sendTransaction (@TODO: Expose a ._throwError??)
|
||||
if (error.responseText.indexOf('insufficient funds') >= 0) {
|
||||
errors.throwError('insufficient funds', errors.INSUFFICIENT_FUNDS, {
|
||||
transaction: tx
|
||||
});
|
||||
}
|
||||
if (error.responseText.indexOf('nonce too low') >= 0) {
|
||||
errors.throwError('nonce has already been used', errors.NONCE_EXPIRED, {
|
||||
transaction: tx
|
||||
});
|
||||
}
|
||||
if (error.responseText.indexOf('replacement transaction underpriced') >= 0) {
|
||||
errors.throwError('replacement fee too low', errors.REPLACEMENT_UNDERPRICED, {
|
||||
transaction: tx
|
||||
});
|
||||
}
|
||||
}
|
||||
throw error;
|
||||
});
|
||||
});
|
||||
};
|
||||
JsonRpcSigner.prototype.sendTransaction = function (transaction) {
|
||||
var _this = this;
|
||||
return this.sendUncheckedTransaction(transaction).then(function (hash) {
|
||||
return web_1.poll(function () {
|
||||
return _this.provider.getTransaction(hash).then(function (tx) {
|
||||
if (tx === null) {
|
||||
return undefined;
|
||||
}
|
||||
return _this.provider._wrapTransaction(tx, hash);
|
||||
});
|
||||
}, { fastRetry: 250, onceBlock: _this.provider }).catch(function (error) {
|
||||
error.transactionHash = hash;
|
||||
throw error;
|
||||
});
|
||||
});
|
||||
};
|
||||
JsonRpcSigner.prototype.signMessage = function (message) {
|
||||
var _this = this;
|
||||
var data = ((typeof (message) === 'string') ? utf8_1.toUtf8Bytes(message) : message);
|
||||
return this.getAddress().then(function (address) {
|
||||
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign
|
||||
return _this.provider.send('eth_sign', [address.toLowerCase(), bytes_1.hexlify(data)]);
|
||||
});
|
||||
};
|
||||
JsonRpcSigner.prototype.unlock = function (password) {
|
||||
var provider = this.provider;
|
||||
return this.getAddress().then(function (address) {
|
||||
return provider.send('personal_unlockAccount', [address.toLowerCase(), password, null]);
|
||||
});
|
||||
};
|
||||
return JsonRpcSigner;
|
||||
}(abstract_signer_1.Signer));
|
||||
exports.JsonRpcSigner = JsonRpcSigner;
|
||||
var allowedTransactionKeys = {
|
||||
chainId: true, data: true, gasLimit: true, gasPrice: true, nonce: true, to: true, value: true
|
||||
};
|
||||
var JsonRpcProvider = /** @class */ (function (_super) {
|
||||
__extends(JsonRpcProvider, _super);
|
||||
function JsonRpcProvider(url, network) {
|
||||
var _this = this;
|
||||
// One parameter, but it is a network name, so swap it with the URL
|
||||
if (typeof (url) === 'string') {
|
||||
if (network === null && networks_1.getNetwork(url)) {
|
||||
network = url;
|
||||
url = null;
|
||||
}
|
||||
}
|
||||
if (network) {
|
||||
// The network has been specified explicitly, we can use it
|
||||
_this = _super.call(this, network) || this;
|
||||
}
|
||||
else {
|
||||
// The network is unknown, query the JSON-RPC for it
|
||||
var ready = new Promise(function (resolve, reject) {
|
||||
setTimeout(function () {
|
||||
_this.send('net_version', []).then(function (result) {
|
||||
return resolve(networks_1.getNetwork(parseInt(result)));
|
||||
}).catch(function (error) {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
});
|
||||
_this = _super.call(this, ready) || this;
|
||||
}
|
||||
errors.checkNew(_this, JsonRpcProvider);
|
||||
// Default URL
|
||||
if (!url) {
|
||||
url = 'http://localhost:8545';
|
||||
}
|
||||
if (typeof (url) === 'string') {
|
||||
_this.connection = {
|
||||
url: url
|
||||
};
|
||||
}
|
||||
else {
|
||||
_this.connection = url;
|
||||
}
|
||||
return _this;
|
||||
}
|
||||
JsonRpcProvider.prototype.getSigner = function (addressOrIndex) {
|
||||
return new JsonRpcSigner(_constructorGuard, this, addressOrIndex);
|
||||
};
|
||||
JsonRpcProvider.prototype.listAccounts = function () {
|
||||
return this.send('eth_accounts', []).then(function (accounts) {
|
||||
return accounts.map(function (a) { return address_1.getAddress(a); });
|
||||
});
|
||||
};
|
||||
JsonRpcProvider.prototype.send = function (method, params) {
|
||||
var _this = this;
|
||||
var request = {
|
||||
method: method,
|
||||
params: params,
|
||||
id: (_nextId++),
|
||||
jsonrpc: "2.0"
|
||||
};
|
||||
return web_1.fetchJson(this.connection, JSON.stringify(request), getResult).then(function (result) {
|
||||
_this.emit('debug', {
|
||||
action: 'send',
|
||||
request: request,
|
||||
response: result,
|
||||
provider: _this
|
||||
});
|
||||
return result;
|
||||
});
|
||||
};
|
||||
JsonRpcProvider.prototype.perform = function (method, params) {
|
||||
switch (method) {
|
||||
case 'getBlockNumber':
|
||||
return this.send('eth_blockNumber', []);
|
||||
case 'getGasPrice':
|
||||
return this.send('eth_gasPrice', []);
|
||||
case 'getBalance':
|
||||
return this.send('eth_getBalance', [getLowerCase(params.address), params.blockTag]);
|
||||
case 'getTransactionCount':
|
||||
return this.send('eth_getTransactionCount', [getLowerCase(params.address), params.blockTag]);
|
||||
case 'getCode':
|
||||
return this.send('eth_getCode', [getLowerCase(params.address), params.blockTag]);
|
||||
case 'getStorageAt':
|
||||
return this.send('eth_getStorageAt', [getLowerCase(params.address), params.position, params.blockTag]);
|
||||
case 'sendTransaction':
|
||||
return this.send('eth_sendRawTransaction', [params.signedTransaction]).catch(function (error) {
|
||||
if (error.responseText) {
|
||||
// "insufficient funds for gas * price + value"
|
||||
if (error.responseText.indexOf('insufficient funds') > 0) {
|
||||
errors.throwError('insufficient funds', errors.INSUFFICIENT_FUNDS, {});
|
||||
}
|
||||
// "nonce too low"
|
||||
if (error.responseText.indexOf('nonce too low') > 0) {
|
||||
errors.throwError('nonce has already been used', errors.NONCE_EXPIRED, {});
|
||||
}
|
||||
// "replacement transaction underpriced"
|
||||
if (error.responseText.indexOf('replacement transaction underpriced') > 0) {
|
||||
errors.throwError('replacement fee too low', errors.REPLACEMENT_UNDERPRICED, {});
|
||||
}
|
||||
}
|
||||
throw error;
|
||||
});
|
||||
case 'getBlock':
|
||||
if (params.blockTag) {
|
||||
return this.send('eth_getBlockByNumber', [params.blockTag, !!params.includeTransactions]);
|
||||
}
|
||||
else if (params.blockHash) {
|
||||
return this.send('eth_getBlockByHash', [params.blockHash, !!params.includeTransactions]);
|
||||
}
|
||||
return Promise.reject(new Error('invalid block tag or block hash'));
|
||||
case 'getTransaction':
|
||||
return this.send('eth_getTransactionByHash', [params.transactionHash]);
|
||||
case 'getTransactionReceipt':
|
||||
return this.send('eth_getTransactionReceipt', [params.transactionHash]);
|
||||
case 'call':
|
||||
return this.send('eth_call', [JsonRpcProvider.hexlifyTransaction(params.transaction, { from: true }), params.blockTag]);
|
||||
case 'estimateGas':
|
||||
return this.send('eth_estimateGas', [JsonRpcProvider.hexlifyTransaction(params.transaction, { from: true })]);
|
||||
case 'getLogs':
|
||||
if (params.filter && params.filter.address != null) {
|
||||
params.filter.address = getLowerCase(params.filter.address);
|
||||
}
|
||||
return this.send('eth_getLogs', [params.filter]);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
errors.throwError(method + ' not implemented', errors.NOT_IMPLEMENTED, { operation: method });
|
||||
return null;
|
||||
};
|
||||
JsonRpcProvider.prototype._startPending = function () {
|
||||
if (this._pendingFilter != null) {
|
||||
return;
|
||||
}
|
||||
var self = this;
|
||||
var pendingFilter = this.send('eth_newPendingTransactionFilter', []);
|
||||
this._pendingFilter = pendingFilter;
|
||||
pendingFilter.then(function (filterId) {
|
||||
function poll() {
|
||||
self.send('eth_getFilterChanges', [filterId]).then(function (hashes) {
|
||||
if (self._pendingFilter != pendingFilter) {
|
||||
return null;
|
||||
}
|
||||
var seq = Promise.resolve();
|
||||
hashes.forEach(function (hash) {
|
||||
// @TODO: This should be garbage collected at some point... How? When?
|
||||
self._emitted['t:' + hash.toLowerCase()] = 'pending';
|
||||
seq = seq.then(function () {
|
||||
return self.getTransaction(hash).then(function (tx) {
|
||||
self.emit('pending', tx);
|
||||
return null;
|
||||
});
|
||||
});
|
||||
});
|
||||
return seq.then(function () {
|
||||
return timer(1000);
|
||||
});
|
||||
}).then(function () {
|
||||
if (self._pendingFilter != pendingFilter) {
|
||||
self.send('eth_uninstallFilter', [filterId]);
|
||||
return;
|
||||
}
|
||||
setTimeout(function () { poll(); }, 0);
|
||||
return null;
|
||||
}).catch(function (error) { });
|
||||
}
|
||||
poll();
|
||||
return filterId;
|
||||
}).catch(function (error) { });
|
||||
};
|
||||
JsonRpcProvider.prototype._stopPending = function () {
|
||||
this._pendingFilter = null;
|
||||
};
|
||||
// Convert an ethers.js transaction into a JSON-RPC transaction
|
||||
// - gasLimit => gas
|
||||
// - All values hexlified
|
||||
// - All numeric values zero-striped
|
||||
// NOTE: This allows a TransactionRequest, but all values should be resolved
|
||||
// before this is called
|
||||
JsonRpcProvider.hexlifyTransaction = function (transaction, allowExtra) {
|
||||
// Check only allowed properties are given
|
||||
var allowed = properties_1.shallowCopy(allowedTransactionKeys);
|
||||
if (allowExtra) {
|
||||
for (var key in allowExtra) {
|
||||
if (allowExtra[key]) {
|
||||
allowed[key] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
properties_1.checkProperties(transaction, allowed);
|
||||
var result = {};
|
||||
// Some nodes (INFURA ropsten; INFURA mainnet is fine) don't like leading zeros.
|
||||
['gasLimit', 'gasPrice', 'nonce', 'value'].forEach(function (key) {
|
||||
if (transaction[key] == null) {
|
||||
return;
|
||||
}
|
||||
var value = bytes_1.hexStripZeros(bytes_1.hexlify(transaction[key]));
|
||||
if (key === 'gasLimit') {
|
||||
key = 'gas';
|
||||
}
|
||||
result[key] = value;
|
||||
});
|
||||
['from', 'to', 'data'].forEach(function (key) {
|
||||
if (transaction[key] == null) {
|
||||
return;
|
||||
}
|
||||
result[key] = bytes_1.hexlify(transaction[key]);
|
||||
});
|
||||
return result;
|
||||
};
|
||||
return JsonRpcProvider;
|
||||
}(base_provider_1.BaseProvider));
|
||||
exports.JsonRpcProvider = JsonRpcProvider;
|
||||
15
providers/web3-provider.d.ts
vendored
Normal file
15
providers/web3-provider.d.ts
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
import { JsonRpcProvider } from './json-rpc-provider';
|
||||
import { Networkish } from '../utils/networks';
|
||||
export declare type AsyncSendable = {
|
||||
isMetaMask?: boolean;
|
||||
host?: string;
|
||||
path?: string;
|
||||
sendAsync?: (request: any, callback: (error: any, response: any) => void) => void;
|
||||
send?: (request: any, callback: (error: any, response: any) => void) => void;
|
||||
};
|
||||
export declare class Web3Provider extends JsonRpcProvider {
|
||||
readonly _web3Provider: AsyncSendable;
|
||||
private _sendAsync;
|
||||
constructor(web3Provider: AsyncSendable, network?: Networkish);
|
||||
send(method: string, params: any): Promise<any>;
|
||||
}
|
||||
85
providers/web3-provider.js
Normal file
85
providers/web3-provider.js
Normal file
@@ -0,0 +1,85 @@
|
||||
'use strict';
|
||||
var __extends = (this && this.__extends) || (function () {
|
||||
var extendStatics = Object.setPrototypeOf ||
|
||||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
||||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
|
||||
return function (d, b) {
|
||||
extendStatics(d, b);
|
||||
function __() { this.constructor = d; }
|
||||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
||||
};
|
||||
})();
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
||||
result["default"] = mod;
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var json_rpc_provider_1 = require("./json-rpc-provider");
|
||||
var properties_1 = require("../utils/properties");
|
||||
var errors = __importStar(require("../errors"));
|
||||
/*
|
||||
@TODO
|
||||
utils.defineProperty(Web3Signer, 'onchange', {
|
||||
|
||||
});
|
||||
|
||||
*/
|
||||
var Web3Provider = /** @class */ (function (_super) {
|
||||
__extends(Web3Provider, _super);
|
||||
function Web3Provider(web3Provider, network) {
|
||||
var _this =
|
||||
// HTTP has a host; IPC has a path.
|
||||
_super.call(this, web3Provider.host || web3Provider.path || '', network) || this;
|
||||
errors.checkNew(_this, Web3Provider);
|
||||
if (web3Provider) {
|
||||
if (web3Provider.sendAsync) {
|
||||
_this._sendAsync = web3Provider.sendAsync.bind(web3Provider);
|
||||
}
|
||||
else if (web3Provider.send) {
|
||||
_this._sendAsync = web3Provider.send.bind(web3Provider);
|
||||
}
|
||||
}
|
||||
if (!web3Provider || !_this._sendAsync) {
|
||||
errors.throwError('invalid web3Provider', errors.INVALID_ARGUMENT, { arg: 'web3Provider', value: web3Provider });
|
||||
}
|
||||
properties_1.defineReadOnly(_this, '_web3Provider', web3Provider);
|
||||
return _this;
|
||||
}
|
||||
Web3Provider.prototype.send = function (method, params) {
|
||||
var _this = this;
|
||||
// Metamask complains about eth_sign (and on some versions hangs)
|
||||
if (method == 'eth_sign' && this._web3Provider.isMetaMask) {
|
||||
// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_sign
|
||||
method = 'personal_sign';
|
||||
params = [params[1], params[0]];
|
||||
}
|
||||
return new Promise(function (resolve, reject) {
|
||||
var request = {
|
||||
method: method,
|
||||
params: params,
|
||||
id: 42,
|
||||
jsonrpc: "2.0"
|
||||
};
|
||||
_this._sendAsync(request, function (error, result) {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
if (result.error) {
|
||||
// @TODO: not any
|
||||
var error = new Error(result.error.message);
|
||||
error.code = result.error.code;
|
||||
error.data = result.error.data;
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
resolve(result.result);
|
||||
});
|
||||
});
|
||||
};
|
||||
return Web3Provider;
|
||||
}(json_rpc_provider_1.JsonRpcProvider));
|
||||
exports.Web3Provider = Web3Provider;
|
||||
21
shims/base64.js
Normal file
21
shims/base64.js
Normal file
@@ -0,0 +1,21 @@
|
||||
'use strict';
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var bytes_1 = require("../utils/bytes");
|
||||
module.exports = {
|
||||
decode: function (textData) {
|
||||
textData = atob(textData);
|
||||
var data = [];
|
||||
for (var i = 0; i < textData.length; i++) {
|
||||
data.push(textData.charCodeAt(i));
|
||||
}
|
||||
return bytes_1.arrayify(data);
|
||||
},
|
||||
encode: function (data) {
|
||||
data = bytes_1.arrayify(data);
|
||||
var textData = '';
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
textData += String.fromCharCode(data[i]);
|
||||
}
|
||||
return btoa(textData);
|
||||
}
|
||||
};
|
||||
2
shims/empty.js
Normal file
2
shims/empty.js
Normal file
@@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
26
shims/hmac.js
Normal file
26
shims/hmac.js
Normal file
@@ -0,0 +1,26 @@
|
||||
"use strict";
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
||||
result["default"] = mod;
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var hash = __importStar(require("hash.js"));
|
||||
var bytes_1 = require("../utils/bytes");
|
||||
var errors = __importStar(require("../errors"));
|
||||
///////////////////////////////
|
||||
var SupportedAlgorithms;
|
||||
(function (SupportedAlgorithms) {
|
||||
SupportedAlgorithms["sha256"] = "sha256";
|
||||
SupportedAlgorithms["sha512"] = "sha512";
|
||||
})(SupportedAlgorithms = exports.SupportedAlgorithms || (exports.SupportedAlgorithms = {}));
|
||||
;
|
||||
function computeHmac(algorithm, key, data) {
|
||||
if (!SupportedAlgorithms[algorithm]) {
|
||||
errors.throwError('unsupported algorithm ' + algorithm, errors.UNSUPPORTED_OPERATION, { operation: 'hmac', algorithm: algorithm });
|
||||
}
|
||||
return bytes_1.arrayify(hash.hmac(hash[algorithm], bytes_1.arrayify(key)).update(bytes_1.arrayify(data)).digest());
|
||||
}
|
||||
exports.computeHmac = computeHmac;
|
||||
6
shims/index.js
Normal file
6
shims/index.js
Normal file
@@ -0,0 +1,6 @@
|
||||
"use strict";
|
||||
/**
|
||||
* This file is not imported anywhere, but is used to trigger TypeScript
|
||||
* compilation of the various shims for the browser.
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
45
shims/pbkdf2.js
Normal file
45
shims/pbkdf2.js
Normal file
@@ -0,0 +1,45 @@
|
||||
'use strict';
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var bytes_1 = require("../utils/bytes");
|
||||
var hmac_1 = require("./hmac");
|
||||
function pbkdf2(password, salt, iterations, keylen, hashAlgorithm) {
|
||||
password = bytes_1.arrayify(password);
|
||||
salt = bytes_1.arrayify(salt);
|
||||
var hLen;
|
||||
var l = 1;
|
||||
var DK = new Uint8Array(keylen);
|
||||
var block1 = new Uint8Array(salt.length + 4);
|
||||
block1.set(salt);
|
||||
//salt.copy(block1, 0, 0, salt.length)
|
||||
var r;
|
||||
var T;
|
||||
for (var i = 1; i <= l; i++) {
|
||||
//block1.writeUInt32BE(i, salt.length)
|
||||
block1[salt.length] = (i >> 24) & 0xff;
|
||||
block1[salt.length + 1] = (i >> 16) & 0xff;
|
||||
block1[salt.length + 2] = (i >> 8) & 0xff;
|
||||
block1[salt.length + 3] = i & 0xff;
|
||||
//var U = createHmac(password).update(block1).digest();
|
||||
var U = hmac_1.computeHmac(hashAlgorithm, password, block1);
|
||||
if (!hLen) {
|
||||
hLen = U.length;
|
||||
T = new Uint8Array(hLen);
|
||||
l = Math.ceil(keylen / hLen);
|
||||
r = keylen - (l - 1) * hLen;
|
||||
}
|
||||
//U.copy(T, 0, 0, hLen)
|
||||
T.set(U);
|
||||
for (var j = 1; j < iterations; j++) {
|
||||
//U = createHmac(password).update(U).digest();
|
||||
U = hmac_1.computeHmac(hashAlgorithm, password, U);
|
||||
for (var k = 0; k < hLen; k++)
|
||||
T[k] ^= U[k];
|
||||
}
|
||||
var destPos = (i - 1) * hLen;
|
||||
var len = (i === l ? r : hLen);
|
||||
//T.copy(DK, destPos, 0, len)
|
||||
DK.set(bytes_1.arrayify(T).slice(0, len), destPos);
|
||||
}
|
||||
return bytes_1.arrayify(DK);
|
||||
}
|
||||
exports.pbkdf2 = pbkdf2;
|
||||
37
shims/random-bytes.js
Normal file
37
shims/random-bytes.js
Normal file
@@ -0,0 +1,37 @@
|
||||
'use strict';
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var bytes_1 = require("../utils/bytes");
|
||||
var properties_1 = require("../utils/properties");
|
||||
var crypto = global.crypto || global.msCrypto;
|
||||
if (!crypto || !crypto.getRandomValues) {
|
||||
console.log('WARNING: Missing strong random number source; using weak randomBytes');
|
||||
crypto = {
|
||||
getRandomValues: function (buffer) {
|
||||
for (var round = 0; round < 20; round++) {
|
||||
for (var i = 0; i < buffer.length; i++) {
|
||||
if (round) {
|
||||
buffer[i] ^= Math.trunc(256 * Math.random());
|
||||
}
|
||||
else {
|
||||
buffer[i] = Math.trunc(256 * Math.random());
|
||||
}
|
||||
}
|
||||
}
|
||||
return buffer;
|
||||
},
|
||||
_weakCrypto: true
|
||||
};
|
||||
}
|
||||
function randomBytes(length) {
|
||||
if (length <= 0 || length > 1024 || parseInt(String(length)) != length) {
|
||||
throw new Error('invalid length');
|
||||
}
|
||||
var result = new Uint8Array(length);
|
||||
crypto.getRandomValues(result);
|
||||
return bytes_1.arrayify(result);
|
||||
}
|
||||
exports.randomBytes = randomBytes;
|
||||
;
|
||||
if (crypto._weakCrypto === true) {
|
||||
properties_1.defineReadOnly(randomBytes, '_weakCrypto', true);
|
||||
}
|
||||
4
shims/shims.js
Normal file
4
shims/shims.js
Normal file
@@ -0,0 +1,4 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
require('setimmediate');
|
||||
exports.platform = "browser";
|
||||
5
shims/wordlists.js
Normal file
5
shims/wordlists.js
Normal file
@@ -0,0 +1,5 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var lang_en_1 = require("../wordlists/lang-en");
|
||||
var en = lang_en_1.langEn;
|
||||
exports.en = en;
|
||||
9
shims/xmlhttprequest.js
Normal file
9
shims/xmlhttprequest.js
Normal file
@@ -0,0 +1,9 @@
|
||||
'use strict';
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
try {
|
||||
module.exports.XMLHttpRequest = XMLHttpRequest;
|
||||
}
|
||||
catch (error) {
|
||||
console.log('Warning: XMLHttpRequest is not defined');
|
||||
module.exports.XMLHttpRequest = null;
|
||||
}
|
||||
1
src.ts/_version.ts
Normal file
1
src.ts/_version.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const version = "4.0.29";
|
||||
32
src.ts/abstract-signer.ts
Normal file
32
src.ts/abstract-signer.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
|
||||
import { isType, setType } from './utils/properties';
|
||||
|
||||
// Imported Abstracts
|
||||
import { Provider } from './providers/abstract-provider';
|
||||
|
||||
// Imported Types
|
||||
import { Arrayish } from './utils/bytes';
|
||||
import { TransactionRequest, TransactionResponse } from './providers/abstract-provider';
|
||||
|
||||
|
||||
export abstract class Signer {
|
||||
readonly provider?: Provider;
|
||||
|
||||
abstract getAddress(): Promise<string>
|
||||
|
||||
abstract signMessage(message: Arrayish | string): Promise<string>;
|
||||
abstract sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse>;
|
||||
|
||||
constructor() {
|
||||
setType(this, 'Signer');
|
||||
}
|
||||
|
||||
static isSigner(value: any): value is Signer {
|
||||
return isType(value, 'Signer');
|
||||
}
|
||||
|
||||
// readonly inherits: (child: any) => void;
|
||||
}
|
||||
|
||||
//defineReadOnly(Signer, 'inherits', inheritable(Signer));
|
||||
|
||||
31
src.ts/constants.ts
Normal file
31
src.ts/constants.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { BigNumber, bigNumberify } from './utils/bignumber';
|
||||
|
||||
const AddressZero = '0x0000000000000000000000000000000000000000';
|
||||
const HashZero = '0x0000000000000000000000000000000000000000000000000000000000000000';
|
||||
|
||||
// NFKD (decomposed)
|
||||
//const EtherSymbol = '\uD835\uDF63';
|
||||
|
||||
// NFKC (composed)
|
||||
const EtherSymbol = '\u039e';
|
||||
|
||||
const NegativeOne: BigNumber = bigNumberify(-1);
|
||||
const Zero: BigNumber = bigNumberify(0);
|
||||
const One: BigNumber = bigNumberify(1);
|
||||
const Two: BigNumber = bigNumberify(2);
|
||||
const WeiPerEther: BigNumber = bigNumberify('1000000000000000000');
|
||||
const MaxUint256: BigNumber = bigNumberify('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
|
||||
|
||||
export {
|
||||
AddressZero,
|
||||
HashZero,
|
||||
|
||||
EtherSymbol,
|
||||
|
||||
NegativeOne,
|
||||
Zero,
|
||||
One,
|
||||
Two,
|
||||
WeiPerEther,
|
||||
MaxUint256
|
||||
};
|
||||
862
src.ts/contract.ts
Normal file
862
src.ts/contract.ts
Normal file
@@ -0,0 +1,862 @@
|
||||
'use strict';
|
||||
|
||||
import { Zero } from './constants';
|
||||
|
||||
import * as errors from './errors';
|
||||
|
||||
import { defaultAbiCoder, formatSignature, parseSignature } from './utils/abi-coder';
|
||||
import { getAddress, getContractAddress } from './utils/address';
|
||||
import { BigNumber, bigNumberify } from './utils/bignumber';
|
||||
import { hexDataLength, hexDataSlice, hexlify, isArrayish, isHexString } from './utils/bytes';
|
||||
import { Indexed, Interface } from './utils/interface';
|
||||
import { defineReadOnly, deepCopy, shallowCopy } from './utils/properties';
|
||||
import { UnsignedTransaction } from './utils/transaction';
|
||||
|
||||
|
||||
///////////////////////////////
|
||||
// Imported Abstracts
|
||||
|
||||
import { BlockTag, Provider } from './providers/abstract-provider';
|
||||
import { Signer } from './abstract-signer';
|
||||
|
||||
///////////////////////////////
|
||||
// Imported Types
|
||||
|
||||
import { Arrayish } from './utils/bytes';
|
||||
import { EventDescription } from './utils/interface';
|
||||
import { ParamType } from './utils/abi-coder';
|
||||
import { Block, Listener, Log, TransactionReceipt, TransactionRequest, TransactionResponse } from './providers/abstract-provider';
|
||||
|
||||
///////////////////////////////
|
||||
// Exported Types
|
||||
|
||||
export type ContractFunction = (...params: Array<any>) => Promise<any>;
|
||||
|
||||
export type EventFilter = {
|
||||
address?: string;
|
||||
topics?: Array<string>;
|
||||
// @TODO: Support OR-style topcis; backwards compatible to make this change
|
||||
//topics?: Array<string | Array<string>>
|
||||
};
|
||||
|
||||
// The (n + 1)th parameter passed to contract event callbacks
|
||||
export interface Event extends Log {
|
||||
args?: Array<any>;
|
||||
decode?: (data: string, topics?: Array<string>) => any;
|
||||
event?: string;
|
||||
eventSignature?: string;
|
||||
|
||||
removeListener: () => void;
|
||||
|
||||
getBlock: () => Promise<Block>;
|
||||
getTransaction: () => Promise<TransactionResponse>;
|
||||
getTransactionReceipt: () => Promise<TransactionReceipt>;
|
||||
}
|
||||
|
||||
export interface ContractReceipt extends TransactionReceipt {
|
||||
events?: Array<Event>;
|
||||
}
|
||||
|
||||
export interface ContractTransaction extends TransactionResponse {
|
||||
wait(confirmations?: number): Promise<ContractReceipt>;
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
export class VoidSigner extends Signer {
|
||||
readonly address: string;
|
||||
|
||||
constructor(address: string, provider: Provider) {
|
||||
super();
|
||||
defineReadOnly(this, 'address', address);
|
||||
defineReadOnly(this, 'provider', provider);
|
||||
}
|
||||
|
||||
getAddress(): Promise<string> {
|
||||
return Promise.resolve(this.address);
|
||||
}
|
||||
|
||||
_fail(message: string, operation: string): Promise<any> {
|
||||
return Promise.resolve().then(() => {
|
||||
errors.throwError(message, errors.UNSUPPORTED_OPERATION, { operation: operation });
|
||||
});
|
||||
}
|
||||
|
||||
signMessage(message: Arrayish | string): Promise<string> {
|
||||
return this._fail('VoidSigner cannot sign messages', 'signMessage');
|
||||
}
|
||||
|
||||
sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse> {
|
||||
return this._fail('VoidSigner cannot sign transactions', 'sendTransaction');
|
||||
}
|
||||
|
||||
connect(provider: Provider): VoidSigner {
|
||||
return new VoidSigner(this.address, provider);
|
||||
}
|
||||
}
|
||||
|
||||
const allowedTransactionKeys: { [ key: string ]: boolean } = {
|
||||
chainId: true, data: true, from: true, gasLimit: true, gasPrice:true, nonce: true, to: true, value: true
|
||||
}
|
||||
|
||||
// Recursively replaces ENS names with promises to resolve the name and
|
||||
// stalls until all promises have returned
|
||||
// @TODO: Expand this to resolve any promises too
|
||||
function resolveAddresses(provider: Provider, value: any, paramType: ParamType | Array<ParamType>): Promise<any> {
|
||||
if (Array.isArray(paramType)) {
|
||||
let promises: Array<Promise<any>> = [];
|
||||
paramType.forEach((paramType, index) => {
|
||||
let v = null;
|
||||
if (Array.isArray(value)) {
|
||||
v = value[index];
|
||||
} else {
|
||||
v = value[paramType.name];
|
||||
}
|
||||
promises.push(resolveAddresses(provider, v, paramType));
|
||||
});
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
if (paramType.type === 'address') {
|
||||
return provider.resolveName(value);
|
||||
}
|
||||
|
||||
if (paramType.type === 'tuple') {
|
||||
return resolveAddresses(provider, value, paramType.components);
|
||||
}
|
||||
|
||||
// Strips one level of array indexing off the end to recuse into
|
||||
let isArrayMatch = paramType.type.match(/(.*)(\[[0-9]*\]$)/);
|
||||
if (isArrayMatch) {
|
||||
if (!Array.isArray(value)) { throw new Error('invalid value for array'); }
|
||||
var promises: Array<Promise<any>> = [];
|
||||
var subParamType = {
|
||||
components: paramType.components,
|
||||
type: isArrayMatch[1],
|
||||
};
|
||||
value.forEach((v) => {
|
||||
promises.push(resolveAddresses(provider, v, subParamType));
|
||||
});
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
return Promise.resolve(value);
|
||||
}
|
||||
|
||||
type RunFunction = (...params: Array<any>) => Promise<any>;
|
||||
|
||||
function runMethod(contract: Contract, functionName: string, estimateOnly: boolean): RunFunction {
|
||||
let method = contract.interface.functions[functionName];
|
||||
return function(...params): Promise<any> {
|
||||
let tx: any = {}
|
||||
|
||||
let blockTag: BlockTag = null;
|
||||
|
||||
// If 1 extra parameter was passed in, it contains overrides
|
||||
if (params.length === method.inputs.length + 1 && typeof(params[params.length - 1]) === 'object') {
|
||||
tx = shallowCopy(params.pop());
|
||||
|
||||
if (tx.blockTag != null) {
|
||||
blockTag = tx.blockTag;
|
||||
}
|
||||
|
||||
delete tx.blockTag;
|
||||
|
||||
// Check for unexpected keys (e.g. using "gas" instead of "gasLimit")
|
||||
for (let key in tx) {
|
||||
if (!allowedTransactionKeys[key]) {
|
||||
throw new Error('unknown transaction override ' + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (params.length != method.inputs.length) {
|
||||
throw new Error('incorrect number of arguments');
|
||||
}
|
||||
|
||||
// Check overrides make sense
|
||||
['data', 'to'].forEach(function(key) {
|
||||
if (tx[key] != null) {
|
||||
errors.throwError('cannot override ' + key, errors.UNSUPPORTED_OPERATION, { operation: key })
|
||||
}
|
||||
});
|
||||
|
||||
tx.to = contract._deployed(blockTag).then(() => {
|
||||
return contract.addressPromise;
|
||||
});
|
||||
|
||||
return resolveAddresses(contract.provider, params, method.inputs).then((params) => {
|
||||
tx.data = method.encode(params);
|
||||
if (method.type === 'call') {
|
||||
|
||||
// Call (constant functions) always cost 0 ether
|
||||
if (estimateOnly) {
|
||||
return Promise.resolve(Zero);
|
||||
}
|
||||
|
||||
if (!contract.provider) {
|
||||
errors.throwError('call (constant functions) require a provider or a signer with a provider', errors.UNSUPPORTED_OPERATION, { operation: 'call' })
|
||||
}
|
||||
|
||||
// Check overrides make sense
|
||||
['gasLimit', 'gasPrice', 'value'].forEach(function(key) {
|
||||
if (tx[key] != null) {
|
||||
throw new Error('call cannot override ' + key) ;
|
||||
}
|
||||
});
|
||||
|
||||
if (tx.from == null && contract.signer) {
|
||||
tx.from = contract.signer.getAddress()
|
||||
}
|
||||
|
||||
return contract.provider.call(tx, blockTag).then((value) => {
|
||||
if ((hexDataLength(value) % 32) === 4 && hexDataSlice(value, 0, 4) === '0x08c379a0') {
|
||||
let reason = defaultAbiCoder.decode([ 'string' ], hexDataSlice(value, 4));
|
||||
errors.throwError('call revert exception', errors.CALL_EXCEPTION, {
|
||||
address: contract.address,
|
||||
args: params,
|
||||
method: method.signature,
|
||||
errorSignature: 'Error(string)',
|
||||
errorArgs: [ reason ],
|
||||
reason: reason,
|
||||
transaction: tx
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
let result = method.decode(value);
|
||||
if (method.outputs.length === 1) {
|
||||
result = result[0];
|
||||
}
|
||||
return result;
|
||||
|
||||
} catch (error) {
|
||||
if (value === '0x' && method.outputs.length > 0) {
|
||||
errors.throwError('call exception', errors.CALL_EXCEPTION, {
|
||||
address: contract.address,
|
||||
method: method.signature,
|
||||
args: params
|
||||
});
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
} else if (method.type === 'transaction') {
|
||||
|
||||
// Only computing the transaction estimate
|
||||
if (estimateOnly) {
|
||||
if (!contract.provider) {
|
||||
errors.throwError('estimate gas require a provider or a signer with a provider', errors.UNSUPPORTED_OPERATION, { operation: 'estimateGas' })
|
||||
}
|
||||
|
||||
if (tx.from == null && contract.signer) {
|
||||
tx.from = contract.signer.getAddress()
|
||||
}
|
||||
|
||||
return contract.provider.estimateGas(tx);
|
||||
}
|
||||
|
||||
if (tx.gasLimit == null && method.gas != null) {
|
||||
tx.gasLimit = bigNumberify(method.gas).add(21000);
|
||||
}
|
||||
|
||||
if (!contract.signer) {
|
||||
errors.throwError('sending a transaction require a signer', errors.UNSUPPORTED_OPERATION, { operation: 'sendTransaction' })
|
||||
}
|
||||
|
||||
// Make sure they aren't overriding something they shouldn't
|
||||
if (tx.from != null) {
|
||||
errors.throwError('cannot override from in a transaction', errors.UNSUPPORTED_OPERATION, { operation: 'sendTransaction' })
|
||||
}
|
||||
|
||||
return contract.signer.sendTransaction(tx).then((tx) => {
|
||||
let wait = tx.wait.bind(tx);
|
||||
|
||||
tx.wait = (confirmations?: number) => {
|
||||
return wait(confirmations).then((receipt: ContractReceipt) => {
|
||||
receipt.events = receipt.logs.map((log) => {
|
||||
let event: Event = (<Event>deepCopy(log));
|
||||
|
||||
let parsed = contract.interface.parseLog(log);
|
||||
if (parsed) {
|
||||
event.args = parsed.values;
|
||||
event.decode = parsed.decode;
|
||||
event.event = parsed.name;
|
||||
event.eventSignature = parsed.signature;
|
||||
}
|
||||
|
||||
event.removeListener = () => { return contract.provider; }
|
||||
event.getBlock = () => {
|
||||
return contract.provider.getBlock(receipt.blockHash);
|
||||
}
|
||||
event.getTransaction = () => {
|
||||
return contract.provider.getTransaction(receipt.transactionHash);
|
||||
}
|
||||
event.getTransactionReceipt = () => {
|
||||
return Promise.resolve(receipt);
|
||||
}
|
||||
|
||||
return event;
|
||||
});
|
||||
|
||||
return receipt;
|
||||
});
|
||||
};
|
||||
|
||||
return tx;
|
||||
});
|
||||
}
|
||||
|
||||
throw new Error('invalid type - ' + method.type);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getEventTag(filter: EventFilter): string {
|
||||
if (filter.address && (filter.topics == null || filter.topics.length === 0)) {
|
||||
return '*';
|
||||
}
|
||||
return (filter.address || '*') + '@' + (filter.topics ? filter.topics.join(':'): '');
|
||||
}
|
||||
|
||||
interface Bucket<T> {
|
||||
[name: string]: T;
|
||||
}
|
||||
|
||||
type _EventFilter = {
|
||||
prepareEvent: (event: Event) => Array<any>;
|
||||
event?: EventDescription;
|
||||
eventTag: string;
|
||||
filter: EventFilter;
|
||||
};
|
||||
|
||||
type _Event = {
|
||||
eventFilter: _EventFilter;
|
||||
listener: Listener;
|
||||
once: boolean;
|
||||
wrappedListener: Listener;
|
||||
};
|
||||
|
||||
export class Contract {
|
||||
readonly address: string;
|
||||
readonly interface: Interface;
|
||||
|
||||
readonly signer: Signer;
|
||||
readonly provider: Provider;
|
||||
|
||||
readonly estimate: Bucket<(...params: Array<any>) => Promise<BigNumber>>;
|
||||
readonly functions: Bucket<ContractFunction>;
|
||||
|
||||
readonly filters: Bucket<(...params: Array<any>) => EventFilter>;
|
||||
|
||||
readonly [name: string]: ContractFunction | any;
|
||||
|
||||
readonly addressPromise: Promise<string>;
|
||||
|
||||
// This is only set if the contract was created with a call to deploy
|
||||
readonly deployTransaction: TransactionResponse;
|
||||
|
||||
private _deployedPromise: Promise<Contract>;
|
||||
|
||||
// https://github.com/Microsoft/TypeScript/issues/5453
|
||||
// Once this issue is resolved (there are open PR) we can do this nicer
|
||||
// by making addressOrName default to null for 2 operand calls. :)
|
||||
|
||||
constructor(addressOrName: string, contractInterface: Array<string | ParamType> | string | Interface, signerOrProvider: Signer | Provider) {
|
||||
errors.checkNew(this, Contract);
|
||||
|
||||
// @TODO: Maybe still check the addressOrName looks like a valid address or name?
|
||||
//address = getAddress(address);
|
||||
|
||||
if (Interface.isInterface(contractInterface)) {
|
||||
defineReadOnly(this, 'interface', contractInterface);
|
||||
} else {
|
||||
defineReadOnly(this, 'interface', new Interface(contractInterface));
|
||||
}
|
||||
|
||||
if (Signer.isSigner(signerOrProvider)) {
|
||||
defineReadOnly(this, 'provider', signerOrProvider.provider);
|
||||
defineReadOnly(this, 'signer', signerOrProvider);
|
||||
} else if (Provider.isProvider(signerOrProvider)) {
|
||||
defineReadOnly(this, 'provider', signerOrProvider);
|
||||
defineReadOnly(this, 'signer', null);
|
||||
} else {
|
||||
errors.throwError('invalid signer or provider', errors.INVALID_ARGUMENT, { arg: 'signerOrProvider', value: signerOrProvider });
|
||||
}
|
||||
|
||||
defineReadOnly(this, 'estimate', { });
|
||||
defineReadOnly(this, 'functions', { });
|
||||
|
||||
defineReadOnly(this, 'filters', { });
|
||||
|
||||
Object.keys(this.interface.events).forEach((eventName) => {
|
||||
let event = this.interface.events[eventName];
|
||||
defineReadOnly(this.filters, eventName, (...args: Array<any>) => {
|
||||
return {
|
||||
address: this.address,
|
||||
topics: event.encodeTopics(args)
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this._events = [];
|
||||
|
||||
defineReadOnly(this, 'address', addressOrName);
|
||||
if (this.provider) {
|
||||
defineReadOnly(this, 'addressPromise', this.provider.resolveName(addressOrName).then((address) => {
|
||||
if (address == null) { throw new Error('name not found'); }
|
||||
return address;
|
||||
}).catch((error: Error) => {
|
||||
throw error;
|
||||
}));
|
||||
} else {
|
||||
try {
|
||||
defineReadOnly(this, 'addressPromise', Promise.resolve(getAddress(addressOrName)));
|
||||
} catch (error) {
|
||||
// Without a provider, we cannot use ENS names
|
||||
errors.throwError('provider is required to use non-address contract address', errors.INVALID_ARGUMENT, { argument: 'addressOrName', value: addressOrName });
|
||||
}
|
||||
}
|
||||
|
||||
Object.keys(this.interface.functions).forEach((name) => {
|
||||
let run = runMethod(this, name, false);
|
||||
|
||||
if ((<any>this)[name] == null) {
|
||||
defineReadOnly(this, name, run);
|
||||
} else {
|
||||
errors.warn('WARNING: Multiple definitions for ' + name);
|
||||
}
|
||||
|
||||
if (this.functions[name] == null) {
|
||||
defineReadOnly(this.functions, name, run);
|
||||
defineReadOnly(this.estimate, name, runMethod(this, name, true));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// @TODO: Allow timeout?
|
||||
deployed(): Promise<Contract> {
|
||||
return this._deployed();
|
||||
}
|
||||
|
||||
_deployed(blockTag?: BlockTag): Promise<Contract> {
|
||||
if (!this._deployedPromise) {
|
||||
|
||||
// If we were just deployed, we know the transaction we should occur in
|
||||
if (this.deployTransaction) {
|
||||
this._deployedPromise = this.deployTransaction.wait().then(() => {
|
||||
return this;
|
||||
});
|
||||
|
||||
} else {
|
||||
// @TODO: Once we allow a timeout to be passed in, we will wait
|
||||
// up to that many blocks for getCode
|
||||
|
||||
// Otherwise, poll for our code to be deployed
|
||||
this._deployedPromise = this.provider.getCode(this.address, blockTag).then((code) => {
|
||||
if (code === '0x') {
|
||||
errors.throwError('contract not deployed', errors.UNSUPPORTED_OPERATION, {
|
||||
contractAddress: this.address,
|
||||
operation: 'getDeployed'
|
||||
});
|
||||
}
|
||||
return this;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return this._deployedPromise;
|
||||
}
|
||||
|
||||
// @TODO:
|
||||
// estimateFallback(overrides?: TransactionRequest): Promise<BigNumber>
|
||||
|
||||
// @TODO:
|
||||
// estimateDeploy(bytecode: string, ...args): Promise<BigNumber>
|
||||
|
||||
fallback(overrides?: TransactionRequest): Promise<TransactionResponse> {
|
||||
if (!this.signer) {
|
||||
errors.throwError('sending a transaction require a signer', errors.UNSUPPORTED_OPERATION, { operation: 'sendTransaction(fallback)' })
|
||||
}
|
||||
|
||||
var tx: TransactionRequest = shallowCopy(overrides || {});
|
||||
|
||||
['from', 'to'].forEach(function(key) {
|
||||
if ((<any>tx)[key] == null) { return; }
|
||||
errors.throwError('cannot override ' + key, errors.UNSUPPORTED_OPERATION, { operation: key })
|
||||
});
|
||||
|
||||
tx.to = this.addressPromise;
|
||||
return this.deployed().then(() => {
|
||||
return this.signer.sendTransaction(tx);
|
||||
});
|
||||
}
|
||||
|
||||
// Reconnect to a different signer or provider
|
||||
connect(signerOrProvider: Signer | Provider | string): Contract {
|
||||
if (typeof(signerOrProvider) === 'string') {
|
||||
signerOrProvider = new VoidSigner(signerOrProvider, this.provider);
|
||||
}
|
||||
|
||||
let contract = new Contract(this.address, this.interface, signerOrProvider);
|
||||
if (this.deployTransaction) {
|
||||
defineReadOnly(contract, 'deployTransaction', this.deployTransaction);
|
||||
}
|
||||
return contract;
|
||||
}
|
||||
|
||||
// Re-attach to a different on=chain instance of this contract
|
||||
attach(addressOrName: string): Contract {
|
||||
return new Contract(addressOrName, this.interface, this.signer || this.provider);
|
||||
}
|
||||
|
||||
static isIndexed(value: any): value is Indexed {
|
||||
return Interface.isIndexed(value);
|
||||
}
|
||||
|
||||
private _events: Array<_Event>;
|
||||
|
||||
private _getEventFilter(eventName: EventFilter | string): _EventFilter {
|
||||
if (typeof(eventName) === 'string') {
|
||||
|
||||
// Listen for any event
|
||||
if (eventName === '*') {
|
||||
return {
|
||||
prepareEvent: (e: Event) => {
|
||||
let parsed = this.interface.parseLog(e);
|
||||
if (parsed) {
|
||||
e.args = parsed.values;
|
||||
e.decode = parsed.decode;
|
||||
e.event = parsed.name;
|
||||
e.eventSignature = parsed.signature;
|
||||
}
|
||||
return [ e ];
|
||||
},
|
||||
eventTag: '*',
|
||||
filter: { address: this.address },
|
||||
};
|
||||
}
|
||||
|
||||
// Normalize the eventName
|
||||
if (eventName.indexOf('(') !== -1) {
|
||||
eventName = formatSignature(parseSignature('event ' + eventName));
|
||||
}
|
||||
|
||||
let event = this.interface.events[eventName];
|
||||
if (!event) {
|
||||
errors.throwError('unknown event - ' + eventName, errors.INVALID_ARGUMENT, { argumnet: 'eventName', value: eventName });
|
||||
}
|
||||
|
||||
let filter = {
|
||||
address: this.address,
|
||||
topics: [ event.topic ]
|
||||
}
|
||||
|
||||
return {
|
||||
prepareEvent: (e: Event) => {
|
||||
let args = event.decode(e.data, e.topics);
|
||||
e.args = args;
|
||||
|
||||
let result = Array.prototype.slice.call(args);
|
||||
result.push(e);
|
||||
|
||||
return result;
|
||||
},
|
||||
event: event,
|
||||
eventTag: getEventTag(filter),
|
||||
filter: filter
|
||||
};
|
||||
}
|
||||
|
||||
let filter: EventFilter = {
|
||||
address: this.address
|
||||
}
|
||||
|
||||
// Find the matching event in the ABI; if none, we still allow filtering
|
||||
// since it may be a filter for an otherwise unknown event
|
||||
let event: EventDescription = null;
|
||||
if (eventName.topics && eventName.topics[0]) {
|
||||
filter.topics = eventName.topics;
|
||||
for (let name in this.interface.events) {
|
||||
if (name.indexOf('(') === -1) { continue; }
|
||||
let e = this.interface.events[name];
|
||||
if (e.topic === eventName.topics[0].toLowerCase()) {
|
||||
event = e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
prepareEvent: (e: Event) => {
|
||||
if (!event) { return [ e ]; }
|
||||
|
||||
let args = event.decode(e.data, e.topics);
|
||||
e.args = args;
|
||||
|
||||
let result = Array.prototype.slice.call(args);
|
||||
result.push(e);
|
||||
|
||||
return result;
|
||||
},
|
||||
event: event,
|
||||
eventTag: getEventTag(filter),
|
||||
filter: filter
|
||||
}
|
||||
}
|
||||
|
||||
private _addEventListener(eventFilter: _EventFilter, listener: Listener, once: boolean): void {
|
||||
if (!this.provider) {
|
||||
errors.throwError('events require a provider or a signer with a provider', errors.UNSUPPORTED_OPERATION, { operation: 'once' })
|
||||
}
|
||||
|
||||
let wrappedListener = (log: Log) => {
|
||||
|
||||
let event: Event = (<Event>deepCopy(log));
|
||||
|
||||
let args = eventFilter.prepareEvent(event);
|
||||
|
||||
if (eventFilter.event) {
|
||||
event.decode = eventFilter.event.decode;
|
||||
event.event = eventFilter.event.name;
|
||||
event.eventSignature = eventFilter.event.signature;
|
||||
}
|
||||
|
||||
event.removeListener = () => { this.removeListener(eventFilter.filter, listener); };
|
||||
|
||||
event.getBlock = () => { return this.provider.getBlock(log.blockHash); }
|
||||
event.getTransaction = () => { return this.provider.getTransaction(log.transactionHash); }
|
||||
event.getTransactionReceipt = () => { return this.provider.getTransactionReceipt(log.transactionHash); }
|
||||
|
||||
this.emit(eventFilter.filter, ...args);
|
||||
};
|
||||
|
||||
this.provider.on(eventFilter.filter, wrappedListener);
|
||||
this._events.push({ eventFilter: eventFilter, listener: listener, wrappedListener: wrappedListener, once: once });
|
||||
}
|
||||
|
||||
on(event: EventFilter | string, listener: Listener): Contract {
|
||||
this._addEventListener(this._getEventFilter(event), listener, false);
|
||||
return this;
|
||||
}
|
||||
|
||||
once(event: EventFilter | string, listener: Listener): Contract {
|
||||
this._addEventListener(this._getEventFilter(event), listener, true);
|
||||
return this;
|
||||
}
|
||||
|
||||
addListener(eventName: EventFilter | string, listener: Listener): Contract {
|
||||
return this.on(eventName, listener);
|
||||
}
|
||||
|
||||
emit(eventName: EventFilter | string, ...args: Array<any>): boolean {
|
||||
if (!this.provider) { return false; }
|
||||
|
||||
let result = false;
|
||||
|
||||
let eventFilter = this._getEventFilter(eventName);
|
||||
this._events = this._events.filter((event) => {
|
||||
|
||||
// Not this event (keep it for later)
|
||||
if (event.eventFilter.eventTag !== eventFilter.eventTag) { return true; }
|
||||
|
||||
// Call the callback in the next event loop
|
||||
setTimeout(() => {
|
||||
event.listener.apply(this, args);
|
||||
}, 0);
|
||||
result = true;
|
||||
|
||||
// Reschedule it if it not "once"
|
||||
return !(event.once);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
listenerCount(eventName?: EventFilter | string): number {
|
||||
if (!this.provider) { return 0; }
|
||||
|
||||
let eventFilter = this._getEventFilter(eventName);
|
||||
return this._events.filter((event) => {
|
||||
return event.eventFilter.eventTag === eventFilter.eventTag
|
||||
}).length;
|
||||
}
|
||||
|
||||
listeners(eventName: EventFilter | string): Array<Listener> {
|
||||
if (!this.provider) { return []; }
|
||||
|
||||
let eventFilter = this._getEventFilter(eventName);
|
||||
return this._events.filter((event) => {
|
||||
return event.eventFilter.eventTag === eventFilter.eventTag
|
||||
}).map((event) => { return event.listener; });
|
||||
}
|
||||
|
||||
removeAllListeners(eventName: EventFilter | string): Contract {
|
||||
if (!this.provider) { return this; }
|
||||
|
||||
let eventFilter = this._getEventFilter(eventName);
|
||||
this._events = this._events.filter((event) => {
|
||||
|
||||
// Keep all other events
|
||||
if (event.eventFilter.eventTag !== eventFilter.eventTag) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Deregister this event from the provider and filter it out
|
||||
this.provider.removeListener(event.eventFilter.filter, event.wrappedListener);
|
||||
return false;
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
removeListener(eventName: any, listener: Listener): Contract {
|
||||
if (!this.provider) { return this; }
|
||||
|
||||
let found = false;
|
||||
|
||||
let eventFilter = this._getEventFilter(eventName);
|
||||
this._events = this._events.filter((event) => {
|
||||
|
||||
// Make sure this event and listener match
|
||||
if (event.eventFilter.eventTag !== eventFilter.eventTag) { return true; }
|
||||
if (event.listener !== listener) { return true; }
|
||||
this.provider.removeListener(event.eventFilter.filter, event.wrappedListener);
|
||||
|
||||
// Already found a matching event in a previous loop
|
||||
if (found) { return true; }
|
||||
|
||||
// REmove this event (returning false filters us out)
|
||||
found = true;
|
||||
return false;
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class ContractFactory {
|
||||
|
||||
readonly interface: Interface;
|
||||
readonly bytecode: string;
|
||||
readonly signer: Signer;
|
||||
|
||||
constructor(contractInterface: Array<string | ParamType> | string | Interface, bytecode: Arrayish | string | { object: string }, signer?: Signer) {
|
||||
|
||||
let bytecodeHex: string = null;
|
||||
|
||||
// Allow the bytecode object from the Solidity compiler
|
||||
if (typeof(bytecode) === 'string') {
|
||||
bytecodeHex = bytecode;
|
||||
} else if (isArrayish(bytecode)) {
|
||||
bytecodeHex = hexlify(bytecode);
|
||||
} else if (typeof(bytecode.object) === 'string') {
|
||||
bytecodeHex = (<any>bytecode).object;
|
||||
} else {
|
||||
errors.throwError('bytecode must be a valid hex string', errors.INVALID_ARGUMENT, { arg: 'bytecode', value: bytecode });
|
||||
}
|
||||
|
||||
// Make sure it is 0x prefixed
|
||||
if (bytecodeHex.substring(0, 2) !== '0x') {
|
||||
bytecodeHex = '0x' + bytecodeHex;
|
||||
}
|
||||
|
||||
if (!isHexString(bytecodeHex)) {
|
||||
errors.throwError('bytecode must be a valid hex string', errors.INVALID_ARGUMENT, { arg: 'bytecode', value: bytecode });
|
||||
}
|
||||
|
||||
if ((bytecodeHex.length % 2) !== 0) {
|
||||
errors.throwError('bytecode must be valid data (even length)', errors.INVALID_ARGUMENT, { arg: 'bytecode', value: bytecode });
|
||||
}
|
||||
|
||||
defineReadOnly(this, 'bytecode', bytecodeHex);
|
||||
|
||||
if (Interface.isInterface(contractInterface)) {
|
||||
defineReadOnly(this, 'interface', contractInterface);
|
||||
} else {
|
||||
defineReadOnly(this, 'interface', new Interface(contractInterface));
|
||||
}
|
||||
|
||||
if (signer && !Signer.isSigner(signer)) {
|
||||
errors.throwError('invalid signer', errors.INVALID_ARGUMENT, { arg: 'signer', value: null });
|
||||
}
|
||||
|
||||
defineReadOnly(this, 'signer', signer || null);
|
||||
}
|
||||
|
||||
getDeployTransaction(...args: Array<any>): UnsignedTransaction {
|
||||
|
||||
let tx: UnsignedTransaction = { };
|
||||
|
||||
// If we have 1 additional argument, we allow transaction overrides
|
||||
if (args.length === this.interface.deployFunction.inputs.length + 1) {
|
||||
tx = shallowCopy(args.pop());
|
||||
for (let key in tx) {
|
||||
if (!allowedTransactionKeys[key]) {
|
||||
throw new Error('unknown transaction override ' + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not allow these to be overridden in a deployment transaction
|
||||
['data', 'from', 'to'].forEach((key) => {
|
||||
if ((<any>tx)[key] == null) { return; }
|
||||
errors.throwError('cannot override ' + key, errors.UNSUPPORTED_OPERATION, { operation: key })
|
||||
});
|
||||
|
||||
// Make sure the call matches the constructor signature
|
||||
errors.checkArgumentCount(args.length, this.interface.deployFunction.inputs.length, ' in Contract constructor');
|
||||
|
||||
// Set the data to the bytecode + the encoded constructor arguments
|
||||
tx.data = this.interface.deployFunction.encode(this.bytecode, args);
|
||||
|
||||
return tx
|
||||
}
|
||||
|
||||
deploy(...args: Array<any>): Promise<Contract> {
|
||||
|
||||
// Get the deployment transaction (with optional overrides)
|
||||
let tx = this.getDeployTransaction(...args);
|
||||
|
||||
// Send the deployment transaction
|
||||
return this.signer.sendTransaction(tx).then((tx) => {
|
||||
let contract = new Contract(getContractAddress(tx), this.interface, this.signer);
|
||||
defineReadOnly(contract, 'deployTransaction', tx);
|
||||
return contract;
|
||||
});
|
||||
}
|
||||
|
||||
attach(address: string): Contract {
|
||||
return new Contract(address, this.interface, this.signer);
|
||||
}
|
||||
|
||||
connect(signer: Signer) {
|
||||
return new ContractFactory(this.interface, this.bytecode, signer);
|
||||
}
|
||||
|
||||
static fromSolidity(compilerOutput: any, signer?: Signer): ContractFactory {
|
||||
if (compilerOutput == null) {
|
||||
errors.throwError('missing compiler output', errors.MISSING_ARGUMENT, { argument: 'compilerOutput' });
|
||||
}
|
||||
|
||||
if (typeof(compilerOutput) === 'string') {
|
||||
compilerOutput = JSON.parse(compilerOutput);
|
||||
}
|
||||
|
||||
let abi = compilerOutput.abi;
|
||||
|
||||
let bytecode: any = null;
|
||||
if (compilerOutput.bytecode) {
|
||||
bytecode = compilerOutput.bytecode;
|
||||
} else if (compilerOutput.evm && compilerOutput.evm.bytecode) {
|
||||
bytecode = compilerOutput.evm.bytecode;
|
||||
}
|
||||
|
||||
|
||||
return new ContractFactory(abi, bytecode, signer);
|
||||
}
|
||||
}
|
||||
|
||||
168
src.ts/errors.ts
Normal file
168
src.ts/errors.ts
Normal file
@@ -0,0 +1,168 @@
|
||||
'use strict';
|
||||
|
||||
import { version } from './_version';
|
||||
|
||||
// Unknown Error
|
||||
export const UNKNOWN_ERROR = 'UNKNOWN_ERROR';
|
||||
|
||||
// Not implemented
|
||||
export const NOT_IMPLEMENTED = 'NOT_IMPLEMENTED';
|
||||
|
||||
// Missing new operator to an object
|
||||
// - name: The name of the class
|
||||
export const MISSING_NEW = 'MISSING_NEW';
|
||||
|
||||
// Call exception
|
||||
// - transaction: the transaction
|
||||
// - address?: the contract address
|
||||
// - args?: The arguments passed into the function
|
||||
// - method?: The Solidity method signature
|
||||
// - errorSignature?: The EIP848 error signature
|
||||
// - errorArgs?: The EIP848 error parameters
|
||||
// - reason: The reason (only for EIP848 "Error(string)")
|
||||
export const CALL_EXCEPTION = 'CALL_EXCEPTION';
|
||||
|
||||
// Invalid argument (e.g. value is incompatible with type) to a function:
|
||||
// - argument: The argument name that was invalid
|
||||
// - value: The value of the argument
|
||||
export const INVALID_ARGUMENT = 'INVALID_ARGUMENT';
|
||||
|
||||
// Missing argument to a function:
|
||||
// - count: The number of arguments received
|
||||
// - expectedCount: The number of arguments expected
|
||||
export const MISSING_ARGUMENT = 'MISSING_ARGUMENT';
|
||||
|
||||
// Too many arguments
|
||||
// - count: The number of arguments received
|
||||
// - expectedCount: The number of arguments expected
|
||||
export const UNEXPECTED_ARGUMENT = 'UNEXPECTED_ARGUMENT';
|
||||
|
||||
// Numeric Fault
|
||||
// - operation: the operation being executed
|
||||
// - fault: the reason this faulted
|
||||
export const NUMERIC_FAULT = 'NUMERIC_FAULT';
|
||||
|
||||
// Insufficien funds (< value + gasLimit * gasPrice)
|
||||
// - transaction: the transaction attempted
|
||||
export const INSUFFICIENT_FUNDS = 'INSUFFICIENT_FUNDS';
|
||||
|
||||
// Nonce has already been used
|
||||
// - transaction: the transaction attempted
|
||||
export const NONCE_EXPIRED = 'NONCE_EXPIRED';
|
||||
|
||||
// The replacement fee for the transaction is too low
|
||||
// - transaction: the transaction attempted
|
||||
export const REPLACEMENT_UNDERPRICED = 'REPLACEMENT_UNDERPRICED';
|
||||
|
||||
// Unsupported operation
|
||||
// - operation
|
||||
export const UNSUPPORTED_OPERATION = 'UNSUPPORTED_OPERATION';
|
||||
|
||||
let _permanentCensorErrors = false;
|
||||
let _censorErrors = false;
|
||||
|
||||
|
||||
// @TODO: Enum
|
||||
export function throwError(message: string, code: string, params: any): never {
|
||||
if (_censorErrors) {
|
||||
throw new Error('unknown error');
|
||||
}
|
||||
|
||||
if (!code) { code = UNKNOWN_ERROR; }
|
||||
if (!params) { params = {}; }
|
||||
|
||||
let messageDetails: Array<string> = [];
|
||||
Object.keys(params).forEach((key) => {
|
||||
try {
|
||||
messageDetails.push(key + '=' + JSON.stringify(params[key]));
|
||||
} catch (error) {
|
||||
messageDetails.push(key + '=' + JSON.stringify(params[key].toString()));
|
||||
}
|
||||
});
|
||||
messageDetails.push("version=" + version);
|
||||
|
||||
let reason = message;
|
||||
if (messageDetails.length) {
|
||||
message += ' (' + messageDetails.join(', ') + ')';
|
||||
}
|
||||
|
||||
// @TODO: Any??
|
||||
let error: any = new Error(message);
|
||||
error.reason = reason;
|
||||
error.code = code
|
||||
|
||||
Object.keys(params).forEach(function(key) {
|
||||
error[key] = params[key];
|
||||
});
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
export function checkNew(self: any, kind: any): void {
|
||||
if (!(self instanceof kind)) {
|
||||
throwError('missing new', MISSING_NEW, { name: kind.name });
|
||||
}
|
||||
}
|
||||
|
||||
export function checkArgumentCount(count: number, expectedCount: number, suffix?: string): void {
|
||||
if (!suffix) { suffix = ''; }
|
||||
if (count < expectedCount) {
|
||||
throwError('missing argument' + suffix, MISSING_ARGUMENT, { count: count, expectedCount: expectedCount });
|
||||
}
|
||||
if (count > expectedCount) {
|
||||
throwError('too many arguments' + suffix, UNEXPECTED_ARGUMENT, { count: count, expectedCount: expectedCount });
|
||||
}
|
||||
}
|
||||
|
||||
export function setCensorship(censorship: boolean, permanent?: boolean): void {
|
||||
if (_permanentCensorErrors) {
|
||||
throwError('error censorship permanent', UNSUPPORTED_OPERATION, { operation: 'setCensorship' });
|
||||
}
|
||||
|
||||
_censorErrors = !!censorship;
|
||||
_permanentCensorErrors = !!permanent;
|
||||
}
|
||||
|
||||
export function checkNormalize(): void {
|
||||
try {
|
||||
// Make sure all forms of normalization are supported
|
||||
["NFD", "NFC", "NFKD", "NFKC"].forEach((form) => {
|
||||
try {
|
||||
"test".normalize(form);
|
||||
} catch(error) {
|
||||
throw new Error('missing ' + form);
|
||||
}
|
||||
});
|
||||
|
||||
if (String.fromCharCode(0xe9).normalize('NFD') !== String.fromCharCode(0x65, 0x0301)) {
|
||||
throw new Error('broken implementation')
|
||||
}
|
||||
} catch (error) {
|
||||
throwError('platform missing String.prototype.normalize', UNSUPPORTED_OPERATION, { operation: 'String.prototype.normalize', form: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
const LogLevels: { [ name: string ]: number } = { debug: 1, "default": 2, info: 2, warn: 3, error: 4, off: 5 };
|
||||
let LogLevel = LogLevels["default"];
|
||||
|
||||
export function setLogLevel(logLevel: string): void {
|
||||
let level = LogLevels[logLevel];
|
||||
if (level == null) {
|
||||
warn("invliad log level - " + logLevel);
|
||||
return;
|
||||
}
|
||||
LogLevel = level;
|
||||
}
|
||||
|
||||
function log(logLevel: string, args: Array<any>): void {
|
||||
if (LogLevel > LogLevels[logLevel]) { return; }
|
||||
console.log.apply(console, args);
|
||||
}
|
||||
|
||||
export function warn(...args: Array<any>): void {
|
||||
log("warn", args);
|
||||
}
|
||||
|
||||
export function info(...args: Array<any>): void {
|
||||
log("info", args);
|
||||
}
|
||||
84
src.ts/ethers.ts
Normal file
84
src.ts/ethers.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
'use strict';
|
||||
|
||||
import { Contract, ContractFactory, VoidSigner } from './contract';
|
||||
|
||||
import { Signer } from './abstract-signer';
|
||||
import { Wallet } from './wallet';
|
||||
|
||||
import * as constants from './constants';
|
||||
import * as errors from './errors';
|
||||
|
||||
import * as providers from './providers';
|
||||
import * as utils from './utils';
|
||||
import * as wordlists from './wordlists';
|
||||
|
||||
|
||||
////////////////////////
|
||||
// Compile-Time Constants
|
||||
|
||||
// This is empty in node, and used by browserify to inject extra goodies
|
||||
import { platform } from './utils/shims';
|
||||
|
||||
// This is generated by "npm run dist"
|
||||
import { version } from './_version';
|
||||
|
||||
|
||||
////////////////////////
|
||||
// Types
|
||||
|
||||
import { ContractFunction, ContractTransaction, Event, EventFilter } from './contract';
|
||||
|
||||
|
||||
////////////////////////
|
||||
// Helper Functions
|
||||
|
||||
function getDefaultProvider(network?: utils.Network | string): providers.BaseProvider {
|
||||
if (network == null) { network = 'homestead'; }
|
||||
let n = utils.getNetwork(network);
|
||||
if (!n || !n._defaultProvider) {
|
||||
errors.throwError('unsupported getDefaultProvider network', errors.UNSUPPORTED_OPERATION, {
|
||||
operation: 'getDefaultProvider',
|
||||
network: network
|
||||
});
|
||||
}
|
||||
return n._defaultProvider(providers);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////
|
||||
// Exports
|
||||
|
||||
export {
|
||||
Signer,
|
||||
|
||||
Wallet,
|
||||
VoidSigner,
|
||||
|
||||
getDefaultProvider,
|
||||
providers,
|
||||
|
||||
Contract,
|
||||
ContractFactory,
|
||||
|
||||
constants,
|
||||
errors,
|
||||
|
||||
utils,
|
||||
|
||||
wordlists,
|
||||
|
||||
////////////////////////
|
||||
// Compile-Time Constants
|
||||
|
||||
platform,
|
||||
version,
|
||||
|
||||
////////////////////////
|
||||
// Types
|
||||
|
||||
ContractFunction,
|
||||
ContractTransaction,
|
||||
Event,
|
||||
EventFilter
|
||||
};
|
||||
|
||||
6
src.ts/index.ts
Normal file
6
src.ts/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
import * as ethers from './ethers';
|
||||
|
||||
export { ethers };
|
||||
|
||||
export * from './ethers';
|
||||
173
src.ts/providers/abstract-provider.ts
Normal file
173
src.ts/providers/abstract-provider.ts
Normal file
@@ -0,0 +1,173 @@
|
||||
|
||||
import { BigNumber } from '../utils/bignumber';
|
||||
import { isType, setType } from '../utils/properties';
|
||||
|
||||
///////////////////////////////
|
||||
// Imported Types
|
||||
|
||||
import { Arrayish } from '../utils/bytes';
|
||||
import { BigNumberish } from '../utils/bignumber';
|
||||
import { Network } from '../utils/networks';
|
||||
import { OnceBlockable } from '../utils/web';
|
||||
import { Transaction } from '../utils/transaction';
|
||||
|
||||
///////////////////////////////
|
||||
// Exported Types
|
||||
|
||||
export interface Block {
|
||||
hash: string;
|
||||
parentHash: string;
|
||||
number: number;
|
||||
|
||||
timestamp: number;
|
||||
nonce: string;
|
||||
difficulty: number;
|
||||
|
||||
gasLimit: BigNumber;
|
||||
gasUsed: BigNumber;
|
||||
|
||||
miner: string;
|
||||
extraData: string;
|
||||
|
||||
transactions: Array<string>;
|
||||
}
|
||||
|
||||
export type BlockTag = string | number;
|
||||
|
||||
export type Filter = {
|
||||
fromBlock?: BlockTag,
|
||||
toBlock?: BlockTag,
|
||||
address?: string,
|
||||
topics?: Array<string | Array<string>>,
|
||||
}
|
||||
|
||||
// @TODO: This is not supported as an EventType yet, as it will
|
||||
// need some additional work to adhere to the serialized
|
||||
// format for events. But we want to allow it for getLogs
|
||||
// for now.
|
||||
export type FilterByBlock = {
|
||||
blockHash?: string,
|
||||
address?: string,
|
||||
topics?: Array<string | Array<string>>,
|
||||
}
|
||||
|
||||
export interface Log {
|
||||
blockNumber?: number;
|
||||
blockHash?: string;
|
||||
transactionIndex?: number;
|
||||
|
||||
removed?: boolean;
|
||||
|
||||
transactionLogIndex?: number,
|
||||
|
||||
address: string;
|
||||
data: string;
|
||||
|
||||
topics: Array<string>;
|
||||
|
||||
transactionHash?: string;
|
||||
logIndex?: number;
|
||||
}
|
||||
|
||||
export interface TransactionReceipt {
|
||||
to?: string;
|
||||
from?: string;
|
||||
contractAddress?: string,
|
||||
transactionIndex?: number,
|
||||
root?: string,
|
||||
gasUsed?: BigNumber,
|
||||
logsBloom?: string,
|
||||
blockHash?: string,
|
||||
transactionHash?: string,
|
||||
logs?: Array<Log>,
|
||||
blockNumber?: number,
|
||||
confirmations?: number,
|
||||
cumulativeGasUsed?: BigNumber,
|
||||
byzantium: boolean,
|
||||
status?: number
|
||||
};
|
||||
|
||||
export type TransactionRequest = {
|
||||
to?: string | Promise<string>,
|
||||
from?: string | Promise<string>,
|
||||
nonce?: BigNumberish | Promise<BigNumberish>,
|
||||
|
||||
gasLimit?: BigNumberish | Promise<BigNumberish>,
|
||||
gasPrice?: BigNumberish | Promise<BigNumberish>,
|
||||
|
||||
data?: Arrayish | Promise<Arrayish>,
|
||||
value?: BigNumberish | Promise<BigNumberish>,
|
||||
chainId?: number | Promise<number>,
|
||||
}
|
||||
|
||||
export interface TransactionResponse extends Transaction {
|
||||
// Only if a transaction has been mined
|
||||
blockNumber?: number,
|
||||
blockHash?: string,
|
||||
timestamp?: number,
|
||||
|
||||
confirmations: number,
|
||||
|
||||
// Not optional (as it is in Transaction)
|
||||
from: string;
|
||||
|
||||
// The raw transaction
|
||||
raw?: string,
|
||||
|
||||
// This function waits until the transaction has been mined
|
||||
wait: (confirmations?: number) => Promise<TransactionReceipt>
|
||||
};
|
||||
|
||||
export type EventType = string | Array<string> | Filter;
|
||||
|
||||
export type Listener = (...args: Array<any>) => void;
|
||||
|
||||
///////////////////////////////
|
||||
// Exported Abstracts
|
||||
|
||||
export abstract class Provider implements OnceBlockable {
|
||||
abstract getNetwork(): Promise<Network>;
|
||||
|
||||
abstract getBlockNumber(): Promise<number>;
|
||||
abstract getGasPrice(): Promise<BigNumber>;
|
||||
|
||||
abstract getBalance(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<BigNumber>;
|
||||
abstract getTransactionCount(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<number>;
|
||||
abstract getCode(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<string> ;
|
||||
abstract getStorageAt(addressOrName: string | Promise<string>, position: BigNumberish | Promise<BigNumberish>, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
|
||||
|
||||
abstract sendTransaction(signedTransaction: string | Promise<string>): Promise<TransactionResponse>;
|
||||
abstract call(transaction: TransactionRequest, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
|
||||
abstract estimateGas(transaction: TransactionRequest): Promise<BigNumber>;
|
||||
|
||||
abstract getBlock(blockHashOrBlockTag: BlockTag | string | Promise<BlockTag | string>, includeTransactions?: boolean): Promise<Block>;
|
||||
abstract getTransaction(transactionHash: string): Promise<TransactionResponse>;
|
||||
abstract getTransactionReceipt(transactionHash: string): Promise<TransactionReceipt>;
|
||||
|
||||
abstract getLogs(filter: Filter | FilterByBlock): Promise<Array<Log>>;
|
||||
|
||||
abstract resolveName(name: string | Promise<string>): Promise<string>;
|
||||
abstract lookupAddress(address: string | Promise<string>): Promise<string>;
|
||||
abstract on(eventName: EventType, listener: Listener): Provider;
|
||||
abstract once(eventName: EventType, listener: Listener): Provider;
|
||||
abstract listenerCount(eventName?: EventType): number;
|
||||
abstract listeners(eventName: EventType): Array<Listener>;
|
||||
abstract removeAllListeners(eventName: EventType): Provider;
|
||||
abstract removeListener(eventName: EventType, listener: Listener): Provider;
|
||||
|
||||
// @TODO: This *could* be implemented here, but would pull in events...
|
||||
abstract waitForTransaction(transactionHash: string, timeout?: number): Promise<TransactionReceipt>;
|
||||
|
||||
constructor() {
|
||||
setType(this, 'Provider');
|
||||
}
|
||||
|
||||
static isProvider(value: any): value is Provider {
|
||||
return isType(value, 'Provider');
|
||||
}
|
||||
|
||||
// readonly inherits: (child: any) => void;
|
||||
}
|
||||
|
||||
//defineReadOnly(Signer, 'inherits', inheritable(Abstract));
|
||||
|
||||
1363
src.ts/providers/base-provider.ts
Normal file
1363
src.ts/providers/base-provider.ts
Normal file
File diff suppressed because it is too large
Load Diff
351
src.ts/providers/etherscan-provider.ts
Normal file
351
src.ts/providers/etherscan-provider.ts
Normal file
@@ -0,0 +1,351 @@
|
||||
|
||||
import { BaseProvider } from './base-provider';
|
||||
|
||||
import { hexlify, hexStripZeros } from '../utils/bytes';
|
||||
import { defineReadOnly } from '../utils/properties';
|
||||
import { fetchJson } from '../utils/web';
|
||||
|
||||
import * as errors from '../errors';
|
||||
|
||||
///////////////////////////////
|
||||
// Imported Types
|
||||
|
||||
import { BlockTag, TransactionRequest, TransactionResponse } from './abstract-provider';
|
||||
|
||||
import { Networkish } from '../utils/networks';
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
// The transaction has already been sanitized by the calls in Provider
|
||||
function getTransactionString(transaction: TransactionRequest): string {
|
||||
var result = [];
|
||||
for (var key in transaction) {
|
||||
if ((<any>transaction)[key] == null) { continue; }
|
||||
var value = hexlify((<any>transaction)[key]);
|
||||
if ((<any>{ gasLimit: true, gasPrice: true, nonce: true, value: true })[key]) {
|
||||
value = hexStripZeros(value);
|
||||
}
|
||||
result.push(key + '=' + value);
|
||||
}
|
||||
return result.join('&');
|
||||
}
|
||||
|
||||
function getResult(result: { status?: number, message?: string, result?: any }): any {
|
||||
// getLogs, getHistory have weird success responses
|
||||
if (result.status == 0 && (result.message === 'No records found' || result.message === 'No transactions found')) {
|
||||
return result.result;
|
||||
}
|
||||
|
||||
if (result.status != 1 || result.message != 'OK') {
|
||||
// @TODO: not any
|
||||
var error: any = new Error('invalid response');
|
||||
error.result = JSON.stringify(result);
|
||||
throw error;
|
||||
}
|
||||
|
||||
return result.result;
|
||||
}
|
||||
|
||||
function getJsonResult(result: { jsonrpc: string, result?: any, error?: { code?: number, data?: any, message?: string} } ): any {
|
||||
if (result.jsonrpc != '2.0') {
|
||||
// @TODO: not any
|
||||
let error: any = new Error('invalid response');
|
||||
error.result = JSON.stringify(result);
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (result.error) {
|
||||
// @TODO: not any
|
||||
let error: any = new Error(result.error.message || 'unknown error');
|
||||
if (result.error.code) { error.code = result.error.code; }
|
||||
if (result.error.data) { error.data = result.error.data; }
|
||||
throw error;
|
||||
}
|
||||
|
||||
return result.result;
|
||||
}
|
||||
|
||||
// The blockTag was normalized as a string by the Provider pre-perform operations
|
||||
function checkLogTag(blockTag: string): number | "latest" {
|
||||
if (blockTag === 'pending') { throw new Error('pending not supported'); }
|
||||
if (blockTag === 'latest') { return blockTag; }
|
||||
|
||||
return parseInt(blockTag.substring(2), 16);
|
||||
}
|
||||
|
||||
|
||||
export class EtherscanProvider extends BaseProvider{
|
||||
readonly baseUrl: string;
|
||||
readonly apiKey: string;
|
||||
constructor(network?: Networkish, apiKey?: string) {
|
||||
super(network);
|
||||
errors.checkNew(this, EtherscanProvider);
|
||||
|
||||
let name = 'invalid';
|
||||
if (this.network) { name = this.network.name; }
|
||||
|
||||
let baseUrl = null;
|
||||
switch(name) {
|
||||
case 'homestead':
|
||||
baseUrl = 'https://api.etherscan.io';
|
||||
break;
|
||||
case 'ropsten':
|
||||
baseUrl = 'https://api-ropsten.etherscan.io';
|
||||
break;
|
||||
case 'rinkeby':
|
||||
baseUrl = 'https://api-rinkeby.etherscan.io';
|
||||
break;
|
||||
case 'kovan':
|
||||
baseUrl = 'https://api-kovan.etherscan.io';
|
||||
break;
|
||||
case 'goerli':
|
||||
baseUrl = 'https://api-goerli.etherscan.io';
|
||||
break;
|
||||
default:
|
||||
throw new Error('unsupported network');
|
||||
}
|
||||
|
||||
defineReadOnly(this, 'baseUrl', baseUrl);
|
||||
defineReadOnly(this, 'apiKey', apiKey);
|
||||
}
|
||||
|
||||
|
||||
perform(method: string, params: any) {
|
||||
let url = this.baseUrl;
|
||||
|
||||
let apiKey = '';
|
||||
if (this.apiKey) { apiKey += '&apikey=' + this.apiKey; }
|
||||
|
||||
let get = (url: string, procFunc?: (value: any) => any) => {
|
||||
return fetchJson(url, null, procFunc || getJsonResult).then((result) => {
|
||||
this.emit('debug', {
|
||||
action: 'perform',
|
||||
request: url,
|
||||
response: result,
|
||||
provider: this
|
||||
});
|
||||
return result;
|
||||
});
|
||||
};
|
||||
|
||||
switch (method) {
|
||||
case 'getBlockNumber':
|
||||
url += '/api?module=proxy&action=eth_blockNumber' + apiKey;
|
||||
return get(url);
|
||||
|
||||
case 'getGasPrice':
|
||||
url += '/api?module=proxy&action=eth_gasPrice' + apiKey;
|
||||
return get(url);
|
||||
|
||||
case 'getBalance':
|
||||
// Returns base-10 result
|
||||
url += '/api?module=account&action=balance&address=' + params.address;
|
||||
url += '&tag=' + params.blockTag + apiKey;
|
||||
return get(url, getResult);
|
||||
|
||||
case 'getTransactionCount':
|
||||
url += '/api?module=proxy&action=eth_getTransactionCount&address=' + params.address;
|
||||
url += '&tag=' + params.blockTag + apiKey;
|
||||
return get(url);
|
||||
|
||||
|
||||
case 'getCode':
|
||||
url += '/api?module=proxy&action=eth_getCode&address=' + params.address;
|
||||
url += '&tag=' + params.blockTag + apiKey;
|
||||
return get(url, getJsonResult);
|
||||
|
||||
case 'getStorageAt':
|
||||
url += '/api?module=proxy&action=eth_getStorageAt&address=' + params.address;
|
||||
url += '&position=' + params.position;
|
||||
url += '&tag=' + params.blockTag + apiKey;
|
||||
return get(url, getJsonResult);
|
||||
|
||||
|
||||
case 'sendTransaction':
|
||||
url += '/api?module=proxy&action=eth_sendRawTransaction&hex=' + params.signedTransaction;
|
||||
url += apiKey;
|
||||
return get(url).catch((error) => {
|
||||
if (error.responseText) {
|
||||
// "Insufficient funds. The account you tried to send transaction from does not have enough funds. Required 21464000000000 and got: 0"
|
||||
if (error.responseText.toLowerCase().indexOf('insufficient funds') >= 0) {
|
||||
errors.throwError('insufficient funds', errors.INSUFFICIENT_FUNDS, { });
|
||||
}
|
||||
// "Transaction with the same hash was already imported."
|
||||
if (error.responseText.indexOf('same hash was already imported') >= 0) {
|
||||
errors.throwError('nonce has already been used', errors.NONCE_EXPIRED, { });
|
||||
}
|
||||
// "Transaction gas price is too low. There is another transaction with same nonce in the queue. Try increasing the gas price or incrementing the nonce."
|
||||
if (error.responseText.indexOf('another transaction with same nonce') >= 0) {
|
||||
errors.throwError('replacement fee too low', errors.REPLACEMENT_UNDERPRICED, { });
|
||||
}
|
||||
}
|
||||
throw error;
|
||||
});
|
||||
|
||||
case 'getBlock':
|
||||
if (params.blockTag) {
|
||||
url += '/api?module=proxy&action=eth_getBlockByNumber&tag=' + params.blockTag;
|
||||
if (params.includeTransactions) {
|
||||
url += '&boolean=true';
|
||||
} else {
|
||||
url += '&boolean=false';
|
||||
}
|
||||
url += apiKey;
|
||||
return get(url);
|
||||
}
|
||||
throw new Error('getBlock by blockHash not implmeneted');
|
||||
|
||||
case 'getTransaction':
|
||||
url += '/api?module=proxy&action=eth_getTransactionByHash&txhash=' + params.transactionHash;
|
||||
url += apiKey;
|
||||
return get(url);
|
||||
|
||||
case 'getTransactionReceipt':
|
||||
url += '/api?module=proxy&action=eth_getTransactionReceipt&txhash=' + params.transactionHash;
|
||||
url += apiKey;
|
||||
return get(url);
|
||||
|
||||
|
||||
case 'call': {
|
||||
let transaction = getTransactionString(params.transaction);
|
||||
if (transaction) { transaction = '&' + transaction; }
|
||||
url += '/api?module=proxy&action=eth_call' + transaction;
|
||||
//url += '&tag=' + params.blockTag + apiKey;
|
||||
if (params.blockTag !== 'latest') {
|
||||
throw new Error('EtherscanProvider does not support blockTag for call');
|
||||
}
|
||||
url += apiKey;
|
||||
return get(url);
|
||||
}
|
||||
|
||||
case 'estimateGas': {
|
||||
let transaction = getTransactionString(params.transaction);
|
||||
if (transaction) { transaction = '&' + transaction; }
|
||||
url += '/api?module=proxy&action=eth_estimateGas&' + transaction;
|
||||
url += apiKey;
|
||||
return get(url);
|
||||
}
|
||||
|
||||
case 'getLogs':
|
||||
url += '/api?module=logs&action=getLogs';
|
||||
try {
|
||||
if (params.filter.fromBlock) {
|
||||
url += '&fromBlock=' + checkLogTag(params.filter.fromBlock);
|
||||
}
|
||||
|
||||
if (params.filter.toBlock) {
|
||||
url += '&toBlock=' + checkLogTag(params.filter.toBlock);
|
||||
}
|
||||
|
||||
if (params.filter.blockHash) {
|
||||
try {
|
||||
errors.throwError("Etherscan does not support blockHash filters", errors.UNSUPPORTED_OPERATION, {
|
||||
operation: "getLogs(blockHash)"
|
||||
});
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (params.filter.address) {
|
||||
url += '&address=' + params.filter.address;
|
||||
}
|
||||
|
||||
// @TODO: We can handle slightly more complicated logs using the logs API
|
||||
if (params.filter.topics && params.filter.topics.length > 0) {
|
||||
if (params.filter.topics.length > 1) {
|
||||
throw new Error('unsupported topic format');
|
||||
}
|
||||
let topic0 = params.filter.topics[0];
|
||||
if (typeof(topic0) !== 'string' || topic0.length !== 66) {
|
||||
throw new Error('unsupported topic0 format');
|
||||
}
|
||||
url += '&topic0=' + topic0;
|
||||
}
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
url += apiKey;
|
||||
|
||||
var self = this;
|
||||
return get(url, getResult).then(function(logs: Array<any>) {
|
||||
var txs: { [hash: string]: string } = {};
|
||||
|
||||
var seq = Promise.resolve();
|
||||
logs.forEach(function(log) {
|
||||
seq = seq.then(function() {
|
||||
if (log.blockHash != null) { return null; }
|
||||
log.blockHash = txs[log.transactionHash];
|
||||
if (log.blockHash == null) {
|
||||
return self.getTransaction(log.transactionHash).then(function(tx) {
|
||||
txs[log.transactionHash] = tx.blockHash;
|
||||
log.blockHash = tx.blockHash;
|
||||
return null;
|
||||
});
|
||||
}
|
||||
return null;
|
||||
});
|
||||
});
|
||||
|
||||
return seq.then(function() {
|
||||
return logs;
|
||||
});
|
||||
});
|
||||
|
||||
case 'getEtherPrice':
|
||||
if (this.network.name !== 'homestead') { return Promise.resolve(0.0); }
|
||||
url += '/api?module=stats&action=ethprice';
|
||||
url += apiKey;
|
||||
return get(url, getResult).then(function(result) {
|
||||
return parseFloat(result.ethusd);
|
||||
});
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return super.perform(method, params);
|
||||
}
|
||||
|
||||
// @TODO: Allow startBlock and endBlock to be Promises
|
||||
getHistory(addressOrName: string | Promise<string>, startBlock?: BlockTag, endBlock?: BlockTag): Promise<Array<TransactionResponse>> {
|
||||
|
||||
let url = this.baseUrl;
|
||||
|
||||
let apiKey = '';
|
||||
if (this.apiKey) { apiKey += '&apikey=' + this.apiKey; }
|
||||
|
||||
if (startBlock == null) { startBlock = 0; }
|
||||
if (endBlock == null) { endBlock = 99999999; }
|
||||
|
||||
return this.resolveName(addressOrName).then((address) => {
|
||||
url += '/api?module=account&action=txlist&address=' + address;
|
||||
url += '&startblock=' + startBlock;
|
||||
url += '&endblock=' + endBlock;
|
||||
url += '&sort=asc' + apiKey;
|
||||
|
||||
return fetchJson(url, null, getResult).then((result: Array<any>) => {
|
||||
this.emit('debug', {
|
||||
action: 'getHistory',
|
||||
request: url,
|
||||
response: result,
|
||||
provider: this
|
||||
});
|
||||
var output: Array<TransactionResponse> = [];
|
||||
result.forEach((tx) => {
|
||||
['contractAddress', 'to'].forEach(function(key) {
|
||||
if (tx[key] == '') { delete tx[key]; }
|
||||
});
|
||||
if (tx.creates == null && tx.contractAddress != null) {
|
||||
tx.creates = tx.contractAddress;
|
||||
}
|
||||
let item = BaseProvider.checkTransactionResponse(tx);
|
||||
if (tx.timeStamp) { item.timestamp = parseInt(tx.timeStamp); }
|
||||
output.push(item);
|
||||
});
|
||||
return output;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
106
src.ts/providers/fallback-provider.ts
Normal file
106
src.ts/providers/fallback-provider.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
'use strict';
|
||||
|
||||
import { BaseProvider } from './base-provider';
|
||||
|
||||
// Imported Types
|
||||
import { Network } from '../utils/networks';
|
||||
|
||||
import * as errors from '../errors';
|
||||
|
||||
// Returns:
|
||||
// - true is all networks match
|
||||
// - false if any network is null
|
||||
// - throws if any 2 networks do not match
|
||||
function checkNetworks(networks: Array<Network>): boolean {
|
||||
var result = true;
|
||||
|
||||
let check: Network = null;
|
||||
networks.forEach((network) => {
|
||||
|
||||
// Null
|
||||
if (network == null) {
|
||||
result = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Have nothing to compre to yet
|
||||
if (check == null) {
|
||||
check = network;
|
||||
return;
|
||||
}
|
||||
|
||||
// Matches!
|
||||
if (check.name === network.name &&
|
||||
check.chainId === network.chainId &&
|
||||
((check.ensAddress === network.ensAddress) ||
|
||||
(check.ensAddress == null && network.ensAddress == null))) { return; }
|
||||
|
||||
errors.throwError(
|
||||
'provider mismatch',
|
||||
errors.INVALID_ARGUMENT,
|
||||
{ arg: 'networks', value: networks }
|
||||
);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export class FallbackProvider extends BaseProvider {
|
||||
private _providers: Array<BaseProvider>;
|
||||
|
||||
constructor(providers: Array<BaseProvider>) {
|
||||
|
||||
if (providers.length === 0) { throw new Error('no providers'); }
|
||||
|
||||
// All networks are ready, we can know the network for certain
|
||||
let ready = checkNetworks(providers.map((p) => p.network));
|
||||
if (ready) {
|
||||
super(providers[0].network);
|
||||
|
||||
} else {
|
||||
// The network won't be known until all child providers know
|
||||
let ready = Promise.all(providers.map((p) => p.getNetwork())).then((networks) => {
|
||||
if (!checkNetworks(networks)) {
|
||||
errors.throwError('getNetwork returned null', errors.UNKNOWN_ERROR, { })
|
||||
}
|
||||
return networks[0];
|
||||
});
|
||||
|
||||
super(ready);
|
||||
}
|
||||
errors.checkNew(this, FallbackProvider);
|
||||
|
||||
// Preserve a copy, so we don't get mutated
|
||||
this._providers = providers.slice(0);
|
||||
}
|
||||
|
||||
get providers(): Array<BaseProvider> {
|
||||
// Return a copy, so we don't get mutated
|
||||
return this._providers.slice(0);
|
||||
}
|
||||
|
||||
perform(method: string, params: { [name: string]: any }): any {
|
||||
// Creates a copy of the providers array
|
||||
var providers = this.providers;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
var firstError: Error = null;
|
||||
function next() {
|
||||
if (!providers.length) {
|
||||
reject(firstError);
|
||||
return;
|
||||
}
|
||||
|
||||
var provider = providers.shift();
|
||||
provider.perform(method, params).then((result) => {
|
||||
return resolve(result);
|
||||
}).catch((error) => {
|
||||
if (!firstError) { firstError = error; }
|
||||
setTimeout(next, 0);
|
||||
});
|
||||
}
|
||||
next();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
77
src.ts/providers/index.ts
Normal file
77
src.ts/providers/index.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
'use strict';
|
||||
|
||||
import { Provider } from './abstract-provider';
|
||||
|
||||
import { BaseProvider } from './base-provider';
|
||||
|
||||
import { EtherscanProvider } from './etherscan-provider';
|
||||
import { FallbackProvider } from './fallback-provider';
|
||||
import { IpcProvider } from './ipc-provider';
|
||||
import { InfuraProvider } from './infura-provider';
|
||||
import { JsonRpcProvider, JsonRpcSigner } from './json-rpc-provider';
|
||||
import { Web3Provider } from './web3-provider';
|
||||
|
||||
////////////////////////
|
||||
// Types
|
||||
|
||||
import {
|
||||
Block,
|
||||
BlockTag,
|
||||
EventType,
|
||||
Filter,
|
||||
Log,
|
||||
Listener,
|
||||
TransactionReceipt,
|
||||
TransactionRequest,
|
||||
TransactionResponse
|
||||
} from './abstract-provider';
|
||||
|
||||
import { AsyncSendable } from './web3-provider';
|
||||
|
||||
|
||||
////////////////////////
|
||||
// Exports
|
||||
|
||||
export {
|
||||
|
||||
///////////////////////
|
||||
// Abstract Providers (or Abstract-ish)
|
||||
Provider,
|
||||
BaseProvider,
|
||||
|
||||
|
||||
///////////////////////
|
||||
// Concreate Providers
|
||||
|
||||
FallbackProvider,
|
||||
|
||||
EtherscanProvider,
|
||||
InfuraProvider,
|
||||
JsonRpcProvider,
|
||||
Web3Provider,
|
||||
|
||||
IpcProvider,
|
||||
|
||||
|
||||
///////////////////////
|
||||
// Signer
|
||||
|
||||
JsonRpcSigner,
|
||||
|
||||
|
||||
///////////////////////
|
||||
// Types
|
||||
|
||||
Block,
|
||||
BlockTag,
|
||||
EventType,
|
||||
Filter,
|
||||
Log,
|
||||
Listener,
|
||||
TransactionReceipt,
|
||||
TransactionRequest,
|
||||
TransactionResponse,
|
||||
|
||||
AsyncSendable
|
||||
};
|
||||
|
||||
80
src.ts/providers/infura-provider.ts
Normal file
80
src.ts/providers/infura-provider.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
'use strict';
|
||||
|
||||
import { JsonRpcProvider, JsonRpcSigner } from './json-rpc-provider';
|
||||
|
||||
import { isHexString } from "../utils/bytes";
|
||||
import { getNetwork } from '../utils/networks';
|
||||
import { defineReadOnly } from '../utils/properties';
|
||||
|
||||
// Imported Types
|
||||
import { Networkish } from '../utils/networks';
|
||||
|
||||
import * as errors from '../errors';
|
||||
|
||||
const defaultProjectId = "7d0d81d0919f4f05b9ab6634be01ee73";
|
||||
|
||||
export class InfuraProvider extends JsonRpcProvider {
|
||||
readonly apiAccessToken: string;
|
||||
readonly projectId: string;
|
||||
|
||||
constructor(network?: Networkish, projectId?: string) {
|
||||
let standard = getNetwork((network == null) ? 'homestead': network);
|
||||
if (projectId == null) { projectId = defaultProjectId; }
|
||||
|
||||
let host = null;
|
||||
switch(standard.name) {
|
||||
case 'homestead':
|
||||
host = 'mainnet.infura.io';
|
||||
break;
|
||||
case 'ropsten':
|
||||
host = 'ropsten.infura.io';
|
||||
break;
|
||||
case 'rinkeby':
|
||||
host = 'rinkeby.infura.io';
|
||||
break;
|
||||
case 'goerli':
|
||||
host = 'goerli.infura.io';
|
||||
break;
|
||||
case 'kovan':
|
||||
host = 'kovan.infura.io';
|
||||
break;
|
||||
default:
|
||||
errors.throwError('unsupported network', errors.INVALID_ARGUMENT, {
|
||||
argument: "network",
|
||||
value: network
|
||||
});
|
||||
}
|
||||
|
||||
// New-style Project ID
|
||||
if (isHexString("0x" + projectId, 16)) {
|
||||
super('https://' + host + '/v3/' + projectId, standard);
|
||||
defineReadOnly(this, 'apiAccessToken', null);
|
||||
defineReadOnly(this, 'projectId', projectId);
|
||||
|
||||
// Legacy API Access Token
|
||||
} else {
|
||||
errors.warn("The legacy INFURA apiAccesToken API is deprecated; please upgrade to a Project ID instead (see INFURA dshboard; https://infura.io)");
|
||||
super('https://' + host + '/' + projectId, standard);
|
||||
defineReadOnly(this, 'apiAccessToken', projectId);
|
||||
defineReadOnly(this, 'projectId', null);
|
||||
}
|
||||
|
||||
errors.checkNew(this, InfuraProvider);
|
||||
}
|
||||
|
||||
protected _startPending(): void {
|
||||
errors.warn('WARNING: INFURA does not support pending filters');
|
||||
}
|
||||
|
||||
getSigner(address?: string): JsonRpcSigner {
|
||||
return errors.throwError(
|
||||
'INFURA does not support signing',
|
||||
errors.UNSUPPORTED_OPERATION,
|
||||
{ operation: 'getSigner' }
|
||||
);
|
||||
}
|
||||
|
||||
listAccounts(): Promise<Array<string>> {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
}
|
||||
75
src.ts/providers/ipc-provider.ts
Normal file
75
src.ts/providers/ipc-provider.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
"use strict";
|
||||
|
||||
import net from 'net';
|
||||
|
||||
import { JsonRpcProvider } from './json-rpc-provider';
|
||||
|
||||
import { defineReadOnly } from '../utils/properties';
|
||||
|
||||
// Imported Types
|
||||
import { Networkish } from '../utils/networks';
|
||||
|
||||
import * as errors from '../errors';
|
||||
|
||||
|
||||
export class IpcProvider extends JsonRpcProvider {
|
||||
readonly path: string;
|
||||
|
||||
constructor(path: string, network?: Networkish) {
|
||||
if (path == null) {
|
||||
errors.throwError('missing path', errors.MISSING_ARGUMENT, {
|
||||
argument: 'path'
|
||||
});
|
||||
}
|
||||
|
||||
super('ipc://' + path, network);
|
||||
errors.checkNew(this, IpcProvider);
|
||||
|
||||
defineReadOnly(this, 'path', path);
|
||||
}
|
||||
|
||||
// @TODO: Create a connection to the IPC path and use filters instead of polling for block
|
||||
|
||||
send(method: string, params: any): Promise<any> {
|
||||
// This method is very simple right now. We create a new socket
|
||||
// connection each time, which may be slower, but the main
|
||||
// advantage we are aiming for now is security. This simplifies
|
||||
// multiplexing requests (since we do not need to multiplex).
|
||||
|
||||
let payload = JSON.stringify({
|
||||
method: method,
|
||||
params: params,
|
||||
id: 42,
|
||||
jsonrpc: "2.0"
|
||||
});
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let response = Buffer.alloc(0);
|
||||
|
||||
let stream = net.connect(this.path);
|
||||
|
||||
stream.on('data', (data) => {
|
||||
response = Buffer.concat([ response, data ]);
|
||||
});
|
||||
|
||||
stream.on("end", () => {
|
||||
try {
|
||||
resolve(JSON.parse(response.toString('utf8')).result);
|
||||
// @TODO: Better pull apart the error
|
||||
stream.destroy();
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
stream.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
stream.on('error', (error) => {
|
||||
reject(error);
|
||||
stream.destroy();
|
||||
});
|
||||
|
||||
stream.write(payload);
|
||||
stream.end();
|
||||
});
|
||||
}
|
||||
}
|
||||
427
src.ts/providers/json-rpc-provider.ts
Normal file
427
src.ts/providers/json-rpc-provider.ts
Normal file
@@ -0,0 +1,427 @@
|
||||
'use strict';
|
||||
|
||||
// See: https://github.com/ethereum/wiki/wiki/JSON-RPC
|
||||
|
||||
import { BaseProvider } from './base-provider';
|
||||
|
||||
import { Signer } from '../abstract-signer';
|
||||
|
||||
import * as errors from '../errors';
|
||||
|
||||
import { getAddress } from '../utils/address';
|
||||
import { BigNumber } from '../utils/bignumber';
|
||||
import { hexlify, hexStripZeros } from '../utils/bytes';
|
||||
import { getNetwork } from '../utils/networks';
|
||||
import { checkProperties, defineReadOnly, resolveProperties, shallowCopy } from '../utils/properties';
|
||||
import { toUtf8Bytes } from '../utils/utf8';
|
||||
import { fetchJson, poll } from '../utils/web';
|
||||
|
||||
// Imported Types
|
||||
import { Arrayish } from '../utils/bytes';
|
||||
import { Network, Networkish } from '../utils/networks';
|
||||
import { ConnectionInfo } from '../utils/web';
|
||||
|
||||
import { BlockTag, TransactionRequest, TransactionResponse } from '../providers/abstract-provider';
|
||||
|
||||
function timer(timeout: number): Promise<any> {
|
||||
return new Promise(function(resolve) {
|
||||
setTimeout(function() {
|
||||
resolve();
|
||||
}, timeout);
|
||||
});
|
||||
}
|
||||
|
||||
function getResult(payload: { error?: { code?: number, data?: any, message?: string }, result?: any }): any {
|
||||
if (payload.error) {
|
||||
// @TODO: not any
|
||||
let error: any = new Error(payload.error.message);
|
||||
error.code = payload.error.code;
|
||||
error.data = payload.error.data;
|
||||
throw error;
|
||||
}
|
||||
|
||||
return payload.result;
|
||||
}
|
||||
|
||||
function getLowerCase(value: string): string {
|
||||
if (value) { return value.toLowerCase(); }
|
||||
return value;
|
||||
}
|
||||
|
||||
const _constructorGuard = {};
|
||||
|
||||
// Some environments (Trust Wallet and company) use a global map
|
||||
// to track JSON-RPC ID, so we try to keep IDs unique across all
|
||||
// connections. See #489.
|
||||
let _nextId = 42;
|
||||
|
||||
export class JsonRpcSigner extends Signer {
|
||||
readonly provider: JsonRpcProvider;
|
||||
private _index: number;
|
||||
private _address: string;
|
||||
|
||||
constructor(constructorGuard: any, provider: JsonRpcProvider, addressOrIndex?: string | number) {
|
||||
super();
|
||||
errors.checkNew(this, JsonRpcSigner);
|
||||
|
||||
if (constructorGuard !== _constructorGuard) {
|
||||
throw new Error('do not call the JsonRpcSigner constructor directly; use provider.getSigner');
|
||||
}
|
||||
|
||||
defineReadOnly(this, 'provider', provider);
|
||||
|
||||
// Statically attach to a given address
|
||||
if (addressOrIndex) {
|
||||
if (typeof(addressOrIndex) === 'string') {
|
||||
defineReadOnly(this, '_address', getAddress(addressOrIndex));
|
||||
} else if (typeof(addressOrIndex) === 'number') {
|
||||
defineReadOnly(this, '_index', addressOrIndex);
|
||||
} else {
|
||||
errors.throwError('invalid address or index', errors.INVALID_ARGUMENT, { argument: 'addressOrIndex', value: addressOrIndex });
|
||||
}
|
||||
} else {
|
||||
defineReadOnly(this, '_index', 0);
|
||||
}
|
||||
}
|
||||
|
||||
getAddress(): Promise<string> {
|
||||
if (this._address) {
|
||||
return Promise.resolve(this._address);
|
||||
}
|
||||
|
||||
return this.provider.send('eth_accounts', []).then((accounts) => {
|
||||
if (accounts.length <= this._index) {
|
||||
errors.throwError('unknown account #' + this._index, errors.UNSUPPORTED_OPERATION, { operation: 'getAddress' });
|
||||
}
|
||||
this._address = getAddress(accounts[this._index]);
|
||||
return this._address;
|
||||
});
|
||||
}
|
||||
|
||||
getBalance(blockTag?: BlockTag): Promise<BigNumber> {
|
||||
return this.provider.getBalance(this.getAddress(), blockTag);
|
||||
}
|
||||
|
||||
getTransactionCount(blockTag?: BlockTag): Promise<number> {
|
||||
return this.provider.getTransactionCount(this.getAddress(), blockTag);
|
||||
}
|
||||
|
||||
sendUncheckedTransaction(transaction: TransactionRequest): Promise<string> {
|
||||
transaction = shallowCopy(transaction);
|
||||
|
||||
let fromAddress = this.getAddress().then((address) => {
|
||||
if (address) { address = address.toLowerCase(); }
|
||||
return address;
|
||||
});
|
||||
|
||||
// The JSON-RPC for eth_sendTransaction uses 90000 gas; if the user
|
||||
// wishes to use this, it is easy to specify explicitly, otherwise
|
||||
// we look it up for them.
|
||||
if (transaction.gasLimit == null) {
|
||||
let estimate = shallowCopy(transaction);
|
||||
estimate.from = fromAddress;
|
||||
transaction.gasLimit = this.provider.estimateGas(estimate);
|
||||
}
|
||||
|
||||
return Promise.all([
|
||||
resolveProperties(transaction),
|
||||
fromAddress
|
||||
]).then((results) => {
|
||||
let tx = results[0];
|
||||
let hexTx = JsonRpcProvider.hexlifyTransaction(tx);
|
||||
hexTx.from = results[1];
|
||||
return this.provider.send('eth_sendTransaction', [ hexTx ]).then((hash) => {
|
||||
return hash;
|
||||
}, (error) => {
|
||||
if (error.responseText) {
|
||||
// See: JsonRpcProvider.sendTransaction (@TODO: Expose a ._throwError??)
|
||||
if (error.responseText.indexOf('insufficient funds') >= 0) {
|
||||
errors.throwError('insufficient funds', errors.INSUFFICIENT_FUNDS, {
|
||||
transaction: tx
|
||||
});
|
||||
}
|
||||
if (error.responseText.indexOf('nonce too low') >= 0) {
|
||||
errors.throwError('nonce has already been used', errors.NONCE_EXPIRED, {
|
||||
transaction: tx
|
||||
});
|
||||
}
|
||||
if (error.responseText.indexOf('replacement transaction underpriced') >= 0) {
|
||||
errors.throwError('replacement fee too low', errors.REPLACEMENT_UNDERPRICED, {
|
||||
transaction: tx
|
||||
});
|
||||
}
|
||||
}
|
||||
throw error;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse> {
|
||||
return this.sendUncheckedTransaction(transaction).then((hash) => {
|
||||
return poll(() => {
|
||||
return this.provider.getTransaction(hash).then((tx: TransactionResponse) => {
|
||||
if (tx === null) { return undefined; }
|
||||
return this.provider._wrapTransaction(tx, hash);
|
||||
});
|
||||
}, { fastRetry: 250, onceBlock: this.provider }).catch((error: Error) => {
|
||||
(<any>error).transactionHash = hash;
|
||||
throw error;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
signMessage(message: Arrayish | string): Promise<string> {
|
||||
let data = ((typeof(message) === 'string') ? toUtf8Bytes(message): message);
|
||||
return this.getAddress().then((address) => {
|
||||
|
||||
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign
|
||||
return this.provider.send('eth_sign', [ address.toLowerCase(), hexlify(data) ]);
|
||||
});
|
||||
}
|
||||
|
||||
unlock(password: string): Promise<boolean> {
|
||||
let provider = this.provider;
|
||||
|
||||
return this.getAddress().then(function(address) {
|
||||
return provider.send('personal_unlockAccount', [ address.toLowerCase(), password, null ]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const allowedTransactionKeys: { [ key: string ]: boolean } = {
|
||||
chainId: true, data: true, gasLimit: true, gasPrice:true, nonce: true, to: true, value: true
|
||||
}
|
||||
|
||||
export class JsonRpcProvider extends BaseProvider {
|
||||
readonly connection: ConnectionInfo;
|
||||
|
||||
private _pendingFilter: Promise<number>;
|
||||
|
||||
constructor(url?: ConnectionInfo | string, network?: Networkish) {
|
||||
|
||||
// One parameter, but it is a network name, so swap it with the URL
|
||||
if (typeof(url) === 'string') {
|
||||
if (network === null && getNetwork(url)) {
|
||||
network = url;
|
||||
url = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (network) {
|
||||
// The network has been specified explicitly, we can use it
|
||||
super(network);
|
||||
|
||||
} else {
|
||||
|
||||
// The network is unknown, query the JSON-RPC for it
|
||||
let ready: Promise<Network> = new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
this.send('net_version', [ ]).then((result) => {
|
||||
return resolve(getNetwork(parseInt(result)));
|
||||
}).catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
});
|
||||
super(ready);
|
||||
}
|
||||
|
||||
errors.checkNew(this, JsonRpcProvider);
|
||||
|
||||
// Default URL
|
||||
if (!url) { url = 'http://localhost:8545'; }
|
||||
|
||||
if (typeof(url) === 'string') {
|
||||
this.connection = {
|
||||
url: url
|
||||
};
|
||||
} else {
|
||||
this.connection = url;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
getSigner(addressOrIndex?: string | number): JsonRpcSigner {
|
||||
return new JsonRpcSigner(_constructorGuard, this, addressOrIndex);
|
||||
}
|
||||
|
||||
listAccounts(): Promise<Array<string>> {
|
||||
return this.send('eth_accounts', []).then((accounts: Array<string>) => {
|
||||
return accounts.map((a) => getAddress(a));
|
||||
});
|
||||
}
|
||||
|
||||
send(method: string, params: any): Promise<any> {
|
||||
let request = {
|
||||
method: method,
|
||||
params: params,
|
||||
id: (_nextId++),
|
||||
jsonrpc: "2.0"
|
||||
};
|
||||
|
||||
return fetchJson(this.connection, JSON.stringify(request), getResult).then((result) => {
|
||||
this.emit('debug', {
|
||||
action: 'send',
|
||||
request: request,
|
||||
response: result,
|
||||
provider: this
|
||||
});
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
perform(method: string, params: any): Promise<any> {
|
||||
switch (method) {
|
||||
case 'getBlockNumber':
|
||||
return this.send('eth_blockNumber', []);
|
||||
|
||||
case 'getGasPrice':
|
||||
return this.send('eth_gasPrice', []);
|
||||
|
||||
case 'getBalance':
|
||||
return this.send('eth_getBalance', [ getLowerCase(params.address), params.blockTag ]);
|
||||
|
||||
case 'getTransactionCount':
|
||||
return this.send('eth_getTransactionCount', [ getLowerCase(params.address), params.blockTag ]);
|
||||
|
||||
case 'getCode':
|
||||
return this.send('eth_getCode', [ getLowerCase(params.address), params.blockTag ]);
|
||||
|
||||
case 'getStorageAt':
|
||||
return this.send('eth_getStorageAt', [ getLowerCase(params.address), params.position, params.blockTag ]);
|
||||
|
||||
case 'sendTransaction':
|
||||
return this.send('eth_sendRawTransaction', [ params.signedTransaction ]).catch((error) => {
|
||||
if (error.responseText) {
|
||||
// "insufficient funds for gas * price + value"
|
||||
if (error.responseText.indexOf('insufficient funds') > 0) {
|
||||
errors.throwError('insufficient funds', errors.INSUFFICIENT_FUNDS, { });
|
||||
}
|
||||
// "nonce too low"
|
||||
if (error.responseText.indexOf('nonce too low') > 0) {
|
||||
errors.throwError('nonce has already been used', errors.NONCE_EXPIRED, { });
|
||||
}
|
||||
// "replacement transaction underpriced"
|
||||
if (error.responseText.indexOf('replacement transaction underpriced') > 0) {
|
||||
errors.throwError('replacement fee too low', errors.REPLACEMENT_UNDERPRICED, { });
|
||||
}
|
||||
}
|
||||
throw error;
|
||||
});
|
||||
|
||||
case 'getBlock':
|
||||
if (params.blockTag) {
|
||||
return this.send('eth_getBlockByNumber', [ params.blockTag, !!params.includeTransactions ]);
|
||||
} else if (params.blockHash) {
|
||||
return this.send('eth_getBlockByHash', [ params.blockHash, !!params.includeTransactions ]);
|
||||
}
|
||||
return Promise.reject(new Error('invalid block tag or block hash'));
|
||||
|
||||
case 'getTransaction':
|
||||
return this.send('eth_getTransactionByHash', [ params.transactionHash ]);
|
||||
|
||||
case 'getTransactionReceipt':
|
||||
return this.send('eth_getTransactionReceipt', [ params.transactionHash ]);
|
||||
|
||||
case 'call':
|
||||
return this.send('eth_call', [ JsonRpcProvider.hexlifyTransaction(params.transaction, { from: true }), params.blockTag ]);
|
||||
|
||||
case 'estimateGas':
|
||||
return this.send('eth_estimateGas', [ JsonRpcProvider.hexlifyTransaction(params.transaction, { from: true }) ]);
|
||||
|
||||
case 'getLogs':
|
||||
if (params.filter && params.filter.address != null) {
|
||||
params.filter.address = getLowerCase(params.filter.address);
|
||||
}
|
||||
return this.send('eth_getLogs', [ params.filter ]);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
errors.throwError(method + ' not implemented', errors.NOT_IMPLEMENTED, { operation: method });
|
||||
return null;
|
||||
}
|
||||
|
||||
protected _startPending(): void {
|
||||
if (this._pendingFilter != null) { return; }
|
||||
let self = this;
|
||||
|
||||
let pendingFilter: Promise<number> = this.send('eth_newPendingTransactionFilter', []);
|
||||
this._pendingFilter = pendingFilter;
|
||||
|
||||
pendingFilter.then(function(filterId) {
|
||||
function poll() {
|
||||
self.send('eth_getFilterChanges', [ filterId ]).then(function(hashes: Array<string>) {
|
||||
if (self._pendingFilter != pendingFilter) { return null; }
|
||||
|
||||
let seq = Promise.resolve();
|
||||
hashes.forEach(function(hash) {
|
||||
// @TODO: This should be garbage collected at some point... How? When?
|
||||
self._emitted['t:' + hash.toLowerCase()] = 'pending';
|
||||
seq = seq.then(function() {
|
||||
return self.getTransaction(hash).then(function(tx) {
|
||||
self.emit('pending', tx);
|
||||
return null;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return seq.then(function() {
|
||||
return timer(1000);
|
||||
});
|
||||
}).then(function() {
|
||||
if (self._pendingFilter != pendingFilter) {
|
||||
self.send('eth_uninstallFilter', [ filterId ]);
|
||||
return;
|
||||
}
|
||||
setTimeout(function() { poll(); }, 0);
|
||||
|
||||
return null;
|
||||
}).catch((error: Error) => { });
|
||||
}
|
||||
poll();
|
||||
|
||||
return filterId;
|
||||
}).catch((error: Error) => { });
|
||||
}
|
||||
|
||||
protected _stopPending(): void {
|
||||
this._pendingFilter = null;
|
||||
}
|
||||
|
||||
// Convert an ethers.js transaction into a JSON-RPC transaction
|
||||
// - gasLimit => gas
|
||||
// - All values hexlified
|
||||
// - All numeric values zero-striped
|
||||
// NOTE: This allows a TransactionRequest, but all values should be resolved
|
||||
// before this is called
|
||||
static hexlifyTransaction(transaction: TransactionRequest, allowExtra?: { [key: string]: boolean }): { [key: string]: string } {
|
||||
|
||||
// Check only allowed properties are given
|
||||
let allowed = shallowCopy(allowedTransactionKeys);
|
||||
if (allowExtra) {
|
||||
for (let key in allowExtra) {
|
||||
if (allowExtra[key]) { allowed[key] = true; }
|
||||
}
|
||||
}
|
||||
checkProperties(transaction, allowed);
|
||||
|
||||
let result: { [key: string]: string } = {};
|
||||
|
||||
// Some nodes (INFURA ropsten; INFURA mainnet is fine) don't like leading zeros.
|
||||
['gasLimit', 'gasPrice', 'nonce', 'value'].forEach(function(key) {
|
||||
if ((<any>transaction)[key] == null) { return; }
|
||||
let value = hexStripZeros(hexlify((<any>transaction)[key]));
|
||||
if (key === 'gasLimit') { key = 'gas'; }
|
||||
result[key] = value;
|
||||
});
|
||||
|
||||
['from', 'to', 'data'].forEach(function(key) {
|
||||
if ((<any>transaction)[key] == null) { return; }
|
||||
result[key] = hexlify((<any>transaction)[key]);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
94
src.ts/providers/web3-provider.ts
Normal file
94
src.ts/providers/web3-provider.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
'use strict';
|
||||
|
||||
import { JsonRpcProvider } from './json-rpc-provider';
|
||||
|
||||
import { defineReadOnly } from '../utils/properties';
|
||||
|
||||
// Imported Types
|
||||
import { Networkish } from '../utils/networks';
|
||||
|
||||
import * as errors from '../errors';
|
||||
|
||||
|
||||
// Exported Types
|
||||
export type AsyncSendable = {
|
||||
isMetaMask?: boolean;
|
||||
host?: string;
|
||||
path?: string;
|
||||
sendAsync?: (request: any, callback: (error: any, response: any) => void) => void
|
||||
send?: (request: any, callback: (error: any, response: any) => void) => void
|
||||
}
|
||||
|
||||
/*
|
||||
@TODO
|
||||
utils.defineProperty(Web3Signer, 'onchange', {
|
||||
|
||||
});
|
||||
|
||||
*/
|
||||
|
||||
export class Web3Provider extends JsonRpcProvider {
|
||||
readonly _web3Provider: AsyncSendable;
|
||||
private _sendAsync: (request: any, callback: (error: any, response: any) => void) => void;
|
||||
|
||||
constructor(web3Provider: AsyncSendable, network?: Networkish) {
|
||||
// HTTP has a host; IPC has a path.
|
||||
super(web3Provider.host || web3Provider.path || '', network);
|
||||
errors.checkNew(this, Web3Provider);
|
||||
|
||||
if (web3Provider) {
|
||||
if (web3Provider.sendAsync) {
|
||||
this._sendAsync = web3Provider.sendAsync.bind(web3Provider);
|
||||
} else if (web3Provider.send) {
|
||||
this._sendAsync = web3Provider.send.bind(web3Provider);
|
||||
}
|
||||
}
|
||||
|
||||
if (!web3Provider || !this._sendAsync) {
|
||||
errors.throwError(
|
||||
'invalid web3Provider',
|
||||
errors.INVALID_ARGUMENT,
|
||||
{ arg: 'web3Provider', value: web3Provider }
|
||||
);
|
||||
}
|
||||
|
||||
defineReadOnly(this, '_web3Provider', web3Provider);
|
||||
}
|
||||
|
||||
send(method: string, params: any): Promise<any> {
|
||||
|
||||
// Metamask complains about eth_sign (and on some versions hangs)
|
||||
if (method == 'eth_sign' && this._web3Provider.isMetaMask) {
|
||||
// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_sign
|
||||
method = 'personal_sign';
|
||||
params = [ params[1], params[0] ];
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
var request = {
|
||||
method: method,
|
||||
params: params,
|
||||
id: 42,
|
||||
jsonrpc: "2.0"
|
||||
};
|
||||
|
||||
this._sendAsync(request, function(error, result) {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.error) {
|
||||
// @TODO: not any
|
||||
var error: any = new Error(result.error.message);
|
||||
error.code = result.error.code;
|
||||
error.data = result.error.data;
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(result.result);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
24
src.ts/shims/base64.ts
Normal file
24
src.ts/shims/base64.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
'use strict';
|
||||
|
||||
import { arrayify } from '../utils/bytes';
|
||||
|
||||
module.exports = {
|
||||
decode: function(textData: string): Uint8Array {
|
||||
textData = atob(textData);
|
||||
var data = [];
|
||||
for (var i = 0; i < textData.length; i++) {
|
||||
data.push(textData.charCodeAt(i));
|
||||
}
|
||||
return arrayify(data);
|
||||
},
|
||||
encode: function(data: Uint8Array): string {
|
||||
data = arrayify(data);
|
||||
var textData = '';
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
textData += String.fromCharCode(data[i]);
|
||||
}
|
||||
return btoa(textData);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
1
src.ts/shims/empty.ts
Normal file
1
src.ts/shims/empty.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { }
|
||||
26
src.ts/shims/hmac.ts
Normal file
26
src.ts/shims/hmac.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
|
||||
import * as hash from 'hash.js';
|
||||
|
||||
import { arrayify } from '../utils/bytes';
|
||||
|
||||
import * as errors from '../errors';
|
||||
|
||||
///////////////////////////////
|
||||
// Imported Types
|
||||
|
||||
import { Arrayish } from '../utils/bytes';
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
export enum SupportedAlgorithms { sha256 = 'sha256', sha512 = 'sha512' };
|
||||
|
||||
export function computeHmac(algorithm: SupportedAlgorithms, key: Arrayish, data: Arrayish): Uint8Array {
|
||||
if (!SupportedAlgorithms[algorithm]) {
|
||||
errors.throwError('unsupported algorithm ' + algorithm, errors.UNSUPPORTED_OPERATION, { operation: 'hmac', algorithm: algorithm });
|
||||
}
|
||||
|
||||
return arrayify(
|
||||
hash.hmac(hash[algorithm], arrayify(key)).update(arrayify(data)).digest()
|
||||
);
|
||||
}
|
||||
|
||||
13
src.ts/shims/index.ts
Normal file
13
src.ts/shims/index.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* This file is not imported anywhere, but is used to trigger TypeScript
|
||||
* compilation of the various shims for the browser.
|
||||
*/
|
||||
|
||||
import { } from './base64';
|
||||
import { } from './empty';
|
||||
import { } from './hmac';
|
||||
import { } from './pbkdf2';
|
||||
import { } from './random-bytes';
|
||||
import { } from './shims';
|
||||
import { } from './wordlists';
|
||||
import { } from './xmlhttprequest';
|
||||
58
src.ts/shims/pbkdf2.ts
Normal file
58
src.ts/shims/pbkdf2.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
'use strict';
|
||||
|
||||
import { arrayify } from '../utils/bytes';
|
||||
import { computeHmac, SupportedAlgorithms } from './hmac';
|
||||
|
||||
// Imported Types
|
||||
import { Arrayish } from '../utils/bytes';
|
||||
|
||||
export function pbkdf2(password: Arrayish, salt: Arrayish, iterations: number, keylen: number, hashAlgorithm: SupportedAlgorithms): Uint8Array {
|
||||
password = arrayify(password);
|
||||
salt = arrayify(salt);
|
||||
var hLen
|
||||
var l = 1
|
||||
var DK = new Uint8Array(keylen)
|
||||
var block1 = new Uint8Array(salt.length + 4)
|
||||
block1.set(salt);
|
||||
//salt.copy(block1, 0, 0, salt.length)
|
||||
|
||||
let r: number;
|
||||
let T: Uint8Array;
|
||||
|
||||
for (var i = 1; i <= l; i++) {
|
||||
//block1.writeUInt32BE(i, salt.length)
|
||||
block1[salt.length] = (i >> 24) & 0xff;
|
||||
block1[salt.length + 1] = (i >> 16) & 0xff;
|
||||
block1[salt.length + 2] = (i >> 8) & 0xff;
|
||||
block1[salt.length + 3] = i & 0xff;
|
||||
|
||||
//var U = createHmac(password).update(block1).digest();
|
||||
var U = computeHmac(hashAlgorithm, password, block1);
|
||||
|
||||
if (!hLen) {
|
||||
hLen = U.length
|
||||
T = new Uint8Array(hLen)
|
||||
l = Math.ceil(keylen / hLen)
|
||||
r = keylen - (l - 1) * hLen
|
||||
}
|
||||
|
||||
//U.copy(T, 0, 0, hLen)
|
||||
T.set(U);
|
||||
|
||||
|
||||
for (var j = 1; j < iterations; j++) {
|
||||
//U = createHmac(password).update(U).digest();
|
||||
U = computeHmac(hashAlgorithm, password, U);
|
||||
for (var k = 0; k < hLen; k++) T[k] ^= U[k]
|
||||
}
|
||||
|
||||
|
||||
var destPos = (i - 1) * hLen
|
||||
var len = (i === l ? r : hLen)
|
||||
//T.copy(DK, destPos, 0, len)
|
||||
DK.set(arrayify(T).slice(0, len), destPos);
|
||||
}
|
||||
|
||||
return arrayify(DK)
|
||||
}
|
||||
|
||||
41
src.ts/shims/random-bytes.ts
Normal file
41
src.ts/shims/random-bytes.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
'use strict';
|
||||
|
||||
import { arrayify } from '../utils/bytes';
|
||||
import { defineReadOnly } from '../utils/properties';
|
||||
|
||||
let crypto: any = (<any>global).crypto || (<any>global).msCrypto;
|
||||
if (!crypto || !crypto.getRandomValues) {
|
||||
|
||||
console.log('WARNING: Missing strong random number source; using weak randomBytes');
|
||||
|
||||
crypto = {
|
||||
getRandomValues: function(buffer: Uint8Array): Uint8Array {
|
||||
for (var round = 0; round < 20; round++) {
|
||||
for (var i = 0; i < buffer.length; i++) {
|
||||
if (round) {
|
||||
buffer[i] ^= Math.trunc(256 * Math.random());
|
||||
} else {
|
||||
buffer[i] = Math.trunc(256 * Math.random());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
},
|
||||
_weakCrypto: true
|
||||
};
|
||||
}
|
||||
|
||||
export function randomBytes(length: number): Uint8Array {
|
||||
if (length <= 0 || length > 1024 || parseInt(String(length)) != length) {
|
||||
throw new Error('invalid length');
|
||||
}
|
||||
|
||||
var result = new Uint8Array(length);
|
||||
crypto.getRandomValues(result);
|
||||
return arrayify(result);
|
||||
};
|
||||
|
||||
if (crypto._weakCrypto === true) {
|
||||
defineReadOnly(randomBytes, '_weakCrypto', true);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user