Compare commits

..

704 Commits

Author SHA1 Message Date
Richard Moore
92c978e5c2 Fixed error in throwing an error for ABI decode (#539). 2019-06-10 01:57:03 -04:00
Richard Moore
34397fa2aa Updated dist files. 2019-05-24 19:41:38 -04:00
Richard Moore
19587eea3f Warn on deprecated INFURA API Token; use Project ID instead (#462). 2019-05-24 14:56:47 -04:00
Richard Moore
4a9373e773 Fixed typo in error message (#470). 2019-05-24 14:48:01 -04:00
Richard Moore
2997bae935 Export poll function (#514). 2019-05-24 14:38:54 -04:00
Richard Moore
b0bd9ee162 Updated dist files. 2019-05-23 18:58:05 -04:00
Richard Moore
7075c8c235 Fixed error message for unconfigured ENS names (#504). 2019-05-23 18:51:08 -04:00
Richard Moore
a4a532fe8e Fixed security recommendations (development deps only; not a problem, but quiets automatic audits). 2019-05-14 18:11:45 -04:00
Richard Moore
77c771bf76 Updated dist files. 2019-05-14 17:45:13 -04:00
Richard Moore
c93b48920e Increment JSON-RPC ID for JsonRpcProviders for environments that unsafely manage callbacks (#489). 2019-04-18 13:49:37 +02:00
Richard Moore
04c92bb8d5 Updated dist files. 2019-03-08 14:29:59 -05:00
Richard Moore
16c9745326 Added goerli to InfuraProvider (#421). 2019-03-08 14:28:39 -05:00
Richard Moore
05648177aa Updated dist files. 2019-02-15 13:44:33 -05:00
Richard Moore
f318fd9cf1 Added fastRetry to polling for JsonRpcSigner to improve polling for sent transactions (#402). 2019-02-15 13:42:12 -05:00
Richard Moore
c15a89832b Fix waitForTransaction delay (#424). 2019-02-15 13:32:08 -05:00
Richard Moore
94b0abc240 Updated dist files. 2019-02-12 00:56:38 -05:00
Richard Moore
72edcd054f Fixed waitForTransaction and removeListener (#410). 2019-02-12 00:54:32 -05:00
Richard Moore
e4a2f8ac6c Updated BIP39 list in readme. 2019-02-11 19:24:48 -05:00
Richard Moore
c6d7b31a84 Updated dist files. 2019-02-11 19:24:27 -05:00
Richard Moore
0ed983a264 Fixed support for calling self-destructed contracts (#411). 2019-02-11 19:21:53 -05:00
Richard Moore
8fab48a380 Updated balance address for Goerli test cases. 2019-02-11 19:19:02 -05:00
Richard Moore
86e815999d Updated dist files. 2019-02-08 19:58:41 -05:00
Richard Moore
a2306f7870 Fixed utils test case for phantomjs. 2019-02-08 19:52:29 -05:00
Richard Moore
60b75c10d7 Initial support for EIP-234; filter by blockHash (#412). 2019-02-08 19:49:12 -05:00
Richard Moore
41c2c8a729 Fixed out-of-safe-range hexlify values to throw an exception (#420). 2019-02-08 17:06:46 -05:00
Richard Moore
9785eed8dd Added goerli testnet support (#421). 2019-02-08 16:50:06 -05:00
Richard Moore
527de7ba5e Updated dist files. 2019-02-04 16:09:35 -05:00
Richard Moore
14484e566e Fixed missing TypeArray slice on constrained environments. 2019-02-04 16:03:45 -05:00
Richard Moore
429af2c40d Fixed test-hdnode for phantomjs; does not support let keyword. 2019-02-01 23:00:01 -05:00
Richard Moore
11fffd1690 Updated dist files. 2019-02-01 19:50:10 -05:00
Richard Moore
af3aed4580 Added xpub and xpriv deserialization (#405). 2019-02-01 19:47:20 -05:00
Richard Moore
3a3764bdb4 Added xpub and xpriv test cases for HD nodes (#405). 2019-02-01 19:46:18 -05:00
Richard Moore
18ee2c518c Support for xpub and xpriv derivation and generating extended keys; no fromExtendedKey yet (#405). 2019-02-01 18:39:50 -05:00
Richard Moore
36172f7f7b Updated dist files. 2019-01-25 19:10:44 -05:00
Richard Moore
908258f8d4 Fixed duplicate events from triggering (#404). 2019-01-25 19:08:31 -05:00
Richard Moore
774b2d5fee Updated dist files. 2019-01-24 16:53:44 -05:00
Richard Moore
b5f720ace6 Ganache does not include from in receipts (#400). 2019-01-24 16:52:24 -05:00
Richard Moore
9f201c386e Updated dist files. 2019-01-23 16:30:12 -05:00
Richard Moore
700dd34137 Added to and from for Transaction Receipts (#398). 2019-01-23 16:25:49 -05:00
Richard Moore
f2dd977de4 Added v3 INFURA end-points to InfuraProvider (#286). 2019-01-23 16:04:54 -05:00
Richard Moore
5f013216c5 Fixed long-response bug in IpcProvider (#384). 2019-01-21 17:03:52 -05:00
Richard Moore
eac0805435 Updated dist files. 2019-01-17 16:34:17 -05:00
Richard Moore
e5bee7e5a3 Fixed path for x-ethers metadata and wallet (#). 2019-01-17 16:32:51 -05:00
Richard Moore
442553620a Updated dist files. 2019-01-15 16:01:45 -05:00
Richard Moore
6d08968b87 Fixed contract removeAllListeners which did not clean up the event loop properly (#391). 2019-01-15 15:58:42 -05:00
Richard Moore
c2ce59f95e Updated dist files. 2018-12-27 15:53:00 -05:00
Richard Moore
f3ec27b95f Added customizable log levels to quiet warnings (#379). 2018-12-27 15:48:38 -05:00
Richard Moore
c88cb5ea90 Updated dist files. 2018-12-14 18:39:46 -05:00
Richard Moore
99a21660ab Allow unchecked transactions which will remain unwrapped for the JsonRpcSigner (#340). 2018-12-14 18:36:24 -05:00
Richard Moore
4bc62a1e8a Make it easier for sub-classes of Wallet to manage nonces. 2018-12-14 18:32:48 -05:00
Richard Moore
26eb6cc01a Updated dist files. 2018-12-12 16:59:25 -05:00
Richard Moore
bcba17a9e7 Allow nonce to be a BigNumber (#228). 2018-12-12 16:56:50 -05:00
Richard Moore
918b66bc2e Fixed typo in error strings (#376). 2018-12-12 16:31:23 -05:00
Richard Moore
152d672278 Add isHexString to exported utils (#367). 2018-12-12 16:23:39 -05:00
Richard Moore
51fb472809 Add abs method to BigNumber (#375). 2018-12-12 16:10:28 -05:00
Richard Moore
66440b8542 Better error messages for namehash (#364). 2018-12-10 17:02:02 -05:00
Richard Moore
fefdd51084 Updated dist files. 2018-12-08 18:48:53 -05:00
Richard Moore
6ca1d77298 Fixed function name in parsed transactions (#370). 2018-12-08 18:46:29 -05:00
Richard Moore
4f6748ec4c Include request body in web errors. 2018-12-05 04:19:34 -05:00
Richard Moore
f56fc572f1 Squashed unhandled promise exception for Providers that are never used (#362). 2018-12-05 04:18:47 -05:00
Richard Moore
16fdf6b621 Added gas estimation back into JsonRpcSigner (#365). 2018-12-05 03:32:24 -05:00
Richard Moore
a863037ca3 Updated dist files. 2018-12-04 17:16:42 -05:00
Richard Moore
2d854bd94c Do not fill in implicit values for JSON-RPC based signers (#335). 2018-12-04 17:13:55 -05:00
Richard Moore
9565c28a91 More relaxed transaction parsing (#357). 2018-12-04 16:31:18 -05:00
Richard Moore
bc457bb3bd Allow any whitespace characters in human-readable ABI (#360). 2018-12-04 16:14:57 -05:00
Richard Moore
db383a3121 Updated dist files. 2018-11-27 17:32:47 -05:00
Richard Moore
3f76f603d9 Fixed contract proxied tx.wait receipt properties (#355). 2018-11-27 17:32:05 -05:00
Richard Moore
68304848ae Updated dist files. 2018-11-27 16:03:39 -05:00
Richard Moore
bb6bc4cac3 Check for partially-working normalize support. 2018-11-27 15:59:14 -05:00
Richard Moore
ef8b9c36ef Support for platforms where UTF-8 is only half broken. 2018-11-27 15:56:50 -05:00
Richard Moore
e6c943d01f Updated dist files. 2018-11-21 16:24:40 -05:00
Richard Moore
31d3ee899f Throw exception instead of returning null for getDefaultProvider (#351). 2018-11-21 16:23:44 -05:00
Richard Moore
98143a845b Updated dist files. 2018-11-20 15:45:47 -05:00
Richard Moore
bffc557be1 Added default provider support for Ethereum classic (#351). 2018-11-20 15:41:12 -05:00
Richard Moore
09208fa8fe Updated dist files. 2018-11-13 07:50:04 -05:00
Richard Moore
048c571d3d Fixed 0 confirmation waiting (#346). 2018-11-13 07:48:37 -05:00
Richard Moore
24757f1064 Updated dist files. 2018-11-12 17:27:47 -05:00
Richard Moore
88f2f51266 Fix spacing in checkArgument errors (#318). 2018-11-12 17:22:18 -05:00
Richard Moore
93152ef863 Do not replay block events when the provider event block is reset (#343). 2018-11-12 17:17:43 -05:00
Richard Moore
09b698b0a9 Updated dist files. 2018-11-09 14:42:29 -05:00
Richard Moore
478aaf9619 Force unorm shim when String.prototype.normalize is broken (#338). 2018-11-09 14:36:21 -05:00
Richard Moore
fad902b438 Better error message when normalize is missing. 2018-11-09 14:34:14 -05:00
Richard Moore
7bfaf292db Added shims for React-Native support. 2018-11-08 18:25:16 -05:00
Richard Moore
be0488a1a0 Updated dist files. 2018-11-08 16:03:33 -05:00
Richard Moore
28a52cd485 Fix for when blockTag is specified as a null equivalent value in contract overrides (#329). 2018-11-08 16:01:48 -05:00
Richard Moore
3a19f43844 Added "debug" event for providers; do not depend on the format as it may change, but this should help debugging in most cases (#320). 2018-11-08 15:59:30 -05:00
Richard Moore
4852e837d2 Fix for Kovan filters without an address (#339). 2018-11-08 15:56:35 -05:00
Richard Moore
fa68385cfe Updated dist files. 2018-10-14 19:05:38 -04:00
Richard Moore
d54609a458 Fixed lingering polling timer when no events left to process in a provider. 2018-10-14 19:01:53 -04:00
Richard Moore
f682861e0b Fixed utils.poll from mutating passed variables. 2018-10-14 19:01:09 -04:00
Richard Moore
023a20ff47 Fixed and refactored populating transaction values for signers (#306). 2018-10-14 19:00:15 -04:00
Richard Moore
e39cd84923 Fixed test cases for phantomjs (must use ES3 syntax). 2018-10-13 17:27:19 -04:00
Richard Moore
5020897f10 Updated dist files. 2018-10-13 17:19:07 -04:00
Richard Moore
6ac2d923b7 Fixed filtering with null non-indexed parameters (#305). 2018-10-13 17:17:02 -04:00
Richard Moore
6996dd86f4 Updated dist files. 2018-10-11 16:50:52 -04:00
Richard Moore
493273d698 Added optional blockTag to call; note that this may not behave as expected on all nodes (#226). 2018-10-11 16:03:18 -04:00
Richard Moore
84344ac4c2 Check all transaction parameters are valid; protect against typos (#299). 2018-10-11 15:16:31 -04:00
Richard Moore
9b118af304 Updated dist files. 2018-10-07 01:13:10 -04:00
Richard Moore
e39e2ed718 Added address to HDNode (#196). 2018-10-07 01:09:56 -04:00
Richard Moore
71f781d542 Added French and Spanish to test-hdnode. 2018-10-07 01:05:49 -04:00
Richard Moore
b2db10e216 Mark progressCallback as optional (#293). 2018-10-07 00:42:52 -04:00
Richard Moore
3736a15714 Updated dist files. 2018-10-04 20:25:07 -04:00
Richard Moore
248158130e Added automatic event parsing for contract transaction receipts from tx.wait. 2018-10-04 19:54:15 -04:00
Richard Moore
f5c7ccbb80 Added ability to wait for a specific number of confirmations (#229). 2018-10-04 17:27:42 -04:00
Richard Moore
24335d0dd7 Fix for geth-etc (official geth is fine), which returns Receipts before the blockHash is synced to the database. 2018-10-04 16:46:19 -04:00
Richard Moore
908c2c1096 Fixed confirmations tests and bootstrap fast blockNumber. 2018-10-04 16:44:29 -04:00
Richard Moore
9797b36186 Added confirmations to TransactionResponse (#156, #238). 2018-10-04 16:24:31 -04:00
Richard Moore
731f189010 Fixed nested errors for providers that were masking true error (#292). 2018-10-04 15:24:29 -04:00
Richard Moore
cc5b157231 Updated dist files. 2018-10-03 22:52:55 -04:00
Richard Moore
99fed75202 Added version to errors. 2018-10-03 22:50:22 -04:00
Richard Moore
cb5f9f576a Fixed French and Spanish for browsers without Uint8Array.forEach. 2018-10-03 22:47:44 -04:00
Richard Moore
aeac2cdb86 Added French and Spanish includes to phantomjs test page. 2018-10-03 20:22:59 -04:00
Richard Moore
0dafd83033 Increased timeout for querying npm registry. 2018-10-03 20:08:01 -04:00
Richard Moore
f6d946cf68 Updated dist files. 2018-10-03 20:03:32 -04:00
Richard Moore
b9c07b549c Added French and Spanish wordlist dist files. 2018-10-03 20:01:35 -04:00
Richard Moore
c34a1f73c6 Added French and Spanish BIP-39 wordlists (#191). 2018-10-03 19:58:45 -04:00
Richard Moore
281bd0613d Added support for JSON serialized BigNumbers in the constructor (#288). 2018-10-03 19:54:38 -04:00
Richard Moore
d936b4cd09 Fixed scrypt for long passwords (#223). 2018-10-03 19:50:15 -04:00
Richard Moore
5a0ebf84ef Updated dist files. 2018-10-01 17:34:44 -04:00
Richard Moore
c9c5b4d641 Added chainId as supported override for contract transactions. 2018-10-01 17:33:09 -04:00
Richard Moore
5e86e02304 Fixed wildcard events and made nested events more robust (#289). 2018-10-01 17:30:45 -04:00
Richard Moore
15cd0d8e58 Fixed twitter link in README. 2018-09-28 11:00:33 -04:00
Richard Moore
061b0eae1d Version bump (major). 2018-09-27 17:17:41 -04:00
Richard Moore
d3876303f2 Merge branch 'master' into typescript 2018-09-27 17:11:25 -04:00
Richard Moore
2ad110548d Updated dist files. 2018-09-27 16:55:40 -04:00
Richard Moore
ef5d19e5c9 More logical interface for hexDataSlice (mimic slice). 2018-09-27 16:52:05 -04:00
Richard Moore
5f6bc405dd Added failure count to test case reporter. 2018-09-27 16:50:59 -04:00
Richard Moore
1ca203ca38 Remove unused dist files. 2018-09-27 16:49:38 -04:00
Richard Moore
c8a75ab01d Updated README. 2018-09-27 16:48:22 -04:00
Richard Moore
8c6eb555de Updated wordlists with new library layout for Wordlist. 2018-09-27 16:47:44 -04:00
Richard Moore
cb68403387 Fixed test case for phantomjs; non-ES3 token. 2018-09-26 16:29:16 -04:00
Richard Moore
7b5ce86c5b Updated dist files. 2018-09-26 16:15:13 -04:00
Richard Moore
f4e247fc92 Fixed trailing-zero issue after commify fix. 2018-09-26 16:11:38 -04:00
Richard Moore
e582bd73ee Updated unit test cases. 2018-09-26 16:11:07 -04:00
Richard Moore
543e12c6ca Changing contract deployment to ContractFactory. 2018-09-26 15:47:09 -04:00
Richard Moore
57439af25d Removed options from unit conversion and added separate otto-safe commify function (#202, prep for #228). 2018-09-26 15:44:39 -04:00
Richard Moore
f4dd5d19e7 Updated dist files. 2018-09-24 16:07:14 -04:00
Richard Moore
05e30f4680 Major overhaul of file layout and import/export structure to better support TypeScript. 2018-09-24 15:55:17 -04:00
Richard Moore
9c87e569cf Added Ethereum Classic Testnet to networks. 2018-09-21 15:34:56 -04:00
Richard Moore
15761027df Added constructor support for Human-Readable ABI. 2018-09-21 15:34:24 -04:00
Richard Moore
65fa0b7b22 Updated dist files. 2018-09-20 15:48:08 -04:00
Richard Moore
8b2e57276f Do not override nonce if forced to 0 (#282). 2018-09-20 15:46:53 -04:00
Richard Moore
25337d4543 Fix typo in error message (#272). 2018-09-20 15:46:04 -04:00
Richard Moore
892f30a051 Check HTTP status before parsing body in fetchJson (#280). 2018-09-20 12:44:46 -04:00
Richard Moore
829a56cc79 Updated dist files. 2018-09-20 11:58:46 -04:00
Richard Moore
9d04f2c1eb Prevent odd-length values from being passed in as bytesXX (#281). 2018-09-20 11:55:27 -04:00
Richard Moore
f02f4bc0c0 Enabled ropsten test cases for Etherscan (it is back up). 2018-09-20 11:53:52 -04:00
Richard Moore
b9829f205f Remove beta dist file. 2018-09-06 16:53:40 -04:00
Richard Moore
eef07e5a4f Updated dist files. 2018-09-06 16:41:02 -04:00
Richard Moore
13e50ec6db Exposed more secp256k1 primitives (#233 #271). 2018-09-06 16:37:44 -04:00
Richard Moore
9dcf6f3253 Added headers and timeout to fetchJson (#224 #257). 2018-09-06 16:34:45 -04:00
Richard Moore
e0cefb0398 Added VoidSigner for read-only access as a specific address. 2018-09-04 10:28:26 -04:00
Richard Moore
6018bf63f9 Make AbstractSigner provider readonly. 2018-09-04 10:27:22 -04:00
Richard Moore
7aebe53e5b Change Buffer constructor to safer operations (#265). 2018-09-04 10:20:31 -04:00
Richard Moore
32a070d909 Added support for fetching block transactions with blocks. 2018-09-04 10:08:50 -04:00
Richard Moore
b5408bcbd0 Updated readme with beta cdn link. 2018-08-27 20:19:06 +02:00
Richard Moore
284ffdfd1f Updated provider testcase for updated address. 2018-08-27 18:58:50 +02:00
Richard Moore
ae4cd90441 Updated dist files. 2018-08-27 14:51:44 +02:00
Richard Moore
5efd0c1f8a Backport fix for EIP-848 status codes from v4; only an error is generated in v3, the error string is ignored. (#262). 2018-08-27 14:51:03 +02:00
Richard Moore
962f75038b Skip dist checks for node 6 to fix Travis CI. 2018-08-27 14:04:21 +02:00
Richard Moore
ea5a56f0f0 Updated dist files. 2018-08-27 13:42:26 +02:00
Richard Moore
afae5cd2f9 Block calling contract methods until it deployed, if it is pending deployment. 2018-08-27 13:38:48 +02:00
Richard Moore
280dc56a74 Removing address from JsonRpcSigner and cache address (#263). 2018-08-27 13:27:59 +02:00
Richard Moore
2b9d8d6d6a Silence warning for external keyword in human-readable ABI. 2018-08-27 13:27:03 +02:00
Richard Moore
2d35e5e9d7 Fixed test cases for finney units. 2018-08-21 13:23:11 +02:00
Richard Moore
102a4e8bf4 Added tests for utf-8 errors. 2018-08-21 13:14:37 +02:00
Richard Moore
ce837e307e Fixed test DRNG. 2018-08-21 13:14:21 +02:00
Richard Moore
810e9e743e Updated dist files. 2018-08-21 13:13:52 +02:00
Richard Moore
e092746165 Removed jsonCopy and added deepCopy instead (#261). 2018-08-21 13:11:44 +02:00
Richard Moore
388b658bfa Invalid utf-8 strings now fail by default. 2018-08-21 13:08:28 +02:00
Richard Moore
0358e8afb7 Fixed typo in units for finney (#259). 2018-08-21 13:01:55 +02:00
Richard Moore
2b88efea65 Updated dist files. 2018-08-13 17:01:44 +01:00
Richard Moore
f4c3073304 Removed BigNumber and AbiCoder objects from utils and exposed them at the root. 2018-08-13 16:58:19 +01:00
Richard Moore
e4d5786861 Fixed isType for null values. 2018-08-13 16:56:59 +01:00
Richard Moore
46a55a1d99 Removing old dead code. 2018-08-13 16:56:20 +01:00
Richard Moore
2c1022cde1 Removed timeout from wait so we can use confirmations in the future instead and remain backwards compatible. 2018-08-13 16:54:32 +01:00
Richard Moore
f9723c42cb Re-use shallowCopy. 2018-08-13 16:52:35 +01:00
Richard Moore
3635a9bb40 Fixed custom network chain ID check. 2018-08-13 16:50:47 +01:00
Richard Moore
9e0bfa733b Updated dist files. 2018-08-05 18:35:26 -04:00
Richard Moore
a80a8a8ced Fixed miscalculating estimated fees for payable functions. 2018-08-05 18:35:00 -04:00
Richard Moore
b44a80800c Removed dead dist files. 2018-08-05 18:31:22 -04:00
Richard Moore
e3b04bb3fc Updated dist files. 2018-08-04 19:40:06 -04:00
Richard Moore
fcee62a3d9 Fixed parseSignature with empty parameter lists. 2018-08-04 19:34:30 -04:00
Richard Moore
7f1fbfc203 Make wallet.encrypt options optional. 2018-08-04 19:33:51 -04:00
Richard Moore
4f63b82bbb Updated dist files. 2018-08-03 15:26:01 -04:00
Richard Moore
f63c844c42 Added support for getting JsonRpcSigner by index and fixed gas limit in sendTransaction. 2018-08-03 15:22:28 -04:00
Richard Moore
694c0b3f7d Added test case to check build for TypeScript host-path mayhem. 2018-08-02 21:36:13 -04:00
Richard Moore
1c45840148 Updated dist files. 2018-08-02 21:35:39 -04:00
Richard Moore
421b2c857b Fixing TypeScript compiler using host paths for resolving constants. 2018-08-02 21:34:10 -04:00
Richard Moore
bacf42a22f Updated dist files. 2018-08-02 20:43:09 -04:00
Richard Moore
5c8703fb30 Restructuring for TypeScript. 2018-08-02 20:30:44 -04:00
Richard Moore
30a84d8577 Fixing long-output-delay issue on Travis CI. 2018-08-02 17:56:50 -04:00
Richard Moore
3198bc27fd Updated dist files. 2018-08-02 17:15:11 -04:00
Richard Moore
c7555516ff Using the Vyper-style gas limits sans the base cost. 2018-08-02 17:14:06 -04:00
Richard Moore
3cd0e8bf88 New custom Mocha reporter so Travis CI output is browser friendly. 2018-08-02 17:10:38 -04:00
Richard Moore
957ccd2eaf Updated dist files and tests. 2018-08-01 18:05:19 -04:00
Richard Moore
558ef2c00d Updated ethers exported structure. 2018-08-01 17:35:23 -04:00
Richard Moore
b73a964bac Moved type to enum for hmac SupportedAlgorithms. 2018-08-01 17:33:23 -04:00
Richard Moore
3e091a2938 Added Vyper-style gas ABI support. 2018-08-01 17:24:11 -04:00
Richard Moore
cb6d18035f Added error code detection for sendTransaction. 2018-08-01 17:02:27 -04:00
Richard Moore
4b15229832 Calculate estimated gasLimit for JsonRpcProvider. 2018-08-01 15:47:02 -04:00
Richard Moore
f0dfa50848 Adding node 8 back into tests. 2018-07-30 23:15:53 -04:00
Richard Moore
22f007498d Updated dist files. 2018-07-30 23:15:36 -04:00
Richard Moore
cc6f658ef1 Added support for Web3 v1.0 providers. 2018-07-30 23:14:07 -04:00
Richard Moore
9b885296d0 Major re-factor of the library layout for TypeScript; still up for discussion. 2018-07-30 18:59:52 -04:00
Richard Moore
c6c5ba0118 Updated dist files. 2018-07-27 17:02:46 -04:00
Richard Moore
a966a60656 Changed constant name for consistency. 2018-07-27 03:46:38 -04:00
Richard Moore
d05da1e487 Added transactionHash to error for consistency. 2018-07-27 03:45:11 -04:00
Richard Moore
294d0fda61 Added longer timeout for basic authentication test. 2018-07-26 18:19:26 -04:00
Richard Moore
29f3d2dea8 Fixed long fixed-length bytes from overflowing encoded ABI. (#237) 2018-07-26 18:02:42 -04:00
Richard Moore
d2b6570c39 Updated dist files. 2018-07-26 17:34:22 -04:00
Richard Moore
a1a765f6e4 Fixed types for Log Description. 2018-07-26 17:31:08 -04:00
Richard Moore
1d98928475 Updated dist files. 2018-07-26 17:02:32 -04:00
Richard Moore
73486d6f39 Added public and stateMutability for payable to ABI coder. 2018-07-26 16:58:31 -04:00
Richard Moore
640ed5dc33 Fixed recursive promise filling for contract calls. 2018-07-26 16:57:47 -04:00
Richard Moore
dda957c8e5 Removing junk files. 2018-07-25 22:05:38 -04:00
Richard Moore
97b6d8a797 Updated dist files. 2018-07-25 21:20:21 -04:00
Richard Moore
00bb20546b No longer using instanceof which conflicts with npm link. 2018-07-25 21:15:43 -04:00
Richard Moore
7cf8596906 Added bounds checks to the number coder and fixed bytes coder. 2018-07-25 19:32:27 -04:00
Richard Moore
5a0607d11b Updated dist files. 2018-07-23 19:23:53 -04:00
Richard Moore
26207e7bb8 Added non-unified types to resolve npm link issues. 2018-07-23 19:21:42 -04:00
Richard Moore
ae8d75fe6a Use ethers.wordlists for testing instead of re-import. 2018-07-23 05:38:53 -04:00
Richard Moore
4db19a1799 Type in console.log for tests. 2018-07-23 05:38:27 -04:00
Richard Moore
543ca19361 Updated dist files. 2018-07-23 05:37:48 -04:00
Richard Moore
7014afaab6 Fixing out-of-order browserify imports again. 2018-07-23 05:37:07 -04:00
Richard Moore
62987ac3a8 Updated test suite utils. 2018-07-23 03:02:39 -04:00
Richard Moore
3a5ec9b4ca Updated dist files. 2018-07-23 02:59:01 -04:00
Richard Moore
3169cf2f60 Generalized gulpfile and split browserify from tsc. 2018-07-23 02:56:40 -04:00
Richard Moore
bceefc7197 Better browserify for English wordlist. 2018-07-22 21:07:50 -04:00
Richard Moore
bfff3ea4cc Simpler empty modules for shims. 2018-07-22 20:41:30 -04:00
Richard Moore
7d4caa05ca Use class instance to detect BigNumber. 2018-07-22 20:06:36 -04:00
Richard Moore
004fd7d37d Make formatUnits options optional. 2018-07-22 20:05:45 -04:00
Richard Moore
e3da92d1ee Lazy load the elliptic curve; also fixed circular dependencies internal to the library that cause issues in browserify. 2018-07-22 20:05:14 -04:00
Richard Moore
0bc72c5ed4 Export computePublicKey in utils. 2018-07-22 19:59:27 -04:00
Richard Moore
d0cf0c8f43 Restructuring browser shims. 2018-07-22 18:25:36 -04:00
Richard Moore
4d4fe60a55 Version bump (pre-release: beta.1) 2018-07-18 16:38:40 -04:00
Richard Moore
81903e99d3 Updated dist files. 2018-07-18 16:36:21 -04:00
Richard Moore
fd40f66766 Removing node8, which fails to load Web3 dependencies. 2018-07-18 16:33:47 -04:00
Richard Moore
2aa2a38e37 Added a (hacky) way to get TypeScript to support the Contract meta-class functions. 2018-07-18 16:32:16 -04:00
Richard Moore
d701995bc0 Correct wallet.createRandom to have optional options. 2018-07-18 16:30:42 -04:00
Richard Moore
c4084fe625 Trying to fix Travis CI for node8; failing to load a devDependency in Web3. 2018-07-17 16:11:51 -04:00
Richard Moore
8c7c592bd1 Updated dist files. 2018-07-17 16:10:13 -04:00
Richard Moore
b9aca357a9 Updated generated version file. 2018-07-17 16:09:35 -04:00
Richard Moore
26f5a558d6 Fixed some concerns of eslint. 2018-07-17 16:09:06 -04:00
Richard Moore
960919d00a Version bump (pre-release: beta.0). 2018-07-17 15:49:37 -04:00
Richard Moore
b712c88088 Updated dist files. 2018-07-17 15:36:14 -04:00
Richard Moore
a062f75d38 Made Signatures more flexible for input. 2018-07-17 15:32:06 -04:00
Richard Moore
a67e3d1d65 Updated demo wallet send for v4 API. 2018-07-17 02:35:39 -04:00
Richard Moore
28927a6f1a Updated dist files. 2018-07-17 02:07:36 -04:00
Richard Moore
ce864fa1b2 Fixed browser shim for setImmediate. 2018-07-17 02:05:24 -04:00
Richard Moore
7e1e760b95 Remove editor swap file. 2018-07-17 01:49:51 -04:00
Richard Moore
d19df0dcae Updated dist files. 2018-07-17 01:46:27 -04:00
Richard Moore
b61b84dfc8 Moved static Wallet calls to utils and refactors exposed types. 2018-07-17 01:44:04 -04:00
Richard Moore
922de67a8b Updated dist files. 2018-07-16 04:00:56 -04:00
Richard Moore
e8c657ba70 Fixed type definitions using absolute path. 2018-07-16 03:59:25 -04:00
Richard Moore
1ce4f75b0d Increase wallet testcases timeout for decrypting and encrypting. 2018-07-16 03:30:09 -04:00
Richard Moore
175966674d Use brwoser hmac directly from browser pbkdf2. 2018-07-16 03:29:08 -04:00
Richard Moore
d1ce9f4964 Refactored imports and exports to be both node-friendly and TypeScript-friendly. 2018-07-16 03:27:49 -04:00
Richard Moore
0f9d0ef533 Updated dist files. 2018-07-16 00:55:34 -04:00
Richard Moore
e4df325e22 Moved getNetwork to utils. 2018-07-16 00:48:41 -04:00
Richard Moore
3b843a1782 Moving away from default exports. 2018-07-16 00:39:29 -04:00
Richard Moore
614b2d0325 Removing default export from wordlists. 2018-07-16 00:32:26 -04:00
Richard Moore
46e4db0b24 Added wordlists to faux-fs for phantomjs testing. 2018-07-16 00:25:13 -04:00
Richard Moore
94171f6c57 Updated dist files. 2018-07-16 00:24:50 -04:00
Richard Moore
e0391ee1bc Increased timeouts for some test cases that are failing in phantomjs on Travis CI. 2018-07-16 00:20:36 -04:00
Richard Moore
7004cd0b92 Added wordlist generation testing. 2018-07-16 00:19:50 -04:00
Richard Moore
b61643fecb Fixed internal exports to use types. 2018-07-16 00:09:13 -04:00
Richard Moore
8d6fa3dc93 Added sanity checksums to all BIP39 wordlists on load. 2018-07-14 20:51:51 -04:00
Richard Moore
e6c8db88bd Japanese sorting in browsers is different than in node; fixing wordlist and added sanity check. 2018-07-14 19:38:35 -04:00
Richard Moore
937194b5ee Updated dist files. 2018-07-14 17:21:32 -04:00
Richard Moore
5f3ceec6f9 Refactored types for TypeScript and to remove circular dependencies. 2018-07-14 17:19:08 -04:00
Richard Moore
3f9f0e02e5 Updated dist files. 2018-07-12 20:14:04 -04:00
Richard Moore
d6260ae11a Fixing weird browserify bug with pacakge resolution order. 2018-07-12 20:11:32 -04:00
Richard Moore
0ecfe4bafd Truly immutable BigNumber and hidden constructor. 2018-07-12 20:07:47 -04:00
Richard Moore
df2a00a2fc Fixed test-wallet for phantomjs (it does not like the "let" keyword). 2018-07-12 03:25:59 -04:00
Richard Moore
dfdbb9bb28 Updated dist files. 2018-07-12 02:53:06 -04:00
Richard Moore
979e374270 Added new-style events (removed old-style) to contracts and added filters to contracts and interfaces. 2018-07-12 02:52:43 -04:00
Richard Moore
b7e143b4f3 Changed Web3Proivder url to empty string instead of unknown if it cannot be determined. 2018-07-12 02:50:44 -04:00
Richard Moore
272265f085 Added common constants to root library. 2018-07-12 02:49:48 -04:00
Richard Moore
27402fafe6 Refactored Provider events. 2018-07-12 02:49:09 -04:00
Richard Moore
ac4211d0c6 Added common constants to utils. 2018-07-12 02:44:45 -04:00
Richard Moore
ee0faba708 Fixed ABI coder for no-name indexed parameters in events. 2018-07-12 02:43:55 -04:00
Richard Moore
e5d5871b95 Better transaction serializing API. 2018-07-12 02:42:46 -04:00
Richard Moore
4514229f27 Added eslint for promises. (#189) 2018-07-03 16:44:05 -04:00
Richard Moore
7ac8cb63c8 Use block polling rather than exponential backoff when possible. Added transactionHash to errors on failed transactions. 2018-07-03 15:48:37 -04:00
Richard Moore
8e83ceaedc Check for invalid strings in BigNumber constructor. (#219) 2018-07-03 14:58:24 -04:00
Richard Moore
b1d026b800 Added deployed to contract and abstracted polling into web. 2018-06-30 23:05:22 -04:00
Richard Moore
ebf42dc9e0 Fixed extra output in array data. (#220) 2018-06-30 22:40:20 -04:00
Richard Moore
a8283ea99f Add longer timeout for contract calling test cases. 2018-06-28 23:21:51 -04:00
Richard Moore
564c5fa364 Fix unresolved promise issues and add Promise linting. 2018-06-27 19:59:08 -04:00
Richard Moore
6faa978767 Fixes for handling weird TestRPC behaviour. 2018-06-26 18:59:22 -04:00
Richard Moore
8a340c8ff3 Fixed sendTransaction for JsonRpcSigner. 2018-06-26 18:37:21 -04:00
Richard Moore
0b35f1959a Fixed JsonRpcSigner for promises and made address optional again. 2018-06-26 17:49:54 -04:00
Richard Moore
98ea4b59a0 Fixed JsonRpcSigner for Promise-based values. 2018-06-26 17:47:31 -04:00
Richard Moore
ac2ad5a886 Travis CI and node 10 need the package-lock. 2018-06-25 21:58:34 -04:00
Richard Moore
8fe19de3ce Added support for unsigned transactions. 2018-06-25 21:02:20 -04:00
Richard Moore
40559b7044 Added node 10 to Travis CI testing. 2018-06-24 21:43:29 -04:00
Richard Moore
0b8b682fee Typo in travis config. 2018-06-24 21:29:04 -04:00
Richard Moore
083aca4693 Merge branch 'typescript' of github.com:ethers-io/ethers.js into typescript 2018-06-24 21:28:09 -04:00
Richard Moore
19475a06f5 Re-enable node 8 for testing. 2018-06-24 21:27:30 -04:00
Richard Moore
5fa014e01d Re-enable node 8 for testing. 2018-06-24 21:25:38 -04:00
Richard Moore
df591cfc4c Increase timeout for testcases that make contract requests. 2018-06-24 21:25:07 -04:00
Richard Moore
fcd57f9756 Added formatParamType and formatSignature. 2018-06-24 20:32:14 -04:00
Richard Moore
c348c60d5c Skip null values (not just undefined) when resolving ENS names. 2018-06-24 18:56:45 -04:00
Richard Moore
1d2fe8993b Populate from in estimateGas for determining gas limit. 2018-06-24 18:47:36 -04:00
Richard Moore
18aa885aef Provider and contract wait return receipts and throw on error status. 2018-06-24 18:41:28 -04:00
Richard Moore
a693576941 Updated dist files for wordlists. 2018-06-24 05:34:57 -04:00
Richard Moore
5c0475ce8c Added version to TypeScript library output. 2018-06-24 05:31:24 -04:00
Richard Moore
380c17a0a5 Removed unecessary private variable from Chinese wordlist. 2018-06-24 05:28:16 -04:00
Richard Moore
bd4ca0d4d3 Fixed TypeScript definition file generation. 2018-06-24 04:03:21 -04:00
Richard Moore
7949444612 Added initial support for EIP838, revert codes (#188). 2018-06-23 01:33:51 -04:00
Richard Moore
cf16b0ffa9 Typo in demo wallet. 2018-06-23 01:30:59 -04:00
Richard Moore
ec40c67c50 Added no implicit any for TypeScript checking. 2018-06-22 20:30:50 -04:00
Richard Moore
55ac3fef88 Increase test timeout for phantomjs hdnode tests. 2018-06-22 03:36:00 -04:00
Richard Moore
c4edd957b4 Updated dist files. 2018-06-22 03:10:59 -04:00
Richard Moore
a19cc91cd4 Updated test cases to reflect API name changes and remove brain wallets. 2018-06-22 02:31:07 -04:00
Richard Moore
c83596455b Updated dist files. 2018-06-22 02:18:19 -04:00
Richard Moore
41a91c3c2d Fixed encrypted JSON wallets in demo wallet. 2018-06-22 02:15:47 -04:00
Richard Moore
b77937ec4c Added shims include to the root library. 2018-06-22 02:14:46 -04:00
Richard Moore
1082105eea Added more specific types to signatures. 2018-06-22 02:13:34 -04:00
Richard Moore
45923ad5dd Added shims to browser in package.json. 2018-06-22 02:11:34 -04:00
Richard Moore
aa67537097 Added fallback operations to contract (#182). 2018-06-22 02:10:46 -04:00
Richard Moore
63e5ad1d95 Removed deprecated wallet operations (brainwallets and bare send). 2018-06-22 02:08:52 -04:00
Richard Moore
b2be7c807f Set up setImmediate for browsers. 2018-06-22 02:07:57 -04:00
Richard Moore
dde33aa5a5 Add tsconfig to npmignore. 2018-06-21 21:15:28 -04:00
Richard Moore
91f6e8ede6 Updated npmignore for TypeScript. 2018-06-21 21:13:34 -04:00
Richard Moore
7d8abf4463 Added sandbox test HTML file for debugging and testing. 2018-06-21 21:01:52 -04:00
Richard Moore
6abab3b974 Moved demo wallet to dist. 2018-06-21 21:01:29 -04:00
Richard Moore
970613fad9 Fixed wallet example. 2018-06-21 20:58:13 -04:00
Richard Moore
edbf8e319c Fixed resovleProperties not resolving properties. 2018-06-21 20:41:09 -04:00
Richard Moore
28cd3cace2 Moved TypeScript generation to match library layout for easier importing sub-components. 2018-06-21 20:24:30 -04:00
Richard Moore
45f8827c6e Moving wordlist generation scripts to tests. 2018-06-21 18:03:29 -04:00
Richard Moore
342be430cb Added types to package.json. 2018-06-21 17:57:33 -04:00
Richard Moore
622f02c369 Fix padding and stripping for signatures. 2018-06-20 22:07:26 -04:00
Richard Moore
263bf8047a Updated dist files (patch version bump). 2018-06-20 22:06:05 -04:00
Richard Moore
2feced5937 Strip the r and s (since we not pad) for transactions. 2018-06-20 22:03:23 -04:00
Richard Moore
6de0a765c9 Updated readme. 2018-06-20 21:19:08 -04:00
Richard Moore
4fd35224b3 Updated readme and dist files. 2018-06-20 21:14:56 -04:00
Richard Moore
52dc35bf0e Added missing String.prototype.normalize shim for phantomjs testing. 2018-06-20 21:12:04 -04:00
Richard Moore
2a111ab6ce Added mnemonic language support for Chinese, Italian, Japanese and Korean. 2018-06-20 20:29:54 -04:00
Li Xuanji
5631f071c3 pad output of signDigest 2018-06-19 13:40:11 -04:00
Richard Moore
a0f92954c8 Removing Etehrscan tests temporarily because Etherscan is down. 2018-06-19 02:21:52 -04:00
Richard Moore
64b312ef6e Adding phantomjs testing for Travis CI back. 2018-06-19 02:14:41 -04:00
Richard Moore
baf92d4ec8 Added missing browser implementations for crypto. 2018-06-19 02:14:06 -04:00
Richard Moore
1d282c544a Fixed up formatting for transforming. 2018-06-19 02:13:34 -04:00
Richard Moore
5b18a63ebc Use node crypto in node environments. 2018-06-19 02:12:57 -04:00
Richard Moore
336df72e04 Refactor Provider. 2018-06-18 18:49:00 -04:00
Richard Moore
cf79190175 Node 6 does not like async/await, which breaks Travis CI testing. 2018-06-18 14:53:41 -04:00
Richard Moore
5da4917c27 Refactoring. 2018-06-18 05:42:41 -04:00
Richard Moore
c1b24e818c Renamed convert to bytes. 2018-06-17 16:47:28 -04:00
Richard Moore
af893e79a4 Refectoring; split getAddress, expose transactions, hex operations. 2018-06-17 16:32:57 -04:00
Richard Moore
aa48dfcdf4 Removed stray localName in array sub-coders. 2018-06-15 17:50:22 -04:00
Richard Moore
efbfed0d40 Refactor API. 2018-06-15 04:18:17 -04:00
Richard Moore
059b03e090 Fixed ABI coder mutabing input. 2018-06-14 16:55:54 -04:00
Richard Moore
7391cf8d19 Added consolidated definition file. 2018-06-14 05:38:37 -04:00
Richard Moore
be7a128bf4 Renaming files. 2018-06-14 02:55:44 -04:00
Richard Moore
b6794b96a5 Removed legacy gruntfile. 2018-06-14 02:26:34 -04:00
Richard Moore
4df78764a2 Updates phantomjs test cases for gulp and TypeScript dist files. 2018-06-14 02:25:56 -04:00
Richard Moore
c411d9744d Added new checks and fixed up deploy for Contract. 2018-06-13 21:10:41 -04:00
Richard Moore
f7bfa50f15 Support for ENS addresses anywhere in a Contract call. 2018-06-13 20:02:28 -04:00
Richard Moore
8c0308dc58 Initial TypeScript migration. 2018-06-13 15:39:39 -04:00
Richard Moore
4758b30cd3 Updated mocha version to resolve node-growl security issue (not important, only part of the test suite). 2018-06-10 01:16:20 -04:00
Richard Moore
6a8ca9c318 Correctly transform uint to uint256 and int to int256 for signatures. 2018-06-06 20:48:45 -04:00
Richard Moore
884593ab76 Updated dist files. 2018-06-05 19:40:28 -04:00
Richard Moore
0517f70946 Temporary: remove ropsten test cases, it is having issues. 2018-06-05 19:38:35 -04:00
Richard Moore
514aab7ee3 Added positional and keyword argument test cases for ABIv2. 2018-06-05 19:32:18 -04:00
Richard Moore
eaaa689f2f Temporary - remove ropsten testcases for Etherscan (it is down). 2018-06-05 19:20:21 -04:00
Richard Moore
cf7872a498 Moved contract tests to Rinkeby. 2018-06-05 19:19:40 -04:00
Richard Moore
f779b6e3cc Fixed named parameters for tuples. 2018-06-05 19:18:41 -04:00
Richard Moore
a6cced81d6 Merge branch 'master' of github.com:ethers-io/ethers.js 2018-06-03 20:50:33 -04:00
Richard Moore
62e51861b8 Added more flexible ABI coder. 2018-06-03 20:50:21 -04:00
Richard Moore
6c6e031254 Added parseTransaction to interface to detect and parse relevant function and args. 2018-06-03 17:10:48 -04:00
Richard Moore
48c07f6ef6 Prevent pending filter hashes from returning null. 2018-06-03 17:08:25 -04:00
Richard Moore
1b6c20341e Updated Etherscan API URLs. 2018-06-03 16:54:13 -04:00
Richard Moore
75eb7ed507 Merge branch 'patch-1' of git://github.com/vasilisAntonakis/ethers.js into vasilisAntonakis-patch-1 2018-06-03 16:54:03 -04:00
Richard Moore
cd360a4f8c Updated CDN file in readme. 2018-06-03 16:51:23 -04:00
Richard Moore
fd27d3e2ce Merge branch 'patch-1' of git://github.com/alcuadrado/ethers.js into alcuadrado-patch-1 2018-06-03 16:51:11 -04:00
Richard Moore
6737128b84 Merge branch 'master' of github.com:ethers-io/ethers.js 2018-06-03 16:46:23 -04:00
Richard Moore
63b2eae4f5 Increase timeout for signing test cases. 2018-06-03 05:02:52 -04:00
Richard Moore
0b34aea23a Increase timeout for signing test cases. 2018-06-03 04:59:53 -04:00
Richard Moore
1290fa0073 Adding browser/process for now to try fixing Travis CI. 2018-06-03 03:16:58 -04:00
Richard Moore
da74e95f88 Fixing Travis CI, missing nextTick. 2018-06-03 02:42:53 -04:00
Richard Moore
ac82a5cb83 Merge branch 'master' of github.com:ethers-io/ethers.js 2018-06-03 01:11:49 -04:00
Richard Moore
ba21348c0f Added package-lock.json to fix PhantomJS tests on Travis CI. 2018-06-03 01:09:57 -04:00
Patricio Palladino
27207037a8 Fix browser installation instructions in README.md
Browser installation instructions were outdated and pointed to v2, while node's one to v3. 
I updated the browser's one.
2018-05-31 14:55:05 -03:00
Richard Moore
a612e1e8fa Added hashMessage to Wallet. 2018-05-31 02:13:59 -04:00
Richard Moore
08d3547d4e Updated dist files. 2018-05-31 02:13:43 -04:00
Vasilis Antonakis
7702d4ce7c API baseUrl changed
Hello, I noticed there is a change in the baseUrl of etherscan APIs

** Important: (Feb-12-2018) The ROPSTEN testnet API service URL has been updated to http://api-ropsten.etherscan.io . The previous API url http://ropsten.etherscan.io will cease to function as of March 31, 2018 

https://ropsten.etherscan.io/apis

same goes for all other testnets

nice work btw :)
2018-05-28 21:49:20 +03:00
Antoni Kedracki
3bf39b3bee Promote getHash into a static method of the Wallet. 2018-05-19 17:47:45 +02:00
Richard Moore
417065bad6 Added wait to transactions from JsonRpcSigner in sendTransaction (#177). 2018-05-15 15:32:32 -04:00
Richard Moore
3cb21dd6c7 Added timestamp to EtherscanProvider history (#174). 2018-05-14 15:50:25 -04:00
Richard Moore
cebf2aab29 Better error messages for contracts. 2018-04-16 21:42:17 -04:00
Richard Moore
72bf73f931 Updated dist files. 2018-04-16 21:31:49 -04:00
Richard Moore
65caec2ea5 Fix for Ganache getLogs. 2018-04-16 19:15:44 -04:00
Richard Moore
ea7c74d48d Version bump (patch). 2018-04-14 17:07:14 -04:00
Richard Moore
86df2306c4 Merge branch 'jlindberg-oss-jlindberg-oss-gethistory' 2018-04-14 17:06:15 -04:00
Richard Moore
6c12d8f30c Merge branch 'jlindberg-oss-gethistory' of git://github.com/jlindberg-oss/ethers.js into jlindberg-oss-jlindberg-oss-gethistory 2018-04-14 17:05:56 -04:00
Richard Moore
e649a22ccf Added basic authentication to JsonRpcProvider. 2018-04-14 16:10:26 -04:00
Richard Moore
eddf9de8f9 Refactor providers; moved getSigner and listAccounts to JsonRpcProvider from Web3Provider, using errors and added initial IpcProvider. 2018-04-13 18:21:48 -04:00
Richard Moore
7e91091cc3 Updated dist files. 2018-04-12 18:31:02 -04:00
Richard Moore
9745a6ecbc Added arg to SigningKey errors. 2018-04-12 18:29:53 -04:00
Richard Moore
3936b15afc Added long chain ID support to parsing. 2018-04-12 18:29:22 -04:00
Richard Moore
efb7dce524 Added splitSignature and beginning of better error messages. 2018-04-12 15:18:11 -04:00
jlindberg-oss
ac4b5d9199 handle 0 results from getHistory without error-ing
Similar to getLogs, getHistory seems to return `result.status == 0` when there are no results found.
2018-04-07 23:44:59 -04:00
Richard Moore
fdb7114511 Version bump (patch). 2018-04-05 15:48:46 -04:00
Richard Moore
03f8c1db86 Added better errors when calling contracts with incorrect number of arguments. 2018-04-05 15:47:34 -04:00
Richard Moore
12b68b0242 Added support for arbitrary number of decimals to parseUnits and formatUnits. 2018-04-05 15:46:44 -04:00
Richard Moore
28ddf485ed Allow large block difficulty, but set it to null for PoA (#152). 2018-04-05 15:31:31 -04:00
Richard Moore
96d420e832 Added parsing function outside of results for Interface. 2018-03-27 17:34:04 -04:00
Richard Moore
0f98bb5ac5 Added some extra checks for Wallet (from reading common mistakes on Stack Exchange). 2018-03-27 17:32:23 -04:00
Richard Moore
296473299c Added blockHash to Etherscan getLogs result (#146). 2018-03-27 17:19:46 -04:00
Richard Moore
ce7718c87e Transaction receipts in Ganache do not return logsBloom; making it optional. 2018-03-17 17:40:47 -04:00
Richard Moore
1ec8f9cf85 Fixed bool and number types not throwing an exception in the ABI decoding for empty bytes. 2018-03-17 17:39:45 -04:00
Richard Moore
46fd2deb8b Added pollingInterval to provider. (#132) 2018-03-15 16:03:20 -04:00
Richard Moore
5a9f440c8f Fixed getHistory range for EtherscanProvider. 2018-03-15 15:00:52 -04:00
Richard Moore
1d67aa3e29 Stall providers for blocks, transactions and transaction receipts with null results for triggered events (i.e. we know they should not be null). (#120) 2018-03-06 18:40:11 -05:00
Richard Moore
bd7e8e708f Updated dist files and version bump (patch) for publishing. 2018-03-05 22:34:12 -05:00
Richard Moore
8a842af999 Merge pull request #133 from ygnr/master
Fixed typo on JsonRpcProvider arguments length.
2018-03-05 21:43:29 -05:00
Gokulnath Reddy
224c17a9b9 Fix typo 2018-03-06 12:16:53 +11:00
Richard Moore
89e1cb02a4 Still problems with Travis CI with node 8; deactivating for now. 2018-03-05 03:19:18 -05:00
Richard Moore
3cdb6aad25 Refactored checkBlockTag. 2018-03-05 02:52:53 -05:00
Richard Moore
8a26f38579 Trying node 8 tests again. 2018-03-05 01:55:20 -05:00
Richard Moore
424cce9224 Fixed npmignore, which legacy was dropping all library packages. 2018-03-05 00:54:33 -05:00
Richard Moore
96abf66333 Updated dist files and fixed phantomjs dependency on them. 2018-03-04 21:17:16 -05:00
Richard Moore
b2ada0cda9 Fixing phantomjs test cases now that there is no sub-packages for test. 2018-03-04 20:00:46 -05:00
Richard Moore
e67a66f900 Major version update; some changes are not backwards compatible. This package has been merged into one package instead of an umbrella pacakge. (See: #95) 2018-03-04 19:31:09 -05:00
Richard Moore
20293fbcfb Updated testcase balance for Ropsen. 2018-03-03 00:18:29 -05:00
Richard Moore
03f20f023f Added ETC (classic) network. 2018-03-02 23:52:25 -05:00
Richard Moore
dfdf4ef317 Updated dist files. 2018-02-08 00:00:54 -05:00
Richard Moore
f10c595ad3 Added units manipulation to utils index. 2018-02-07 20:34:39 -05:00
Richard Moore
f611dba0ad Updated dist files. 2018-02-03 21:02:34 -05:00
Richard Moore
da852d0012 Merge branch 'master' of github.com:ethers-io/ethers.js 2018-02-03 20:32:02 -05:00
Richard Moore
6ade579542 Added bool support to Solidity-style hash functions (#113). 2018-02-03 20:31:50 -05:00
Richard Moore
e17947930c Updated dist files. 2018-01-28 21:30:50 -05:00
Richard Moore
74c71e6677 Fixed Contract method overrides with updated Interface. 2018-01-28 20:58:56 -05:00
Richard Moore
7635708c21 Merge branch 'master' of github.com:ethers-io/ethers.js 2018-01-26 21:56:33 -05:00
Richard Moore
4ee9a4d191 Added x-ethers compatible encrypted mnemonic to JSON wallets. 2018-01-26 21:56:20 -05:00
Richard Moore
bb24fce859 Added strict mode to properties. 2018-01-26 17:44:54 -05:00
Richard Moore
21d53f190b Updated dist files. 2018-01-17 19:11:04 -05:00
Richard Moore
5fc59dd1cf Some nodes (e.g. TestRPC) do not like checksum addresses in their RPC calls. 2018-01-17 17:26:31 -05:00
Richard Moore
34fd2882d8 Fix typo when detecting MetaMask. 2018-01-17 17:21:38 -05:00
Richard Moore
4a23b0ddc7 Merge branch 'master' of github.com:ethers-io/ethers.js 2018-01-17 16:23:12 -05:00
Richard Moore
c4690f9e7b Fixed auto-address and signing with Metamask and Web3Provicder. 2018-01-17 16:23:01 -05:00
Richard Moore
36c6d53757 Updated dist files. 2018-01-17 02:51:36 -05:00
Richard Moore
c550f2eb07 Merge branch 'master' of github.com:ethers-io/ethers.js 2018-01-16 18:56:10 -05:00
Richard Moore
67bb0b7f93 Added Web3Provider. 2018-01-16 18:55:56 -05:00
Richard Moore
c5b411811a Updated dist files. 2018-01-10 20:29:50 -05:00
Richard Moore
eddf93b200 Added named parameters to tuple encoding. 2018-01-10 19:20:18 -05:00
Richard Moore
9aeb309d9d Updated dist files. 2018-01-10 16:42:36 -05:00
Richard Moore
bea0ea3aaf Fixed overwriting name on Events. 2018-01-10 14:34:36 -05:00
Richard Moore
df930103e7 Fixed ABIv2 signatures for calling methods. 2018-01-09 20:45:38 -05:00
Richard Moore
d6cf970ae9 Added errors for providers trying to use ENS on networks without ENS. 2018-01-09 18:43:09 -05:00
Richard Moore
e778ab6f30 Added general unit conversion to utils/units. 2018-01-09 18:25:44 -05:00
Richard Moore
b7fc74a99d Updted dist files. 2017-12-30 14:05:40 -05:00
Richard Moore
b38a36b2f5 Version bump (patch). 2017-12-30 14:02:36 -05:00
Richard Moore
046a4a4dc0 Fixed new contract features on old platform strict mode. 2017-12-30 01:42:50 -05:00
Richard Moore
357d49ecee Version bump (patch). 2017-12-29 21:02:56 -05:00
Richard Moore
a568be9df3 Added getEtherPrice and getHistory calls for Etherscan Provider. 2017-12-29 21:01:43 -05:00
Richard Moore
43061c691b Verify message supports normalized and non-normalized recovery parameter. 2017-12-29 20:44:32 -05:00
Richard Moore
a4e768e2b3 Added pending transaction event for providers that support it. 2017-12-29 20:41:16 -05:00
Richard Moore
2d4fb371c2 Added get functions to the contract event callbacks. 2017-12-29 20:17:00 -05:00
Richard Moore
9b54b53081 Merge branch 'master' of github.com:ethers-io/ethers.js 2017-12-05 16:01:28 -05:00
Richard Moore
3512922bef Temporary - remove InfuraProvider homestead testcases; it is down. 2017-12-05 16:01:17 -05:00
Richard Moore
60334b66ba Updated dist files. 2017-12-05 03:25:08 -05:00
Richard Moore
d38bf992c2 Removing Node 8 for now in Travis CI; it is not working. 2017-12-05 02:36:33 -05:00
Richard Moore
ce95391804 Trying to unjam Travic CI build. 2017-12-05 02:33:04 -05:00
Richard Moore
511fff1390 Fixed message signing when the values are zero-padded. 2017-12-05 01:59:37 -05:00
Richard Moore
178980c34a Fixed address filtering and added more features to the event metadata. 2017-12-05 01:58:20 -05:00
Richard Moore
132e56e65e Added more provider log details. 2017-12-05 01:56:57 -05:00
Richard Moore
ff63e99c86 Updated dist files. 2017-12-02 02:52:36 -05:00
Richard Moore
a3fab18a93 Version bump (patch) for contracts. 2017-12-01 22:26:58 -05:00
Richard Moore
a9cc1bf641 Version bump (patch) for providers. 2017-12-01 22:26:12 -05:00
Richard Moore
fa4db2cecc Add binary message support for wallet.signMessage (#80). 2017-12-01 22:25:19 -05:00
Richard Moore
ca249054e5 If a block does not exist, return null (instead of rejecting). 2017-12-01 21:59:26 -05:00
Richard Moore
f6ace914e9 Allow contract overrides for nonce. 2017-12-01 21:58:36 -05:00
Richard Moore
1d38633e69 Updated dist files. 2017-11-23 03:49:36 -05:00
Richard Moore
8129f0cb8b Added tightly packed (aka non-standard packed) Solidity hash functions. 2017-11-23 02:11:08 -05:00
Richard Moore
b06002a2a8 Support for negative hexidecimal BigNumber. 2017-11-23 01:37:58 -05:00
Richard Moore
464584265a Merge pull request #76 from andrew--r/patch-1
Fixed error for incorrect private key.
2017-11-22 02:55:26 -05:00
Andrew Romanov
184839a57a Fix typo 2017-11-22 08:46:49 +03:00
Richard Moore
3ed66f5c16 Updated dist files. 2017-11-21 21:28:58 -05:00
Richard Moore
0d106d01af Added names for nested tuples and fixed topics (now optional) for event parsing. 2017-11-21 19:24:44 -05:00
Richard Moore
807c8133ea Fixed getTransaction to address and raw computation for providers that do not include it. 2017-11-21 19:05:34 -05:00
Richard Moore
f6cc207ae5 Merge branch 'master' of github.com:ethers-io/ethers.js 2017-11-20 01:41:15 -05:00
Richard Moore
388a77258e Updated dist files. 2017-11-20 01:41:01 -05:00
Richard Moore
424abc3395 Merge branch 'master' of github.com:ethers-io/ethers.js 2017-11-19 22:26:41 -05:00
Richard Moore
83b5e4b4a3 Merge branch 'master' of github.com:ethers-io/ethers.js 2017-11-19 22:26:14 -05:00
Richard Moore
03c10086e7 Expose BigNumber object for TypeScript. 2017-11-19 22:25:41 -05:00
Richard Moore
dae3a5b8b1 Updated dist files. 2017-11-18 18:58:53 -05:00
Richard Moore
67a29eb92e Etherscan is back up; re-adding the test cases. 2017-11-18 18:01:02 -05:00
Richard Moore
5a837e1b0a Fixed some weird cases for TestRPC and getTransaction. 2017-11-18 18:00:36 -05:00
Richard Moore
a1f2c84d03 Merge branch 'master' of github.com:ethers-io/ethers.js 2017-11-17 14:59:44 -05:00
Richard Moore
628c99936e Added test cases for legacy parameters. 2017-11-17 14:59:42 -05:00
Richard Moore
e8b23c29a5 Fixed for PoA networks getTransactionReceipt (#69). 2017-11-17 14:55:14 -05:00
Richard Moore
6e19e6eca9 Updated dist files. 2017-11-12 16:39:41 -05:00
Richard Moore
ef71271db6 Merge branch 'master' of github.com:ethers-io/ethers.js 2017-11-12 16:02:17 -05:00
Richard Moore
11f0434bdd Fixed legacy parameters for Providers and added test cases. 2017-11-12 16:02:07 -05:00
Richard Moore
46ff8e891e Updated dist files for umbrella package. 2017-11-09 19:54:28 -05:00
Richard Moore
4df288e244 Removed shims from PhantomJS tests. 2017-11-09 03:14:48 -05:00
Richard Moore
a9bc2b5ea8 Fixed slice for limited environments in HD and PBKDF2. 2017-11-08 22:38:11 -05:00
Richard Moore
d49beb8374 Added connect method and fixed missing provider/signer error. 2017-11-08 21:13:06 -05:00
Richard Moore
f88b3523c0 Version bump (patch) for provider. 2017-11-08 21:08:27 -05:00
Richard Moore
04f0a33489 Added slice and missing Math libraries for limited environments. 2017-11-08 20:19:41 -05:00
Richard Moore
b4bc01f2c3 Merge pull request #63 from ConnorGutman/patch-1
Remove double declaration of  transactionIndex for TransactionReceipt.
2017-11-08 19:04:11 -05:00
Connor Gutman
7eaf22f573 Remove double declaration of transactionIndex
This little guy nearly broke my react native app's production build by sneaking past me. 👋 Buh-bye 👋
2017-11-08 16:28:56 -07:00
Richard Moore
255b7e0a0f Version bump (patch) for utils. 2017-11-07 18:15:11 -05:00
Richard Moore
9adcbbb0fe Small README change. 2017-11-07 01:00:36 -05:00
Richard Moore
31b5650489 Merge branch 'master' of github.com:ethers-io/ethers.js 2017-11-06 22:45:06 -05:00
Richard Moore
2e90718d6a Merge branch 'master' of github.com:ethers-io/ethers.js 2017-11-06 19:36:22 -05:00
Richard Moore
fe3ac0e94d Added ABI v2 coder to Interface (still experimental in Solidity though). 2017-11-06 19:35:18 -05:00
Richard Moore
50422b6060 Removing duplicate license. 2017-11-05 13:44:21 -05:00
Richard Moore
4351a3ac86 Merge branch 'master' of github.com:ethers-io/ethers.js 2017-11-05 13:41:00 -05:00
Richard Moore
ccffc46f67 Added pow to BigNumber. 2017-11-05 13:40:54 -05:00
Richard Moore
e8bb624560 Adding default code of conduct
Adding the default code of conduct document.
2017-11-04 02:03:41 -05:00
Richard Moore
21c6fdce3a Creating license file in GitHub UI
Apparently GitHub doesn’t recognize license.txt, so adding license.
2017-11-03 14:53:11 -05:00
Richard Moore
18cd678b71 Version bump (patch) for providers. 2017-11-03 02:41:41 -05:00
Richard Moore
9cac6b06f5 Updated list of networks (legacy provider list). 2017-11-03 01:34:57 -05:00
Richard Moore
73ee7664a1 Merge branch 'master' of github.com:ethers-io/ethers.js 2017-11-03 01:31:09 -05:00
Richard Moore
8835225655 Updated getTransactionReceipt for Byzantium. 2017-11-03 01:30:56 -05:00
Richard Moore
54c19dfb5a Fixed default network for providers (#51). 2017-10-28 17:00:59 -04:00
Richard Moore
e4c455bdb6 Less strict parsing for loose providers. 2017-10-27 17:03:26 -04:00
Richard Moore
3c9f6f6ab9 Merge branch 'master' of github.com:ethers-io/ethers.js 2017-10-27 01:16:10 -04:00
Richard Moore
fb65772dd6 Added sighash and signature the Interface function properties. 2017-10-27 00:24:59 -04:00
Richard Moore
aeec6d6d52 Added Rinkeby and Kovan support. 2017-10-25 19:59:54 -04:00
Richard Moore
9be0053d62 Exclude the slower node version when running PhantomJS. 2017-10-22 18:36:47 -04:00
Richard Moore
466fbcb8da Using exclude instead of include for Travis-CI. 2017-10-22 18:20:20 -04:00
Richard Moore
f43a03a151 Using matrix include and fix test bug. 2017-10-22 18:17:31 -04:00
Richard Moore
b3134642a2 Splitting PhantomJS testing out of node testing. 2017-10-22 18:05:44 -04:00
Richard Moore
4d00215098 Added all test cases for PhantomJS. 2017-10-22 17:56:01 -04:00
Richard Moore
6926e5f423 Fix for case-sensitive Gruntfile. 2017-10-22 17:51:30 -04:00
Richard Moore
a5e1f40c30 First attempt at PhantomJS test cases. 2017-10-22 17:47:17 -04:00
Richard Moore
857688f36c Fix in test-accounts for case-sensitive file systems. 2017-10-21 04:03:51 -04:00
Richard Moore
0ac2bfd1cf Travis-CI updates. 2017-10-21 04:01:00 -04:00
Richard Moore
0adedcab50 Updating root test script for travis-ci. 2017-10-21 03:36:55 -04:00
Richard Moore
74406cc0b7 Adding travis-ci. 2017-10-21 03:28:01 -04:00
Richard Moore
8754bb80e0 Fixed documentation link in readme. 2017-10-20 23:01:08 -04:00
Richard Moore
67ac227fec Updated readme with latest dist version in CDN (and added utf-8 charset). 2017-10-20 22:59:30 -04:00
Richard Moore
666210a061 Fixed link in readme to documentation. 2017-10-20 22:01:51 -04:00
Richard Moore
2403a98219 Moved documentation to its own repository (see the ethers-io/documentation repo). 2017-10-20 21:59:30 -04:00
Richard Moore
f140fa6017 Updated dist files and dist scripts. 2017-10-20 15:44:54 -04:00
Richard Moore
a5df551689 Version bump (patch) for utils. 2017-10-20 15:16:18 -04:00
Richard Moore
ea6177d19e Updating some links in utils README. 2017-10-20 15:14:14 -04:00
Richard Moore
5c776a83ff Removed chatty library (See issue #36). 2017-10-20 15:13:29 -04:00
Richard Moore
bdcdfdabec Added Etherscan URL for testcase. 2017-10-19 02:56:40 -04:00
Richard Moore
264a7da9f7 Added some comparison test cases against ethereumjs-abi. 2017-10-19 02:55:38 -04:00
Richard Moore
f5f2bf85c5 Added message signing to Wallet. 2017-10-19 02:52:55 -04:00
Richard Moore
cb7e40a892 Updated to most recent stable web3 and ethereum lib. 2017-10-19 02:51:43 -04:00
Richard Moore
121d104d46 Removed old nodeunit test cases. 2017-10-19 02:51:20 -04:00
Richard Moore
44729ffba9 Migrated most test cases to mocha and added pre-generated gzipped test suite. 2017-10-18 21:28:45 -04:00
Richard Moore
4a3d68eada Version bump (patch). 2017-10-17 16:09:43 -04:00
Richard Moore
afb35ec307 Fixed Provider.lookup to support any address format. 2017-10-17 16:07:32 -04:00
Richard Moore
bbd3c94ec3 Added id to utils. 2017-10-17 16:05:44 -04:00
Richard Moore
394761a6ca Added ENS reverse lookup to provider. 2017-10-03 19:37:06 -04:00
Richard Moore
c566f00567 Updated dist files. 2017-07-05 20:39:03 -04:00
Richard Moore
03b967c99a Version bump (patch) for wallet. 2017-07-05 20:34:21 -04:00
Richard Moore
966267edf9 Version bump (patch) for providers. 2017-07-05 20:32:16 -04:00
Richard Moore
a53595eea5 Version bump (patch) for utils. 2017-07-05 20:31:13 -04:00
Richard Moore
eec9799706 Updated dist files. 2017-07-05 20:30:28 -04:00
Richard Moore
811710c8f0 Updated provider for picky JSON-RPC nodes. 2017-07-05 20:26:03 -04:00
Richard Moore
1c0d0157ad Add gt to big numbers. 2017-06-30 14:38:48 -04:00
Richard Moore
b4d622b02b Do not allow (non-hex-prefixed) strings to be treated as arrayish. 2017-06-30 14:38:27 -04:00
ricmoo
1b3b117b06 Fixed secret storage for bad passwords. 2017-05-29 23:08:47 -05:00
ricmoo
33ca1fc670 Version bump (patch). 2017-05-28 15:48:58 -05:00
ricmoo
91f9a47afa Added support for PBKD2 encrypted wallets (which Parity uses). 2017-05-28 15:46:57 -05:00
ricmoo
70f10cd23e Version bump (patch); updated dist files. 2017-05-21 20:39:16 -04:00
ricmoo
0a52beb539 Updated dist files. 2017-05-21 20:38:41 -04:00
ricmoo
0d36f839c3 Updated documents to include ENS. 2017-05-21 20:37:29 -04:00
ricmoo
c815166eb5 Added ENS null resolving cases. 2017-05-21 20:34:45 -04:00
ricmoo
9616720abb Version bump (minor); ENS support. 2017-05-21 20:31:37 -04:00
ricmoo
01f909fc05 Fixed getLogs bug. 2017-05-21 20:29:09 -04:00
ricmoo
b69d3de3e3 Contract is now ENS ready. 2017-05-21 20:27:47 -04:00
ricmoo
7b1856aacb Added name resolve test cases (need more though). 2017-05-21 18:09:17 -04:00
ricmoo
d5402d017a Allow getLogs filter to include ENS names. 2017-05-21 18:01:23 -04:00
ricmoo
c59414b74b Added ENS name support to Provider and Wallet. 2017-05-20 15:42:16 -04:00
ricmoo
0aee62df4d Updated dist and added donation address to readme. 2017-05-10 19:09:58 -04:00
ricmoo
1531793084 Fixed getDeployTransaction with ABI objects and added support for indexed parameters in event logs. 2017-05-09 21:48:56 -04:00
ricmoo
4b41c0e1a2 Added resetEventsBlock feature and fixed a problem with some INFURA nodes. 2017-05-09 21:45:36 -04:00
ricmoo
12eb60d2f7 Fixed createRandom when no options are passed in. 2017-05-09 18:53:32 -04:00
ricmoo
00b628c6cc Changed filenames of namehash test cases. 2017-05-08 16:14:55 -04:00
ricmoo
9e80c55275 Fixed up testcases for namehash. 2017-05-08 16:13:01 -04:00
ricmoo
301d803fc5 Version bump (patch) for namehash. 2017-05-08 12:14:56 -04:00
ricmoo
a07dfa9572 Added ENS test generation script. 2017-05-03 20:25:04 -04:00
ricmoo
b528131937 Added namehash to utils. 2017-05-03 20:24:07 -04:00
ricmoo
169347d0c4 Version bump (patch). 2017-04-22 11:58:43 -04:00
ricmoo
e4dcec7de7 Updating dist package for updated EtherscanProvider. 2017-04-22 11:57:39 -04:00
ricmoo
4407c7a4e1 Updated to new Etherscan URL. 2017-04-22 11:46:04 -04:00
ricmoo
cafd16b960 Updted etherscan provider (they changed their URL). 2017-04-22 11:43:32 -04:00
ricmoo
3d56251d15 Added npmignore to thin down the umbrella package. 2017-04-06 14:13:22 -04:00
ricmoo
a90ab3dbb1 Removed old file. 2017-04-06 14:10:32 -04:00
ricmoo
c137f5fe9f Version bump (patch) for Issue#15; browser-random-bytes using undefined utils. 2017-04-06 13:50:02 -04:00
Richard Moore
f7bdaadd7e Merge pull request #15 from slavik0329/master
Fixed call to defineProperty.
2017-04-06 13:48:16 -04:00
Steve Dakh
36a5b13a0c Fixed call to defineProperty 2017-04-06 13:39:21 -04:00
ricmoo
42f4ff398c Remove old hdnode (now part of Wallet). 2017-04-05 17:19:13 -04:00
ricmoo
d65a1f120e Updated test location (test files are too big to include in dist). 2017-04-05 17:14:57 -04:00
ricmoo
ef2be2e86d Updated wallet tool to use new library. 2017-04-05 17:14:09 -04:00
ricmoo
14841641c1 Updated gitignore. 2017-04-05 17:13:26 -04:00
ricmoo
cde70b1494 Updated documentation. 2017-04-05 17:06:11 -04:00
ricmoo
79a7f3dd64 Updated umbrella README. 2017-04-05 16:56:41 -04:00
ricmoo
ef2f0add35 Moved to using npm versions of packages in umbrella package. 2017-04-05 16:53:29 -04:00
ricmoo
2ff0b10084 Added repo ro package.json. 2017-04-05 16:43:57 -04:00
ricmoo
40634da90b Added basic README for npm. 2017-04-05 16:43:34 -04:00
ricmoo
afea5e81f9 Added repo to package.json 2017-04-05 16:35:53 -04:00
ricmoo
29cc0795b4 Fixed cross-package isBigNumber. 2017-04-05 16:33:37 -04:00
ricmoo
f77d5ff909 Added repo to package.json. 2017-04-05 16:27:06 -04:00
ricmoo
88b48738cc Added basic README for npm. 2017-04-05 16:26:39 -04:00
ricmoo
617f7a1ce6 Version bump (patch) to include new readme. 2017-04-05 16:10:06 -04:00
ricmoo
2ab382a355 Added basic readme for utils. 2017-04-05 16:09:33 -04:00
ricmoo
28f40287ce Adding repo to package.json. 2017-04-05 16:06:14 -04:00
ricmoo
7250e96850 Use most recent compatible version of ethers packages. 2017-04-04 18:55:36 -04:00
ricmoo
92bef64979 Removed HDNode dist files (moved to Wallet). 2017-04-04 18:54:46 -04:00
ricmoo
1c2d7c466e Updated dist packages. 2017-04-04 18:53:59 -04:00
ricmoo
85bce0582e Added HTML build of docs. 2017-04-04 18:48:34 -04:00
ricmoo
e746891ae0 Updated documentation. 2017-04-04 18:43:41 -04:00
ricmoo
9fa2c878b9 Updated to new version of etehreumjs-tx (for testing EIP155 support). 2017-04-04 18:32:35 -04:00
ricmoo
6c963f6404 Transactions are returned instead of hashes. 2017-04-04 18:32:00 -04:00
ricmoo
bb8f06c248 If raw is missing from a transaction, compute it. 2017-04-04 18:23:31 -04:00
ricmoo
cb9bfccb9a Moved HDNode into Wallet. 2017-04-04 17:50:50 -04:00
ricmoo
3069fa759b Contracts now return hex strings instead of arrays. 2017-04-04 17:40:25 -04:00
ricmoo
ecd9d31371 Renamed summonBrainWallet to fromBrainWallet. Removed crowdslae tests (they will be added again in the future). 2017-04-04 17:39:32 -04:00
ricmoo
6c0723bc43 Added testcase for nonce (which should be truncated). 2017-04-04 17:36:59 -04:00
ricmoo
dc4f2dcee7 Cleaned up umbrella index. 2017-04-04 17:35:34 -04:00
ricmoo
f772ce111e Renamed "execute" to "functions". Use signer estimateGas if possible. 2017-04-04 17:33:47 -04:00
ricmoo
d686374e05 Make safe integers into JavaScript numbers. Added more detail error messages. Prevent contracts from hijacking "functions" and "events". 2017-04-04 17:32:04 -04:00
ricmoo
73eef741b6 Small bug fixes and improved network ID population. 2017-04-04 17:30:17 -04:00
ricmoo
4075f330e2 Using Provider.inherits. 2017-04-04 17:28:25 -04:00
ricmoo
c10c3b937c Make fallback providers immutable. 2017-04-04 17:27:28 -04:00
ricmoo
8603c836e1 Cleaning up utils index. 2017-04-04 17:26:09 -04:00
ricmoo
27bdceaf32 Using throw-error to populate meaningful errors. 2017-04-04 17:22:53 -04:00
ricmoo
9672b9d3b8 Renamed rlp to RLP. 2017-04-04 17:21:59 -04:00
ricmoo
4f05bf87ac Fixed signing key recover when hex strings are passed in for r and s. 2017-04-04 17:19:55 -04:00
ricmoo
c88d796f0e Fixed progressCallback canel bug. Return transaction instead of transaction hash. Renamed rlp to RLP. 2017-04-04 17:19:18 -04:00
ricmoo
0b8cf42fcf Changed validMnemonic to isValidMnemonic. 2017-04-04 17:18:23 -04:00
ricmoo
807b27048b Added throw-error utility. 2017-04-04 17:06:49 -04:00
ricmoo
6a01b622a2 Updated a lot of the documentation. 2017-03-23 05:20:27 -04:00
ricmoo
7e61b6b730 Suppress too many levels of api-advanded in the toc. 2017-03-23 05:19:33 -04:00
ricmoo
156534e2ba Initial documentation (still lots to do). 2017-03-12 19:12:18 -04:00
ricmoo
5f650572ce Added sup override for dt. 2017-03-12 19:08:13 -04:00
ricmoo
9bed4096b8 Fixed contract-address bug (nonce was not zero-stripped). 2017-03-10 17:16:58 -05:00
ricmoo
b097e48efc Grunt file to produce browser test cases. 2017-03-08 01:55:02 -05:00
ricmoo
1a3aa4fd1a Added listener event for address balance changes. 2017-03-08 01:53:59 -05:00
ricmoo
83d099b37c Fixed constructor calls. 2017-03-08 01:52:53 -05:00
ricmoo
637a6d7f40 Better checks for trying to call contract methods with weird options. 2017-03-08 01:52:23 -05:00
ricmoo
78c4af5e4b Removed base option to toString. 2017-03-08 01:51:28 -05:00
ricmoo
730ccd36b2 Fixed fallback randomBytes for weak-crypto browsers.. 2017-03-08 01:51:04 -05:00
ricmoo
33a1459505 Re-org packages. 2017-03-08 01:50:12 -05:00
ricmoo
30cf4a1702 Moved wallet to separate file. 2017-03-08 01:49:05 -05:00
ricmoo
16529909f9 House cleaning. 2017-03-08 01:48:27 -05:00
ricmoo
adc1d93ac2 Added EIP155 support. 2017-03-08 01:47:29 -05:00
ricmoo
94ba940382 Moved hdnode to wallet. 2017-03-06 16:11:24 -05:00
ricmoo
cf83d92cc1 Fixing up browser test cases. 2017-03-01 15:56:49 -05:00
ricmoo
f6990b6e1f Changed name of local created test private keys. 2017-03-01 15:24:02 -05:00
ricmoo
da1526b92f Added dummy file to keep dist directory in tests. 2017-03-01 15:20:00 -05:00
ricmoo
4cd710f84e Added npm test to umbrella pacakge. 2017-03-01 15:18:54 -05:00
ricmoo
55833fcad0 Added Contract getDeployFunction. 2017-03-01 03:59:46 -05:00
ricmoo
f1d35ba18a Fixed to address bug and simplified code. 2017-03-01 02:36:14 -05:00
ricmoo
54685bd660 Fixed name bug (typo). 2017-03-01 02:34:52 -05:00
ricmoo
b0f0e8841e Moved to more flexible contract signers. 2017-03-01 02:34:18 -05:00
ricmoo
a073968597 Added constructor and simplified interface. 2017-03-01 02:33:29 -05:00
ricmoo
5ecba670a5 Moved to Provider provided inherits. 2017-03-01 02:31:11 -05:00
ricmoo
45c1108ca8 Added topic events and fixed bugs. 2017-03-01 02:30:37 -05:00
ricmoo
e115b2d8a8 Fixed call bug for strict JSON-RPC nodes. 2017-03-01 02:29:12 -05:00
ricmoo
cf20071189 Fixed empty response for getLogs and call bug. 2017-03-01 02:27:59 -05:00
ricmoo
b4b60b64aa Added provider test cases. 2017-03-01 02:26:50 -05:00
ricmoo
8111ef74b2 Refactored RLP coder a bit. 2017-02-28 20:50:53 -05:00
ricmoo
b4c498f088 Added TestContract.sol for testing contracts and providers. 2017-02-28 20:50:20 -05:00
ricmoo
8286c63ddc Gruntfile produces proper stand-alone files for browser. 2017-02-27 17:35:23 -05:00
ricmoo
7394e83eeb Added testcase name to failed test cases. 2017-02-27 17:29:23 -05:00
ricmoo
ebd9bbde2e Added browser test harness. 2017-02-27 01:51:27 -05:00
ricmoo
90f27da293 Fixed umbrella test harness. 2017-02-27 01:50:17 -05:00
ricmoo
7d11687f15 Updated hdnode API in test case. 2017-02-27 00:17:22 -05:00
ricmoo
3234f04b27 Added brainwallet test. 2017-02-27 00:17:01 -05:00
ricmoo
5c16c14fed Fixed filename typo. 2017-02-27 00:13:09 -05:00
ricmoo
6aca737405 Added contracts pacakge.json. 2017-02-27 00:10:19 -05:00
ricmoo
c884627e21 Added dist files. 2017-02-27 00:09:47 -05:00
ricmoo
8fa9b34e87 Added providers to umbrella package. 2017-02-27 00:09:07 -05:00
ricmoo
b1190e6e0b Optimized gruntfile (manually remove weird pulled in dependencies). 2017-02-27 00:08:48 -05:00
ricmoo
0c1615aef5 Custom pbkdf2 without create-hash and create-hmac. 2017-02-27 00:07:46 -05:00
ricmoo
e31f20e441 Added default url for JSON-RPC provider. 2017-02-27 00:06:32 -05:00
ricmoo
735e492f8c Cleaning up utils. 2017-02-27 00:05:39 -05:00
ricmoo
a107a7c3e2 Cleaning up hdnode. 2017-02-27 00:04:03 -05:00
ricmoo
b5f4d3b113 Cleaning up wallet. 2017-02-27 00:02:40 -05:00
ricmoo
005c0d2cdc Removed dead code. 2017-02-25 01:24:54 -05:00
ricmoo
0db4b352b6 Added split up providers. 2017-02-25 01:23:48 -05:00
ricmoo
0694a5db0e Added auto-running testcases. 2017-02-24 15:24:11 -05:00
ricmoo
73c7efffb0 Added wallet package.json. 2017-02-24 15:10:54 -05:00
ricmoo
729170ec97 Added split up contract. 2017-02-24 15:10:28 -05:00
ricmoo
2b0c40feb4 Added HDNode and BIP39 mnemonic phrases. 2017-02-24 14:59:05 -05:00
ricmoo
91543a0029 Split up utils. 2017-02-24 14:57:46 -05:00
ricmoo
9625745f4c Added new regression tests (based on pregenerated JSON files). 2017-02-24 14:56:55 -05:00
ricmoo
89c4c75e3c Scripts to run test cases. 2017-02-24 14:44:19 -05:00
ricmoo
6aceee8486 Added test case generation files. 2017-02-24 14:42:54 -05:00
ricmoo
26958d74d8 Moving test cases around; preparing to delete. 2017-02-24 14:41:24 -05:00
ricmoo
38d578a2c6 Migrating to a split up package; renaming files. 2017-02-24 14:32:44 -05:00
ricmoo
96aaed7395 Version bump (patch). 2017-02-02 17:06:44 -05:00
ricmoo
7027935ff7 Fixed failing contracts with fallback function. 2017-02-02 17:06:07 -05:00
ricmoo
5d692f4908 Some JSON-RPC clients do not allow call blockTag to be optional. 2017-01-27 16:24:53 -05:00
ricmoo
9def4a6170 Fix data bug for sendTransaction (was not incuded). 2017-01-27 15:49:15 -05:00
ricmoo
3a0d79b1e4 Just pulling in bn.js and hash.js libraries, instead of relying on the version pulled from elliptic. 2016-11-21 18:45:24 -05:00
ricmoo
048ef96ad0 Fixed package dependency issue when elliptic is already installed. 2016-11-21 18:20:36 -05:00
ricmoo
51f1fa50dd Expose the Interface and Contract objects. 2016-11-21 17:46:36 -05:00
ricmoo
d8a88c18d3 Added keyword arguments and proper result types for the Interface and Solidity parser. 2016-11-09 17:27:34 -05:00
ricmoo
db7cff9582 BN.js fixed nmask for us, so we use that now instead of our mask hack. 2016-11-09 16:39:33 -05:00
ricmoo
dc2583ddd8 Updating dist. 2016-10-05 00:12:15 +08:00
ricmoo
3be962f09d Made secret-storage more closely resemble geth. 2016-10-05 00:06:05 +08:00
322 changed files with 75200 additions and 24504 deletions

31
.eslintrc.js Normal file
View File

@@ -0,0 +1,31 @@
module.exports = {
"env": {
"browser": true,
"commonjs": true,
"node": true
},
"extends": [
// "eslint:recommended",
// "plugin:promise/recommended"
],
"parserOptions": {
"ecmaVersion": 5
},
"plugins": [
"promise"
],
"rules": {
"promise/always-return": "error",
"promise/no-return-wrap": "error",
"promise/param-names": "error",
"promise/catch-or-return": "error",
"promise/no-native": "off",
// "promise/no-nesting": "warn",
"promise/no-promise-in-callback": "warn",
"promise/no-callback-in-promise": "warn",
// "promise/avoid-new": "warn",
"promise/no-new-statics": "error",
"promise/no-return-in-finally": "warn",
"promise/valid-params": "warn"
}
};

9
.gitignore vendored
View File

@@ -1 +1,10 @@
node_modules/
obsolete/
.test-account.key
.account.key
.DS_Store
.tmp/
docs/build/doctrees/
docs/build/html/_sources/
dist/types/shims/
shims/*.d.ts

13
.npmignore Normal file
View 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
View 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
View 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/

View File

@@ -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']);
};

View File

@@ -1,6 +1,6 @@
The MIT License (MIT)
MIT License
Copyright (c) 2014 Richard Moore
Copyright (c) 2017 Richard Moore
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -9,14 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

417
README.md
View File

@@ -1,20 +1,30 @@
ethers-wallet
=============
ethers.js
=========
[![npm version](https://badge.fury.io/js/ethers-wallet.svg)](https://badge.fury.io/js/ethers-wallet)
[![npm version](https://badge.fury.io/js/ethers.svg)](https://badge.fury.io/js/ethers)
Complete Ethereum wallet implementation and utilities in JavaScript.
Complete Ethereum wallet implementation and utilities in JavaScript (and TypeScript).
**Features:**
- Keep your private keys in your browser
- Import and export JSON wallets (Geth and crowdsale) and brain wallets
- Meta-classes create JavaScript objects from any contract ABI
- Connect to Ethereum nodes over RPC, injected Web3 or [Etherscan](https://etherscan.io)
- Small (~100kb compressed; 290kb uncompressed)
- MIT licensed (with one exception, which we are migrating off of; see below)
- Keep your private keys in your client, **safe** and sound
- Import and export **JSON wallets** (Geth, Parity and crowdsale)
- Import and export BIP 39 **mnemonic phrases** (12 word backup phrases) and **HD Wallets** (English, French, Italian, Japanese, Korean, Simplified Chinese, Spanish, Traditional Chinese)
- Meta-classes create JavaScript objects from any contract ABI, including **ABIv2** and **Human-Readable ABI**
- Connect to Ethereum nodes over [JSON-RPC](https://github.com/ethereum/wiki/wiki/JSON-RPC), [INFURA](https://infura.io), [Etherscan](https://etherscan.io), or [MetaMask](https://metamask.io)
- **ENS names** are first-class citizens; they can be used anywhere an Ethereum addresses can be used
- **Tiny** (~84kb compressed; 270kb uncompressed)
- **Complete** functionality for all your Ethereum needs
- Extensive [documentation](https://docs.ethers.io/ethers.js/html/)
- Large collection of **test cases** which are maintained and added to
- Fully **TypeScript** ready, with definition files and full TypeScript source
- **MIT License** (including ALL dependencies); completely open source to do with as you please
**NOTE: This is still very beta; please only use it on the testnet for now, or with VERY small amounts of ether on the livenet that you are willing to lose due to bugs.**
Keep Updated
------------
For the latest news and advisories, please follow [@ethersproject](https://twitter.com/ethersproject) on Twitter as well as this GitHub project.
Installing
@@ -23,368 +33,73 @@ Installing
To use in a browser:
```html
<script type="text/javascript" src="https://rawgit.com/ethers-io/ethers-wallet/master/dist/ethers-wallet.min.js"></script>
<script charset="utf-8"
src="https://cdn.ethers.io/scripts/ethers-v4.min.js"
type="text/javascript">
</script>
```
To use in [node.js](https://nodejs.org/):
```
npm install ethers-wallet
/Users/ethers/my-app> npm install --save ethers
```
API
---
Documentation
-------------
Browse the [API Documentation](https://docs.ethers.io/ethers.js/html/) online.
### Wallet API
To fork and submit pull requests to the documentation, please see the
[documentation repository](https://github.com/ethers-io/documentation).
An *Ethereum* wallet wraps a cryptographic private key, which is used to sign transactions and control the ether located at the wallet's address. These transactions can then be broadcast to the *Ethereum* network.
```javascript
// A private key can be specified as a 32 byte buffer or hexidecimal string
var privateKey = new Wallet.utils.Buffer([
0x31, 0x41, 0x59, 0x26, 0x53, 0x58, 0x97, 0x93,
0x23, 0x84, 0x62, 0x64, 0x33, 0x83, 0x27, 0x95,
0x02, 0x88, 0x41, 0x97, 0x16, 0x93, 0x99, 0x37,
0x51, 0x05, 0x82, 0x09, 0x74, 0x94, 0x45, 0x92
])
Related Libraries
---------------
// or equivalently:
var privateKey = '0x3141592653589793238462643383279502884197169399375105820974944592'
- [Command Line Interface](https://github.com/ethers-io/ethers-cli) - Command Line Tools for ethers
- [CryptoKitties](https://github.com/ricmoo/ethers-meow) - CryptoKitties utility libraries
- [ENS](https://github.com/ethers-io/ethers-ens) - ENS utility libraries for managing names
- [LedgerSigner](https://github.com/ethers-io/ethers-ledger) - Use a Ledger Hardware Wallet as an ethers Signer (supports HID (node.js) and U2F (browser); or specify your own transport)
- [Web3 Bridge](https://github.com/ethers-io/ethers-web3-bridge) - Use ethers as the backend for a Web3 front-end
// Create a wallet object
var wallet = new Wallet(privateKey)
// Wallet privateKey
console.log(wallet.privateKey)
/// "0x3141592653589793238462643383279502884197169399375105820974944592"
Hacking and Contributing
------------------------
// Wallet address
console.log(wallet.address)
/// "0x7357589f8e367c2C31F51242fB77B350A11830F3"
// Sign transactions
wallet.sign({
to: "0x06B5955A67D827CDF91823E3bB8F069e6c89c1D6",
gasLimit: 3000000,
gasPrice: "0x1000",
value: "0x1000"
})
```
### Converting addresses
Addresses come in various forms, and it is often useful to convert between them. You can pass any valid address into any function, and the library will convert it internally as needed. The address types are:
- **hexidecimal** - 0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef (all letters must be either lower case or uppercase; no mixed case, as this implies a checksum address)
- **ICAP** - XE49Q0EPSW7XTS5PRIE9226HRPOO69XRVU7 (uses the International Bank Account Number format)
- **checksum** - 0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF (notice the case is adjusted encoding checkum information)
```javascript
// ICAP Addresses
Wallet.getIcapAddress(wallet.address)
/// "XE39DH16QOXYG5JY9BYY6JGZW8ORUPBX71V"
// Get checksummed address (from ICAP)
Wallet.getAddress("XE39DH16QOXYG5JY9BYY6JGZW8ORUPBX71V")
/// "0x7357589f8e367c2C31F51242fB77B350A11830F3"
// Get checksummed addresses (from unchecksumed)
Wallet.getAddress("0x7357589f8e367c2c31f51242fb77b350a11830f3")
/// "0x7357589f8e367c2C31F51242fB77B350A11830F3"
// Detect address checksum errors (notice the last "f" should be lowercase)
Wallet.getAddress('0x7357589f8e367c2c31f51242fb77b350a11830F3')
/// throws Error: invalid checksum address
```
### Crowdsale JSON Wallets
During the crowdsale, the Ethereum Project sold ether by generating *crowdsale JSON wallet*. These functions allow you to decrypt those files and retreive the private key.
```javascript
// See the test-wallets directory samples (the variable should be a string)
var json = "... see the test-wallets directory for samples ...";
// Detect crowdsale JSON wallets
Wallet.isCrowdsaleWallet(json)
// Get a wallet from a crowdsale JSON wallet
var wallet = Wallet.decryptCrowdsaleWallet(json, password);
console.log(wallet.address)
console.log(wallet.privateKey)
```
### Secret Storage JSON Wallet
This API allows you to decrypt and encrypt the [Secret Storage](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) format used by *Geth* and many other wallet platforms (such as *ethers.io*).
The Secret Storage JSON Wallet format uses an algorithm called *scrypt*, which is intentionally CPU-intensive, which ensures that an attacker would need to tie up considerable resources to attempt to brute-force guess your password. It aslo means it may take some time (10-30 seconds) to decrypt or encrypt a wallet. So, these API calls use a callback to provide progress feedback as well as the opportunity to cancel the process.
The callback should look like `function(progress)` where progress is a Number between 0 and 1 (inclusive) and if the function returns `true`, then the process will be cancelled, calling the callback once more with `callback(new Error('cancelled'))`.
#### Wallet.decrypt(json, password[, callback]);
```javascript
// See the test-wallets directory samples (the variable should be a string)
var json = "... see the test-wallets directory for samples ...";
// Decrypt a Secret Storage JSON wallet
var shouldCancelDecrypt = false;
function updateInterface(progress) {
console.log('The wallet is ' + parseInt(100 * progress) + '% decrypted');
// Optionally return true to stop this decryption; this callback will get
// called once more with callback(new Error("cancelled"))
return shouldCancelDecrypt;
}
Wallet.decrypt(json, password, updateInterface).then(function(wallet) {
// The wallet was successfully decrypted
}, function(error) {
if (error.message === 'invalid password') {
// Wrong password
} else if (error.message === 'cancelled') {
// The decryption was cancelled
}
});
```
#### Wallet.prototype.encrypt(password[, options][, callback]);
```javascript
// Encrypt a wallet into a Secret Storage JSON Wallet (all options are optional)
var bytes16 = '0xdeadbeef1deadbeef2deadbeef301234';
var options = {
salt: bytes16, // hex string or Buffer, any length
iv: bytes16, // hex string or Buffer, 16 bytes
uuid: bytes16, // hex string or Buffer, 16 bytes
scrypt: {
N: (1 << 17), // Number, power of 2 greater than 2
p: 8, // Number
r: 1 // Number
}
}
var wallet = new Wallet(privateKey);
var shouldCancelEncrypt = false;
function updateInterface(progress) {
console.log('The wallet is ' + parseInt(100 * progress) + '% encrypted');
// Optionally return true to stop this encryption; this callback will get
// called once more with callback(new Error("cancelled"))
return shouldCancelEncrypt;
}
wallet.encrypt(password, options, updateInterface).then(function(json) {
// The wallet was successfully encrypted as a json string
}, function(error) {
if (error.message === 'cancelled') {
// Cancelled
}
});
```
### Brain Wallets
Brain wallets should not be considered a secure way to store large amounts of ether; anyone who knows your username/password can steal your funds.
```javascript
// Username and passwords must be buffers; see scrypt-js library for summary
// of UTF-8 gotchas (@TOOD: include a link)
var email = new Wallet.utils.Buffer('github@ricmoo.com', 'utf8');
var password = new Wallet.utils.Buffer('password', 'utf8');
var shouldCancelSummon = false;
function updateInterface(progress) {
console.log('The wallet is ' + parseInt(100 * progress) + '% generated');
// Optionally return true to stop this generation; this callback will get
// called once more with callback(new Error("cancelled"))
return shouldCancelSummon;
}
Wallet.summonBrainWallet(email, password, updateInterface).then(function(wallet) {
// The wallet was successfully generated
}, function(error) {
if (error.message === 'cancelled') {
// Cancelled
}
});
```
Provider API
------------
Connect to standard *Ethereum* nodes via RPC (if you have a local [parity](https://ethcore.io/parity.html) or [geth](https://github.com/ethereum/go-ethereum/wiki/Geth) instance running), or via [Etherscan](https://etherscan.io):
```javascript
// The Web3 library is NOT required, but if you have one (for example from Metamask)
var web3Provider = new Web3.providers.HttpProvider('http://localhost:8545');
var web3 = new Web3(web3Provider);
// All these are equivalent
var wallet = new Wallet(privateKey, 'http://localhost:8545');
var wallet = new Wallet(privateKey, web3Provider);
var wallet = new Wallet(privateKey, web3);
// Or use Etherscan:
var wallet = new Wallet(privateKey, new Wallet.providers.EtherscanProvider({testnet: true}));
// With a provider attached, you can call additional methods on the wallet
// Get the wallet's balance
wallet.getBalance().then(function(balance) {
console.log(balance);
});
// Get the current nonce for this wallet
wallet.getTransactionCount().then(function(transactionCount) {
console.log(transactionCount);
})
// Send ether to another account or contract
wallet.send(targetAddress, Wallet.parseEther('1.0')).then(function(txid) {
console.log(txid);
})
```
Contract API
------------
```javascript
var privateKey = '0x3141592653589793238462643383279502884197169399375105820974944592';
// Create your wallet with any method from the above Provider API
var wallet = new Wallet(privateKey, 'http://localhost:8545')
console.log(wallet.address);
/// "0x7357589f8e367c2C31F51242fB77B350A11830F3"
// Find an existing contract address and ABI
// See: https://gist.github.com/ricmoo/e78709e075ff8082a86c875ac062c3c3
var simpleStorageAddress = '0xdfaf84077cF4bCECA4F79d167F47041Ed3006D5b'
var simpleStorageAbi = [
{
"constant":true,
"inputs":[],
"name":"getValue",
"outputs":[{"name":"","type":"string"}],
"type":"function"
}, {
"constant":false,
"inputs":[{"name":"value","type":"string"}],
"name":"setValue",
"outputs":[],
"type":"function"
}, {
"anonymous":false,
"inputs":[
{"indexed":false,"name":"oldValue","type":"string"},
{"indexed":false,"name":"newValue","type":"string"}
],
"name":"valueChanged",
"type":"event"
}
];
// Get the contract
var contract = wallet.getContract(simpleStorageAddress, simpleStorageAbi)
// Set up events
contract.onvaluechanged = function(oldValue, newValue) {
console.log('Value Changed from "' + oldValue + '" to "' + newValue + '".')
}
// Call constant methods, which don't alter state (free).
// Returns a promise.
contract.getValue().then(function(value) {
console.log('Value is "' + value + '".')
})
// Call state-changing methods (which will cost you ether, so use testnet to test!)
// Returns a promise.
contract.setValue("Hello World").then(function(txid) {
console.log('txid: ' + txid);
});
// Include custom parameters with a state-changing call
var options = {
gasPrice: 1000 // in wei (default: from network)
gasLimit: 3000000, // is gas (default: 3000000)
value: 1000 // in wei (default: 0)
}
contract.setValue("Hello World", options).then(function(txid) {
console.log('txid: ' + txid);
});
// Estimate the gas cost of calling a state-changing method (returns a BN.js)
contract.estimate.setValue("Hello World").then(function(gasCost) {
console.log(gasCost.toString(10));
});
```
Testing
-------
A lot of the test cases are performed by comparing against known working implementations of many of the features of this library. To run the test suite, you must use `npm install` (without the `--production` flag, which would skip the development dependencies.)
To run the test suite,
The JavaScript code is now generated from TypeScript, so make sure you modify the
TypeScript and compile it, rather than modifying the JavaScript directly. To start
auto-compiling the TypeScript code, you may use:
```
/Users/ethers> npm test
> ethers-wallet@0.0.9 test /Users/ethers/ethers-wallet
> nodeunit test.js
Running test cases... (this can take a long time, please be patient)
index.js
+ testPrivateKeyToAddress
+ testChecksumAddress
+ testIcapAddress
+ testEtherFormat
+ testTrasactions
+ testSolidityCoder
+ testContracts
+ testSecretStorage
+ testBrainWallet
+ testContractAddress
+ testProviders
OK: 52178 assertions (147788ms)
/home/ethers> npm run auto-build
```
There are also some test JSON wallets available in the [test-wallets](https://github.com/ethers-io/ethers-wallet/tree/master/test-wallets) directory.
A very important part of ethers is its exhaustive test cases, so before making any
bug fix, please add a test case that fails prior to the fix, and succeeds after the
fix. All regression tests must pass.
Pull requests are always welcome, but please keep a few points in mind:
- Compatibility-breaking changes will not be accepted; they may be considered for the next major version
- Security is important; adding dependencies require fairly convincing arguments
- The library aims to be lean, so keep an eye on the `dist/ethers.min.js` file size before and after your changes
- Add test cases for both expected and unexpected input
- Any new features need to be supported by us (issues, documentation, testing), so anything that is overly complicated or specific may not be accepted
In general, **please start an issue before beginning a pull request**, so we can have a public discussion. :)
Donations
---------
I do this because I love it, but if you want to buy me a coffee, I won't say no. **:o)**
Ethereum: `0xEA517D5a070e6705Cc5467858681Ed953d285Eb9`
License
-------
MIT Licensed, with the exceptions:
- RLP (MPL-2.0)
We are working on our own implementations and will have the library 100% MIT in the near future.
Stay tuned!
Completely MIT Licensed. Including ALL dependencies.

1
_version.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
export declare const version = "4.0.29";

3
_version.js Normal file
View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.version = "4.0.29";

11
abstract-signer.d.ts vendored Normal file
View 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
View 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
View 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
View File

@@ -0,0 +1,24 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var bignumber_1 = require("./utils/bignumber");
var AddressZero = '0x0000000000000000000000000000000000000000';
exports.AddressZero = AddressZero;
var HashZero = '0x0000000000000000000000000000000000000000000000000000000000000000';
exports.HashZero = HashZero;
// NFKD (decomposed)
//const EtherSymbol = '\uD835\uDF63';
// NFKC (composed)
var EtherSymbol = '\u039e';
exports.EtherSymbol = EtherSymbol;
var NegativeOne = bignumber_1.bigNumberify(-1);
exports.NegativeOne = NegativeOne;
var Zero = bignumber_1.bigNumberify(0);
exports.Zero = Zero;
var One = bignumber_1.bigNumberify(1);
exports.One = One;
var Two = bignumber_1.bigNumberify(2);
exports.Two = Two;
var WeiPerEther = bignumber_1.bigNumberify('1000000000000000000');
exports.WeiPerEther = WeiPerEther;
var MaxUint256 = bignumber_1.bigNumberify('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
exports.MaxUint256 = MaxUint256;

86
contract.d.ts vendored Normal file
View File

@@ -0,0 +1,86 @@
import { BigNumber } from './utils/bignumber';
import { Indexed, Interface } from './utils/interface';
import { UnsignedTransaction } from './utils/transaction';
import { BlockTag, Provider } from './providers/abstract-provider';
import { Signer } from './abstract-signer';
import { Arrayish } from './utils/bytes';
import { ParamType } from './utils/abi-coder';
import { Block, Listener, Log, TransactionReceipt, TransactionRequest, TransactionResponse } from './providers/abstract-provider';
export declare type ContractFunction = (...params: Array<any>) => Promise<any>;
export declare type EventFilter = {
address?: string;
topics?: Array<string>;
};
export interface Event extends Log {
args?: Array<any>;
decode?: (data: string, topics?: Array<string>) => any;
event?: string;
eventSignature?: string;
removeListener: () => void;
getBlock: () => Promise<Block>;
getTransaction: () => Promise<TransactionResponse>;
getTransactionReceipt: () => Promise<TransactionReceipt>;
}
export interface ContractReceipt extends TransactionReceipt {
events?: Array<Event>;
}
export interface ContractTransaction extends TransactionResponse {
wait(confirmations?: number): Promise<ContractReceipt>;
}
export declare class VoidSigner extends Signer {
readonly address: string;
constructor(address: string, provider: Provider);
getAddress(): Promise<string>;
_fail(message: string, operation: string): Promise<any>;
signMessage(message: Arrayish | string): Promise<string>;
sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse>;
connect(provider: Provider): VoidSigner;
}
interface Bucket<T> {
[name: string]: T;
}
export declare class Contract {
readonly address: string;
readonly interface: Interface;
readonly signer: Signer;
readonly provider: Provider;
readonly estimate: Bucket<(...params: Array<any>) => Promise<BigNumber>>;
readonly functions: Bucket<ContractFunction>;
readonly filters: Bucket<(...params: Array<any>) => EventFilter>;
readonly [name: string]: ContractFunction | any;
readonly addressPromise: Promise<string>;
readonly deployTransaction: TransactionResponse;
private _deployedPromise;
constructor(addressOrName: string, contractInterface: Array<string | ParamType> | string | Interface, signerOrProvider: Signer | Provider);
deployed(): Promise<Contract>;
_deployed(blockTag?: BlockTag): Promise<Contract>;
fallback(overrides?: TransactionRequest): Promise<TransactionResponse>;
connect(signerOrProvider: Signer | Provider | string): Contract;
attach(addressOrName: string): Contract;
static isIndexed(value: any): value is Indexed;
private _events;
private _getEventFilter;
private _addEventListener;
on(event: EventFilter | string, listener: Listener): Contract;
once(event: EventFilter | string, listener: Listener): Contract;
addListener(eventName: EventFilter | string, listener: Listener): Contract;
emit(eventName: EventFilter | string, ...args: Array<any>): boolean;
listenerCount(eventName?: EventFilter | string): number;
listeners(eventName: EventFilter | string): Array<Listener>;
removeAllListeners(eventName: EventFilter | string): Contract;
removeListener(eventName: any, listener: Listener): Contract;
}
export declare class ContractFactory {
readonly interface: Interface;
readonly bytecode: string;
readonly signer: Signer;
constructor(contractInterface: Array<string | ParamType> | string | Interface, bytecode: Arrayish | string | {
object: string;
}, signer?: Signer);
getDeployTransaction(...args: Array<any>): UnsignedTransaction;
deploy(...args: Array<any>): Promise<Contract>;
attach(address: string): Contract;
connect(signer: Signer): ContractFactory;
static fromSolidity(compilerOutput: any, signer?: Signer): ContractFactory;
}
export {};

710
contract.js Normal file
View File

@@ -0,0 +1,710 @@
'use strict';
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
var constants_1 = require("./constants");
var errors = __importStar(require("./errors"));
var abi_coder_1 = require("./utils/abi-coder");
var address_1 = require("./utils/address");
var bignumber_1 = require("./utils/bignumber");
var bytes_1 = require("./utils/bytes");
var interface_1 = require("./utils/interface");
var properties_1 = require("./utils/properties");
///////////////////////////////
// Imported Abstracts
var abstract_provider_1 = require("./providers/abstract-provider");
var abstract_signer_1 = require("./abstract-signer");
///////////////////////////////
var VoidSigner = /** @class */ (function (_super) {
__extends(VoidSigner, _super);
function VoidSigner(address, provider) {
var _this = _super.call(this) || this;
properties_1.defineReadOnly(_this, 'address', address);
properties_1.defineReadOnly(_this, 'provider', provider);
return _this;
}
VoidSigner.prototype.getAddress = function () {
return Promise.resolve(this.address);
};
VoidSigner.prototype._fail = function (message, operation) {
return Promise.resolve().then(function () {
errors.throwError(message, errors.UNSUPPORTED_OPERATION, { operation: operation });
});
};
VoidSigner.prototype.signMessage = function (message) {
return this._fail('VoidSigner cannot sign messages', 'signMessage');
};
VoidSigner.prototype.sendTransaction = function (transaction) {
return this._fail('VoidSigner cannot sign transactions', 'sendTransaction');
};
VoidSigner.prototype.connect = function (provider) {
return new VoidSigner(this.address, provider);
};
return VoidSigner;
}(abstract_signer_1.Signer));
exports.VoidSigner = VoidSigner;
var allowedTransactionKeys = {
chainId: true, data: true, from: true, gasLimit: true, gasPrice: true, nonce: true, to: true, value: true
};
// Recursively replaces ENS names with promises to resolve the name and
// stalls until all promises have returned
// @TODO: Expand this to resolve any promises too
function resolveAddresses(provider, value, paramType) {
if (Array.isArray(paramType)) {
var promises_1 = [];
paramType.forEach(function (paramType, index) {
var v = null;
if (Array.isArray(value)) {
v = value[index];
}
else {
v = value[paramType.name];
}
promises_1.push(resolveAddresses(provider, v, paramType));
});
return Promise.all(promises_1);
}
if (paramType.type === 'address') {
return provider.resolveName(value);
}
if (paramType.type === 'tuple') {
return resolveAddresses(provider, value, paramType.components);
}
// Strips one level of array indexing off the end to recuse into
var isArrayMatch = paramType.type.match(/(.*)(\[[0-9]*\]$)/);
if (isArrayMatch) {
if (!Array.isArray(value)) {
throw new Error('invalid value for array');
}
var promises = [];
var subParamType = {
components: paramType.components,
type: isArrayMatch[1],
};
value.forEach(function (v) {
promises.push(resolveAddresses(provider, v, subParamType));
});
return Promise.all(promises);
}
return Promise.resolve(value);
}
function runMethod(contract, functionName, estimateOnly) {
var method = contract.interface.functions[functionName];
return function () {
var params = [];
for (var _i = 0; _i < arguments.length; _i++) {
params[_i] = arguments[_i];
}
var tx = {};
var blockTag = null;
// If 1 extra parameter was passed in, it contains overrides
if (params.length === method.inputs.length + 1 && typeof (params[params.length - 1]) === 'object') {
tx = properties_1.shallowCopy(params.pop());
if (tx.blockTag != null) {
blockTag = tx.blockTag;
}
delete tx.blockTag;
// Check for unexpected keys (e.g. using "gas" instead of "gasLimit")
for (var key in tx) {
if (!allowedTransactionKeys[key]) {
throw new Error('unknown transaction override ' + key);
}
}
}
if (params.length != method.inputs.length) {
throw new Error('incorrect number of arguments');
}
// Check overrides make sense
['data', 'to'].forEach(function (key) {
if (tx[key] != null) {
errors.throwError('cannot override ' + key, errors.UNSUPPORTED_OPERATION, { operation: key });
}
});
tx.to = contract._deployed(blockTag).then(function () {
return contract.addressPromise;
});
return resolveAddresses(contract.provider, params, method.inputs).then(function (params) {
tx.data = method.encode(params);
if (method.type === 'call') {
// Call (constant functions) always cost 0 ether
if (estimateOnly) {
return Promise.resolve(constants_1.Zero);
}
if (!contract.provider) {
errors.throwError('call (constant functions) require a provider or a signer with a provider', errors.UNSUPPORTED_OPERATION, { operation: 'call' });
}
// Check overrides make sense
['gasLimit', 'gasPrice', 'value'].forEach(function (key) {
if (tx[key] != null) {
throw new Error('call cannot override ' + key);
}
});
if (tx.from == null && contract.signer) {
tx.from = contract.signer.getAddress();
}
return contract.provider.call(tx, blockTag).then(function (value) {
if ((bytes_1.hexDataLength(value) % 32) === 4 && bytes_1.hexDataSlice(value, 0, 4) === '0x08c379a0') {
var reason = abi_coder_1.defaultAbiCoder.decode(['string'], bytes_1.hexDataSlice(value, 4));
errors.throwError('call revert exception', errors.CALL_EXCEPTION, {
address: contract.address,
args: params,
method: method.signature,
errorSignature: 'Error(string)',
errorArgs: [reason],
reason: reason,
transaction: tx
});
}
try {
var result = method.decode(value);
if (method.outputs.length === 1) {
result = result[0];
}
return result;
}
catch (error) {
if (value === '0x' && method.outputs.length > 0) {
errors.throwError('call exception', errors.CALL_EXCEPTION, {
address: contract.address,
method: method.signature,
args: params
});
}
throw error;
}
});
}
else if (method.type === 'transaction') {
// Only computing the transaction estimate
if (estimateOnly) {
if (!contract.provider) {
errors.throwError('estimate gas require a provider or a signer with a provider', errors.UNSUPPORTED_OPERATION, { operation: 'estimateGas' });
}
if (tx.from == null && contract.signer) {
tx.from = contract.signer.getAddress();
}
return contract.provider.estimateGas(tx);
}
if (tx.gasLimit == null && method.gas != null) {
tx.gasLimit = bignumber_1.bigNumberify(method.gas).add(21000);
}
if (!contract.signer) {
errors.throwError('sending a transaction require a signer', errors.UNSUPPORTED_OPERATION, { operation: 'sendTransaction' });
}
// Make sure they aren't overriding something they shouldn't
if (tx.from != null) {
errors.throwError('cannot override from in a transaction', errors.UNSUPPORTED_OPERATION, { operation: 'sendTransaction' });
}
return contract.signer.sendTransaction(tx).then(function (tx) {
var wait = tx.wait.bind(tx);
tx.wait = function (confirmations) {
return wait(confirmations).then(function (receipt) {
receipt.events = receipt.logs.map(function (log) {
var event = properties_1.deepCopy(log);
var parsed = contract.interface.parseLog(log);
if (parsed) {
event.args = parsed.values;
event.decode = parsed.decode;
event.event = parsed.name;
event.eventSignature = parsed.signature;
}
event.removeListener = function () { return contract.provider; };
event.getBlock = function () {
return contract.provider.getBlock(receipt.blockHash);
};
event.getTransaction = function () {
return contract.provider.getTransaction(receipt.transactionHash);
};
event.getTransactionReceipt = function () {
return Promise.resolve(receipt);
};
return event;
});
return receipt;
});
};
return tx;
});
}
throw new Error('invalid type - ' + method.type);
return null;
});
};
}
function getEventTag(filter) {
if (filter.address && (filter.topics == null || filter.topics.length === 0)) {
return '*';
}
return (filter.address || '*') + '@' + (filter.topics ? filter.topics.join(':') : '');
}
var Contract = /** @class */ (function () {
// https://github.com/Microsoft/TypeScript/issues/5453
// Once this issue is resolved (there are open PR) we can do this nicer
// by making addressOrName default to null for 2 operand calls. :)
function Contract(addressOrName, contractInterface, signerOrProvider) {
var _this = this;
errors.checkNew(this, Contract);
// @TODO: Maybe still check the addressOrName looks like a valid address or name?
//address = getAddress(address);
if (interface_1.Interface.isInterface(contractInterface)) {
properties_1.defineReadOnly(this, 'interface', contractInterface);
}
else {
properties_1.defineReadOnly(this, 'interface', new interface_1.Interface(contractInterface));
}
if (abstract_signer_1.Signer.isSigner(signerOrProvider)) {
properties_1.defineReadOnly(this, 'provider', signerOrProvider.provider);
properties_1.defineReadOnly(this, 'signer', signerOrProvider);
}
else if (abstract_provider_1.Provider.isProvider(signerOrProvider)) {
properties_1.defineReadOnly(this, 'provider', signerOrProvider);
properties_1.defineReadOnly(this, 'signer', null);
}
else {
errors.throwError('invalid signer or provider', errors.INVALID_ARGUMENT, { arg: 'signerOrProvider', value: signerOrProvider });
}
properties_1.defineReadOnly(this, 'estimate', {});
properties_1.defineReadOnly(this, 'functions', {});
properties_1.defineReadOnly(this, 'filters', {});
Object.keys(this.interface.events).forEach(function (eventName) {
var event = _this.interface.events[eventName];
properties_1.defineReadOnly(_this.filters, eventName, function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
return {
address: _this.address,
topics: event.encodeTopics(args)
};
});
});
this._events = [];
properties_1.defineReadOnly(this, 'address', addressOrName);
if (this.provider) {
properties_1.defineReadOnly(this, 'addressPromise', this.provider.resolveName(addressOrName).then(function (address) {
if (address == null) {
throw new Error('name not found');
}
return address;
}).catch(function (error) {
throw error;
}));
}
else {
try {
properties_1.defineReadOnly(this, 'addressPromise', Promise.resolve(address_1.getAddress(addressOrName)));
}
catch (error) {
// Without a provider, we cannot use ENS names
errors.throwError('provider is required to use non-address contract address', errors.INVALID_ARGUMENT, { argument: 'addressOrName', value: addressOrName });
}
}
Object.keys(this.interface.functions).forEach(function (name) {
var run = runMethod(_this, name, false);
if (_this[name] == null) {
properties_1.defineReadOnly(_this, name, run);
}
else {
errors.warn('WARNING: Multiple definitions for ' + name);
}
if (_this.functions[name] == null) {
properties_1.defineReadOnly(_this.functions, name, run);
properties_1.defineReadOnly(_this.estimate, name, runMethod(_this, name, true));
}
});
}
// @TODO: Allow timeout?
Contract.prototype.deployed = function () {
return this._deployed();
};
Contract.prototype._deployed = function (blockTag) {
var _this = this;
if (!this._deployedPromise) {
// If we were just deployed, we know the transaction we should occur in
if (this.deployTransaction) {
this._deployedPromise = this.deployTransaction.wait().then(function () {
return _this;
});
}
else {
// @TODO: Once we allow a timeout to be passed in, we will wait
// up to that many blocks for getCode
// Otherwise, poll for our code to be deployed
this._deployedPromise = this.provider.getCode(this.address, blockTag).then(function (code) {
if (code === '0x') {
errors.throwError('contract not deployed', errors.UNSUPPORTED_OPERATION, {
contractAddress: _this.address,
operation: 'getDeployed'
});
}
return _this;
});
}
}
return this._deployedPromise;
};
// @TODO:
// estimateFallback(overrides?: TransactionRequest): Promise<BigNumber>
// @TODO:
// estimateDeploy(bytecode: string, ...args): Promise<BigNumber>
Contract.prototype.fallback = function (overrides) {
var _this = this;
if (!this.signer) {
errors.throwError('sending a transaction require a signer', errors.UNSUPPORTED_OPERATION, { operation: 'sendTransaction(fallback)' });
}
var tx = properties_1.shallowCopy(overrides || {});
['from', 'to'].forEach(function (key) {
if (tx[key] == null) {
return;
}
errors.throwError('cannot override ' + key, errors.UNSUPPORTED_OPERATION, { operation: key });
});
tx.to = this.addressPromise;
return this.deployed().then(function () {
return _this.signer.sendTransaction(tx);
});
};
// Reconnect to a different signer or provider
Contract.prototype.connect = function (signerOrProvider) {
if (typeof (signerOrProvider) === 'string') {
signerOrProvider = new VoidSigner(signerOrProvider, this.provider);
}
var contract = new Contract(this.address, this.interface, signerOrProvider);
if (this.deployTransaction) {
properties_1.defineReadOnly(contract, 'deployTransaction', this.deployTransaction);
}
return contract;
};
// Re-attach to a different on=chain instance of this contract
Contract.prototype.attach = function (addressOrName) {
return new Contract(addressOrName, this.interface, this.signer || this.provider);
};
Contract.isIndexed = function (value) {
return interface_1.Interface.isIndexed(value);
};
Contract.prototype._getEventFilter = function (eventName) {
var _this = this;
if (typeof (eventName) === 'string') {
// Listen for any event
if (eventName === '*') {
return {
prepareEvent: function (e) {
var parsed = _this.interface.parseLog(e);
if (parsed) {
e.args = parsed.values;
e.decode = parsed.decode;
e.event = parsed.name;
e.eventSignature = parsed.signature;
}
return [e];
},
eventTag: '*',
filter: { address: this.address },
};
}
// Normalize the eventName
if (eventName.indexOf('(') !== -1) {
eventName = abi_coder_1.formatSignature(abi_coder_1.parseSignature('event ' + eventName));
}
var event_1 = this.interface.events[eventName];
if (!event_1) {
errors.throwError('unknown event - ' + eventName, errors.INVALID_ARGUMENT, { argumnet: 'eventName', value: eventName });
}
var filter_1 = {
address: this.address,
topics: [event_1.topic]
};
return {
prepareEvent: function (e) {
var args = event_1.decode(e.data, e.topics);
e.args = args;
var result = Array.prototype.slice.call(args);
result.push(e);
return result;
},
event: event_1,
eventTag: getEventTag(filter_1),
filter: filter_1
};
}
var filter = {
address: this.address
};
// Find the matching event in the ABI; if none, we still allow filtering
// since it may be a filter for an otherwise unknown event
var event = null;
if (eventName.topics && eventName.topics[0]) {
filter.topics = eventName.topics;
for (var name_1 in this.interface.events) {
if (name_1.indexOf('(') === -1) {
continue;
}
var e = this.interface.events[name_1];
if (e.topic === eventName.topics[0].toLowerCase()) {
event = e;
break;
}
}
}
return {
prepareEvent: function (e) {
if (!event) {
return [e];
}
var args = event.decode(e.data, e.topics);
e.args = args;
var result = Array.prototype.slice.call(args);
result.push(e);
return result;
},
event: event,
eventTag: getEventTag(filter),
filter: filter
};
};
Contract.prototype._addEventListener = function (eventFilter, listener, once) {
var _this = this;
if (!this.provider) {
errors.throwError('events require a provider or a signer with a provider', errors.UNSUPPORTED_OPERATION, { operation: 'once' });
}
var wrappedListener = function (log) {
var event = properties_1.deepCopy(log);
var args = eventFilter.prepareEvent(event);
if (eventFilter.event) {
event.decode = eventFilter.event.decode;
event.event = eventFilter.event.name;
event.eventSignature = eventFilter.event.signature;
}
event.removeListener = function () { _this.removeListener(eventFilter.filter, listener); };
event.getBlock = function () { return _this.provider.getBlock(log.blockHash); };
event.getTransaction = function () { return _this.provider.getTransaction(log.transactionHash); };
event.getTransactionReceipt = function () { return _this.provider.getTransactionReceipt(log.transactionHash); };
_this.emit.apply(_this, [eventFilter.filter].concat(args));
};
this.provider.on(eventFilter.filter, wrappedListener);
this._events.push({ eventFilter: eventFilter, listener: listener, wrappedListener: wrappedListener, once: once });
};
Contract.prototype.on = function (event, listener) {
this._addEventListener(this._getEventFilter(event), listener, false);
return this;
};
Contract.prototype.once = function (event, listener) {
this._addEventListener(this._getEventFilter(event), listener, true);
return this;
};
Contract.prototype.addListener = function (eventName, listener) {
return this.on(eventName, listener);
};
Contract.prototype.emit = function (eventName) {
var _this = this;
var args = [];
for (var _i = 1; _i < arguments.length; _i++) {
args[_i - 1] = arguments[_i];
}
if (!this.provider) {
return false;
}
var result = false;
var eventFilter = this._getEventFilter(eventName);
this._events = this._events.filter(function (event) {
// Not this event (keep it for later)
if (event.eventFilter.eventTag !== eventFilter.eventTag) {
return true;
}
// Call the callback in the next event loop
setTimeout(function () {
event.listener.apply(_this, args);
}, 0);
result = true;
// Reschedule it if it not "once"
return !(event.once);
});
return result;
};
Contract.prototype.listenerCount = function (eventName) {
if (!this.provider) {
return 0;
}
var eventFilter = this._getEventFilter(eventName);
return this._events.filter(function (event) {
return event.eventFilter.eventTag === eventFilter.eventTag;
}).length;
};
Contract.prototype.listeners = function (eventName) {
if (!this.provider) {
return [];
}
var eventFilter = this._getEventFilter(eventName);
return this._events.filter(function (event) {
return event.eventFilter.eventTag === eventFilter.eventTag;
}).map(function (event) { return event.listener; });
};
Contract.prototype.removeAllListeners = function (eventName) {
var _this = this;
if (!this.provider) {
return this;
}
var eventFilter = this._getEventFilter(eventName);
this._events = this._events.filter(function (event) {
// Keep all other events
if (event.eventFilter.eventTag !== eventFilter.eventTag) {
return true;
}
// Deregister this event from the provider and filter it out
_this.provider.removeListener(event.eventFilter.filter, event.wrappedListener);
return false;
});
return this;
};
Contract.prototype.removeListener = function (eventName, listener) {
var _this = this;
if (!this.provider) {
return this;
}
var found = false;
var eventFilter = this._getEventFilter(eventName);
this._events = this._events.filter(function (event) {
// Make sure this event and listener match
if (event.eventFilter.eventTag !== eventFilter.eventTag) {
return true;
}
if (event.listener !== listener) {
return true;
}
_this.provider.removeListener(event.eventFilter.filter, event.wrappedListener);
// Already found a matching event in a previous loop
if (found) {
return true;
}
// REmove this event (returning false filters us out)
found = true;
return false;
});
return this;
};
return Contract;
}());
exports.Contract = Contract;
var ContractFactory = /** @class */ (function () {
function ContractFactory(contractInterface, bytecode, signer) {
var bytecodeHex = null;
// Allow the bytecode object from the Solidity compiler
if (typeof (bytecode) === 'string') {
bytecodeHex = bytecode;
}
else if (bytes_1.isArrayish(bytecode)) {
bytecodeHex = bytes_1.hexlify(bytecode);
}
else if (typeof (bytecode.object) === 'string') {
bytecodeHex = bytecode.object;
}
else {
errors.throwError('bytecode must be a valid hex string', errors.INVALID_ARGUMENT, { arg: 'bytecode', value: bytecode });
}
// Make sure it is 0x prefixed
if (bytecodeHex.substring(0, 2) !== '0x') {
bytecodeHex = '0x' + bytecodeHex;
}
if (!bytes_1.isHexString(bytecodeHex)) {
errors.throwError('bytecode must be a valid hex string', errors.INVALID_ARGUMENT, { arg: 'bytecode', value: bytecode });
}
if ((bytecodeHex.length % 2) !== 0) {
errors.throwError('bytecode must be valid data (even length)', errors.INVALID_ARGUMENT, { arg: 'bytecode', value: bytecode });
}
properties_1.defineReadOnly(this, 'bytecode', bytecodeHex);
if (interface_1.Interface.isInterface(contractInterface)) {
properties_1.defineReadOnly(this, 'interface', contractInterface);
}
else {
properties_1.defineReadOnly(this, 'interface', new interface_1.Interface(contractInterface));
}
if (signer && !abstract_signer_1.Signer.isSigner(signer)) {
errors.throwError('invalid signer', errors.INVALID_ARGUMENT, { arg: 'signer', value: null });
}
properties_1.defineReadOnly(this, 'signer', signer || null);
}
ContractFactory.prototype.getDeployTransaction = function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
var tx = {};
// If we have 1 additional argument, we allow transaction overrides
if (args.length === this.interface.deployFunction.inputs.length + 1) {
tx = properties_1.shallowCopy(args.pop());
for (var key in tx) {
if (!allowedTransactionKeys[key]) {
throw new Error('unknown transaction override ' + key);
}
}
}
// Do not allow these to be overridden in a deployment transaction
['data', 'from', 'to'].forEach(function (key) {
if (tx[key] == null) {
return;
}
errors.throwError('cannot override ' + key, errors.UNSUPPORTED_OPERATION, { operation: key });
});
// Make sure the call matches the constructor signature
errors.checkArgumentCount(args.length, this.interface.deployFunction.inputs.length, ' in Contract constructor');
// Set the data to the bytecode + the encoded constructor arguments
tx.data = this.interface.deployFunction.encode(this.bytecode, args);
return tx;
};
ContractFactory.prototype.deploy = function () {
var _this = this;
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
// Get the deployment transaction (with optional overrides)
var tx = this.getDeployTransaction.apply(this, args);
// Send the deployment transaction
return this.signer.sendTransaction(tx).then(function (tx) {
var contract = new Contract(address_1.getContractAddress(tx), _this.interface, _this.signer);
properties_1.defineReadOnly(contract, 'deployTransaction', tx);
return contract;
});
};
ContractFactory.prototype.attach = function (address) {
return new Contract(address, this.interface, this.signer);
};
ContractFactory.prototype.connect = function (signer) {
return new ContractFactory(this.interface, this.bytecode, signer);
};
ContractFactory.fromSolidity = function (compilerOutput, signer) {
if (compilerOutput == null) {
errors.throwError('missing compiler output', errors.MISSING_ARGUMENT, { argument: 'compilerOutput' });
}
if (typeof (compilerOutput) === 'string') {
compilerOutput = JSON.parse(compilerOutput);
}
var abi = compilerOutput.abi;
var bytecode = null;
if (compilerOutput.bytecode) {
bytecode = compilerOutput.bytecode;
}
else if (compilerOutput.evm && compilerOutput.evm.bytecode) {
bytecode = compilerOutput.evm.bytecode;
}
return new ContractFactory(abi, bytecode, signer);
};
return ContractFactory;
}());
exports.ContractFactory = ContractFactory;

View File

@@ -1,42 +1,13 @@
<html>
<head>
<title>Ethereum Wallet</title>
<link rel="stylesheet" type="text/css" href="../style.css">
<link rel="stylesheet" type="text/css" href="./style.css">
</head>
<body>
<div class="centerer" id="screen-select">
<div class="centered">
<h1>Ethereum Wallet Tool</h1>
<hr />
<h2>Summon Brain Wallet</h2>
<p>
A brain wallet generates an <i>Ethereum</i> wallet from a username and a password
without using any servers to store your information. If you lose your username
or password, <b>no one</b> can help you recover them. Anyone who can guess your
username and password can steal your funds. Brain wallets should <b>not</b> be
considered a safe way to store large amounts of ether nor for long periods of time.
</p>
<table>
<tr>
<th>E-mail Address:</th>
<td><input type="text" placeholder="(e-mail address)" id="select-brainwallet-username" /></td>
</tr>
<tr>
<th>Password:</th>
<td><input type="password" placeholder="(password)" id="select-brainwallet-password" /></td>
</tr>
<tr>
<th>Confirm Password:</th>
<td><input type="password" placeholder="(same password)" id="select-brainwallet-confirm-password" /></td>
</tr>
<tr>
<td> </td>
<td>
<div id="select-submit-brainwallet" class="submit disable">Summon Brain Wallet</div>
</td>
</tr>
</table>
<hr />
<h2>Load JSON Wallet</h2>
<p>
If you have a JSON wallet file from <i>geth</i> or from the initial <i>Ethereum</i>
@@ -60,6 +31,29 @@
</tr>
</table>
<hr />
<h2>Mnemonic Phrase Wallet</h2>
<p>
If you have a 12 word mnemonic phrase, you can generate your wallet here.
No information is shared with <b>any</b> server.
</p>
<table>
<tr>
<th>Mnemonic Phrase:</th>
<td><input type="text" placeholder="(mnemonic phrase)" id="select-mnemonic-phrase" /></td>
</tr>
<tr>
<th>Path:</th>
<td><input type="text" placeholder="(path)" id="select-mnemonic-path" value="m/44'/60'/0'/0/0" /></td>
</tr>
<tr>
<td> </td>
<td>
<div id="select-submit-mnemonic" class="submit disable">Derive Wallet</div>
</td>
</tr>
</table>
<hr />
<h2>Raw Private Key</h2>
<p>
@@ -72,7 +66,7 @@
<tr>
<td> </td>
<td>
<div id="select-submit-privatekey" class="submit disable">Unlock JSON Wallet</div>
<div id="select-submit-privatekey" class="submit disable">Load Raw Private Key</div>
</td>
</tr>
</table>
@@ -126,8 +120,12 @@
<tr>
<th>Network:</th>
<td>
<div class="option left" id="option-morden">Morden (testnet)</div>
<div class="option right selected" id="option-homestead">Homestead (mainnet)</div>
<div class="clearfix"></div>
<div class="network option" data-network="ropsten">Ropsten<br /><span>testnet</span></div>
<div class="network option" data-network="rinkeby">Rinkeby<br /><span>testnet</span></div>
<div class="network option" data-network="kovan">Kovan<br /><span>testnet</span></div>
<div class="network option last selected" data-network="homestead">Homestead<br /><span>mainnet</span></div>
<div class="clearfix"></div>
</td>
</tr>
<tr>
@@ -177,8 +175,16 @@
</div>
</div>
<script type="text/javascript" src="../../dist/ethers-wallet.js"></script>
<script type="text/javascript" src="../../dist/ethers.js"></script>
<script type="text/javascript">
function query(el, selector) {
if (!selector) {
selector = el;
el = document;
}
return Array.prototype.slice.call(el.querySelectorAll(selector));
}
function setEnter(source, target) {
source.onkeyup = function(e) {
if (e.which === 13) { target.click(); }
@@ -190,58 +196,15 @@
cancelScrypt = true;
};
(function() {
var inputUsername = document.getElementById('select-brainwallet-username');
var inputPassword = document.getElementById('select-brainwallet-password');
var inputConfirmPassword = document.getElementById('select-brainwallet-confirm-password');
var submit = document.getElementById('select-submit-brainwallet');
function checkBrainwallet() {
if (inputUsername.value && inputPassword.value && inputPassword.value === inputConfirmPassword.value) {
submit.classList.remove('disable');
} else {
submit.classList.add('disable');
}
}
inputUsername.oninput = checkBrainwallet;
inputPassword.oninput = checkBrainwallet;
inputConfirmPassword.oninput = checkBrainwallet;
setEnter(inputUsername, submit);
setEnter(inputPassword, submit);
setEnter(inputConfirmPassword, submit);
submit.onclick = function() {
if (submit.classList.contains('disable')) { return; }
var username = new Wallet.utils.Buffer(inputUsername.value, 'utf8');
var password = new Wallet.utils.Buffer(inputPassword.value, 'utf8');
showLoading('Summoning Brain Wallet...');
cancelScrypt = false;
Wallet.summonBrainWallet(username, password, function(error, wallet, progress) {
if (error) {
if (error.message !== 'cancelled') {
alert('Unknown error');
}
showSelect();
} else if (wallet) {
showWallet(wallet);
document.getElementById('wallet-username').textContent = inputUsername.value;
} else {
updateLoading(progress);
}
return cancelScrypt;
});
};
var updateLoading = (function() {
var loadingStatus = document.getElementById('loading-status');
return (function(progress) {
loadingStatus.value = (parseInt(progress * 100)) + '%';
return cancelScrypt;
});
})();
// JSON Wallet
(function() {
var inputFile = document.getElementById('select-wallet-file');
var targetDrop = document.getElementById('select-wallet-drop');
@@ -278,33 +241,22 @@
fileReader.onload = function(e) {
var json = e.target.result;
if (Wallet.isCrowdsaleWallet(json)) {
showWallet(Wallet.decryptCrowdsale(json, password));
} else if (Wallet.isValidWallet(json)) {
if (ethers.utils.getJsonWalletAddress(json)) {
showLoading('Decrypting Wallet...');
var password = new Wallet.utils.Buffer(inputPassword.value);
cancelScrypt = false;
Wallet.decrypt(json, password, function(error, wallet, progress) {
if (error) {
if (error.message === 'invalid password') {
alert('Wrong Password');
} else {
console.log(error);
alert('Error Decrypting Wallet');
}
showSelect();
} else if (wallet) {
showWallet(wallet);
ethers.Wallet.fromEncryptedJson(json, inputPassword.value, updateLoading).then(function(wallet) {
showWallet(wallet);
}, function(error) {
if (error.message === 'invalid password') {
alert('Wrong Password');
} else {
updateLoading(progress);
console.log(error);
alert('Error Decrypting Wallet');
}
return cancelScrypt;
showSelect();
});
} else {
alert('Unknown JSON wallet format');
@@ -315,6 +267,7 @@
})();
// Raw Private Key
(function() {
var inputPrivatekey = document.getElementById('select-privatekey');
var submit = document.getElementById('select-submit-privatekey');
@@ -334,7 +287,32 @@
if (submit.classList.contains('disable')) { return; }
var privateKey = inputPrivatekey.value;
if (privateKey.substring(0, 2) !== '0x') { privateKey = '0x' + privateKey; }
showWallet(new Wallet(privateKey));
showWallet(new ethers.Wallet(privateKey));
}
})();
// Mnemonic Phrase
(function() {
var inputPhrase = document.getElementById('select-mnemonic-phrase');
var inputPath = document.getElementById('select-mnemonic-path');
var submit = document.getElementById('select-submit-mnemonic');
function check() {
if (ethers.HDNode.isValidMnemonic(inputPhrase.value)) {
submit.classList.remove('disable');
} else {
submit.classList.add('disable');
}
}
inputPhrase.oninput = check;
inputPath.oninput = check;
setEnter(inputPhrase, submit);
setEnter(inputPath, submit);
submit.onclick = function() {
if (submit.classList.contains('disable')) { return; }
showWallet(ethers.Wallet.fromMnemonic(inputPhrase.value, inputPath.value));
}
})();
@@ -355,7 +333,7 @@
addActivity('> Refreshing details...');
activeWallet.getBalance('pending').then(function(balance) {
addActivity('< Balance: ' + balance.toString(10));
inputBalance.value = Wallet.formatEther(balance, {commify: true});
inputBalance.value = ethers.utils.formatEther(balance, { commify: true });
}, function(error) {
showError(error);
});
@@ -376,7 +354,10 @@
return function(message, url) {
var line = document.createElement('a');
line.textContent = message;
if (url) { line.setAttribute('href', url); }
if (url) {
line.setAttribute('href', url);
line.setAttribute('target', '_blank');
}
activity.appendChild(line);
}
})();
@@ -391,8 +372,8 @@
// Validate the address and value (to enable the send button)
function check() {
try {
Wallet.getAddress(inputTargetAddress.value);
Wallet.parseEther(inputAmount.value);
ethers.utils.getAddress(inputTargetAddress.value);
ethers.utils.parseEther(inputAmount.value);
} catch (error) {
submit.classList.add('disable');
return;
@@ -402,45 +383,43 @@
inputTargetAddress.oninput = check;
inputAmount.oninput = check;
var optionMorden = document.getElementById('option-morden');
var optionHomestead = document.getElementById('option-homestead');
// Select the morden network
optionMorden.onclick = function() {
if (optionMorden.classList.contains('selected')) { return; }
addActivity('! Switched network: Morden');
activeWallet.provider = new Wallet.providers.EtherscanProvider({testnet: true});
optionMorden.classList.add('selected');
optionHomestead.classList.remove('selected');
refresh();
}
// Select the homestead network
optionHomestead.onclick = function() {
if (optionHomestead.classList.contains('selected')) { return; }
addActivity('! Switched network: Homestead');
activeWallet.provider = new Wallet.providers.EtherscanProvider({testnet: false});
optionMorden.classList.remove('selected');
optionHomestead.classList.add('selected');
refresh();
}
query('.network.option').forEach(function(el) {
var network = el.getAttribute('data-network');
el.onclick = function() {
addActivity('! Switched network: ' + network);
activeWallet = activeWallet.connect(ethers.providers.getDefaultProvider(network));
query('.network.option.selected').forEach(function(el) {
el.classList.remove('selected');
});
el.classList.add('selected');
refresh();
};
});
// Send ether
submit.onclick = function() {
// Matt (from Etherscan) is working on a gasPrice API call, which
// should be done within a week or so.
var gasPrice = (activeWallet.provider.testnet ? 0x4a817c800: 0xba43b7400);
console.log('GasPrice: ' + gasPrice);
// @TODO
//var gasPrice = (activeWallet.provider.testnet ? 0x4a817c800: 0xba43b7400);
//console.log('GasPrice: ' + gasPrice);
var targetAddress = Wallet.getAddress(inputTargetAddress.value);
var amountWei = Wallet.parseEther(inputAmount.value);
activeWallet.send(targetAddress, amountWei, {
gasPrice: gasPrice,
gasLimit: 21000,
}).then(function(txid) {
var url = (activeWallet.provider.testnet ? 'https://testnet.etherscan.io/tx/': 'https://etherscan.io/tx/') + txid;
addActivity('< Transaction sent: ' + txid.substring(0, 20) + '...', url);
var targetAddress = ethers.utils.getAddress(inputTargetAddress.value);
var amountWei = ethers.utils.parseEther(inputAmount.value);
activeWallet.sendTransaction({
to: targetAddress,
value: amountWei,
//gasPrice: activeWallet.provider.getGasPrice(),
//gasLimit: 21000,
}).then(function(tx) {
console.log(tx);
// Since we only use standard networks, network will always be known
var tag = activeWallet.provider.network.name + '.';
if (tag === 'homestead.') { tag = ''; }
var url = 'https://' + tag + 'etherscan.io/tx/' + tx.hash;
addActivity('< Transaction sent: ' + tx.hash.substring(0, 20) + '...', url);
alert('Success!');
inputTargetAddress.value = '';
@@ -449,6 +428,7 @@
refresh();
}, function(error) {
console.log(error);
showError(error);
});
}
@@ -468,15 +448,9 @@
document.getElementById('loading-header').textContent = title;
}
var loadingStatus = document.getElementById('loading-status');
function updateLoading(progress) {
loadingStatus.value = (parseInt(progress * 100)) + '%';
}
function showWallet(wallet) {
var testnet = document.getElementById('option-morden').classList.contains('selected');
activeWallet = wallet;
activeWallet.provider = new Wallet.providers.EtherscanProvider({testnet: testnet});
var network = document.querySelector('.network.option.selected').getAttribute('data-network');
activeWallet = wallet.connect(new ethers.providers.getDefaultProvider(network));
document.getElementById('screen-select').style.display = 'none';
document.getElementById('screen-loading').style.display = 'none';
@@ -492,7 +466,7 @@
}
//var privateKey = '0x3141592653589793238462643383279502884197169399375105820974944592';
//showWallet(new Wallet(privateKey));
//showWallet(new ethers.Wallet(privateKey));
</script>
</body>

View File

@@ -62,14 +62,14 @@ input[type=text] {
border: 1px solid #555;
font-size: 16px;
padding: 10px;
width: 500px;
width: 501px;
}
input[type=password] {
border: 1px solid #555;
font-size: 16px;
padding: 10px;
width: 500px;
width: 501px;
}
input[type=file] {
@@ -81,7 +81,7 @@ input[type=file] {
padding: 10px;
position: relative;
dvisibility: hidden;
width: 500px;
width: 501px;
}
input[type=text].readonly {
@@ -105,15 +105,27 @@ input[type=text].readonly {
box-shadow: 0px 0px 5px #888;
}
.clearfix {
clear: both;
}
.option {
border: 1px solid #999;
box-sizing: border-box;
cursor: pointer;
float: left;
font-size: 16px;
opacity: 0.3;
padding: 10px;
margin-right: 20px;
text-align: center;
transition: opacity 0.1s linear;
width: 200px;
width: 110px;
}
.option span {
font-size: 0.8em;
opacity: 0.5;
}
.option.selected {

20201
dist/ethers-wallet.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

17282
dist/ethers.js vendored Normal file

File diff suppressed because one or more lines are too long

2
dist/ethers.min.js vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/ethers.min.js.map vendored Normal file

File diff suppressed because one or more lines are too long

1059
dist/ethers.types.txt vendored Normal file

File diff suppressed because it is too large Load Diff

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

File diff suppressed because one or more lines are too long

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

File diff suppressed because one or more lines are too long

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

File diff suppressed because one or more lines are too long

1
dist/wordlist-zh.js vendored Normal file

File diff suppressed because one or more lines are too long

20
errors.d.ts vendored Normal file
View File

@@ -0,0 +1,20 @@
export declare const UNKNOWN_ERROR = "UNKNOWN_ERROR";
export declare const NOT_IMPLEMENTED = "NOT_IMPLEMENTED";
export declare const MISSING_NEW = "MISSING_NEW";
export declare const CALL_EXCEPTION = "CALL_EXCEPTION";
export declare const INVALID_ARGUMENT = "INVALID_ARGUMENT";
export declare const MISSING_ARGUMENT = "MISSING_ARGUMENT";
export declare const UNEXPECTED_ARGUMENT = "UNEXPECTED_ARGUMENT";
export declare const NUMERIC_FAULT = "NUMERIC_FAULT";
export declare const INSUFFICIENT_FUNDS = "INSUFFICIENT_FUNDS";
export declare const NONCE_EXPIRED = "NONCE_EXPIRED";
export declare const REPLACEMENT_UNDERPRICED = "REPLACEMENT_UNDERPRICED";
export declare const UNSUPPORTED_OPERATION = "UNSUPPORTED_OPERATION";
export declare function throwError(message: string, code: string, params: any): never;
export declare function checkNew(self: any, kind: any): void;
export declare function checkArgumentCount(count: number, expectedCount: number, suffix?: string): void;
export declare function setCensorship(censorship: boolean, permanent?: boolean): void;
export declare function checkNormalize(): void;
export declare function setLogLevel(logLevel: string): void;
export declare function warn(...args: Array<any>): void;
export declare function info(...args: Array<any>): void;

163
errors.js Normal file
View File

@@ -0,0 +1,163 @@
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
var _version_1 = require("./_version");
// Unknown Error
exports.UNKNOWN_ERROR = 'UNKNOWN_ERROR';
// Not implemented
exports.NOT_IMPLEMENTED = 'NOT_IMPLEMENTED';
// Missing new operator to an object
// - name: The name of the class
exports.MISSING_NEW = 'MISSING_NEW';
// Call exception
// - transaction: the transaction
// - address?: the contract address
// - args?: The arguments passed into the function
// - method?: The Solidity method signature
// - errorSignature?: The EIP848 error signature
// - errorArgs?: The EIP848 error parameters
// - reason: The reason (only for EIP848 "Error(string)")
exports.CALL_EXCEPTION = 'CALL_EXCEPTION';
// Invalid argument (e.g. value is incompatible with type) to a function:
// - argument: The argument name that was invalid
// - value: The value of the argument
exports.INVALID_ARGUMENT = 'INVALID_ARGUMENT';
// Missing argument to a function:
// - count: The number of arguments received
// - expectedCount: The number of arguments expected
exports.MISSING_ARGUMENT = 'MISSING_ARGUMENT';
// Too many arguments
// - count: The number of arguments received
// - expectedCount: The number of arguments expected
exports.UNEXPECTED_ARGUMENT = 'UNEXPECTED_ARGUMENT';
// Numeric Fault
// - operation: the operation being executed
// - fault: the reason this faulted
exports.NUMERIC_FAULT = 'NUMERIC_FAULT';
// Insufficien funds (< value + gasLimit * gasPrice)
// - transaction: the transaction attempted
exports.INSUFFICIENT_FUNDS = 'INSUFFICIENT_FUNDS';
// Nonce has already been used
// - transaction: the transaction attempted
exports.NONCE_EXPIRED = 'NONCE_EXPIRED';
// The replacement fee for the transaction is too low
// - transaction: the transaction attempted
exports.REPLACEMENT_UNDERPRICED = 'REPLACEMENT_UNDERPRICED';
// Unsupported operation
// - operation
exports.UNSUPPORTED_OPERATION = 'UNSUPPORTED_OPERATION';
var _permanentCensorErrors = false;
var _censorErrors = false;
// @TODO: Enum
function throwError(message, code, params) {
if (_censorErrors) {
throw new Error('unknown error');
}
if (!code) {
code = exports.UNKNOWN_ERROR;
}
if (!params) {
params = {};
}
var messageDetails = [];
Object.keys(params).forEach(function (key) {
try {
messageDetails.push(key + '=' + JSON.stringify(params[key]));
}
catch (error) {
messageDetails.push(key + '=' + JSON.stringify(params[key].toString()));
}
});
messageDetails.push("version=" + _version_1.version);
var reason = message;
if (messageDetails.length) {
message += ' (' + messageDetails.join(', ') + ')';
}
// @TODO: Any??
var error = new Error(message);
error.reason = reason;
error.code = code;
Object.keys(params).forEach(function (key) {
error[key] = params[key];
});
throw error;
}
exports.throwError = throwError;
function checkNew(self, kind) {
if (!(self instanceof kind)) {
throwError('missing new', exports.MISSING_NEW, { name: kind.name });
}
}
exports.checkNew = checkNew;
function checkArgumentCount(count, expectedCount, suffix) {
if (!suffix) {
suffix = '';
}
if (count < expectedCount) {
throwError('missing argument' + suffix, exports.MISSING_ARGUMENT, { count: count, expectedCount: expectedCount });
}
if (count > expectedCount) {
throwError('too many arguments' + suffix, exports.UNEXPECTED_ARGUMENT, { count: count, expectedCount: expectedCount });
}
}
exports.checkArgumentCount = checkArgumentCount;
function setCensorship(censorship, permanent) {
if (_permanentCensorErrors) {
throwError('error censorship permanent', exports.UNSUPPORTED_OPERATION, { operation: 'setCensorship' });
}
_censorErrors = !!censorship;
_permanentCensorErrors = !!permanent;
}
exports.setCensorship = setCensorship;
function checkNormalize() {
try {
// Make sure all forms of normalization are supported
["NFD", "NFC", "NFKD", "NFKC"].forEach(function (form) {
try {
"test".normalize(form);
}
catch (error) {
throw new Error('missing ' + form);
}
});
if (String.fromCharCode(0xe9).normalize('NFD') !== String.fromCharCode(0x65, 0x0301)) {
throw new Error('broken implementation');
}
}
catch (error) {
throwError('platform missing String.prototype.normalize', exports.UNSUPPORTED_OPERATION, { operation: 'String.prototype.normalize', form: error.message });
}
}
exports.checkNormalize = checkNormalize;
var LogLevels = { debug: 1, "default": 2, info: 2, warn: 3, error: 4, off: 5 };
var LogLevel = LogLevels["default"];
function setLogLevel(logLevel) {
var level = LogLevels[logLevel];
if (level == null) {
warn("invliad log level - " + logLevel);
return;
}
LogLevel = level;
}
exports.setLogLevel = setLogLevel;
function log(logLevel, args) {
if (LogLevel > LogLevels[logLevel]) {
return;
}
console.log.apply(console, args);
}
function warn() {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
log("warn", args);
}
exports.warn = warn;
function info() {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
log("info", args);
}
exports.info = info;

13
ethers.d.ts vendored Normal file
View File

@@ -0,0 +1,13 @@
import { Contract, ContractFactory, VoidSigner } from './contract';
import { Signer } from './abstract-signer';
import { Wallet } from './wallet';
import * as constants from './constants';
import * as errors from './errors';
import * as providers from './providers';
import * as utils from './utils';
import * as wordlists from './wordlists';
import { platform } from './utils/shims';
import { version } from './_version';
import { ContractFunction, ContractTransaction, Event, EventFilter } from './contract';
declare function getDefaultProvider(network?: utils.Network | string): providers.BaseProvider;
export { Signer, Wallet, VoidSigner, getDefaultProvider, providers, Contract, ContractFactory, constants, errors, utils, wordlists, platform, version, ContractFunction, ContractTransaction, Event, EventFilter };

51
ethers.js Normal file
View File

@@ -0,0 +1,51 @@
'use strict';
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
var contract_1 = require("./contract");
exports.Contract = contract_1.Contract;
exports.ContractFactory = contract_1.ContractFactory;
exports.VoidSigner = contract_1.VoidSigner;
var abstract_signer_1 = require("./abstract-signer");
exports.Signer = abstract_signer_1.Signer;
var wallet_1 = require("./wallet");
exports.Wallet = wallet_1.Wallet;
var constants = __importStar(require("./constants"));
exports.constants = constants;
var errors = __importStar(require("./errors"));
exports.errors = errors;
var providers = __importStar(require("./providers"));
exports.providers = providers;
var utils = __importStar(require("./utils"));
exports.utils = utils;
var wordlists = __importStar(require("./wordlists"));
exports.wordlists = wordlists;
////////////////////////
// Compile-Time Constants
// This is empty in node, and used by browserify to inject extra goodies
var shims_1 = require("./utils/shims");
exports.platform = shims_1.platform;
// This is generated by "npm run dist"
var _version_1 = require("./_version");
exports.version = _version_1.version;
////////////////////////
// Helper Functions
function getDefaultProvider(network) {
if (network == null) {
network = 'homestead';
}
var n = utils.getNetwork(network);
if (!n || !n._defaultProvider) {
errors.throwError('unsupported getDefaultProvider network', errors.UNSUPPORTED_OPERATION, {
operation: 'getDefaultProvider',
network: network
});
}
return n._defaultProvider(providers);
}
exports.getDefaultProvider = getDefaultProvider;

View File

@@ -1,284 +0,0 @@
<html>
<head>
<title>Ethereum Classic Split Tool</title>
<link rel="stylesheet" type="text/css" href="../style.css">
</head>
<body>
<div class="centerer">
<div class="centered">
<h1>Split Ether Classic</h1>
<hr />
<h2>What does this tool do?</h2>
<p>
This tool will take a <i>geth</i> (or crowdsale) JSON wallet, decrypt it and
send all its funds to <a href="http://etherscan.io/address/0x3474627d4f63a678266bc17171d87f8570936622#code">this contract</a>,
which will:
</p>
<ul>
<li>On the ETH branch &mdash; send the funds back to the original address</li>
<li>On the ETC branch &mdash; send the funds to the provided target address (for example, a <a href="https://www.poloniex.com">Poloniex</a> deposit address)</li>
</ul>
<br />
<h3>Disclaimer:</h3>
<p>
I threw this together in couple of hours, mainly to split my own ether
and test my <i>ethers-wallet</i> library (which is still missing features
and is itself not ready for production use). Testing has been fairly minimal
beyond trying it on a few wallets. <b>Use this at your own risk.</b>
</p>
<hr />
<h2>Check Current ETC Balance</h2>
<table>
<tr>
<th>ETC Address:</th>
<td><input type="text" id="checkAddress" /></td>
</tr>
<tr>
<td> </td>
<td>
<div id="submitCheck" class="submit disable">Check Classic Ether Balance</div>
</td>
</tr>
</table>
<hr />
<h2>Split ETC/ETH</h2>
<table>
<tr>
<th>JSON Wallet:</th>
<td><div class="file" id="drop">Drop JSON wallet file here</div><input type="file" id="json" /></td>
</tr>
<tr>
<th>Password:</th>
<td><input type="password" id="password" /></td>
</tr>
<tr>
<th>Target ETC Address:</th>
<td><input type="text" id="targetAddress" /></td>
</tr>
<tr>
<td> </td>
<td>
<div id="submitSplit" class="submit disable">Split Classic Ether</div>
</td>
</tr>
</table>
</div>
</div>
<script type="text/javascript" src="../../dist/ethers-wallet.js"></script>
<script type="text/javascript">
var submitCheck = document.getElementById('submitCheck');
var submitSplit = document.getElementById('submitSplit');
var inputJson = document.getElementById('json');
var inputCheckAddress = document.getElementById('checkAddress')
var inputTargetAddress = document.getElementById('targetAddress')
var inputPassword = document.getElementById('password');
var targetDrop = document.getElementById('drop');
var provider = new Wallet.providers.HttpProvider('https://linode-newark.ethers.ws:8002');
var contractAddress = '0x3474627d4f63a678266bc17171d87f8570936622';
var contractAbi = JSON.parse('[{"constant":false,"inputs":[{"name":"balance","type":"uint256"}],"name":"claimDonations","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"isClassic","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"classicAddress","type":"address"}],"name":"split","outputs":[],"type":"function"},{"inputs":[],"type":"constructor"}]');
submitCheck.onclick = function() {
if (submitCheck.classList.contains('disable')) { return; }
var address = document.getElementById('checkAddress').value;
try {
address = Wallet.getAddress(address);
} catch (error) {
console.log(error);
alert('invalid address');
return;
}
provider.getBalance(address, 'latest').then(function(balance) {
alert('Balance: ' + Wallet.formatEther(balance, {commify: true}) + ' ' + Wallet.etherSymbol);
}, function(error) {
if (error) {
console.log(error);
alert('Faied to get balance');
}
});
console.log('check', address);
}
submitSplit.onclick = function() {
if (submitSplit.classList.contains('disable')) { return; }
function done() {
submitSplit.textContent = 'Split Classic Ether';
inputJson.readOnly = false;
inputPassword.readOnly = false;
inputTargetAddress.readOnly = false;
checkSplit();
}
inputJson.readOnly = true;
inputPassword.readOnly = true;
inputTargetAddress.readOnly = true;
submitSplit.classList.add('disable');
var files = inputJson.files;
if (files.length !== 1) {
alert('No wallet found');
return done();
}
var password = new Wallet.utils.Buffer(inputPassword.value, 'utf8');
var targetAddress = document.getElementById('targetAddress').value;
try {
targetAddress = Wallet.getAddress(targetAddress);
} catch (error) {
console.log(error);
alert('invalid target address');
return done();
}
function processWallet(wallet) {
console.log(wallet);
if (wallet.address === targetAddress) {
alert('Wallet address and target address cannot be the same.');
return done();
}
submitSplit.textContent = 'Decrypted \u2014 Processing (please wait)';
var contract = wallet.getContract(contractAddress, contractAbi);
var data = contract.interface.split(targetAddress).data;
var transaction = {
to: contractAddress,
data: data
};
Promise.all([
provider.getBalance(wallet.address, 'latest'),
provider.getTransactionCount(wallet.address, 'latest'),
provider.getGasPrice(),
// provider.estimateGas(transaction)
]).then(function(results) {
var balance = results[0];
var transactionCount = results[1];
var gasPrice = results[2];
var gasEstimate = new Wallet.utils.BN('29735');
//results[3].add(new Wallet.utils.BN(21000));
transaction.gasPrice = '0x' + gasPrice.toString(16);
transaction.nonce = transactionCount;
transaction.gasLimit = gasEstimate;
var toSend = balance.sub(gasPrice.mul(gasEstimate));
transaction.value = toSend;
console.log(transaction);
var accept = confirm('Balance: ' + Wallet.formatEther(balance, {commify: true}) + ' ' + Wallet.etherSymbol + '. Are you sure you want to split Classic Ether to ' + targetAddress + '?');
if (accept) {
var signedTransaction = wallet.sign(transaction);
function showError(error) {
console.log(error);
done();
alert('Error sending transaction');
}
provider.sendTransaction(signedTransaction).then(function(txid) {
if (txid.match(/^0x0+$/)) {
showError(new Error('txid was zero'));
return;
}
done();
console.log('txid:' + txid);
alert('Success \u2014 ' + txid);
}, function(error) {
showError(error);
});
} else {
done();
}
}, function(error) {
done();
console.log(error);
alert('Error: Faied to fetch info');
});
}
var fileReader = new FileReader();
fileReader.onload = function(e) {
var json = e.target.result;
if (Wallet.isCrowdsaleWallet(json)) {
var wallet = Wallet.decryptCrowdsale(json, password);
processWallet(wallet);
} else if (Wallet.isValidWallet(json)) {
Wallet.decrypt(json, password, function(error, wallet, progress) {
if (error) {
done();
console.log(error);
if (error.message === 'invalid password') {
alert('Wrong Password');
} else {
alert('Error Decrypting Wallet');
}
} else if (wallet) {
processWallet(wallet);
} else {
submitSplit.textContent = 'Decrypting \u2014 ' + (parseInt(progress * 100) + '%');
}
});
} else {
alert('unknown walet format');
done();
}
}
fileReader.readAsText(files[0]);
}
inputCheckAddress.oninput = function() {
try {
Wallet.getAddress(inputCheckAddress.value);
submitCheck.classList.remove('disable');
} catch (error) {
submitCheck.classList.add('disable');
}
}
function checkSplit() {
if (!inputJson.files || inputJson.files.length !== 1) {
submitSplit.classList.add('disable');
return;
}
targetDrop.textContent = inputJson.files[0].name;
try {
Wallet.getAddress(inputTargetAddress.value);
} catch (error) {
submitSplit.classList.add('disable');
return;
}
submitSplit.classList.remove('disable');
}
inputTargetAddress.oninput = checkSplit;
inputJson.onchange = checkSplit;
inputJson.addEventListener('dragover', function(event) {
event.preventDefault();
event.stopPropagation();
targetDrop.classList.add('highlight');
}, true);
inputJson.addEventListener('drop', function(event) {
targetDrop.classList.remove('highlight');
}, true);
var check
</script>
</body>
</html>

356
gulpfile.js Normal file
View 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
View File

@@ -0,0 +1,3 @@
import * as ethers from './ethers';
export { ethers };
export * from './ethers';

111
index.js
View File

@@ -1,96 +1,15 @@
'use strict';
var scrypt = require('scrypt-js');
var Contract = require('./lib/contract.js');
var providers = require('./lib/providers.js');
var secretStorage = require('./lib/secret-storage.js');
var Randomish = require('./lib/randomish.js');
var SigningKey = require('./lib/signing-key.js');
var Wallet = require('./lib/wallet.js');
var units = require('./lib/units.js');
var utils = require('./lib/utils.js');
var BN = utils.BN;
var exportUtils = {};
utils.defineProperty(Wallet, 'utils', exportUtils);
utils.defineProperty(exportUtils, 'BN', BN);
utils.defineProperty(exportUtils, 'Buffer', Buffer);
utils.defineProperty(exportUtils, 'sha3', utils.sha3);
utils.defineProperty(exportUtils, 'sha256', utils.sha256);
utils.defineProperty(exportUtils, 'getContractAddress', utils.getContractAddress);
module.exports = Wallet;
utils.defineProperty(Wallet, 'etherSymbol', '\uD835\uDF63');
utils.defineProperty(Wallet, 'formatEther', units.formatEther);
utils.defineProperty(Wallet, 'parseEther', units.parseEther);
utils.defineProperty(Wallet, 'getAddress', utils.getAddress);
utils.defineProperty(Wallet, 'getIcapAddress', utils.getIcapAddress);
utils.defineProperty(Wallet, 'isCrowdsaleWallet', secretStorage.isCrowdsaleWallet);
utils.defineProperty(Wallet, 'isValidWallet', secretStorage.isValidWallet);
utils.defineProperty(Wallet, 'decryptCrowdsale', function(json, password) {
return new Wallet(secretStorage.decryptCrowdsale(json, password));
});
utils.defineProperty(Wallet, 'decrypt', function(json, password, progressCallback) {
if (progressCallback && typeof(progressCallback) !== 'function') {
throw new Error('invalid callback');
}
return new Promise(function(resolve, reject) {
secretStorage.decrypt(json, password, progressCallback).then(function(signingKey) {
resolve(new Wallet(signingKey));
}, function(error) {
reject(error);
});
});
});
utils.defineProperty(Wallet.prototype, 'encrypt', function(password, options, progressCallback) {
if (typeof(options) === 'function' && !progressCallback) {
progressCallback = options;
options = {};
}
if (progressCallback && typeof(progressCallback) !== 'function') {
throw new Error('invalid callback');
}
return secretStorage.encrypt(this.privateKey, password, options, progressCallback);
});
utils.defineProperty(Wallet, 'summonBrainWallet', function(username, password, progressCallback) {
if (progressCallback && typeof(progressCallback) !== 'function') {
throw new Error('invalid callback');
}
return new Promise(function(resolve, reject) {
scrypt(password, username, (1 << 18), 8, 1, 32, function(error, progress, key) {
if (error) {
reject(error);
} else if (key) {
resolve(new Wallet(new Buffer(key)));
} else if (progressCallback) {
progressCallback(progress);
}
});
});
});
utils.defineProperty(Wallet, 'providers', providers);
utils.defineProperty(Wallet, 'randomish', new Randomish());
module.exports = Wallet;
"use strict";
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
var ethers = __importStar(require("./ethers"));
exports.ethers = ethers;
__export(require("./ethers"));

View File

@@ -1,37 +0,0 @@
'use strict';
var utils = require('./utils.js');
var crypto = global.crypto || global.msCrypto;
if (!crypto || !crypto.getRandomValues) {
console.log('WARNING: Missing strong random number source; using weak randomBytes');
crypto = {
getRandomValues: function(length) {
for (var i = 0; i < buffer.length; i++) {
buffer[i] = parseInt(256 * Math.random());
}
return buffer;
},
_weakCrypto: true
};
} else {
console.log('Found strong random number source');
}
function randomBytes(length) {
if (length <= 0 || length > 1024 || parseInt(length) != length) {
throw new Error('invalid length');
}
var buffer = new Buffer(length);
crypto.getRandomValues(buffer);
return buffer;
};
if (crypto._weakCrypto === true) {
utils.defineProperty(randomBytes, '_weakCrypto', true);
}
module.exports = randomBytes;

View File

@@ -1,658 +0,0 @@
'use strict';
var utils = require('./utils.js');
// Creates property that is immutable
function defineFrozen(object, name, value) {
var frozen = JSON.stringify(value);
Object.defineProperty(object, name, {
enumerable: true,
get: function() { return JSON.parse(frozen); }
});
}
// getKeys([{a: 1, b: 2}, {a: 3, b: 4}], 'a') => [1, 3]
function getKeys(params, key) {
if (!Array.isArray(params)) { throw new Error('invalid params'); }
var result = [];
for (var i = 0; i < params.length; i++) {
if (typeof(params[i][key]) !== 'string') { throw new Error('invalid abi'); }
result.push(params[i][key]);
}
return result;
}
// Convert the value from a Number to a BN (if necessary)
function numberOrBN(value) {
if (!value.eq) {
if (typeof(value) !== 'number') {
throw new Error('invalid number');
}
value = new utils.BN(value);
}
return value;
}
function zpad(buffer, length) {
var zero = new Buffer([0]);
while (buffer.length < length) {
buffer = Buffer.concat([zero, buffer]);
}
return buffer;
}
// There seems to be a but in maskn, so we are doing this for now.
var bitmasks = [];
(function() {
var mask = '';
for (var i = 0; i < 33; i++) {
bitmasks.push(new utils.BN(mask, 16));
mask += 'ff';
}
})();
function coderNumber(size, signed) {
return {
encode: function(value) {
value = numberOrBN(value)
value = value.toTwos(size * 8).and(bitmasks[size]);
if (signed) {
value = value.fromTwos(size * 8).toTwos(256);
}
return value.toArrayLike(Buffer, 'be', 32);
},
decode: function(data, offset) {
var junkLength = 32 - size;
var value = new utils.BN(data.slice(offset + junkLength, offset + 32));
if (signed) {
value = value.fromTwos(size * 8);
} else {
value = value.and(bitmasks[size]);
}
return {
consumed: 32,
value: value,
}
}
};
}
var uint256Coder = coderNumber(32, false);
var coderBoolean = {
encode: function(value) {
return uint256Coder.encode(value ? 1: 0);
},
decode: function(data, offset) {
var result = uint256Coder.decode(data, offset);
return {
consumed: result.consumed,
value: !result.value.isZero()
}
}
}
function coderFixedBytes(length) {
return {
encode: function(value) {
value = utils.hexOrBuffer(value);
if (length === 32) { return value; }
var result = new Buffer(32);
result.fill(0);
value.copy(result);
return result;
},
decode: function(data, offset) {
if (data.length < offset + 32) { throw new Error('invalid bytes' + length); }
return {
consumed: 32,
value: '0x' + data.slice(offset, offset + length).toString('hex')
}
}
};
}
var coderAddress = {
encode: function(value) {
if (!utils.isHexString(value, 20)) { throw new Error('invalid address'); }
value = utils.hexOrBuffer(value);
var result = new Buffer(32);
result.fill(0);
value.copy(result, 12);
return result;
},
decode: function(data, offset) {
if (data.length < offset + 32) { throw new Error('invalid address'); }
return {
consumed: 32,
value: '0x' + data.slice(offset + 12, offset + 32).toString('hex')
}
}
}
function _encodeDynamicBytes(value) {
var dataLength = parseInt(32 * Math.ceil(value.length / 32));
var padding = new Buffer(dataLength - value.length);
padding.fill(0);
return Buffer.concat([
uint256Coder.encode(value.length),
value,
padding
]);
}
function _decodeDynamicBytes(data, offset) {
if (data.length < offset + 32) { throw new Error('invalid bytes'); }
var length = uint256Coder.decode(data, offset).value;
length = length.toNumber();
if (data.length < offset + 32 + length) { throw new Error('invalid bytes'); }
return {
consumed: parseInt(32 + 32 * Math.ceil(length / 32)),
value: data.slice(offset + 32, offset + 32 + length),
}
}
var coderDynamicBytes = {
encode: function(value) {
return _encodeDynamicBytes(utils.hexOrBuffer(value));
},
decode: function(data, offset) {
var result = _decodeDynamicBytes(data, offset);
result.value = '0x' + result.value.toString('hex');
return result;
},
dynamic: true
};
var coderString = {
encode: function(value) {
return _encodeDynamicBytes(new Buffer(value, 'utf8'));
},
decode: function(data, offset) {
var result = _decodeDynamicBytes(data, offset);
result.value = result.value.toString('utf8');
return result;
},
dynamic: true
};
function coderArray(coder, length) {
return {
encode: function(value) {
if (!Array.isArray(value)) { throw new Error('invalid array'); }
var result = new Buffer(0);
if (length === -1) {
length = value.length;
result = uint256Coder.encode(length);
}
if (length !== value.length) { throw new Error('size mismatch'); }
value.forEach(function(value) {
result = Buffer.concat([
result,
coder.encode(value)
]);
});
return result;
},
decode: function(data, offset) {
// @TODO:
//if (data.length < offset + length * 32) { throw new Error('invalid array'); }
var consumed = 0;
var result;
if (length === -1) {
result = uint256Coder.decode(data, offset);
length = result.value.toNumber();
consumed += result.consumed;
offset += result.consumed;
}
var value = [];
for (var i = 0; i < length; i++) {
var result = coder.decode(data, offset);
consumed += result.consumed;
offset += result.consumed;
value.push(result.value);
}
return {
consumed: consumed,
value: value,
}
},
dynamic: (length === -1)
}
}
// Break the type up into [staticType][staticArray]*[dynamicArray]? | [dynamicType] and
// build the coder up from its parts
var paramTypePart = new RegExp(/^((u?int|bytes)([0-9]*)|(address|bool|string)|(\[([0-9]*)\]))/);
function getParamCoder(type) {
var coder = null;
while (type) {
var part = type.match(paramTypePart);
if (!part) { throw new Error('invalid type: ' + type); }
type = type.substring(part[0].length);
var prefix = (part[2] || part[4] || part[5]);
switch (prefix) {
case 'int': case 'uint':
if (coder) { throw new Error('invalid type ' + type); }
var size = parseInt(part[3] || 256);
if (size === 0 || size > 256 || (size % 8) !== 0) {
throw new Error('invalid type ' + type);
}
coder = coderNumber(size / 8, (prefix === 'int'));
break;
case 'bool':
if (coder) { throw new Error('invalid type ' + type); }
coder = coderBoolean;
break;
case 'string':
if (coder) { throw new Error('invalid type ' + type); }
coder = coderString;
break;
case 'bytes':
if (coder) { throw new Error('invalid type ' + type); }
if (part[3]) {
var size = parseInt(part[3]);
if (size === 0 || size > 32) {
throw new Error('invalid type ' + type);
}
coder = coderFixedBytes(size);
} else {
coder = coderDynamicBytes;
}
break;
case 'address':
if (coder) { throw new Error('invalid type ' + type); }
coder = coderAddress;
break;
case '[]':
if (!coder || coder.dynamic) { throw new Error('invalid type ' + type); }
coder = coderArray(coder, -1);
break;
// "[0-9+]"
default:
if (!coder || coder.dynamic) { throw new Error('invalid type ' + type); }
var size = parseInt(part[6]);
coder = coderArray(coder, size);
}
}
if (!coder) { throw new Error('invalid type'); }
return coder;
}
function Interface(abi) {
if (!(this instanceof Interface)) { throw new Error('missing new'); }
//defineProperty(this, 'address', address);
// Wrap this up as JSON so we can return a "copy" and avoid mutation
defineFrozen(this, 'abi', abi);
var methods = [], events = [];
abi.forEach(function(method) {
var func = null;
switch (method.type) {
case 'function':
methods.push(method.name);
func = (function() {
var inputTypes = getKeys(method.inputs, 'type');
var outputTypes = getKeys(method.outputs, 'type');
var func = function() {
var signature = method.name + '(' + getKeys(method.inputs, 'type').join(',') + ')';
var result = {
name: method.name,
signature: signature,
};
var params = Array.prototype.slice.call(arguments, 0);
if (params.length < inputTypes.length) {
throw new Error('missing parameter');
} else if (params.length > inputTypes.length) {
throw new Error('too many parameters');
}
signature = '0x' + utils.sha3(signature).slice(0, 4).toString('hex');
result.data = signature + Interface.encodeParams(inputTypes, params).substring(2);
if (method.constant) {
result.type = 'call';
result.parse = function(data) {
return Interface.decodeParams(
outputTypes,
utils.hexOrBuffer(data)
);
};
} else {
result.type = 'transaction';
}
return result;
}
defineFrozen(func, 'inputs', getKeys(method.inputs, 'name'));
defineFrozen(func, 'outputs', getKeys(method.outputs, 'name'));
return func;
})();
break;
case 'event':
events.push(method.name);
func = (function() {
var inputTypes = getKeys(method.inputs, 'type');
var func = function() {
var signature = method.name + '(' + getKeys(method.inputs, 'type').join(',') + ')';
var result = {
inputs: method.inputs,
name: method.name,
type: 'filter',
signature: signature,
topics: ['0x' + utils.sha3(signature).toString('hex')],
};
result.parse = function(data) {
return Interface.decodeParams(
inputTypes,
utils.hexOrBuffer(data)
);
};
return result;
}
defineFrozen(func, 'inputs', getKeys(method.inputs, 'name'));
return func;
})();
break;
default:
func = (function() {
return function() {
return {type: 'unknown'}
}
})();
break;
}
utils.defineProperty(this, method.name, func);
}, this);
defineFrozen(this, 'methods', methods);
defineFrozen(this, 'events', events);
}
utils.defineProperty(Interface, 'encodeParams', function(types, values) {
if (types.length !== values.length) { throw new Error('types/values mismatch'); }
var parts = [];
types.forEach(function(type, index) {
var coder = getParamCoder(type);
parts.push({dynamic: coder.dynamic, value: coder.encode(values[index])});
})
function alignSize(size) {
return parseInt(32 * Math.ceil(size / 32));
}
var staticSize = 0, dynamicSize = 0;
parts.forEach(function(part) {
if (part.dynamic) {
staticSize += 32;
dynamicSize += alignSize(part.value.length);
} else {
staticSize += alignSize(part.value.length);
}
});
var offset = 0, dynamicOffset = staticSize;
var data = new Buffer(staticSize + dynamicSize);
parts.forEach(function(part, index) {
if (part.dynamic) {
uint256Coder.encode(dynamicOffset).copy(data, offset);
offset += 32;
part.value.copy(data, dynamicOffset);
dynamicOffset += alignSize(part.value.length);
} else {
part.value.copy(data, offset);
offset += alignSize(part.value.length);
}
});
return '0x' + data.toString('hex');
});
utils.defineProperty(Interface, 'decodeParams', function(types, data) {
data = utils.hexOrBuffer(data);
var values = [];
var offset = 0;
types.forEach(function(type) {
var coder = getParamCoder(type);
if (coder.dynamic) {
var dynamicOffset = uint256Coder.decode(data, offset);
var result = coder.decode(data, dynamicOffset.value.toNumber());
offset += dynamicOffset.consumed;
} else {
var result = coder.decode(data, offset);
offset += result.consumed;
}
values.push(result.value);
});
return values;
});
var allowedTransactionKeys = {
data: true, from: true, gasLimit: true, gasPrice:true, to: true, value: true
}
function Contract(wallet, contractAddress, contractInterface) {
utils.defineProperty(this, 'wallet', wallet);
utils.defineProperty(this, 'contractAddress', contractAddress);
utils.defineProperty(this, 'interface', contractInterface);
var self = this;
var filters = {};
function setupFilter(call, callback) {
var info = filters[call.name];
// Stop and remove the filter
if (!callback) {
if (info) { info.filter.stopWatching(); }
delete filters[call.name];
return;
}
if (typeof(callback) !== 'function') {
throw new Error('invalid callback');
}
// Already have a filter, just update the callback
if (info) {
info.callback = callback;
return;
}
info = {callback: callback};
filters[call.name] = info;
// Start a new filter
/*
info.filter = web3.eth.filter({
address: contractAddress,
topics: call.topics
}, function(error, result) {
// @TODO: Emit errors to .onerror? Maybe?
if (error) {
console.log(error);
return;
}
try {
info.callback.apply(self, call.parse(result.data));
} catch(error) {
console.log(error);
}
});
*/
}
function runMethod(method, estimateOnly) {
return function() {
var provider = wallet._provider;
var transaction = {}
var params = Array.prototype.slice.call(arguments);
if (params.length == contractInterface[method].inputs.length + 1) {
transaction = params.pop();
if (typeof(transaction) !== 'object') {
throw new Error('invalid transaction overrides');
}
for (var key in transaction) {
if (!allowedTransactionKeys[key]) {
throw new Error('unknown transaction override ' + key);
}
}
}
var call = contractInterface[method].apply(contractInterface, params);
switch (call.type) {
case 'call':
['data', 'gasLimit', 'gasPrice', 'to', 'value'].forEach(function(key) {
if (transaction[key] != null) {
throw new Error('call cannot override ' + key) ;
}
});
transaction.data = call.data;
if (transaction.from == null) {
transaction.from = wallet.address;
}
transaction.to = contractAddress;
if (estimateOnly) {
return new Promise(function(resolve, reject) {
resolve(new utils.BN(0));
});
}
return new Promise(function(resolve, reject) {
provider.call(transaction).then(function(value) {
resolve(call.parse(value));
}, function(error) {
reject(error);
});
});
case 'transaction':
['data', 'from', 'to'].forEach(function(key) {
if (transaction[key] != null) {
throw new Error('transaction cannot override ' + key) ;
}
});
transaction.data = call.data;
transaction.to = contractAddress;
if (transaction.gasLimit == null) {
transaction.gasLimit = 3000000;
}
if (estimateOnly) {
return new Promise(function(resolve, reject) {
provider.estimateGas(transaction).then(function(gasEstimate) {
resolve(gasEstimate);
}, function(error) {
reject(error);
});
});
}
return new Promise(function(resolve, reject) {
Promise.all([
provider.getTransactionCount(wallet.address, 'pending'),
provider.getGasPrice(),
]).then(function(results) {
if (transaction.nonce == null) {
transaction.nonce = results[0];
} else if (console.warn) {
console.warn('Overriding suggested nonce: ' + results[0]);
}
if (transaction.gasPrice == null) {
transaction.gasPrice = results[1];
} else if (console.warn) {
console.warn('Overriding suggested gasPrice: ' + utils.hexlify(results[1]));
}
var signedTransaction = wallet.sign(transaction);
provider.sendTransaction(signedTransaction).then(function(txid) {
resolve(txid);
}, function(error) {
reject(error);
});
}, function(error) {
reject(error);
});
});
}
};
}
var estimate = {};
utils.defineProperty(this, 'estimate', estimate);
contractInterface.methods.forEach(function(method) {
utils.defineProperty(this, method, runMethod(method, false));
utils.defineProperty(estimate, method, runMethod(method, true));
}, this);
contractInterface.events.forEach(function(method) {
var call = contractInterface[method].apply(contractInterface, []);
Object.defineProperty(self, 'on' + call.name.toLowerCase(), {
enumerable: true,
get: function() {
//console.log('get');
var info = filters[call.name];
if (!info || !info[call.name]) { return null; }
return info.callback;
},
set: function(value) {
//console.log('set');
setupFilter(call, value);
}
});
}, this);
}
utils.defineProperty(Contract, 'Interface', Interface);
module.exports = Contract;

View File

@@ -1,331 +0,0 @@
'use strict';
var inherits = require('inherits');
var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest;
var utils = require('./utils.js');
// The required methods a provider must support
var methods = [
'getBalance',
'getTransactionCount',
'getGasPrice',
'sendTransaction',
'call',
'estimateGas'
];
// Manages JSON-RPC to an Ethereum node
function Web3Connector(provider) {
if (!(this instanceof Web3Connector)) { throw new Error('missing new'); }
var nextMessageId = 1;
utils.defineProperty(this, 'sendMessage', function(method, params) {
return new Promise(function(resolve, reject) {
provider.sendAsync({
id: (nextMessageId++),
jsonrpc: '2.0',
method: method,
params: params
}, function(error, result) {
if (error) {
reject(error);
} else {
if (result.error) {
var error = new Error(result.error.message);
error.code = result.error.code;
error.data = result.error.data;
reject(error);
} else {
resolve(result.result);
}
}
});
});
});
}
// Mimics Web3 interface
function rpcSendAsync(url) {
return {
sendAsync: function(payload, callback) {
var request = new XMLHttpRequest();
request.open('POST', url, true);
request.setRequestHeader('Content-Type','application/json');
request.onreadystatechange = function() {
if (request.readyState !== 4) { return; }
if (typeof(callback) !== 'function') { return; }
var result = request.responseText;
try {
callback(null, JSON.parse(result));
} catch (error) {
var responseError = new Error('invalid response');
responseError.orginialError = error;
responseError.data = result;
callback(responseError);
}
};
try {
request.send(JSON.stringify(payload));
} catch (error) {
var connectionError = new Error('connection error');
connectionError.error = error;
callback(connectionError);
}
}
}
}
function SendAsyncProvider(sendAsync) {
if (!(this instanceof SendAsyncProvider)) { throw new Error('missing new'); }
utils.defineProperty(this, 'client', new Web3Connector(sendAsync));
}
function validBlock(value) {
if (value == null) { return 'latest'; }
if (value === 'latest' || value === 'pending') { return value; }
if (typeof(value) === 'number' && value == parseInt(value)) {
return parseInt(value);
}
throw new Error('invalid blockNumber');
}
function postProcess(client, method, params, makeBN) {
return new Promise(function(resolve, reject) {
client.sendMessage(method, params).then(function (result) {
if (!utils.isHexString(result)) {
reject(new Error('invalid server response'));
} else {
result = result.substring(2);
if (makeBN) {
result = new utils.BN(result, 16);
} else {
result = parseInt(result, 16);
}
resolve(result);
}
}, function(error) {
reject(error);
});
});
}
utils.defineProperty(SendAsyncProvider.prototype, 'getBalance', function(address, blockNumber) {
return postProcess(this.client, 'eth_getBalance', [
utils.getAddress(address),
validBlock(blockNumber)
], true);
});
utils.defineProperty(SendAsyncProvider.prototype, 'getTransactionCount', function(address, blockNumber) {
return postProcess(this.client, 'eth_getTransactionCount', [
utils.getAddress(address),
validBlock(blockNumber)
], false);
});
utils.defineProperty(SendAsyncProvider.prototype, 'getGasPrice', function() {
return postProcess(this.client, 'eth_gasPrice', [], true);
});
utils.defineProperty(SendAsyncProvider.prototype, 'sendTransaction', function(signedTransaction) {
if (!utils.isHexString(signedTransaction)) { throw new Error('invalid transaction'); }
return this.client.sendMessage('eth_sendRawTransaction', [signedTransaction]);
});
utils.defineProperty(SendAsyncProvider.prototype, 'call', function(transaction) {
// @TODO: check validTransaction?
return this.client.sendMessage('eth_call', [transaction]);
});
utils.defineProperty(SendAsyncProvider.prototype, 'estimateGas', function(transaction) {
// @TODO: check validTransaction?
return postProcess(this.client, 'eth_estimateGas', [transaction], true);
});
var providers = {};
function HttpProvider(url) {
if (!(this instanceof HttpProvider)) { throw new Error('missing new'); }
SendAsyncProvider.call(this, rpcSendAsync(url));
}
inherits(HttpProvider, SendAsyncProvider);
utils.defineProperty(providers, 'HttpProvider', HttpProvider);
function Web3Provider(provider) {
if (!(this instanceof Web3Provider)) { throw new Error('missing new'); }
if (provider.currentProvider) { provider = provider.currentProvider; }
if (!provider.sendAsync) { throw new Error('invalid provider'); }
SendAsyncProvider.call(this, provider);
}
inherits(Web3Provider, SendAsyncProvider);
utils.defineProperty(providers, 'Web3Provider', Web3Provider);
function base10ToBN(value) {
return new utils.BN(value);
}
function hexToBN(value) {
return new utils.BN(ensureHex(value).substring(2), 16);
}
function hexToNumber(value) {
if (!utils.isHexString(value)) { throw new Error('invalid hex string'); }
return parseInt(value.substring(2), 16);
}
function ensureHex(value) {
if (!utils.isHexString(value)) { throw new Error('invalid hex string'); }
return value;
}
function ensureTxid(value) {
if (!utils.isHexString(value, 32)) { throw new Error('invalid hex string'); }
return value;
}
function getGasPrice(value) {
if (!value || !value.transactions || value.transactions.length === 0) {
throw new Error('invalid response');
}
return hexToBN(value.transactions[0].gasPrice);
}
function EtherscanProvider(options) {
if (!(this instanceof EtherscanProvider)) { throw new Error('missing new'); }
if (!options) { options = {}; }
var testnet = options.testnet;
var apiKey = options.apiKey;
utils.defineProperty(this, 'testnet', testnet);
utils.defineProperty(this, 'apiKey', apiKey);
utils.defineProperty(this, '_send', function(query, check) {
var url = (testnet ? 'https://testnet.etherscan.io/api?': 'https://api.etherscan.io/api?');
url += query;
if (apiKey) { url += 'apikey=' + apiKey; }
//console.log('URL', url);
return new Promise(function(resolve, reject) {
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.onreadystatechange = function() {
if (request.readyState !== 4) { return; }
var result = request.responseText;
//console.log(result);
try {
result = JSON.parse(result);
if (result.message) {
if (result.message === 'OK') {
resolve(check(result.result));
} else {
reject(new Error('invalid response'));
}
} else {
if (result.error) {
console.log(result.error);
reject(new Error('invalid response'));
} else {
resolve(check(result.result));
}
}
} catch (error) {
console.log(error);
reject(new Error('invalid response'));
}
}
try {
request.send();
} catch (error) {
var connectionError = new Error('connection error');
connectionError.error = error;
reject(connectionError);
}
});
});
}
utils.defineProperty(providers, 'EtherscanProvider', EtherscanProvider);
utils.defineProperty(EtherscanProvider.prototype, 'getBalance', function(address, blockNumber) {
address = utils.getAddress(address);
blockNumber = validBlock(blockNumber);
var query = ('module=account&action=balance&address=' + address + '&tag=' + blockNumber);
return this._send(query, base10ToBN);
});
utils.defineProperty(EtherscanProvider.prototype, 'getTransactionCount', function(address, blockNumber) {
address = utils.getAddress(address);
blockNumber = validBlock(blockNumber);
var query = ('module=proxy&action=eth_getTransactionCount&address=' + address + '&tag=' + blockNumber);
return this._send(query, hexToNumber);
});
utils.defineProperty(EtherscanProvider.prototype, 'getGasPrice', function() {
var query = ('module=proxy&action=eth_gasPrice');
return this._send(query, hexToBN);
});
utils.defineProperty(EtherscanProvider.prototype, 'sendTransaction', function(signedTransaction) {
if (!utils.isHexString(signedTransaction)) { throw new Error('invalid transaction'); }
var query = ('module=proxy&action=eth_sendRawTransaction&hex=' + signedTransaction);
return this._send(query, ensureTxid);
});
utils.defineProperty(EtherscanProvider.prototype, 'call', function(transaction) {
var address = utils.getAddress(transaction.to);
var data = transaction.data;
if (!utils.isHexString(data)) { throw new Error('invalid data'); }
var query = ('module=proxy&action=eth_call&to=' + address + '&data=' + data);
return this._send(query, ensureHex);
});
utils.defineProperty(EtherscanProvider.prototype, 'estimateGas', function(transaction) {
var address = utils.getAddress(transaction.to);
var query = 'module=proxy&action=eth_estimateGas&to=' + address;
if (transaction.gasPrice) {
query += '&gasPrice=' + utils.hexlify(transaction.gasPrice);
}
if (transaction.gasLimit) {
query += '&gas=' + utils.hexlify(transaction.gasLimit);
}
if (transaction.from) {
query += '&from=' + utils.getAddress(transaction.from);
}
if (transaction.data) {
query += '&data=' + ensureHex(transaction.data);
}
if (transaction.value) {
query += '&value=' + utils.hexlify(transaction.value);
}
return this._send(query, hexToBN);
});
utils.defineProperty(providers, 'isProvider', function(provider) {
if (!provider) { return false; }
for (var i = 0; i < methods; i++) {
if (typeof(provider[methods[i]]) !== 'function') {
return false;
}
}
return true;
});
module.exports = providers;

View File

@@ -1,4 +0,0 @@
'use strict';
module.exports = require('crypto').randomBytes;

View File

@@ -1,74 +0,0 @@
'use strict';
var aes = require('aes-js');
var randomBytes = require('./random-bytes.js');
var utils = require('./utils.js');
function Randomish() {
if (!(this instanceof Randomish)) { throw new Error('missing new'); }
var weak = (randomBytes._weakCrypto || false);
var entropyBits = (weak ? 0: ((32 + 16) * 8));
Object.defineProperty(this, 'entropy', {
enumerable: true,
get: function() { return entropyBits; }
});
var entropy = new aes.ModeOfOperation.cbc(
Randomish.randomishBytes(32),
Randomish.randomishBytes(16)
);
utils.defineProperty(this, 'feedEntropy', function(data, expectedEntropyBits) {
if (!data) { data = ''; }
if (!expectedEntropyBits) { expectedEntropyBits = 0; }
if (parseInt(expectedEntropyBits) != expectedEntropyBits) {
throw new Error('invalid expectedEntropyBits');
}
data = (new Date()).getTime() + '-' + JSON.stringify(data) + '-' + data.toString();
var hashed = utils.sha3(new Buffer(data, 'utf8'));
entropyBits += expectedEntropyBits + (weak ? 0: ((32) * 8));
// Feed the hashed data and random data to the mode of operation
entropy.encrypt(hashed.slice(0, 16));
entropy.encrypt(randomBytes(16));
entropy.encrypt(hashed.slice(0, 16));
return new Buffer(entropy.encrypt(randomBytes(16)));
});
utils.defineProperty(this, 'randomBytes', function(length, key) {
if (parseInt(length) != length || length <= 0 || length > 1024) {
throw new Error('invalid length');
}
// If we don't have a key, create one
if (!key) {
key = Buffer.concat([this.feedEntropy(), this.feedEntropy()]);
}
if (!Buffer.isBuffer(key) || key.length !== 32) {
throw new Error('invalid key');
}
var aesCbc = new aes.ModeOfOperation.cbc(key, this.feedEntropy());
var result = new Buffer(0);
while (result.length < length) {
result = Buffer.concat([result, this.feedEntropy()]);
}
return result.slice(0, length);
});
this.feedEntropy();
}
utils.defineProperty(Randomish, 'randomishBytes', function(length) {
return randomBytes(length);
});
module.exports = Randomish

View File

@@ -1,345 +0,0 @@
'use strict';
var aes = require('aes-js');
var pbkdf2 = require('pbkdf2');
var scrypt = require('scrypt-js');
var uuid = require('uuid');
var Randomish = require('./randomish.js');
var SigningKey = require('./signing-key.js');
var utils = require('./utils.js')
// Search an Object and its children recursively, caselessly.
function searchPath(object, path) {
var currentChild = object;
var comps = path.toLowerCase().split('/');
for (var i = 0; i < comps.length; i++) {
// Search for a child object with a case-insensitive matching key
var matchingChild = null;
for (var key in currentChild) {
if (key.toLowerCase() === comps[i]) {
matchingChild = currentChild[key];
break;
}
}
// Didn't find one. :'(
if (matchingChild === null) {
return null;
}
// Now check this child...
currentChild = matchingChild;
}
return currentChild;
}
/*
function SecretStorage(json, signingKey) {
if (!(this instanceof SecretStorage)) { throw new Error('missing new'); }
utils.defineProperty(this, 'json', json);
Object.defineProperty(this, 'data', {
enumerable: true,
get: function() { return JSON.parse(json); }
});
utils.defineProperty(this, 'address', signingKey.privateKey);
utils.defineProperty(this, 'signingKey', signingKey);
}
*/
var secretStorage = {};
utils.defineProperty(secretStorage, 'isCrowdsaleWallet', function(json) {
try {
var data = JSON.parse(json);
} catch (error) { return false; }
return (data.encseed && data.ethaddr);
});
utils.defineProperty(secretStorage, 'isValidWallet', function(json) {
try {
var data = JSON.parse(json);
} catch (error) { return false; }
if (!data.version || parseInt(data.version) !== data.version || parseInt(data.version) !== 3) {
return false;
}
// @TODO: Put more checks to make sure it has kdf, iv and all that good stuff
return true;
});
// See: https://github.com/ethereum/pyethsaletool
utils.defineProperty(secretStorage, 'decryptCrowdsale', function(json, password) {
var data = JSON.parse(json);
// Ethereum Address
var ethaddr = utils.getAddress(searchPath(data, 'ethaddr'));
// Encrypted Seed
var encseed = new Buffer(searchPath(data, 'encseed'), 'hex');
if (!encseed || (encseed.length % 16) !== 0) {
throw new Error('invalid encseed');
}
var key = pbkdf2.pbkdf2Sync(password, password, 2000, 32, 'sha256').slice(0, 16);
var iv = encseed.slice(0, 16);
var encryptedSeed = encseed.slice(16);
// Decrypt the seed
var seed = new Buffer(0);
var aesCbc = new aes.ModeOfOperation.cbc(key, iv);
for (var i = 0; i < encryptedSeed.length; i += 16) {
seed = Buffer.concat([seed, new Buffer(aesCbc.decrypt(encryptedSeed.slice(i, i + 16)))]);
}
// Check PKCS#7 padding is valid
var pad = seed[seed.length - 1];
if (pad > 16 || pad > seed.length) {
throw new Error('invalid password');
}
for (var i = seed.length - pad; i < seed.length; i++) {
if (seed[i] !== pad) {
throw new Error('invalid password');
}
}
// Strip the padding
seed = seed.slice(0, seed.length - pad);
// This wallet format is weird... Convert the binary encoded hex to a string.
var seedHex = '';
for (var i = 0; i < seed.length; i++) {
seedHex += String.fromCharCode(seed[i]);
}
var signingKey = new SigningKey(utils.sha3(new Buffer(seedHex)));
if (signingKey.address !== ethaddr) {
throw new Error('corrupt crowdsale wallet');
}
return signingKey;
});
utils.defineProperty(secretStorage, 'decrypt', function(json, password, progressCallback) {
if (!Buffer.isBuffer(password)) { throw new Error('password must be a buffer'); }
var data = JSON.parse(json);
var decrypt = function(key, ciphertext) {
var cipher = searchPath(data, 'crypto/cipher');
if (cipher === 'aes-128-ctr') {
var iv = new Buffer(searchPath(data, 'crypto/cipherparams/iv'), 'hex')
var counter = new aes.Counter(iv);
var aesCtr = new aes.ModeOfOperation.ctr(key, counter);
return new Buffer(aesCtr.decrypt(ciphertext));
}
return null;
};
var computeMAC = function(derivedHalf, ciphertext) {
return utils.sha3(Buffer.concat([derivedHalf, ciphertext]));
}
return new Promise(function(resolve, reject) {
var kdf = searchPath(data, 'crypto/kdf');
if (kdf && kdf.toLowerCase() === 'scrypt') {
var salt = new Buffer(searchPath(data, 'crypto/kdfparams/salt'), 'hex');
var N = parseInt(searchPath(data, 'crypto/kdfparams/n'));
var r = parseInt(searchPath(data, 'crypto/kdfparams/r'));
var p = parseInt(searchPath(data, 'crypto/kdfparams/p'));
if (!N || !r || !p) {
reject(new Error('unsupported key-derivation function parameters'));
return;
}
// Make sure N is a power of 2
if ((N & (N - 1)) !== 0) {
reject(new Error('unsupported key-derivation function parameter value for N'));
return;
}
var dkLen = searchPath(data, 'crypto/kdfparams/dklen');
if (dkLen !== 32) {
reject( new Error('unsupported key-derivation derived-key length'));
return;
}
scrypt(password, salt, N, r, p, dkLen, function(error, progress, key) {
if (error) {
error.progress = progress;
reject(error);
} else if (key) {
key = new Buffer(key);
var ciphertext = new Buffer(searchPath(data, 'crypto/ciphertext'), 'hex');
var computedMAC = computeMAC(key.slice(16, 32), ciphertext).toString('hex').toLowerCase();
if (computedMAC !== searchPath(data, 'crypto/mac').toLowerCase()) {
reject(new Error('invalid password'));
return;
}
var privateKey = decrypt(key.slice(0, 16), ciphertext);
if (!privateKey) {
reject(new Error('unsupported cipher'));
return;
}
var signingKey = new SigningKey(privateKey);
if (signingKey.address !== utils.getAddress(data.address)) {
reject(new Error('address mismatch'));
return;
}
if (progressCallback) { progressCallback(1); }
resolve(signingKey);
} else if (progressCallback) {
return progressCallback(progress);
}
});
} else {
// @TOOD: Support pbkdf2 kdf
reject(new Error('unsupported key-derivation function'));
}
});
});
utils.defineProperty(secretStorage, 'encrypt', function(privateKey, password, options, progressCallback) {
// the options are optional, so adjust the call as needed
if (typeof(options) === 'function' && !progressCallback) {
progressCallback = options;
options = {};
}
if (!options) { options = {}; }
// Check the private key
if (privateKey instanceof SigningKey) {
privateKey = privateKey.privateKey;
}
privateKey = utils.hexOrBuffer(privateKey, 'private key');
if (privateKey.length !== 32) { throw new Erro('invalid private key'); }
// Check the password
if (!Buffer.isBuffer(password)) { throw new Error('password must be a buffer'); }
// Check/generate the salt
var salt = options.salt;
if (salt) {
salt = utils.hexOrBuffer(salt, 'salt');
} else {
salt = (new Randomish()).randomBytes(32);;
}
// Override initialization vector
var iv = null;
if (options.iv) {
iv = utils.hexOrBuffer(options.iv, 'iv');
if (iv.length !== 16) { throw new Error('invalid iv'); }
}
// Override the uuid
var uuidRandom = options.uuid;
if (uuidRandom) {
uuidRandom = utils.hexOrBuffer(uuidRandom, 'uuid');
if (uuidRandom.length !== 16) { throw new Error('invalid uuid'); }
}
// Override the scrypt password-based key derivation function parameters
var N = (1 << 17), r = 8, p = 1;
if (options.scrypt) {
if (options.scrypt.N) { N = options.scrypt.N; }
if (options.scrypt.r) { r = options.scrypt.r; }
if (options.scrypt.p) { p = options.scrypt.p; }
}
return new Promise(function(resolve, reject) {
// We take 64 bytes:
// - 32 bytes As normal for the Web3 secret storage (derivedKey, macPrefix)
// - 16 bytes The initialization vector
// - 16 bytes The UUID random bytes
scrypt(password, salt, N, r, p, 64, function(error, progress, key) {
if (error) {
error.progress = progress;
reject(error);
} else if (key) {
// Convert the array-like to a Buffer
key = new Buffer(key);
// These will be used to encrypt the wallet (as per Web3 secret storage)
var derivedKey = key.slice(0, 16);
var macPrefix = key.slice(16, 32);
// Get the initialization vector
if (!iv) { iv = key.slice(32, 48); }
// Get the UUID random data
if (!uuidRandom) { uuidRandom = key.slice(48, 64); }
// Get the address for this private key
var address = (new SigningKey(privateKey)).address;
// Encrypt the private key
var counter = new aes.Counter(iv);
var aesCtr = new aes.ModeOfOperation.ctr(derivedKey, counter);
var ciphertext = new Buffer(aesCtr.encrypt(privateKey));
// Compute the message authentication code, used to check the password
var mac = utils.sha3(Buffer.concat([macPrefix, ciphertext]))
// See: https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition
var data = {
address: address,
id: uuid.v4({random: uuidRandom}),
version: 3,
Crypto: {
cipher: 'aes-128-ctr',
cipherparams: {
iv: iv.toString('hex')
},
ciphertext: ciphertext.toString('hex'),
kdf: 'scrypt',
kdfparams: {
salt: salt.toString('hex'),
n: N,
dklen: 32,
p: p,
r: r
},
mac: mac.toString('hex')
}
};
if (progressCallback) { progressCallback(1); }
resolve(JSON.stringify(data));
} else if (progressCallback) {
return progressCallback(progress);
}
});
});
});
module.exports = secretStorage;

View File

@@ -1,38 +0,0 @@
'use strict';
var elliptic = require('elliptic');
var utils = require('./utils.js');
var secp256k1 = new (elliptic.ec)('secp256k1');
function SigningKey(privateKey) {
if (!(this instanceof SigningKey)) { throw new Error('missing new'); }
if (utils.isHexString(privateKey, 32)) {
privateKey = utils.hexOrBuffer(privateKey);
} else if (!Buffer.isBuffer(privateKey) || privateKey.length !== 32) {
throw new Error('invalid private key');
}
utils.defineProperty(this, 'privateKey', '0x' + privateKey.toString('hex'))
var keyPair = secp256k1.keyFromPrivate(privateKey);
var publicKey = (new Buffer(keyPair.getPublic(false, 'hex'), 'hex')).slice(1);
var address = utils.getAddress(utils.sha3(publicKey).slice(12).toString('hex'));
utils.defineProperty(this, 'address', address)
utils.defineProperty(this, 'signDigest', function(digest) {
return keyPair.sign(digest, {canonical: true});
});
}
utils.defineProperty(SigningKey, 'recover', function(digest, r, s, recoveryParam) {
var publicKey = secp256k1.recoverPubKey(digest, {r: r, s: s}, recoveryParam);
publicKey = (new Buffer(publicKey.encode('hex', false), 'hex')).slice(1);
return utils.getAddress(utils.sha3(publicKey).slice(12).toString('hex'));
});
module.exports = SigningKey;

View File

@@ -1,78 +0,0 @@
var utils = require('./utils.js');
var zero = new utils.BN(0);
var negative1 = new utils.BN(-1);
var tenPower18 = new utils.BN('1000000000000000000');
function formatEther(wei, options) {
if (typeof(wei) === 'number') {
// @TODO: Warn if truncation will occur?
wei = new utils.BN(wei);
} else if (utils.isHexString(wei)) {
wei = new utils.BN(wei.substring(2), 16);
}
if (!options) { options = {}; }
if (!(wei instanceof utils.BN)) { throw new Error('invalid wei'); }
var negative = wei.lt(zero);
if (negative) { wei = wei.mul(negative1); }
var fraction = wei.mod(tenPower18).toString(10);
while (fraction.length < 18) { fraction = '0' + fraction; }
if (!options.pad) {
fraction = fraction.match(/^([0-9]*[1-9]|0)(0*)/)[1];
}
var whole = wei.div(tenPower18).toString(10);
if (options.commify) {
whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, ",")
}
var value = whole + '.' + fraction;
if (negative) { value = '-' + value; }
return value;
}
function parseEther(ether) {
if (typeof(ether) !== 'string' || !ether.match(/^-?[0-9.]+$/)) {
throw new Error('invalid value');
}
// Is it negative?
var negative = (ether.substring(0, 1) === '-');
if (negative) { ether = ether.substring(1); }
if (ether === '.') { throw new Error('invalid value'); }
// Split it into a whole and fractional part
var comps = ether.split('.');
if (comps.length > 2) { throw new Error('too many decimal points'); }
var whole = comps[0], fraction = comps[1];
if (!whole) { whole = '0'; }
if (!fraction) { fraction = '0'; }
if (fraction.length > 18) { throw new Error('too many decimal places'); }
while (fraction.length < 18) { fraction += '0'; }
whole = new utils.BN(whole);
fraction = new utils.BN(fraction);
var wei = (whole.mul(tenPower18)).add(fraction);
if (negative) { wei = wei.mul(negative1); }
return wei;
}
module.exports = {
formatEther: formatEther,
parseEther: parseEther,
}

View File

@@ -1,402 +0,0 @@
'use strict';
var rlp = require('rlp');
var BN = require('../node_modules/elliptic/node_modules/bn.js/lib/bn.js');
var hash = require('../node_modules/elliptic/node_modules/hash.js/lib/hash.js');
// See: https://github.com/emn178/js-sha3
var padding = [1, 256, 65536, 16777216];
var HEX_CHARS = '0123456789abcdef'.split('');
var SHIFT = [0, 8, 16, 24];
var RC = [1, 0, 32898, 0, 32906, 2147483648, 2147516416, 2147483648, 32907, 0, 2147483649,
0, 2147516545, 2147483648, 32777, 2147483648, 138, 0, 136, 0, 2147516425, 0,
2147483658, 0, 2147516555, 0, 139, 2147483648, 32905, 2147483648, 32771,
2147483648, 32770, 2147483648, 128, 2147483648, 32778, 0, 2147483658, 2147483648,
2147516545, 2147483648, 32896, 2147483648, 2147483649, 0, 2147516424, 2147483648];
var blocks = [], s = [];
function keccak(message) {
var block, code, end = false, index = 0, start = 0, length = message.length,
n, i, h, l, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9,
b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17,
b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32, b33,
b34, b35, b36, b37, b38, b39, b40, b41, b42, b43, b44, b45, b46, b47, b48, b49;
var blockCount = 34;
var byteCount = blockCount * 4;
for (i = 0; i < 50; ++i) { s[i] = 0; }
block = 0;
do {
blocks[0] = block;
for (i = 1; i < blockCount + 1; ++i) { blocks[i] = 0; }
for (i = start; index < length && i < byteCount; ++index) {
blocks[i >> 2] |= message[index] << SHIFT[i++ & 3];
}
start = i - byteCount;
if (index == length) {
blocks[i >> 2] |= padding[i & 3];
++index;
}
block = blocks[blockCount];
if (index > length && i < byteCount) {
blocks[blockCount - 1] |= 0x80000000;
end = true;
}
for (i = 0;i < blockCount;++i) { s[i] ^= blocks[i]; }
for (n = 0; n < 48; n += 2) {
c0 = s[0] ^ s[10] ^ s[20] ^ s[30] ^ s[40];
c1 = s[1] ^ s[11] ^ s[21] ^ s[31] ^ s[41];
c2 = s[2] ^ s[12] ^ s[22] ^ s[32] ^ s[42];
c3 = s[3] ^ s[13] ^ s[23] ^ s[33] ^ s[43];
c4 = s[4] ^ s[14] ^ s[24] ^ s[34] ^ s[44];
c5 = s[5] ^ s[15] ^ s[25] ^ s[35] ^ s[45];
c6 = s[6] ^ s[16] ^ s[26] ^ s[36] ^ s[46];
c7 = s[7] ^ s[17] ^ s[27] ^ s[37] ^ s[47];
c8 = s[8] ^ s[18] ^ s[28] ^ s[38] ^ s[48];
c9 = s[9] ^ s[19] ^ s[29] ^ s[39] ^ s[49];
h = c8 ^ ((c2 << 1) | (c3 >>> 31));
l = c9 ^ ((c3 << 1) | (c2 >>> 31));
s[0] ^= h; s[1] ^= l; s[10] ^= h; s[11] ^= l;
s[20] ^= h; s[21] ^= l; s[30] ^= h; s[31] ^= l;
s[40] ^= h; s[41] ^= l;
h = c0 ^ ((c4 << 1) | (c5 >>> 31));
l = c1 ^ ((c5 << 1) | (c4 >>> 31));
s[2] ^= h; s[3] ^= l; s[12] ^= h; s[13] ^= l;
s[22] ^= h; s[23] ^= l; s[32] ^= h; s[33] ^= l;
s[42] ^= h; s[43] ^= l;
h = c2 ^ ((c6 << 1) | (c7 >>> 31));
l = c3 ^ ((c7 << 1) | (c6 >>> 31));
s[4] ^= h; s[5] ^= l; s[14] ^= h; s[15] ^= l;
s[24] ^= h; s[25] ^= l; s[34] ^= h; s[35] ^= l;
s[44] ^= h; s[45] ^= l;
h = c4 ^ ((c8 << 1) | (c9 >>> 31));
l = c5 ^ ((c9 << 1) | (c8 >>> 31));
s[6] ^= h; s[7] ^= l; s[16] ^= h; s[17] ^= l;
s[26] ^= h; s[27] ^= l; s[36] ^= h; s[37] ^= l;
s[46] ^= h; s[47] ^= l;
h = c6 ^ ((c0 << 1) | (c1 >>> 31));
l = c7 ^ ((c1 << 1) | (c0 >>> 31));
s[8] ^= h; s[9] ^= l; s[18] ^= h; s[19] ^= l;
s[28] ^= h; s[29] ^= l; s[38] ^= h; s[39] ^= l;
s[48] ^= h; s[49] ^= l;
b0 = s[0]; b1 = s[1];
b32 = (s[11] << 4) | (s[10] >>> 28);
b33 = (s[10] << 4) | (s[11] >>> 28);
b14 = (s[20] << 3) | (s[21] >>> 29);
b15 = (s[21] << 3) | (s[20] >>> 29);
b46 = (s[31] << 9) | (s[30] >>> 23);
b47 = (s[30] << 9) | (s[31] >>> 23);
b28 = (s[40] << 18) | (s[41] >>> 14);
b29 = (s[41] << 18) | (s[40] >>> 14);
b20 = (s[2] << 1) | (s[3] >>> 31);
b21 = (s[3] << 1) | (s[2] >>> 31);
b2 = (s[13] << 12) | (s[12] >>> 20);
b3 = (s[12] << 12) | (s[13] >>> 20);
b34 = (s[22] << 10) | (s[23] >>> 22);
b35 = (s[23] << 10) | (s[22] >>> 22);
b16 = (s[33] << 13) | (s[32] >>> 19);
b17 = (s[32] << 13) | (s[33] >>> 19);
b48 = (s[42] << 2) | (s[43] >>> 30);
b49 = (s[43] << 2) | (s[42] >>> 30);
b40 = (s[5] << 30) | (s[4] >>> 2);
b41 = (s[4] << 30) | (s[5] >>> 2);
b22 = (s[14] << 6) | (s[15] >>> 26);
b23 = (s[15] << 6) | (s[14] >>> 26);
b4 = (s[25] << 11) | (s[24] >>> 21);
b5 = (s[24] << 11) | (s[25] >>> 21);
b36 = (s[34] << 15) | (s[35] >>> 17);
b37 = (s[35] << 15) | (s[34] >>> 17);
b18 = (s[45] << 29) | (s[44] >>> 3);
b19 = (s[44] << 29) | (s[45] >>> 3);
b10 = (s[6] << 28) | (s[7] >>> 4);
b11 = (s[7] << 28) | (s[6] >>> 4);
b42 = (s[17] << 23) | (s[16] >>> 9);
b43 = (s[16] << 23) | (s[17] >>> 9);
b24 = (s[26] << 25) | (s[27] >>> 7);
b25 = (s[27] << 25) | (s[26] >>> 7);
b6 = (s[36] << 21) | (s[37] >>> 11);
b7 = (s[37] << 21) | (s[36] >>> 11);
b38 = (s[47] << 24) | (s[46] >>> 8);
b39 = (s[46] << 24) | (s[47] >>> 8);
b30 = (s[8] << 27) | (s[9] >>> 5);
b31 = (s[9] << 27) | (s[8] >>> 5);
b12 = (s[18] << 20) | (s[19] >>> 12);
b13 = (s[19] << 20) | (s[18] >>> 12);
b44 = (s[29] << 7) | (s[28] >>> 25);
b45 = (s[28] << 7) | (s[29] >>> 25);
b26 = (s[38] << 8) | (s[39] >>> 24);
b27 = (s[39] << 8) | (s[38] >>> 24);
b8 = (s[48] << 14) | (s[49] >>> 18);
b9 = (s[49] << 14) | (s[48] >>> 18);
s[0] = b0 ^ (~b2 & b4); s[1] = b1 ^ (~b3 & b5);
s[10] = b10 ^ (~b12 & b14); s[11] = b11 ^ (~b13 & b15);
s[20] = b20 ^ (~b22 & b24); s[21] = b21 ^ (~b23 & b25);
s[30] = b30 ^ (~b32 & b34); s[31] = b31 ^ (~b33 & b35);
s[40] = b40 ^ (~b42 & b44); s[41] = b41 ^ (~b43 & b45);
s[2] = b2 ^ (~b4 & b6); s[3] = b3 ^ (~b5 & b7);
s[12] = b12 ^ (~b14 & b16); s[13] = b13 ^ (~b15 & b17);
s[22] = b22 ^ (~b24 & b26); s[23] = b23 ^ (~b25 & b27);
s[32] = b32 ^ (~b34 & b36); s[33] = b33 ^ (~b35 & b37);
s[42] = b42 ^ (~b44 & b46); s[43] = b43 ^ (~b45 & b47);
s[4] = b4 ^ (~b6 & b8); s[5] = b5 ^ (~b7 & b9);
s[14] = b14 ^ (~b16 & b18); s[15] = b15 ^ (~b17 & b19);
s[24] = b24 ^ (~b26 & b28); s[25] = b25 ^ (~b27 & b29);
s[34] = b34 ^ (~b36 & b38); s[35] = b35 ^ (~b37 & b39);
s[44] = b44 ^ (~b46 & b48); s[45] = b45 ^ (~b47 & b49);
s[6] = b6 ^ (~b8 & b0); s[7] = b7 ^ (~b9 & b1);
s[16] = b16 ^ (~b18 & b10); s[17] = b17 ^ (~b19 & b11);
s[26] = b26 ^ (~b28 & b20); s[27] = b27 ^ (~b29 & b21);
s[36] = b36 ^ (~b38 & b30); s[37] = b37 ^ (~b39 & b31);
s[46] = b46 ^ (~b48 & b40); s[47] = b47 ^ (~b49 & b41);
s[8] = b8 ^ (~b0 & b2); s[9] = b9 ^ (~b1 & b3);
s[18] = b18 ^ (~b10 & b12); s[19] = b19 ^ (~b11 & b13);
s[28] = b28 ^ (~b20 & b22); s[29] = b29 ^ (~b21 & b23);
s[38] = b38 ^ (~b30 & b32); s[39] = b39 ^ (~b31 & b33);
s[48] = b48 ^ (~b40 & b42); s[49] = b49 ^ (~b41 & b43);
s[0] ^= RC[n]; s[1] ^= RC[n + 1];
}
} while(!end);
var hex = '';
for (i = 0, n = 8; i < n; ++i) {
h = s[i];
hex += HEX_CHARS[(h >> 4) & 0x0F] + HEX_CHARS[h & 0x0F] +
HEX_CHARS[(h >> 12) & 0x0F] + HEX_CHARS[(h >> 8) & 0x0F] +
HEX_CHARS[(h >> 20) & 0x0F] + HEX_CHARS[(h >> 16) & 0x0F] +
HEX_CHARS[(h >> 28) & 0x0F] + HEX_CHARS[(h >> 24) & 0x0F];
}
return hex;
};
// Quick sanity check
//if (keccak(new Buffer('ricmoo')) !== 'b05e424817fb90aa7a79e9da5c5f94070a316219c6ebb863a9ff7ca357dc9fa9') {
// throw new Error('problem with sh3?!');
//}
function sha256(data) {
if (typeof(data) === 'string') {
data = new Buffer(data, 'utf8');
} else if (!Buffer.isBuffer(data)) {
throw new Error('must be a sting');
}
return (new Buffer(hash.sha256().update(data).digest('hex'), 'hex'));
}
function sha3(data) {
if (typeof(data) === 'string') {
data = new Buffer(data, 'utf8');
} else if (!Buffer.isBuffer(data)) {
throw new Error('must be a sting');
}
return new Buffer(keccak(data), 'hex');
}
function defineProperty(object, name, value) {
Object.defineProperty(object, name, {
enumerable: true,
value: value
});
}
function getChecksumAddress(address) {
if (typeof(address) !== 'string' || !address.match(/^0x[0-9A-Fa-f]{40}$/)) {
throw new Error('invalid address');
}
address = address.substring(2).toLowerCase();
var hashed = sha3(address);
address = address.split('');
for (var i = 0; i < 40; i += 2) {
if ((hashed[i >> 1] >> 4) >= 8) {
address[i] = address[i].toUpperCase();
}
if ((hashed[i >> 1] & 0x0f) >= 8) {
address[i + 1] = address[i + 1].toUpperCase();
}
}
return '0x' + address.join('');
}
function getAddress(address) {
var result = null;
if (typeof(address) !== 'string') { throw new Error('invalid address'); }
if (address.match(/^(0x)?[0-9a-fA-F]{40}$/)) {
// Missing the 0x prefix
if (address.substring(0, 2) !== '0x') { address = '0x' + address; }
result = getChecksumAddress(address);
// It is a checksummed address with a bad checksum
if (address.match(/([A-F].*[a-f])|([a-f].*[A-F])/) && result !== address) {
throw new Error('invalid address checksum');
}
// Maybe ICAP? (we only support direct mode)
} else if (address.match(/^XE[0-9]{2}[0-9A-Za-z]{30,31}$/)) {
// It is an ICAP address with a bad checksum
if (address.substring(2, 4) !== ibanChecksum(address)) {
throw new Error('invalid address icap checksum');
}
result = (new BN(address.substring(4), 36)).toString(16);
while (result.length < 40) { result = '0' + result; }
result = getChecksumAddress('0x' + result);
} else {
throw new Error('invalid address');
}
return result;
}
// See: https://en.wikipedia.org/wiki/International_Bank_Account_Number
var ibanChecksum = (function() {
// Create lookup table
var ibanLookup = {};
for (var i = 0; i < 10; i++) { ibanLookup[String(i)] = String(i); }
for (var i = 0; i < 26; i++) { ibanLookup[String.fromCharCode(65 + i)] = String(10 + i); }
// How many decimal digits can we process? (for 64-bit float, this is 15)
var safeDigits = Math.floor(Math.log10(Number.MAX_SAFE_INTEGER));
return function(address) {
address = address.toUpperCase();
address = address.substring(4) + address.substring(0, 2) + '00';
var expanded = address.split('');
for (var i = 0; i < expanded.length; i++) {
expanded[i] = ibanLookup[expanded[i]];
}
expanded = expanded.join('');
// Javascript can handle integers safely up to 15 (decimal) digits
while (expanded.length >= safeDigits){
var block = expanded.substring(0, safeDigits);
expanded = parseInt(block, 10) % 97 + expanded.substring(block.length);
}
var checksum = String(98 - (parseInt(expanded, 10) % 97));
while (checksum.length < 2) { checksum = '0' + checksum; }
return checksum;
};
})();
function getIcapAddress(address) {
address = getAddress(address).substring(2);
var base36 = (new BN(address, 16)).toString(36).toUpperCase();
while (base36.length < 30) { base36 = '0' + base36; }
return 'XE' + ibanChecksum('XE00' + base36) + base36;
}
// http://ethereum.stackexchange.com/questions/760/how-is-the-address-of-an-ethereum-contract-computed
function getContractAddress(transaction) {
return getAddress('0x' + sha3(rlp.encode([
hexOrBuffer(getAddress(transaction.from)),
hexOrBuffer(hexlify(transaction.nonce, 'nonce'))
])).slice(12).toString('hex'));
}
function cloneObject(object) {
var clone = {};
for (var key in object) { clone[key] = object[key]; }
return clone;
}
function stripZeros(buffer) {
var i = 0;
for (i = 0; i < buffer.length; i++) {
if (buffer[i] !== 0) { break; }
}
return (i > 0) ? buffer.slice(i): buffer;
}
function bnToBuffer(bn) {
var hex = bn.toString(16);
if (hex.length % 2) { hex = '0' + hex; }
return stripZeros(new Buffer(hex, 'hex'));
}
function isHexString(value, length) {
if (typeof(value) !== 'string' || !value.match(/^0x[0-9A-Fa-f]*$/)) {
return false
}
if (length && value.length !== 2 + 2 * length) { return false; }
return true;
}
function hexOrBuffer(value, name) {
if (!Buffer.isBuffer(value)) {
if (!isHexString(value)) {
var error = new Error(name ? ('invalid ' + name) : 'invalid hex or buffer');
error.reason = 'invalid hex string';
error.value = value;
throw error;
}
value = value.substring(2);
if (value.length % 2) { value = '0' + value; }
value = new Buffer(value, 'hex');
}
return value;
}
function hexlify(value, name) {
if (typeof(value) === 'number') {
return '0x' + bnToBuffer(new BN(value)).toString('hex');
} else if (value.mod || value.modulo) {
return '0x' + bnToBuffer(value).toString('hex')
} else {
return '0x' + hexOrBuffer(value).toString('hex');
}
throw new Error('invalid value');
}
module.exports = {
BN: BN,
defineProperty: defineProperty,
getAddress: getAddress,
getIcapAddress: getIcapAddress,
getContractAddress: getContractAddress,
cloneObject: cloneObject,
bnToBuffer: bnToBuffer,
isHexString: isHexString,
hexOrBuffer: hexOrBuffer,
hexlify: hexlify,
stripZeros: stripZeros,
sha256: sha256,
sha3: sha3,
}

View File

@@ -1,283 +0,0 @@
'use strict';
var rlp = require('rlp');
var Contract = require('./contract.js');
var providers = require('./providers.js');
var SigningKey = require('./signing-key.js');
var utils = require('./utils.js');
// This ensures we inject a setImmediate into the global space, which
// dramatically improves the performance of the scrypt PBKDF.
require('setimmediate');
var transactionFields = [
{name: 'nonce', maxLength: 32, },
{name: 'gasPrice', maxLength: 32, },
{name: 'gasLimit', maxLength: 32, },
{name: 'to', length: 20, },
{name: 'value', maxLength: 32, },
{name: 'data'},
];
function Wallet(privateKey, provider) {
if (!(this instanceof Wallet)) { throw new Error('missing new'); }
// Make sure we have a valid signing key
var signingKey = privateKey;
if (!(privateKey instanceof SigningKey)) {
signingKey = new SigningKey(privateKey);
}
utils.defineProperty(this, 'privateKey', signingKey.privateKey);
Object.defineProperty(this, 'provider', {
enumerable: true,
get: function() { return provider; },
set: function(value) {
if (value !== null && !providers.isProvider(value)) {
throw new Error('invalid provider');
}
provider = value;
}
});
Object.defineProperty(this, '_provider', {
enumerable: true,
get: function() {
if (!provider) { throw new Error('missing provider'); }
return provider;
},
});
if (provider !== null) {
// If no provider was provided, check for metamask or ilk
if (provider === undefined) {
if (global.web3 && global.web3.currentProvider && global.web3.currentProvider.sendAsync) {
this.provider = new providers.Web3Provider(global.web3.currentProvider);
}
// An Ethereum RPC node
} else if (typeof(provider) === 'string' && provider.match(/^https?:\/\//)) {
this.provider = new providers.HttpProvider(provider);
} else {
this.provider = provider;
}
}
utils.defineProperty(this, 'address', signingKey.address);
utils.defineProperty(this, 'sign', function(transaction) {
var raw = [];
transactionFields.forEach(function(fieldInfo) {
var value = transaction[fieldInfo.name] || (new Buffer(0));
value = utils.hexOrBuffer(utils.hexlify(value), fieldInfo.name);
// Fixed-width field
if (fieldInfo.length && value.length !== fieldInfo.length && value.length > 0) {
var error = new Error('invalid ' + fieldInfo.name);
error.reason = 'wrong length';
error.value = value;
throw error;
}
// Variable-width (with a maximum)
if (fieldInfo.maxLength) {
value = utils.stripZeros(value);
if (value.length > fieldInfo.maxLength) {
var error = new Error('invalid ' + fieldInfo.name);
error.reason = 'too long';
error.value = value;
throw error;
}
}
raw.push(value);
});
var digest = utils.sha3(rlp.encode(raw));
var signature = signingKey.signDigest(digest);
var s = signature.s;
var v = signature.recoveryParam;
raw.push(new Buffer([27 + v]));
raw.push(utils.bnToBuffer(signature.r));
raw.push(utils.bnToBuffer(s));
return ('0x' + rlp.encode(raw).toString('hex'));
});
}
utils.defineProperty(Wallet, 'parseTransaction', function(rawTransaction) {
rawTransaction = utils.hexOrBuffer(rawTransaction, 'rawTransaction');
var signedTransaction = rlp.decode(rawTransaction);
var raw = [];
var transaction = {};
transactionFields.forEach(function(fieldInfo, index) {
transaction[fieldInfo.name] = signedTransaction[index];
raw.push(signedTransaction[index]);
});
if (transaction.to) {
if (transaction.to.length === 0) {
delete transaction.to;
} else {
transaction.to = utils.getAddress('0x' + transaction.to.toString('hex'));
}
}
['gasPrice', 'gasLimit', 'nonce', 'value'].forEach(function(name) {
if (!transaction[name]) { return; }
if (transaction[name].length === 0) {
transaction[name] = new utils.BN(0);
} else {
transaction[name] = new utils.BN(transaction[name].toString('hex'), 16);
}
});
/* @TODO: Maybe? In the future, all nonces stored as numbers? (obviously, major version change)
if (transaction.nonce) {
transaction.nonce = transaction.nonce.toNumber()
}
*/
if (signedTransaction.length > 6 && signedTransaction[6].length === 1 &&
signedTransaction[7].length >= 1 && signedTransaction[7].length <= 32 &&
signedTransaction[8].length >= 1 && signedTransaction[7].length <= 32) {
transaction.v = signedTransaction[6][0];
transaction.r = signedTransaction[7];
transaction.s = signedTransaction[8];
var digest = utils.sha3(rlp.encode(raw));
try {
transaction.from = SigningKey.recover(digest, transaction.r, transaction.s, transaction.v - 27);
} catch (error) { }
}
return transaction;
});
utils.defineProperty(Wallet.prototype, 'getBalance', function(blockNumber) {
var provider = this._provider;
var self = this;
return new Promise(function(resolve, reject) {
provider.getBalance(self.address, blockNumber).then(function(balance) {
resolve(balance);
}, function(error) {
reject(error);
});
});
});
utils.defineProperty(Wallet.prototype, 'getTransactionCount', function(blockNumber) {
var provider = this._provider;
var self = this;
return new Promise(function(resolve, reject) {
provider.getTransactionCount(self.address, blockNumber).then(function(transactionCount) {
resolve(transactionCount);
}, function(error) {
reject(error);
});
});
});
utils.defineProperty(Wallet.prototype, 'estimateGas', function(transaction) {
var provider = this._provider;
transaction = utils.cloneObject(transaction);
if (transaction.from == null) { transaction.from = this.address; }
return new Promise(function(resolve, reject) {
provider.estimateGas(transaction).then(function(gasEstimate) {
resolve(gasEstimate);
}, function(error) {
reject(error);
});
});
});
utils.defineProperty(Wallet.prototype, 'sendTransaction', function(transaction) {
var gasLimit = transaction.gasLimit;
if (gasLimit == null) { gasLimit = 3000000; }
var self = this;
var provider = this._provider;
var gasPrice = new Promise(function(resolve, reject) {
if (transaction.gasPrice) {
return resolve(transaction.gasPrice);
}
provider.getGasPrice().then(function(gasPrice) {
resolve(gasPrice);
}, function(error) {
reject(error);
});
});
var nonce = new Promise(function(resolve, reject) {
if (transaction.nonce) {
return resolve(transaction.nonce);
}
provider.getTransactionCount(self.address, 'pending').then(function(transactionCount) {
resolve(transactionCount);
}, function(error) {
reject(error);
});
});
return new Promise(function(resolve, reject) {
Promise.all([gasPrice, nonce]).then(function(results) {
var signedTransaction = self.sign({
to: transaction.to,
gasLimit: gasLimit,
gasPrice: results[0],
nonce: results[1],
value: transaction.value
});
provider.sendTransaction(signedTransaction).then(function(txid) {
resolve(txid);
}, function(error) {
reject(error);
});
}, function(error) {
reject(error);
});
});
});
utils.defineProperty(Wallet.prototype, 'send', function(address, amountWei, options) {
address = utils.getAddress(address);
if (utils.BN.isBN(amountWei)) {
amountWei = '0x' + utils.bnToBuffer(amountWei).toString('hex');
}
if (!utils.isHexString(amountWei)) { throw new Error('invalid amountWei'); }
if (!options) { options = {}; }
return this.sendTransaction({
to: address,
gasLimit: options.gasLimit,
gasPrice: options.gasPrice,
nonce: options.nonce,
value: amountWei,
});
});
utils.defineProperty(Wallet.prototype, 'getContract', function(address, abi) {
return new Contract(this, address, new Contract.Interface(abi));
});
utils.defineProperty(Wallet, '_Contract', Contract);
module.exports = Wallet;

7603
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,48 +1,68 @@
{
"name": "ethers-wallet",
"version": "1.0.4",
"name": "ethers",
"version": "4.0.29",
"description": "Ethereum wallet library.",
"main": "index.js",
"main": "./index.js",
"types": "./index.d.ts",
"scripts": {
"test": "./node_modules/.bin/nodeunit tests/index.js",
"version": "grunt dist"
"build": "npm run dist-version && tsc -p ./tsconfig.json",
"auto-build": "npm run build -- -w",
"dist": "npm run dist-version && npm run build && gulp default minified shims && npm run dist-types",
"dist-test": "gulp default-test minified-test shims",
"dist-bip39": "gulp bip39-es bip39-fr bip39-it bip39-ja bip39-ko bip39-zh",
"dist-types": "dts-bundle --name ethers --main ./index.d.ts --out ./dist/ethers.types.txt",
"dist-version": "node -e \"let v = require('./package.json').version; require('fs').writeFileSync('./src.ts/_version.ts', 'export const version = \\\"' + v +'\\\";\\n')\"",
"eslint": "eslint index.js contracts/*.js providers/*.js utils/*.js wallet/*.js wordlists/*.js",
"test": "if [ \"$RUN_PHANTOMJS\" = \"1\" ]; then npm run-script test-phantomjs; else npm run-script test-node; fi",
"test-node": "npm run dist-test && mocha --no-colors --reporter tests/reporter tests/test-*.js",
"test-phantomjs": "npm run dist-test && gulp tests && phantomjs --web-security=false ./node_modules/mocha-phantomjs-core/mocha-phantomjs-core.js ./tests/test.html ./tests/reporter.js",
"version": "npm dist"
},
"dependencies": {
"aes-js": "2.0.0",
"elliptic": "6.3.1",
"inherits": "2.0.1",
"pbkdf2": "3.0.4",
"rlp": "2.0.0",
"@types/node": "^10.3.2",
"aes-js": "3.0.0",
"bn.js": "^4.4.0",
"elliptic": "6.3.3",
"hash.js": "1.1.3",
"js-sha3": "0.5.7",
"scrypt-js": "2.0.4",
"setimmediate": "1.0.4",
"scrypt-js": "2.0.3",
"uuid": "2.0.1",
"xmlhttprequest": "1.8.0"
},
"browser": {
"./lib/random-bytes.js": "./lib/browser-random-bytes.js",
"xmlhttprequest": "./lib/browser-xmlhttprequest.js"
},
"devDependencies": {
"ethereumjs-abi": "0.6.2",
"ethereumjs-tx": "1.1.1",
"ethereumjs-util": "4.3.0",
"grunt": "^0.4.5",
"grunt-browserify": "^5.0.0",
"grunt-contrib-uglify": "^1.0.1",
"nodeunit": "0.9.1",
"web3": "0.15.3",
"ethereumjs-vm": "1.4.0",
"solc": "0.3.5"
"browserify": "^16.2.3",
"browserify-zlib": "^0.2.0",
"dts-bundle": "^0.7.3",
"eslint": "^5.16.0",
"eslint-plugin-promise": "^3.8.0",
"ethereumjs-tx": "^1.3.5",
"ethereumjs-util": "^5.2.0",
"gulp": "^4.0.0",
"gulp-cli": "^2.0.1",
"gulp-sourcemaps": "^2.6.4",
"gulp-typescript": "^5.0.0-alpha.1",
"gulp-uglify": "^3.0.0",
"mocha": "^5.2.0",
"mocha-phantomjs-core": "2.1.2",
"solc": "0.4.20",
"tsify": "^4.0.0",
"tslint": "^5.10.0",
"typescript": "^2.9.1",
"vinyl-buffer": "^1.0.1",
"vinyl-source-stream": "^2.0.0",
"web3-providers-http": "1.0.0-beta.35"
},
"browser": "./dist/ethers.min.js",
"keywords": [
"ethereum",
"library",
"wallet"
],
"author": "Richard Moore <me@ricmoo.com>",
"repository": {
"type": "git",
"url": "git://github.com/ethers-io/ethers-wallet.git"
"url": "git://github.com/ethers-io/ethers.js.git"
},
"license": "MIT"
}

108
providers/abstract-provider.d.ts vendored Normal file
View File

@@ -0,0 +1,108 @@
import { BigNumber } from '../utils/bignumber';
import { Arrayish } from '../utils/bytes';
import { BigNumberish } from '../utils/bignumber';
import { Network } from '../utils/networks';
import { OnceBlockable } from '../utils/web';
import { Transaction } from '../utils/transaction';
export interface Block {
hash: string;
parentHash: string;
number: number;
timestamp: number;
nonce: string;
difficulty: number;
gasLimit: BigNumber;
gasUsed: BigNumber;
miner: string;
extraData: string;
transactions: Array<string>;
}
export declare type BlockTag = string | number;
export declare type Filter = {
fromBlock?: BlockTag;
toBlock?: BlockTag;
address?: string;
topics?: Array<string | Array<string>>;
};
export declare type FilterByBlock = {
blockHash?: string;
address?: string;
topics?: Array<string | Array<string>>;
};
export interface Log {
blockNumber?: number;
blockHash?: string;
transactionIndex?: number;
removed?: boolean;
transactionLogIndex?: number;
address: string;
data: string;
topics: Array<string>;
transactionHash?: string;
logIndex?: number;
}
export interface TransactionReceipt {
to?: string;
from?: string;
contractAddress?: string;
transactionIndex?: number;
root?: string;
gasUsed?: BigNumber;
logsBloom?: string;
blockHash?: string;
transactionHash?: string;
logs?: Array<Log>;
blockNumber?: number;
confirmations?: number;
cumulativeGasUsed?: BigNumber;
byzantium: boolean;
status?: number;
}
export declare type TransactionRequest = {
to?: string | Promise<string>;
from?: string | Promise<string>;
nonce?: BigNumberish | Promise<BigNumberish>;
gasLimit?: BigNumberish | Promise<BigNumberish>;
gasPrice?: BigNumberish | Promise<BigNumberish>;
data?: Arrayish | Promise<Arrayish>;
value?: BigNumberish | Promise<BigNumberish>;
chainId?: number | Promise<number>;
};
export interface TransactionResponse extends Transaction {
blockNumber?: number;
blockHash?: string;
timestamp?: number;
confirmations: number;
from: string;
raw?: string;
wait: (confirmations?: number) => Promise<TransactionReceipt>;
}
export declare type EventType = string | Array<string> | Filter;
export declare type Listener = (...args: Array<any>) => void;
export declare abstract class Provider implements OnceBlockable {
abstract getNetwork(): Promise<Network>;
abstract getBlockNumber(): Promise<number>;
abstract getGasPrice(): Promise<BigNumber>;
abstract getBalance(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<BigNumber>;
abstract getTransactionCount(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<number>;
abstract getCode(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
abstract getStorageAt(addressOrName: string | Promise<string>, position: BigNumberish | Promise<BigNumberish>, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
abstract sendTransaction(signedTransaction: string | Promise<string>): Promise<TransactionResponse>;
abstract call(transaction: TransactionRequest, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
abstract estimateGas(transaction: TransactionRequest): Promise<BigNumber>;
abstract getBlock(blockHashOrBlockTag: BlockTag | string | Promise<BlockTag | string>, includeTransactions?: boolean): Promise<Block>;
abstract getTransaction(transactionHash: string): Promise<TransactionResponse>;
abstract getTransactionReceipt(transactionHash: string): Promise<TransactionReceipt>;
abstract getLogs(filter: Filter | FilterByBlock): Promise<Array<Log>>;
abstract resolveName(name: string | Promise<string>): Promise<string>;
abstract lookupAddress(address: string | Promise<string>): Promise<string>;
abstract on(eventName: EventType, listener: Listener): Provider;
abstract once(eventName: EventType, listener: Listener): Provider;
abstract listenerCount(eventName?: EventType): number;
abstract listeners(eventName: EventType): Array<Listener>;
abstract removeAllListeners(eventName: EventType): Provider;
abstract removeListener(eventName: EventType, listener: Listener): Provider;
abstract waitForTransaction(transactionHash: string, timeout?: number): Promise<TransactionReceipt>;
constructor();
static isProvider(value: any): value is Provider;
}

View File

@@ -0,0 +1,18 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var properties_1 = require("../utils/properties");
;
;
///////////////////////////////
// Exported Abstracts
var Provider = /** @class */ (function () {
function Provider() {
properties_1.setType(this, 'Provider');
}
Provider.isProvider = function (value) {
return properties_1.isType(value, 'Provider');
};
return Provider;
}());
exports.Provider = Provider;
//defineReadOnly(Signer, 'inherits', inheritable(Abstract));

75
providers/base-provider.d.ts vendored Normal file
View File

@@ -0,0 +1,75 @@
import { BigNumber } from '../utils/bignumber';
import { Provider } from './abstract-provider';
import { Block, BlockTag, EventType, Filter, FilterByBlock, Listener, Log, TransactionReceipt, TransactionRequest, TransactionResponse } from './abstract-provider';
import { BigNumberish } from '../utils/bignumber';
import { Transaction } from '../utils/transaction';
import { Network, Networkish } from '../utils/networks';
export declare class BaseProvider extends Provider {
private _network;
private _events;
protected _emitted: {
[eventName: string]: number | 'pending';
};
private _pollingInterval;
private _poller;
private _lastBlockNumber;
private _balances;
private _fastBlockNumber;
private _fastBlockNumberPromise;
private _fastQueryDate;
/**
* ready
*
* A Promise<Network> that resolves only once the provider is ready.
*
* Sub-classes that call the super with a network without a chainId
* MUST set this. Standard named networks have a known chainId.
*
*/
protected ready: Promise<Network>;
constructor(network: Networkish | Promise<Network>);
private _doPoll;
resetEventsBlock(blockNumber: number): void;
readonly network: Network;
getNetwork(): Promise<Network>;
readonly blockNumber: number;
polling: boolean;
pollingInterval: number;
_getFastBlockNumber(): Promise<number>;
_setFastBlockNumber(blockNumber: number): void;
waitForTransaction(transactionHash: string, confirmations?: number): Promise<TransactionReceipt>;
getBlockNumber(): Promise<number>;
getGasPrice(): Promise<BigNumber>;
getBalance(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<BigNumber>;
getTransactionCount(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<number>;
getCode(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
getStorageAt(addressOrName: string | Promise<string>, position: BigNumberish | Promise<BigNumberish>, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
sendTransaction(signedTransaction: string | Promise<string>): Promise<TransactionResponse>;
_wrapTransaction(tx: Transaction, hash?: string): TransactionResponse;
call(transaction: TransactionRequest, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
estimateGas(transaction: TransactionRequest): Promise<BigNumber>;
getBlock(blockHashOrBlockTag: BlockTag | string | Promise<BlockTag | string>, includeTransactions?: boolean): Promise<Block>;
getTransaction(transactionHash: string): Promise<TransactionResponse>;
getTransactionReceipt(transactionHash: string): Promise<TransactionReceipt>;
getLogs(filter: Filter | FilterByBlock): Promise<Array<Log>>;
getEtherPrice(): Promise<number>;
_getAddress(addressOrName: string): Promise<string>;
private _resolveNames;
private _getResolver;
resolveName(name: string | Promise<string>): Promise<string>;
lookupAddress(address: string | Promise<string>): Promise<string>;
static checkTransactionResponse(transaction: any): TransactionResponse;
doPoll(): void;
perform(method: string, params: any): Promise<any>;
protected _startPending(): void;
protected _stopPending(): void;
private _addEventListener;
on(eventName: EventType, listener: Listener): Provider;
once(eventName: EventType, listener: Listener): Provider;
addEventListener(eventName: EventType, listener: Listener): Provider;
emit(eventName: EventType, ...args: Array<any>): boolean;
listenerCount(eventName?: EventType): number;
listeners(eventName: EventType): Array<Listener>;
removeAllListeners(eventName?: EventType): Provider;
removeListener(eventName: EventType, listener: Listener): Provider;
}

1234
providers/base-provider.js Normal file

File diff suppressed because it is too large Load Diff

10
providers/etherscan-provider.d.ts vendored Normal file
View 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>>;
}

View File

@@ -0,0 +1,347 @@
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
var base_provider_1 = require("./base-provider");
var bytes_1 = require("../utils/bytes");
var properties_1 = require("../utils/properties");
var web_1 = require("../utils/web");
var errors = __importStar(require("../errors"));
///////////////////////////////
// The transaction has already been sanitized by the calls in Provider
function getTransactionString(transaction) {
var result = [];
for (var key in transaction) {
if (transaction[key] == null) {
continue;
}
var value = bytes_1.hexlify(transaction[key]);
if ({ gasLimit: true, gasPrice: true, nonce: true, value: true }[key]) {
value = bytes_1.hexStripZeros(value);
}
result.push(key + '=' + value);
}
return result.join('&');
}
function getResult(result) {
// getLogs, getHistory have weird success responses
if (result.status == 0 && (result.message === 'No records found' || result.message === 'No transactions found')) {
return result.result;
}
if (result.status != 1 || result.message != 'OK') {
// @TODO: not any
var error = new Error('invalid response');
error.result = JSON.stringify(result);
throw error;
}
return result.result;
}
function getJsonResult(result) {
if (result.jsonrpc != '2.0') {
// @TODO: not any
var error = new Error('invalid response');
error.result = JSON.stringify(result);
throw error;
}
if (result.error) {
// @TODO: not any
var error = new Error(result.error.message || 'unknown error');
if (result.error.code) {
error.code = result.error.code;
}
if (result.error.data) {
error.data = result.error.data;
}
throw error;
}
return result.result;
}
// The blockTag was normalized as a string by the Provider pre-perform operations
function checkLogTag(blockTag) {
if (blockTag === 'pending') {
throw new Error('pending not supported');
}
if (blockTag === 'latest') {
return blockTag;
}
return parseInt(blockTag.substring(2), 16);
}
var EtherscanProvider = /** @class */ (function (_super) {
__extends(EtherscanProvider, _super);
function EtherscanProvider(network, apiKey) {
var _this = _super.call(this, network) || this;
errors.checkNew(_this, EtherscanProvider);
var name = 'invalid';
if (_this.network) {
name = _this.network.name;
}
var baseUrl = null;
switch (name) {
case 'homestead':
baseUrl = 'https://api.etherscan.io';
break;
case 'ropsten':
baseUrl = 'https://api-ropsten.etherscan.io';
break;
case 'rinkeby':
baseUrl = 'https://api-rinkeby.etherscan.io';
break;
case 'kovan':
baseUrl = 'https://api-kovan.etherscan.io';
break;
case 'goerli':
baseUrl = 'https://api-goerli.etherscan.io';
break;
default:
throw new Error('unsupported network');
}
properties_1.defineReadOnly(_this, 'baseUrl', baseUrl);
properties_1.defineReadOnly(_this, 'apiKey', apiKey);
return _this;
}
EtherscanProvider.prototype.perform = function (method, params) {
var _this = this;
var url = this.baseUrl;
var apiKey = '';
if (this.apiKey) {
apiKey += '&apikey=' + this.apiKey;
}
var get = function (url, procFunc) {
return web_1.fetchJson(url, null, procFunc || getJsonResult).then(function (result) {
_this.emit('debug', {
action: 'perform',
request: url,
response: result,
provider: _this
});
return result;
});
};
switch (method) {
case 'getBlockNumber':
url += '/api?module=proxy&action=eth_blockNumber' + apiKey;
return get(url);
case 'getGasPrice':
url += '/api?module=proxy&action=eth_gasPrice' + apiKey;
return get(url);
case 'getBalance':
// Returns base-10 result
url += '/api?module=account&action=balance&address=' + params.address;
url += '&tag=' + params.blockTag + apiKey;
return get(url, getResult);
case 'getTransactionCount':
url += '/api?module=proxy&action=eth_getTransactionCount&address=' + params.address;
url += '&tag=' + params.blockTag + apiKey;
return get(url);
case 'getCode':
url += '/api?module=proxy&action=eth_getCode&address=' + params.address;
url += '&tag=' + params.blockTag + apiKey;
return get(url, getJsonResult);
case 'getStorageAt':
url += '/api?module=proxy&action=eth_getStorageAt&address=' + params.address;
url += '&position=' + params.position;
url += '&tag=' + params.blockTag + apiKey;
return get(url, getJsonResult);
case 'sendTransaction':
url += '/api?module=proxy&action=eth_sendRawTransaction&hex=' + params.signedTransaction;
url += apiKey;
return get(url).catch(function (error) {
if (error.responseText) {
// "Insufficient funds. The account you tried to send transaction from does not have enough funds. Required 21464000000000 and got: 0"
if (error.responseText.toLowerCase().indexOf('insufficient funds') >= 0) {
errors.throwError('insufficient funds', errors.INSUFFICIENT_FUNDS, {});
}
// "Transaction with the same hash was already imported."
if (error.responseText.indexOf('same hash was already imported') >= 0) {
errors.throwError('nonce has already been used', errors.NONCE_EXPIRED, {});
}
// "Transaction gas price is too low. There is another transaction with same nonce in the queue. Try increasing the gas price or incrementing the nonce."
if (error.responseText.indexOf('another transaction with same nonce') >= 0) {
errors.throwError('replacement fee too low', errors.REPLACEMENT_UNDERPRICED, {});
}
}
throw error;
});
case 'getBlock':
if (params.blockTag) {
url += '/api?module=proxy&action=eth_getBlockByNumber&tag=' + params.blockTag;
if (params.includeTransactions) {
url += '&boolean=true';
}
else {
url += '&boolean=false';
}
url += apiKey;
return get(url);
}
throw new Error('getBlock by blockHash not implmeneted');
case 'getTransaction':
url += '/api?module=proxy&action=eth_getTransactionByHash&txhash=' + params.transactionHash;
url += apiKey;
return get(url);
case 'getTransactionReceipt':
url += '/api?module=proxy&action=eth_getTransactionReceipt&txhash=' + params.transactionHash;
url += apiKey;
return get(url);
case 'call': {
var transaction = getTransactionString(params.transaction);
if (transaction) {
transaction = '&' + transaction;
}
url += '/api?module=proxy&action=eth_call' + transaction;
//url += '&tag=' + params.blockTag + apiKey;
if (params.blockTag !== 'latest') {
throw new Error('EtherscanProvider does not support blockTag for call');
}
url += apiKey;
return get(url);
}
case 'estimateGas': {
var transaction = getTransactionString(params.transaction);
if (transaction) {
transaction = '&' + transaction;
}
url += '/api?module=proxy&action=eth_estimateGas&' + transaction;
url += apiKey;
return get(url);
}
case 'getLogs':
url += '/api?module=logs&action=getLogs';
try {
if (params.filter.fromBlock) {
url += '&fromBlock=' + checkLogTag(params.filter.fromBlock);
}
if (params.filter.toBlock) {
url += '&toBlock=' + checkLogTag(params.filter.toBlock);
}
if (params.filter.blockHash) {
try {
errors.throwError("Etherscan does not support blockHash filters", errors.UNSUPPORTED_OPERATION, {
operation: "getLogs(blockHash)"
});
}
catch (error) {
return Promise.reject(error);
}
}
if (params.filter.address) {
url += '&address=' + params.filter.address;
}
// @TODO: We can handle slightly more complicated logs using the logs API
if (params.filter.topics && params.filter.topics.length > 0) {
if (params.filter.topics.length > 1) {
throw new Error('unsupported topic format');
}
var topic0 = params.filter.topics[0];
if (typeof (topic0) !== 'string' || topic0.length !== 66) {
throw new Error('unsupported topic0 format');
}
url += '&topic0=' + topic0;
}
}
catch (error) {
return Promise.reject(error);
}
url += apiKey;
var self = this;
return get(url, getResult).then(function (logs) {
var txs = {};
var seq = Promise.resolve();
logs.forEach(function (log) {
seq = seq.then(function () {
if (log.blockHash != null) {
return null;
}
log.blockHash = txs[log.transactionHash];
if (log.blockHash == null) {
return self.getTransaction(log.transactionHash).then(function (tx) {
txs[log.transactionHash] = tx.blockHash;
log.blockHash = tx.blockHash;
return null;
});
}
return null;
});
});
return seq.then(function () {
return logs;
});
});
case 'getEtherPrice':
if (this.network.name !== 'homestead') {
return Promise.resolve(0.0);
}
url += '/api?module=stats&action=ethprice';
url += apiKey;
return get(url, getResult).then(function (result) {
return parseFloat(result.ethusd);
});
default:
break;
}
return _super.prototype.perform.call(this, method, params);
};
// @TODO: Allow startBlock and endBlock to be Promises
EtherscanProvider.prototype.getHistory = function (addressOrName, startBlock, endBlock) {
var _this = this;
var url = this.baseUrl;
var apiKey = '';
if (this.apiKey) {
apiKey += '&apikey=' + this.apiKey;
}
if (startBlock == null) {
startBlock = 0;
}
if (endBlock == null) {
endBlock = 99999999;
}
return this.resolveName(addressOrName).then(function (address) {
url += '/api?module=account&action=txlist&address=' + address;
url += '&startblock=' + startBlock;
url += '&endblock=' + endBlock;
url += '&sort=asc' + apiKey;
return web_1.fetchJson(url, null, getResult).then(function (result) {
_this.emit('debug', {
action: 'getHistory',
request: url,
response: result,
provider: _this
});
var output = [];
result.forEach(function (tx) {
['contractAddress', 'to'].forEach(function (key) {
if (tx[key] == '') {
delete tx[key];
}
});
if (tx.creates == null && tx.contractAddress != null) {
tx.creates = tx.contractAddress;
}
var item = base_provider_1.BaseProvider.checkTransactionResponse(tx);
if (tx.timeStamp) {
item.timestamp = parseInt(tx.timeStamp);
}
output.push(item);
});
return output;
});
});
};
return EtherscanProvider;
}(base_provider_1.BaseProvider));
exports.EtherscanProvider = EtherscanProvider;

9
providers/fallback-provider.d.ts vendored Normal file
View 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;
}

View File

@@ -0,0 +1,111 @@
'use strict';
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
var base_provider_1 = require("./base-provider");
var errors = __importStar(require("../errors"));
// Returns:
// - true is all networks match
// - false if any network is null
// - throws if any 2 networks do not match
function checkNetworks(networks) {
var result = true;
var check = null;
networks.forEach(function (network) {
// Null
if (network == null) {
result = false;
return;
}
// Have nothing to compre to yet
if (check == null) {
check = network;
return;
}
// Matches!
if (check.name === network.name &&
check.chainId === network.chainId &&
((check.ensAddress === network.ensAddress) ||
(check.ensAddress == null && network.ensAddress == null))) {
return;
}
errors.throwError('provider mismatch', errors.INVALID_ARGUMENT, { arg: 'networks', value: networks });
});
return result;
}
var FallbackProvider = /** @class */ (function (_super) {
__extends(FallbackProvider, _super);
function FallbackProvider(providers) {
var _this = this;
if (providers.length === 0) {
throw new Error('no providers');
}
// All networks are ready, we can know the network for certain
var ready = checkNetworks(providers.map(function (p) { return p.network; }));
if (ready) {
_this = _super.call(this, providers[0].network) || this;
}
else {
// The network won't be known until all child providers know
var ready_1 = Promise.all(providers.map(function (p) { return p.getNetwork(); })).then(function (networks) {
if (!checkNetworks(networks)) {
errors.throwError('getNetwork returned null', errors.UNKNOWN_ERROR, {});
}
return networks[0];
});
_this = _super.call(this, ready_1) || this;
}
errors.checkNew(_this, FallbackProvider);
// Preserve a copy, so we don't get mutated
_this._providers = providers.slice(0);
return _this;
}
Object.defineProperty(FallbackProvider.prototype, "providers", {
get: function () {
// Return a copy, so we don't get mutated
return this._providers.slice(0);
},
enumerable: true,
configurable: true
});
FallbackProvider.prototype.perform = function (method, params) {
// Creates a copy of the providers array
var providers = this.providers;
return new Promise(function (resolve, reject) {
var firstError = null;
function next() {
if (!providers.length) {
reject(firstError);
return;
}
var provider = providers.shift();
provider.perform(method, params).then(function (result) {
return resolve(result);
}).catch(function (error) {
if (!firstError) {
firstError = error;
}
setTimeout(next, 0);
});
}
next();
});
};
return FallbackProvider;
}(base_provider_1.BaseProvider));
exports.FallbackProvider = FallbackProvider;

11
providers/index.d.ts vendored Normal file
View 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
View File

@@ -0,0 +1,19 @@
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
var abstract_provider_1 = require("./abstract-provider");
exports.Provider = abstract_provider_1.Provider;
var base_provider_1 = require("./base-provider");
exports.BaseProvider = base_provider_1.BaseProvider;
var etherscan_provider_1 = require("./etherscan-provider");
exports.EtherscanProvider = etherscan_provider_1.EtherscanProvider;
var fallback_provider_1 = require("./fallback-provider");
exports.FallbackProvider = fallback_provider_1.FallbackProvider;
var ipc_provider_1 = require("./ipc-provider");
exports.IpcProvider = ipc_provider_1.IpcProvider;
var infura_provider_1 = require("./infura-provider");
exports.InfuraProvider = infura_provider_1.InfuraProvider;
var json_rpc_provider_1 = require("./json-rpc-provider");
exports.JsonRpcProvider = json_rpc_provider_1.JsonRpcProvider;
exports.JsonRpcSigner = json_rpc_provider_1.JsonRpcSigner;
var web3_provider_1 = require("./web3-provider");
exports.Web3Provider = web3_provider_1.Web3Provider;

10
providers/infura-provider.d.ts vendored Normal file
View File

@@ -0,0 +1,10 @@
import { JsonRpcProvider, JsonRpcSigner } from './json-rpc-provider';
import { Networkish } from '../utils/networks';
export declare class InfuraProvider extends JsonRpcProvider {
readonly apiAccessToken: string;
readonly projectId: string;
constructor(network?: Networkish, projectId?: string);
protected _startPending(): void;
getSigner(address?: string): JsonRpcSigner;
listAccounts(): Promise<Array<string>>;
}

View File

@@ -0,0 +1,84 @@
'use strict';
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
var json_rpc_provider_1 = require("./json-rpc-provider");
var bytes_1 = require("../utils/bytes");
var networks_1 = require("../utils/networks");
var properties_1 = require("../utils/properties");
var errors = __importStar(require("../errors"));
var defaultProjectId = "7d0d81d0919f4f05b9ab6634be01ee73";
var InfuraProvider = /** @class */ (function (_super) {
__extends(InfuraProvider, _super);
function InfuraProvider(network, projectId) {
var _this = this;
var standard = networks_1.getNetwork((network == null) ? 'homestead' : network);
if (projectId == null) {
projectId = defaultProjectId;
}
var host = null;
switch (standard.name) {
case 'homestead':
host = 'mainnet.infura.io';
break;
case 'ropsten':
host = 'ropsten.infura.io';
break;
case 'rinkeby':
host = 'rinkeby.infura.io';
break;
case 'goerli':
host = 'goerli.infura.io';
break;
case 'kovan':
host = 'kovan.infura.io';
break;
default:
errors.throwError('unsupported network', errors.INVALID_ARGUMENT, {
argument: "network",
value: network
});
}
// New-style Project ID
if (bytes_1.isHexString("0x" + projectId, 16)) {
_this = _super.call(this, 'https://' + host + '/v3/' + projectId, standard) || this;
properties_1.defineReadOnly(_this, 'apiAccessToken', null);
properties_1.defineReadOnly(_this, 'projectId', projectId);
// Legacy API Access Token
}
else {
errors.warn("The legacy INFURA apiAccesToken API is deprecated; please upgrade to a Project ID instead (see INFURA dshboard; https://infura.io)");
_this = _super.call(this, 'https://' + host + '/' + projectId, standard) || this;
properties_1.defineReadOnly(_this, 'apiAccessToken', projectId);
properties_1.defineReadOnly(_this, 'projectId', null);
}
errors.checkNew(_this, InfuraProvider);
return _this;
}
InfuraProvider.prototype._startPending = function () {
errors.warn('WARNING: INFURA does not support pending filters');
};
InfuraProvider.prototype.getSigner = function (address) {
return errors.throwError('INFURA does not support signing', errors.UNSUPPORTED_OPERATION, { operation: 'getSigner' });
};
InfuraProvider.prototype.listAccounts = function () {
return Promise.resolve([]);
};
return InfuraProvider;
}(json_rpc_provider_1.JsonRpcProvider));
exports.InfuraProvider = InfuraProvider;

7
providers/ipc-provider.d.ts vendored Normal file
View File

@@ -0,0 +1,7 @@
import { JsonRpcProvider } from './json-rpc-provider';
import { Networkish } from '../utils/networks';
export declare class IpcProvider extends JsonRpcProvider {
readonly path: string;
constructor(path: string, network?: Networkish);
send(method: string, params: any): Promise<any>;
}

81
providers/ipc-provider.js Normal file
View File

@@ -0,0 +1,81 @@
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
var net_1 = __importDefault(require("net"));
var json_rpc_provider_1 = require("./json-rpc-provider");
var properties_1 = require("../utils/properties");
var errors = __importStar(require("../errors"));
var IpcProvider = /** @class */ (function (_super) {
__extends(IpcProvider, _super);
function IpcProvider(path, network) {
var _this = this;
if (path == null) {
errors.throwError('missing path', errors.MISSING_ARGUMENT, {
argument: 'path'
});
}
_this = _super.call(this, 'ipc://' + path, network) || this;
errors.checkNew(_this, IpcProvider);
properties_1.defineReadOnly(_this, 'path', path);
return _this;
}
// @TODO: Create a connection to the IPC path and use filters instead of polling for block
IpcProvider.prototype.send = function (method, params) {
// This method is very simple right now. We create a new socket
// connection each time, which may be slower, but the main
// advantage we are aiming for now is security. This simplifies
// multiplexing requests (since we do not need to multiplex).
var _this = this;
var payload = JSON.stringify({
method: method,
params: params,
id: 42,
jsonrpc: "2.0"
});
return new Promise(function (resolve, reject) {
var response = Buffer.alloc(0);
var stream = net_1.default.connect(_this.path);
stream.on('data', function (data) {
response = Buffer.concat([response, data]);
});
stream.on("end", function () {
try {
resolve(JSON.parse(response.toString('utf8')).result);
// @TODO: Better pull apart the error
stream.destroy();
}
catch (error) {
reject(error);
stream.destroy();
}
});
stream.on('error', function (error) {
reject(error);
stream.destroy();
});
stream.write(payload);
stream.end();
});
};
return IpcProvider;
}(json_rpc_provider_1.JsonRpcProvider));
exports.IpcProvider = IpcProvider;

36
providers/json-rpc-provider.d.ts vendored Normal file
View File

@@ -0,0 +1,36 @@
import { BaseProvider } from './base-provider';
import { Signer } from '../abstract-signer';
import { BigNumber } from '../utils/bignumber';
import { Arrayish } from '../utils/bytes';
import { Networkish } from '../utils/networks';
import { ConnectionInfo } from '../utils/web';
import { BlockTag, TransactionRequest, TransactionResponse } from '../providers/abstract-provider';
export declare class JsonRpcSigner extends Signer {
readonly provider: JsonRpcProvider;
private _index;
private _address;
constructor(constructorGuard: any, provider: JsonRpcProvider, addressOrIndex?: string | number);
getAddress(): Promise<string>;
getBalance(blockTag?: BlockTag): Promise<BigNumber>;
getTransactionCount(blockTag?: BlockTag): Promise<number>;
sendUncheckedTransaction(transaction: TransactionRequest): Promise<string>;
sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse>;
signMessage(message: Arrayish | string): Promise<string>;
unlock(password: string): Promise<boolean>;
}
export declare class JsonRpcProvider extends BaseProvider {
readonly connection: ConnectionInfo;
private _pendingFilter;
constructor(url?: ConnectionInfo | string, network?: Networkish);
getSigner(addressOrIndex?: string | number): JsonRpcSigner;
listAccounts(): Promise<Array<string>>;
send(method: string, params: any): Promise<any>;
perform(method: string, params: any): Promise<any>;
protected _startPending(): void;
protected _stopPending(): void;
static hexlifyTransaction(transaction: TransactionRequest, allowExtra?: {
[key: string]: boolean;
}): {
[key: string]: string;
};
}

View File

@@ -0,0 +1,398 @@
'use strict';
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
// See: https://github.com/ethereum/wiki/wiki/JSON-RPC
var base_provider_1 = require("./base-provider");
var abstract_signer_1 = require("../abstract-signer");
var errors = __importStar(require("../errors"));
var address_1 = require("../utils/address");
var bytes_1 = require("../utils/bytes");
var networks_1 = require("../utils/networks");
var properties_1 = require("../utils/properties");
var utf8_1 = require("../utils/utf8");
var web_1 = require("../utils/web");
function timer(timeout) {
return new Promise(function (resolve) {
setTimeout(function () {
resolve();
}, timeout);
});
}
function getResult(payload) {
if (payload.error) {
// @TODO: not any
var error = new Error(payload.error.message);
error.code = payload.error.code;
error.data = payload.error.data;
throw error;
}
return payload.result;
}
function getLowerCase(value) {
if (value) {
return value.toLowerCase();
}
return value;
}
var _constructorGuard = {};
// Some environments (Trust Wallet and company) use a global map
// to track JSON-RPC ID, so we try to keep IDs unique across all
// connections. See #489.
var _nextId = 42;
var JsonRpcSigner = /** @class */ (function (_super) {
__extends(JsonRpcSigner, _super);
function JsonRpcSigner(constructorGuard, provider, addressOrIndex) {
var _this = _super.call(this) || this;
errors.checkNew(_this, JsonRpcSigner);
if (constructorGuard !== _constructorGuard) {
throw new Error('do not call the JsonRpcSigner constructor directly; use provider.getSigner');
}
properties_1.defineReadOnly(_this, 'provider', provider);
// Statically attach to a given address
if (addressOrIndex) {
if (typeof (addressOrIndex) === 'string') {
properties_1.defineReadOnly(_this, '_address', address_1.getAddress(addressOrIndex));
}
else if (typeof (addressOrIndex) === 'number') {
properties_1.defineReadOnly(_this, '_index', addressOrIndex);
}
else {
errors.throwError('invalid address or index', errors.INVALID_ARGUMENT, { argument: 'addressOrIndex', value: addressOrIndex });
}
}
else {
properties_1.defineReadOnly(_this, '_index', 0);
}
return _this;
}
JsonRpcSigner.prototype.getAddress = function () {
var _this = this;
if (this._address) {
return Promise.resolve(this._address);
}
return this.provider.send('eth_accounts', []).then(function (accounts) {
if (accounts.length <= _this._index) {
errors.throwError('unknown account #' + _this._index, errors.UNSUPPORTED_OPERATION, { operation: 'getAddress' });
}
_this._address = address_1.getAddress(accounts[_this._index]);
return _this._address;
});
};
JsonRpcSigner.prototype.getBalance = function (blockTag) {
return this.provider.getBalance(this.getAddress(), blockTag);
};
JsonRpcSigner.prototype.getTransactionCount = function (blockTag) {
return this.provider.getTransactionCount(this.getAddress(), blockTag);
};
JsonRpcSigner.prototype.sendUncheckedTransaction = function (transaction) {
var _this = this;
transaction = properties_1.shallowCopy(transaction);
var fromAddress = this.getAddress().then(function (address) {
if (address) {
address = address.toLowerCase();
}
return address;
});
// The JSON-RPC for eth_sendTransaction uses 90000 gas; if the user
// wishes to use this, it is easy to specify explicitly, otherwise
// we look it up for them.
if (transaction.gasLimit == null) {
var estimate = properties_1.shallowCopy(transaction);
estimate.from = fromAddress;
transaction.gasLimit = this.provider.estimateGas(estimate);
}
return Promise.all([
properties_1.resolveProperties(transaction),
fromAddress
]).then(function (results) {
var tx = results[0];
var hexTx = JsonRpcProvider.hexlifyTransaction(tx);
hexTx.from = results[1];
return _this.provider.send('eth_sendTransaction', [hexTx]).then(function (hash) {
return hash;
}, function (error) {
if (error.responseText) {
// See: JsonRpcProvider.sendTransaction (@TODO: Expose a ._throwError??)
if (error.responseText.indexOf('insufficient funds') >= 0) {
errors.throwError('insufficient funds', errors.INSUFFICIENT_FUNDS, {
transaction: tx
});
}
if (error.responseText.indexOf('nonce too low') >= 0) {
errors.throwError('nonce has already been used', errors.NONCE_EXPIRED, {
transaction: tx
});
}
if (error.responseText.indexOf('replacement transaction underpriced') >= 0) {
errors.throwError('replacement fee too low', errors.REPLACEMENT_UNDERPRICED, {
transaction: tx
});
}
}
throw error;
});
});
};
JsonRpcSigner.prototype.sendTransaction = function (transaction) {
var _this = this;
return this.sendUncheckedTransaction(transaction).then(function (hash) {
return web_1.poll(function () {
return _this.provider.getTransaction(hash).then(function (tx) {
if (tx === null) {
return undefined;
}
return _this.provider._wrapTransaction(tx, hash);
});
}, { fastRetry: 250, onceBlock: _this.provider }).catch(function (error) {
error.transactionHash = hash;
throw error;
});
});
};
JsonRpcSigner.prototype.signMessage = function (message) {
var _this = this;
var data = ((typeof (message) === 'string') ? utf8_1.toUtf8Bytes(message) : message);
return this.getAddress().then(function (address) {
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign
return _this.provider.send('eth_sign', [address.toLowerCase(), bytes_1.hexlify(data)]);
});
};
JsonRpcSigner.prototype.unlock = function (password) {
var provider = this.provider;
return this.getAddress().then(function (address) {
return provider.send('personal_unlockAccount', [address.toLowerCase(), password, null]);
});
};
return JsonRpcSigner;
}(abstract_signer_1.Signer));
exports.JsonRpcSigner = JsonRpcSigner;
var allowedTransactionKeys = {
chainId: true, data: true, gasLimit: true, gasPrice: true, nonce: true, to: true, value: true
};
var JsonRpcProvider = /** @class */ (function (_super) {
__extends(JsonRpcProvider, _super);
function JsonRpcProvider(url, network) {
var _this = this;
// One parameter, but it is a network name, so swap it with the URL
if (typeof (url) === 'string') {
if (network === null && networks_1.getNetwork(url)) {
network = url;
url = null;
}
}
if (network) {
// The network has been specified explicitly, we can use it
_this = _super.call(this, network) || this;
}
else {
// The network is unknown, query the JSON-RPC for it
var ready = new Promise(function (resolve, reject) {
setTimeout(function () {
_this.send('net_version', []).then(function (result) {
return resolve(networks_1.getNetwork(parseInt(result)));
}).catch(function (error) {
reject(error);
});
});
});
_this = _super.call(this, ready) || this;
}
errors.checkNew(_this, JsonRpcProvider);
// Default URL
if (!url) {
url = 'http://localhost:8545';
}
if (typeof (url) === 'string') {
_this.connection = {
url: url
};
}
else {
_this.connection = url;
}
return _this;
}
JsonRpcProvider.prototype.getSigner = function (addressOrIndex) {
return new JsonRpcSigner(_constructorGuard, this, addressOrIndex);
};
JsonRpcProvider.prototype.listAccounts = function () {
return this.send('eth_accounts', []).then(function (accounts) {
return accounts.map(function (a) { return address_1.getAddress(a); });
});
};
JsonRpcProvider.prototype.send = function (method, params) {
var _this = this;
var request = {
method: method,
params: params,
id: (_nextId++),
jsonrpc: "2.0"
};
return web_1.fetchJson(this.connection, JSON.stringify(request), getResult).then(function (result) {
_this.emit('debug', {
action: 'send',
request: request,
response: result,
provider: _this
});
return result;
});
};
JsonRpcProvider.prototype.perform = function (method, params) {
switch (method) {
case 'getBlockNumber':
return this.send('eth_blockNumber', []);
case 'getGasPrice':
return this.send('eth_gasPrice', []);
case 'getBalance':
return this.send('eth_getBalance', [getLowerCase(params.address), params.blockTag]);
case 'getTransactionCount':
return this.send('eth_getTransactionCount', [getLowerCase(params.address), params.blockTag]);
case 'getCode':
return this.send('eth_getCode', [getLowerCase(params.address), params.blockTag]);
case 'getStorageAt':
return this.send('eth_getStorageAt', [getLowerCase(params.address), params.position, params.blockTag]);
case 'sendTransaction':
return this.send('eth_sendRawTransaction', [params.signedTransaction]).catch(function (error) {
if (error.responseText) {
// "insufficient funds for gas * price + value"
if (error.responseText.indexOf('insufficient funds') > 0) {
errors.throwError('insufficient funds', errors.INSUFFICIENT_FUNDS, {});
}
// "nonce too low"
if (error.responseText.indexOf('nonce too low') > 0) {
errors.throwError('nonce has already been used', errors.NONCE_EXPIRED, {});
}
// "replacement transaction underpriced"
if (error.responseText.indexOf('replacement transaction underpriced') > 0) {
errors.throwError('replacement fee too low', errors.REPLACEMENT_UNDERPRICED, {});
}
}
throw error;
});
case 'getBlock':
if (params.blockTag) {
return this.send('eth_getBlockByNumber', [params.blockTag, !!params.includeTransactions]);
}
else if (params.blockHash) {
return this.send('eth_getBlockByHash', [params.blockHash, !!params.includeTransactions]);
}
return Promise.reject(new Error('invalid block tag or block hash'));
case 'getTransaction':
return this.send('eth_getTransactionByHash', [params.transactionHash]);
case 'getTransactionReceipt':
return this.send('eth_getTransactionReceipt', [params.transactionHash]);
case 'call':
return this.send('eth_call', [JsonRpcProvider.hexlifyTransaction(params.transaction, { from: true }), params.blockTag]);
case 'estimateGas':
return this.send('eth_estimateGas', [JsonRpcProvider.hexlifyTransaction(params.transaction, { from: true })]);
case 'getLogs':
if (params.filter && params.filter.address != null) {
params.filter.address = getLowerCase(params.filter.address);
}
return this.send('eth_getLogs', [params.filter]);
default:
break;
}
errors.throwError(method + ' not implemented', errors.NOT_IMPLEMENTED, { operation: method });
return null;
};
JsonRpcProvider.prototype._startPending = function () {
if (this._pendingFilter != null) {
return;
}
var self = this;
var pendingFilter = this.send('eth_newPendingTransactionFilter', []);
this._pendingFilter = pendingFilter;
pendingFilter.then(function (filterId) {
function poll() {
self.send('eth_getFilterChanges', [filterId]).then(function (hashes) {
if (self._pendingFilter != pendingFilter) {
return null;
}
var seq = Promise.resolve();
hashes.forEach(function (hash) {
// @TODO: This should be garbage collected at some point... How? When?
self._emitted['t:' + hash.toLowerCase()] = 'pending';
seq = seq.then(function () {
return self.getTransaction(hash).then(function (tx) {
self.emit('pending', tx);
return null;
});
});
});
return seq.then(function () {
return timer(1000);
});
}).then(function () {
if (self._pendingFilter != pendingFilter) {
self.send('eth_uninstallFilter', [filterId]);
return;
}
setTimeout(function () { poll(); }, 0);
return null;
}).catch(function (error) { });
}
poll();
return filterId;
}).catch(function (error) { });
};
JsonRpcProvider.prototype._stopPending = function () {
this._pendingFilter = null;
};
// Convert an ethers.js transaction into a JSON-RPC transaction
// - gasLimit => gas
// - All values hexlified
// - All numeric values zero-striped
// NOTE: This allows a TransactionRequest, but all values should be resolved
// before this is called
JsonRpcProvider.hexlifyTransaction = function (transaction, allowExtra) {
// Check only allowed properties are given
var allowed = properties_1.shallowCopy(allowedTransactionKeys);
if (allowExtra) {
for (var key in allowExtra) {
if (allowExtra[key]) {
allowed[key] = true;
}
}
}
properties_1.checkProperties(transaction, allowed);
var result = {};
// Some nodes (INFURA ropsten; INFURA mainnet is fine) don't like leading zeros.
['gasLimit', 'gasPrice', 'nonce', 'value'].forEach(function (key) {
if (transaction[key] == null) {
return;
}
var value = bytes_1.hexStripZeros(bytes_1.hexlify(transaction[key]));
if (key === 'gasLimit') {
key = 'gas';
}
result[key] = value;
});
['from', 'to', 'data'].forEach(function (key) {
if (transaction[key] == null) {
return;
}
result[key] = bytes_1.hexlify(transaction[key]);
});
return result;
};
return JsonRpcProvider;
}(base_provider_1.BaseProvider));
exports.JsonRpcProvider = JsonRpcProvider;

15
providers/web3-provider.d.ts vendored Normal file
View 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>;
}

View 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
View 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
View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

26
shims/hmac.js Normal file
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1,4 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
require('setimmediate');
exports.platform = "browser";

5
shims/wordlists.js Normal file
View 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
View 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
View File

@@ -0,0 +1 @@
export const version = "4.0.29";

32
src.ts/abstract-signer.ts Normal file
View 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
View File

@@ -0,0 +1,31 @@
import { BigNumber, bigNumberify } from './utils/bignumber';
const AddressZero = '0x0000000000000000000000000000000000000000';
const HashZero = '0x0000000000000000000000000000000000000000000000000000000000000000';
// NFKD (decomposed)
//const EtherSymbol = '\uD835\uDF63';
// NFKC (composed)
const EtherSymbol = '\u039e';
const NegativeOne: BigNumber = bigNumberify(-1);
const Zero: BigNumber = bigNumberify(0);
const One: BigNumber = bigNumberify(1);
const Two: BigNumber = bigNumberify(2);
const WeiPerEther: BigNumber = bigNumberify('1000000000000000000');
const MaxUint256: BigNumber = bigNumberify('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
export {
AddressZero,
HashZero,
EtherSymbol,
NegativeOne,
Zero,
One,
Two,
WeiPerEther,
MaxUint256
};

862
src.ts/contract.ts Normal file
View File

@@ -0,0 +1,862 @@
'use strict';
import { Zero } from './constants';
import * as errors from './errors';
import { defaultAbiCoder, formatSignature, parseSignature } from './utils/abi-coder';
import { getAddress, getContractAddress } from './utils/address';
import { BigNumber, bigNumberify } from './utils/bignumber';
import { hexDataLength, hexDataSlice, hexlify, isArrayish, isHexString } from './utils/bytes';
import { Indexed, Interface } from './utils/interface';
import { defineReadOnly, deepCopy, shallowCopy } from './utils/properties';
import { UnsignedTransaction } from './utils/transaction';
///////////////////////////////
// Imported Abstracts
import { BlockTag, Provider } from './providers/abstract-provider';
import { Signer } from './abstract-signer';
///////////////////////////////
// Imported Types
import { Arrayish } from './utils/bytes';
import { EventDescription } from './utils/interface';
import { ParamType } from './utils/abi-coder';
import { Block, Listener, Log, TransactionReceipt, TransactionRequest, TransactionResponse } from './providers/abstract-provider';
///////////////////////////////
// Exported Types
export type ContractFunction = (...params: Array<any>) => Promise<any>;
export type EventFilter = {
address?: string;
topics?: Array<string>;
// @TODO: Support OR-style topcis; backwards compatible to make this change
//topics?: Array<string | Array<string>>
};
// The (n + 1)th parameter passed to contract event callbacks
export interface Event extends Log {
args?: Array<any>;
decode?: (data: string, topics?: Array<string>) => any;
event?: string;
eventSignature?: string;
removeListener: () => void;
getBlock: () => Promise<Block>;
getTransaction: () => Promise<TransactionResponse>;
getTransactionReceipt: () => Promise<TransactionReceipt>;
}
export interface ContractReceipt extends TransactionReceipt {
events?: Array<Event>;
}
export interface ContractTransaction extends TransactionResponse {
wait(confirmations?: number): Promise<ContractReceipt>;
}
///////////////////////////////
export class VoidSigner extends Signer {
readonly address: string;
constructor(address: string, provider: Provider) {
super();
defineReadOnly(this, 'address', address);
defineReadOnly(this, 'provider', provider);
}
getAddress(): Promise<string> {
return Promise.resolve(this.address);
}
_fail(message: string, operation: string): Promise<any> {
return Promise.resolve().then(() => {
errors.throwError(message, errors.UNSUPPORTED_OPERATION, { operation: operation });
});
}
signMessage(message: Arrayish | string): Promise<string> {
return this._fail('VoidSigner cannot sign messages', 'signMessage');
}
sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse> {
return this._fail('VoidSigner cannot sign transactions', 'sendTransaction');
}
connect(provider: Provider): VoidSigner {
return new VoidSigner(this.address, provider);
}
}
const allowedTransactionKeys: { [ key: string ]: boolean } = {
chainId: true, data: true, from: true, gasLimit: true, gasPrice:true, nonce: true, to: true, value: true
}
// Recursively replaces ENS names with promises to resolve the name and
// stalls until all promises have returned
// @TODO: Expand this to resolve any promises too
function resolveAddresses(provider: Provider, value: any, paramType: ParamType | Array<ParamType>): Promise<any> {
if (Array.isArray(paramType)) {
let promises: Array<Promise<any>> = [];
paramType.forEach((paramType, index) => {
let v = null;
if (Array.isArray(value)) {
v = value[index];
} else {
v = value[paramType.name];
}
promises.push(resolveAddresses(provider, v, paramType));
});
return Promise.all(promises);
}
if (paramType.type === 'address') {
return provider.resolveName(value);
}
if (paramType.type === 'tuple') {
return resolveAddresses(provider, value, paramType.components);
}
// Strips one level of array indexing off the end to recuse into
let isArrayMatch = paramType.type.match(/(.*)(\[[0-9]*\]$)/);
if (isArrayMatch) {
if (!Array.isArray(value)) { throw new Error('invalid value for array'); }
var promises: Array<Promise<any>> = [];
var subParamType = {
components: paramType.components,
type: isArrayMatch[1],
};
value.forEach((v) => {
promises.push(resolveAddresses(provider, v, subParamType));
});
return Promise.all(promises);
}
return Promise.resolve(value);
}
type RunFunction = (...params: Array<any>) => Promise<any>;
function runMethod(contract: Contract, functionName: string, estimateOnly: boolean): RunFunction {
let method = contract.interface.functions[functionName];
return function(...params): Promise<any> {
let tx: any = {}
let blockTag: BlockTag = null;
// If 1 extra parameter was passed in, it contains overrides
if (params.length === method.inputs.length + 1 && typeof(params[params.length - 1]) === 'object') {
tx = shallowCopy(params.pop());
if (tx.blockTag != null) {
blockTag = tx.blockTag;
}
delete tx.blockTag;
// Check for unexpected keys (e.g. using "gas" instead of "gasLimit")
for (let key in tx) {
if (!allowedTransactionKeys[key]) {
throw new Error('unknown transaction override ' + key);
}
}
}
if (params.length != method.inputs.length) {
throw new Error('incorrect number of arguments');
}
// Check overrides make sense
['data', 'to'].forEach(function(key) {
if (tx[key] != null) {
errors.throwError('cannot override ' + key, errors.UNSUPPORTED_OPERATION, { operation: key })
}
});
tx.to = contract._deployed(blockTag).then(() => {
return contract.addressPromise;
});
return resolveAddresses(contract.provider, params, method.inputs).then((params) => {
tx.data = method.encode(params);
if (method.type === 'call') {
// Call (constant functions) always cost 0 ether
if (estimateOnly) {
return Promise.resolve(Zero);
}
if (!contract.provider) {
errors.throwError('call (constant functions) require a provider or a signer with a provider', errors.UNSUPPORTED_OPERATION, { operation: 'call' })
}
// Check overrides make sense
['gasLimit', 'gasPrice', 'value'].forEach(function(key) {
if (tx[key] != null) {
throw new Error('call cannot override ' + key) ;
}
});
if (tx.from == null && contract.signer) {
tx.from = contract.signer.getAddress()
}
return contract.provider.call(tx, blockTag).then((value) => {
if ((hexDataLength(value) % 32) === 4 && hexDataSlice(value, 0, 4) === '0x08c379a0') {
let reason = defaultAbiCoder.decode([ 'string' ], hexDataSlice(value, 4));
errors.throwError('call revert exception', errors.CALL_EXCEPTION, {
address: contract.address,
args: params,
method: method.signature,
errorSignature: 'Error(string)',
errorArgs: [ reason ],
reason: reason,
transaction: tx
});
}
try {
let result = method.decode(value);
if (method.outputs.length === 1) {
result = result[0];
}
return result;
} catch (error) {
if (value === '0x' && method.outputs.length > 0) {
errors.throwError('call exception', errors.CALL_EXCEPTION, {
address: contract.address,
method: method.signature,
args: params
});
}
throw error;
}
});
} else if (method.type === 'transaction') {
// Only computing the transaction estimate
if (estimateOnly) {
if (!contract.provider) {
errors.throwError('estimate gas require a provider or a signer with a provider', errors.UNSUPPORTED_OPERATION, { operation: 'estimateGas' })
}
if (tx.from == null && contract.signer) {
tx.from = contract.signer.getAddress()
}
return contract.provider.estimateGas(tx);
}
if (tx.gasLimit == null && method.gas != null) {
tx.gasLimit = bigNumberify(method.gas).add(21000);
}
if (!contract.signer) {
errors.throwError('sending a transaction require a signer', errors.UNSUPPORTED_OPERATION, { operation: 'sendTransaction' })
}
// Make sure they aren't overriding something they shouldn't
if (tx.from != null) {
errors.throwError('cannot override from in a transaction', errors.UNSUPPORTED_OPERATION, { operation: 'sendTransaction' })
}
return contract.signer.sendTransaction(tx).then((tx) => {
let wait = tx.wait.bind(tx);
tx.wait = (confirmations?: number) => {
return wait(confirmations).then((receipt: ContractReceipt) => {
receipt.events = receipt.logs.map((log) => {
let event: Event = (<Event>deepCopy(log));
let parsed = contract.interface.parseLog(log);
if (parsed) {
event.args = parsed.values;
event.decode = parsed.decode;
event.event = parsed.name;
event.eventSignature = parsed.signature;
}
event.removeListener = () => { return contract.provider; }
event.getBlock = () => {
return contract.provider.getBlock(receipt.blockHash);
}
event.getTransaction = () => {
return contract.provider.getTransaction(receipt.transactionHash);
}
event.getTransactionReceipt = () => {
return Promise.resolve(receipt);
}
return event;
});
return receipt;
});
};
return tx;
});
}
throw new Error('invalid type - ' + method.type);
return null;
});
}
}
function getEventTag(filter: EventFilter): string {
if (filter.address && (filter.topics == null || filter.topics.length === 0)) {
return '*';
}
return (filter.address || '*') + '@' + (filter.topics ? filter.topics.join(':'): '');
}
interface Bucket<T> {
[name: string]: T;
}
type _EventFilter = {
prepareEvent: (event: Event) => Array<any>;
event?: EventDescription;
eventTag: string;
filter: EventFilter;
};
type _Event = {
eventFilter: _EventFilter;
listener: Listener;
once: boolean;
wrappedListener: Listener;
};
export class Contract {
readonly address: string;
readonly interface: Interface;
readonly signer: Signer;
readonly provider: Provider;
readonly estimate: Bucket<(...params: Array<any>) => Promise<BigNumber>>;
readonly functions: Bucket<ContractFunction>;
readonly filters: Bucket<(...params: Array<any>) => EventFilter>;
readonly [name: string]: ContractFunction | any;
readonly addressPromise: Promise<string>;
// This is only set if the contract was created with a call to deploy
readonly deployTransaction: TransactionResponse;
private _deployedPromise: Promise<Contract>;
// https://github.com/Microsoft/TypeScript/issues/5453
// Once this issue is resolved (there are open PR) we can do this nicer
// by making addressOrName default to null for 2 operand calls. :)
constructor(addressOrName: string, contractInterface: Array<string | ParamType> | string | Interface, signerOrProvider: Signer | Provider) {
errors.checkNew(this, Contract);
// @TODO: Maybe still check the addressOrName looks like a valid address or name?
//address = getAddress(address);
if (Interface.isInterface(contractInterface)) {
defineReadOnly(this, 'interface', contractInterface);
} else {
defineReadOnly(this, 'interface', new Interface(contractInterface));
}
if (Signer.isSigner(signerOrProvider)) {
defineReadOnly(this, 'provider', signerOrProvider.provider);
defineReadOnly(this, 'signer', signerOrProvider);
} else if (Provider.isProvider(signerOrProvider)) {
defineReadOnly(this, 'provider', signerOrProvider);
defineReadOnly(this, 'signer', null);
} else {
errors.throwError('invalid signer or provider', errors.INVALID_ARGUMENT, { arg: 'signerOrProvider', value: signerOrProvider });
}
defineReadOnly(this, 'estimate', { });
defineReadOnly(this, 'functions', { });
defineReadOnly(this, 'filters', { });
Object.keys(this.interface.events).forEach((eventName) => {
let event = this.interface.events[eventName];
defineReadOnly(this.filters, eventName, (...args: Array<any>) => {
return {
address: this.address,
topics: event.encodeTopics(args)
}
});
});
this._events = [];
defineReadOnly(this, 'address', addressOrName);
if (this.provider) {
defineReadOnly(this, 'addressPromise', this.provider.resolveName(addressOrName).then((address) => {
if (address == null) { throw new Error('name not found'); }
return address;
}).catch((error: Error) => {
throw error;
}));
} else {
try {
defineReadOnly(this, 'addressPromise', Promise.resolve(getAddress(addressOrName)));
} catch (error) {
// Without a provider, we cannot use ENS names
errors.throwError('provider is required to use non-address contract address', errors.INVALID_ARGUMENT, { argument: 'addressOrName', value: addressOrName });
}
}
Object.keys(this.interface.functions).forEach((name) => {
let run = runMethod(this, name, false);
if ((<any>this)[name] == null) {
defineReadOnly(this, name, run);
} else {
errors.warn('WARNING: Multiple definitions for ' + name);
}
if (this.functions[name] == null) {
defineReadOnly(this.functions, name, run);
defineReadOnly(this.estimate, name, runMethod(this, name, true));
}
});
}
// @TODO: Allow timeout?
deployed(): Promise<Contract> {
return this._deployed();
}
_deployed(blockTag?: BlockTag): Promise<Contract> {
if (!this._deployedPromise) {
// If we were just deployed, we know the transaction we should occur in
if (this.deployTransaction) {
this._deployedPromise = this.deployTransaction.wait().then(() => {
return this;
});
} else {
// @TODO: Once we allow a timeout to be passed in, we will wait
// up to that many blocks for getCode
// Otherwise, poll for our code to be deployed
this._deployedPromise = this.provider.getCode(this.address, blockTag).then((code) => {
if (code === '0x') {
errors.throwError('contract not deployed', errors.UNSUPPORTED_OPERATION, {
contractAddress: this.address,
operation: 'getDeployed'
});
}
return this;
});
}
}
return this._deployedPromise;
}
// @TODO:
// estimateFallback(overrides?: TransactionRequest): Promise<BigNumber>
// @TODO:
// estimateDeploy(bytecode: string, ...args): Promise<BigNumber>
fallback(overrides?: TransactionRequest): Promise<TransactionResponse> {
if (!this.signer) {
errors.throwError('sending a transaction require a signer', errors.UNSUPPORTED_OPERATION, { operation: 'sendTransaction(fallback)' })
}
var tx: TransactionRequest = shallowCopy(overrides || {});
['from', 'to'].forEach(function(key) {
if ((<any>tx)[key] == null) { return; }
errors.throwError('cannot override ' + key, errors.UNSUPPORTED_OPERATION, { operation: key })
});
tx.to = this.addressPromise;
return this.deployed().then(() => {
return this.signer.sendTransaction(tx);
});
}
// Reconnect to a different signer or provider
connect(signerOrProvider: Signer | Provider | string): Contract {
if (typeof(signerOrProvider) === 'string') {
signerOrProvider = new VoidSigner(signerOrProvider, this.provider);
}
let contract = new Contract(this.address, this.interface, signerOrProvider);
if (this.deployTransaction) {
defineReadOnly(contract, 'deployTransaction', this.deployTransaction);
}
return contract;
}
// Re-attach to a different on=chain instance of this contract
attach(addressOrName: string): Contract {
return new Contract(addressOrName, this.interface, this.signer || this.provider);
}
static isIndexed(value: any): value is Indexed {
return Interface.isIndexed(value);
}
private _events: Array<_Event>;
private _getEventFilter(eventName: EventFilter | string): _EventFilter {
if (typeof(eventName) === 'string') {
// Listen for any event
if (eventName === '*') {
return {
prepareEvent: (e: Event) => {
let parsed = this.interface.parseLog(e);
if (parsed) {
e.args = parsed.values;
e.decode = parsed.decode;
e.event = parsed.name;
e.eventSignature = parsed.signature;
}
return [ e ];
},
eventTag: '*',
filter: { address: this.address },
};
}
// Normalize the eventName
if (eventName.indexOf('(') !== -1) {
eventName = formatSignature(parseSignature('event ' + eventName));
}
let event = this.interface.events[eventName];
if (!event) {
errors.throwError('unknown event - ' + eventName, errors.INVALID_ARGUMENT, { argumnet: 'eventName', value: eventName });
}
let filter = {
address: this.address,
topics: [ event.topic ]
}
return {
prepareEvent: (e: Event) => {
let args = event.decode(e.data, e.topics);
e.args = args;
let result = Array.prototype.slice.call(args);
result.push(e);
return result;
},
event: event,
eventTag: getEventTag(filter),
filter: filter
};
}
let filter: EventFilter = {
address: this.address
}
// Find the matching event in the ABI; if none, we still allow filtering
// since it may be a filter for an otherwise unknown event
let event: EventDescription = null;
if (eventName.topics && eventName.topics[0]) {
filter.topics = eventName.topics;
for (let name in this.interface.events) {
if (name.indexOf('(') === -1) { continue; }
let e = this.interface.events[name];
if (e.topic === eventName.topics[0].toLowerCase()) {
event = e;
break;
}
}
}
return {
prepareEvent: (e: Event) => {
if (!event) { return [ e ]; }
let args = event.decode(e.data, e.topics);
e.args = args;
let result = Array.prototype.slice.call(args);
result.push(e);
return result;
},
event: event,
eventTag: getEventTag(filter),
filter: filter
}
}
private _addEventListener(eventFilter: _EventFilter, listener: Listener, once: boolean): void {
if (!this.provider) {
errors.throwError('events require a provider or a signer with a provider', errors.UNSUPPORTED_OPERATION, { operation: 'once' })
}
let wrappedListener = (log: Log) => {
let event: Event = (<Event>deepCopy(log));
let args = eventFilter.prepareEvent(event);
if (eventFilter.event) {
event.decode = eventFilter.event.decode;
event.event = eventFilter.event.name;
event.eventSignature = eventFilter.event.signature;
}
event.removeListener = () => { this.removeListener(eventFilter.filter, listener); };
event.getBlock = () => { return this.provider.getBlock(log.blockHash); }
event.getTransaction = () => { return this.provider.getTransaction(log.transactionHash); }
event.getTransactionReceipt = () => { return this.provider.getTransactionReceipt(log.transactionHash); }
this.emit(eventFilter.filter, ...args);
};
this.provider.on(eventFilter.filter, wrappedListener);
this._events.push({ eventFilter: eventFilter, listener: listener, wrappedListener: wrappedListener, once: once });
}
on(event: EventFilter | string, listener: Listener): Contract {
this._addEventListener(this._getEventFilter(event), listener, false);
return this;
}
once(event: EventFilter | string, listener: Listener): Contract {
this._addEventListener(this._getEventFilter(event), listener, true);
return this;
}
addListener(eventName: EventFilter | string, listener: Listener): Contract {
return this.on(eventName, listener);
}
emit(eventName: EventFilter | string, ...args: Array<any>): boolean {
if (!this.provider) { return false; }
let result = false;
let eventFilter = this._getEventFilter(eventName);
this._events = this._events.filter((event) => {
// Not this event (keep it for later)
if (event.eventFilter.eventTag !== eventFilter.eventTag) { return true; }
// Call the callback in the next event loop
setTimeout(() => {
event.listener.apply(this, args);
}, 0);
result = true;
// Reschedule it if it not "once"
return !(event.once);
});
return result;
}
listenerCount(eventName?: EventFilter | string): number {
if (!this.provider) { return 0; }
let eventFilter = this._getEventFilter(eventName);
return this._events.filter((event) => {
return event.eventFilter.eventTag === eventFilter.eventTag
}).length;
}
listeners(eventName: EventFilter | string): Array<Listener> {
if (!this.provider) { return []; }
let eventFilter = this._getEventFilter(eventName);
return this._events.filter((event) => {
return event.eventFilter.eventTag === eventFilter.eventTag
}).map((event) => { return event.listener; });
}
removeAllListeners(eventName: EventFilter | string): Contract {
if (!this.provider) { return this; }
let eventFilter = this._getEventFilter(eventName);
this._events = this._events.filter((event) => {
// Keep all other events
if (event.eventFilter.eventTag !== eventFilter.eventTag) {
return true;
}
// Deregister this event from the provider and filter it out
this.provider.removeListener(event.eventFilter.filter, event.wrappedListener);
return false;
});
return this;
}
removeListener(eventName: any, listener: Listener): Contract {
if (!this.provider) { return this; }
let found = false;
let eventFilter = this._getEventFilter(eventName);
this._events = this._events.filter((event) => {
// Make sure this event and listener match
if (event.eventFilter.eventTag !== eventFilter.eventTag) { return true; }
if (event.listener !== listener) { return true; }
this.provider.removeListener(event.eventFilter.filter, event.wrappedListener);
// Already found a matching event in a previous loop
if (found) { return true; }
// REmove this event (returning false filters us out)
found = true;
return false;
});
return this;
}
}
export class ContractFactory {
readonly interface: Interface;
readonly bytecode: string;
readonly signer: Signer;
constructor(contractInterface: Array<string | ParamType> | string | Interface, bytecode: Arrayish | string | { object: string }, signer?: Signer) {
let bytecodeHex: string = null;
// Allow the bytecode object from the Solidity compiler
if (typeof(bytecode) === 'string') {
bytecodeHex = bytecode;
} else if (isArrayish(bytecode)) {
bytecodeHex = hexlify(bytecode);
} else if (typeof(bytecode.object) === 'string') {
bytecodeHex = (<any>bytecode).object;
} else {
errors.throwError('bytecode must be a valid hex string', errors.INVALID_ARGUMENT, { arg: 'bytecode', value: bytecode });
}
// Make sure it is 0x prefixed
if (bytecodeHex.substring(0, 2) !== '0x') {
bytecodeHex = '0x' + bytecodeHex;
}
if (!isHexString(bytecodeHex)) {
errors.throwError('bytecode must be a valid hex string', errors.INVALID_ARGUMENT, { arg: 'bytecode', value: bytecode });
}
if ((bytecodeHex.length % 2) !== 0) {
errors.throwError('bytecode must be valid data (even length)', errors.INVALID_ARGUMENT, { arg: 'bytecode', value: bytecode });
}
defineReadOnly(this, 'bytecode', bytecodeHex);
if (Interface.isInterface(contractInterface)) {
defineReadOnly(this, 'interface', contractInterface);
} else {
defineReadOnly(this, 'interface', new Interface(contractInterface));
}
if (signer && !Signer.isSigner(signer)) {
errors.throwError('invalid signer', errors.INVALID_ARGUMENT, { arg: 'signer', value: null });
}
defineReadOnly(this, 'signer', signer || null);
}
getDeployTransaction(...args: Array<any>): UnsignedTransaction {
let tx: UnsignedTransaction = { };
// If we have 1 additional argument, we allow transaction overrides
if (args.length === this.interface.deployFunction.inputs.length + 1) {
tx = shallowCopy(args.pop());
for (let key in tx) {
if (!allowedTransactionKeys[key]) {
throw new Error('unknown transaction override ' + key);
}
}
}
// Do not allow these to be overridden in a deployment transaction
['data', 'from', 'to'].forEach((key) => {
if ((<any>tx)[key] == null) { return; }
errors.throwError('cannot override ' + key, errors.UNSUPPORTED_OPERATION, { operation: key })
});
// Make sure the call matches the constructor signature
errors.checkArgumentCount(args.length, this.interface.deployFunction.inputs.length, ' in Contract constructor');
// Set the data to the bytecode + the encoded constructor arguments
tx.data = this.interface.deployFunction.encode(this.bytecode, args);
return tx
}
deploy(...args: Array<any>): Promise<Contract> {
// Get the deployment transaction (with optional overrides)
let tx = this.getDeployTransaction(...args);
// Send the deployment transaction
return this.signer.sendTransaction(tx).then((tx) => {
let contract = new Contract(getContractAddress(tx), this.interface, this.signer);
defineReadOnly(contract, 'deployTransaction', tx);
return contract;
});
}
attach(address: string): Contract {
return new Contract(address, this.interface, this.signer);
}
connect(signer: Signer) {
return new ContractFactory(this.interface, this.bytecode, signer);
}
static fromSolidity(compilerOutput: any, signer?: Signer): ContractFactory {
if (compilerOutput == null) {
errors.throwError('missing compiler output', errors.MISSING_ARGUMENT, { argument: 'compilerOutput' });
}
if (typeof(compilerOutput) === 'string') {
compilerOutput = JSON.parse(compilerOutput);
}
let abi = compilerOutput.abi;
let bytecode: any = null;
if (compilerOutput.bytecode) {
bytecode = compilerOutput.bytecode;
} else if (compilerOutput.evm && compilerOutput.evm.bytecode) {
bytecode = compilerOutput.evm.bytecode;
}
return new ContractFactory(abi, bytecode, signer);
}
}

168
src.ts/errors.ts Normal file
View File

@@ -0,0 +1,168 @@
'use strict';
import { version } from './_version';
// Unknown Error
export const UNKNOWN_ERROR = 'UNKNOWN_ERROR';
// Not implemented
export const NOT_IMPLEMENTED = 'NOT_IMPLEMENTED';
// Missing new operator to an object
// - name: The name of the class
export const MISSING_NEW = 'MISSING_NEW';
// Call exception
// - transaction: the transaction
// - address?: the contract address
// - args?: The arguments passed into the function
// - method?: The Solidity method signature
// - errorSignature?: The EIP848 error signature
// - errorArgs?: The EIP848 error parameters
// - reason: The reason (only for EIP848 "Error(string)")
export const CALL_EXCEPTION = 'CALL_EXCEPTION';
// Invalid argument (e.g. value is incompatible with type) to a function:
// - argument: The argument name that was invalid
// - value: The value of the argument
export const INVALID_ARGUMENT = 'INVALID_ARGUMENT';
// Missing argument to a function:
// - count: The number of arguments received
// - expectedCount: The number of arguments expected
export const MISSING_ARGUMENT = 'MISSING_ARGUMENT';
// Too many arguments
// - count: The number of arguments received
// - expectedCount: The number of arguments expected
export const UNEXPECTED_ARGUMENT = 'UNEXPECTED_ARGUMENT';
// Numeric Fault
// - operation: the operation being executed
// - fault: the reason this faulted
export const NUMERIC_FAULT = 'NUMERIC_FAULT';
// Insufficien funds (< value + gasLimit * gasPrice)
// - transaction: the transaction attempted
export const INSUFFICIENT_FUNDS = 'INSUFFICIENT_FUNDS';
// Nonce has already been used
// - transaction: the transaction attempted
export const NONCE_EXPIRED = 'NONCE_EXPIRED';
// The replacement fee for the transaction is too low
// - transaction: the transaction attempted
export const REPLACEMENT_UNDERPRICED = 'REPLACEMENT_UNDERPRICED';
// Unsupported operation
// - operation
export const UNSUPPORTED_OPERATION = 'UNSUPPORTED_OPERATION';
let _permanentCensorErrors = false;
let _censorErrors = false;
// @TODO: Enum
export function throwError(message: string, code: string, params: any): never {
if (_censorErrors) {
throw new Error('unknown error');
}
if (!code) { code = UNKNOWN_ERROR; }
if (!params) { params = {}; }
let messageDetails: Array<string> = [];
Object.keys(params).forEach((key) => {
try {
messageDetails.push(key + '=' + JSON.stringify(params[key]));
} catch (error) {
messageDetails.push(key + '=' + JSON.stringify(params[key].toString()));
}
});
messageDetails.push("version=" + version);
let reason = message;
if (messageDetails.length) {
message += ' (' + messageDetails.join(', ') + ')';
}
// @TODO: Any??
let error: any = new Error(message);
error.reason = reason;
error.code = code
Object.keys(params).forEach(function(key) {
error[key] = params[key];
});
throw error;
}
export function checkNew(self: any, kind: any): void {
if (!(self instanceof kind)) {
throwError('missing new', MISSING_NEW, { name: kind.name });
}
}
export function checkArgumentCount(count: number, expectedCount: number, suffix?: string): void {
if (!suffix) { suffix = ''; }
if (count < expectedCount) {
throwError('missing argument' + suffix, MISSING_ARGUMENT, { count: count, expectedCount: expectedCount });
}
if (count > expectedCount) {
throwError('too many arguments' + suffix, UNEXPECTED_ARGUMENT, { count: count, expectedCount: expectedCount });
}
}
export function setCensorship(censorship: boolean, permanent?: boolean): void {
if (_permanentCensorErrors) {
throwError('error censorship permanent', UNSUPPORTED_OPERATION, { operation: 'setCensorship' });
}
_censorErrors = !!censorship;
_permanentCensorErrors = !!permanent;
}
export function checkNormalize(): void {
try {
// Make sure all forms of normalization are supported
["NFD", "NFC", "NFKD", "NFKC"].forEach((form) => {
try {
"test".normalize(form);
} catch(error) {
throw new Error('missing ' + form);
}
});
if (String.fromCharCode(0xe9).normalize('NFD') !== String.fromCharCode(0x65, 0x0301)) {
throw new Error('broken implementation')
}
} catch (error) {
throwError('platform missing String.prototype.normalize', UNSUPPORTED_OPERATION, { operation: 'String.prototype.normalize', form: error.message });
}
}
const LogLevels: { [ name: string ]: number } = { debug: 1, "default": 2, info: 2, warn: 3, error: 4, off: 5 };
let LogLevel = LogLevels["default"];
export function setLogLevel(logLevel: string): void {
let level = LogLevels[logLevel];
if (level == null) {
warn("invliad log level - " + logLevel);
return;
}
LogLevel = level;
}
function log(logLevel: string, args: Array<any>): void {
if (LogLevel > LogLevels[logLevel]) { return; }
console.log.apply(console, args);
}
export function warn(...args: Array<any>): void {
log("warn", args);
}
export function info(...args: Array<any>): void {
log("info", args);
}

84
src.ts/ethers.ts Normal file
View File

@@ -0,0 +1,84 @@
'use strict';
import { Contract, ContractFactory, VoidSigner } from './contract';
import { Signer } from './abstract-signer';
import { Wallet } from './wallet';
import * as constants from './constants';
import * as errors from './errors';
import * as providers from './providers';
import * as utils from './utils';
import * as wordlists from './wordlists';
////////////////////////
// Compile-Time Constants
// This is empty in node, and used by browserify to inject extra goodies
import { platform } from './utils/shims';
// This is generated by "npm run dist"
import { version } from './_version';
////////////////////////
// Types
import { ContractFunction, ContractTransaction, Event, EventFilter } from './contract';
////////////////////////
// Helper Functions
function getDefaultProvider(network?: utils.Network | string): providers.BaseProvider {
if (network == null) { network = 'homestead'; }
let n = utils.getNetwork(network);
if (!n || !n._defaultProvider) {
errors.throwError('unsupported getDefaultProvider network', errors.UNSUPPORTED_OPERATION, {
operation: 'getDefaultProvider',
network: network
});
}
return n._defaultProvider(providers);
}
////////////////////////
// Exports
export {
Signer,
Wallet,
VoidSigner,
getDefaultProvider,
providers,
Contract,
ContractFactory,
constants,
errors,
utils,
wordlists,
////////////////////////
// Compile-Time Constants
platform,
version,
////////////////////////
// Types
ContractFunction,
ContractTransaction,
Event,
EventFilter
};

6
src.ts/index.ts Normal file
View File

@@ -0,0 +1,6 @@
import * as ethers from './ethers';
export { ethers };
export * from './ethers';

View File

@@ -0,0 +1,173 @@
import { BigNumber } from '../utils/bignumber';
import { isType, setType } from '../utils/properties';
///////////////////////////////
// Imported Types
import { Arrayish } from '../utils/bytes';
import { BigNumberish } from '../utils/bignumber';
import { Network } from '../utils/networks';
import { OnceBlockable } from '../utils/web';
import { Transaction } from '../utils/transaction';
///////////////////////////////
// Exported Types
export interface Block {
hash: string;
parentHash: string;
number: number;
timestamp: number;
nonce: string;
difficulty: number;
gasLimit: BigNumber;
gasUsed: BigNumber;
miner: string;
extraData: string;
transactions: Array<string>;
}
export type BlockTag = string | number;
export type Filter = {
fromBlock?: BlockTag,
toBlock?: BlockTag,
address?: string,
topics?: Array<string | Array<string>>,
}
// @TODO: This is not supported as an EventType yet, as it will
// need some additional work to adhere to the serialized
// format for events. But we want to allow it for getLogs
// for now.
export type FilterByBlock = {
blockHash?: string,
address?: string,
topics?: Array<string | Array<string>>,
}
export interface Log {
blockNumber?: number;
blockHash?: string;
transactionIndex?: number;
removed?: boolean;
transactionLogIndex?: number,
address: string;
data: string;
topics: Array<string>;
transactionHash?: string;
logIndex?: number;
}
export interface TransactionReceipt {
to?: string;
from?: string;
contractAddress?: string,
transactionIndex?: number,
root?: string,
gasUsed?: BigNumber,
logsBloom?: string,
blockHash?: string,
transactionHash?: string,
logs?: Array<Log>,
blockNumber?: number,
confirmations?: number,
cumulativeGasUsed?: BigNumber,
byzantium: boolean,
status?: number
};
export type TransactionRequest = {
to?: string | Promise<string>,
from?: string | Promise<string>,
nonce?: BigNumberish | Promise<BigNumberish>,
gasLimit?: BigNumberish | Promise<BigNumberish>,
gasPrice?: BigNumberish | Promise<BigNumberish>,
data?: Arrayish | Promise<Arrayish>,
value?: BigNumberish | Promise<BigNumberish>,
chainId?: number | Promise<number>,
}
export interface TransactionResponse extends Transaction {
// Only if a transaction has been mined
blockNumber?: number,
blockHash?: string,
timestamp?: number,
confirmations: number,
// Not optional (as it is in Transaction)
from: string;
// The raw transaction
raw?: string,
// This function waits until the transaction has been mined
wait: (confirmations?: number) => Promise<TransactionReceipt>
};
export type EventType = string | Array<string> | Filter;
export type Listener = (...args: Array<any>) => void;
///////////////////////////////
// Exported Abstracts
export abstract class Provider implements OnceBlockable {
abstract getNetwork(): Promise<Network>;
abstract getBlockNumber(): Promise<number>;
abstract getGasPrice(): Promise<BigNumber>;
abstract getBalance(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<BigNumber>;
abstract getTransactionCount(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<number>;
abstract getCode(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<string> ;
abstract getStorageAt(addressOrName: string | Promise<string>, position: BigNumberish | Promise<BigNumberish>, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
abstract sendTransaction(signedTransaction: string | Promise<string>): Promise<TransactionResponse>;
abstract call(transaction: TransactionRequest, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
abstract estimateGas(transaction: TransactionRequest): Promise<BigNumber>;
abstract getBlock(blockHashOrBlockTag: BlockTag | string | Promise<BlockTag | string>, includeTransactions?: boolean): Promise<Block>;
abstract getTransaction(transactionHash: string): Promise<TransactionResponse>;
abstract getTransactionReceipt(transactionHash: string): Promise<TransactionReceipt>;
abstract getLogs(filter: Filter | FilterByBlock): Promise<Array<Log>>;
abstract resolveName(name: string | Promise<string>): Promise<string>;
abstract lookupAddress(address: string | Promise<string>): Promise<string>;
abstract on(eventName: EventType, listener: Listener): Provider;
abstract once(eventName: EventType, listener: Listener): Provider;
abstract listenerCount(eventName?: EventType): number;
abstract listeners(eventName: EventType): Array<Listener>;
abstract removeAllListeners(eventName: EventType): Provider;
abstract removeListener(eventName: EventType, listener: Listener): Provider;
// @TODO: This *could* be implemented here, but would pull in events...
abstract waitForTransaction(transactionHash: string, timeout?: number): Promise<TransactionReceipt>;
constructor() {
setType(this, 'Provider');
}
static isProvider(value: any): value is Provider {
return isType(value, 'Provider');
}
// readonly inherits: (child: any) => void;
}
//defineReadOnly(Signer, 'inherits', inheritable(Abstract));

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,351 @@
import { BaseProvider } from './base-provider';
import { hexlify, hexStripZeros } from '../utils/bytes';
import { defineReadOnly } from '../utils/properties';
import { fetchJson } from '../utils/web';
import * as errors from '../errors';
///////////////////////////////
// Imported Types
import { BlockTag, TransactionRequest, TransactionResponse } from './abstract-provider';
import { Networkish } from '../utils/networks';
///////////////////////////////
// The transaction has already been sanitized by the calls in Provider
function getTransactionString(transaction: TransactionRequest): string {
var result = [];
for (var key in transaction) {
if ((<any>transaction)[key] == null) { continue; }
var value = hexlify((<any>transaction)[key]);
if ((<any>{ gasLimit: true, gasPrice: true, nonce: true, value: true })[key]) {
value = hexStripZeros(value);
}
result.push(key + '=' + value);
}
return result.join('&');
}
function getResult(result: { status?: number, message?: string, result?: any }): any {
// getLogs, getHistory have weird success responses
if (result.status == 0 && (result.message === 'No records found' || result.message === 'No transactions found')) {
return result.result;
}
if (result.status != 1 || result.message != 'OK') {
// @TODO: not any
var error: any = new Error('invalid response');
error.result = JSON.stringify(result);
throw error;
}
return result.result;
}
function getJsonResult(result: { jsonrpc: string, result?: any, error?: { code?: number, data?: any, message?: string} } ): any {
if (result.jsonrpc != '2.0') {
// @TODO: not any
let error: any = new Error('invalid response');
error.result = JSON.stringify(result);
throw error;
}
if (result.error) {
// @TODO: not any
let error: any = new Error(result.error.message || 'unknown error');
if (result.error.code) { error.code = result.error.code; }
if (result.error.data) { error.data = result.error.data; }
throw error;
}
return result.result;
}
// The blockTag was normalized as a string by the Provider pre-perform operations
function checkLogTag(blockTag: string): number | "latest" {
if (blockTag === 'pending') { throw new Error('pending not supported'); }
if (blockTag === 'latest') { return blockTag; }
return parseInt(blockTag.substring(2), 16);
}
export class EtherscanProvider extends BaseProvider{
readonly baseUrl: string;
readonly apiKey: string;
constructor(network?: Networkish, apiKey?: string) {
super(network);
errors.checkNew(this, EtherscanProvider);
let name = 'invalid';
if (this.network) { name = this.network.name; }
let baseUrl = null;
switch(name) {
case 'homestead':
baseUrl = 'https://api.etherscan.io';
break;
case 'ropsten':
baseUrl = 'https://api-ropsten.etherscan.io';
break;
case 'rinkeby':
baseUrl = 'https://api-rinkeby.etherscan.io';
break;
case 'kovan':
baseUrl = 'https://api-kovan.etherscan.io';
break;
case 'goerli':
baseUrl = 'https://api-goerli.etherscan.io';
break;
default:
throw new Error('unsupported network');
}
defineReadOnly(this, 'baseUrl', baseUrl);
defineReadOnly(this, 'apiKey', apiKey);
}
perform(method: string, params: any) {
let url = this.baseUrl;
let apiKey = '';
if (this.apiKey) { apiKey += '&apikey=' + this.apiKey; }
let get = (url: string, procFunc?: (value: any) => any) => {
return fetchJson(url, null, procFunc || getJsonResult).then((result) => {
this.emit('debug', {
action: 'perform',
request: url,
response: result,
provider: this
});
return result;
});
};
switch (method) {
case 'getBlockNumber':
url += '/api?module=proxy&action=eth_blockNumber' + apiKey;
return get(url);
case 'getGasPrice':
url += '/api?module=proxy&action=eth_gasPrice' + apiKey;
return get(url);
case 'getBalance':
// Returns base-10 result
url += '/api?module=account&action=balance&address=' + params.address;
url += '&tag=' + params.blockTag + apiKey;
return get(url, getResult);
case 'getTransactionCount':
url += '/api?module=proxy&action=eth_getTransactionCount&address=' + params.address;
url += '&tag=' + params.blockTag + apiKey;
return get(url);
case 'getCode':
url += '/api?module=proxy&action=eth_getCode&address=' + params.address;
url += '&tag=' + params.blockTag + apiKey;
return get(url, getJsonResult);
case 'getStorageAt':
url += '/api?module=proxy&action=eth_getStorageAt&address=' + params.address;
url += '&position=' + params.position;
url += '&tag=' + params.blockTag + apiKey;
return get(url, getJsonResult);
case 'sendTransaction':
url += '/api?module=proxy&action=eth_sendRawTransaction&hex=' + params.signedTransaction;
url += apiKey;
return get(url).catch((error) => {
if (error.responseText) {
// "Insufficient funds. The account you tried to send transaction from does not have enough funds. Required 21464000000000 and got: 0"
if (error.responseText.toLowerCase().indexOf('insufficient funds') >= 0) {
errors.throwError('insufficient funds', errors.INSUFFICIENT_FUNDS, { });
}
// "Transaction with the same hash was already imported."
if (error.responseText.indexOf('same hash was already imported') >= 0) {
errors.throwError('nonce has already been used', errors.NONCE_EXPIRED, { });
}
// "Transaction gas price is too low. There is another transaction with same nonce in the queue. Try increasing the gas price or incrementing the nonce."
if (error.responseText.indexOf('another transaction with same nonce') >= 0) {
errors.throwError('replacement fee too low', errors.REPLACEMENT_UNDERPRICED, { });
}
}
throw error;
});
case 'getBlock':
if (params.blockTag) {
url += '/api?module=proxy&action=eth_getBlockByNumber&tag=' + params.blockTag;
if (params.includeTransactions) {
url += '&boolean=true';
} else {
url += '&boolean=false';
}
url += apiKey;
return get(url);
}
throw new Error('getBlock by blockHash not implmeneted');
case 'getTransaction':
url += '/api?module=proxy&action=eth_getTransactionByHash&txhash=' + params.transactionHash;
url += apiKey;
return get(url);
case 'getTransactionReceipt':
url += '/api?module=proxy&action=eth_getTransactionReceipt&txhash=' + params.transactionHash;
url += apiKey;
return get(url);
case 'call': {
let transaction = getTransactionString(params.transaction);
if (transaction) { transaction = '&' + transaction; }
url += '/api?module=proxy&action=eth_call' + transaction;
//url += '&tag=' + params.blockTag + apiKey;
if (params.blockTag !== 'latest') {
throw new Error('EtherscanProvider does not support blockTag for call');
}
url += apiKey;
return get(url);
}
case 'estimateGas': {
let transaction = getTransactionString(params.transaction);
if (transaction) { transaction = '&' + transaction; }
url += '/api?module=proxy&action=eth_estimateGas&' + transaction;
url += apiKey;
return get(url);
}
case 'getLogs':
url += '/api?module=logs&action=getLogs';
try {
if (params.filter.fromBlock) {
url += '&fromBlock=' + checkLogTag(params.filter.fromBlock);
}
if (params.filter.toBlock) {
url += '&toBlock=' + checkLogTag(params.filter.toBlock);
}
if (params.filter.blockHash) {
try {
errors.throwError("Etherscan does not support blockHash filters", errors.UNSUPPORTED_OPERATION, {
operation: "getLogs(blockHash)"
});
} catch (error) {
return Promise.reject(error);
}
}
if (params.filter.address) {
url += '&address=' + params.filter.address;
}
// @TODO: We can handle slightly more complicated logs using the logs API
if (params.filter.topics && params.filter.topics.length > 0) {
if (params.filter.topics.length > 1) {
throw new Error('unsupported topic format');
}
let topic0 = params.filter.topics[0];
if (typeof(topic0) !== 'string' || topic0.length !== 66) {
throw new Error('unsupported topic0 format');
}
url += '&topic0=' + topic0;
}
} catch (error) {
return Promise.reject(error);
}
url += apiKey;
var self = this;
return get(url, getResult).then(function(logs: Array<any>) {
var txs: { [hash: string]: string } = {};
var seq = Promise.resolve();
logs.forEach(function(log) {
seq = seq.then(function() {
if (log.blockHash != null) { return null; }
log.blockHash = txs[log.transactionHash];
if (log.blockHash == null) {
return self.getTransaction(log.transactionHash).then(function(tx) {
txs[log.transactionHash] = tx.blockHash;
log.blockHash = tx.blockHash;
return null;
});
}
return null;
});
});
return seq.then(function() {
return logs;
});
});
case 'getEtherPrice':
if (this.network.name !== 'homestead') { return Promise.resolve(0.0); }
url += '/api?module=stats&action=ethprice';
url += apiKey;
return get(url, getResult).then(function(result) {
return parseFloat(result.ethusd);
});
default:
break;
}
return super.perform(method, params);
}
// @TODO: Allow startBlock and endBlock to be Promises
getHistory(addressOrName: string | Promise<string>, startBlock?: BlockTag, endBlock?: BlockTag): Promise<Array<TransactionResponse>> {
let url = this.baseUrl;
let apiKey = '';
if (this.apiKey) { apiKey += '&apikey=' + this.apiKey; }
if (startBlock == null) { startBlock = 0; }
if (endBlock == null) { endBlock = 99999999; }
return this.resolveName(addressOrName).then((address) => {
url += '/api?module=account&action=txlist&address=' + address;
url += '&startblock=' + startBlock;
url += '&endblock=' + endBlock;
url += '&sort=asc' + apiKey;
return fetchJson(url, null, getResult).then((result: Array<any>) => {
this.emit('debug', {
action: 'getHistory',
request: url,
response: result,
provider: this
});
var output: Array<TransactionResponse> = [];
result.forEach((tx) => {
['contractAddress', 'to'].forEach(function(key) {
if (tx[key] == '') { delete tx[key]; }
});
if (tx.creates == null && tx.contractAddress != null) {
tx.creates = tx.contractAddress;
}
let item = BaseProvider.checkTransactionResponse(tx);
if (tx.timeStamp) { item.timestamp = parseInt(tx.timeStamp); }
output.push(item);
});
return output;
});
});
}
}

View File

@@ -0,0 +1,106 @@
'use strict';
import { BaseProvider } from './base-provider';
// Imported Types
import { Network } from '../utils/networks';
import * as errors from '../errors';
// Returns:
// - true is all networks match
// - false if any network is null
// - throws if any 2 networks do not match
function checkNetworks(networks: Array<Network>): boolean {
var result = true;
let check: Network = null;
networks.forEach((network) => {
// Null
if (network == null) {
result = false;
return;
}
// Have nothing to compre to yet
if (check == null) {
check = network;
return;
}
// Matches!
if (check.name === network.name &&
check.chainId === network.chainId &&
((check.ensAddress === network.ensAddress) ||
(check.ensAddress == null && network.ensAddress == null))) { return; }
errors.throwError(
'provider mismatch',
errors.INVALID_ARGUMENT,
{ arg: 'networks', value: networks }
);
});
return result;
}
export class FallbackProvider extends BaseProvider {
private _providers: Array<BaseProvider>;
constructor(providers: Array<BaseProvider>) {
if (providers.length === 0) { throw new Error('no providers'); }
// All networks are ready, we can know the network for certain
let ready = checkNetworks(providers.map((p) => p.network));
if (ready) {
super(providers[0].network);
} else {
// The network won't be known until all child providers know
let ready = Promise.all(providers.map((p) => p.getNetwork())).then((networks) => {
if (!checkNetworks(networks)) {
errors.throwError('getNetwork returned null', errors.UNKNOWN_ERROR, { })
}
return networks[0];
});
super(ready);
}
errors.checkNew(this, FallbackProvider);
// Preserve a copy, so we don't get mutated
this._providers = providers.slice(0);
}
get providers(): Array<BaseProvider> {
// Return a copy, so we don't get mutated
return this._providers.slice(0);
}
perform(method: string, params: { [name: string]: any }): any {
// Creates a copy of the providers array
var providers = this.providers;
return new Promise((resolve, reject) => {
var firstError: Error = null;
function next() {
if (!providers.length) {
reject(firstError);
return;
}
var provider = providers.shift();
provider.perform(method, params).then((result) => {
return resolve(result);
}).catch((error) => {
if (!firstError) { firstError = error; }
setTimeout(next, 0);
});
}
next();
});
}
}

77
src.ts/providers/index.ts Normal file
View 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
};

View File

@@ -0,0 +1,80 @@
'use strict';
import { JsonRpcProvider, JsonRpcSigner } from './json-rpc-provider';
import { isHexString } from "../utils/bytes";
import { getNetwork } from '../utils/networks';
import { defineReadOnly } from '../utils/properties';
// Imported Types
import { Networkish } from '../utils/networks';
import * as errors from '../errors';
const defaultProjectId = "7d0d81d0919f4f05b9ab6634be01ee73";
export class InfuraProvider extends JsonRpcProvider {
readonly apiAccessToken: string;
readonly projectId: string;
constructor(network?: Networkish, projectId?: string) {
let standard = getNetwork((network == null) ? 'homestead': network);
if (projectId == null) { projectId = defaultProjectId; }
let host = null;
switch(standard.name) {
case 'homestead':
host = 'mainnet.infura.io';
break;
case 'ropsten':
host = 'ropsten.infura.io';
break;
case 'rinkeby':
host = 'rinkeby.infura.io';
break;
case 'goerli':
host = 'goerli.infura.io';
break;
case 'kovan':
host = 'kovan.infura.io';
break;
default:
errors.throwError('unsupported network', errors.INVALID_ARGUMENT, {
argument: "network",
value: network
});
}
// New-style Project ID
if (isHexString("0x" + projectId, 16)) {
super('https://' + host + '/v3/' + projectId, standard);
defineReadOnly(this, 'apiAccessToken', null);
defineReadOnly(this, 'projectId', projectId);
// Legacy API Access Token
} else {
errors.warn("The legacy INFURA apiAccesToken API is deprecated; please upgrade to a Project ID instead (see INFURA dshboard; https://infura.io)");
super('https://' + host + '/' + projectId, standard);
defineReadOnly(this, 'apiAccessToken', projectId);
defineReadOnly(this, 'projectId', null);
}
errors.checkNew(this, InfuraProvider);
}
protected _startPending(): void {
errors.warn('WARNING: INFURA does not support pending filters');
}
getSigner(address?: string): JsonRpcSigner {
return errors.throwError(
'INFURA does not support signing',
errors.UNSUPPORTED_OPERATION,
{ operation: 'getSigner' }
);
}
listAccounts(): Promise<Array<string>> {
return Promise.resolve([]);
}
}

View File

@@ -0,0 +1,75 @@
"use strict";
import net from 'net';
import { JsonRpcProvider } from './json-rpc-provider';
import { defineReadOnly } from '../utils/properties';
// Imported Types
import { Networkish } from '../utils/networks';
import * as errors from '../errors';
export class IpcProvider extends JsonRpcProvider {
readonly path: string;
constructor(path: string, network?: Networkish) {
if (path == null) {
errors.throwError('missing path', errors.MISSING_ARGUMENT, {
argument: 'path'
});
}
super('ipc://' + path, network);
errors.checkNew(this, IpcProvider);
defineReadOnly(this, 'path', path);
}
// @TODO: Create a connection to the IPC path and use filters instead of polling for block
send(method: string, params: any): Promise<any> {
// This method is very simple right now. We create a new socket
// connection each time, which may be slower, but the main
// advantage we are aiming for now is security. This simplifies
// multiplexing requests (since we do not need to multiplex).
let payload = JSON.stringify({
method: method,
params: params,
id: 42,
jsonrpc: "2.0"
});
return new Promise((resolve, reject) => {
let response = Buffer.alloc(0);
let stream = net.connect(this.path);
stream.on('data', (data) => {
response = Buffer.concat([ response, data ]);
});
stream.on("end", () => {
try {
resolve(JSON.parse(response.toString('utf8')).result);
// @TODO: Better pull apart the error
stream.destroy();
} catch (error) {
reject(error);
stream.destroy();
}
});
stream.on('error', (error) => {
reject(error);
stream.destroy();
});
stream.write(payload);
stream.end();
});
}
}

View File

@@ -0,0 +1,427 @@
'use strict';
// See: https://github.com/ethereum/wiki/wiki/JSON-RPC
import { BaseProvider } from './base-provider';
import { Signer } from '../abstract-signer';
import * as errors from '../errors';
import { getAddress } from '../utils/address';
import { BigNumber } from '../utils/bignumber';
import { hexlify, hexStripZeros } from '../utils/bytes';
import { getNetwork } from '../utils/networks';
import { checkProperties, defineReadOnly, resolveProperties, shallowCopy } from '../utils/properties';
import { toUtf8Bytes } from '../utils/utf8';
import { fetchJson, poll } from '../utils/web';
// Imported Types
import { Arrayish } from '../utils/bytes';
import { Network, Networkish } from '../utils/networks';
import { ConnectionInfo } from '../utils/web';
import { BlockTag, TransactionRequest, TransactionResponse } from '../providers/abstract-provider';
function timer(timeout: number): Promise<any> {
return new Promise(function(resolve) {
setTimeout(function() {
resolve();
}, timeout);
});
}
function getResult(payload: { error?: { code?: number, data?: any, message?: string }, result?: any }): any {
if (payload.error) {
// @TODO: not any
let error: any = new Error(payload.error.message);
error.code = payload.error.code;
error.data = payload.error.data;
throw error;
}
return payload.result;
}
function getLowerCase(value: string): string {
if (value) { return value.toLowerCase(); }
return value;
}
const _constructorGuard = {};
// Some environments (Trust Wallet and company) use a global map
// to track JSON-RPC ID, so we try to keep IDs unique across all
// connections. See #489.
let _nextId = 42;
export class JsonRpcSigner extends Signer {
readonly provider: JsonRpcProvider;
private _index: number;
private _address: string;
constructor(constructorGuard: any, provider: JsonRpcProvider, addressOrIndex?: string | number) {
super();
errors.checkNew(this, JsonRpcSigner);
if (constructorGuard !== _constructorGuard) {
throw new Error('do not call the JsonRpcSigner constructor directly; use provider.getSigner');
}
defineReadOnly(this, 'provider', provider);
// Statically attach to a given address
if (addressOrIndex) {
if (typeof(addressOrIndex) === 'string') {
defineReadOnly(this, '_address', getAddress(addressOrIndex));
} else if (typeof(addressOrIndex) === 'number') {
defineReadOnly(this, '_index', addressOrIndex);
} else {
errors.throwError('invalid address or index', errors.INVALID_ARGUMENT, { argument: 'addressOrIndex', value: addressOrIndex });
}
} else {
defineReadOnly(this, '_index', 0);
}
}
getAddress(): Promise<string> {
if (this._address) {
return Promise.resolve(this._address);
}
return this.provider.send('eth_accounts', []).then((accounts) => {
if (accounts.length <= this._index) {
errors.throwError('unknown account #' + this._index, errors.UNSUPPORTED_OPERATION, { operation: 'getAddress' });
}
this._address = getAddress(accounts[this._index]);
return this._address;
});
}
getBalance(blockTag?: BlockTag): Promise<BigNumber> {
return this.provider.getBalance(this.getAddress(), blockTag);
}
getTransactionCount(blockTag?: BlockTag): Promise<number> {
return this.provider.getTransactionCount(this.getAddress(), blockTag);
}
sendUncheckedTransaction(transaction: TransactionRequest): Promise<string> {
transaction = shallowCopy(transaction);
let fromAddress = this.getAddress().then((address) => {
if (address) { address = address.toLowerCase(); }
return address;
});
// The JSON-RPC for eth_sendTransaction uses 90000 gas; if the user
// wishes to use this, it is easy to specify explicitly, otherwise
// we look it up for them.
if (transaction.gasLimit == null) {
let estimate = shallowCopy(transaction);
estimate.from = fromAddress;
transaction.gasLimit = this.provider.estimateGas(estimate);
}
return Promise.all([
resolveProperties(transaction),
fromAddress
]).then((results) => {
let tx = results[0];
let hexTx = JsonRpcProvider.hexlifyTransaction(tx);
hexTx.from = results[1];
return this.provider.send('eth_sendTransaction', [ hexTx ]).then((hash) => {
return hash;
}, (error) => {
if (error.responseText) {
// See: JsonRpcProvider.sendTransaction (@TODO: Expose a ._throwError??)
if (error.responseText.indexOf('insufficient funds') >= 0) {
errors.throwError('insufficient funds', errors.INSUFFICIENT_FUNDS, {
transaction: tx
});
}
if (error.responseText.indexOf('nonce too low') >= 0) {
errors.throwError('nonce has already been used', errors.NONCE_EXPIRED, {
transaction: tx
});
}
if (error.responseText.indexOf('replacement transaction underpriced') >= 0) {
errors.throwError('replacement fee too low', errors.REPLACEMENT_UNDERPRICED, {
transaction: tx
});
}
}
throw error;
});
});
}
sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse> {
return this.sendUncheckedTransaction(transaction).then((hash) => {
return poll(() => {
return this.provider.getTransaction(hash).then((tx: TransactionResponse) => {
if (tx === null) { return undefined; }
return this.provider._wrapTransaction(tx, hash);
});
}, { fastRetry: 250, onceBlock: this.provider }).catch((error: Error) => {
(<any>error).transactionHash = hash;
throw error;
});
});
}
signMessage(message: Arrayish | string): Promise<string> {
let data = ((typeof(message) === 'string') ? toUtf8Bytes(message): message);
return this.getAddress().then((address) => {
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign
return this.provider.send('eth_sign', [ address.toLowerCase(), hexlify(data) ]);
});
}
unlock(password: string): Promise<boolean> {
let provider = this.provider;
return this.getAddress().then(function(address) {
return provider.send('personal_unlockAccount', [ address.toLowerCase(), password, null ]);
});
}
}
const allowedTransactionKeys: { [ key: string ]: boolean } = {
chainId: true, data: true, gasLimit: true, gasPrice:true, nonce: true, to: true, value: true
}
export class JsonRpcProvider extends BaseProvider {
readonly connection: ConnectionInfo;
private _pendingFilter: Promise<number>;
constructor(url?: ConnectionInfo | string, network?: Networkish) {
// One parameter, but it is a network name, so swap it with the URL
if (typeof(url) === 'string') {
if (network === null && getNetwork(url)) {
network = url;
url = null;
}
}
if (network) {
// The network has been specified explicitly, we can use it
super(network);
} else {
// The network is unknown, query the JSON-RPC for it
let ready: Promise<Network> = new Promise((resolve, reject) => {
setTimeout(() => {
this.send('net_version', [ ]).then((result) => {
return resolve(getNetwork(parseInt(result)));
}).catch((error) => {
reject(error);
});
});
});
super(ready);
}
errors.checkNew(this, JsonRpcProvider);
// Default URL
if (!url) { url = 'http://localhost:8545'; }
if (typeof(url) === 'string') {
this.connection = {
url: url
};
} else {
this.connection = url;
}
}
getSigner(addressOrIndex?: string | number): JsonRpcSigner {
return new JsonRpcSigner(_constructorGuard, this, addressOrIndex);
}
listAccounts(): Promise<Array<string>> {
return this.send('eth_accounts', []).then((accounts: Array<string>) => {
return accounts.map((a) => getAddress(a));
});
}
send(method: string, params: any): Promise<any> {
let request = {
method: method,
params: params,
id: (_nextId++),
jsonrpc: "2.0"
};
return fetchJson(this.connection, JSON.stringify(request), getResult).then((result) => {
this.emit('debug', {
action: 'send',
request: request,
response: result,
provider: this
});
return result;
});
}
perform(method: string, params: any): Promise<any> {
switch (method) {
case 'getBlockNumber':
return this.send('eth_blockNumber', []);
case 'getGasPrice':
return this.send('eth_gasPrice', []);
case 'getBalance':
return this.send('eth_getBalance', [ getLowerCase(params.address), params.blockTag ]);
case 'getTransactionCount':
return this.send('eth_getTransactionCount', [ getLowerCase(params.address), params.blockTag ]);
case 'getCode':
return this.send('eth_getCode', [ getLowerCase(params.address), params.blockTag ]);
case 'getStorageAt':
return this.send('eth_getStorageAt', [ getLowerCase(params.address), params.position, params.blockTag ]);
case 'sendTransaction':
return this.send('eth_sendRawTransaction', [ params.signedTransaction ]).catch((error) => {
if (error.responseText) {
// "insufficient funds for gas * price + value"
if (error.responseText.indexOf('insufficient funds') > 0) {
errors.throwError('insufficient funds', errors.INSUFFICIENT_FUNDS, { });
}
// "nonce too low"
if (error.responseText.indexOf('nonce too low') > 0) {
errors.throwError('nonce has already been used', errors.NONCE_EXPIRED, { });
}
// "replacement transaction underpriced"
if (error.responseText.indexOf('replacement transaction underpriced') > 0) {
errors.throwError('replacement fee too low', errors.REPLACEMENT_UNDERPRICED, { });
}
}
throw error;
});
case 'getBlock':
if (params.blockTag) {
return this.send('eth_getBlockByNumber', [ params.blockTag, !!params.includeTransactions ]);
} else if (params.blockHash) {
return this.send('eth_getBlockByHash', [ params.blockHash, !!params.includeTransactions ]);
}
return Promise.reject(new Error('invalid block tag or block hash'));
case 'getTransaction':
return this.send('eth_getTransactionByHash', [ params.transactionHash ]);
case 'getTransactionReceipt':
return this.send('eth_getTransactionReceipt', [ params.transactionHash ]);
case 'call':
return this.send('eth_call', [ JsonRpcProvider.hexlifyTransaction(params.transaction, { from: true }), params.blockTag ]);
case 'estimateGas':
return this.send('eth_estimateGas', [ JsonRpcProvider.hexlifyTransaction(params.transaction, { from: true }) ]);
case 'getLogs':
if (params.filter && params.filter.address != null) {
params.filter.address = getLowerCase(params.filter.address);
}
return this.send('eth_getLogs', [ params.filter ]);
default:
break;
}
errors.throwError(method + ' not implemented', errors.NOT_IMPLEMENTED, { operation: method });
return null;
}
protected _startPending(): void {
if (this._pendingFilter != null) { return; }
let self = this;
let pendingFilter: Promise<number> = this.send('eth_newPendingTransactionFilter', []);
this._pendingFilter = pendingFilter;
pendingFilter.then(function(filterId) {
function poll() {
self.send('eth_getFilterChanges', [ filterId ]).then(function(hashes: Array<string>) {
if (self._pendingFilter != pendingFilter) { return null; }
let seq = Promise.resolve();
hashes.forEach(function(hash) {
// @TODO: This should be garbage collected at some point... How? When?
self._emitted['t:' + hash.toLowerCase()] = 'pending';
seq = seq.then(function() {
return self.getTransaction(hash).then(function(tx) {
self.emit('pending', tx);
return null;
});
});
});
return seq.then(function() {
return timer(1000);
});
}).then(function() {
if (self._pendingFilter != pendingFilter) {
self.send('eth_uninstallFilter', [ filterId ]);
return;
}
setTimeout(function() { poll(); }, 0);
return null;
}).catch((error: Error) => { });
}
poll();
return filterId;
}).catch((error: Error) => { });
}
protected _stopPending(): void {
this._pendingFilter = null;
}
// Convert an ethers.js transaction into a JSON-RPC transaction
// - gasLimit => gas
// - All values hexlified
// - All numeric values zero-striped
// NOTE: This allows a TransactionRequest, but all values should be resolved
// before this is called
static hexlifyTransaction(transaction: TransactionRequest, allowExtra?: { [key: string]: boolean }): { [key: string]: string } {
// Check only allowed properties are given
let allowed = shallowCopy(allowedTransactionKeys);
if (allowExtra) {
for (let key in allowExtra) {
if (allowExtra[key]) { allowed[key] = true; }
}
}
checkProperties(transaction, allowed);
let result: { [key: string]: string } = {};
// Some nodes (INFURA ropsten; INFURA mainnet is fine) don't like leading zeros.
['gasLimit', 'gasPrice', 'nonce', 'value'].forEach(function(key) {
if ((<any>transaction)[key] == null) { return; }
let value = hexStripZeros(hexlify((<any>transaction)[key]));
if (key === 'gasLimit') { key = 'gas'; }
result[key] = value;
});
['from', 'to', 'data'].forEach(function(key) {
if ((<any>transaction)[key] == null) { return; }
result[key] = hexlify((<any>transaction)[key]);
});
return result;
}
}

View 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
View 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
View File

@@ -0,0 +1 @@
export { }

26
src.ts/shims/hmac.ts Normal file
View 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
View 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
View 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)
}

View File

@@ -0,0 +1,41 @@
'use strict';
import { arrayify } from '../utils/bytes';
import { defineReadOnly } from '../utils/properties';
let crypto: any = (<any>global).crypto || (<any>global).msCrypto;
if (!crypto || !crypto.getRandomValues) {
console.log('WARNING: Missing strong random number source; using weak randomBytes');
crypto = {
getRandomValues: function(buffer: Uint8Array): Uint8Array {
for (var round = 0; round < 20; round++) {
for (var i = 0; i < buffer.length; i++) {
if (round) {
buffer[i] ^= Math.trunc(256 * Math.random());
} else {
buffer[i] = Math.trunc(256 * Math.random());
}
}
}
return buffer;
},
_weakCrypto: true
};
}
export function randomBytes(length: number): Uint8Array {
if (length <= 0 || length > 1024 || parseInt(String(length)) != length) {
throw new Error('invalid length');
}
var result = new Uint8Array(length);
crypto.getRandomValues(result);
return arrayify(result);
};
if (crypto._weakCrypto === true) {
defineReadOnly(randomBytes, '_weakCrypto', true);
}

Some files were not shown because too many files have changed in this diff Show More