Compare commits
722 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
736b08e016 | ||
|
|
d8013cae37 | ||
|
|
b26b1b9c53 | ||
|
|
1c9c7b7a7d | ||
|
|
a9aaeefe24 | ||
|
|
8257e3f885 | ||
|
|
965c987761 | ||
|
|
142e5276fe | ||
|
|
351d1a2dad | ||
|
|
72424ea2d2 | ||
|
|
1e703bfdb4 | ||
|
|
dee0a4bf3e | ||
|
|
9c1d6051fb | ||
|
|
3c67736cbb | ||
|
|
c697a5bc81 | ||
|
|
0e9df5cb76 | ||
|
|
7a37dd6949 | ||
|
|
cecefd416d | ||
|
|
79f9047c21 | ||
|
|
f9f7469ccf | ||
|
|
1e5933eb45 | ||
|
|
a4e1f531b7 | ||
|
|
0a38d14930 | ||
|
|
4e7f2fa3d4 | ||
|
|
4b99bb579c | ||
|
|
0b59519c19 | ||
|
|
4d18119ccf | ||
|
|
4af236d3ac | ||
|
|
eddb9c28a8 | ||
|
|
40a6fdd95e | ||
|
|
3f80001e4d | ||
|
|
89082164ce | ||
|
|
9df7c2f37a | ||
|
|
840f510bf1 | ||
|
|
1bd95b127b | ||
|
|
97154e49a6 | ||
|
|
db757b5bbb | ||
|
|
af62f54042 | ||
|
|
1b0dc18bdc | ||
|
|
1db36b3132 | ||
|
|
805879a213 | ||
|
|
e1157f3a4c | ||
|
|
46e0866410 | ||
|
|
621c1ee74e | ||
|
|
b3f9070b39 | ||
|
|
14b15dcf5a | ||
|
|
e4c62d2939 | ||
|
|
b50c0bd615 | ||
|
|
e55ee253ab | ||
|
|
f81fa6e4f5 | ||
|
|
4785c650f3 | ||
|
|
be99fa858e | ||
|
|
196d4f6659 | ||
|
|
1233233c6c | ||
|
|
742bcc753f | ||
|
|
839c2a10aa | ||
|
|
b298d41c22 | ||
|
|
c70a9f61cb | ||
|
|
6440d8e69d | ||
|
|
97dcd4d450 | ||
|
|
b8d4514dd0 | ||
|
|
0ceee56de3 | ||
|
|
024cc1806f | ||
|
|
5e421d2636 | ||
|
|
f71ea72d54 | ||
|
|
d65379f6c4 | ||
|
|
06281e85c4 | ||
|
|
5dc2621a32 | ||
|
|
c024f88ce1 | ||
|
|
b5e6ac7db9 | ||
|
|
60732f8243 | ||
|
|
4a557349a3 | ||
|
|
5bf0c310d3 | ||
|
|
99ec103e97 | ||
|
|
829505d548 | ||
|
|
5c9ee7f2a5 | ||
|
|
fa0d54966d | ||
|
|
401dd5162d | ||
|
|
2dee42449a | ||
|
|
224e8aff07 | ||
|
|
b5710cd710 | ||
|
|
8033a8f94c | ||
|
|
4bcef804b6 | ||
|
|
66a5b0ecdc | ||
|
|
2605e7de40 | ||
|
|
f50f171223 | ||
|
|
7626b541f5 | ||
|
|
24c7fc533a | ||
|
|
21fdfe9581 | ||
|
|
ca8ee1c0e3 | ||
|
|
0980b864fa | ||
|
|
d0a822f02a | ||
|
|
4dbce955ee |
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"
|
||||
}
|
||||
};
|
||||
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
@@ -0,0 +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']);
|
||||
};
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
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
|
||||
in the Software without restriction, including without limitation the rights
|
||||
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 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.
|
||||
214
README.md
214
README.md
@@ -1,147 +1,105 @@
|
||||
ethers-wallet
|
||||
=============
|
||||
ethers.js
|
||||
=========
|
||||
|
||||
Complete Ethereum wallet implementation in JavaScript.
|
||||
[](https://badge.fury.io/js/ethers)
|
||||
|
||||
Features
|
||||
- Keep your private keys in the browser
|
||||
- Small (~155kb compressed; hopefully under 100kb soon)
|
||||
- MIT licensed (with a few exceptions, which we are migrating off of; see below)
|
||||
Complete Ethereum wallet implementation and utilities in JavaScript (and TypeScript).
|
||||
|
||||
*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.*
|
||||
**Features:**
|
||||
|
||||
- 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, Italian, Japanese, Korean, Simplified Chinese, Traditional Chinese; more coming soon)
|
||||
- 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
|
||||
|
||||
|
||||
Wallet API
|
||||
----------
|
||||
|
||||
```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
|
||||
])
|
||||
|
||||
// or equivalently:
|
||||
var privateKey = '0x3141592653589793238462643383279502884197169399375105820974944592'
|
||||
|
||||
// Create a wallet object
|
||||
var wallet = new Wallet(privateKey)
|
||||
|
||||
// Wallet address
|
||||
console.log(wallet.address)
|
||||
/// "0x7357589f8e367c2C31F51242fB77B350A11830F3"
|
||||
|
||||
// ICAP Addresses
|
||||
Wallet.getIcapAddress(wallet.address)
|
||||
/// "XE39DH16QOXYG5JY9BYY6JGZW8ORUPBX71V"
|
||||
|
||||
Wallet.getIcapAddress("XE39DH16QOXYG5JY9BYY6JGZW8ORUPBX71V")
|
||||
/// "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')
|
||||
/// Error: invalid checksum address
|
||||
|
||||
// Sign transactions
|
||||
wallet.sign({
|
||||
to: "0x06B5955A67D827CDF91823E3bB8F069e6c89c1D6",
|
||||
gasLimit: 3000000,
|
||||
gasPrice: "0x1000",
|
||||
value: "0x1000"
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
Contract API
|
||||
Keep Updated
|
||||
------------
|
||||
|
||||
```javascript
|
||||
// Load a normal web3 object (you need a local RPC-enabled ethereum node running)
|
||||
var web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'))
|
||||
For the latest news and advisories, please follow [@ethersproject](https://twitter.com/ethersproject) on Twitter as well as this GitHub project.
|
||||
|
||||
// Create your wallet
|
||||
var wallet = new Wallet('0x3141592653589793238462643383279502884197169399375105820974944592')
|
||||
|
||||
console.log(wallet.address);
|
||||
/// "0x7357589f8e367c2C31F51242fB77B350A11830F3"
|
||||
Installing
|
||||
----------
|
||||
|
||||
// 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"
|
||||
}
|
||||
];
|
||||
To use in a browser:
|
||||
|
||||
// Get the contract
|
||||
var contract = wallet.getContract(web3, simpleStorageAddress, simpleStorageAbi)
|
||||
```html
|
||||
<script charset="utf-8"
|
||||
src="https://cdn.ethers.io/scripts/ethers-v4.min.js"
|
||||
type="text/javascript">
|
||||
</script>
|
||||
```
|
||||
|
||||
// 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 ether with a state-changing call, or custom gasLimit or gasPrice
|
||||
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);
|
||||
});
|
||||
To use in [node.js](https://nodejs.org/):
|
||||
|
||||
```
|
||||
/Users/ethers/my-app> npm install --save ethers
|
||||
```
|
||||
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
Browse the [API Documentation](https://docs.ethers.io/ethers.js/html/) online.
|
||||
|
||||
To fork and submit pull requests to the documentation, please see the
|
||||
[documentation repository](https://github.com/ethers-io/documentation).
|
||||
|
||||
|
||||
Related Libraries
|
||||
---------------
|
||||
|
||||
- [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
|
||||
|
||||
|
||||
Hacking and Contributing
|
||||
------------------------
|
||||
|
||||
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:
|
||||
|
||||
```
|
||||
/home/ethers> npm run auto-build
|
||||
```
|
||||
|
||||
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:
|
||||
- The Solidity encoder/decoder (LGPL)
|
||||
- RLP (MPL-2.0)
|
||||
|
||||
We are working on our own implementations so we can move off of them and have a completely MIT licensed implementation 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.11";
|
||||
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.11";
|
||||
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;
|
||||
85
contract.d.ts
vendored
Normal file
85
contract.d.ts
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
import { BigNumber } from './utils/bignumber';
|
||||
import { Indexed, Interface } from './utils/interface';
|
||||
import { UnsignedTransaction } from './utils/transaction';
|
||||
import { 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 _deployed;
|
||||
constructor(addressOrName: string, contractInterface: Array<string | ParamType> | string | Interface, signerOrProvider: Signer | Provider);
|
||||
deployed(): 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 {};
|
||||
703
contract.js
Normal file
703
contract.js
Normal file
@@ -0,0 +1,703 @@
|
||||
'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 _this = this;
|
||||
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 });
|
||||
}
|
||||
});
|
||||
// Send to the contract address (after checking the contract is deployed)
|
||||
tx.to = contract.deployed().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 = _this.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 _this.provider; };
|
||||
event.getBlock = function () {
|
||||
return _this.provider.getBlock(receipt.blockHash);
|
||||
};
|
||||
event.getTransaction = function () {
|
||||
return _this.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) {
|
||||
console.log('ERROR: Cannot find Contract - ' + addressOrName);
|
||||
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 {
|
||||
console.log('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 () {
|
||||
var _this = this;
|
||||
if (!this._deployed) {
|
||||
// If we were just deployed, we know the transaction we should occur in
|
||||
if (this.deployTransaction) {
|
||||
this._deployed = 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._deployed = this.provider.getCode(this.address).then(function (code) {
|
||||
if (code === '0x') {
|
||||
errors.throwError('contract not deployed', errors.UNSUPPORTED_OPERATION, {
|
||||
contractAddress: _this.address,
|
||||
operation: 'getDeployed'
|
||||
});
|
||||
}
|
||||
return _this;
|
||||
});
|
||||
}
|
||||
}
|
||||
return this._deployed;
|
||||
};
|
||||
// @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) {
|
||||
if (!this.provider) {
|
||||
return this;
|
||||
}
|
||||
var eventFilter = this._getEventFilter(eventName);
|
||||
this._events = this._events.filter(function (event) {
|
||||
return event.eventFilter.eventTag !== eventFilter.eventTag;
|
||||
});
|
||||
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;
|
||||
474
dist/demo/index.html
vendored
Normal file
474
dist/demo/index.html
vendored
Normal file
@@ -0,0 +1,474 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Ethereum Wallet</title>
|
||||
<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>Load JSON Wallet</h2>
|
||||
<p>
|
||||
If you have a JSON wallet file from <i>geth</i> or from the initial <i>Ethereum</i>
|
||||
crowd sale, you can decrypt it here. No information is shared with <b>any</b>
|
||||
server.
|
||||
</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>JSON Wallet:</th>
|
||||
<td><div class="file" id="select-wallet-drop">Drop JSON wallet file here</div><input type="file" id="select-wallet-file" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Password:</th>
|
||||
<td><input type="password" placeholder="(password)" id="select-wallet-password" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>
|
||||
<div id="select-submit-wallet" class="submit disable">Unlock JSON Wallet</div>
|
||||
</td>
|
||||
</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>
|
||||
</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Private Key:</th>
|
||||
<td><input type="text" placeholder="(private key)" id="select-privatekey" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>
|
||||
<div id="select-submit-privatekey" class="submit disable">Load Raw Private Key</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr />
|
||||
<h3>Disclaimer:</h3>
|
||||
<p>
|
||||
This is beta software, with no warranty. <b>Use at your own risk.</b>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="centerer hidden" id="screen-loading">
|
||||
<div class="centered">
|
||||
<h1>Loading Wallet</h1>
|
||||
<hr />
|
||||
<h2 id="loading-header"></h2>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Progress:</th>
|
||||
<td>
|
||||
<input type="text" readonly="readonly" class="readonly" id="loading-status" value="" /></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>
|
||||
<div id="loading-cancel" class="submit">Cancel</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr />
|
||||
<h3>Disclaimer:</h3>
|
||||
<p>
|
||||
This is beta software, with no warranty. <b>Use at your own risk.</b>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="centerer hidden" id="screen-wallet">
|
||||
<div class="centered">
|
||||
<h1>Ethereum Wallet<span id="wallet-username" class="username right"></span></h1>
|
||||
<hr />
|
||||
<h3>Wallet Details:</h3>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Address:</th>
|
||||
<td>
|
||||
<input type="text" readonly="readonly" class="readonly" id="wallet-address" value="" /></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Network:</th>
|
||||
<td>
|
||||
<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>
|
||||
<th>Balance:</th>
|
||||
<td>
|
||||
<input type="text" readonly="readonly" class="readonly" id="wallet-balance" value="0.0" /></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Nonce:</th>
|
||||
<td>
|
||||
<input type="text" readonly="readonly" class="readonly" id="wallet-transaction-count" value="0" /></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>
|
||||
<div id="wallet-submit-refresh" class="submit">Refresh</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<h3>Send Ether:</h3>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Target Address:</th>
|
||||
<td><input type="text" placeholder="(target address)" id="wallet-send-target-address" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Amount:</th>
|
||||
<td><input type="text" placeholder="(amount)" id="wallet-send-amount" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>
|
||||
<div id="wallet-submit-send" class="submit disable">Send Ether</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<h3>Session Activity</h3>
|
||||
<div id="wallet-activity" class="activity"></div>
|
||||
|
||||
<hr />
|
||||
<h3>Disclaimer:</h3>
|
||||
<p>
|
||||
This is beta software, with no warranty. <b>Use at your own risk.</b>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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(); }
|
||||
}
|
||||
}
|
||||
|
||||
var cancelScrypt = false;
|
||||
document.getElementById('loading-cancel').onclick = function() {
|
||||
cancelScrypt = true;
|
||||
};
|
||||
|
||||
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');
|
||||
var inputPassword = document.getElementById('select-wallet-password');
|
||||
var submit = document.getElementById('select-submit-wallet');
|
||||
|
||||
function check() {
|
||||
if (inputFile.files && inputFile.files.length === 1) {
|
||||
submit.classList.remove('disable');
|
||||
targetDrop.textContent = inputFile.files[0].name;
|
||||
} else {
|
||||
submit.classList.add('disable');
|
||||
}
|
||||
}
|
||||
inputFile.onchange = check;
|
||||
inputPassword.oninput = check;
|
||||
|
||||
setEnter(inputPassword, submit);
|
||||
|
||||
inputFile.addEventListener('dragover', function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
targetDrop.classList.add('highlight');
|
||||
}, true);
|
||||
|
||||
inputFile.addEventListener('drop', function(event) {
|
||||
targetDrop.classList.remove('highlight');
|
||||
}, true);
|
||||
|
||||
submit.onclick = function() {
|
||||
if (submit.classList.contains('disable')) { return; }
|
||||
|
||||
var fileReader = new FileReader();
|
||||
fileReader.onload = function(e) {
|
||||
var json = e.target.result;
|
||||
|
||||
if (ethers.utils.getJsonWalletAddress(json)) {
|
||||
showLoading('Decrypting Wallet...');
|
||||
|
||||
cancelScrypt = false;
|
||||
|
||||
ethers.Wallet.fromEncryptedJson(json, inputPassword.value, updateLoading).then(function(wallet) {
|
||||
showWallet(wallet);
|
||||
|
||||
}, function(error) {
|
||||
if (error.message === 'invalid password') {
|
||||
alert('Wrong Password');
|
||||
} else {
|
||||
console.log(error);
|
||||
alert('Error Decrypting Wallet');
|
||||
}
|
||||
showSelect();
|
||||
});
|
||||
} else {
|
||||
alert('Unknown JSON wallet format');
|
||||
}
|
||||
};
|
||||
fileReader.readAsText(inputFile.files[0]);
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
// Raw Private Key
|
||||
(function() {
|
||||
var inputPrivatekey = document.getElementById('select-privatekey');
|
||||
var submit = document.getElementById('select-submit-privatekey');
|
||||
|
||||
function check() {
|
||||
if (inputPrivatekey.value.match(/^(0x)?[0-9A-fa-f]{64}$/)) {
|
||||
submit.classList.remove('disable');
|
||||
} else {
|
||||
submit.classList.add('disable');
|
||||
}
|
||||
}
|
||||
inputPrivatekey.oninput = check;
|
||||
|
||||
setEnter(inputPrivatekey, submit);
|
||||
|
||||
submit.onclick = function() {
|
||||
if (submit.classList.contains('disable')) { return; }
|
||||
var privateKey = inputPrivatekey.value;
|
||||
if (privateKey.substring(0, 2) !== '0x') { privateKey = '0x' + 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));
|
||||
}
|
||||
})();
|
||||
|
||||
|
||||
var activeWallet = null;
|
||||
|
||||
function showError(error) {
|
||||
alert('Error \u2014 ' + error.message);
|
||||
}
|
||||
|
||||
// Refresh balance and transaction count in the UI
|
||||
var refresh = (function() {
|
||||
var inputBalance = document.getElementById('wallet-balance');
|
||||
var inputTransactionCount = document.getElementById('wallet-transaction-count');
|
||||
var submit = document.getElementById('wallet-submit-refresh');
|
||||
|
||||
function refresh() {
|
||||
addActivity('> Refreshing details...');
|
||||
activeWallet.getBalance('pending').then(function(balance) {
|
||||
addActivity('< Balance: ' + balance.toString(10));
|
||||
inputBalance.value = ethers.utils.formatEther(balance, { commify: true });
|
||||
}, function(error) {
|
||||
showError(error);
|
||||
});
|
||||
activeWallet.getTransactionCount('pending').then(function(transactionCount) {
|
||||
addActivity('< TransactionCount: ' + transactionCount);
|
||||
inputTransactionCount.value = transactionCount;
|
||||
}, function(error) {
|
||||
showError(error);
|
||||
});
|
||||
}
|
||||
submit.onclick = refresh;
|
||||
|
||||
return refresh;
|
||||
})();
|
||||
|
||||
var addActivity = (function() {
|
||||
var activity = document.getElementById('wallet-activity');
|
||||
return function(message, url) {
|
||||
var line = document.createElement('a');
|
||||
line.textContent = message;
|
||||
if (url) {
|
||||
line.setAttribute('href', url);
|
||||
line.setAttribute('target', '_blank');
|
||||
}
|
||||
activity.appendChild(line);
|
||||
}
|
||||
})();
|
||||
|
||||
// Set up the wallet page
|
||||
(function() {
|
||||
|
||||
var inputTargetAddress = document.getElementById('wallet-send-target-address');
|
||||
var inputAmount = document.getElementById('wallet-send-amount');
|
||||
var submit = document.getElementById('wallet-submit-send');
|
||||
|
||||
// Validate the address and value (to enable the send button)
|
||||
function check() {
|
||||
try {
|
||||
ethers.utils.getAddress(inputTargetAddress.value);
|
||||
ethers.utils.parseEther(inputAmount.value);
|
||||
} catch (error) {
|
||||
submit.classList.add('disable');
|
||||
return;
|
||||
}
|
||||
submit.classList.remove('disable');
|
||||
}
|
||||
inputTargetAddress.oninput = check;
|
||||
inputAmount.oninput = check;
|
||||
|
||||
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.
|
||||
// @TODO
|
||||
//var gasPrice = (activeWallet.provider.testnet ? 0x4a817c800: 0xba43b7400);
|
||||
//console.log('GasPrice: ' + gasPrice);
|
||||
|
||||
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 = '';
|
||||
inputAmount.value = '';
|
||||
submit.classList.add('disable');
|
||||
|
||||
refresh();
|
||||
}, function(error) {
|
||||
console.log(error);
|
||||
showError(error);
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
||||
function showSelect() {
|
||||
document.getElementById('screen-select').style.display = 'block';
|
||||
document.getElementById('screen-loading').style.display = 'none';
|
||||
document.getElementById('screen-wallet').style.display = 'none';
|
||||
}
|
||||
|
||||
function showLoading(title) {
|
||||
document.getElementById('screen-select').style.display = 'none';
|
||||
document.getElementById('screen-loading').style.display = 'block';
|
||||
document.getElementById('screen-wallet').style.display = 'none';
|
||||
|
||||
document.getElementById('loading-header').textContent = title;
|
||||
}
|
||||
|
||||
function showWallet(wallet) {
|
||||
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';
|
||||
document.getElementById('screen-wallet').style.display = 'block';
|
||||
|
||||
var inputWalletAddress = document.getElementById('wallet-address');
|
||||
inputWalletAddress.value = wallet.address;
|
||||
inputWalletAddress.onclick = function() {
|
||||
this.select();
|
||||
};
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
//var privateKey = '0x3141592653589793238462643383279502884197169399375105820974944592';
|
||||
//showWallet(new ethers.Wallet(privateKey));
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
199
dist/demo/style.css
vendored
Normal file
199
dist/demo/style.css
vendored
Normal file
@@ -0,0 +1,199 @@
|
||||
body {
|
||||
background-color: #eee;
|
||||
font-family: sans-serif;
|
||||
font-size: 18px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.centerer {
|
||||
margin-left: 50%;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.centered:before {
|
||||
box-shadow: -10px 0 15px -10px #999 inset;
|
||||
content: " ";
|
||||
height: 100%;
|
||||
left: -10px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
.centered:after {
|
||||
box-shadow: 10px 0 15px -10px #999 inset;
|
||||
content: " ";
|
||||
height: 100%;
|
||||
right: -10px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
.centered {
|
||||
background-color: #fff;
|
||||
border-left: 1px solid #888;
|
||||
border-right: 1px solid #888;
|
||||
dbox-shadow: -1px 0 15px 0 #999;
|
||||
margin-left: -370px;
|
||||
min-height: 100%;
|
||||
padding: 20px;
|
||||
position: relative;
|
||||
width: 700px;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
p {
|
||||
text-align: justify;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
th {
|
||||
text-align: left;
|
||||
padding: 0 15px 15px 0;
|
||||
}
|
||||
td {
|
||||
padding: 0 15px 15px 0;
|
||||
}
|
||||
|
||||
input[type=text] {
|
||||
border: 1px solid #555;
|
||||
font-size: 16px;
|
||||
padding: 10px;
|
||||
width: 501px;
|
||||
}
|
||||
|
||||
input[type=password] {
|
||||
border: 1px solid #555;
|
||||
font-size: 16px;
|
||||
padding: 10px;
|
||||
width: 501px;
|
||||
}
|
||||
|
||||
input[type=file] {
|
||||
dbackground: #fff;
|
||||
border: 1px solid #555;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
opacity: 0;
|
||||
padding: 10px;
|
||||
position: relative;
|
||||
dvisibility: hidden;
|
||||
width: 501px;
|
||||
}
|
||||
|
||||
input[type=text].readonly {
|
||||
border: 1px solid #ccc;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.file {
|
||||
border: 1px solid green;
|
||||
color: #444;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
margin-top: 2px;
|
||||
padding: 13px 10px 12px;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
width: 478px;
|
||||
}
|
||||
|
||||
.file.highlight {
|
||||
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: 110px;
|
||||
}
|
||||
|
||||
.option span {
|
||||
font-size: 0.8em;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.option.selected {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.option:hover {
|
||||
box-shadow: 0px 0px 5px #888;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
table {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
div.activity {
|
||||
font-family: monospace;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
div.activity a {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.username {
|
||||
color: #888;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.submit {
|
||||
border: 1px solid #555;
|
||||
box-shadow: 0px 0px 5px #888;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
transition: opacity 0.1s linear;
|
||||
width: 480px;
|
||||
}
|
||||
|
||||
.submit:hover {
|
||||
border: 1px solid #999;
|
||||
box-shadow: 0px 0px 5px #aaa;
|
||||
}
|
||||
|
||||
.submit:active {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.submit.disable {
|
||||
box-shadow: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.submit.disable:hover {
|
||||
border: 1px solid #555;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.submit.disable:active {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.left {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.right {
|
||||
float: right;
|
||||
}
|
||||
35631
dist/ethers-wallet.js
vendored
35631
dist/ethers-wallet.js
vendored
File diff suppressed because it is too large
Load Diff
14
dist/ethers-wallet.min.js
vendored
14
dist/ethers-wallet.min.js
vendored
File diff suppressed because one or more lines are too long
16647
dist/ethers.js
vendored
Normal file
16647
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
1035
dist/ethers.types.txt
vendored
Normal file
1035
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
17
errors.d.ts
vendored
Normal file
17
errors.d.ts
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
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;
|
||||
121
errors.js
Normal file
121
errors.js
Normal file
@@ -0,0 +1,121 @@
|
||||
'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:
|
||||
// - arg: 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: 'setCersorship' });
|
||||
}
|
||||
_censorErrors = !!censorship;
|
||||
_permanentCensorErrors = !!permanent;
|
||||
}
|
||||
exports.setCensorship = setCensorship;
|
||||
function checkNormalize() {
|
||||
try {
|
||||
if (String.fromCharCode(0xe9).normalize('NFD') !== String.fromCharCode(0x65, 0x0301)) {
|
||||
throw new Error('broken');
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
throwError('platform missing String.prototype.normalize', exports.UNSUPPORTED_OPERATION, { operation: 'String.prototype.normalize' });
|
||||
}
|
||||
}
|
||||
exports.checkNormalize = checkNormalize;
|
||||
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 };
|
||||
44
ethers.js
Normal file
44
ethers.js
Normal file
@@ -0,0 +1,44 @@
|
||||
'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) {
|
||||
return new providers.FallbackProvider([
|
||||
new providers.InfuraProvider(network),
|
||||
new providers.EtherscanProvider(network),
|
||||
]);
|
||||
}
|
||||
exports.getDefaultProvider = getDefaultProvider;
|
||||
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';
|
||||
419
index.js
419
index.js
@@ -1,408 +1,15 @@
|
||||
var aes = require('aes-js');
|
||||
var elliptic = require('elliptic');
|
||||
//var elliptic = require('./elliptic/lib/elliptic.js');
|
||||
var pbkdf2 = require('pbkdf2');
|
||||
var rlp = require('rlp');
|
||||
var scrypt = require('scrypt-js');
|
||||
var uuid = require('uuid');
|
||||
|
||||
var Contract = require('./lib/contract.js');
|
||||
|
||||
var utils = require('./lib/utils.js');
|
||||
var BN = utils.BN;
|
||||
|
||||
var secp256k1 = new (elliptic.ec)('secp256k1');
|
||||
|
||||
/*
|
||||
// @TODO: Make our own implementation of rlp; the existing one is MLP-2.0, we want MIT. :)
|
||||
function rlpEncodeLength(length) {
|
||||
"use strict";
|
||||
function __export(m) {
|
||||
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
|
||||
}
|
||||
|
||||
function rlpArray(items) {
|
||||
var output = new Buffer(0);
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
function defineProperty(object, name, value) {
|
||||
Object.defineProperty(object, name, {
|
||||
enumerable: true,
|
||||
value: value
|
||||
});
|
||||
}
|
||||
|
||||
var exportUtils = {};
|
||||
|
||||
// These may go away in the future...
|
||||
defineProperty(exportUtils, '_aes', aes);
|
||||
defineProperty(exportUtils, '_scrypt', scrypt);
|
||||
|
||||
defineProperty(exportUtils, 'BN', BN);
|
||||
defineProperty(exportUtils, 'Buffer', Buffer);
|
||||
defineProperty(exportUtils, 'sha3', utils.sha3);
|
||||
|
||||
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 hash = utils.sha3(address);
|
||||
|
||||
address = address.split('');
|
||||
for (var i = 0; i < 40; i += 2) {
|
||||
if ((hash[i >> 1] >> 4) >= 8) {
|
||||
address[i] = address[i].toUpperCase();
|
||||
}
|
||||
if ((hash[i >> 1] & 0x0f) >= 8) {
|
||||
address[i + 1] = address[i + 1].toUpperCase();
|
||||
}
|
||||
}
|
||||
|
||||
return '0x' + address.join('');
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
checksum = String(98 - (parseInt(expanded, 10) % 97));
|
||||
while (checksum.length < 2) { checksum = '0' + checksum; }
|
||||
|
||||
return checksum;
|
||||
};
|
||||
})();
|
||||
|
||||
// http://ethereum.stackexchange.com/questions/760/how-is-the-address-of-an-ethereum-contract-computed
|
||||
defineProperty(exportUtils, 'getContractAddress', function(transaction) {
|
||||
if (typeof(address) !== 'string' || !address.match(/^0x[0-9A-Fa-f]{40}$/)) {
|
||||
throw new Error('invalid from');
|
||||
}
|
||||
|
||||
return '0x' + utils.sha3(rlp.encode([
|
||||
utils.hexOrBuffer(transaction.from),
|
||||
utils.hexOrBuffer(utils.hexlify(transaction.nonce, 'nonce'))
|
||||
])).slice(12).toString('hex');
|
||||
});
|
||||
|
||||
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) {
|
||||
if (!(this instanceof Wallet)) { throw new Error('missing new'); }
|
||||
|
||||
if (typeof(privateKey) === 'string' && privateKey.match(/^0x[0-9A-Fa-f]{64}$/)) {
|
||||
privateKey = new Buffer(privateKey.substring(2), 'hex');
|
||||
} else if (!Buffer.isBuffer(privateKey) || privateKey.length !== 32) {
|
||||
throw new Error('invalid private key');
|
||||
}
|
||||
|
||||
var keyPair = secp256k1.keyFromPrivate(privateKey);
|
||||
|
||||
var publicKey = (new Buffer(keyPair.getPublic(false, 'hex'), 'hex')).slice(1);
|
||||
var address = Wallet.getAddress(utils.sha3(publicKey).slice(12).toString('hex'));
|
||||
defineProperty(this, 'address', address);
|
||||
|
||||
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) {
|
||||
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 = keyPair.sign(digest, {canonical: true});
|
||||
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'));
|
||||
});
|
||||
}
|
||||
|
||||
defineProperty(Wallet, 'utils', exportUtils);
|
||||
|
||||
defineProperty(Wallet, 'getAddress', function(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');
|
||||
}
|
||||
|
||||
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;
|
||||
});
|
||||
|
||||
defineProperty(Wallet, 'getIcapAddress', function(address) {
|
||||
address = Wallet.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;
|
||||
});
|
||||
|
||||
defineProperty(Wallet, '_Contract', Contract);
|
||||
|
||||
var allowedTransactionKeys = {
|
||||
data: true, from: true, gasLimit: true, gasPrice:true, to: true, value: true
|
||||
}
|
||||
function AttachedContract(web3, wallet, contractAddress, contract) {
|
||||
utils.defineProperty(this, 'web3', web3);
|
||||
utils.defineProperty(this, 'wallet', wallet);
|
||||
utils.defineProperty(this, 'contractAddress', contractAddress);
|
||||
|
||||
utils.defineProperty(this, '_contract', contract);
|
||||
|
||||
function getWeb3Promise(method) {
|
||||
var params = Array.prototype.slice.call(arguments, 1);
|
||||
return new Promise(function(resolve, reject) {
|
||||
params.push(function(error, result) {
|
||||
if (error) {
|
||||
return reject(error);
|
||||
}
|
||||
resolve(result);
|
||||
});
|
||||
web3.eth[method].apply(web3, params);
|
||||
});
|
||||
}
|
||||
|
||||
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) {
|
||||
return function() {
|
||||
var transaction = {}
|
||||
|
||||
var params = Array.prototype.slice.call(arguments);
|
||||
if (params.length == contract[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 = contract[method].apply(contract, 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;
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
getWeb3Promise('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;
|
||||
}
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
Promise.all([
|
||||
getWeb3Promise('getTransactionCount', wallet.address, 'pending'),
|
||||
getWeb3Promise('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);
|
||||
/*
|
||||
data: call.data,
|
||||
gasLimit: 3000000,
|
||||
gasPrice: results[1],
|
||||
nonce: results[0],
|
||||
to: contractAddress,
|
||||
});
|
||||
*/
|
||||
getWeb3Promise('sendRawTransaction', signedTransaction).then(function(txid) {
|
||||
resolve(txid);
|
||||
}, function(error) {
|
||||
reject(error);
|
||||
});
|
||||
}, function(error) {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
contract.methods.forEach(function(method) {
|
||||
utils.defineProperty(this, method, runMethod(method));
|
||||
}, this);
|
||||
|
||||
contract.events.forEach(function(method) {
|
||||
var call = contract[method].apply(contract, []);
|
||||
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);
|
||||
}
|
||||
|
||||
defineProperty(Wallet.prototype, 'getContract', function(web3, address, abi) {
|
||||
return new AttachedContract(web3, this, address, new Contract(abi));
|
||||
});
|
||||
|
||||
module.exports = Wallet;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var ethers = __importStar(require("./ethers"));
|
||||
exports.ethers = ethers;
|
||||
__export(require("./ethers"));
|
||||
|
||||
127
lib/contract.js
127
lib/contract.js
@@ -1,127 +0,0 @@
|
||||
// "coder" is not MIT... We need to find/make an MIT compatible implementation
|
||||
var coder = require('../node_modules/web3/lib/solidity/coder.js');
|
||||
|
||||
var utils = require('./utils.js');
|
||||
|
||||
function defineFrozen(object, name, value) {
|
||||
var frozen = JSON.stringify(value);
|
||||
Object.defineProperty(object, name, {
|
||||
enumerable: true,
|
||||
get: function() { return JSON.parse(frozen); }
|
||||
});
|
||||
}
|
||||
|
||||
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'); }
|
||||
// @TODO: Check type are valid?
|
||||
result.push(params[i][key]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function Contract(abi) {
|
||||
if (!(this instanceof Contract)) { 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) {
|
||||
|
||||
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 + coder.encodeParams(inputTypes, params);
|
||||
if (method.constant) {
|
||||
result.type = 'call';
|
||||
result.parse = function(data) {
|
||||
return coder.decodeParams(
|
||||
outputTypes,
|
||||
utils.hexOrBuffer(data).toString('hex')
|
||||
)
|
||||
};
|
||||
} 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 coder.decodeParams(
|
||||
inputTypes,
|
||||
utils.hexOrBuffer(data).toString('hex')
|
||||
);
|
||||
};
|
||||
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);
|
||||
}
|
||||
|
||||
module.exports = Contract;
|
||||
258
lib/utils.js
258
lib/utils.js
@@ -1,258 +0,0 @@
|
||||
|
||||
var BN = require('../node_modules/elliptic/node_modules/bn.js/lib/bn.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 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 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 hexOrBuffer(value, name) {
|
||||
if (!Buffer.isBuffer(value)) {
|
||||
if (typeof(value) !== 'string' || !value.match(/^0x[0-9A-Fa-f]*$/)) {
|
||||
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,
|
||||
|
||||
bnToBuffer: bnToBuffer,
|
||||
hexOrBuffer: hexOrBuffer,
|
||||
hexlify: hexlify,
|
||||
stripZeros: stripZeros,
|
||||
|
||||
sha3: sha3,
|
||||
}
|
||||
7557
package-lock.json
generated
Normal file
7557
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
70
package.json
70
package.json
@@ -1,32 +1,68 @@
|
||||
{
|
||||
"name": "ethers-wallet",
|
||||
"version": "0.0.1",
|
||||
"name": "ethers",
|
||||
"version": "4.0.11",
|
||||
"description": "Ethereum wallet library.",
|
||||
"main": "index.js",
|
||||
"main": "./index.js",
|
||||
"types": "./index.d.ts",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"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": "0.2.4",
|
||||
"elliptic": "6.3.1",
|
||||
"pbkdf2": "3.0.4",
|
||||
"rlp": "2.0.0",
|
||||
"scrypt-js": "2.0.0",
|
||||
"uuid": "2.0.1"
|
||||
"@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",
|
||||
"uuid": "2.0.1",
|
||||
"xmlhttprequest": "1.8.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"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",
|
||||
"web3": "0.15.3"
|
||||
"browserify": "^16.2.3",
|
||||
"browserify-zlib": "^0.2.0",
|
||||
"dts-bundle": "^0.7.3",
|
||||
"eslint": "^5.0.1",
|
||||
"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.js.git"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
|
||||
101
providers/abstract-provider.d.ts
vendored
Normal file
101
providers/abstract-provider.d.ts
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
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 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 {
|
||||
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?: number | string | Promise<number | string>;
|
||||
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): 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));
|
||||
74
providers/base-provider.d.ts
vendored
Normal file
74
providers/base-provider.d.ts
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
import { BigNumber } from '../utils/bignumber';
|
||||
import { Provider } from './abstract-provider';
|
||||
import { Block, BlockTag, EventType, Filter, 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): Promise<Array<Log>>;
|
||||
getEtherPrice(): Promise<number>;
|
||||
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;
|
||||
}
|
||||
1178
providers/base-provider.js
Normal file
1178
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>>;
|
||||
}
|
||||
334
providers/etherscan-provider.js
Normal file
334
providers/etherscan-provider.js
Normal file
@@ -0,0 +1,334 @@
|
||||
"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;
|
||||
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.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;
|
||||
}
|
||||
110
providers/fallback-provider.js
Normal file
110
providers/fallback-provider.js
Normal file
@@ -0,0 +1,110 @@
|
||||
'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) {
|
||||
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;
|
||||
9
providers/infura-provider.d.ts
vendored
Normal file
9
providers/infura-provider.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import { JsonRpcProvider, JsonRpcSigner } from './json-rpc-provider';
|
||||
import { Networkish } from '../utils/networks';
|
||||
export declare class InfuraProvider extends JsonRpcProvider {
|
||||
readonly apiAccessToken: string;
|
||||
constructor(network?: Networkish, apiAccessToken?: string);
|
||||
protected _startPending(): void;
|
||||
getSigner(address?: string): JsonRpcSigner;
|
||||
listAccounts(): Promise<Array<string>>;
|
||||
}
|
||||
63
providers/infura-provider.js
Normal file
63
providers/infura-provider.js
Normal file
@@ -0,0 +1,63 @@
|
||||
'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 networks_1 = require("../utils/networks");
|
||||
var properties_1 = require("../utils/properties");
|
||||
var errors = __importStar(require("../errors"));
|
||||
var InfuraProvider = /** @class */ (function (_super) {
|
||||
__extends(InfuraProvider, _super);
|
||||
function InfuraProvider(network, apiAccessToken) {
|
||||
var _this = this;
|
||||
network = networks_1.getNetwork((network == null) ? 'homestead' : network);
|
||||
var host = null;
|
||||
switch (network.name) {
|
||||
case 'homestead':
|
||||
host = 'mainnet.infura.io';
|
||||
break;
|
||||
case 'ropsten':
|
||||
host = 'ropsten.infura.io';
|
||||
break;
|
||||
case 'rinkeby':
|
||||
host = 'rinkeby.infura.io';
|
||||
break;
|
||||
case 'kovan':
|
||||
host = 'kovan.infura.io';
|
||||
break;
|
||||
default:
|
||||
throw new Error('unsupported network');
|
||||
}
|
||||
_this = _super.call(this, 'https://' + host + '/' + (apiAccessToken || ''), network) || this;
|
||||
errors.checkNew(_this, InfuraProvider);
|
||||
properties_1.defineReadOnly(_this, 'apiAccessToken', apiAccessToken || null);
|
||||
return _this;
|
||||
}
|
||||
InfuraProvider.prototype._startPending = function () {
|
||||
console.log('WARNING: INFURA does not support pending filters');
|
||||
};
|
||||
InfuraProvider.prototype.getSigner = function (address) {
|
||||
errors.throwError('INFURA does not support signing', errors.UNSUPPORTED_OPERATION, { operation: 'getSigner' });
|
||||
return null;
|
||||
};
|
||||
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>;
|
||||
}
|
||||
78
providers/ipc-provider.js
Normal file
78
providers/ipc-provider.js
Normal file
@@ -0,0 +1,78 @@
|
||||
"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, { arg: '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 stream = net_1.default.connect(_this.path);
|
||||
stream.on('data', function (data) {
|
||||
try {
|
||||
resolve(JSON.parse(data.toString('utf8')).result);
|
||||
// @TODO: Better pull apart the error
|
||||
stream.destroy();
|
||||
}
|
||||
catch (error) {
|
||||
reject(error);
|
||||
stream.destroy();
|
||||
}
|
||||
});
|
||||
stream.on('end', function () {
|
||||
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;
|
||||
35
providers/json-rpc-provider.d.ts
vendored
Normal file
35
providers/json-rpc-provider.d.ts
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
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>;
|
||||
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;
|
||||
};
|
||||
}
|
||||
378
providers/json-rpc-provider.js
Normal file
378
providers/json-rpc-provider.js
Normal file
@@ -0,0 +1,378 @@
|
||||
'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 transaction_1 = require("../utils/transaction");
|
||||
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 = {};
|
||||
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.sendTransaction = function (transaction) {
|
||||
var _this = this;
|
||||
// Once populateTransaction resolves, the from address will be populated from getAddress
|
||||
var from = null;
|
||||
var getAddress = this.getAddress().then(function (address) {
|
||||
if (address) {
|
||||
from = address.toLowerCase();
|
||||
}
|
||||
return from;
|
||||
});
|
||||
return transaction_1.populateTransaction(transaction, this.provider, getAddress).then(function (tx) {
|
||||
var hexTx = JsonRpcProvider.hexlifyTransaction(tx);
|
||||
hexTx.from = from;
|
||||
return _this.provider.send('eth_sendTransaction', [hexTx]).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);
|
||||
});
|
||||
}, { onceBlock: _this.provider }).catch(function (error) {
|
||||
error.transactionHash = hash;
|
||||
throw error;
|
||||
});
|
||||
}, 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.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: 42,
|
||||
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.11";
|
||||
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
|
||||
};
|
||||
853
src.ts/contract.ts
Normal file
853
src.ts/contract.ts
Normal file
@@ -0,0 +1,853 @@
|
||||
'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 })
|
||||
}
|
||||
});
|
||||
|
||||
// Send to the contract address (after checking the contract is deployed)
|
||||
tx.to = contract.deployed().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 = this.interface.parseLog(log);
|
||||
if (parsed) {
|
||||
event.args = parsed.values;
|
||||
event.decode = parsed.decode;
|
||||
event.event = parsed.name;
|
||||
event.eventSignature = parsed.signature;
|
||||
}
|
||||
|
||||
event.removeListener = () => { return this.provider; }
|
||||
event.getBlock = () => {
|
||||
return this.provider.getBlock(receipt.blockHash);
|
||||
}
|
||||
event.getTransaction = () => {
|
||||
return this.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 _deployed: 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) => {
|
||||
console.log('ERROR: Cannot find Contract - ' + addressOrName);
|
||||
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 {
|
||||
console.log('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> {
|
||||
if (!this._deployed) {
|
||||
|
||||
// If we were just deployed, we know the transaction we should occur in
|
||||
if (this.deployTransaction) {
|
||||
this._deployed = 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._deployed = this.provider.getCode(this.address).then((code) => {
|
||||
if (code === '0x') {
|
||||
errors.throwError('contract not deployed', errors.UNSUPPORTED_OPERATION, {
|
||||
contractAddress: this.address,
|
||||
operation: 'getDeployed'
|
||||
});
|
||||
}
|
||||
return this;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return this._deployed;
|
||||
}
|
||||
|
||||
// @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) => {
|
||||
return event.eventFilter.eventTag !== eventFilter.eventTag
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
134
src.ts/errors.ts
Normal file
134
src.ts/errors.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
'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:
|
||||
// - arg: 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: 'setCersorship' });
|
||||
}
|
||||
|
||||
_censorErrors = !!censorship;
|
||||
_permanentCensorErrors = !!permanent;
|
||||
}
|
||||
|
||||
export function checkNormalize(): void {
|
||||
try {
|
||||
if (String.fromCharCode(0xe9).normalize('NFD') !== String.fromCharCode(0x65, 0x0301)) {
|
||||
throw new Error('broken')
|
||||
}
|
||||
} catch (error) {
|
||||
throwError('platform missing String.prototype.normalize', UNSUPPORTED_OPERATION, { operation: 'String.prototype.normalize' });
|
||||
}
|
||||
}
|
||||
79
src.ts/ethers.ts
Normal file
79
src.ts/ethers.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
'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 {
|
||||
return new providers.FallbackProvider([
|
||||
new providers.InfuraProvider(network),
|
||||
new providers.EtherscanProvider(network),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////
|
||||
// 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';
|
||||
161
src.ts/providers/abstract-provider.ts
Normal file
161
src.ts/providers/abstract-provider.ts
Normal file
@@ -0,0 +1,161 @@
|
||||
|
||||
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>>,
|
||||
}
|
||||
|
||||
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 {
|
||||
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?: number | string | Promise<number | string>,
|
||||
|
||||
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): 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));
|
||||
|
||||
1313
src.ts/providers/base-provider.ts
Normal file
1313
src.ts/providers/base-provider.ts
Normal file
File diff suppressed because it is too large
Load Diff
338
src.ts/providers/etherscan-provider.ts
Normal file
338
src.ts/providers/etherscan-provider.ts
Normal file
@@ -0,0 +1,338 @@
|
||||
|
||||
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;
|
||||
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.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;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
105
src.ts/providers/fallback-provider.ts
Normal file
105
src.ts/providers/fallback-provider.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
'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) { 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
|
||||
};
|
||||
|
||||
59
src.ts/providers/infura-provider.ts
Normal file
59
src.ts/providers/infura-provider.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
'use strict';
|
||||
|
||||
import { JsonRpcProvider, JsonRpcSigner } from './json-rpc-provider';
|
||||
|
||||
import { getNetwork } from '../utils/networks';
|
||||
import { defineReadOnly } from '../utils/properties';
|
||||
|
||||
// Imported Types
|
||||
import { Networkish } from '../utils/networks';
|
||||
|
||||
import * as errors from '../errors';
|
||||
|
||||
export class InfuraProvider extends JsonRpcProvider {
|
||||
readonly apiAccessToken: string;
|
||||
|
||||
constructor(network?: Networkish, apiAccessToken?: string) {
|
||||
network = getNetwork((network == null) ? 'homestead': network);
|
||||
|
||||
var host = null;
|
||||
switch(network.name) {
|
||||
case 'homestead':
|
||||
host = 'mainnet.infura.io';
|
||||
break;
|
||||
case 'ropsten':
|
||||
host = 'ropsten.infura.io';
|
||||
break;
|
||||
case 'rinkeby':
|
||||
host = 'rinkeby.infura.io';
|
||||
break;
|
||||
case 'kovan':
|
||||
host = 'kovan.infura.io';
|
||||
break;
|
||||
default:
|
||||
throw new Error('unsupported network');
|
||||
}
|
||||
|
||||
super('https://' + host + '/' + (apiAccessToken || ''), network);
|
||||
errors.checkNew(this, InfuraProvider);
|
||||
|
||||
defineReadOnly(this, 'apiAccessToken', apiAccessToken || null);
|
||||
}
|
||||
|
||||
protected _startPending(): void {
|
||||
console.log('WARNING: INFURA does not support pending filters');
|
||||
}
|
||||
|
||||
getSigner(address?: string): JsonRpcSigner {
|
||||
errors.throwError(
|
||||
'INFURA does not support signing',
|
||||
errors.UNSUPPORTED_OPERATION,
|
||||
{ operation: 'getSigner' }
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
listAccounts(): Promise<Array<string>> {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
}
|
||||
67
src.ts/providers/ipc-provider.ts
Normal file
67
src.ts/providers/ipc-provider.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
|
||||
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, { arg: '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).
|
||||
|
||||
var payload = JSON.stringify({
|
||||
method: method,
|
||||
params: params,
|
||||
id: 42,
|
||||
jsonrpc: "2.0"
|
||||
});
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
var stream = net.connect(this.path);
|
||||
stream.on('data', function(data) {
|
||||
try {
|
||||
resolve(JSON.parse(data.toString('utf8')).result);
|
||||
// @TODO: Better pull apart the error
|
||||
stream.destroy();
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
stream.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
stream.on('end', function() {
|
||||
stream.destroy();
|
||||
});
|
||||
|
||||
stream.on('error', function(error) {
|
||||
reject(error);
|
||||
stream.destroy();
|
||||
});
|
||||
stream.write(payload);
|
||||
stream.end();
|
||||
});
|
||||
}
|
||||
}
|
||||
405
src.ts/providers/json-rpc-provider.ts
Normal file
405
src.ts/providers/json-rpc-provider.ts
Normal file
@@ -0,0 +1,405 @@
|
||||
'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, shallowCopy } from '../utils/properties';
|
||||
import { populateTransaction } from '../utils/transaction';
|
||||
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
|
||||
var 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 = {};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse> {
|
||||
|
||||
// Once populateTransaction resolves, the from address will be populated from getAddress
|
||||
let from: string = null;
|
||||
let getAddress = this.getAddress().then((address) => {
|
||||
if (address) { from = address.toLowerCase(); }
|
||||
return from;
|
||||
});
|
||||
|
||||
return populateTransaction(transaction, this.provider, getAddress).then((tx) => {
|
||||
let hexTx = JsonRpcProvider.hexlifyTransaction(tx);
|
||||
hexTx.from = from;
|
||||
return this.provider.send('eth_sendTransaction', [ hexTx ]).then((hash) => {
|
||||
return poll(() => {
|
||||
return this.provider.getTransaction(hash).then((tx: TransactionResponse) => {
|
||||
if (tx === null) { return undefined; }
|
||||
return this.provider._wrapTransaction(tx, hash);
|
||||
});
|
||||
}, { onceBlock: this.provider }).catch((error: Error) => {
|
||||
(<any>error).transactionHash = hash;
|
||||
throw error;
|
||||
});
|
||||
}, (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;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
signMessage(message: Arrayish | string): Promise<string> {
|
||||
var 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> {
|
||||
var 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: 42,
|
||||
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; }
|
||||
var self = this;
|
||||
|
||||
var 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; }
|
||||
|
||||
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((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);
|
||||
}
|
||||
4
src.ts/shims/shims.ts
Normal file
4
src.ts/shims/shims.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
require('setimmediate');
|
||||
|
||||
export const platform = "browser";
|
||||
8
src.ts/shims/wordlists.ts
Normal file
8
src.ts/shims/wordlists.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
import { Wordlist } from '../utils/wordlist';
|
||||
|
||||
import { langEn as _en } from '../wordlists/lang-en';
|
||||
|
||||
const en: Wordlist = _en;
|
||||
|
||||
export { en }
|
||||
10
src.ts/shims/xmlhttprequest.ts
Normal file
10
src.ts/shims/xmlhttprequest.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
try {
|
||||
module.exports.XMLHttpRequest = XMLHttpRequest;
|
||||
} catch(error) {
|
||||
console.log('Warning: XMLHttpRequest is not defined');
|
||||
module.exports.XMLHttpRequest = null;
|
||||
}
|
||||
|
||||
export { }
|
||||
1084
src.ts/utils/abi-coder.ts
Normal file
1084
src.ts/utils/abi-coder.ts
Normal file
File diff suppressed because it is too large
Load Diff
142
src.ts/utils/address.ts
Normal file
142
src.ts/utils/address.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
'use strict';
|
||||
|
||||
// We use this for base 36 maths
|
||||
import BN from 'bn.js';
|
||||
|
||||
import { arrayify, stripZeros, hexlify } from './bytes';
|
||||
import { BigNumber } from './bignumber';
|
||||
import { keccak256 } from './keccak256';
|
||||
import { encode } from './rlp';
|
||||
|
||||
import errors = require('../errors');
|
||||
|
||||
///////////////////////////////
|
||||
// Imported Types
|
||||
|
||||
import { Arrayish } from './bytes';
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
|
||||
function getChecksumAddress(address: string): string {
|
||||
if (typeof(address) !== 'string' || !address.match(/^0x[0-9A-Fa-f]{40}$/)) {
|
||||
errors.throwError('invalid address', errors.INVALID_ARGUMENT, { arg: 'address', value: address });
|
||||
}
|
||||
|
||||
address = address.toLowerCase();
|
||||
|
||||
let chars = address.substring(2).split('');
|
||||
|
||||
let hashed = new Uint8Array(40);
|
||||
for (let i = 0; i < 40; i++) {
|
||||
hashed[i] = chars[i].charCodeAt(0);
|
||||
}
|
||||
hashed = arrayify(keccak256(hashed));
|
||||
|
||||
for (var i = 0; i < 40; i += 2) {
|
||||
if ((hashed[i >> 1] >> 4) >= 8) {
|
||||
chars[i] = chars[i].toUpperCase();
|
||||
}
|
||||
if ((hashed[i >> 1] & 0x0f) >= 8) {
|
||||
chars[i + 1] = chars[i + 1].toUpperCase();
|
||||
}
|
||||
}
|
||||
|
||||
return '0x' + chars.join('');
|
||||
}
|
||||
|
||||
// Shims for environments that are missing some required constants and functions
|
||||
var MAX_SAFE_INTEGER: number = 0x1fffffffffffff;
|
||||
|
||||
function log10(x: number): number {
|
||||
if (Math.log10) { return Math.log10(x); }
|
||||
return Math.log(x) / Math.LN10;
|
||||
}
|
||||
|
||||
|
||||
// See: https://en.wikipedia.org/wiki/International_Bank_Account_Number
|
||||
|
||||
// Create lookup table
|
||||
var ibanLookup: { [character: string]: string } = {};
|
||||
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(log10(MAX_SAFE_INTEGER));
|
||||
|
||||
function ibanChecksum(address: string): string {
|
||||
address = address.toUpperCase();
|
||||
address = address.substring(4) + address.substring(0, 2) + '00';
|
||||
|
||||
var expanded = '';
|
||||
address.split('').forEach(function(c) {
|
||||
expanded += ibanLookup[c];
|
||||
});
|
||||
|
||||
// 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;
|
||||
};
|
||||
|
||||
export function getAddress(address: string): string {
|
||||
var result = null;
|
||||
|
||||
if (typeof(address) !== 'string') {
|
||||
errors.throwError('invalid address', errors.INVALID_ARGUMENT, { arg: 'address', value: 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) {
|
||||
errors.throwError('bad address checksum', errors.INVALID_ARGUMENT, { arg: 'address', value: address });
|
||||
}
|
||||
|
||||
// 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)) {
|
||||
errors.throwError('bad icap checksum', errors.INVALID_ARGUMENT, { arg: 'address', value: address });
|
||||
}
|
||||
|
||||
result = (new BN.BN(address.substring(4), 36)).toString(16);
|
||||
while (result.length < 40) { result = '0' + result; }
|
||||
result = getChecksumAddress('0x' + result);
|
||||
|
||||
} else {
|
||||
errors.throwError('invalid address', errors.INVALID_ARGUMENT, { arg: 'address', value: address });
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function getIcapAddress(address: string): string {
|
||||
var base36 = (new BN.BN(getAddress(address).substring(2), 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
|
||||
export function getContractAddress(transaction: { from: string, nonce: Arrayish | BigNumber | number }) {
|
||||
if (!transaction.from) { throw new Error('missing from address'); }
|
||||
var nonce = transaction.nonce;
|
||||
|
||||
return getAddress('0x' + keccak256(encode([
|
||||
getAddress(transaction.from),
|
||||
stripZeros(hexlify(nonce))
|
||||
])).substring(26));
|
||||
}
|
||||
|
||||
25
src.ts/utils/base64.ts
Normal file
25
src.ts/utils/base64.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
'use strict';
|
||||
|
||||
import { arrayify } from './bytes';
|
||||
|
||||
///////////////////////////////
|
||||
// Imported Types
|
||||
|
||||
import { Arrayish } from './bytes';
|
||||
|
||||
///////////////////////////////
|
||||
/*
|
||||
declare class Buffer implements ArrayLike<number> {
|
||||
constructor(data: any, encoding?: string);
|
||||
toString(encoding?: string): any;
|
||||
[key: number]: number;
|
||||
length: number;
|
||||
}
|
||||
*/
|
||||
export function decode(textData: string): Uint8Array {
|
||||
return arrayify(new Uint8Array(Buffer.from(textData, 'base64')));
|
||||
};
|
||||
|
||||
export function encode(data: Arrayish): string {
|
||||
return Buffer.from(arrayify(data)).toString('base64');
|
||||
}
|
||||
193
src.ts/utils/bignumber.ts
Normal file
193
src.ts/utils/bignumber.ts
Normal file
@@ -0,0 +1,193 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* BigNumber
|
||||
*
|
||||
* A wrapper around the BN.js object. We use the BN.js library
|
||||
* because it is used by elliptic, so it is required regardles.
|
||||
*
|
||||
*/
|
||||
|
||||
import BN from 'bn.js';
|
||||
|
||||
import { Hexable, hexlify, isArrayish, isHexString } from './bytes';
|
||||
import { defineReadOnly, isType, setType } from './properties';
|
||||
|
||||
import { Arrayish } from './bytes';
|
||||
|
||||
import * as errors from '../errors';
|
||||
|
||||
const BN_1 = new BN.BN(-1);
|
||||
|
||||
function toHex(bn: BN.BN): string {
|
||||
let value = bn.toString(16);
|
||||
if (value[0] === '-') {
|
||||
if ((value.length % 2) === 0) {
|
||||
return '-0x0' + value.substring(1);
|
||||
}
|
||||
return "-0x" + value.substring(1);
|
||||
}
|
||||
if ((value.length % 2) === 1) { return '0x0' + value; }
|
||||
return '0x' + value;
|
||||
}
|
||||
|
||||
function toBN(value: BigNumberish): BN.BN {
|
||||
return _bnify(bigNumberify(value));
|
||||
}
|
||||
|
||||
function toBigNumber(bn: BN.BN): BigNumber {
|
||||
return new BigNumber(toHex(bn));
|
||||
}
|
||||
|
||||
function _bnify(value: BigNumber): BN.BN {
|
||||
let hex: string = (<any>value)._hex;
|
||||
if (hex[0] === '-') {
|
||||
return (new BN.BN(hex.substring(3), 16)).mul(BN_1);
|
||||
}
|
||||
return new BN.BN(hex.substring(2), 16);
|
||||
}
|
||||
|
||||
|
||||
export type BigNumberish = BigNumber | string | number | Arrayish;
|
||||
|
||||
export class BigNumber implements Hexable {
|
||||
private readonly _hex: string;
|
||||
|
||||
constructor(value: BigNumberish) {
|
||||
errors.checkNew(this, BigNumber);
|
||||
setType(this, 'BigNumber');
|
||||
|
||||
if (typeof(value) === 'string') {
|
||||
if (isHexString(value)) {
|
||||
if (value == '0x') { value = '0x0'; }
|
||||
defineReadOnly(this, '_hex', value);
|
||||
|
||||
} else if (value[0] === '-' && isHexString(value.substring(1))) {
|
||||
defineReadOnly(this, '_hex', value);
|
||||
|
||||
} else if (value.match(/^-?[0-9]*$/)) {
|
||||
if (value == '') { value = '0'; }
|
||||
defineReadOnly(this, '_hex', toHex(new BN.BN(value)));
|
||||
|
||||
} else {
|
||||
errors.throwError('invalid BigNumber string value', errors.INVALID_ARGUMENT, { arg: 'value', value: value });
|
||||
}
|
||||
|
||||
} else if (typeof(value) === 'number') {
|
||||
if (parseInt(String(value)) !== value) {
|
||||
errors.throwError('underflow', errors.NUMERIC_FAULT, { operation: 'setValue', fault: 'underflow', value: value, outputValue: parseInt(String(value)) });
|
||||
}
|
||||
try {
|
||||
defineReadOnly(this, '_hex', toHex(new BN.BN(value)));
|
||||
} catch (error) {
|
||||
errors.throwError('overflow', errors.NUMERIC_FAULT, { operation: 'setValue', fault: 'overflow', details: error.message });
|
||||
}
|
||||
|
||||
} else if (value instanceof BigNumber) {
|
||||
defineReadOnly(this, '_hex', value._hex);
|
||||
|
||||
} else if ((<any>value).toHexString) {
|
||||
defineReadOnly(this, '_hex', toHex(toBN((<any>value).toHexString())));
|
||||
|
||||
} else if ((<any>value)._hex && isHexString((<any>value)._hex)) {
|
||||
defineReadOnly(this, '_hex', (<any>value)._hex);
|
||||
|
||||
} else if (isArrayish(value)) {
|
||||
defineReadOnly(this, '_hex', toHex(new BN.BN(hexlify(value).substring(2), 16)));
|
||||
|
||||
} else {
|
||||
errors.throwError('invalid BigNumber value', errors.INVALID_ARGUMENT, { arg: 'value', value: value });
|
||||
}
|
||||
}
|
||||
|
||||
fromTwos(value: number): BigNumber {
|
||||
return toBigNumber(_bnify(this).fromTwos(value));
|
||||
}
|
||||
|
||||
toTwos(value: number): BigNumber {
|
||||
return toBigNumber(_bnify(this).toTwos(value));
|
||||
}
|
||||
|
||||
add(other: BigNumberish): BigNumber {
|
||||
return toBigNumber(_bnify(this).add(toBN(other)));
|
||||
}
|
||||
|
||||
sub(other: BigNumberish): BigNumber {
|
||||
return toBigNumber(_bnify(this).sub(toBN(other)));
|
||||
}
|
||||
|
||||
div(other: BigNumberish): BigNumber {
|
||||
let o: BigNumber = bigNumberify(other);
|
||||
if (o.isZero()) {
|
||||
errors.throwError('division by zero', errors.NUMERIC_FAULT, { operation: 'divide', fault: 'division by zero' });
|
||||
}
|
||||
return toBigNumber(_bnify(this).div(toBN(other)));
|
||||
}
|
||||
|
||||
mul(other: BigNumberish): BigNumber {
|
||||
return toBigNumber(_bnify(this).mul(toBN(other)));
|
||||
}
|
||||
|
||||
mod(other: BigNumberish): BigNumber {
|
||||
return toBigNumber(_bnify(this).mod(toBN(other)));
|
||||
}
|
||||
|
||||
pow(other: BigNumberish): BigNumber {
|
||||
return toBigNumber(_bnify(this).pow(toBN(other)));
|
||||
}
|
||||
|
||||
maskn(value: number): BigNumber {
|
||||
return toBigNumber(_bnify(this).maskn(value));
|
||||
}
|
||||
|
||||
eq(other: BigNumberish): boolean {
|
||||
return _bnify(this).eq(toBN(other));
|
||||
}
|
||||
|
||||
lt(other: BigNumberish): boolean {
|
||||
return _bnify(this).lt(toBN(other));
|
||||
}
|
||||
|
||||
lte(other: BigNumberish): boolean {
|
||||
return _bnify(this).lte(toBN(other));
|
||||
}
|
||||
|
||||
gt(other: BigNumberish): boolean {
|
||||
return _bnify(this).gt(toBN(other));
|
||||
}
|
||||
|
||||
gte(other: BigNumberish): boolean {
|
||||
return _bnify(this).gte(toBN(other));
|
||||
}
|
||||
|
||||
isZero(): boolean {
|
||||
return _bnify(this).isZero();
|
||||
}
|
||||
|
||||
toNumber(): number {
|
||||
try {
|
||||
return _bnify(this).toNumber();
|
||||
} catch (error) {
|
||||
errors.throwError('overflow', errors.NUMERIC_FAULT, { operation: 'setValue', fault: 'overflow', details: error.message });
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return _bnify(this).toString(10);
|
||||
}
|
||||
|
||||
toHexString(): string {
|
||||
return this._hex;
|
||||
}
|
||||
|
||||
static isBigNumber(value: any): value is BigNumber {
|
||||
return isType(value, 'BigNumber');
|
||||
}
|
||||
}
|
||||
|
||||
export function bigNumberify(value: BigNumberish): BigNumber {
|
||||
if (BigNumber.isBigNumber(value)) { return value; }
|
||||
return new BigNumber(value);
|
||||
}
|
||||
|
||||
314
src.ts/utils/bytes.ts
Normal file
314
src.ts/utils/bytes.ts
Normal file
@@ -0,0 +1,314 @@
|
||||
/**
|
||||
* Conversion Utilities
|
||||
*
|
||||
*/
|
||||
|
||||
import * as errors from '../errors';
|
||||
|
||||
///////////////////////////////
|
||||
// Imported Types
|
||||
|
||||
import { Arrayish } from './bytes';
|
||||
|
||||
///////////////////////////////
|
||||
// Exported Types
|
||||
|
||||
export type Arrayish = string | ArrayLike<number>;
|
||||
|
||||
export interface Hexable {
|
||||
toHexString(): string;
|
||||
}
|
||||
|
||||
export interface Signature {
|
||||
r: string;
|
||||
s: string;
|
||||
|
||||
/* At least one of the following MUST be specified; the other will be derived */
|
||||
recoveryParam?: number;
|
||||
v?: number;
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
|
||||
export function isHexable(value: any): value is Hexable {
|
||||
return !!(value.toHexString);
|
||||
}
|
||||
|
||||
function addSlice(array: Uint8Array): Uint8Array {
|
||||
if (array.slice) { return array; }
|
||||
|
||||
array.slice = function() {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
return new Uint8Array(Array.prototype.slice.apply(array, args));
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
export function isArrayish(value: any): value is Arrayish {
|
||||
if (!value || parseInt(String(value.length)) != value.length || typeof(value) === 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < value.length; i++) {
|
||||
var v = value[i];
|
||||
if (v < 0 || v >= 256 || parseInt(String(v)) != v) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function arrayify(value: Arrayish | Hexable): Uint8Array {
|
||||
if (value == null) {
|
||||
errors.throwError('cannot convert null value to array', errors.INVALID_ARGUMENT, { arg: 'value', value: value });
|
||||
}
|
||||
|
||||
if (isHexable(value)) {
|
||||
value = value.toHexString();
|
||||
}
|
||||
|
||||
if (typeof(value) === 'string') {
|
||||
let match = value.match(/^(0x)?[0-9a-fA-F]*$/);
|
||||
|
||||
if (!match) {
|
||||
errors.throwError('invalid hexidecimal string', errors.INVALID_ARGUMENT, { arg: 'value', value: value });
|
||||
}
|
||||
|
||||
if (match[1] !== '0x') {
|
||||
errors.throwError('hex string must have 0x prefix', errors.INVALID_ARGUMENT, { arg: 'value', value: value });
|
||||
}
|
||||
|
||||
value = value.substring(2);
|
||||
if (value.length % 2) { value = '0' + value; }
|
||||
|
||||
var result = [];
|
||||
for (var i = 0; i < value.length; i += 2) {
|
||||
result.push(parseInt(value.substr(i, 2), 16));
|
||||
}
|
||||
|
||||
return addSlice(new Uint8Array(result));
|
||||
}
|
||||
|
||||
if (isArrayish(value)) {
|
||||
return addSlice(new Uint8Array(value));
|
||||
}
|
||||
|
||||
errors.throwError('invalid arrayify value', null, { arg: 'value', value: value, type: typeof(value) });
|
||||
return null;
|
||||
}
|
||||
|
||||
export function concat(objects: Array<Arrayish>): Uint8Array {
|
||||
var arrays = [];
|
||||
var length = 0;
|
||||
for (var i = 0; i < objects.length; i++) {
|
||||
var object = arrayify(objects[i])
|
||||
arrays.push(object);
|
||||
length += object.length;
|
||||
}
|
||||
|
||||
var result = new Uint8Array(length);
|
||||
var offset = 0;
|
||||
for (var i = 0; i < arrays.length; i++) {
|
||||
result.set(arrays[i], offset);
|
||||
offset += arrays[i].length;
|
||||
}
|
||||
|
||||
return addSlice(result);
|
||||
}
|
||||
|
||||
export function stripZeros(value: Arrayish): Uint8Array {
|
||||
let result: Uint8Array = arrayify(value);
|
||||
|
||||
if (result.length === 0) { return result; }
|
||||
|
||||
// Find the first non-zero entry
|
||||
var start = 0;
|
||||
while (result[start] === 0) { start++ }
|
||||
|
||||
// If we started with zeros, strip them
|
||||
if (start) {
|
||||
result = result.slice(start);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function padZeros(value: Arrayish, length: number): Uint8Array {
|
||||
value = arrayify(value);
|
||||
|
||||
if (length < value.length) { throw new Error('cannot pad'); }
|
||||
|
||||
var result = new Uint8Array(length);
|
||||
result.set(value, length - value.length);
|
||||
return addSlice(result);
|
||||
}
|
||||
|
||||
|
||||
export function isHexString(value: any, length?: number): boolean {
|
||||
if (typeof(value) !== 'string' || !value.match(/^0x[0-9A-Fa-f]*$/)) {
|
||||
return false
|
||||
}
|
||||
if (length && value.length !== 2 + 2 * length) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
const HexCharacters: string = '0123456789abcdef';
|
||||
|
||||
export function hexlify(value: Arrayish | Hexable | number): string {
|
||||
|
||||
if (isHexable(value)) {
|
||||
return value.toHexString();
|
||||
}
|
||||
|
||||
if (typeof(value) === 'number') {
|
||||
if (value < 0) {
|
||||
errors.throwError('cannot hexlify negative value', errors.INVALID_ARGUMENT, { arg: 'value', value: value });
|
||||
}
|
||||
|
||||
var hex = '';
|
||||
while (value) {
|
||||
hex = HexCharacters[value & 0x0f] + hex;
|
||||
value = Math.floor(value / 16);
|
||||
}
|
||||
|
||||
if (hex.length) {
|
||||
if (hex.length % 2) { hex = '0' + hex; }
|
||||
return '0x' + hex;
|
||||
}
|
||||
|
||||
return '0x00';
|
||||
}
|
||||
|
||||
if (typeof(value) === 'string') {
|
||||
let match = value.match(/^(0x)?[0-9a-fA-F]*$/);
|
||||
|
||||
if (!match) {
|
||||
errors.throwError('invalid hexidecimal string', errors.INVALID_ARGUMENT, { arg: 'value', value: value });
|
||||
}
|
||||
|
||||
if (match[1] !== '0x') {
|
||||
errors.throwError('hex string must have 0x prefix', errors.INVALID_ARGUMENT, { arg: 'value', value: value });
|
||||
}
|
||||
|
||||
if (value.length % 2) {
|
||||
value = '0x0' + value.substring(2);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
if (isArrayish(value)) {
|
||||
var result = [];
|
||||
for (var i = 0; i < value.length; i++) {
|
||||
var v = value[i];
|
||||
result.push(HexCharacters[(v & 0xf0) >> 4] + HexCharacters[v & 0x0f]);
|
||||
}
|
||||
return '0x' + result.join('');
|
||||
}
|
||||
|
||||
errors.throwError('invalid hexlify value', null, { arg: 'value', value: value });
|
||||
return 'never';
|
||||
}
|
||||
|
||||
export function hexDataLength(data: string) {
|
||||
if (!isHexString(data) || (data.length % 2) !== 0) {
|
||||
return null;
|
||||
}
|
||||
return (data.length - 2) / 2;
|
||||
}
|
||||
|
||||
export function hexDataSlice(data: string, offset: number, endOffset?: number): string {
|
||||
if (!isHexString(data)) {
|
||||
errors.throwError('invalid hex data', errors.INVALID_ARGUMENT, { arg: 'value', value: data });
|
||||
}
|
||||
if ((data.length % 2) !== 0) {
|
||||
errors.throwError('hex data length must be even', errors.INVALID_ARGUMENT, { arg: 'value', value: data });
|
||||
}
|
||||
offset = 2 + 2 * offset;
|
||||
|
||||
if (endOffset != null) {
|
||||
return '0x' + data.substring(offset, 2 + 2 * endOffset);
|
||||
}
|
||||
|
||||
return '0x' + data.substring(offset);
|
||||
}
|
||||
|
||||
export function hexStripZeros(value: string): string {
|
||||
if (!isHexString(value)) {
|
||||
errors.throwError('invalid hex string', errors.INVALID_ARGUMENT, { arg: 'value', value: value });
|
||||
}
|
||||
while (value.length > 3 && value.substring(0, 3) === '0x0') {
|
||||
value = '0x' + value.substring(3);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export function hexZeroPad(value: string, length: number): string {
|
||||
if (!isHexString(value)) {
|
||||
errors.throwError('invalid hex string', errors.INVALID_ARGUMENT, { arg: 'value', value: value });
|
||||
}
|
||||
|
||||
while (value.length < 2 * length + 2) {
|
||||
value = '0x0' + value.substring(2);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function isSignature(value: any): value is Signature {
|
||||
return (value && value.r != null && value.s != null);
|
||||
}
|
||||
|
||||
export function splitSignature(signature: Arrayish | Signature): Signature {
|
||||
let v = 0;
|
||||
let r = '0x', s = '0x';
|
||||
|
||||
if (isSignature(signature)) {
|
||||
if (signature.v == null && signature.recoveryParam == null) {
|
||||
errors.throwError('at least on of recoveryParam or v must be specified', errors.INVALID_ARGUMENT, { argument: 'signature', value: signature });
|
||||
}
|
||||
r = hexZeroPad(signature.r, 32);
|
||||
s = hexZeroPad(signature.s, 32);
|
||||
|
||||
v = signature.v;
|
||||
if (typeof(v) === 'string') { v = parseInt(v, 16); }
|
||||
|
||||
let recoveryParam = signature.recoveryParam;
|
||||
if (recoveryParam == null && signature.v != null) {
|
||||
recoveryParam = 1 - (v % 2);
|
||||
}
|
||||
v = 27 + recoveryParam;
|
||||
|
||||
} else {
|
||||
let bytes: Uint8Array = arrayify(signature);
|
||||
if (bytes.length !== 65) {
|
||||
throw new Error('invalid signature');
|
||||
}
|
||||
r = hexlify(bytes.slice(0, 32));
|
||||
s = hexlify(bytes.slice(32, 64));
|
||||
|
||||
v = bytes[64];
|
||||
if (v !== 27 && v !== 28) {
|
||||
v = 27 + (v % 2);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
r: r,
|
||||
s: s,
|
||||
recoveryParam: (v - 27),
|
||||
v: v
|
||||
}
|
||||
}
|
||||
|
||||
export function joinSignature(signature: Signature): string {
|
||||
signature = splitSignature(signature);
|
||||
|
||||
return hexlify(concat([
|
||||
signature.r,
|
||||
signature.s,
|
||||
(signature.recoveryParam ? '0x1c': '0x1b')
|
||||
]));
|
||||
}
|
||||
|
||||
54
src.ts/utils/hash.ts
Normal file
54
src.ts/utils/hash.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
'use strict';
|
||||
|
||||
import { concat, hexlify } from './bytes';
|
||||
import { toUtf8Bytes } from './utf8';
|
||||
import { keccak256 } from './keccak256';
|
||||
|
||||
///////////////////////////////
|
||||
// Imported Types
|
||||
|
||||
import { Arrayish } from './bytes';
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
var Zeros = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
var Partition = new RegExp("^((.*)\\.)?([^.]+)$");
|
||||
var UseSTD3ASCIIRules = new RegExp("^[a-z0-9.-]*$");
|
||||
|
||||
export function namehash(name: string): string {
|
||||
name = name.toLowerCase();
|
||||
|
||||
// Supporting the full UTF-8 space requires additional (and large)
|
||||
// libraries, so for now we simply do not support them.
|
||||
// It should be fairly easy in the future to support systems with
|
||||
// String.normalize, but that is future work.
|
||||
if (!name.match(UseSTD3ASCIIRules)) {
|
||||
throw new Error('contains invalid UseSTD3ASCIIRules characters');
|
||||
}
|
||||
|
||||
var result: string | Uint8Array = Zeros;
|
||||
while (name.length) {
|
||||
var partition = name.match(Partition);
|
||||
var label = toUtf8Bytes(partition[3]);
|
||||
result = keccak256(concat([result, keccak256(label)]));
|
||||
|
||||
name = partition[2] || '';
|
||||
}
|
||||
|
||||
return hexlify(result);
|
||||
}
|
||||
|
||||
|
||||
export function id(text: string): string {
|
||||
return keccak256(toUtf8Bytes(text));
|
||||
}
|
||||
|
||||
export function hashMessage(message: Arrayish | string): string {
|
||||
var payload = concat([
|
||||
toUtf8Bytes('\x19Ethereum Signed Message:\n'),
|
||||
toUtf8Bytes(String(message.length)),
|
||||
((typeof(message) === 'string') ? toUtf8Bytes(message): message)
|
||||
]);
|
||||
return keccak256(payload);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user