Compare commits

..

302 Commits

Author SHA1 Message Date
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
268 changed files with 50960 additions and 41818 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"
}
};

3
.gitignore vendored
View File

@@ -3,5 +3,8 @@ obsolete/
.test-account.key
.account.key
.DS_Store
.tmp/
docs/build/doctrees/
docs/build/html/_sources/
dist/types/shims/
shims/*.d.ts

View File

@@ -1,8 +1,13 @@
Gruntfile.js
Gruntfile-test.js
# Config files for testing and linting
.eslintrc.js
.travis.yml
# If you need these, checkout GitHub
examples/
# 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/

View File

@@ -3,6 +3,7 @@ language: node_js
node_js:
- "6"
- "8"
- "10"
env:
- RUN_PHANTOMJS=0
@@ -12,3 +13,5 @@ matrix:
exclude:
- node_js: "6"
env: RUN_PHANTOMJS=1
- node_js: "8"
env: RUN_PHANTOMJS=1

View File

@@ -1,34 +0,0 @@
'use strict';
var fs = require('fs');
// 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.writeFileSync('./tests/dist/tests.json', JSON.stringify(data));
module.exports = function(grunt) {
grunt.initConfig({
browserify: {
dist: {
files: {
'tests/dist/tests.js': './tests/browser.js',
},
options: {
browserifyOptions: {
standalone: 'tests',
},
},
},
},
});
grunt.loadNpmTasks('grunt-browserify');
grunt.registerTask('dist', ['browserify']);
};

View File

@@ -1,170 +0,0 @@
'use strict';
var through = require('through');
// 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 = {};";
// We already have a random Uint8Array browser/node safe source
// @TODO: Use path construction instead of ../..
var brorand = "var randomBytes = require('../../utils').randomBytes; module.exports = function(length) { return randomBytes(length); };";
var transforms = {
'ethers.js/package.json': JSON.stringify({ version: version }),
// 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
"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,
// Used by sha3 if it exists; (so make it no exist)
"process/.*": undef,
};
var modified = {};
var unmodified = {};
function transformFile(path) {
for (var pattern in transforms) {
if (path.match(new RegExp('/' + pattern + '$'))) {
modified[pattern] = true;
return transforms[pattern];
}
}
return null;
}
function transform(path, options) {
var data = '';
return through(function(chunk) {
data += chunk;
}, function () {
var transformed = transformFile(path);
if (transformed != null) {
console.log('Transformed:' + path);
data = transformed;
} else {
unmodified[path] = true;
}
this.queue(data);
this.queue(null);
});
}
var inflight = 0;
function preBundle(bundle) {
inflight++;
}
function postBundle(error, source, next) {
if (error) {
console.log(error);
// } else {
// source = source.toString();
}
inflight--
if (inflight === 0) {
// List all files that passed though unchanged
var preserved = {};
Object.keys(unmodified).forEach(function(filename) {
var match = filename.match(/(node_modules.*)$/);
if (!match) {
match = filename.match(/(ethers\.js.*)$/);
}
if (!match) {
match = [null, filename];
}
preserved[match[1]] = true;
});
preserved = Object.keys(preserved);
preserved.sort();
console.log('Preserved:');
preserved.forEach(function(path) {
console.log(' ', path);
});
// Make sure there were no replacement patterns that went unused
var skipped = [];
for (var key in transforms) {
if (!modified[key]) { skipped.push(key); }
}
skipped.sort();
if (skipped.length) {
console.log('Unused Patterns:');
skipped.forEach(function(pattern) {
console.log(' ', pattern);
});
}
}
next(error, source);
}
module.exports = function(grunt) {
grunt.initConfig({
browserify: {
dist: {
files: {
'dist/ethers.js': './index.js',
'dist/ethers-contracts.js': './contracts/index.js',
'dist/ethers-providers.js': './providers/index.js',
'dist/ethers-utils.js': './utils/index.js',
'dist/ethers-wallet.js': './wallet/index.js',
},
options: {
transform: [
[ transform, { global: true } ],
],
browserifyOptions: {
standalone: 'ethers',
},
preBundleCB: preBundle,
postBundleCB: postBundle
},
},
},
uglify: {
dist: {
files: {
'dist/ethers.min.js' : [ './dist/ethers.js' ],
'dist/ethers-contracts.min.js' : [ './dist/ethers-contracts.js' ],
'dist/ethers-providers.min.js' : [ './dist/ethers-providers.js' ],
'dist/ethers-utils.min.js' : [ './dist/ethers-utils.js' ],
'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

@@ -3,23 +3,30 @@ ethers.js
[![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 client, **safe** and sound
- Import and export **JSON wallets** (Geth, Parity and crowdsale) and brain wallets
- Import and export BIP 39 **mnemonic phrases** (12 word backup phrases) and **HD Wallets**
- Meta-classes create JavaScript objects from any contract ABI
- Import and export **JSON wallets** (Geth, Parity and crowdsale)
- Import and export BIP 39 **mnemonic phrases** (12 word backup phrases) and **HD Wallets** (English, Italian, Japanese, Korean, Simplified Chinese, Traditional Chinese; more coming soon)
- Meta-classes create JavaScript objects from any contract ABI, including **ABIv2** and **Human-Readable ABI**
- Connect to Ethereum nodes over [JSON-RPC](https://github.com/ethereum/wiki/wiki/JSON-RPC), [INFURA](https://infura.io), [Etherscan](https://etherscan.io), or [MetaMask](https://metamask.io)
- ENS names are first-class citizens; they can almost always used instead of Ethereum addresses
- **Tiny** (~79kb compressed; 242kb uncompressed)
- **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
- 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
Keep Updated
------------
For the latest news and advisories, please follow [@ethersproject](https://twitter.com/ethersproject) on Twitter as well as this GitHub project.
Installing
----------
@@ -27,7 +34,7 @@ To use in a browser:
```html
<script charset="utf-8"
src="https://cdn.ethers.io/scripts/ethers-v2.min.js"
src="https://cdn.ethers.io/scripts/ethers-v4.min.js"
type="text/javascript">
</script>
```
@@ -39,15 +46,50 @@ To use in [node.js](https://nodejs.org/):
```
Documentation
-------------
Browse the [API Documentation](https://docs.ethers.io/ethers.js/html/) online.
Documentation is generated using [Sphinx](http://www.sphinx-doc.org) and can be browsed locally from the /docs/build/html directory.
To fork and submit pull requests to the documentation, please see the
[documentation repository](https://github.com/ethers-io/documentation).
Related Libraries
---------------
- [Command Line Interface](https://github.com/ethers-io/ethers-cli) - Command Line Tools for ethers
- [CryptoKitties](https://github.com/ricmoo/ethers-meow) - CryptoKitties utility libraries
- [ENS](https://github.com/ethers-io/ethers-ens) - ENS utility libraries for managing names
- [LedgerSigner](https://github.com/ethers-io/ethers-ledger) - Use a Ledger Hardware Wallet as an ethers Signer (supports HID (node.js) and U2F (browser); or specify your own transport)
- [Web3 Bridge](https://github.com/ethers-io/ethers-web3-bridge) - Use ethers as the backend for a Web3 front-end
Hacking and Contributing
------------------------
The JavaScript code is now generated from TypeScript, so make sure you modify the
TypeScript and compile it, rather than modifying the JavaScript directly. To start
auto-compiling the TypeScript code, you may use:
```
/home/ethers> npm run auto-build
```
A very important part of ethers is its exhaustive test cases, so before making any
bug fix, please add a test case that fails prior to the fix, and succeeds after the
fix. All regression tests must pass.
Pull requests are always welcome, but please keep a few points in mind:
- Compatibility-breaking changes will not be accepted; they may be considered for the next major version
- Security is important; adding dependencies require fairly convincing arguments
- The library aims to be lean, so keep an eye on the `dist/ethers.min.js` file size before and after your changes
- Add test cases for both expected and unexpected input
- Any new features need to be supported by us (issues, documentation, testing), so anything that is overly complicated or specific may not be accepted
In general, **please start an issue before beginning a pull request**, so we can have a public discussion. :)
Donations
---------

1
_version.d.ts vendored Normal file
View File

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

3
_version.js Normal file
View File

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

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;

79
contract.d.ts vendored Normal file
View File

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

647
contract.js Normal file
View File

@@ -0,0 +1,647 @@
'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 = {
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 = {};
// 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());
// Check for unexpected keys (e.g. using "gas" instead of "gasLimit")
for (var key in tx) {
if (!allowedTransactionKeys[key]) {
throw new Error('unknown transaction override ' + key);
}
}
}
if (params.length != method.inputs.length) {
throw new Error('incorrect number of arguments');
}
// Check overrides make sense
['data', 'to'].forEach(function (key) {
if (tx[key] != null) {
errors.throwError('cannot override ' + key, errors.UNSUPPORTED_OPERATION, { operation: key });
}
});
// Send to the contract address (after checking the contract is deployed)
tx.to = contract.deployed().then(function () {
return contract.addressPromise;
});
return resolveAddresses(contract.provider, params, method.inputs).then(function (params) {
tx.data = method.encode(params);
if (method.type === 'call') {
// Call (constant functions) always cost 0 ether
if (estimateOnly) {
return Promise.resolve(constants_1.Zero);
}
if (!contract.provider) {
errors.throwError('call (constant functions) require a provider or a signer with a provider', errors.UNSUPPORTED_OPERATION, { operation: 'call' });
}
// Check overrides make sense
['gasLimit', 'gasPrice', 'value'].forEach(function (key) {
if (tx[key] != null) {
throw new Error('call cannot override ' + key);
}
});
if (tx.from == null && contract.signer) {
tx.from = contract.signer.getAddress();
}
return contract.provider.call(tx).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);
}
throw new Error('invalid type - ' + method.type);
return null;
});
};
}
function getEventTag(filter) {
return (filter.address || '') + (filter.topics ? filter.topics.join(':') : '');
}
var Contract = /** @class */ (function () {
// https://github.com/Microsoft/TypeScript/issues/5453
// Once this issue is resolved (there are open PR) we can do this nicer
// by making addressOrName default to null for 2 operand calls. :)
function Contract(addressOrName, contractInterface, signerOrProvider) {
var _this = this;
errors.checkNew(this, Contract);
// @TODO: Maybe still check the addressOrName looks like a valid address or name?
//address = getAddress(address);
if (interface_1.Interface.isInterface(contractInterface)) {
properties_1.defineReadOnly(this, 'interface', contractInterface);
}
else {
properties_1.defineReadOnly(this, 'interface', new interface_1.Interface(contractInterface));
}
if (abstract_signer_1.Signer.isSigner(signerOrProvider)) {
properties_1.defineReadOnly(this, 'provider', signerOrProvider.provider);
properties_1.defineReadOnly(this, 'signer', signerOrProvider);
}
else if (abstract_provider_1.Provider.isProvider(signerOrProvider)) {
properties_1.defineReadOnly(this, 'provider', signerOrProvider);
properties_1.defineReadOnly(this, 'signer', null);
}
else {
errors.throwError('invalid signer or provider', errors.INVALID_ARGUMENT, { arg: 'signerOrProvider', value: signerOrProvider });
}
properties_1.defineReadOnly(this, 'estimate', {});
properties_1.defineReadOnly(this, 'functions', {});
properties_1.defineReadOnly(this, 'filters', {});
Object.keys(this.interface.events).forEach(function (eventName) {
var event = _this.interface.events[eventName];
properties_1.defineReadOnly(_this.filters, eventName, function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
return {
address: _this.address,
topics: event.encodeTopics(args)
};
});
});
this._events = [];
properties_1.defineReadOnly(this, 'address', addressOrName);
if (this.provider) {
properties_1.defineReadOnly(this, 'addressPromise', this.provider.resolveName(addressOrName).then(function (address) {
if (address == null) {
throw new Error('name not found');
}
return address;
}).catch(function (error) {
console.log('ERROR: Cannot find Contract - ' + addressOrName);
throw error;
}));
}
else {
try {
properties_1.defineReadOnly(this, 'addressPromise', Promise.resolve(address_1.getAddress(addressOrName)));
}
catch (error) {
// Without a provider, we cannot use ENS names
errors.throwError('provider is required to use non-address contract address', errors.INVALID_ARGUMENT, { argument: 'addressOrName', value: addressOrName });
}
}
Object.keys(this.interface.functions).forEach(function (name) {
var run = runMethod(_this, name, false);
if (_this[name] == null) {
properties_1.defineReadOnly(_this, name, run);
}
else {
console.log('WARNING: Multiple definitions for ' + name);
}
if (_this.functions[name] == null) {
properties_1.defineReadOnly(_this.functions, name, run);
properties_1.defineReadOnly(_this.estimate, name, runMethod(_this, name, true));
}
});
}
// @TODO: Allow timeout?
Contract.prototype.deployed = function () {
var _this = this;
if (!this._deployed) {
// If we were just deployed, we know the transaction we should occur in
if (this.deployTransaction) {
this._deployed = this.deployTransaction.wait().then(function () {
return _this;
});
}
else {
// @TODO: Once we allow a timeout to be passed in, we will wait
// up to that many blocks for getCode
// Otherwise, poll for our code to be deployed
this._deployed = this.provider.getCode(this.address).then(function (code) {
if (code === '0x') {
errors.throwError('contract not deployed', errors.UNSUPPORTED_OPERATION, {
contractAddress: _this.address,
operation: 'getDeployed'
});
}
return _this;
});
}
}
return this._deployed;
};
// @TODO:
// estimateFallback(overrides?: TransactionRequest): Promise<BigNumber>
// @TODO:
// estimateDeploy(bytecode: string, ...args): Promise<BigNumber>
Contract.prototype.fallback = function (overrides) {
var _this = this;
if (!this.signer) {
errors.throwError('sending a transaction require a signer', errors.UNSUPPORTED_OPERATION, { operation: 'sendTransaction(fallback)' });
}
var tx = properties_1.shallowCopy(overrides || {});
['from', 'to'].forEach(function (key) {
if (tx[key] == null) {
return;
}
errors.throwError('cannot override ' + key, errors.UNSUPPORTED_OPERATION, { operation: key });
});
tx.to = this.addressPromise;
return this.deployed().then(function () {
return _this.signer.sendTransaction(tx);
});
};
// Reconnect to a different signer or provider
Contract.prototype.connect = function (signerOrProvider) {
if (typeof (signerOrProvider) === 'string') {
signerOrProvider = new VoidSigner(signerOrProvider, this.provider);
}
var contract = new Contract(this.address, this.interface, signerOrProvider);
if (this.deployTransaction) {
properties_1.defineReadOnly(contract, 'deployTransaction', this.deployTransaction);
}
return contract;
};
// Re-attach to a different on=chain instance of this contract
Contract.prototype.attach = function (addressOrName) {
return new Contract(addressOrName, this.interface, this.signer || this.provider);
};
Contract.isIndexed = function (value) {
return interface_1.Interface.isIndexed(value);
};
Contract.prototype._getEventFilter = function (eventName) {
var _this = this;
if (typeof (eventName) === 'string') {
// Listen for any event
if (eventName === '*') {
return {
decode: function (log) {
return [_this.interface.parseLog(log)];
},
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 {
decode: function (log) {
return event_1.decode(log.data, log.topics);
},
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 in this.interface.events) {
if (name.indexOf('(') === -1) {
continue;
}
var e = this.interface.events[name];
if (e.topic === eventName.topics[0].toLowerCase()) {
event = e;
break;
}
}
}
return {
decode: function (log) {
if (event) {
return event.decode(log.data, log.topics);
}
return [log];
},
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 decoded = Array.prototype.slice.call(eventFilter.decode(log));
var event = properties_1.deepCopy(log);
event.args = decoded;
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.getTransactionReceipt(log.transactionHash); };
event.getTransactionReceipt = function () { return _this.provider.getTransactionReceipt(log.transactionHash); };
decoded.push(event);
_this.emit.apply(_this, [eventFilter.filter].concat(decoded));
};
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) {
if (event.eventFilter.eventTag !== eventFilter.eventTag) {
return true;
}
setTimeout(function () {
event.listener.apply(_this, args);
}, 0);
result = true;
return !(event.once);
});
return result;
};
Contract.prototype.listenerCount = function (eventName) {
if (!this.provider) {
return 0;
}
var eventFilter = this._getEventFilter(eventName);
return this._events.filter(function (event) {
return event.eventFilter.eventTag === eventFilter.eventTag;
}).length;
};
Contract.prototype.listeners = function (eventName) {
if (!this.provider) {
return [];
}
var eventFilter = this._getEventFilter(eventName);
return this._events.filter(function (event) {
return event.eventFilter.eventTag === eventFilter.eventTag;
}).map(function (event) { return event.listener; });
};
Contract.prototype.removeAllListeners = function (eventName) {
if (!this.provider) {
return this;
}
var eventFilter = this._getEventFilter(eventName);
this._events = this._events.filter(function (event) {
return event.eventFilter.eventTag !== eventFilter.eventTag;
});
return this;
};
Contract.prototype.removeListener = function (eventName, listener) {
var _this = this;
if (!this.provider) {
return this;
}
var found = false;
var eventFilter = this._getEventFilter(eventName);
this._events = this._events.filter(function (event) {
// Make sure this event and listener match
if (event.eventFilter.eventTag !== eventFilter.eventTag) {
return true;
}
if (event.listener !== listener) {
return true;
}
_this.provider.removeListener(event.eventFilter.filter, event.wrappedListener);
// Already found a matching event in a previous loop
if (found) {
return true;
}
// REmove this event (returning false filters us out)
found = true;
return false;
});
return this;
};
return Contract;
}());
exports.Contract = Contract;
var ContractFactory = /** @class */ (function () {
function ContractFactory(contractInterface, bytecode, signer) {
var bytecodeHex = null;
// Allow the bytecode object from the Solidity compiler
if (typeof (bytecode) === 'string') {
bytecodeHex = bytecode;
}
else if (bytes_1.isArrayish(bytecode)) {
bytecodeHex = bytes_1.hexlify(bytecode);
}
else if (typeof (bytecode.object) === 'string') {
bytecodeHex = bytecode.object;
}
else {
errors.throwError('bytecode must be a valid hex string', errors.INVALID_ARGUMENT, { arg: 'bytecode', value: bytecode });
}
// Make sure it is 0x prefixed
if (bytecodeHex.substring(0, 2) !== '0x') {
bytecodeHex = '0x' + bytecodeHex;
}
if (!bytes_1.isHexString(bytecodeHex)) {
errors.throwError('bytecode must be a valid hex string', errors.INVALID_ARGUMENT, { arg: 'bytecode', value: bytecode });
}
if ((bytecodeHex.length % 2) !== 0) {
errors.throwError('bytecode must be valid data (even length)', errors.INVALID_ARGUMENT, { arg: 'bytecode', value: bytecode });
}
properties_1.defineReadOnly(this, 'bytecode', bytecodeHex);
if (interface_1.Interface.isInterface(contractInterface)) {
properties_1.defineReadOnly(this, 'interface', contractInterface);
}
else {
properties_1.defineReadOnly(this, 'interface', new interface_1.Interface(contractInterface));
}
if (signer && !abstract_signer_1.Signer.isSigner(signer)) {
errors.throwError('invalid signer', errors.INVALID_ARGUMENT, { arg: 'signer', value: null });
}
properties_1.defineReadOnly(this, 'signer', signer || null);
}
ContractFactory.prototype.getDeployTransaction = function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
var tx = {};
// If we have 1 additional argument, we allow transaction overrides
if (args.length === this.interface.deployFunction.inputs.length + 1) {
tx = properties_1.shallowCopy(args.pop());
for (var key in tx) {
if (!allowedTransactionKeys[key]) {
throw new Error('unknown transaction override ' + key);
}
}
}
// Do not allow these to be overridden in a deployment transaction
['data', 'from', 'to'].forEach(function (key) {
if (tx[key] == null) {
return;
}
errors.throwError('cannot override ' + key, errors.UNSUPPORTED_OPERATION, { operation: key });
});
// Make sure the call matches the constructor signature
errors.checkArgumentCount(args.length, this.interface.deployFunction.inputs.length, 'in Contract constructor');
// Set the data to the bytecode + the encoded constructor arguments
tx.data = this.interface.deployFunction.encode(this.bytecode, args);
return tx;
};
ContractFactory.prototype.deploy = function () {
var _this = this;
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
// Get the deployment transaction (with optional overrides)
var tx = this.getDeployTransaction.apply(this, args);
// Send the deployment transaction
return this.signer.sendTransaction(tx).then(function (tx) {
var contract = new Contract(address_1.getContractAddress(tx), _this.interface, _this.signer);
properties_1.defineReadOnly(contract, 'deployTransaction', tx);
return contract;
});
};
ContractFactory.prototype.attach = function (address) {
return new Contract(address, this.interface, this.signer);
};
ContractFactory.prototype.connect = function (signer) {
return new ContractFactory(this.interface, this.bytecode, signer);
};
ContractFactory.fromSolidity = function (compilerOutput, signer) {
if (compilerOutput == null) {
errors.throwError('missing compiler output', errors.MISSING_ARGUMENT, { argument: 'compilerOutput' });
}
if (typeof (compilerOutput) === 'string') {
compilerOutput = JSON.parse(compilerOutput);
}
var abi = compilerOutput.abi;
var bytecode = null;
if (compilerOutput.bytecode) {
bytecode = compilerOutput.bytecode;
}
else if (compilerOutput.evm && compilerOutput.evm.bytecode) {
bytecode = compilerOutput.evm.bytecode;
}
return new ContractFactory(abi, bytecode, signer);
};
return ContractFactory;
}());
exports.ContractFactory = ContractFactory;

View File

@@ -1,330 +0,0 @@
'use strict';
var Interface = require('./interface.js');
var utils = (function() {
return {
defineProperty: require('../utils/properties.js').defineProperty,
getAddress: require('../utils/address.js').getAddress,
bigNumberify: require('../utils/bignumber.js').bigNumberify,
hexlify: require('../utils/convert.js').hexlify,
};
})();
var allowedTransactionKeys = {
data: true, from: true, gasLimit: true, gasPrice:true, nonce: true, to: true, value: true
}
function copyObject(object) {
var result = {};
for (var key in object) {
result[key] = object[key];
}
return result;
}
function Contract(addressOrName, contractInterface, signerOrProvider) {
if (!(this instanceof Contract)) { throw new Error('missing new'); }
// @TODO: Maybe still check the addressOrName looks like a valid address or name?
//address = utils.getAddress(address);
if (!(contractInterface instanceof Interface)) {
contractInterface = new Interface(contractInterface);
}
if (!signerOrProvider) { throw new Error('missing signer or provider'); }
var signer = signerOrProvider;
var provider = null;
if (signerOrProvider.provider) {
provider = signerOrProvider.provider;
} else {
provider = signerOrProvider;
signer = null;
}
utils.defineProperty(this, 'address', addressOrName);
utils.defineProperty(this, 'interface', contractInterface);
utils.defineProperty(this, 'signer', signer);
utils.defineProperty(this, 'provider', provider);
var addressPromise = provider.resolveName(addressOrName);
function runMethod(method, estimateOnly) {
return function() {
var transaction = {}
var params = Array.prototype.slice.call(arguments);
// If 1 extra parameter was passed in, it contains overrides
if (params.length == method.inputs.types.length + 1) {
transaction = params.pop();
if (typeof(transaction) !== 'object') {
throw new Error('invalid transaction overrides');
}
transaction = copyObject(transaction);
// Check for unexpected keys (e.g. using "gas" instead of "gasLimit")
for (var key in transaction) {
if (!allowedTransactionKeys[key]) {
throw new Error('unknown transaction override ' + key);
}
}
}
// Check overrides make sense
['data', 'to'].forEach(function(key) {
if (transaction[key] != null) {
throw new Error('cannot override ' + key) ;
}
});
var call = method.apply(contractInterface, params);
// Send to the contract address
transaction.to = addressOrName;
// Set the transaction data
transaction.data = call.data;
switch (call.type) {
case 'call':
// Call (constant functions) always cost 0 ether
if (estimateOnly) {
return Promise.resolve(new utils.bigNumberify(0));
}
// Check overrides make sense
['gasLimit', 'gasPrice', 'value'].forEach(function(key) {
if (transaction[key] != null) {
throw new Error('call cannot override ' + key) ;
}
});
var fromPromise = null;
if (transaction.from == null && signer && signer.getAddress) {
fromPromise = signer.getAddress();
if (!(fromPromise instanceof Promise)) {
fromPromise = Promise.resolve(fromPromise);
}
} else {
fromPromise = Promise.resolve(null);
}
return fromPromise.then(function(address) {
if (address) {
transaction.from = utils.getAddress(address);
}
return provider.call(transaction);
}).then(function(value) {
var result = call.parse(value);
if (method.outputs.types.length === 1) {
result = result[0];
}
return result;
});
case 'transaction':
if (!signer) { return Promise.reject(new Error('missing signer')); }
// Make sure they aren't overriding something they shouldn't
if (transaction.from != null) {
throw new Error('transaction cannot override from') ;
}
// Only computing the transaction estimate
if (estimateOnly) {
if (signer && signer.estimateGas) {
return signer.estimateGas(transaction);
}
return provider.estimateGas(transaction)
}
// If the signer supports sendTrasaction, use it
if (signer.sendTransaction) {
return signer.sendTransaction(transaction);
}
if (!signer.sign) {
return Promise.reject(new Error('custom signer does not support signing'));
}
if (transaction.gasLimit == null) {
transaction.gasLimit = signer.defaultGasLimit || 2000000;
}
var noncePromise = null;
if (transaction.nonce) {
noncePromise = Promise.resolve(transaction.nonce)
} else if (signer.getTransactionCount) {
noncePromise = signer.getTransactionCount();
if (!(noncePromise instanceof Promise)) {
noncePromise = Promise.resolve(noncePromise);
}
} else {
var addressPromise = signer.getAddress();
if (!(addressPromise instanceof Promise)) {
addressPromise = Promise.resolve(addressPromise);
}
noncePromise = addressPromise.then(function(address) {
return provider.getTransactionCount(address, 'pending');
});
}
var gasPricePromise = null;
if (transaction.gasPrice) {
gasPricePromise = Promise.resolve(transaction.gasPrice);
} else {
gasPricePromise = provider.getGasPrice();
}
return Promise.all([
noncePromise,
gasPricePromise
]).then(function(results) {
transaction.nonce = results[0];
transaction.gasPrice = results[1];
return signer.sign(transaction);
}).then(function(signedTransaction) {
return provider.sendTransaction(signedTransaction);
});
}
};
}
var estimate = {};
utils.defineProperty(this, 'estimate', estimate);
var functions = {};
utils.defineProperty(this, 'functions', functions);
var events = {};
utils.defineProperty(this, 'events', events);
Object.keys(contractInterface.functions).forEach(function(methodName) {
var method = contractInterface.functions[methodName];
var run = runMethod(method, false);
if (this[methodName] == null) {
utils.defineProperty(this, methodName, run);
} else {
console.log('WARNING: Multiple definitions for ' + method);
}
if (functions[method] == null) {
utils.defineProperty(functions, methodName, run);
utils.defineProperty(estimate, methodName, runMethod(method, true));
}
}, this);
Object.keys(contractInterface.events).forEach(function(eventName) {
var eventInfo = contractInterface.events[eventName];
var eventCallback = null;
function handleEvent(log) {
addressPromise.then(function(address) {
// Not meant for us (the topics just has the same name)
if (address != log.address) { return; }
try {
var result = eventInfo.parse(log.topics, log.data);
// Some useful things to have with the log
log.args = result;
log.event = eventName;
log.parse = eventInfo.parse;
log.removeListener = function() {
provider.removeListener(eventInfo.topics, handleEvent);
}
var poller = function(func, key) {
return new Promise(function(resolve, reject) {
function poll() {
provider[func](log[key]).then(function(value) {
if (value == null) {
setTimeout(poll, 1000);
return;
}
resolve(value);
}, function(error) {
reject(error);
});
}
poll();
});
}
log.getBlock = function() { return poller('getBlock', 'blockHash'); }
log.getTransaction = function() { return poller('getTransaction', 'transactionHash'); }
log.getTransactionReceipt = function() { return poller('getTransactionReceipt', 'transactionHash'); }
log.eventSignature = eventInfo.signature;
eventCallback.apply(log, Array.prototype.slice.call(result));
} catch (error) {
console.log(error);
}
});
}
var property = {
enumerable: true,
get: function() {
return eventCallback;
},
set: function(value) {
if (!value) { value = null; }
if (!value && eventCallback) {
provider.removeListener(eventInfo.topics, handleEvent);
} else if (value && !eventCallback) {
provider.on(eventInfo.topics, handleEvent);
}
eventCallback = value;
}
};
var propertyName = 'on' + eventName.toLowerCase();
if (this[propertyName] == null) {
Object.defineProperty(this, propertyName, property);
}
Object.defineProperty(events, eventName, property);
}, this);
}
utils.defineProperty(Contract.prototype, 'connect', function(signerOrProvider) {
return new Contract(this.address, this.interface, signerOrProvider);
});
utils.defineProperty(Contract, 'getDeployTransaction', function(bytecode, contractInterface) {
if (!(contractInterface instanceof Interface)) {
contractInterface = new Interface(contractInterface);
}
var args = Array.prototype.slice.call(arguments);
args.splice(1, 1);
return {
data: contractInterface.deployFunction.apply(contractInterface, args).bytecode
}
});
module.exports = Contract;

View File

@@ -1,10 +0,0 @@
'use strict';
var Contract = require('./contract.js');
var Interface = require('./interface.js');
module.exports = {
Contract: Contract,
Interface: Interface,
}

View File

@@ -1,337 +0,0 @@
'use strict';
// See: https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI
var throwError = require('../utils/throw-error');
var utils = (function() {
var convert = require('../utils/convert');
var properties = require('../utils/properties');
var utf8 = require('../utils/utf8');
return {
defineFrozen: properties.defineFrozen,
defineProperty: properties.defineProperty,
coder: require('../utils/abi-coder').defaultCoder,
arrayify: convert.arrayify,
concat: convert.concat,
isHexString: convert.isHexString,
toUtf8Bytes: utf8.toUtf8Bytes,
keccak256: require('../utils/keccak256'),
};
})();
function parseParams(params) {
var names = [];
var types = [];
params.forEach(function(param) {
if (param.components != null) {
if (param.type.substring(0, 5) !== 'tuple') {
throw new Error('internal error; report on GitHub');
}
var suffix = '';
var arrayBracket = param.type.indexOf('[');
if (arrayBracket >= 0) { suffix = param.type.substring(arrayBracket); }
var result = parseParams(param.components);
names.push({ name: (param.name || null), names: result.names });
types.push('tuple(' + result.types.join(',') + ')' + suffix)
} else {
names.push(param.name || null);
types.push(param.type);
}
});
return {
names: names,
types: types
}
}
function populateDescription(object, items) {
for (var key in items) {
utils.defineProperty(object, key, items[key]);
}
return object;
}
/**
* - bytecode (optional; only for deploy)
* - type ("deploy")
*/
function DeployDescription() { }
/**
* - name
* - signature
* - sighash
* -
* -
* -
* -
* - type: ("call" | "transaction")
*/
function FunctionDescription() { }
/**
* - anonymous
* - name
* - signature
* - parse
* - topics
* - inputs
* - type ("event")
*/
function EventDescription() { }
function Indexed(value) {
utils.defineProperty(this, 'indexed', true);
utils.defineProperty(this, 'hash', value);
}
function Result() {}
function Interface(abi) {
if (!(this instanceof Interface)) { throw new Error('missing new'); }
if (typeof(abi) === 'string') {
try {
abi = JSON.parse(abi);
} catch (error) {
throwError('invalid abi', { input: abi });
}
}
utils.defineFrozen(this, 'abi', abi);
var methods = {}, events = {}, deploy = null;
utils.defineProperty(this, 'functions', methods);
utils.defineProperty(this, 'events', events);
function addMethod(method) {
switch (method.type) {
case 'constructor':
var func = (function() {
var inputParams = parseParams(method.inputs);
var func = function(bytecode) {
if (!utils.isHexString(bytecode)) {
throwError('invalid bytecode', { input: bytecode });
}
var params = Array.prototype.slice.call(arguments, 1);
if (params.length < inputParams.types.length) {
throwError('missing parameter');
} else if (params.length > inputParams.types.length) {
throwError('too many parameters');
}
var result = {
bytecode: bytecode + utils.coder.encode(inputParams.names, inputParams.types, params).substring(2),
type: 'deploy'
}
return populateDescription(new DeployDescription(), result);
}
utils.defineFrozen(func, 'inputs', inputParams);
utils.defineProperty(func, 'payable', (method.payable == null || !!method.payable))
return func;
})();
if (!deploy) { deploy = func; }
break;
case 'function':
var func = (function() {
var inputParams = parseParams(method.inputs);
var outputParams = parseParams(method.outputs);
var signature = '(' + inputParams.types.join(',') + ')';
signature = signature.replace(/tuple/g, '');
signature = method.name + signature;
var sighash = utils.keccak256(utils.toUtf8Bytes(signature)).substring(0, 10);
var func = function() {
var result = {
name: method.name,
signature: signature,
sighash: sighash,
type: ((method.constant) ? 'call': 'transaction')
};
var params = Array.prototype.slice.call(arguments, 0);
if (params.length < inputParams.types.length) {
throwError('missing parameter');
} else if (params.length > inputParams.types.length) {
throwError('too many parameters');
}
result.data = sighash + utils.coder.encode(inputParams.names, inputParams.types, params).substring(2);
result.parse = function(data) {
return utils.coder.decode(
outputParams.names,
outputParams.types,
utils.arrayify(data)
);
};
return populateDescription(new FunctionDescription(), result);
}
utils.defineFrozen(func, 'inputs', inputParams);
utils.defineFrozen(func, 'outputs', outputParams);
utils.defineProperty(func, 'payable', (method.payable == null || !!method.payable))
utils.defineProperty(func, 'signature', signature);
utils.defineProperty(func, 'sighash', sighash);
return func;
})();
// Expose the first (and hopefully unique named function
if (method.name && methods[method.name] == null) {
utils.defineProperty(methods, method.name, func);
}
// Expose all methods by their signature, for overloaded functions
if (methods[func.signature] == null) {
utils.defineProperty(methods, func.signature, func);
}
break;
case 'event':
var func = (function() {
var inputParams = parseParams(method.inputs);
var signature = '(' + inputParams.types.join(',') + ')';
signature = signature.replace(/tuple/g, '');
signature = method.name + signature;
var result = {
anonymous: (!!method.anonymous),
name: method.name,
signature: signature,
type: 'event'
};
result.parse = function(topics, data) {
if (data == null) {
data = topics;
topics = null;
}
// Strip the signature off of non-anonymous topics
if (topics != null && !method.anonymous) { topics = topics.slice(1); }
var inputNamesIndexed = [], inputNamesNonIndexed = [];
var inputTypesIndexed = [], inputTypesNonIndexed = [];
var inputDynamic = [];
method.inputs.forEach(function(input, index) {
var type = inputParams.types[index];
var name = inputParams.names[index];
if (input.indexed) {
if (type === 'string' || type === 'bytes' || type.indexOf('[') >= 0 || type.substring(0, 5) === 'tuple') {
inputTypesIndexed.push('bytes32');
inputDynamic.push(true);
} else {
inputTypesIndexed.push(type);
inputDynamic.push(false);
}
inputNamesIndexed.push(name);
} else {
inputNamesNonIndexed.push(name);
inputTypesNonIndexed.push(type);
inputDynamic.push(false);
}
});
if (topics != null) {
var resultIndexed = utils.coder.decode(
inputNamesIndexed,
inputTypesIndexed,
utils.concat(topics)
);
}
var resultNonIndexed = utils.coder.decode(
inputNamesNonIndexed,
inputTypesNonIndexed,
utils.arrayify(data)
);
var result = new Result();
var nonIndexedIndex = 0, indexedIndex = 0;
method.inputs.forEach(function(input, index) {
if (input.indexed) {
if (topics == null) {
result[index] = new Indexed(null);
} else if (inputDynamic[index]) {
result[index] = new Indexed(resultIndexed[indexedIndex++]);
} else {
result[index] = resultIndexed[indexedIndex++];
}
} else {
result[index] = resultNonIndexed[nonIndexedIndex++];
}
if (input.name) { result[input.name] = result[index]; }
});
result.length = method.inputs.length;
return result;
};
var func = populateDescription(new EventDescription(), result)
utils.defineFrozen(func, 'topics', [ utils.keccak256(utils.toUtf8Bytes(signature)) ]);
utils.defineFrozen(func, 'inputs', inputParams);
return func;
})();
// Expose the first (and hopefully unique) event name
if (method.name && events[method.name] == null) {
utils.defineProperty(events, method.name, func);
}
// Expose all events by their signature, for overloaded functions
if (methods[func.signature] == null) {
utils.defineProperty(methods, func.signature, func);
}
break;
case 'fallback':
// Nothing to do for fallback
break;
default:
console.log('WARNING: unsupported ABI type - ' + method.type);
break;
}
};
this.abi.forEach(addMethod, this);
// If there wasn't a constructor, create the default constructor
if (!deploy) {
addMethod({type: 'constructor', inputs: []});
}
utils.defineProperty(this, 'deployFunction', deploy);
}
module.exports = Interface;

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>
@@ -95,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>
@@ -149,8 +120,12 @@
<tr>
<th>Network:</th>
<td>
<div class="option left" id="option-testnet">Testnet (Ropsten)</div>
<div class="option right selected" id="option-mainnet">Mainnet (Homestead)</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>
@@ -202,6 +177,14 @@
<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(); }
@@ -221,51 +204,7 @@
});
})();
(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 ethers.utils.toUtf8Bytes(inputUsername.value);
var password = new ethers.utils.toUtf8Bytes(inputPassword.value);
showLoading('Summoning Brain Wallet...');
cancelScrypt = false;
ethers.Wallet.fromBrainWallet(username, password, updateLoading).then(function(wallet) {
showWallet(wallet);
document.getElementById('wallet-username').textContent = inputUsername.value;
}, function (error) {
if (error.message !== 'cancelled') {
alert('Unknown error');
}
showSelect();
});
};
})();
// JSON Wallet
(function() {
var inputFile = document.getElementById('select-wallet-file');
var targetDrop = document.getElementById('select-wallet-drop');
@@ -302,33 +241,22 @@
fileReader.onload = function(e) {
var json = e.target.result;
if (ethers.Wallet.isCrowdsaleWallet(json)) {
showWallet(Wallet.decryptCrowdsale(json, password));
} else if (ethers.Wallet.isValidWallet(json)) {
if (ethers.utils.getJsonWalletAddress(json)) {
showLoading('Decrypting Wallet...');
var password = new ethers.Wallet.utils.Buffer(inputPassword.value);
cancelScrypt = false;
ethers.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');
@@ -339,6 +267,7 @@
})();
// Raw Private Key
(function() {
var inputPrivatekey = document.getElementById('select-privatekey');
var submit = document.getElementById('select-submit-privatekey');
@@ -362,6 +291,31 @@
}
})();
// Mnemonic Phrase
(function() {
var inputPhrase = document.getElementById('select-mnemonic-phrase');
var inputPath = document.getElementById('select-mnemonic-path');
var submit = document.getElementById('select-submit-mnemonic');
function check() {
if (ethers.HDNode.isValidMnemonic(inputPhrase.value)) {
submit.classList.remove('disable');
} else {
submit.classList.add('disable');
}
}
inputPhrase.oninput = check;
inputPath.oninput = check;
setEnter(inputPhrase, submit);
setEnter(inputPath, submit);
submit.onclick = function() {
if (submit.classList.contains('disable')) { return; }
showWallet(ethers.Wallet.fromMnemonic(inputPhrase.value, inputPath.value));
}
})();
var activeWallet = null;
@@ -379,7 +333,7 @@
addActivity('> Refreshing details...');
activeWallet.getBalance('pending').then(function(balance) {
addActivity('< Balance: ' + balance.toString(10));
inputBalance.value = ethers.utils.formatEther(balance, {commify: true});
inputBalance.value = ethers.utils.formatEther(balance, { commify: true });
}, function(error) {
showError(error);
});
@@ -400,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);
}
})();
@@ -426,28 +383,18 @@
inputTargetAddress.oninput = check;
inputAmount.oninput = check;
var optionTestnet = document.getElementById('option-testnet');
var optionMainnet = document.getElementById('option-mainnet');
// Select the testnet network
optionTestnet.onclick = function() {
if (optionTestnet.classList.contains('selected')) { return; }
addActivity('! Switched network: Testnet');
activeWallet.provider = new ethers.providers.getDefaultProvider(true);
optionTestnet.classList.add('selected');
optionMainnet.classList.remove('selected');
refresh();
}
// Select the mainnet network
optionMainnet.onclick = function() {
if (optionMainnet.classList.contains('selected')) { return; }
addActivity('! Switched network: Mainnet');
activeWallet.provider = new ethers.providers.getDefaultProvider(false);
optionTestnet.classList.remove('selected');
optionMainnet.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() {
@@ -455,17 +402,24 @@
// Matt (from Etherscan) is working on a gasPrice API call, which
// should be done within a week or so.
// @TODO
var gasPrice = (activeWallet.provider.testnet ? 0x4a817c800: 0xba43b7400);
console.log('GasPrice: ' + gasPrice);
//var gasPrice = (activeWallet.provider.testnet ? 0x4a817c800: 0xba43b7400);
//console.log('GasPrice: ' + gasPrice);
var targetAddress = ethers.utils.getAddress(inputTargetAddress.value);
var amountWei = ethers.utils.parseEther(inputAmount.value);
activeWallet.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);
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 = '';
@@ -474,6 +428,7 @@
refresh();
}, function(error) {
console.log(error);
showError(error);
});
}
@@ -494,9 +449,8 @@
}
function showWallet(wallet) {
var testnet = document.getElementById('option-testnet').classList.contains('selected');
activeWallet = wallet;
activeWallet.provider = new ethers.providers.getDefaultProvider(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';
@@ -512,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 {

5763
dist/ethers-contracts.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

6686
dist/ethers-providers.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

6635
dist/ethers-utils.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

12012
dist/ethers-wallet.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

11583
dist/ethers.js vendored

File diff suppressed because one or more lines are too long

10
dist/ethers.min.js vendored

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

1009
dist/ethers.types.txt vendored Normal file

File diff suppressed because it is too large Load Diff

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

16
errors.d.ts vendored Normal file
View File

@@ -0,0 +1,16 @@
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;

108
errors.js Normal file
View File

@@ -0,0 +1,108 @@
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
// Unknown Error
exports.UNKNOWN_ERROR = 'UNKNOWN_ERROR';
// Not implemented
exports.NOT_IMPLEMENTED = 'NOT_IMPLEMENTED';
// Missing new operator to an object
// - name: The name of the class
exports.MISSING_NEW = 'MISSING_NEW';
// Call exception
// - transaction: the transaction
// - address?: the contract address
// - args?: The arguments passed into the function
// - method?: The Solidity method signature
// - errorSignature?: The EIP848 error signature
// - errorArgs?: The EIP848 error parameters
// - reason: The reason (only for EIP848 "Error(string)")
exports.CALL_EXCEPTION = 'CALL_EXCEPTION';
// Invalid argument (e.g. value is incompatible with type) to a function:
// - arg: The argument name that was invalid
// - value: The value of the argument
exports.INVALID_ARGUMENT = 'INVALID_ARGUMENT';
// Missing argument to a function:
// - count: The number of arguments received
// - expectedCount: The number of arguments expected
exports.MISSING_ARGUMENT = 'MISSING_ARGUMENT';
// Too many arguments
// - count: The number of arguments received
// - expectedCount: The number of arguments expected
exports.UNEXPECTED_ARGUMENT = 'UNEXPECTED_ARGUMENT';
// Numeric Fault
// - operation: the operation being executed
// - fault: the reason this faulted
exports.NUMERIC_FAULT = 'NUMERIC_FAULT';
// Insufficien funds (< value + gasLimit * gasPrice)
// - transaction: the transaction attempted
exports.INSUFFICIENT_FUNDS = 'INSUFFICIENT_FUNDS';
// Nonce has already been used
// - transaction: the transaction attempted
exports.NONCE_EXPIRED = 'NONCE_EXPIRED';
// The replacement fee for the transaction is too low
// - transaction: the transaction attempted
exports.REPLACEMENT_UNDERPRICED = 'REPLACEMENT_UNDERPRICED';
// Unsupported operation
// - operation
exports.UNSUPPORTED_OPERATION = 'UNSUPPORTED_OPERATION';
var _permanentCensorErrors = false;
var _censorErrors = false;
// @TODO: Enum
function throwError(message, code, params) {
if (_censorErrors) {
throw new Error('unknown error');
}
if (!code) {
code = exports.UNKNOWN_ERROR;
}
if (!params) {
params = {};
}
var messageDetails = [];
Object.keys(params).forEach(function (key) {
try {
messageDetails.push(key + '=' + JSON.stringify(params[key]));
}
catch (error) {
messageDetails.push(key + '=' + JSON.stringify(params[key].toString()));
}
});
var reason = message;
if (messageDetails.length) {
message += ' (' + messageDetails.join(', ') + ')';
}
// @TODO: Any??
var error = new Error(message);
error.reason = reason;
error.code = code;
Object.keys(params).forEach(function (key) {
error[key] = params[key];
});
throw error;
}
exports.throwError = throwError;
function checkNew(self, kind) {
if (!(self instanceof kind)) {
throwError('missing new', exports.MISSING_NEW, { name: kind.name });
}
}
exports.checkNew = checkNew;
function checkArgumentCount(count, expectedCount, suffix) {
if (!suffix) {
suffix = '';
}
if (count < expectedCount) {
throwError('missing argument' + suffix, exports.MISSING_ARGUMENT, { count: count, expectedCount: expectedCount });
}
if (count > expectedCount) {
throwError('too many arguments' + suffix, exports.UNEXPECTED_ARGUMENT, { count: count, expectedCount: expectedCount });
}
}
exports.checkArgumentCount = checkArgumentCount;
function setCensorship(censorship, permanent) {
if (_permanentCensorErrors) {
throwError('error censorship permanent', exports.UNSUPPORTED_OPERATION, { operation: 'setCersorship' });
}
_censorErrors = !!censorship;
_permanentCensorErrors = !!permanent;
}
exports.setCensorship = setCensorship;

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, 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, Event, EventFilter };

44
ethers.js Normal file
View File

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

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>

326
gulpfile.js Normal file
View File

@@ -0,0 +1,326 @@
'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())
.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 });
/*
// 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())
.pipe(gulp.dest("dist"));
});
}
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';

View File

@@ -1,25 +1,15 @@
'use strict';
var version = require('./package.json').version;
var contracts = require('./contracts');
var providers = require('./providers');
var utils = require('./utils');
var wallet = require('./wallet');
module.exports = {
Wallet: wallet.Wallet,
HDNode: wallet.HDNode,
SigningKey: wallet.SigningKey,
Contract: contracts.Contract,
Interface: contracts.Interface,
networks: providers.networks,
providers: providers,
utils: utils,
version: version,
"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"));

7544
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,20 +1,29 @@
{
"name": "ethers",
"version": "3.0.1",
"version": "4.0.0",
"description": "Ethereum wallet library.",
"main": "index.js",
"main": "./index.js",
"types": "./index.d.ts",
"scripts": {
"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 && npm run dist-types",
"dist-test": "gulp default-test minified-test",
"dist-bip39": "gulp 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": "./node_modules/.bin/mocha tests/test-*.js",
"test-phantomjs": "./node_modules/.bin/grunt dist && ./node_modules/.bin/grunt --gruntfile Gruntfile-test.js dist && phantomjs --web-security=false ./node_modules/mocha-phantomjs-core/mocha-phantomjs-core.js ./tests/test.html",
"version": "grunt dist"
"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": {
"@types/node": "^10.3.2",
"aes-js": "3.0.0",
"bn.js": "^4.4.0",
"elliptic": "6.3.3",
"hash.js": "^1.0.0",
"inherits": "2.0.1",
"hash.js": "1.1.3",
"js-sha3": "0.5.7",
"scrypt-js": "2.0.3",
"setimmediate": "1.0.4",
@@ -22,30 +31,38 @@
"xmlhttprequest": "1.8.0"
},
"devDependencies": {
"browserify": "^16.2.2",
"browserify-zlib": "^0.2.0",
"grunt": "^0.4.5",
"grunt-browserify": "^5.0.0",
"grunt-cli": "1.2.0",
"grunt-contrib-uglify": "^1.0.1",
"mocha": "^3.2.0",
"dts-bundle": "^0.7.3",
"eslint": "^5.0.1",
"eslint-plugin-promise": "^3.8.0",
"ethereumjs-tx": "^1.3.5",
"ethereumjs-util": "^5.2.0",
"gulp": "^4.0.0",
"gulp-cli": "^2.0.1",
"gulp-sourcemaps": "^2.6.4",
"gulp-typescript": "^5.0.0-alpha.1",
"gulp-uglify": "^3.0.0",
"mocha": "^5.2.0",
"mocha-phantomjs-core": "2.1.2",
"solc": "0.4.20",
"web3": "0.20.2"
},
"browser": {
"fs": "./tests/browser-fs.js",
"zlib": "browserify-zlib",
"./utils/random-bytes.js": "./utils/browser-random-bytes.js",
"xmlhttprequest": "./providers/browser-xmlhttprequest.js"
"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.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"
}

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

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

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));

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

@@ -0,0 +1,67 @@
import { BigNumber } from '../utils/bignumber';
import { Provider } from './abstract-provider';
import { Block, BlockTag, EventType, Filter, Listener, Log, TransactionReceipt, TransactionRequest, TransactionResponse } from './abstract-provider';
import { BigNumberish } from '../utils/bignumber';
import { Transaction } from '../utils/transaction';
import { Network, Networkish } from '../utils/networks';
export declare class BaseProvider extends Provider {
private _network;
private _events;
protected _emitted: any;
private _pollingInterval;
private _poller;
private _lastBlockNumber;
private _balances;
/**
* 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;
waitForTransaction(transactionHash: string, timeout?: 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): Promise<string>;
estimateGas(transaction: TransactionRequest): Promise<BigNumber>;
getBlock(blockHashOrBlockTag: BlockTag | string | Promise<BlockTag | string>, includeTransactions?: boolean): Promise<Block>;
getTransaction(transactionHash: string): Promise<TransactionResponse>;
getTransactionReceipt(transactionHash: string): Promise<TransactionReceipt>;
getLogs(filter: Filter): Promise<Array<Log>>;
getEtherPrice(): Promise<number>;
private _resolveNames;
private _getResolver;
resolveName(name: string | Promise<string>): Promise<string>;
lookupAddress(address: string | Promise<string>): Promise<string>;
static checkTransactionResponse(transaction: any): TransactionResponse;
doPoll(): void;
perform(method: string, params: any): Promise<any>;
protected _startPending(): void;
protected _stopPending(): void;
private _addEventListener;
on(eventName: EventType, listener: Listener): Provider;
once(eventName: EventType, listener: Listener): Provider;
addEventListener(eventName: EventType, listener: Listener): Provider;
emit(eventName: EventType, ...args: Array<any>): boolean;
listenerCount(eventName?: EventType): number;
listeners(eventName: EventType): Array<Listener>;
removeAllListeners(eventName: EventType): Provider;
removeListener(eventName: EventType, listener: Listener): Provider;
}

1070
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

@@ -1,262 +1,308 @@
'use strict';
var Provider = require('./provider.js');
var utils = (function() {
var convert = require('../utils/convert.js');
return {
defineProperty: require('../utils/properties.js').defineProperty,
hexlify: convert.hexlify,
hexStripZeros: convert.hexStripZeros,
"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 = utils.hexlify(transaction[key]);
if (transaction[key] == null) {
continue;
}
var value = bytes_1.hexlify(transaction[key]);
if ({ gasLimit: true, gasPrice: true, nonce: true, value: true }[key]) {
value = utils.hexStripZeros(value);
value = bytes_1.hexStripZeros(value);
}
result.push(key + '=' + value);
}
return result.join('&');
}
function EtherscanProvider(network, apiKey) {
Provider.call(this, network);
var baseUrl = null;
switch(this.name) {
case 'homestead':
baseUrl = 'https://api.etherscan.io';
break;
case 'ropsten':
baseUrl = 'https://ropsten.etherscan.io';
break;
case 'rinkeby':
baseUrl = 'https://rinkeby.etherscan.io';
break;
case 'kovan':
baseUrl = 'https://kovan.etherscan.io';
break;
default:
throw new Error('unsupported network');
}
utils.defineProperty(this, 'baseUrl', baseUrl);
utils.defineProperty(this, 'apiKey', apiKey || null);
}
Provider.inherits(EtherscanProvider);
utils.defineProperty(EtherscanProvider.prototype, '_call', function() {
});
utils.defineProperty(EtherscanProvider.prototype, '_callProxy', function() {
});
function getResult(result) {
// getLogs has weird success responses
if (result.status == 0 && result.message === 'No records found') {
// 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; }
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; }
if (blockTag === 'pending') {
throw new Error('pending not supported');
}
if (blockTag === 'latest') {
return blockTag;
}
return parseInt(blockTag.substring(2), 16);
}
utils.defineProperty(EtherscanProvider.prototype, 'perform', function(method, params) {
if (!params) { params = {}; }
var url = this.baseUrl;
var apiKey = '';
if (this.apiKey) { apiKey += '&apikey=' + this.apiKey; }
switch (method) {
case 'getBlockNumber':
url += '/api?module=proxy&action=eth_blockNumber' + apiKey;
return Provider.fetchJSON(url, null, getJsonResult);
case 'getGasPrice':
url += '/api?module=proxy&action=eth_gasPrice' + apiKey;
return Provider.fetchJSON(url, null, getJsonResult);
case 'getBalance':
// Returns base-10 result
url += '/api?module=account&action=balance&address=' + params.address;
url += '&tag=' + params.blockTag + apiKey;
return Provider.fetchJSON(url, null, getResult);
case 'getTransactionCount':
url += '/api?module=proxy&action=eth_getTransactionCount&address=' + params.address;
url += '&tag=' + params.blockTag + apiKey;
return Provider.fetchJSON(url, null, getJsonResult);
case 'getCode':
url += '/api?module=proxy&action=eth_getCode&address=' + params.address;
url += '&tag=' + params.blockTag + apiKey;
return Provider.fetchJSON(url, null, getJsonResult);
case 'getStorageAt':
url += '/api?module=proxy&action=eth_getStorageAt&address=' + params.address;
url += '&position=' + utils.hexStripZeros(params.position);
url += '&tag=' + utils.hexStripZeros(params.blockTag) + apiKey;
return Provider.fetchJSON(url, null, getJsonResult);
case 'sendTransaction':
url += '/api?module=proxy&action=eth_sendRawTransaction&hex=' + params.signedTransaction;
url += apiKey;
return Provider.fetchJSON(url, null, getJsonResult);
case 'getBlock':
if (params.blockTag) {
url += '/api?module=proxy&action=eth_getBlockByNumber&tag=' + utils.hexStripZeros(params.blockTag);
url += '&boolean=false';
url += apiKey;
return Provider.fetchJSON(url, null, getJsonResult);
}
throw new Error('getBlock by blockHash not implmeneted');
case 'getTransaction':
url += '/api?module=proxy&action=eth_getTransactionByHash&txhash=' + params.transactionHash;
url += apiKey;
return Provider.fetchJSON(url, null, getJsonResult);
case 'getTransactionReceipt':
url += '/api?module=proxy&action=eth_getTransactionReceipt&txhash=' + params.transactionHash;
url += apiKey;
return Provider.fetchJSON(url, null, getJsonResult);
case 'call':
var transaction = getTransactionString(params.transaction);
if (transaction) { transaction = '&' + transaction; }
url += '/api?module=proxy&action=eth_call' + transaction;
url += apiKey;
return Provider.fetchJSON(url, null, getJsonResult);
case 'estimateGas':
var transaction = getTransactionString(params.transaction);
if (transaction) { transaction = '&' + transaction; }
url += '/api?module=proxy&action=eth_estimateGas&' + transaction;
url += apiKey;
return Provider.fetchJSON(url, null, getJsonResult);
case 'getLogs':
url += '/api?module=logs&action=getLogs';
try {
if (params.filter.fromBlock) {
url += '&fromBlock=' + checkLogTag(params.filter.fromBlock);
}
if (params.filter.toBlock) {
url += '&toBlock=' + checkLogTag(params.filter.toBlock);
}
if (params.filter.address) {
url += '&address=' + params.filter.address;
}
// @TODO: We can handle slightly more complicated logs using the logs API
if (params.filter.topics && params.filter.topics.length > 0) {
if (params.filter.topics.length > 1) {
throw new Error('unsupported topic format');
}
var topic0 = params.filter.topics[0];
if (typeof(topic0) !== 'string' || topic0.length !== 66) {
throw new Error('unsupported topic0 format');
}
url += '&topic0=' + topic0;
}
} catch (error) {
return Promise.reject(error);
}
url += apiKey;
return Provider.fetchJSON(url, null, getResult);
case 'getEtherPrice':
if (this.name !== 'homestead') { return Promise.resolve(0.0); }
url += '/api?module=stats&action=ethprice';
url += apiKey;
return Provider.fetchJSON(url, null, getResult).then(function(result) {
return parseFloat(result.ethusd);
});
default:
break;
var EtherscanProvider = /** @class */ (function (_super) {
__extends(EtherscanProvider, _super);
function EtherscanProvider(network, apiKey) {
var _this = _super.call(this, network) || this;
errors.checkNew(_this, EtherscanProvider);
var name = 'invalid';
if (_this.network) {
name = _this.network.name;
}
var baseUrl = null;
switch (name) {
case 'homestead':
baseUrl = 'https://api.etherscan.io';
break;
case 'ropsten':
baseUrl = 'https://api-ropsten.etherscan.io';
break;
case 'rinkeby':
baseUrl = 'https://api-rinkeby.etherscan.io';
break;
case 'kovan':
baseUrl = 'https://api-kovan.etherscan.io';
break;
default:
throw new Error('unsupported network');
}
properties_1.defineReadOnly(_this, 'baseUrl', baseUrl);
properties_1.defineReadOnly(_this, 'apiKey', apiKey);
return _this;
}
return Promise.reject(new Error('not implemented - ' + method));
});
utils.defineProperty(EtherscanProvider.prototype, 'getHistory', function(addressOrName, startBlock, endBlock) {
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 += '&fromBlock=' + startBlock;
url += '&endBlock=' + endBlock;
url += '&sort=asc';
return Provider.fetchJSON(url, null, getResult).then(function(result) {
var output = [];
result.forEach(function(tx) {
['contractAddress', 'to'].forEach(function(key) {
if (tx[key] == '') { delete tx[key]; }
EtherscanProvider.prototype.perform = function (method, params) {
//if (!params) { params = {}; }
var url = this.baseUrl;
var apiKey = '';
if (this.apiKey) {
apiKey += '&apikey=' + this.apiKey;
}
switch (method) {
case 'getBlockNumber':
url += '/api?module=proxy&action=eth_blockNumber' + apiKey;
return web_1.fetchJson(url, null, getJsonResult);
case 'getGasPrice':
url += '/api?module=proxy&action=eth_gasPrice' + apiKey;
return web_1.fetchJson(url, null, getJsonResult);
case 'getBalance':
// Returns base-10 result
url += '/api?module=account&action=balance&address=' + params.address;
url += '&tag=' + params.blockTag + apiKey;
return web_1.fetchJson(url, null, getResult);
case 'getTransactionCount':
url += '/api?module=proxy&action=eth_getTransactionCount&address=' + params.address;
url += '&tag=' + params.blockTag + apiKey;
return web_1.fetchJson(url, null, getJsonResult);
case 'getCode':
url += '/api?module=proxy&action=eth_getCode&address=' + params.address;
url += '&tag=' + params.blockTag + apiKey;
return web_1.fetchJson(url, null, getJsonResult);
case 'getStorageAt':
url += '/api?module=proxy&action=eth_getStorageAt&address=' + params.address;
url += '&position=' + params.position;
url += '&tag=' + params.blockTag + apiKey;
return web_1.fetchJson(url, null, getJsonResult);
case 'sendTransaction':
url += '/api?module=proxy&action=eth_sendRawTransaction&hex=' + params.signedTransaction;
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult).catch(function (error) {
// "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;
});
if (tx.creates == null && tx.contractAddress != null) {
tx.creates = tx.contractAddress;
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 web_1.fetchJson(url, null, getJsonResult);
}
output.push(Provider._formatters.checkTransactionResponse(tx));
throw new Error('getBlock by blockHash not implmeneted');
case 'getTransaction':
url += '/api?module=proxy&action=eth_getTransactionByHash&txhash=' + params.transactionHash;
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult);
case 'getTransactionReceipt':
url += '/api?module=proxy&action=eth_getTransactionReceipt&txhash=' + params.transactionHash;
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult);
case 'call':
var transaction = getTransactionString(params.transaction);
if (transaction) {
transaction = '&' + transaction;
}
url += '/api?module=proxy&action=eth_call' + transaction;
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult);
case 'estimateGas':
var transaction = getTransactionString(params.transaction);
if (transaction) {
transaction = '&' + transaction;
}
url += '/api?module=proxy&action=eth_estimateGas&' + transaction;
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult);
case 'getLogs':
url += '/api?module=logs&action=getLogs';
try {
if (params.filter.fromBlock) {
url += '&fromBlock=' + checkLogTag(params.filter.fromBlock);
}
if (params.filter.toBlock) {
url += '&toBlock=' + checkLogTag(params.filter.toBlock);
}
if (params.filter.address) {
url += '&address=' + params.filter.address;
}
// @TODO: We can handle slightly more complicated logs using the logs API
if (params.filter.topics && params.filter.topics.length > 0) {
if (params.filter.topics.length > 1) {
throw new Error('unsupported topic format');
}
var topic0 = params.filter.topics[0];
if (typeof (topic0) !== 'string' || topic0.length !== 66) {
throw new Error('unsupported topic0 format');
}
url += '&topic0=' + topic0;
}
}
catch (error) {
return Promise.reject(error);
}
url += apiKey;
var self = this;
return web_1.fetchJson(url, null, 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 web_1.fetchJson(url, null, 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 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) {
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 output;
});
});
});
module.exports = EtherscanProvider;;
};
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

@@ -1,62 +1,110 @@
'use strict';
var inherits = require('inherits');
var Provider = require('./provider.js');
var utils = (function() {
return {
defineProperty: require('../utils/properties.js').defineProperty,
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 __());
};
})();
function FallbackProvider(providers) {
if (providers.length === 0) { throw new Error('no providers'); }
var network = {};
['chainId', 'ensAddress', 'name', 'testnet'].forEach(function(key) {
for (var i = 1; i < providers.length; i++) {
if (providers[0][key] !== providers[i][key]) {
throw new Error('incompatible providers - ' + key + ' mismatch');
}
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;
}
network[key] = providers[0][key];
});
if (!(this instanceof FallbackProvider)) { throw new Error('missing new'); }
Provider.call(this, network);
providers = providers.slice(0);
Object.defineProperty(this, 'providers', {
get: function() {
return providers.slice(0);
// Have nothing to compre to yet
if (check == null) {
check = network;
return;
}
// Matches!
if (check.name === network.name &&
check.chainId === network.chainId &&
check.ensAddress === network.ensAddress) {
return;
}
errors.throwError('provider mismatch', errors.INVALID_ARGUMENT, { arg: 'networks', value: networks });
});
return result;
}
inherits(FallbackProvider, Provider);
utils.defineProperty(FallbackProvider.prototype, 'perform', function(method, params) {
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) {
resolve(result);
}, function (error) {
if (!firstError) { firstError = error; }
next();
});
var FallbackProvider = /** @class */ (function (_super) {
__extends(FallbackProvider, _super);
function FallbackProvider(providers) {
var _this = this;
if (providers.length === 0) {
throw new Error('no providers');
}
next();
// 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
});
});
module.exports = FallbackProvider;
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 };

View File

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

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

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

View File

@@ -1,50 +1,63 @@
'use strict';
var Provider = require('./provider');
var JsonRpcProvider = require('./json-rpc-provider');
var utils = (function() {
return {
defineProperty: require('../utils/properties.js').defineProperty
}
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 __());
};
})();
function InfuraProvider(network, apiAccessToken) {
if (!(this instanceof InfuraProvider)) { throw new Error('missing new'); }
network = Provider.getNetwork(network);
var host = null;
switch(network.name) {
case 'homestead':
host = 'mainnet.infura.io';
break;
case 'ropsten':
host = 'ropsten.infura.io';
break;
case 'rinkeby':
host = 'rinkeby.infura.io';
break;
case 'kovan':
host = 'kovan.infura.io';
break;
default:
throw new Error('unsupported network');
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
var json_rpc_provider_1 = require("./json-rpc-provider");
var networks_1 = require("../utils/networks");
var properties_1 = require("../utils/properties");
var errors = __importStar(require("../errors"));
var InfuraProvider = /** @class */ (function (_super) {
__extends(InfuraProvider, _super);
function InfuraProvider(network, apiAccessToken) {
var _this = this;
network = networks_1.getNetwork((network == null) ? 'homestead' : network);
var host = null;
switch (network.name) {
case 'homestead':
host = 'mainnet.infura.io';
break;
case 'ropsten':
host = 'ropsten.infura.io';
break;
case 'rinkeby':
host = 'rinkeby.infura.io';
break;
case 'kovan':
host = 'kovan.infura.io';
break;
default:
throw new Error('unsupported network');
}
_this = _super.call(this, 'https://' + host + '/' + (apiAccessToken || ''), network) || this;
errors.checkNew(_this, InfuraProvider);
properties_1.defineReadOnly(_this, 'apiAccessToken', apiAccessToken || null);
return _this;
}
var url = 'https://' + host + '/' + (apiAccessToken || '');
JsonRpcProvider.call(this, url, network);
utils.defineProperty(this, 'apiAccessToken', apiAccessToken || null);
}
JsonRpcProvider.inherits(InfuraProvider);
utils.defineProperty(InfuraProvider.prototype, '_startPending', function() {
console.log('WARNING: INFURA does not support pending filters');
});
utils.defineProperty(InfuraProvider.prototype, '_stopPending', function() {
});
module.exports = InfuraProvider;
InfuraProvider.prototype._startPending = function () {
console.log('WARNING: INFURA does not support pending filters');
};
InfuraProvider.prototype.getSigner = function (address) {
errors.throwError('INFURA does not support signing', errors.UNSUPPORTED_OPERATION, { operation: 'getSigner' });
return null;
};
InfuraProvider.prototype.listAccounts = function () {
return Promise.resolve([]);
};
return InfuraProvider;
}(json_rpc_provider_1.JsonRpcProvider));
exports.InfuraProvider = InfuraProvider;

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

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

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

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

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

View File

@@ -1,203 +1,359 @@
'use strict';
// See: https://github.com/ethereum/wiki/wiki/JSON-RPC
var Provider = require('./provider.js');
var utils = (function() {
var convert = require('../utils/convert');
return {
defineProperty: require('../utils/properties').defineProperty,
hexlify: convert.hexlify,
isHexString: convert.isHexString,
hexStripZeros: convert.hexStripZeros,
}
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 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");
var errors = __importStar(require("../errors"));
function timer(timeout) {
return new Promise(function(resolve) {
setTimeout(function() {
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 getTransaction(transaction) {
var result = {};
for (var key in transaction) {
result[key] = utils.hexlify(transaction[key]);
function getLowerCase(value) {
if (value) {
return value.toLowerCase();
}
// Some nodes (INFURA ropsten; INFURA mainnet is fine) don't like extra zeros.
['gasLimit', 'gasPrice', 'nonce', 'value'].forEach(function(key) {
if (!result[key]) { return; }
result[key] = utils.hexStripZeros(result[key]);
});
// Transform "gasLimit" to "gas"
if (result.gasLimit != null && result.gas == null) {
result.gas = result.gasLimit;
delete result.gasLimit;
}
return result;
return value;
}
function JsonRpcProvider(url, network) {
if (!(this instanceof JsonRpcProvider)) { throw new Error('missing new'); }
if (arguments.lengt == 1) {
if (typeof(url) === 'string') {
try {
network = Provider.getNetwork(url);
url = null;
} catch (error) { }
} else {
network = url;
url = null;
var _constructorGuard = {};
var JsonRpcSigner = /** @class */ (function (_super) {
__extends(JsonRpcSigner, _super);
function JsonRpcSigner(constructorGuard, provider, addressOrIndex) {
var _this = _super.call(this) || this;
errors.checkNew(_this, JsonRpcSigner);
if (constructorGuard !== _constructorGuard) {
throw new Error('do not call the JsonRpcSigner constructor directly; use provider.getSigner');
}
}
Provider.call(this, network);
if (!url) { url = 'http://localhost:8545'; }
utils.defineProperty(this, 'url', url);
}
Provider.inherits(JsonRpcProvider);
utils.defineProperty(JsonRpcProvider.prototype, 'send', function(method, params) {
var request = {
method: method,
params: params,
id: 42,
jsonrpc: "2.0"
};
return Provider.fetchJSON(this.url, JSON.stringify(request), getResult);
});
utils.defineProperty(JsonRpcProvider.prototype, 'perform', function(method, params) {
switch (method) {
case 'getBlockNumber':
return this.send('eth_blockNumber', []);
case 'getGasPrice':
return this.send('eth_gasPrice', []);
case 'getBalance':
var blockTag = params.blockTag;
if (utils.isHexString(blockTag)) { blockTag = utils.hexStripZeros(blockTag); }
return this.send('eth_getBalance', [params.address, blockTag]);
case 'getTransactionCount':
var blockTag = params.blockTag;
if (utils.isHexString(blockTag)) { blockTag = utils.hexStripZeros(blockTag); }
return this.send('eth_getTransactionCount', [params.address, blockTag]);
case 'getCode':
var blockTag = params.blockTag;
if (utils.isHexString(blockTag)) { blockTag = utils.hexStripZeros(blockTag); }
return this.send('eth_getCode', [params.address, blockTag]);
case 'getStorageAt':
var position = params.position;
if (utils.isHexString(position)) { position = utils.hexStripZeros(position); }
var blockTag = params.blockTag;
if (utils.isHexString(blockTag)) { blockTag = utils.hexStripZeros(blockTag); }
return this.send('eth_getStorageAt', [params.address, position, blockTag]);
case 'sendTransaction':
return this.send('eth_sendRawTransaction', [params.signedTransaction]);
case 'getBlock':
if (params.blockTag) {
var blockTag = params.blockTag;
if (utils.isHexString(blockTag)) { blockTag = utils.hexStripZeros(blockTag); }
return this.send('eth_getBlockByNumber', [blockTag, false]);
} else if (params.blockHash) {
return this.send('eth_getBlockByHash', [params.blockHash, false]);
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));
}
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', [getTransaction(params.transaction), 'latest']);
case 'estimateGas':
return this.send('eth_estimateGas', [getTransaction(params.transaction)]);
case 'getLogs':
return this.send('eth_getLogs', [params.filter]);
default:
break;
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;
}
return Promise.reject(new Error('not implemented - ' + method));
});
utils.defineProperty(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; }
var seq = Promise.resolve();
hashes.forEach(function(hash) {
seq = seq.then(function() {
return self.getTransaction(hash).then(function(tx) {
self.emit('pending', tx);
});
});
});
return seq.then(function() {
return timer(1000);
});
}).then(function() {
if (self._pendingFilter != pendingFilter) {
self.send('eth_uninstallFilter', [ filterIf ]);
return;
/* May add back in the future; for now it is considered confusing. :)
get address(): string {
if (!this._address) {
errors.throwError('no sync sync address available; use getAddress', errors.UNSUPPORTED_OPERATION, { operation: 'address' });
}
return this._address
}
*/
JsonRpcSigner.prototype.getAddress = function () {
var _this = this;
if (this._address) {
return Promise.resolve(this._address);
}
return this.provider.send('eth_accounts', []).then(function (accounts) {
if (accounts.length <= _this._index) {
errors.throwError('unknown account #' + _this._index, errors.UNSUPPORTED_OPERATION, { operation: 'getAddress' });
}
_this._address = address_1.getAddress(accounts[_this._index]);
return _this._address;
});
};
JsonRpcSigner.prototype.getBalance = function (blockTag) {
return this.provider.getBalance(this.getAddress(), blockTag);
};
JsonRpcSigner.prototype.getTransactionCount = function (blockTag) {
return this.provider.getTransactionCount(this.getAddress(), blockTag);
};
JsonRpcSigner.prototype.sendTransaction = function (transaction) {
var _this = this;
var tx = properties_1.shallowCopy(transaction);
if (tx.from == null) {
tx.from = this.getAddress().then(function (address) {
if (!address) {
return null;
}
setTimeout(function() { poll(); }, 0);
return address.toLowerCase();
});
}
poll();
return filterId;
});
});
utils.defineProperty(JsonRpcProvider.prototype, '_stopPending', function() {
this._pendingFilter = null;
});
utils.defineProperty(JsonRpcProvider, '_hexlifyTransaction', function(transaction) {
return getTransaction(transaction);
});
module.exports = JsonRpcProvider;
if (transaction.gasLimit == null) {
tx.gasLimit = this.provider.estimateGas(tx);
}
return properties_1.resolveProperties(tx).then(function (tx) {
return _this.provider.send('eth_sendTransaction', [JsonRpcProvider.hexlifyTransaction(tx)]).then(function (hash) {
return web_1.poll(function () {
return _this.provider.getTransaction(hash).then(function (tx) {
if (tx === null) {
return undefined;
}
return _this.provider._wrapTransaction(tx, hash);
});
}, { onceBlock: _this.provider }).catch(function (error) {
error.transactionHash = hash;
throw error;
});
}, function (error) {
// See: JsonRpcProvider.sendTransaction (@TODO: Expose a ._throwError??)
if (error.responseText.indexOf('insufficient funds') >= 0) {
errors.throwError('insufficient funds', errors.INSUFFICIENT_FUNDS, {
transaction: tx
});
}
if (error.responseText.indexOf('nonce too low') >= 0) {
errors.throwError('nonce has already been used', errors.NONCE_EXPIRED, {
transaction: tx
});
}
if (error.responseText.indexOf('replacement transaction underpriced') >= 0) {
errors.throwError('replacement fee too low', errors.REPLACEMENT_UNDERPRICED, {
transaction: tx
});
}
throw error;
});
});
};
JsonRpcSigner.prototype.signMessage = function (message) {
var _this = this;
var data = ((typeof (message) === 'string') ? utf8_1.toUtf8Bytes(message) : message);
return this.getAddress().then(function (address) {
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign
return _this.provider.send('eth_sign', [address.toLowerCase(), bytes_1.hexlify(data)]);
});
};
JsonRpcSigner.prototype.unlock = function (password) {
var provider = this.provider;
return this.getAddress().then(function (address) {
return provider.send('personal_unlockAccount', [address.toLowerCase(), password, null]);
});
};
return JsonRpcSigner;
}(abstract_signer_1.Signer));
exports.JsonRpcSigner = JsonRpcSigner;
var 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 request = {
method: method,
params: params,
id: 42,
jsonrpc: "2.0"
};
return web_1.fetchJson(this.connection, JSON.stringify(request), getResult);
};
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) {
// "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), 'latest']);
case 'estimateGas':
return this.send('eth_estimateGas', [JsonRpcProvider.hexlifyTransaction(params.transaction)]);
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) {
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
// @TODO: Not any, a dictionary of string to strings
JsonRpcProvider.hexlifyTransaction = function (transaction) {
var result = {};
// Some nodes (INFURA ropsten; INFURA mainnet is fine) don't like extra 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;

View File

@@ -1,48 +0,0 @@
{
"unspecified": {
"chainId": 0,
"name": "unspecified"
},
"homestead": {
"chainId": 1,
"ensAddress": "0x314159265dd8dbb310642f98f50c066173c1259b",
"name": "homestead"
},
"mainnet": {
"chainId": 1,
"ensAddress": "0x314159265dd8dbb310642f98f50c066173c1259b",
"name": "homestead"
},
"morden": {
"chainId": 2,
"name": "morden"
},
"ropsten": {
"chainId": 3,
"ensAddress": "0x112234455c3a32fd11230c42e7bccd4a84e02010",
"name": "ropsten"
},
"testnet": {
"chainId": 3,
"ensAddress": "0x112234455c3a32fd11230c42e7bccd4a84e02010",
"name": "ropsten"
},
"rinkeby": {
"chainId": 4,
"name": "rinkeby"
},
"kovan": {
"chainId": 42,
"name": "kovan"
},
"classic": {
"chainId": 61,
"name": "classic"
}
}

File diff suppressed because it is too large Load Diff

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

@@ -1,171 +1,85 @@
'use strict';
var Provider = require('./provider');
var JsonRpcProvider = require('./json-rpc-provider');
var utils = (function() {
return {
defineProperty: require('../utils/properties').defineProperty,
getAddress: require('../utils/address').getAddress,
toUtf8Bytes: require('../utils/utf8').toUtf8Bytes,
hexlify: require('../utils/convert').hexlify
}
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 __());
};
})();
function Web3Signer(provider, address) {
if (!(this instanceof Web3Signer)) { throw new Error('missing new'); }
utils.defineProperty(this, 'provider', provider);
// Statically attach to a given address
if (address) {
utils.defineProperty(this, 'address', address);
utils.defineProperty(this, '_syncAddress', true);
} else {
Object.defineProperty(this, 'address', {
enumerable: true,
get: function() {
throw new Error('unsupported sync operation; use getAddress');
}
});
utils.defineProperty(this, '_syncAddress', false);
}
}
utils.defineProperty(Web3Signer.prototype, 'getAddress', function() {
if (this._syncAddress) { return Promise.resolve(this.address); }
return this.provider.send('eth_accounts', []).then(function(accounts) {
if (accounts.length === 0) {
throw new Error('no account');
}
return utils.getAddress(accounts[0]);
});
});
utils.defineProperty(Web3Signer.prototype, 'getBalance', function(blockTag) {
var provider = this.provider;
return this.getAddress().then(function(address) {
return provider.getBalance(address, blockTag);
});
});
utils.defineProperty(Web3Signer.prototype, 'getTransactionCount', function(blockTag) {
var provider = this.provider;
return this.getAddress().then(function(address) {
return provider.getTransactionCount(address, blockTag);
});
});
utils.defineProperty(Web3Signer.prototype, 'sendTransaction', function(transaction) {
var provider = this.provider;
transaction = JsonRpcProvider._hexlifyTransaction(transaction);
return this.getAddress().then(function(address) {
transaction.from = address.toLowerCase();
return provider.send('eth_sendTransaction', [ transaction ]).then(function(hash) {
return new Promise(function(resolve, reject) {
function check() {
provider.getTransaction(hash).then(function(transaction) {
if (!transaction) {
setTimeout(check, 1000);
return;
}
resolve(transaction);
});
}
check();
});
});
});
});
utils.defineProperty(Web3Signer.prototype, 'signMessage', function(message) {
var provider = this.provider;
var data = ((typeof(message) === 'string') ? utils.toUtf8Bytes(message): message);
return this.getAddress().then(function(address) {
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign
var method = 'eth_sign';
var params = [ address.toLowerCase(), utils.hexlify(data) ];
// Metamask complains about eth_sign (and on some versions hangs)
if (provider._web3Provider.isMetaMask) {
// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_sign
method = 'personal_sign';
params = [ utils.hexlify(data), address.toLowerCase() ];
}
return provider.send(method, params);
});
});
utils.defineProperty(Web3Signer.prototype, 'unlock', function(password) {
var provider = this.provider;
return this.getAddress().then(function(address) {
return provider.send('personal_unlockAccount', [ address.toLowerCase(), password, null ]);
});
});
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', {
});
*/
function Web3Provider(web3Provider, network) {
if (!(this instanceof Web3Provider)) { throw new Error('missing new'); }
// HTTP has a host; IPC has a path.
var url = web3Provider.host || web3Provider.path || 'unknown';
JsonRpcProvider.call(this, url, network);
utils.defineProperty(this, '_web3Provider', web3Provider);
}
JsonRpcProvider.inherits(Web3Provider);
utils.defineProperty(Web3Provider.prototype, 'getSigner', function(address) {
return new Web3Signer(this, address);
});
utils.defineProperty(Web3Provider.prototype, 'listAccounts', function() {
return this.send('eth_accounts', []).then(function(accounts) {
accounts.forEach(function(address, index) {
accounts[index] = utils.getAddress(address);
});
return accounts;
});
});
utils.defineProperty(Web3Provider.prototype, 'send', function(method, params) {
var provider = this._web3Provider;
return new Promise(function(resolve, reject) {
var request = {
method: method,
params: params,
id: 42,
jsonrpc: "2.0"
};
provider.sendAsync(request, function(error, result) {
if (error) {
reject(error);
return;
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);
}
if (result.error) {
var error = new Error(result.error.message);
error.code = result.error.code;
error.data = result.error.data;
reject(error);
return;
else if (web3Provider.send) {
_this._sendAsync = web3Provider.send.bind(web3Provider);
}
resolve(result.result);
}
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);
});
});
});
});
module.exports = Web3Provider;
};
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;

View File

@@ -1,43 +1,37 @@
'use strict';
var convert = require('./convert');
var defineProperty = require('./properties').defineProperty;
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) {
getRandomValues: function (buffer) {
for (var round = 0; round < 20; round++) {
for (var i = 0; i < buffer.length; i++) {
if (round) {
buffer[i] ^= parseInt(256 * Math.random());
} else {
buffer[i] = parseInt(256 * Math.random());
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(length) != length) {
if (length <= 0 || length > 1024 || parseInt(String(length)) != length) {
throw new Error('invalid length');
}
var result = new Uint8Array(length);
crypto.getRandomValues(result);
return convert.arrayify(result);
};
if (crypto._weakCrypto === true) {
defineProperty(randomBytes, '_weakCrypto', true);
return bytes_1.arrayify(result);
}
exports.randomBytes = randomBytes;
;
if (crypto._weakCrypto === true) {
properties_1.defineReadOnly(randomBytes, '_weakCrypto', true);
}
module.exports = randomBytes;

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.0";

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
};

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

@@ -0,0 +1,802 @@
'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 { 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 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> {
var tx: any = {}
// 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());
// Check for unexpected keys (e.g. using "gas" instead of "gasLimit")
for (var key in tx) {
if (!allowedTransactionKeys[key]) {
throw new Error('unknown transaction override ' + key);
}
}
}
if (params.length != method.inputs.length) {
throw new Error('incorrect number of arguments');
}
// Check overrides make sense
['data', 'to'].forEach(function(key) {
if (tx[key] != null) {
errors.throwError('cannot override ' + key, errors.UNSUPPORTED_OPERATION, { operation: key })
}
});
// Send to the contract address (after checking the contract is deployed)
tx.to = contract.deployed().then(() => {
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).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);
}
throw new Error('invalid type - ' + method.type);
return null;
});
}
}
function getEventTag(filter: EventFilter): string {
if (filter.address && (filter.topics == null || filter.topics.length === 0)) {
return '*';
}
return (filter.address || '*') + '@' + (filter.topics ? filter.topics.join(':'): '');
}
interface Bucket<T> {
[name: string]: T;
}
type _EventFilter = {
prepareEvent: (event: Event) => Array<any>;
event?: EventDescription;
eventTag: string;
filter: EventFilter;
};
type _Event = {
eventFilter: _EventFilter;
listener: Listener;
once: boolean;
wrappedListener: Listener;
};
export class Contract {
readonly address: string;
readonly interface: Interface;
readonly signer: Signer;
readonly provider: Provider;
readonly estimate: Bucket<(...params: Array<any>) => Promise<BigNumber>>;
readonly functions: Bucket<ContractFunction>;
readonly filters: Bucket<(...params: Array<any>) => EventFilter>;
readonly [name: string]: ContractFunction | any;
readonly addressPromise: Promise<string>;
// This is only set if the contract was created with a call to deploy
readonly deployTransaction: TransactionResponse;
private _deployed: Promise<Contract>;
// https://github.com/Microsoft/TypeScript/issues/5453
// Once this issue is resolved (there are open PR) we can do this nicer
// by making addressOrName default to null for 2 operand calls. :)
constructor(addressOrName: string, contractInterface: Array<string | ParamType> | string | Interface, signerOrProvider: Signer | Provider) {
errors.checkNew(this, Contract);
// @TODO: Maybe still check the addressOrName looks like a valid address or name?
//address = getAddress(address);
if (Interface.isInterface(contractInterface)) {
defineReadOnly(this, 'interface', contractInterface);
} else {
defineReadOnly(this, 'interface', new Interface(contractInterface));
}
if (Signer.isSigner(signerOrProvider)) {
defineReadOnly(this, 'provider', signerOrProvider.provider);
defineReadOnly(this, 'signer', signerOrProvider);
} else if (Provider.isProvider(signerOrProvider)) {
defineReadOnly(this, 'provider', signerOrProvider);
defineReadOnly(this, 'signer', null);
} else {
errors.throwError('invalid signer or provider', errors.INVALID_ARGUMENT, { arg: 'signerOrProvider', value: signerOrProvider });
}
defineReadOnly(this, 'estimate', { });
defineReadOnly(this, 'functions', { });
defineReadOnly(this, 'filters', { });
Object.keys(this.interface.events).forEach((eventName) => {
let event = this.interface.events[eventName];
defineReadOnly(this.filters, eventName, (...args: Array<any>) => {
return {
address: this.address,
topics: event.encodeTopics(args)
}
});
});
this._events = [];
defineReadOnly(this, 'address', addressOrName);
if (this.provider) {
defineReadOnly(this, 'addressPromise', this.provider.resolveName(addressOrName).then((address) => {
if (address == null) { throw new Error('name not found'); }
return address;
}).catch((error: Error) => {
console.log('ERROR: Cannot find Contract - ' + addressOrName);
throw error;
}));
} else {
try {
defineReadOnly(this, 'addressPromise', Promise.resolve(getAddress(addressOrName)));
} catch (error) {
// Without a provider, we cannot use ENS names
errors.throwError('provider is required to use non-address contract address', errors.INVALID_ARGUMENT, { argument: 'addressOrName', value: addressOrName });
}
}
Object.keys(this.interface.functions).forEach((name) => {
let run = runMethod(this, name, false);
if ((<any>this)[name] == null) {
defineReadOnly(this, name, run);
} else {
console.log('WARNING: Multiple definitions for ' + name);
}
if (this.functions[name] == null) {
defineReadOnly(this.functions, name, run);
defineReadOnly(this.estimate, name, runMethod(this, name, true));
}
});
}
// @TODO: Allow timeout?
deployed(): Promise<Contract> {
if (!this._deployed) {
// If we were just deployed, we know the transaction we should occur in
if (this.deployTransaction) {
this._deployed = this.deployTransaction.wait().then(() => {
return this;
});
} else {
// @TODO: Once we allow a timeout to be passed in, we will wait
// up to that many blocks for getCode
// Otherwise, poll for our code to be deployed
this._deployed = this.provider.getCode(this.address).then((code) => {
if (code === '0x') {
errors.throwError('contract not deployed', errors.UNSUPPORTED_OPERATION, {
contractAddress: this.address,
operation: 'getDeployed'
});
}
return this;
});
}
}
return this._deployed;
}
// @TODO:
// estimateFallback(overrides?: TransactionRequest): Promise<BigNumber>
// @TODO:
// estimateDeploy(bytecode: string, ...args): Promise<BigNumber>
fallback(overrides?: TransactionRequest): Promise<TransactionResponse> {
if (!this.signer) {
errors.throwError('sending a transaction require a signer', errors.UNSUPPORTED_OPERATION, { operation: 'sendTransaction(fallback)' })
}
var tx: TransactionRequest = shallowCopy(overrides || {});
['from', 'to'].forEach(function(key) {
if ((<any>tx)[key] == null) { return; }
errors.throwError('cannot override ' + key, errors.UNSUPPORTED_OPERATION, { operation: key })
});
tx.to = this.addressPromise;
return this.deployed().then(() => {
return this.signer.sendTransaction(tx);
});
}
// Reconnect to a different signer or provider
connect(signerOrProvider: Signer | Provider | string): Contract {
if (typeof(signerOrProvider) === 'string') {
signerOrProvider = new VoidSigner(signerOrProvider, this.provider);
}
let contract = new Contract(this.address, this.interface, signerOrProvider);
if (this.deployTransaction) {
defineReadOnly(contract, 'deployTransaction', this.deployTransaction);
}
return contract;
}
// Re-attach to a different on=chain instance of this contract
attach(addressOrName: string): Contract {
return new Contract(addressOrName, this.interface, this.signer || this.provider);
}
static isIndexed(value: any): value is Indexed {
return Interface.isIndexed(value);
}
private _events: Array<_Event>;
private _getEventFilter(eventName: EventFilter | string): _EventFilter {
if (typeof(eventName) === 'string') {
// Listen for any event
if (eventName === '*') {
return {
prepareEvent: (e: Event) => {
let parsed = this.interface.parseLog(e);
if (parsed) {
e.args = parsed.values;
e.decode = parsed.decode;
e.event = parsed.name;
e.eventSignature = parsed.signature;
}
return [ e ];
},
eventTag: '*',
filter: { address: this.address },
};
}
// Normalize the eventName
if (eventName.indexOf('(') !== -1) {
eventName = formatSignature(parseSignature('event ' + eventName));
}
let event = this.interface.events[eventName];
if (!event) {
errors.throwError('unknown event - ' + eventName, errors.INVALID_ARGUMENT, { argumnet: 'eventName', value: eventName });
}
let filter = {
address: this.address,
topics: [ event.topic ]
}
return {
prepareEvent: (e: Event) => {
let args = event.decode(e.data, e.topics);
e.args = args;
let result = Array.prototype.slice.call(args);
result.push(e);
return result;
},
event: event,
eventTag: getEventTag(filter),
filter: filter
};
}
let filter: EventFilter = {
address: this.address
}
// Find the matching event in the ABI; if none, we still allow filtering
// since it may be a filter for an otherwise unknown event
let event: EventDescription = null;
if (eventName.topics && eventName.topics[0]) {
filter.topics = eventName.topics;
for (let name in this.interface.events) {
if (name.indexOf('(') === -1) { continue; }
let e = this.interface.events[name];
if (e.topic === eventName.topics[0].toLowerCase()) {
event = e;
break;
}
}
}
return {
prepareEvent: (e: Event) => {
if (!event) { return [ e ]; }
let args = event.decode(e.data, e.topics);
e.args = args;
let result = Array.prototype.slice.call(args);
result.push(e);
return result;
},
event: event,
eventTag: getEventTag(filter),
filter: filter
}
}
private _addEventListener(eventFilter: _EventFilter, listener: Listener, once: boolean): void {
if (!this.provider) {
errors.throwError('events require a provider or a signer with a provider', errors.UNSUPPORTED_OPERATION, { operation: 'once' })
}
let wrappedListener = (log: Log) => {
let event: Event = (<Event>deepCopy(log));
let args = eventFilter.prepareEvent(event);
if (eventFilter.event) {
event.decode = eventFilter.event.decode;
event.event = eventFilter.event.name;
event.eventSignature = eventFilter.event.signature;
}
event.removeListener = () => { this.removeListener(eventFilter.filter, listener); };
event.getBlock = () => { return this.provider.getBlock(log.blockHash); }
event.getTransaction = () => { return this.provider.getTransaction(log.transactionHash); }
event.getTransactionReceipt = () => { return this.provider.getTransactionReceipt(log.transactionHash); }
this.emit(eventFilter.filter, ...args);
};
this.provider.on(eventFilter.filter, wrappedListener);
this._events.push({ eventFilter: eventFilter, listener: listener, wrappedListener: wrappedListener, once: once });
}
on(event: EventFilter | string, listener: Listener): Contract {
this._addEventListener(this._getEventFilter(event), listener, false);
return this;
}
once(event: EventFilter | string, listener: Listener): Contract {
this._addEventListener(this._getEventFilter(event), listener, true);
return this;
}
addListener(eventName: EventFilter | string, listener: Listener): Contract {
return this.on(eventName, listener);
}
emit(eventName: EventFilter | string, ...args: Array<any>): boolean {
if (!this.provider) { return false; }
let result = false;
let eventFilter = this._getEventFilter(eventName);
this._events = this._events.filter((event) => {
// Not this event (keep it for later)
if (event.eventFilter.eventTag !== eventFilter.eventTag) { return true; }
// Call the callback in the next event loop
setTimeout(() => {
event.listener.apply(this, args);
}, 0);
result = true;
// Reschedule it if it not "once"
return !(event.once);
});
return result;
}
listenerCount(eventName?: EventFilter | string): number {
if (!this.provider) { return 0; }
let eventFilter = this._getEventFilter(eventName);
return this._events.filter((event) => {
return event.eventFilter.eventTag === eventFilter.eventTag
}).length;
}
listeners(eventName: EventFilter | string): Array<Listener> {
if (!this.provider) { return []; }
let eventFilter = this._getEventFilter(eventName);
return this._events.filter((event) => {
return event.eventFilter.eventTag === eventFilter.eventTag
}).map((event) => { return event.listener; });
}
removeAllListeners(eventName: EventFilter | string): Contract {
if (!this.provider) { return this; }
let eventFilter = this._getEventFilter(eventName);
this._events = this._events.filter((event) => {
return event.eventFilter.eventTag !== eventFilter.eventTag
});
return this;
}
removeListener(eventName: any, listener: Listener): Contract {
if (!this.provider) { return this; }
let found = false;
let eventFilter = this._getEventFilter(eventName);
this._events = this._events.filter((event) => {
// Make sure this event and listener match
if (event.eventFilter.eventTag !== eventFilter.eventTag) { return true; }
if (event.listener !== listener) { return true; }
this.provider.removeListener(event.eventFilter.filter, event.wrappedListener);
// Already found a matching event in a previous loop
if (found) { return true; }
// REmove this event (returning false filters us out)
found = true;
return false;
});
return this;
}
}
export class ContractFactory {
readonly interface: Interface;
readonly bytecode: string;
readonly signer: Signer;
constructor(contractInterface: Array<string | ParamType> | string | Interface, bytecode: Arrayish | string | { object: string }, signer?: Signer) {
let bytecodeHex: string = null;
// Allow the bytecode object from the Solidity compiler
if (typeof(bytecode) === 'string') {
bytecodeHex = bytecode;
} else if (isArrayish(bytecode)) {
bytecodeHex = hexlify(bytecode);
} else if (typeof(bytecode.object) === 'string') {
bytecodeHex = (<any>bytecode).object;
} else {
errors.throwError('bytecode must be a valid hex string', errors.INVALID_ARGUMENT, { arg: 'bytecode', value: bytecode });
}
// Make sure it is 0x prefixed
if (bytecodeHex.substring(0, 2) !== '0x') {
bytecodeHex = '0x' + bytecodeHex;
}
if (!isHexString(bytecodeHex)) {
errors.throwError('bytecode must be a valid hex string', errors.INVALID_ARGUMENT, { arg: 'bytecode', value: bytecode });
}
if ((bytecodeHex.length % 2) !== 0) {
errors.throwError('bytecode must be valid data (even length)', errors.INVALID_ARGUMENT, { arg: 'bytecode', value: bytecode });
}
defineReadOnly(this, 'bytecode', bytecodeHex);
if (Interface.isInterface(contractInterface)) {
defineReadOnly(this, 'interface', contractInterface);
} else {
defineReadOnly(this, 'interface', new Interface(contractInterface));
}
if (signer && !Signer.isSigner(signer)) {
errors.throwError('invalid signer', errors.INVALID_ARGUMENT, { arg: 'signer', value: null });
}
defineReadOnly(this, 'signer', signer || null);
}
getDeployTransaction(...args: Array<any>): UnsignedTransaction {
let tx: UnsignedTransaction = { };
// If we have 1 additional argument, we allow transaction overrides
if (args.length === this.interface.deployFunction.inputs.length + 1) {
tx = shallowCopy(args.pop());
for (let key in tx) {
if (!allowedTransactionKeys[key]) {
throw new Error('unknown transaction override ' + key);
}
}
}
// Do not allow these to be overridden in a deployment transaction
['data', 'from', 'to'].forEach((key) => {
if ((<any>tx)[key] == null) { return; }
errors.throwError('cannot override ' + key, errors.UNSUPPORTED_OPERATION, { operation: key })
});
// Make sure the call matches the constructor signature
errors.checkArgumentCount(args.length, this.interface.deployFunction.inputs.length, 'in Contract constructor');
// Set the data to the bytecode + the encoded constructor arguments
tx.data = this.interface.deployFunction.encode(this.bytecode, args);
return tx
}
deploy(...args: Array<any>): Promise<Contract> {
// Get the deployment transaction (with optional overrides)
let tx = this.getDeployTransaction(...args);
// Send the deployment transaction
return this.signer.sendTransaction(tx).then((tx) => {
let contract = new Contract(getContractAddress(tx), this.interface, this.signer);
defineReadOnly(contract, 'deployTransaction', tx);
return contract;
});
}
attach(address: string): Contract {
return new Contract(address, this.interface, this.signer);
}
connect(signer: Signer) {
return new ContractFactory(this.interface, this.bytecode, signer);
}
static fromSolidity(compilerOutput: any, signer?: Signer): ContractFactory {
if (compilerOutput == null) {
errors.throwError('missing compiler output', errors.MISSING_ARGUMENT, { argument: 'compilerOutput' });
}
if (typeof(compilerOutput) === 'string') {
compilerOutput = JSON.parse(compilerOutput);
}
let abi = compilerOutput.abi;
let bytecode: any = null;
if (compilerOutput.bytecode) {
bytecode = compilerOutput.bytecode;
} else if (compilerOutput.evm && compilerOutput.evm.bytecode) {
bytecode = compilerOutput.evm.bytecode;
}
return new ContractFactory(abi, bytecode, signer);
}
}

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

@@ -0,0 +1,120 @@
'use strict';
// Unknown Error
export const UNKNOWN_ERROR = 'UNKNOWN_ERROR';
// Not implemented
export const NOT_IMPLEMENTED = 'NOT_IMPLEMENTED';
// Missing new operator to an object
// - name: The name of the class
export const MISSING_NEW = 'MISSING_NEW';
// Call exception
// - transaction: the transaction
// - address?: the contract address
// - args?: The arguments passed into the function
// - method?: The Solidity method signature
// - errorSignature?: The EIP848 error signature
// - errorArgs?: The EIP848 error parameters
// - reason: The reason (only for EIP848 "Error(string)")
export const CALL_EXCEPTION = 'CALL_EXCEPTION';
// Invalid argument (e.g. value is incompatible with type) to a function:
// - arg: The argument name that was invalid
// - value: The value of the argument
export const INVALID_ARGUMENT = 'INVALID_ARGUMENT';
// Missing argument to a function:
// - count: The number of arguments received
// - expectedCount: The number of arguments expected
export const MISSING_ARGUMENT = 'MISSING_ARGUMENT';
// Too many arguments
// - count: The number of arguments received
// - expectedCount: The number of arguments expected
export const UNEXPECTED_ARGUMENT = 'UNEXPECTED_ARGUMENT';
// Numeric Fault
// - operation: the operation being executed
// - fault: the reason this faulted
export const NUMERIC_FAULT = 'NUMERIC_FAULT';
// Insufficien funds (< value + gasLimit * gasPrice)
// - transaction: the transaction attempted
export const INSUFFICIENT_FUNDS = 'INSUFFICIENT_FUNDS';
// Nonce has already been used
// - transaction: the transaction attempted
export const NONCE_EXPIRED = 'NONCE_EXPIRED';
// The replacement fee for the transaction is too low
// - transaction: the transaction attempted
export const REPLACEMENT_UNDERPRICED = 'REPLACEMENT_UNDERPRICED';
// Unsupported operation
// - operation
export const UNSUPPORTED_OPERATION = 'UNSUPPORTED_OPERATION';
let _permanentCensorErrors = false;
let _censorErrors = false;
// @TODO: Enum
export function throwError(message: string, code: string, params: any): never {
if (_censorErrors) {
throw new Error('unknown error');
}
if (!code) { code = UNKNOWN_ERROR; }
if (!params) { params = {}; }
var messageDetails: Array<string> = [];
Object.keys(params).forEach(function(key) {
try {
messageDetails.push(key + '=' + JSON.stringify(params[key]));
} catch (error) {
messageDetails.push(key + '=' + JSON.stringify(params[key].toString()));
}
});
var reason = message;
if (messageDetails.length) {
message += ' (' + messageDetails.join(', ') + ')';
}
// @TODO: Any??
var error: any = new Error(message);
error.reason = reason;
error.code = code
Object.keys(params).forEach(function(key) {
error[key] = params[key];
});
throw error;
}
export function checkNew(self: any, kind: any): void {
if (!(self instanceof kind)) {
throwError('missing new', MISSING_NEW, { name: kind.name });
}
}
export function checkArgumentCount(count: number, expectedCount: number, suffix?: string): void {
if (!suffix) { suffix = ''; }
if (count < expectedCount) {
throwError('missing argument' + suffix, MISSING_ARGUMENT, { count: count, expectedCount: expectedCount });
}
if (count > expectedCount) {
throwError('too many arguments' + suffix, UNEXPECTED_ARGUMENT, { count: count, expectedCount: expectedCount });
}
}
export function setCensorship(censorship: boolean, permanent?: boolean): void {
if (_permanentCensorErrors) {
throwError('error censorship permanent', UNSUPPORTED_OPERATION, { operation: 'setCersorship' });
}
_censorErrors = !!censorship;
_permanentCensorErrors = !!permanent;
}

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

@@ -0,0 +1,78 @@
'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, Event, EventFilter } from './contract';
////////////////////////
// Helper Functions
function getDefaultProvider(network?: utils.Network | string): providers.BaseProvider {
return new providers.FallbackProvider([
new providers.InfuraProvider(network),
new providers.EtherscanProvider(network),
]);
}
////////////////////////
// Exports
export {
Signer,
Wallet,
VoidSigner,
getDefaultProvider,
providers,
Contract,
ContractFactory,
constants,
errors,
utils,
wordlists,
////////////////////////
// Compile-Time Constants
platform,
version,
////////////////////////
// Types
ContractFunction,
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,158 @@
import { BigNumber } from '../utils/bignumber';
import { isType, setType } from '../utils/properties';
///////////////////////////////
// Imported Types
import { Arrayish } from '../utils/bytes';
import { BigNumberish } from '../utils/bignumber';
import { Network } from '../utils/networks';
import { OnceBlockable } from '../utils/web';
import { Transaction } from '../utils/transaction';
///////////////////////////////
// Exported Types
export interface Block {
hash: string;
parentHash: string;
number: number;
timestamp: number;
nonce: string;
difficulty: number;
gasLimit: BigNumber;
gasUsed: BigNumber;
miner: string;
extraData: string;
transactions: Array<string>;
}
export type BlockTag = string | number;
export type Filter = {
fromBlock?: BlockTag,
toBlock?: BlockTag,
address?: string,
topics?: Array<string | Array<string>>,
}
export interface Log {
blockNumber?: number;
blockHash?: string;
transactionIndex?: number;
removed?: boolean;
transactionLogIndex?: number,
address: string;
data: string;
topics: Array<string>;
transactionHash?: string;
logIndex?: number;
}
export interface TransactionReceipt {
contractAddress?: string,
transactionIndex?: number,
root?: string,
gasUsed?: BigNumber,
logsBloom?: string,
blockHash?: string,
transactionHash?: string,
logs?: Array<Log>,
blockNumber?: number,
cumulativeGasUsed?: BigNumber,
byzantium: boolean,
status?: number
};
export type TransactionRequest = {
to?: string | Promise<string>,
from?: string | Promise<string>,
nonce?: number | string | Promise<number | string>,
gasLimit?: BigNumberish | Promise<BigNumberish>,
gasPrice?: BigNumberish | Promise<BigNumberish>,
data?: Arrayish | Promise<Arrayish>,
value?: BigNumberish | Promise<BigNumberish>,
chainId?: number | Promise<number>,
}
export interface TransactionResponse extends Transaction {
// Only if a transaction has been mined
blockNumber?: number,
blockHash?: string,
timestamp?: number,
// Not optional (as it is in Transaction)
from: string;
// The raw transaction
raw?: string,
// This function waits until the transaction has been mined
wait: (timeout?: 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): Promise<string>;
abstract estimateGas(transaction: TransactionRequest): Promise<BigNumber>;
abstract getBlock(blockHashOrBlockTag: BlockTag | string | Promise<BlockTag | string>, includeTransactions?: boolean): Promise<Block>;
abstract getTransaction(transactionHash: string): Promise<TransactionResponse>;
abstract getTransactionReceipt(transactionHash: string): Promise<TransactionReceipt>;
abstract getLogs(filter: Filter): Promise<Array<Log>>;
abstract resolveName(name: string | Promise<string>): Promise<string>;
abstract lookupAddress(address: string | Promise<string>): Promise<string>;
abstract on(eventName: EventType, listener: Listener): Provider;
abstract once(eventName: EventType, listener: Listener): Provider;
abstract listenerCount(eventName?: EventType): number;
abstract listeners(eventName: EventType): Array<Listener>;
abstract removeAllListeners(eventName: EventType): Provider;
abstract removeListener(eventName: EventType, listener: Listener): Provider;
// @TODO: This *could* be implemented here, but would pull in events...
abstract waitForTransaction(transactionHash: string, timeout?: number): Promise<TransactionReceipt>;
constructor() {
setType(this, 'Provider');
}
static isProvider(value: any): value is Provider {
return isType(value, 'Provider');
}
// readonly inherits: (child: any) => void;
}
//defineReadOnly(Signer, 'inherits', inheritable(Abstract));

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,314 @@
import { BaseProvider } from './base-provider';
import { hexlify, hexStripZeros } from '../utils/bytes';
import { defineReadOnly } from '../utils/properties';
import { fetchJson } from '../utils/web';
import * as errors from '../errors';
///////////////////////////////
// Imported Types
import { BlockTag, TransactionRequest, TransactionResponse } from './abstract-provider';
import { Networkish } from '../utils/networks';
///////////////////////////////
// The transaction has already been sanitized by the calls in Provider
function getTransactionString(transaction: TransactionRequest): string {
var result = [];
for (var key in transaction) {
if ((<any>transaction)[key] == null) { continue; }
var value = hexlify((<any>transaction)[key]);
if ((<any>{ gasLimit: true, gasPrice: true, nonce: true, value: true })[key]) {
value = hexStripZeros(value);
}
result.push(key + '=' + value);
}
return result.join('&');
}
function getResult(result: { status?: number, message?: string, result?: any }): any {
// getLogs, getHistory have weird success responses
if (result.status == 0 && (result.message === 'No records found' || result.message === 'No transactions found')) {
return result.result;
}
if (result.status != 1 || result.message != 'OK') {
// @TODO: not any
var error: any = new Error('invalid response');
error.result = JSON.stringify(result);
throw error;
}
return result.result;
}
function getJsonResult(result: { jsonrpc: string, result?: any, error?: { code?: number, data?: any, message?: string} } ): any {
if (result.jsonrpc != '2.0') {
// @TODO: not any
let error: any = new Error('invalid response');
error.result = JSON.stringify(result);
throw error;
}
if (result.error) {
// @TODO: not any
let error: any = new Error(result.error.message || 'unknown error');
if (result.error.code) { error.code = result.error.code; }
if (result.error.data) { error.data = result.error.data; }
throw error;
}
return result.result;
}
// The blockTag was normalized as a string by the Provider pre-perform operations
function checkLogTag(blockTag: string): number | "latest" {
if (blockTag === 'pending') { throw new Error('pending not supported'); }
if (blockTag === 'latest') { return blockTag; }
return parseInt(blockTag.substring(2), 16);
}
export class EtherscanProvider extends BaseProvider{
readonly baseUrl: string;
readonly apiKey: string;
constructor(network?: Networkish, apiKey?: string) {
super(network);
errors.checkNew(this, EtherscanProvider);
let name = 'invalid';
if (this.network) { name = this.network.name; }
let baseUrl = null;
switch(name) {
case 'homestead':
baseUrl = 'https://api.etherscan.io';
break;
case 'ropsten':
baseUrl = 'https://api-ropsten.etherscan.io';
break;
case 'rinkeby':
baseUrl = 'https://api-rinkeby.etherscan.io';
break;
case 'kovan':
baseUrl = 'https://api-kovan.etherscan.io';
break;
default:
throw new Error('unsupported network');
}
defineReadOnly(this, 'baseUrl', baseUrl);
defineReadOnly(this, 'apiKey', apiKey);
}
perform(method: string, params: any) {
//if (!params) { params = {}; }
var url = this.baseUrl;
let apiKey = '';
if (this.apiKey) { apiKey += '&apikey=' + this.apiKey; }
switch (method) {
case 'getBlockNumber':
url += '/api?module=proxy&action=eth_blockNumber' + apiKey;
return fetchJson(url, null, getJsonResult);
case 'getGasPrice':
url += '/api?module=proxy&action=eth_gasPrice' + apiKey;
return fetchJson(url, null, getJsonResult);
case 'getBalance':
// Returns base-10 result
url += '/api?module=account&action=balance&address=' + params.address;
url += '&tag=' + params.blockTag + apiKey;
return fetchJson(url, null, getResult);
case 'getTransactionCount':
url += '/api?module=proxy&action=eth_getTransactionCount&address=' + params.address;
url += '&tag=' + params.blockTag + apiKey;
return fetchJson(url, null, getJsonResult);
case 'getCode':
url += '/api?module=proxy&action=eth_getCode&address=' + params.address;
url += '&tag=' + params.blockTag + apiKey;
return fetchJson(url, null, getJsonResult);
case 'getStorageAt':
url += '/api?module=proxy&action=eth_getStorageAt&address=' + params.address;
url += '&position=' + params.position;
url += '&tag=' + params.blockTag + apiKey;
return fetchJson(url, null, getJsonResult);
case 'sendTransaction':
url += '/api?module=proxy&action=eth_sendRawTransaction&hex=' + params.signedTransaction;
url += apiKey;
return fetchJson(url, null, getJsonResult).catch((error) => {
// "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 fetchJson(url, null, getJsonResult);
}
throw new Error('getBlock by blockHash not implmeneted');
case 'getTransaction':
url += '/api?module=proxy&action=eth_getTransactionByHash&txhash=' + params.transactionHash;
url += apiKey;
return fetchJson(url, null, getJsonResult);
case 'getTransactionReceipt':
url += '/api?module=proxy&action=eth_getTransactionReceipt&txhash=' + params.transactionHash;
url += apiKey;
return fetchJson(url, null, getJsonResult);
case 'call':
var transaction = getTransactionString(params.transaction);
if (transaction) { transaction = '&' + transaction; }
url += '/api?module=proxy&action=eth_call' + transaction;
url += apiKey;
return fetchJson(url, null, getJsonResult);
case 'estimateGas':
var transaction = getTransactionString(params.transaction);
if (transaction) { transaction = '&' + transaction; }
url += '/api?module=proxy&action=eth_estimateGas&' + transaction;
url += apiKey;
return fetchJson(url, null, getJsonResult);
case 'getLogs':
url += '/api?module=logs&action=getLogs';
try {
if (params.filter.fromBlock) {
url += '&fromBlock=' + checkLogTag(params.filter.fromBlock);
}
if (params.filter.toBlock) {
url += '&toBlock=' + checkLogTag(params.filter.toBlock);
}
if (params.filter.address) {
url += '&address=' + params.filter.address;
}
// @TODO: We can handle slightly more complicated logs using the logs API
if (params.filter.topics && params.filter.topics.length > 0) {
if (params.filter.topics.length > 1) {
throw new Error('unsupported topic format');
}
var topic0 = params.filter.topics[0];
if (typeof(topic0) !== 'string' || topic0.length !== 66) {
throw new Error('unsupported topic0 format');
}
url += '&topic0=' + topic0;
}
} catch (error) {
return Promise.reject(error);
}
url += apiKey;
var self = this;
return fetchJson(url, null, 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 fetchJson(url, null, 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>) => {
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,105 @@
'use strict';
import { BaseProvider } from './base-provider';
// Imported Types
import { Network } from '../utils/networks';
import * as errors from '../errors';
// Returns:
// - true is all networks match
// - false if any network is null
// - throws if any 2 networks do not match
function checkNetworks(networks: Array<Network>): boolean {
var result = true;
let check: Network = null;
networks.forEach((network) => {
// Null
if (network == null) {
result = false;
return;
}
// Have nothing to compre to yet
if (check == null) {
check = network;
return;
}
// Matches!
if (check.name === network.name &&
check.chainId === network.chainId &&
check.ensAddress === network.ensAddress) { return; }
errors.throwError(
'provider mismatch',
errors.INVALID_ARGUMENT,
{ arg: 'networks', value: networks }
);
});
return result;
}
export class FallbackProvider extends BaseProvider {
private _providers: Array<BaseProvider>;
constructor(providers: Array<BaseProvider>) {
if (providers.length === 0) { throw new Error('no providers'); }
// All networks are ready, we can know the network for certain
let ready = checkNetworks(providers.map((p) => p.network));
if (ready) {
super(providers[0].network);
} else {
// The network won't be known until all child providers know
let ready = Promise.all(providers.map((p) => p.getNetwork())).then((networks) => {
if (!checkNetworks(networks)) {
errors.throwError('getNetwork returned null', errors.UNKNOWN_ERROR, { })
}
return networks[0];
});
super(ready);
}
errors.checkNew(this, FallbackProvider);
// Preserve a copy, so we don't get mutated
this._providers = providers.slice(0);
}
get providers(): Array<BaseProvider> {
// Return a copy, so we don't get mutated
return this._providers.slice(0);
}
perform(method: string, params: { [name: string]: any }): any {
// Creates a copy of the providers array
var providers = this.providers;
return new Promise((resolve, reject) => {
var firstError: Error = null;
function next() {
if (!providers.length) {
reject(firstError);
return;
}
var provider = providers.shift();
provider.perform(method, params).then((result) => {
return resolve(result);
}).catch((error) => {
if (!firstError) { firstError = error; }
setTimeout(next, 0);
});
}
next();
});
}
}

77
src.ts/providers/index.ts Normal file
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,59 @@
'use strict';
import { JsonRpcProvider, JsonRpcSigner } from './json-rpc-provider';
import { getNetwork } from '../utils/networks';
import { defineReadOnly } from '../utils/properties';
// Imported Types
import { Networkish } from '../utils/networks';
import * as errors from '../errors';
export class InfuraProvider extends JsonRpcProvider {
readonly apiAccessToken: string;
constructor(network?: Networkish, apiAccessToken?: string) {
network = getNetwork((network == null) ? 'homestead': network);
var host = null;
switch(network.name) {
case 'homestead':
host = 'mainnet.infura.io';
break;
case 'ropsten':
host = 'ropsten.infura.io';
break;
case 'rinkeby':
host = 'rinkeby.infura.io';
break;
case 'kovan':
host = 'kovan.infura.io';
break;
default:
throw new Error('unsupported network');
}
super('https://' + host + '/' + (apiAccessToken || ''), network);
errors.checkNew(this, InfuraProvider);
defineReadOnly(this, 'apiAccessToken', apiAccessToken || null);
}
protected _startPending(): void {
console.log('WARNING: INFURA does not support pending filters');
}
getSigner(address?: string): JsonRpcSigner {
errors.throwError(
'INFURA does not support signing',
errors.UNSUPPORTED_OPERATION,
{ operation: 'getSigner' }
);
return null;
}
listAccounts(): Promise<Array<string>> {
return Promise.resolve([]);
}
}

View File

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

View File

@@ -0,0 +1,387 @@
'use strict';
// See: https://github.com/ethereum/wiki/wiki/JSON-RPC
import { BaseProvider } from './base-provider';
import { Signer } from '../abstract-signer';
import { getAddress } from '../utils/address';
import { BigNumber } from '../utils/bignumber';
import { hexlify, hexStripZeros } from '../utils/bytes';
import { getNetwork } from '../utils/networks';
import { 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';
import * as errors from '../errors';
function timer(timeout: number): Promise<any> {
return new Promise(function(resolve) {
setTimeout(function() {
resolve();
}, timeout);
});
}
function getResult(payload: { error?: { code?: number, data?: any, message?: string }, result?: any }): any {
if (payload.error) {
// @TODO: not any
var error: any = new Error(payload.error.message);
error.code = payload.error.code;
error.data = payload.error.data;
throw error;
}
return payload.result;
}
function getLowerCase(value: string): string {
if (value) { return value.toLowerCase(); }
return value;
}
const _constructorGuard = {};
export class JsonRpcSigner extends Signer {
readonly provider: JsonRpcProvider;
private _index: number;
private _address: string;
constructor(constructorGuard: any, provider: JsonRpcProvider, addressOrIndex?: string | number) {
super();
errors.checkNew(this, JsonRpcSigner);
if (constructorGuard !== _constructorGuard) {
throw new Error('do not call the JsonRpcSigner constructor directly; use provider.getSigner');
}
defineReadOnly(this, 'provider', provider);
// Statically attach to a given address
if (addressOrIndex) {
if (typeof(addressOrIndex) === 'string') {
defineReadOnly(this, '_address', getAddress(addressOrIndex));
} else if (typeof(addressOrIndex) === 'number') {
defineReadOnly(this, '_index', addressOrIndex);
} else {
errors.throwError('invalid address or index', errors.INVALID_ARGUMENT, { argument: 'addressOrIndex', value: addressOrIndex });
}
} else {
defineReadOnly(this, '_index', 0);
}
}
/* May add back in the future; for now it is considered confusing. :)
get address(): string {
if (!this._address) {
errors.throwError('no sync sync address available; use getAddress', errors.UNSUPPORTED_OPERATION, { operation: 'address' });
}
return this._address
}
*/
getAddress(): Promise<string> {
if (this._address) {
return Promise.resolve(this._address);
}
return this.provider.send('eth_accounts', []).then((accounts) => {
if (accounts.length <= this._index) {
errors.throwError('unknown account #' + this._index, errors.UNSUPPORTED_OPERATION, { operation: 'getAddress' });
}
this._address = getAddress(accounts[this._index]);
return this._address;
});
}
getBalance(blockTag?: BlockTag): Promise<BigNumber> {
return this.provider.getBalance(this.getAddress(), blockTag);
}
getTransactionCount(blockTag?: BlockTag): Promise<number> {
return this.provider.getTransactionCount(this.getAddress(), blockTag);
}
sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse> {
let tx: TransactionRequest = shallowCopy(transaction);
if (tx.from == null) {
tx.from = this.getAddress().then((address) => {
if (!address) { return null; }
return address.toLowerCase();
});
}
if (transaction.gasLimit == null) {
tx.gasLimit = this.provider.estimateGas(tx);
}
return resolveProperties(tx).then((tx) => {
return this.provider.send('eth_sendTransaction', [ JsonRpcProvider.hexlifyTransaction(tx) ]).then((hash) => {
return poll(() => {
return this.provider.getTransaction(hash).then((tx: TransactionResponse) => {
if (tx === null) { return undefined; }
return this.provider._wrapTransaction(tx, hash);
});
}, { onceBlock: this.provider }).catch((error: Error) => {
(<any>error).transactionHash = hash;
throw error;
});
}, (error) => {
// See: JsonRpcProvider.sendTransaction (@TODO: Expose a ._throwError??)
if (error.responseText.indexOf('insufficient funds') >= 0) {
errors.throwError('insufficient funds', errors.INSUFFICIENT_FUNDS, {
transaction: tx
});
}
if (error.responseText.indexOf('nonce too low') >= 0) {
errors.throwError('nonce has already been used', errors.NONCE_EXPIRED, {
transaction: tx
});
}
if (error.responseText.indexOf('replacement transaction underpriced') >= 0) {
errors.throwError('replacement fee too low', errors.REPLACEMENT_UNDERPRICED, {
transaction: tx
});
}
throw error;
});
});
}
signMessage(message: Arrayish | string): Promise<string> {
var data = ((typeof(message) === 'string') ? toUtf8Bytes(message): message);
return this.getAddress().then((address) => {
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign
return this.provider.send('eth_sign', [ address.toLowerCase(), hexlify(data) ]);
});
}
unlock(password: string): Promise<boolean> {
var provider = this.provider;
return this.getAddress().then(function(address) {
return provider.send('personal_unlockAccount', [ address.toLowerCase(), password, null ]);
});
}
}
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> {
var request = {
method: method,
params: params,
id: 42,
jsonrpc: "2.0"
};
return fetchJson(this.connection, JSON.stringify(request), getResult);
}
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) => {
// "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), 'latest' ]);
case 'estimateGas':
return this.send('eth_estimateGas', [ JsonRpcProvider.hexlifyTransaction(params.transaction) ]);
case 'getLogs':
if (params.filter && params.filter.address != null) {
params.filter.address = getLowerCase(params.filter.address);
}
return this.send('eth_getLogs', [ params.filter ]);
default:
break;
}
errors.throwError(method + ' not implemented', errors.NOT_IMPLEMENTED, { operation: method });
return null;
}
protected _startPending(): void {
if (this._pendingFilter != null) { return; }
var self = this;
var pendingFilter: Promise<number> = this.send('eth_newPendingTransactionFilter', []);
this._pendingFilter = pendingFilter;
pendingFilter.then(function(filterId) {
function poll() {
self.send('eth_getFilterChanges', [ filterId ]).then(function(hashes: Array<string>) {
if (self._pendingFilter != pendingFilter) { return null; }
var seq = Promise.resolve();
hashes.forEach(function(hash) {
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
// @TODO: Not any, a dictionary of string to strings
static hexlifyTransaction(transaction: TransactionRequest): any {
var result: any = {};
// Some nodes (INFURA ropsten; INFURA mainnet is fine) don't like extra 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);
}

4
src.ts/shims/shims.ts Normal file
View File

@@ -0,0 +1,4 @@
require('setimmediate');
export const platform = "browser";

View File

@@ -0,0 +1,8 @@
import { Wordlist } from '../utils/wordlist';
import { langEn as _en } from '../wordlists/lang-en';
const en: Wordlist = _en;
export { en }

View File

@@ -6,3 +6,5 @@ try {
console.log('Warning: XMLHttpRequest is not defined');
module.exports.XMLHttpRequest = null;
}
export { }

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