forked from tornado-packages/ethers.js
Compare commits
302 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c9c5b4d641 | ||
|
|
5e86e02304 | ||
|
|
15cd0d8e58 | ||
|
|
061b0eae1d | ||
|
|
d3876303f2 | ||
|
|
2ad110548d | ||
|
|
ef5d19e5c9 | ||
|
|
5f6bc405dd | ||
|
|
1ca203ca38 | ||
|
|
c8a75ab01d | ||
|
|
8c6eb555de | ||
|
|
cb68403387 | ||
|
|
7b5ce86c5b | ||
|
|
f4e247fc92 | ||
|
|
e582bd73ee | ||
|
|
543e12c6ca | ||
|
|
57439af25d | ||
|
|
f4dd5d19e7 | ||
|
|
05e30f4680 | ||
|
|
9c87e569cf | ||
|
|
15761027df | ||
|
|
65fa0b7b22 | ||
|
|
8b2e57276f | ||
|
|
25337d4543 | ||
|
|
892f30a051 | ||
|
|
829a56cc79 | ||
|
|
9d04f2c1eb | ||
|
|
f02f4bc0c0 | ||
|
|
b9829f205f | ||
|
|
eef07e5a4f | ||
|
|
13e50ec6db | ||
|
|
9dcf6f3253 | ||
|
|
e0cefb0398 | ||
|
|
6018bf63f9 | ||
|
|
7aebe53e5b | ||
|
|
32a070d909 | ||
|
|
b5408bcbd0 | ||
|
|
284ffdfd1f | ||
|
|
ae4cd90441 | ||
|
|
5efd0c1f8a | ||
|
|
962f75038b | ||
|
|
ea5a56f0f0 | ||
|
|
afae5cd2f9 | ||
|
|
280dc56a74 | ||
|
|
2b9d8d6d6a | ||
|
|
2d35e5e9d7 | ||
|
|
102a4e8bf4 | ||
|
|
ce837e307e | ||
|
|
810e9e743e | ||
|
|
e092746165 | ||
|
|
388b658bfa | ||
|
|
0358e8afb7 | ||
|
|
2b88efea65 | ||
|
|
f4c3073304 | ||
|
|
e4d5786861 | ||
|
|
46a55a1d99 | ||
|
|
2c1022cde1 | ||
|
|
f9723c42cb | ||
|
|
3635a9bb40 | ||
|
|
9e0bfa733b | ||
|
|
a80a8a8ced | ||
|
|
b44a80800c | ||
|
|
e3b04bb3fc | ||
|
|
fcee62a3d9 | ||
|
|
7f1fbfc203 | ||
|
|
4f63b82bbb | ||
|
|
f63c844c42 | ||
|
|
694c0b3f7d | ||
|
|
1c45840148 | ||
|
|
421b2c857b | ||
|
|
bacf42a22f | ||
|
|
5c8703fb30 | ||
|
|
30a84d8577 | ||
|
|
3198bc27fd | ||
|
|
c7555516ff | ||
|
|
3cd0e8bf88 | ||
|
|
957ccd2eaf | ||
|
|
558ef2c00d | ||
|
|
b73a964bac | ||
|
|
3e091a2938 | ||
|
|
cb6d18035f | ||
|
|
4b15229832 | ||
|
|
f0dfa50848 | ||
|
|
22f007498d | ||
|
|
cc6f658ef1 | ||
|
|
9b885296d0 | ||
|
|
c6c5ba0118 | ||
|
|
a966a60656 | ||
|
|
d05da1e487 | ||
|
|
294d0fda61 | ||
|
|
29f3d2dea8 | ||
|
|
d2b6570c39 | ||
|
|
a1a765f6e4 | ||
|
|
1d98928475 | ||
|
|
73486d6f39 | ||
|
|
640ed5dc33 | ||
|
|
dda957c8e5 | ||
|
|
97b6d8a797 | ||
|
|
00bb20546b | ||
|
|
7cf8596906 | ||
|
|
5a0607d11b | ||
|
|
26207e7bb8 | ||
|
|
ae8d75fe6a | ||
|
|
4db19a1799 | ||
|
|
543ca19361 | ||
|
|
7014afaab6 | ||
|
|
62987ac3a8 | ||
|
|
3a5ec9b4ca | ||
|
|
3169cf2f60 | ||
|
|
bceefc7197 | ||
|
|
bfff3ea4cc | ||
|
|
7d4caa05ca | ||
|
|
004fd7d37d | ||
|
|
e3da92d1ee | ||
|
|
0bc72c5ed4 | ||
|
|
d0cf0c8f43 | ||
|
|
4d4fe60a55 | ||
|
|
81903e99d3 | ||
|
|
fd40f66766 | ||
|
|
2aa2a38e37 | ||
|
|
d701995bc0 | ||
|
|
c4084fe625 | ||
|
|
8c7c592bd1 | ||
|
|
b9aca357a9 | ||
|
|
26f5a558d6 | ||
|
|
960919d00a | ||
|
|
b712c88088 | ||
|
|
a062f75d38 | ||
|
|
a67e3d1d65 | ||
|
|
28927a6f1a | ||
|
|
ce864fa1b2 | ||
|
|
7e1e760b95 | ||
|
|
d19df0dcae | ||
|
|
b61b84dfc8 | ||
|
|
922de67a8b | ||
|
|
e8c657ba70 | ||
|
|
1ce4f75b0d | ||
|
|
175966674d | ||
|
|
d1ce9f4964 | ||
|
|
0f9d0ef533 | ||
|
|
e4df325e22 | ||
|
|
3b843a1782 | ||
|
|
614b2d0325 | ||
|
|
46e4db0b24 | ||
|
|
94171f6c57 | ||
|
|
e0391ee1bc | ||
|
|
7004cd0b92 | ||
|
|
b61643fecb | ||
|
|
8d6fa3dc93 | ||
|
|
e6c8db88bd | ||
|
|
937194b5ee | ||
|
|
5f3ceec6f9 | ||
|
|
3f9f0e02e5 | ||
|
|
d6260ae11a | ||
|
|
0ecfe4bafd | ||
|
|
df2a00a2fc | ||
|
|
dfdbb9bb28 | ||
|
|
979e374270 | ||
|
|
b7e143b4f3 | ||
|
|
272265f085 | ||
|
|
27402fafe6 | ||
|
|
ac4211d0c6 | ||
|
|
ee0faba708 | ||
|
|
e5d5871b95 | ||
|
|
4514229f27 | ||
|
|
7ac8cb63c8 | ||
|
|
8e83ceaedc | ||
|
|
b1d026b800 | ||
|
|
ebf42dc9e0 | ||
|
|
a8283ea99f | ||
|
|
564c5fa364 | ||
|
|
6faa978767 | ||
|
|
8a340c8ff3 | ||
|
|
0b35f1959a | ||
|
|
98ea4b59a0 | ||
|
|
ac2ad5a886 | ||
|
|
8fe19de3ce | ||
|
|
40559b7044 | ||
|
|
0b8b682fee | ||
|
|
083aca4693 | ||
|
|
19475a06f5 | ||
|
|
5fa014e01d | ||
|
|
df591cfc4c | ||
|
|
fcd57f9756 | ||
|
|
c348c60d5c | ||
|
|
1d2fe8993b | ||
|
|
18aa885aef | ||
|
|
a693576941 | ||
|
|
5c0475ce8c | ||
|
|
380c17a0a5 | ||
|
|
bd4ca0d4d3 | ||
|
|
7949444612 | ||
|
|
cf16b0ffa9 | ||
|
|
ec40c67c50 | ||
|
|
55ac3fef88 | ||
|
|
c4edd957b4 | ||
|
|
a19cc91cd4 | ||
|
|
c83596455b | ||
|
|
41a91c3c2d | ||
|
|
b77937ec4c | ||
|
|
1082105eea | ||
|
|
45923ad5dd | ||
|
|
aa67537097 | ||
|
|
63e5ad1d95 | ||
|
|
b2be7c807f | ||
|
|
dde33aa5a5 | ||
|
|
91f6e8ede6 | ||
|
|
7d8abf4463 | ||
|
|
6abab3b974 | ||
|
|
970613fad9 | ||
|
|
edbf8e319c | ||
|
|
28cd3cace2 | ||
|
|
45f8827c6e | ||
|
|
342be430cb | ||
|
|
622f02c369 | ||
|
|
263bf8047a | ||
|
|
2feced5937 | ||
|
|
6de0a765c9 | ||
|
|
4fd35224b3 | ||
|
|
52dc35bf0e | ||
|
|
2a111ab6ce | ||
|
|
5631f071c3 | ||
|
|
a0f92954c8 | ||
|
|
64b312ef6e | ||
|
|
baf92d4ec8 | ||
|
|
1d282c544a | ||
|
|
5b18a63ebc | ||
|
|
336df72e04 | ||
|
|
cf79190175 | ||
|
|
5da4917c27 | ||
|
|
c1b24e818c | ||
|
|
af893e79a4 | ||
|
|
aa48dfcdf4 | ||
|
|
efbfed0d40 | ||
|
|
059b03e090 | ||
|
|
7391cf8d19 | ||
|
|
be7a128bf4 | ||
|
|
b6794b96a5 | ||
|
|
4df78764a2 | ||
|
|
c411d9744d | ||
|
|
f7bfa50f15 | ||
|
|
8c0308dc58 | ||
|
|
4758b30cd3 | ||
|
|
6a8ca9c318 | ||
|
|
884593ab76 | ||
|
|
0517f70946 | ||
|
|
514aab7ee3 | ||
|
|
eaaa689f2f | ||
|
|
cf7872a498 | ||
|
|
f779b6e3cc | ||
|
|
a6cced81d6 | ||
|
|
62e51861b8 | ||
|
|
6c6e031254 | ||
|
|
48c07f6ef6 | ||
|
|
1b6c20341e | ||
|
|
75eb7ed507 | ||
|
|
cd360a4f8c | ||
|
|
fd27d3e2ce | ||
|
|
6737128b84 | ||
|
|
63b2eae4f5 | ||
|
|
0b34aea23a | ||
|
|
1290fa0073 | ||
|
|
da74e95f88 | ||
|
|
ac82a5cb83 | ||
|
|
ba21348c0f | ||
|
|
27207037a8 | ||
|
|
a612e1e8fa | ||
|
|
08d3547d4e | ||
|
|
7702d4ce7c | ||
|
|
3bf39b3bee | ||
|
|
417065bad6 | ||
|
|
3cb21dd6c7 | ||
|
|
cebf2aab29 | ||
|
|
72bf73f931 | ||
|
|
65caec2ea5 | ||
|
|
ea7c74d48d | ||
|
|
86df2306c4 | ||
|
|
6c12d8f30c | ||
|
|
e649a22ccf | ||
|
|
eddf9de8f9 | ||
|
|
7e91091cc3 | ||
|
|
9745a6ecbc | ||
|
|
3936b15afc | ||
|
|
efb7dce524 | ||
|
|
ac4b5d9199 | ||
|
|
fdb7114511 | ||
|
|
03f8c1db86 | ||
|
|
12b68b0242 | ||
|
|
28ddf485ed | ||
|
|
96d420e832 | ||
|
|
0f98bb5ac5 | ||
|
|
296473299c | ||
|
|
ce7718c87e | ||
|
|
1ec8f9cf85 | ||
|
|
46fd2deb8b | ||
|
|
5a9f440c8f | ||
|
|
1d67aa3e29 | ||
|
|
bd7e8e708f | ||
|
|
8a842af999 | ||
|
|
224c17a9b9 | ||
|
|
89e1cb02a4 | ||
|
|
3cdb6aad25 |
31
.eslintrc.js
Normal file
31
.eslintrc.js
Normal file
@@ -0,0 +1,31 @@
|
||||
module.exports = {
|
||||
"env": {
|
||||
"browser": true,
|
||||
"commonjs": true,
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
// "eslint:recommended",
|
||||
// "plugin:promise/recommended"
|
||||
],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 5
|
||||
},
|
||||
"plugins": [
|
||||
"promise"
|
||||
],
|
||||
"rules": {
|
||||
"promise/always-return": "error",
|
||||
"promise/no-return-wrap": "error",
|
||||
"promise/param-names": "error",
|
||||
"promise/catch-or-return": "error",
|
||||
"promise/no-native": "off",
|
||||
// "promise/no-nesting": "warn",
|
||||
"promise/no-promise-in-callback": "warn",
|
||||
"promise/no-callback-in-promise": "warn",
|
||||
// "promise/avoid-new": "warn",
|
||||
"promise/no-new-statics": "error",
|
||||
"promise/no-return-in-finally": "warn",
|
||||
"promise/valid-params": "warn"
|
||||
}
|
||||
};
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -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
|
||||
|
||||
13
.npmignore
13
.npmignore
@@ -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/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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']);
|
||||
};
|
||||
170
Gruntfile.js
170
Gruntfile.js
@@ -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']);
|
||||
};
|
||||
62
README.md
62
README.md
@@ -3,23 +3,30 @@ ethers.js
|
||||
|
||||
[](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
1
_version.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export declare const version = "4.0.0";
|
||||
3
_version.js
Normal file
3
_version.js
Normal file
@@ -0,0 +1,3 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.version = "4.0.0";
|
||||
11
abstract-signer.d.ts
vendored
Normal file
11
abstract-signer.d.ts
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Provider } from './providers/abstract-provider';
|
||||
import { Arrayish } from './utils/bytes';
|
||||
import { TransactionRequest, TransactionResponse } from './providers/abstract-provider';
|
||||
export declare abstract class Signer {
|
||||
readonly provider?: Provider;
|
||||
abstract getAddress(): Promise<string>;
|
||||
abstract signMessage(message: Arrayish | string): Promise<string>;
|
||||
abstract sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse>;
|
||||
constructor();
|
||||
static isSigner(value: any): value is Signer;
|
||||
}
|
||||
14
abstract-signer.js
Normal file
14
abstract-signer.js
Normal file
@@ -0,0 +1,14 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var properties_1 = require("./utils/properties");
|
||||
var Signer = /** @class */ (function () {
|
||||
function Signer() {
|
||||
properties_1.setType(this, 'Signer');
|
||||
}
|
||||
Signer.isSigner = function (value) {
|
||||
return properties_1.isType(value, 'Signer');
|
||||
};
|
||||
return Signer;
|
||||
}());
|
||||
exports.Signer = Signer;
|
||||
//defineReadOnly(Signer, 'inherits', inheritable(Signer));
|
||||
11
constants.d.ts
vendored
Normal file
11
constants.d.ts
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { BigNumber } from './utils/bignumber';
|
||||
declare const AddressZero = "0x0000000000000000000000000000000000000000";
|
||||
declare const HashZero = "0x0000000000000000000000000000000000000000000000000000000000000000";
|
||||
declare const EtherSymbol = "\u039E";
|
||||
declare const NegativeOne: BigNumber;
|
||||
declare const Zero: BigNumber;
|
||||
declare const One: BigNumber;
|
||||
declare const Two: BigNumber;
|
||||
declare const WeiPerEther: BigNumber;
|
||||
declare const MaxUint256: BigNumber;
|
||||
export { AddressZero, HashZero, EtherSymbol, NegativeOne, Zero, One, Two, WeiPerEther, MaxUint256 };
|
||||
24
constants.js
Normal file
24
constants.js
Normal file
@@ -0,0 +1,24 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var bignumber_1 = require("./utils/bignumber");
|
||||
var AddressZero = '0x0000000000000000000000000000000000000000';
|
||||
exports.AddressZero = AddressZero;
|
||||
var HashZero = '0x0000000000000000000000000000000000000000000000000000000000000000';
|
||||
exports.HashZero = HashZero;
|
||||
// NFKD (decomposed)
|
||||
//const EtherSymbol = '\uD835\uDF63';
|
||||
// NFKC (composed)
|
||||
var EtherSymbol = '\u039e';
|
||||
exports.EtherSymbol = EtherSymbol;
|
||||
var NegativeOne = bignumber_1.bigNumberify(-1);
|
||||
exports.NegativeOne = NegativeOne;
|
||||
var Zero = bignumber_1.bigNumberify(0);
|
||||
exports.Zero = Zero;
|
||||
var One = bignumber_1.bigNumberify(1);
|
||||
exports.One = One;
|
||||
var Two = bignumber_1.bigNumberify(2);
|
||||
exports.Two = Two;
|
||||
var WeiPerEther = bignumber_1.bigNumberify('1000000000000000000');
|
||||
exports.WeiPerEther = WeiPerEther;
|
||||
var MaxUint256 = bignumber_1.bigNumberify('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
|
||||
exports.MaxUint256 = MaxUint256;
|
||||
79
contract.d.ts
vendored
Normal file
79
contract.d.ts
vendored
Normal 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
647
contract.js
Normal 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;
|
||||
@@ -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;
|
||||
@@ -1,10 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var Contract = require('./contract.js');
|
||||
var Interface = require('./interface.js');
|
||||
|
||||
module.exports = {
|
||||
Contract: Contract,
|
||||
Interface: Interface,
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
222
examples/wallet/index.html → dist/demo/index.html
vendored
222
examples/wallet/index.html → dist/demo/index.html
vendored
@@ -1,42 +1,13 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Ethereum Wallet</title>
|
||||
<link rel="stylesheet" type="text/css" href="../style.css">
|
||||
<link rel="stylesheet" type="text/css" href="./style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="centerer" id="screen-select">
|
||||
<div class="centered">
|
||||
<h1>Ethereum Wallet Tool</h1>
|
||||
<hr />
|
||||
<h2>Summon Brain Wallet</h2>
|
||||
<p>
|
||||
A brain wallet generates an <i>Ethereum</i> wallet from a username and a password
|
||||
without using any servers to store your information. If you lose your username
|
||||
or password, <b>no one</b> can help you recover them. Anyone who can guess your
|
||||
username and password can steal your funds. Brain wallets should <b>not</b> be
|
||||
considered a safe way to store large amounts of ether nor for long periods of time.
|
||||
</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>E-mail Address:</th>
|
||||
<td><input type="text" placeholder="(e-mail address)" id="select-brainwallet-username" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Password:</th>
|
||||
<td><input type="password" placeholder="(password)" id="select-brainwallet-password" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Confirm Password:</th>
|
||||
<td><input type="password" placeholder="(same password)" id="select-brainwallet-confirm-password" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>
|
||||
<div id="select-submit-brainwallet" class="submit disable">Summon Brain Wallet</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr />
|
||||
<h2>Load JSON Wallet</h2>
|
||||
<p>
|
||||
If you have a JSON wallet file from <i>geth</i> or from the initial <i>Ethereum</i>
|
||||
@@ -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>
|
||||
20
examples/style.css → dist/demo/style.css
vendored
20
examples/style.css → dist/demo/style.css
vendored
@@ -62,14 +62,14 @@ input[type=text] {
|
||||
border: 1px solid #555;
|
||||
font-size: 16px;
|
||||
padding: 10px;
|
||||
width: 500px;
|
||||
width: 501px;
|
||||
}
|
||||
|
||||
input[type=password] {
|
||||
border: 1px solid #555;
|
||||
font-size: 16px;
|
||||
padding: 10px;
|
||||
width: 500px;
|
||||
width: 501px;
|
||||
}
|
||||
|
||||
input[type=file] {
|
||||
@@ -81,7 +81,7 @@ input[type=file] {
|
||||
padding: 10px;
|
||||
position: relative;
|
||||
dvisibility: hidden;
|
||||
width: 500px;
|
||||
width: 501px;
|
||||
}
|
||||
|
||||
input[type=text].readonly {
|
||||
@@ -105,15 +105,27 @@ input[type=text].readonly {
|
||||
box-shadow: 0px 0px 5px #888;
|
||||
}
|
||||
|
||||
.clearfix {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.option {
|
||||
border: 1px solid #999;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
float: left;
|
||||
font-size: 16px;
|
||||
opacity: 0.3;
|
||||
padding: 10px;
|
||||
margin-right: 20px;
|
||||
text-align: center;
|
||||
transition: opacity 0.1s linear;
|
||||
width: 200px;
|
||||
width: 110px;
|
||||
}
|
||||
|
||||
.option span {
|
||||
font-size: 0.8em;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.option.selected {
|
||||
5763
dist/ethers-contracts.js
vendored
5763
dist/ethers-contracts.js
vendored
File diff suppressed because it is too large
Load Diff
3
dist/ethers-contracts.min.js
vendored
3
dist/ethers-contracts.min.js
vendored
File diff suppressed because one or more lines are too long
6686
dist/ethers-providers.js
vendored
6686
dist/ethers-providers.js
vendored
File diff suppressed because it is too large
Load Diff
3
dist/ethers-providers.min.js
vendored
3
dist/ethers-providers.min.js
vendored
File diff suppressed because one or more lines are too long
6635
dist/ethers-utils.js
vendored
6635
dist/ethers-utils.js
vendored
File diff suppressed because it is too large
Load Diff
3
dist/ethers-utils.min.js
vendored
3
dist/ethers-utils.min.js
vendored
File diff suppressed because one or more lines are too long
12012
dist/ethers-wallet.js
vendored
12012
dist/ethers-wallet.js
vendored
File diff suppressed because one or more lines are too long
7
dist/ethers-wallet.min.js
vendored
7
dist/ethers-wallet.min.js
vendored
File diff suppressed because one or more lines are too long
11583
dist/ethers.js
vendored
11583
dist/ethers.js
vendored
File diff suppressed because one or more lines are too long
10
dist/ethers.min.js
vendored
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
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
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
1
dist/wordlist-it.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/wordlist-ja.js
vendored
Normal file
1
dist/wordlist-ja.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/wordlist-ko.js
vendored
Normal file
1
dist/wordlist-ko.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/wordlist-zh.js
vendored
Normal file
1
dist/wordlist-zh.js
vendored
Normal file
File diff suppressed because one or more lines are too long
16
errors.d.ts
vendored
Normal file
16
errors.d.ts
vendored
Normal 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
108
errors.js
Normal 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
13
ethers.d.ts
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Contract, ContractFactory, VoidSigner } from './contract';
|
||||
import { Signer } from './abstract-signer';
|
||||
import { Wallet } from './wallet';
|
||||
import * as constants from './constants';
|
||||
import * as errors from './errors';
|
||||
import * as providers from './providers';
|
||||
import * as utils from './utils';
|
||||
import * as wordlists from './wordlists';
|
||||
import { platform } from './utils/shims';
|
||||
import { version } from './_version';
|
||||
import { ContractFunction, 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
44
ethers.js
Normal file
@@ -0,0 +1,44 @@
|
||||
'use strict';
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
||||
result["default"] = mod;
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var contract_1 = require("./contract");
|
||||
exports.Contract = contract_1.Contract;
|
||||
exports.ContractFactory = contract_1.ContractFactory;
|
||||
exports.VoidSigner = contract_1.VoidSigner;
|
||||
var abstract_signer_1 = require("./abstract-signer");
|
||||
exports.Signer = abstract_signer_1.Signer;
|
||||
var wallet_1 = require("./wallet");
|
||||
exports.Wallet = wallet_1.Wallet;
|
||||
var constants = __importStar(require("./constants"));
|
||||
exports.constants = constants;
|
||||
var errors = __importStar(require("./errors"));
|
||||
exports.errors = errors;
|
||||
var providers = __importStar(require("./providers"));
|
||||
exports.providers = providers;
|
||||
var utils = __importStar(require("./utils"));
|
||||
exports.utils = utils;
|
||||
var wordlists = __importStar(require("./wordlists"));
|
||||
exports.wordlists = wordlists;
|
||||
////////////////////////
|
||||
// Compile-Time Constants
|
||||
// This is empty in node, and used by browserify to inject extra goodies
|
||||
var shims_1 = require("./utils/shims");
|
||||
exports.platform = shims_1.platform;
|
||||
// This is generated by "npm run dist"
|
||||
var _version_1 = require("./_version");
|
||||
exports.version = _version_1.version;
|
||||
////////////////////////
|
||||
// Helper Functions
|
||||
function getDefaultProvider(network) {
|
||||
return new providers.FallbackProvider([
|
||||
new providers.InfuraProvider(network),
|
||||
new providers.EtherscanProvider(network),
|
||||
]);
|
||||
}
|
||||
exports.getDefaultProvider = getDefaultProvider;
|
||||
@@ -1,284 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Ethereum Classic Split Tool</title>
|
||||
<link rel="stylesheet" type="text/css" href="../style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="centerer">
|
||||
<div class="centered">
|
||||
<h1>Split Ether Classic</h1>
|
||||
<hr />
|
||||
<h2>What does this tool do?</h2>
|
||||
<p>
|
||||
This tool will take a <i>geth</i> (or crowdsale) JSON wallet, decrypt it and
|
||||
send all its funds to <a href="http://etherscan.io/address/0x3474627d4f63a678266bc17171d87f8570936622#code">this contract</a>,
|
||||
which will:
|
||||
</p>
|
||||
<ul>
|
||||
<li>On the ETH branch — send the funds back to the original address</li>
|
||||
<li>On the ETC branch — send the funds to the provided target address (for example, a <a href="https://www.poloniex.com">Poloniex</a> deposit address)</li>
|
||||
</ul>
|
||||
<br />
|
||||
<h3>Disclaimer:</h3>
|
||||
<p>
|
||||
I threw this together in couple of hours, mainly to split my own ether
|
||||
and test my <i>ethers-wallet</i> library (which is still missing features
|
||||
and is itself not ready for production use). Testing has been fairly minimal
|
||||
beyond trying it on a few wallets. <b>Use this at your own risk.</b>
|
||||
</p>
|
||||
<hr />
|
||||
<h2>Check Current ETC Balance</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<th>ETC Address:</th>
|
||||
<td><input type="text" id="checkAddress" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>
|
||||
<div id="submitCheck" class="submit disable">Check Classic Ether Balance</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr />
|
||||
<h2>Split ETC/ETH</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<th>JSON Wallet:</th>
|
||||
<td><div class="file" id="drop">Drop JSON wallet file here</div><input type="file" id="json" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Password:</th>
|
||||
<td><input type="password" id="password" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Target ETC Address:</th>
|
||||
<td><input type="text" id="targetAddress" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>
|
||||
<div id="submitSplit" class="submit disable">Split Classic Ether</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="../../dist/ethers-wallet.js"></script>
|
||||
<script type="text/javascript">
|
||||
var submitCheck = document.getElementById('submitCheck');
|
||||
var submitSplit = document.getElementById('submitSplit');
|
||||
|
||||
var inputJson = document.getElementById('json');
|
||||
var inputCheckAddress = document.getElementById('checkAddress')
|
||||
var inputTargetAddress = document.getElementById('targetAddress')
|
||||
var inputPassword = document.getElementById('password');
|
||||
|
||||
var targetDrop = document.getElementById('drop');
|
||||
|
||||
var provider = new Wallet.providers.HttpProvider('https://linode-newark.ethers.ws:8002');
|
||||
|
||||
var contractAddress = '0x3474627d4f63a678266bc17171d87f8570936622';
|
||||
var contractAbi = JSON.parse('[{"constant":false,"inputs":[{"name":"balance","type":"uint256"}],"name":"claimDonations","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"isClassic","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"classicAddress","type":"address"}],"name":"split","outputs":[],"type":"function"},{"inputs":[],"type":"constructor"}]');
|
||||
|
||||
submitCheck.onclick = function() {
|
||||
if (submitCheck.classList.contains('disable')) { return; }
|
||||
|
||||
var address = document.getElementById('checkAddress').value;
|
||||
try {
|
||||
address = Wallet.getAddress(address);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
alert('invalid address');
|
||||
return;
|
||||
}
|
||||
provider.getBalance(address, 'latest').then(function(balance) {
|
||||
alert('Balance: ' + Wallet.formatEther(balance, {commify: true}) + ' ' + Wallet.etherSymbol);
|
||||
}, function(error) {
|
||||
if (error) {
|
||||
console.log(error);
|
||||
alert('Faied to get balance');
|
||||
}
|
||||
});
|
||||
|
||||
console.log('check', address);
|
||||
}
|
||||
|
||||
submitSplit.onclick = function() {
|
||||
if (submitSplit.classList.contains('disable')) { return; }
|
||||
|
||||
function done() {
|
||||
submitSplit.textContent = 'Split Classic Ether';
|
||||
inputJson.readOnly = false;
|
||||
inputPassword.readOnly = false;
|
||||
inputTargetAddress.readOnly = false;
|
||||
checkSplit();
|
||||
}
|
||||
|
||||
inputJson.readOnly = true;
|
||||
inputPassword.readOnly = true;
|
||||
inputTargetAddress.readOnly = true;
|
||||
|
||||
submitSplit.classList.add('disable');
|
||||
|
||||
var files = inputJson.files;
|
||||
if (files.length !== 1) {
|
||||
alert('No wallet found');
|
||||
return done();
|
||||
}
|
||||
|
||||
var password = new Wallet.utils.Buffer(inputPassword.value, 'utf8');
|
||||
|
||||
var targetAddress = document.getElementById('targetAddress').value;
|
||||
try {
|
||||
targetAddress = Wallet.getAddress(targetAddress);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
alert('invalid target address');
|
||||
return done();
|
||||
}
|
||||
|
||||
function processWallet(wallet) {
|
||||
console.log(wallet);
|
||||
if (wallet.address === targetAddress) {
|
||||
alert('Wallet address and target address cannot be the same.');
|
||||
return done();
|
||||
}
|
||||
|
||||
submitSplit.textContent = 'Decrypted \u2014 Processing (please wait)';
|
||||
|
||||
var contract = wallet.getContract(contractAddress, contractAbi);
|
||||
var data = contract.interface.split(targetAddress).data;
|
||||
|
||||
var transaction = {
|
||||
to: contractAddress,
|
||||
data: data
|
||||
};
|
||||
|
||||
Promise.all([
|
||||
provider.getBalance(wallet.address, 'latest'),
|
||||
provider.getTransactionCount(wallet.address, 'latest'),
|
||||
provider.getGasPrice(),
|
||||
// provider.estimateGas(transaction)
|
||||
]).then(function(results) {
|
||||
var balance = results[0];
|
||||
var transactionCount = results[1];
|
||||
var gasPrice = results[2];
|
||||
var gasEstimate = new Wallet.utils.BN('29735');
|
||||
//results[3].add(new Wallet.utils.BN(21000));
|
||||
|
||||
transaction.gasPrice = '0x' + gasPrice.toString(16);
|
||||
transaction.nonce = transactionCount;
|
||||
transaction.gasLimit = gasEstimate;
|
||||
|
||||
var toSend = balance.sub(gasPrice.mul(gasEstimate));
|
||||
transaction.value = toSend;
|
||||
|
||||
console.log(transaction);
|
||||
|
||||
var accept = confirm('Balance: ' + Wallet.formatEther(balance, {commify: true}) + ' ' + Wallet.etherSymbol + '. Are you sure you want to split Classic Ether to ' + targetAddress + '?');
|
||||
if (accept) {
|
||||
var signedTransaction = wallet.sign(transaction);
|
||||
function showError(error) {
|
||||
console.log(error);
|
||||
done();
|
||||
alert('Error sending transaction');
|
||||
}
|
||||
provider.sendTransaction(signedTransaction).then(function(txid) {
|
||||
if (txid.match(/^0x0+$/)) {
|
||||
showError(new Error('txid was zero'));
|
||||
return;
|
||||
}
|
||||
done();
|
||||
console.log('txid:' + txid);
|
||||
alert('Success \u2014 ' + txid);
|
||||
}, function(error) {
|
||||
showError(error);
|
||||
});
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
}, function(error) {
|
||||
done();
|
||||
console.log(error);
|
||||
alert('Error: Faied to fetch info');
|
||||
});
|
||||
}
|
||||
|
||||
var fileReader = new FileReader();
|
||||
fileReader.onload = function(e) {
|
||||
var json = e.target.result;
|
||||
if (Wallet.isCrowdsaleWallet(json)) {
|
||||
var wallet = Wallet.decryptCrowdsale(json, password);
|
||||
processWallet(wallet);
|
||||
} else if (Wallet.isValidWallet(json)) {
|
||||
Wallet.decrypt(json, password, function(error, wallet, progress) {
|
||||
if (error) {
|
||||
done();
|
||||
console.log(error);
|
||||
if (error.message === 'invalid password') {
|
||||
alert('Wrong Password');
|
||||
} else {
|
||||
alert('Error Decrypting Wallet');
|
||||
}
|
||||
} else if (wallet) {
|
||||
processWallet(wallet);
|
||||
} else {
|
||||
submitSplit.textContent = 'Decrypting \u2014 ' + (parseInt(progress * 100) + '%');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
alert('unknown walet format');
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
fileReader.readAsText(files[0]);
|
||||
}
|
||||
|
||||
inputCheckAddress.oninput = function() {
|
||||
try {
|
||||
Wallet.getAddress(inputCheckAddress.value);
|
||||
submitCheck.classList.remove('disable');
|
||||
} catch (error) {
|
||||
submitCheck.classList.add('disable');
|
||||
}
|
||||
}
|
||||
|
||||
function checkSplit() {
|
||||
if (!inputJson.files || inputJson.files.length !== 1) {
|
||||
submitSplit.classList.add('disable');
|
||||
return;
|
||||
}
|
||||
targetDrop.textContent = inputJson.files[0].name;
|
||||
|
||||
try {
|
||||
Wallet.getAddress(inputTargetAddress.value);
|
||||
} catch (error) {
|
||||
submitSplit.classList.add('disable');
|
||||
return;
|
||||
}
|
||||
|
||||
submitSplit.classList.remove('disable');
|
||||
}
|
||||
|
||||
inputTargetAddress.oninput = checkSplit;
|
||||
inputJson.onchange = checkSplit;
|
||||
|
||||
inputJson.addEventListener('dragover', function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
targetDrop.classList.add('highlight');
|
||||
}, true);
|
||||
|
||||
inputJson.addEventListener('drop', function(event) {
|
||||
targetDrop.classList.remove('highlight');
|
||||
}, true);
|
||||
|
||||
|
||||
var check
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
326
gulpfile.js
Normal file
326
gulpfile.js
Normal 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
3
index.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import * as ethers from './ethers';
|
||||
export { ethers };
|
||||
export * from './ethers';
|
||||
38
index.js
38
index.js
@@ -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
7544
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
57
package.json
57
package.json
@@ -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
99
providers/abstract-provider.d.ts
vendored
Normal 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;
|
||||
}
|
||||
18
providers/abstract-provider.js
Normal file
18
providers/abstract-provider.js
Normal file
@@ -0,0 +1,18 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var properties_1 = require("../utils/properties");
|
||||
;
|
||||
;
|
||||
///////////////////////////////
|
||||
// Exported Abstracts
|
||||
var Provider = /** @class */ (function () {
|
||||
function Provider() {
|
||||
properties_1.setType(this, 'Provider');
|
||||
}
|
||||
Provider.isProvider = function (value) {
|
||||
return properties_1.isType(value, 'Provider');
|
||||
};
|
||||
return Provider;
|
||||
}());
|
||||
exports.Provider = Provider;
|
||||
//defineReadOnly(Signer, 'inherits', inheritable(Abstract));
|
||||
67
providers/base-provider.d.ts
vendored
Normal file
67
providers/base-provider.d.ts
vendored
Normal 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
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
10
providers/etherscan-provider.d.ts
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
import { BaseProvider } from './base-provider';
|
||||
import { BlockTag, TransactionResponse } from './abstract-provider';
|
||||
import { Networkish } from '../utils/networks';
|
||||
export declare class EtherscanProvider extends BaseProvider {
|
||||
readonly baseUrl: string;
|
||||
readonly apiKey: string;
|
||||
constructor(network?: Networkish, apiKey?: string);
|
||||
perform(method: string, params: any): Promise<any>;
|
||||
getHistory(addressOrName: string | Promise<string>, startBlock?: BlockTag, endBlock?: BlockTag): Promise<Array<TransactionResponse>>;
|
||||
}
|
||||
@@ -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
9
providers/fallback-provider.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import { BaseProvider } from './base-provider';
|
||||
export declare class FallbackProvider extends BaseProvider {
|
||||
private _providers;
|
||||
constructor(providers: Array<BaseProvider>);
|
||||
readonly providers: Array<BaseProvider>;
|
||||
perform(method: string, params: {
|
||||
[name: string]: any;
|
||||
}): any;
|
||||
}
|
||||
@@ -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
11
providers/index.d.ts
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Provider } from './abstract-provider';
|
||||
import { BaseProvider } from './base-provider';
|
||||
import { EtherscanProvider } from './etherscan-provider';
|
||||
import { FallbackProvider } from './fallback-provider';
|
||||
import { IpcProvider } from './ipc-provider';
|
||||
import { InfuraProvider } from './infura-provider';
|
||||
import { JsonRpcProvider, JsonRpcSigner } from './json-rpc-provider';
|
||||
import { Web3Provider } from './web3-provider';
|
||||
import { Block, BlockTag, EventType, Filter, Log, Listener, TransactionReceipt, TransactionRequest, TransactionResponse } from './abstract-provider';
|
||||
import { AsyncSendable } from './web3-provider';
|
||||
export { Provider, BaseProvider, FallbackProvider, EtherscanProvider, InfuraProvider, JsonRpcProvider, Web3Provider, IpcProvider, JsonRpcSigner, Block, BlockTag, EventType, Filter, Log, Listener, TransactionReceipt, TransactionRequest, TransactionResponse, AsyncSendable };
|
||||
@@ -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
9
providers/infura-provider.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import { JsonRpcProvider, JsonRpcSigner } from './json-rpc-provider';
|
||||
import { Networkish } from '../utils/networks';
|
||||
export declare class InfuraProvider extends JsonRpcProvider {
|
||||
readonly apiAccessToken: string;
|
||||
constructor(network?: Networkish, apiAccessToken?: string);
|
||||
protected _startPending(): void;
|
||||
getSigner(address?: string): JsonRpcSigner;
|
||||
listAccounts(): Promise<Array<string>>;
|
||||
}
|
||||
@@ -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
7
providers/ipc-provider.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
import { JsonRpcProvider } from './json-rpc-provider';
|
||||
import { Networkish } from '../utils/networks';
|
||||
export declare class IpcProvider extends JsonRpcProvider {
|
||||
readonly path: string;
|
||||
constructor(path: string, network?: Networkish);
|
||||
send(method: string, params: any): Promise<any>;
|
||||
}
|
||||
78
providers/ipc-provider.js
Normal file
78
providers/ipc-provider.js
Normal file
@@ -0,0 +1,78 @@
|
||||
"use strict";
|
||||
var __extends = (this && this.__extends) || (function () {
|
||||
var extendStatics = Object.setPrototypeOf ||
|
||||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
||||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
|
||||
return function (d, b) {
|
||||
extendStatics(d, b);
|
||||
function __() { this.constructor = d; }
|
||||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
||||
};
|
||||
})();
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
||||
result["default"] = mod;
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var net_1 = __importDefault(require("net"));
|
||||
var json_rpc_provider_1 = require("./json-rpc-provider");
|
||||
var properties_1 = require("../utils/properties");
|
||||
var errors = __importStar(require("../errors"));
|
||||
var IpcProvider = /** @class */ (function (_super) {
|
||||
__extends(IpcProvider, _super);
|
||||
function IpcProvider(path, network) {
|
||||
var _this = this;
|
||||
if (path == null) {
|
||||
errors.throwError('missing path', errors.MISSING_ARGUMENT, { arg: 'path' });
|
||||
}
|
||||
_this = _super.call(this, 'ipc://' + path, network) || this;
|
||||
errors.checkNew(_this, IpcProvider);
|
||||
properties_1.defineReadOnly(_this, 'path', path);
|
||||
return _this;
|
||||
}
|
||||
// @TODO: Create a connection to the IPC path and use filters instead of polling for block
|
||||
IpcProvider.prototype.send = function (method, params) {
|
||||
// This method is very simple right now. We create a new socket
|
||||
// connection each time, which may be slower, but the main
|
||||
// advantage we are aiming for now is security. This simplifies
|
||||
// multiplexing requests (since we do not need to multiplex).
|
||||
var _this = this;
|
||||
var payload = JSON.stringify({
|
||||
method: method,
|
||||
params: params,
|
||||
id: 42,
|
||||
jsonrpc: "2.0"
|
||||
});
|
||||
return new Promise(function (resolve, reject) {
|
||||
var stream = net_1.default.connect(_this.path);
|
||||
stream.on('data', function (data) {
|
||||
try {
|
||||
resolve(JSON.parse(data.toString('utf8')).result);
|
||||
// @TODO: Better pull apart the error
|
||||
stream.destroy();
|
||||
}
|
||||
catch (error) {
|
||||
reject(error);
|
||||
stream.destroy();
|
||||
}
|
||||
});
|
||||
stream.on('end', function () {
|
||||
stream.destroy();
|
||||
});
|
||||
stream.on('error', function (error) {
|
||||
reject(error);
|
||||
stream.destroy();
|
||||
});
|
||||
stream.write(payload);
|
||||
stream.end();
|
||||
});
|
||||
};
|
||||
return IpcProvider;
|
||||
}(json_rpc_provider_1.JsonRpcProvider));
|
||||
exports.IpcProvider = IpcProvider;
|
||||
31
providers/json-rpc-provider.d.ts
vendored
Normal file
31
providers/json-rpc-provider.d.ts
vendored
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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
15
providers/web3-provider.d.ts
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
import { JsonRpcProvider } from './json-rpc-provider';
|
||||
import { Networkish } from '../utils/networks';
|
||||
export declare type AsyncSendable = {
|
||||
isMetaMask?: boolean;
|
||||
host?: string;
|
||||
path?: string;
|
||||
sendAsync?: (request: any, callback: (error: any, response: any) => void) => void;
|
||||
send?: (request: any, callback: (error: any, response: any) => void) => void;
|
||||
};
|
||||
export declare class Web3Provider extends JsonRpcProvider {
|
||||
readonly _web3Provider: AsyncSendable;
|
||||
private _sendAsync;
|
||||
constructor(web3Provider: AsyncSendable, network?: Networkish);
|
||||
send(method: string, params: any): Promise<any>;
|
||||
}
|
||||
@@ -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
21
shims/base64.js
Normal file
@@ -0,0 +1,21 @@
|
||||
'use strict';
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var bytes_1 = require("../utils/bytes");
|
||||
module.exports = {
|
||||
decode: function (textData) {
|
||||
textData = atob(textData);
|
||||
var data = [];
|
||||
for (var i = 0; i < textData.length; i++) {
|
||||
data.push(textData.charCodeAt(i));
|
||||
}
|
||||
return bytes_1.arrayify(data);
|
||||
},
|
||||
encode: function (data) {
|
||||
data = bytes_1.arrayify(data);
|
||||
var textData = '';
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
textData += String.fromCharCode(data[i]);
|
||||
}
|
||||
return btoa(textData);
|
||||
}
|
||||
};
|
||||
2
shims/empty.js
Normal file
2
shims/empty.js
Normal file
@@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
26
shims/hmac.js
Normal file
26
shims/hmac.js
Normal file
@@ -0,0 +1,26 @@
|
||||
"use strict";
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
||||
result["default"] = mod;
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var hash = __importStar(require("hash.js"));
|
||||
var bytes_1 = require("../utils/bytes");
|
||||
var errors = __importStar(require("../errors"));
|
||||
///////////////////////////////
|
||||
var SupportedAlgorithms;
|
||||
(function (SupportedAlgorithms) {
|
||||
SupportedAlgorithms["sha256"] = "sha256";
|
||||
SupportedAlgorithms["sha512"] = "sha512";
|
||||
})(SupportedAlgorithms = exports.SupportedAlgorithms || (exports.SupportedAlgorithms = {}));
|
||||
;
|
||||
function computeHmac(algorithm, key, data) {
|
||||
if (!SupportedAlgorithms[algorithm]) {
|
||||
errors.throwError('unsupported algorithm ' + algorithm, errors.UNSUPPORTED_OPERATION, { operation: 'hmac', algorithm: algorithm });
|
||||
}
|
||||
return bytes_1.arrayify(hash.hmac(hash[algorithm], bytes_1.arrayify(key)).update(bytes_1.arrayify(data)).digest());
|
||||
}
|
||||
exports.computeHmac = computeHmac;
|
||||
6
shims/index.js
Normal file
6
shims/index.js
Normal file
@@ -0,0 +1,6 @@
|
||||
"use strict";
|
||||
/**
|
||||
* This file is not imported anywhere, but is used to trigger TypeScript
|
||||
* compilation of the various shims for the browser.
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
45
shims/pbkdf2.js
Normal file
45
shims/pbkdf2.js
Normal file
@@ -0,0 +1,45 @@
|
||||
'use strict';
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var bytes_1 = require("../utils/bytes");
|
||||
var hmac_1 = require("./hmac");
|
||||
function pbkdf2(password, salt, iterations, keylen, hashAlgorithm) {
|
||||
password = bytes_1.arrayify(password);
|
||||
salt = bytes_1.arrayify(salt);
|
||||
var hLen;
|
||||
var l = 1;
|
||||
var DK = new Uint8Array(keylen);
|
||||
var block1 = new Uint8Array(salt.length + 4);
|
||||
block1.set(salt);
|
||||
//salt.copy(block1, 0, 0, salt.length)
|
||||
var r;
|
||||
var T;
|
||||
for (var i = 1; i <= l; i++) {
|
||||
//block1.writeUInt32BE(i, salt.length)
|
||||
block1[salt.length] = (i >> 24) & 0xff;
|
||||
block1[salt.length + 1] = (i >> 16) & 0xff;
|
||||
block1[salt.length + 2] = (i >> 8) & 0xff;
|
||||
block1[salt.length + 3] = i & 0xff;
|
||||
//var U = createHmac(password).update(block1).digest();
|
||||
var U = hmac_1.computeHmac(hashAlgorithm, password, block1);
|
||||
if (!hLen) {
|
||||
hLen = U.length;
|
||||
T = new Uint8Array(hLen);
|
||||
l = Math.ceil(keylen / hLen);
|
||||
r = keylen - (l - 1) * hLen;
|
||||
}
|
||||
//U.copy(T, 0, 0, hLen)
|
||||
T.set(U);
|
||||
for (var j = 1; j < iterations; j++) {
|
||||
//U = createHmac(password).update(U).digest();
|
||||
U = hmac_1.computeHmac(hashAlgorithm, password, U);
|
||||
for (var k = 0; k < hLen; k++)
|
||||
T[k] ^= U[k];
|
||||
}
|
||||
var destPos = (i - 1) * hLen;
|
||||
var len = (i === l ? r : hLen);
|
||||
//T.copy(DK, destPos, 0, len)
|
||||
DK.set(bytes_1.arrayify(T).slice(0, len), destPos);
|
||||
}
|
||||
return bytes_1.arrayify(DK);
|
||||
}
|
||||
exports.pbkdf2 = pbkdf2;
|
||||
@@ -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
4
shims/shims.js
Normal file
@@ -0,0 +1,4 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
require('setimmediate');
|
||||
exports.platform = "browser";
|
||||
5
shims/wordlists.js
Normal file
5
shims/wordlists.js
Normal file
@@ -0,0 +1,5 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var lang_en_1 = require("../wordlists/lang-en");
|
||||
var en = lang_en_1.langEn;
|
||||
exports.en = en;
|
||||
9
shims/xmlhttprequest.js
Normal file
9
shims/xmlhttprequest.js
Normal file
@@ -0,0 +1,9 @@
|
||||
'use strict';
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
try {
|
||||
module.exports.XMLHttpRequest = XMLHttpRequest;
|
||||
}
|
||||
catch (error) {
|
||||
console.log('Warning: XMLHttpRequest is not defined');
|
||||
module.exports.XMLHttpRequest = null;
|
||||
}
|
||||
1
src.ts/_version.ts
Normal file
1
src.ts/_version.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const version = "4.0.0";
|
||||
32
src.ts/abstract-signer.ts
Normal file
32
src.ts/abstract-signer.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
|
||||
import { isType, setType } from './utils/properties';
|
||||
|
||||
// Imported Abstracts
|
||||
import { Provider } from './providers/abstract-provider';
|
||||
|
||||
// Imported Types
|
||||
import { Arrayish } from './utils/bytes';
|
||||
import { TransactionRequest, TransactionResponse } from './providers/abstract-provider';
|
||||
|
||||
|
||||
export abstract class Signer {
|
||||
readonly provider?: Provider;
|
||||
|
||||
abstract getAddress(): Promise<string>
|
||||
|
||||
abstract signMessage(message: Arrayish | string): Promise<string>;
|
||||
abstract sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse>;
|
||||
|
||||
constructor() {
|
||||
setType(this, 'Signer');
|
||||
}
|
||||
|
||||
static isSigner(value: any): value is Signer {
|
||||
return isType(value, 'Signer');
|
||||
}
|
||||
|
||||
// readonly inherits: (child: any) => void;
|
||||
}
|
||||
|
||||
//defineReadOnly(Signer, 'inherits', inheritable(Signer));
|
||||
|
||||
31
src.ts/constants.ts
Normal file
31
src.ts/constants.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { BigNumber, bigNumberify } from './utils/bignumber';
|
||||
|
||||
const AddressZero = '0x0000000000000000000000000000000000000000';
|
||||
const HashZero = '0x0000000000000000000000000000000000000000000000000000000000000000';
|
||||
|
||||
// NFKD (decomposed)
|
||||
//const EtherSymbol = '\uD835\uDF63';
|
||||
|
||||
// NFKC (composed)
|
||||
const EtherSymbol = '\u039e';
|
||||
|
||||
const NegativeOne: BigNumber = bigNumberify(-1);
|
||||
const Zero: BigNumber = bigNumberify(0);
|
||||
const One: BigNumber = bigNumberify(1);
|
||||
const Two: BigNumber = bigNumberify(2);
|
||||
const WeiPerEther: BigNumber = bigNumberify('1000000000000000000');
|
||||
const MaxUint256: BigNumber = bigNumberify('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
|
||||
|
||||
export {
|
||||
AddressZero,
|
||||
HashZero,
|
||||
|
||||
EtherSymbol,
|
||||
|
||||
NegativeOne,
|
||||
Zero,
|
||||
One,
|
||||
Two,
|
||||
WeiPerEther,
|
||||
MaxUint256
|
||||
};
|
||||
802
src.ts/contract.ts
Normal file
802
src.ts/contract.ts
Normal 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
120
src.ts/errors.ts
Normal 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
78
src.ts/ethers.ts
Normal 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
6
src.ts/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
import * as ethers from './ethers';
|
||||
|
||||
export { ethers };
|
||||
|
||||
export * from './ethers';
|
||||
158
src.ts/providers/abstract-provider.ts
Normal file
158
src.ts/providers/abstract-provider.ts
Normal 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));
|
||||
|
||||
1175
src.ts/providers/base-provider.ts
Normal file
1175
src.ts/providers/base-provider.ts
Normal file
File diff suppressed because it is too large
Load Diff
314
src.ts/providers/etherscan-provider.ts
Normal file
314
src.ts/providers/etherscan-provider.ts
Normal 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;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
105
src.ts/providers/fallback-provider.ts
Normal file
105
src.ts/providers/fallback-provider.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
'use strict';
|
||||
|
||||
import { BaseProvider } from './base-provider';
|
||||
|
||||
// Imported Types
|
||||
import { Network } from '../utils/networks';
|
||||
|
||||
import * as errors from '../errors';
|
||||
|
||||
// Returns:
|
||||
// - true is all networks match
|
||||
// - false if any network is null
|
||||
// - throws if any 2 networks do not match
|
||||
function checkNetworks(networks: Array<Network>): boolean {
|
||||
var result = true;
|
||||
|
||||
let check: Network = null;
|
||||
networks.forEach((network) => {
|
||||
|
||||
// Null
|
||||
if (network == null) {
|
||||
result = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Have nothing to compre to yet
|
||||
if (check == null) {
|
||||
check = network;
|
||||
return;
|
||||
}
|
||||
|
||||
// Matches!
|
||||
if (check.name === network.name &&
|
||||
check.chainId === network.chainId &&
|
||||
check.ensAddress === network.ensAddress) { return; }
|
||||
|
||||
errors.throwError(
|
||||
'provider mismatch',
|
||||
errors.INVALID_ARGUMENT,
|
||||
{ arg: 'networks', value: networks }
|
||||
);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export class FallbackProvider extends BaseProvider {
|
||||
private _providers: Array<BaseProvider>;
|
||||
|
||||
constructor(providers: Array<BaseProvider>) {
|
||||
|
||||
if (providers.length === 0) { throw new Error('no providers'); }
|
||||
|
||||
// All networks are ready, we can know the network for certain
|
||||
let ready = checkNetworks(providers.map((p) => p.network));
|
||||
if (ready) {
|
||||
super(providers[0].network);
|
||||
|
||||
} else {
|
||||
// The network won't be known until all child providers know
|
||||
let ready = Promise.all(providers.map((p) => p.getNetwork())).then((networks) => {
|
||||
if (!checkNetworks(networks)) {
|
||||
errors.throwError('getNetwork returned null', errors.UNKNOWN_ERROR, { })
|
||||
}
|
||||
return networks[0];
|
||||
});
|
||||
|
||||
super(ready);
|
||||
}
|
||||
errors.checkNew(this, FallbackProvider);
|
||||
|
||||
// Preserve a copy, so we don't get mutated
|
||||
this._providers = providers.slice(0);
|
||||
}
|
||||
|
||||
get providers(): Array<BaseProvider> {
|
||||
// Return a copy, so we don't get mutated
|
||||
return this._providers.slice(0);
|
||||
}
|
||||
|
||||
perform(method: string, params: { [name: string]: any }): any {
|
||||
// Creates a copy of the providers array
|
||||
var providers = this.providers;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
var firstError: Error = null;
|
||||
function next() {
|
||||
if (!providers.length) {
|
||||
reject(firstError);
|
||||
return;
|
||||
}
|
||||
|
||||
var provider = providers.shift();
|
||||
provider.perform(method, params).then((result) => {
|
||||
return resolve(result);
|
||||
}).catch((error) => {
|
||||
if (!firstError) { firstError = error; }
|
||||
setTimeout(next, 0);
|
||||
});
|
||||
}
|
||||
next();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
77
src.ts/providers/index.ts
Normal file
77
src.ts/providers/index.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
'use strict';
|
||||
|
||||
import { Provider } from './abstract-provider';
|
||||
|
||||
import { BaseProvider } from './base-provider';
|
||||
|
||||
import { EtherscanProvider } from './etherscan-provider';
|
||||
import { FallbackProvider } from './fallback-provider';
|
||||
import { IpcProvider } from './ipc-provider';
|
||||
import { InfuraProvider } from './infura-provider';
|
||||
import { JsonRpcProvider, JsonRpcSigner } from './json-rpc-provider';
|
||||
import { Web3Provider } from './web3-provider';
|
||||
|
||||
////////////////////////
|
||||
// Types
|
||||
|
||||
import {
|
||||
Block,
|
||||
BlockTag,
|
||||
EventType,
|
||||
Filter,
|
||||
Log,
|
||||
Listener,
|
||||
TransactionReceipt,
|
||||
TransactionRequest,
|
||||
TransactionResponse
|
||||
} from './abstract-provider';
|
||||
|
||||
import { AsyncSendable } from './web3-provider';
|
||||
|
||||
|
||||
////////////////////////
|
||||
// Exports
|
||||
|
||||
export {
|
||||
|
||||
///////////////////////
|
||||
// Abstract Providers (or Abstract-ish)
|
||||
Provider,
|
||||
BaseProvider,
|
||||
|
||||
|
||||
///////////////////////
|
||||
// Concreate Providers
|
||||
|
||||
FallbackProvider,
|
||||
|
||||
EtherscanProvider,
|
||||
InfuraProvider,
|
||||
JsonRpcProvider,
|
||||
Web3Provider,
|
||||
|
||||
IpcProvider,
|
||||
|
||||
|
||||
///////////////////////
|
||||
// Signer
|
||||
|
||||
JsonRpcSigner,
|
||||
|
||||
|
||||
///////////////////////
|
||||
// Types
|
||||
|
||||
Block,
|
||||
BlockTag,
|
||||
EventType,
|
||||
Filter,
|
||||
Log,
|
||||
Listener,
|
||||
TransactionReceipt,
|
||||
TransactionRequest,
|
||||
TransactionResponse,
|
||||
|
||||
AsyncSendable
|
||||
};
|
||||
|
||||
59
src.ts/providers/infura-provider.ts
Normal file
59
src.ts/providers/infura-provider.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
'use strict';
|
||||
|
||||
import { JsonRpcProvider, JsonRpcSigner } from './json-rpc-provider';
|
||||
|
||||
import { getNetwork } from '../utils/networks';
|
||||
import { defineReadOnly } from '../utils/properties';
|
||||
|
||||
// Imported Types
|
||||
import { Networkish } from '../utils/networks';
|
||||
|
||||
import * as errors from '../errors';
|
||||
|
||||
export class InfuraProvider extends JsonRpcProvider {
|
||||
readonly apiAccessToken: string;
|
||||
|
||||
constructor(network?: Networkish, apiAccessToken?: string) {
|
||||
network = getNetwork((network == null) ? 'homestead': network);
|
||||
|
||||
var host = null;
|
||||
switch(network.name) {
|
||||
case 'homestead':
|
||||
host = 'mainnet.infura.io';
|
||||
break;
|
||||
case 'ropsten':
|
||||
host = 'ropsten.infura.io';
|
||||
break;
|
||||
case 'rinkeby':
|
||||
host = 'rinkeby.infura.io';
|
||||
break;
|
||||
case 'kovan':
|
||||
host = 'kovan.infura.io';
|
||||
break;
|
||||
default:
|
||||
throw new Error('unsupported network');
|
||||
}
|
||||
|
||||
super('https://' + host + '/' + (apiAccessToken || ''), network);
|
||||
errors.checkNew(this, InfuraProvider);
|
||||
|
||||
defineReadOnly(this, 'apiAccessToken', apiAccessToken || null);
|
||||
}
|
||||
|
||||
protected _startPending(): void {
|
||||
console.log('WARNING: INFURA does not support pending filters');
|
||||
}
|
||||
|
||||
getSigner(address?: string): JsonRpcSigner {
|
||||
errors.throwError(
|
||||
'INFURA does not support signing',
|
||||
errors.UNSUPPORTED_OPERATION,
|
||||
{ operation: 'getSigner' }
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
listAccounts(): Promise<Array<string>> {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
}
|
||||
67
src.ts/providers/ipc-provider.ts
Normal file
67
src.ts/providers/ipc-provider.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
|
||||
import net from 'net';
|
||||
|
||||
import { JsonRpcProvider } from './json-rpc-provider';
|
||||
|
||||
import { defineReadOnly } from '../utils/properties';
|
||||
|
||||
// Imported Types
|
||||
import { Networkish } from '../utils/networks';
|
||||
|
||||
import * as errors from '../errors';
|
||||
|
||||
export class IpcProvider extends JsonRpcProvider {
|
||||
readonly path: string;
|
||||
|
||||
constructor(path: string, network?: Networkish) {
|
||||
if (path == null) {
|
||||
errors.throwError('missing path', errors.MISSING_ARGUMENT, { arg: 'path' });
|
||||
}
|
||||
|
||||
super('ipc://' + path, network);
|
||||
errors.checkNew(this, IpcProvider);
|
||||
|
||||
defineReadOnly(this, 'path', path);
|
||||
}
|
||||
|
||||
// @TODO: Create a connection to the IPC path and use filters instead of polling for block
|
||||
|
||||
send(method: string, params: any): Promise<any> {
|
||||
// This method is very simple right now. We create a new socket
|
||||
// connection each time, which may be slower, but the main
|
||||
// advantage we are aiming for now is security. This simplifies
|
||||
// multiplexing requests (since we do not need to multiplex).
|
||||
|
||||
var payload = JSON.stringify({
|
||||
method: method,
|
||||
params: params,
|
||||
id: 42,
|
||||
jsonrpc: "2.0"
|
||||
});
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
var stream = net.connect(this.path);
|
||||
stream.on('data', function(data) {
|
||||
try {
|
||||
resolve(JSON.parse(data.toString('utf8')).result);
|
||||
// @TODO: Better pull apart the error
|
||||
stream.destroy();
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
stream.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
stream.on('end', function() {
|
||||
stream.destroy();
|
||||
});
|
||||
|
||||
stream.on('error', function(error) {
|
||||
reject(error);
|
||||
stream.destroy();
|
||||
});
|
||||
stream.write(payload);
|
||||
stream.end();
|
||||
});
|
||||
}
|
||||
}
|
||||
387
src.ts/providers/json-rpc-provider.ts
Normal file
387
src.ts/providers/json-rpc-provider.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
94
src.ts/providers/web3-provider.ts
Normal file
94
src.ts/providers/web3-provider.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
'use strict';
|
||||
|
||||
import { JsonRpcProvider } from './json-rpc-provider';
|
||||
|
||||
import { defineReadOnly } from '../utils/properties';
|
||||
|
||||
// Imported Types
|
||||
import { Networkish } from '../utils/networks';
|
||||
|
||||
import * as errors from '../errors';
|
||||
|
||||
|
||||
// Exported Types
|
||||
export type AsyncSendable = {
|
||||
isMetaMask?: boolean;
|
||||
host?: string;
|
||||
path?: string;
|
||||
sendAsync?: (request: any, callback: (error: any, response: any) => void) => void
|
||||
send?: (request: any, callback: (error: any, response: any) => void) => void
|
||||
}
|
||||
|
||||
/*
|
||||
@TODO
|
||||
utils.defineProperty(Web3Signer, 'onchange', {
|
||||
|
||||
});
|
||||
|
||||
*/
|
||||
|
||||
export class Web3Provider extends JsonRpcProvider {
|
||||
readonly _web3Provider: AsyncSendable;
|
||||
private _sendAsync: (request: any, callback: (error: any, response: any) => void) => void;
|
||||
|
||||
constructor(web3Provider: AsyncSendable, network?: Networkish) {
|
||||
// HTTP has a host; IPC has a path.
|
||||
super(web3Provider.host || web3Provider.path || '', network);
|
||||
errors.checkNew(this, Web3Provider);
|
||||
|
||||
if (web3Provider) {
|
||||
if (web3Provider.sendAsync) {
|
||||
this._sendAsync = web3Provider.sendAsync.bind(web3Provider);
|
||||
} else if (web3Provider.send) {
|
||||
this._sendAsync = web3Provider.send.bind(web3Provider);
|
||||
}
|
||||
}
|
||||
|
||||
if (!web3Provider || !this._sendAsync) {
|
||||
errors.throwError(
|
||||
'invalid web3Provider',
|
||||
errors.INVALID_ARGUMENT,
|
||||
{ arg: 'web3Provider', value: web3Provider }
|
||||
);
|
||||
}
|
||||
|
||||
defineReadOnly(this, '_web3Provider', web3Provider);
|
||||
}
|
||||
|
||||
send(method: string, params: any): Promise<any> {
|
||||
|
||||
// Metamask complains about eth_sign (and on some versions hangs)
|
||||
if (method == 'eth_sign' && this._web3Provider.isMetaMask) {
|
||||
// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_sign
|
||||
method = 'personal_sign';
|
||||
params = [ params[1], params[0] ];
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
var request = {
|
||||
method: method,
|
||||
params: params,
|
||||
id: 42,
|
||||
jsonrpc: "2.0"
|
||||
};
|
||||
|
||||
this._sendAsync(request, function(error, result) {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.error) {
|
||||
// @TODO: not any
|
||||
var error: any = new Error(result.error.message);
|
||||
error.code = result.error.code;
|
||||
error.data = result.error.data;
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(result.result);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
24
src.ts/shims/base64.ts
Normal file
24
src.ts/shims/base64.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
'use strict';
|
||||
|
||||
import { arrayify } from '../utils/bytes';
|
||||
|
||||
module.exports = {
|
||||
decode: function(textData: string): Uint8Array {
|
||||
textData = atob(textData);
|
||||
var data = [];
|
||||
for (var i = 0; i < textData.length; i++) {
|
||||
data.push(textData.charCodeAt(i));
|
||||
}
|
||||
return arrayify(data);
|
||||
},
|
||||
encode: function(data: Uint8Array): string {
|
||||
data = arrayify(data);
|
||||
var textData = '';
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
textData += String.fromCharCode(data[i]);
|
||||
}
|
||||
return btoa(textData);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
1
src.ts/shims/empty.ts
Normal file
1
src.ts/shims/empty.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { }
|
||||
26
src.ts/shims/hmac.ts
Normal file
26
src.ts/shims/hmac.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
|
||||
import * as hash from 'hash.js';
|
||||
|
||||
import { arrayify } from '../utils/bytes';
|
||||
|
||||
import * as errors from '../errors';
|
||||
|
||||
///////////////////////////////
|
||||
// Imported Types
|
||||
|
||||
import { Arrayish } from '../utils/bytes';
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
export enum SupportedAlgorithms { sha256 = 'sha256', sha512 = 'sha512' };
|
||||
|
||||
export function computeHmac(algorithm: SupportedAlgorithms, key: Arrayish, data: Arrayish): Uint8Array {
|
||||
if (!SupportedAlgorithms[algorithm]) {
|
||||
errors.throwError('unsupported algorithm ' + algorithm, errors.UNSUPPORTED_OPERATION, { operation: 'hmac', algorithm: algorithm });
|
||||
}
|
||||
|
||||
return arrayify(
|
||||
hash.hmac(hash[algorithm], arrayify(key)).update(arrayify(data)).digest()
|
||||
);
|
||||
}
|
||||
|
||||
13
src.ts/shims/index.ts
Normal file
13
src.ts/shims/index.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* This file is not imported anywhere, but is used to trigger TypeScript
|
||||
* compilation of the various shims for the browser.
|
||||
*/
|
||||
|
||||
import { } from './base64';
|
||||
import { } from './empty';
|
||||
import { } from './hmac';
|
||||
import { } from './pbkdf2';
|
||||
import { } from './random-bytes';
|
||||
import { } from './shims';
|
||||
import { } from './wordlists';
|
||||
import { } from './xmlhttprequest';
|
||||
58
src.ts/shims/pbkdf2.ts
Normal file
58
src.ts/shims/pbkdf2.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
'use strict';
|
||||
|
||||
import { arrayify } from '../utils/bytes';
|
||||
import { computeHmac, SupportedAlgorithms } from './hmac';
|
||||
|
||||
// Imported Types
|
||||
import { Arrayish } from '../utils/bytes';
|
||||
|
||||
export function pbkdf2(password: Arrayish, salt: Arrayish, iterations: number, keylen: number, hashAlgorithm: SupportedAlgorithms): Uint8Array {
|
||||
password = arrayify(password);
|
||||
salt = arrayify(salt);
|
||||
var hLen
|
||||
var l = 1
|
||||
var DK = new Uint8Array(keylen)
|
||||
var block1 = new Uint8Array(salt.length + 4)
|
||||
block1.set(salt);
|
||||
//salt.copy(block1, 0, 0, salt.length)
|
||||
|
||||
let r: number;
|
||||
let T: Uint8Array;
|
||||
|
||||
for (var i = 1; i <= l; i++) {
|
||||
//block1.writeUInt32BE(i, salt.length)
|
||||
block1[salt.length] = (i >> 24) & 0xff;
|
||||
block1[salt.length + 1] = (i >> 16) & 0xff;
|
||||
block1[salt.length + 2] = (i >> 8) & 0xff;
|
||||
block1[salt.length + 3] = i & 0xff;
|
||||
|
||||
//var U = createHmac(password).update(block1).digest();
|
||||
var U = computeHmac(hashAlgorithm, password, block1);
|
||||
|
||||
if (!hLen) {
|
||||
hLen = U.length
|
||||
T = new Uint8Array(hLen)
|
||||
l = Math.ceil(keylen / hLen)
|
||||
r = keylen - (l - 1) * hLen
|
||||
}
|
||||
|
||||
//U.copy(T, 0, 0, hLen)
|
||||
T.set(U);
|
||||
|
||||
|
||||
for (var j = 1; j < iterations; j++) {
|
||||
//U = createHmac(password).update(U).digest();
|
||||
U = computeHmac(hashAlgorithm, password, U);
|
||||
for (var k = 0; k < hLen; k++) T[k] ^= U[k]
|
||||
}
|
||||
|
||||
|
||||
var destPos = (i - 1) * hLen
|
||||
var len = (i === l ? r : hLen)
|
||||
//T.copy(DK, destPos, 0, len)
|
||||
DK.set(arrayify(T).slice(0, len), destPos);
|
||||
}
|
||||
|
||||
return arrayify(DK)
|
||||
}
|
||||
|
||||
41
src.ts/shims/random-bytes.ts
Normal file
41
src.ts/shims/random-bytes.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
'use strict';
|
||||
|
||||
import { arrayify } from '../utils/bytes';
|
||||
import { defineReadOnly } from '../utils/properties';
|
||||
|
||||
let crypto: any = (<any>global).crypto || (<any>global).msCrypto;
|
||||
if (!crypto || !crypto.getRandomValues) {
|
||||
|
||||
console.log('WARNING: Missing strong random number source; using weak randomBytes');
|
||||
|
||||
crypto = {
|
||||
getRandomValues: function(buffer: Uint8Array): Uint8Array {
|
||||
for (var round = 0; round < 20; round++) {
|
||||
for (var i = 0; i < buffer.length; i++) {
|
||||
if (round) {
|
||||
buffer[i] ^= Math.trunc(256 * Math.random());
|
||||
} else {
|
||||
buffer[i] = Math.trunc(256 * Math.random());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
},
|
||||
_weakCrypto: true
|
||||
};
|
||||
}
|
||||
|
||||
export function randomBytes(length: number): Uint8Array {
|
||||
if (length <= 0 || length > 1024 || parseInt(String(length)) != length) {
|
||||
throw new Error('invalid length');
|
||||
}
|
||||
|
||||
var result = new Uint8Array(length);
|
||||
crypto.getRandomValues(result);
|
||||
return arrayify(result);
|
||||
};
|
||||
|
||||
if (crypto._weakCrypto === true) {
|
||||
defineReadOnly(randomBytes, '_weakCrypto', true);
|
||||
}
|
||||
4
src.ts/shims/shims.ts
Normal file
4
src.ts/shims/shims.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
require('setimmediate');
|
||||
|
||||
export const platform = "browser";
|
||||
8
src.ts/shims/wordlists.ts
Normal file
8
src.ts/shims/wordlists.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
import { Wordlist } from '../utils/wordlist';
|
||||
|
||||
import { langEn as _en } from '../wordlists/lang-en';
|
||||
|
||||
const en: Wordlist = _en;
|
||||
|
||||
export { en }
|
||||
@@ -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
Reference in New Issue
Block a user