Compare commits

..

93 Commits

Author SHA1 Message Date
Richard Moore
c93b48920e Increment JSON-RPC ID for JsonRpcProviders for environments that unsafely manage callbacks (#489). 2019-04-18 13:49:37 +02:00
Richard Moore
04c92bb8d5 Updated dist files. 2019-03-08 14:29:59 -05:00
Richard Moore
16c9745326 Added goerli to InfuraProvider (#421). 2019-03-08 14:28:39 -05:00
Richard Moore
05648177aa Updated dist files. 2019-02-15 13:44:33 -05:00
Richard Moore
f318fd9cf1 Added fastRetry to polling for JsonRpcSigner to improve polling for sent transactions (#402). 2019-02-15 13:42:12 -05:00
Richard Moore
c15a89832b Fix waitForTransaction delay (#424). 2019-02-15 13:32:08 -05:00
Richard Moore
94b0abc240 Updated dist files. 2019-02-12 00:56:38 -05:00
Richard Moore
72edcd054f Fixed waitForTransaction and removeListener (#410). 2019-02-12 00:54:32 -05:00
Richard Moore
e4a2f8ac6c Updated BIP39 list in readme. 2019-02-11 19:24:48 -05:00
Richard Moore
c6d7b31a84 Updated dist files. 2019-02-11 19:24:27 -05:00
Richard Moore
0ed983a264 Fixed support for calling self-destructed contracts (#411). 2019-02-11 19:21:53 -05:00
Richard Moore
8fab48a380 Updated balance address for Goerli test cases. 2019-02-11 19:19:02 -05:00
Richard Moore
86e815999d Updated dist files. 2019-02-08 19:58:41 -05:00
Richard Moore
a2306f7870 Fixed utils test case for phantomjs. 2019-02-08 19:52:29 -05:00
Richard Moore
60b75c10d7 Initial support for EIP-234; filter by blockHash (#412). 2019-02-08 19:49:12 -05:00
Richard Moore
41c2c8a729 Fixed out-of-safe-range hexlify values to throw an exception (#420). 2019-02-08 17:06:46 -05:00
Richard Moore
9785eed8dd Added goerli testnet support (#421). 2019-02-08 16:50:06 -05:00
Richard Moore
527de7ba5e Updated dist files. 2019-02-04 16:09:35 -05:00
Richard Moore
14484e566e Fixed missing TypeArray slice on constrained environments. 2019-02-04 16:03:45 -05:00
Richard Moore
429af2c40d Fixed test-hdnode for phantomjs; does not support let keyword. 2019-02-01 23:00:01 -05:00
Richard Moore
11fffd1690 Updated dist files. 2019-02-01 19:50:10 -05:00
Richard Moore
af3aed4580 Added xpub and xpriv deserialization (#405). 2019-02-01 19:47:20 -05:00
Richard Moore
3a3764bdb4 Added xpub and xpriv test cases for HD nodes (#405). 2019-02-01 19:46:18 -05:00
Richard Moore
18ee2c518c Support for xpub and xpriv derivation and generating extended keys; no fromExtendedKey yet (#405). 2019-02-01 18:39:50 -05:00
Richard Moore
36172f7f7b Updated dist files. 2019-01-25 19:10:44 -05:00
Richard Moore
908258f8d4 Fixed duplicate events from triggering (#404). 2019-01-25 19:08:31 -05:00
Richard Moore
774b2d5fee Updated dist files. 2019-01-24 16:53:44 -05:00
Richard Moore
b5f720ace6 Ganache does not include from in receipts (#400). 2019-01-24 16:52:24 -05:00
Richard Moore
9f201c386e Updated dist files. 2019-01-23 16:30:12 -05:00
Richard Moore
700dd34137 Added to and from for Transaction Receipts (#398). 2019-01-23 16:25:49 -05:00
Richard Moore
f2dd977de4 Added v3 INFURA end-points to InfuraProvider (#286). 2019-01-23 16:04:54 -05:00
Richard Moore
5f013216c5 Fixed long-response bug in IpcProvider (#384). 2019-01-21 17:03:52 -05:00
Richard Moore
eac0805435 Updated dist files. 2019-01-17 16:34:17 -05:00
Richard Moore
e5bee7e5a3 Fixed path for x-ethers metadata and wallet (#). 2019-01-17 16:32:51 -05:00
Richard Moore
442553620a Updated dist files. 2019-01-15 16:01:45 -05:00
Richard Moore
6d08968b87 Fixed contract removeAllListeners which did not clean up the event loop properly (#391). 2019-01-15 15:58:42 -05:00
Richard Moore
c2ce59f95e Updated dist files. 2018-12-27 15:53:00 -05:00
Richard Moore
f3ec27b95f Added customizable log levels to quiet warnings (#379). 2018-12-27 15:48:38 -05:00
Richard Moore
c88cb5ea90 Updated dist files. 2018-12-14 18:39:46 -05:00
Richard Moore
99a21660ab Allow unchecked transactions which will remain unwrapped for the JsonRpcSigner (#340). 2018-12-14 18:36:24 -05:00
Richard Moore
4bc62a1e8a Make it easier for sub-classes of Wallet to manage nonces. 2018-12-14 18:32:48 -05:00
Richard Moore
26eb6cc01a Updated dist files. 2018-12-12 16:59:25 -05:00
Richard Moore
bcba17a9e7 Allow nonce to be a BigNumber (#228). 2018-12-12 16:56:50 -05:00
Richard Moore
918b66bc2e Fixed typo in error strings (#376). 2018-12-12 16:31:23 -05:00
Richard Moore
152d672278 Add isHexString to exported utils (#367). 2018-12-12 16:23:39 -05:00
Richard Moore
51fb472809 Add abs method to BigNumber (#375). 2018-12-12 16:10:28 -05:00
Richard Moore
66440b8542 Better error messages for namehash (#364). 2018-12-10 17:02:02 -05:00
Richard Moore
fefdd51084 Updated dist files. 2018-12-08 18:48:53 -05:00
Richard Moore
6ca1d77298 Fixed function name in parsed transactions (#370). 2018-12-08 18:46:29 -05:00
Richard Moore
4f6748ec4c Include request body in web errors. 2018-12-05 04:19:34 -05:00
Richard Moore
f56fc572f1 Squashed unhandled promise exception for Providers that are never used (#362). 2018-12-05 04:18:47 -05:00
Richard Moore
16fdf6b621 Added gas estimation back into JsonRpcSigner (#365). 2018-12-05 03:32:24 -05:00
Richard Moore
a863037ca3 Updated dist files. 2018-12-04 17:16:42 -05:00
Richard Moore
2d854bd94c Do not fill in implicit values for JSON-RPC based signers (#335). 2018-12-04 17:13:55 -05:00
Richard Moore
9565c28a91 More relaxed transaction parsing (#357). 2018-12-04 16:31:18 -05:00
Richard Moore
bc457bb3bd Allow any whitespace characters in human-readable ABI (#360). 2018-12-04 16:14:57 -05:00
Richard Moore
db383a3121 Updated dist files. 2018-11-27 17:32:47 -05:00
Richard Moore
3f76f603d9 Fixed contract proxied tx.wait receipt properties (#355). 2018-11-27 17:32:05 -05:00
Richard Moore
68304848ae Updated dist files. 2018-11-27 16:03:39 -05:00
Richard Moore
bb6bc4cac3 Check for partially-working normalize support. 2018-11-27 15:59:14 -05:00
Richard Moore
ef8b9c36ef Support for platforms where UTF-8 is only half broken. 2018-11-27 15:56:50 -05:00
Richard Moore
e6c943d01f Updated dist files. 2018-11-21 16:24:40 -05:00
Richard Moore
31d3ee899f Throw exception instead of returning null for getDefaultProvider (#351). 2018-11-21 16:23:44 -05:00
Richard Moore
98143a845b Updated dist files. 2018-11-20 15:45:47 -05:00
Richard Moore
bffc557be1 Added default provider support for Ethereum classic (#351). 2018-11-20 15:41:12 -05:00
Richard Moore
09208fa8fe Updated dist files. 2018-11-13 07:50:04 -05:00
Richard Moore
048c571d3d Fixed 0 confirmation waiting (#346). 2018-11-13 07:48:37 -05:00
Richard Moore
24757f1064 Updated dist files. 2018-11-12 17:27:47 -05:00
Richard Moore
88f2f51266 Fix spacing in checkArgument errors (#318). 2018-11-12 17:22:18 -05:00
Richard Moore
93152ef863 Do not replay block events when the provider event block is reset (#343). 2018-11-12 17:17:43 -05:00
Richard Moore
09b698b0a9 Updated dist files. 2018-11-09 14:42:29 -05:00
Richard Moore
478aaf9619 Force unorm shim when String.prototype.normalize is broken (#338). 2018-11-09 14:36:21 -05:00
Richard Moore
fad902b438 Better error message when normalize is missing. 2018-11-09 14:34:14 -05:00
Richard Moore
7bfaf292db Added shims for React-Native support. 2018-11-08 18:25:16 -05:00
Richard Moore
be0488a1a0 Updated dist files. 2018-11-08 16:03:33 -05:00
Richard Moore
28a52cd485 Fix for when blockTag is specified as a null equivalent value in contract overrides (#329). 2018-11-08 16:01:48 -05:00
Richard Moore
3a19f43844 Added "debug" event for providers; do not depend on the format as it may change, but this should help debugging in most cases (#320). 2018-11-08 15:59:30 -05:00
Richard Moore
4852e837d2 Fix for Kovan filters without an address (#339). 2018-11-08 15:56:35 -05:00
Richard Moore
fa68385cfe Updated dist files. 2018-10-14 19:05:38 -04:00
Richard Moore
d54609a458 Fixed lingering polling timer when no events left to process in a provider. 2018-10-14 19:01:53 -04:00
Richard Moore
f682861e0b Fixed utils.poll from mutating passed variables. 2018-10-14 19:01:09 -04:00
Richard Moore
023a20ff47 Fixed and refactored populating transaction values for signers (#306). 2018-10-14 19:00:15 -04:00
Richard Moore
e39cd84923 Fixed test cases for phantomjs (must use ES3 syntax). 2018-10-13 17:27:19 -04:00
Richard Moore
5020897f10 Updated dist files. 2018-10-13 17:19:07 -04:00
Richard Moore
6ac2d923b7 Fixed filtering with null non-indexed parameters (#305). 2018-10-13 17:17:02 -04:00
Richard Moore
6996dd86f4 Updated dist files. 2018-10-11 16:50:52 -04:00
Richard Moore
493273d698 Added optional blockTag to call; note that this may not behave as expected on all nodes (#226). 2018-10-11 16:03:18 -04:00
Richard Moore
84344ac4c2 Check all transaction parameters are valid; protect against typos (#299). 2018-10-11 15:16:31 -04:00
Richard Moore
9b118af304 Updated dist files. 2018-10-07 01:13:10 -04:00
Richard Moore
e39e2ed718 Added address to HDNode (#196). 2018-10-07 01:09:56 -04:00
Richard Moore
71f781d542 Added French and Spanish to test-hdnode. 2018-10-07 01:05:49 -04:00
Richard Moore
b2db10e216 Mark progressCallback as optional (#293). 2018-10-07 00:42:52 -04:00
Richard Moore
3736a15714 Updated dist files. 2018-10-04 20:25:07 -04:00
103 changed files with 4017 additions and 1127 deletions

View File

@@ -9,7 +9,7 @@ Complete Ethereum wallet implementation and utilities in JavaScript (and TypeScr
- Keep your private keys in your client, **safe** and sound
- Import and export **JSON wallets** (Geth, Parity and crowdsale)
- Import and export BIP 39 **mnemonic phrases** (12 word backup phrases) and **HD Wallets** (English, Italian, Japanese, Korean, Simplified Chinese, Traditional Chinese; more coming soon)
- Import and export BIP 39 **mnemonic phrases** (12 word backup phrases) and **HD Wallets** (English, French, Italian, Japanese, Korean, Simplified Chinese, Spanish, Traditional Chinese)
- Meta-classes create JavaScript objects from any contract ABI, including **ABIv2** and **Human-Readable ABI**
- Connect to Ethereum nodes over [JSON-RPC](https://github.com/ethereum/wiki/wiki/JSON-RPC), [INFURA](https://infura.io), [Etherscan](https://etherscan.io), or [MetaMask](https://metamask.io)
- **ENS names** are first-class citizens; they can be used anywhere an Ethereum addresses can be used

2
_version.d.ts vendored
View File

@@ -1 +1 @@
export declare const version = "4.0.2";
export declare const version = "4.0.27";

View File

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

11
contract.d.ts vendored
View File

@@ -1,7 +1,7 @@
import { BigNumber } from './utils/bignumber';
import { Indexed, Interface } from './utils/interface';
import { UnsignedTransaction } from './utils/transaction';
import { Provider } from './providers/abstract-provider';
import { BlockTag, Provider } from './providers/abstract-provider';
import { Signer } from './abstract-signer';
import { Arrayish } from './utils/bytes';
import { ParamType } from './utils/abi-coder';
@@ -21,6 +21,12 @@ export interface Event extends Log {
getTransaction: () => Promise<TransactionResponse>;
getTransactionReceipt: () => Promise<TransactionReceipt>;
}
export interface ContractReceipt extends TransactionReceipt {
events?: Array<Event>;
}
export interface ContractTransaction extends TransactionResponse {
wait(confirmations?: number): Promise<ContractReceipt>;
}
export declare class VoidSigner extends Signer {
readonly address: string;
constructor(address: string, provider: Provider);
@@ -44,9 +50,10 @@ export declare class Contract {
readonly [name: string]: ContractFunction | any;
readonly addressPromise: Promise<string>;
readonly deployTransaction: TransactionResponse;
private _deployed;
private _deployedPromise;
constructor(addressOrName: string, contractInterface: Array<string | ParamType> | string | Interface, signerOrProvider: Signer | Provider);
deployed(): Promise<Contract>;
_deployed(blockTag?: BlockTag): Promise<Contract>;
fallback(overrides?: TransactionRequest): Promise<TransactionResponse>;
connect(signerOrProvider: Signer | Provider | string): Contract;
attach(addressOrName: string): Contract;

View File

@@ -111,9 +111,14 @@ function runMethod(contract, functionName, estimateOnly) {
params[_i] = arguments[_i];
}
var tx = {};
var blockTag = null;
// If 1 extra parameter was passed in, it contains overrides
if (params.length === method.inputs.length + 1 && typeof (params[params.length - 1]) === 'object') {
tx = properties_1.shallowCopy(params.pop());
if (tx.blockTag != null) {
blockTag = tx.blockTag;
}
delete tx.blockTag;
// Check for unexpected keys (e.g. using "gas" instead of "gasLimit")
for (var key in tx) {
if (!allowedTransactionKeys[key]) {
@@ -130,8 +135,7 @@ function runMethod(contract, functionName, estimateOnly) {
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 () {
tx.to = contract._deployed(blockTag).then(function () {
return contract.addressPromise;
});
return resolveAddresses(contract.provider, params, method.inputs).then(function (params) {
@@ -153,7 +157,7 @@ function runMethod(contract, functionName, estimateOnly) {
if (tx.from == null && contract.signer) {
tx.from = contract.signer.getAddress();
}
return contract.provider.call(tx).then(function (value) {
return contract.provider.call(tx, blockTag).then(function (value) {
if ((bytes_1.hexDataLength(value) % 32) === 4 && bytes_1.hexDataSlice(value, 0, 4) === '0x08c379a0') {
var reason = abi_coder_1.defaultAbiCoder.decode(['string'], bytes_1.hexDataSlice(value, 4));
errors.throwError('call revert exception', errors.CALL_EXCEPTION, {
@@ -206,7 +210,36 @@ function runMethod(contract, functionName, estimateOnly) {
if (tx.from != null) {
errors.throwError('cannot override from in a transaction', errors.UNSUPPORTED_OPERATION, { operation: 'sendTransaction' });
}
return contract.signer.sendTransaction(tx);
return contract.signer.sendTransaction(tx).then(function (tx) {
var wait = tx.wait.bind(tx);
tx.wait = function (confirmations) {
return wait(confirmations).then(function (receipt) {
receipt.events = receipt.logs.map(function (log) {
var event = properties_1.deepCopy(log);
var parsed = contract.interface.parseLog(log);
if (parsed) {
event.args = parsed.values;
event.decode = parsed.decode;
event.event = parsed.name;
event.eventSignature = parsed.signature;
}
event.removeListener = function () { return contract.provider; };
event.getBlock = function () {
return contract.provider.getBlock(receipt.blockHash);
};
event.getTransaction = function () {
return contract.provider.getTransaction(receipt.transactionHash);
};
event.getTransactionReceipt = function () {
return Promise.resolve(receipt);
};
return event;
});
return receipt;
});
};
return tx;
});
}
throw new Error('invalid type - ' + method.type);
return null;
@@ -270,7 +303,6 @@ var Contract = /** @class */ (function () {
}
return address;
}).catch(function (error) {
console.log('ERROR: Cannot find Contract - ' + addressOrName);
throw error;
}));
}
@@ -289,7 +321,7 @@ var Contract = /** @class */ (function () {
properties_1.defineReadOnly(_this, name, run);
}
else {
console.log('WARNING: Multiple definitions for ' + name);
errors.warn('WARNING: Multiple definitions for ' + name);
}
if (_this.functions[name] == null) {
properties_1.defineReadOnly(_this.functions, name, run);
@@ -299,11 +331,14 @@ var Contract = /** @class */ (function () {
}
// @TODO: Allow timeout?
Contract.prototype.deployed = function () {
return this._deployed();
};
Contract.prototype._deployed = function (blockTag) {
var _this = this;
if (!this._deployed) {
if (!this._deployedPromise) {
// If we were just deployed, we know the transaction we should occur in
if (this.deployTransaction) {
this._deployed = this.deployTransaction.wait().then(function () {
this._deployedPromise = this.deployTransaction.wait().then(function () {
return _this;
});
}
@@ -311,7 +346,7 @@ var Contract = /** @class */ (function () {
// @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) {
this._deployedPromise = this.provider.getCode(this.address, blockTag).then(function (code) {
if (code === '0x') {
errors.throwError('contract not deployed', errors.UNSUPPORTED_OPERATION, {
contractAddress: _this.address,
@@ -322,7 +357,7 @@ var Contract = /** @class */ (function () {
});
}
}
return this._deployed;
return this._deployedPromise;
};
// @TODO:
// estimateFallback(overrides?: TransactionRequest): Promise<BigNumber>
@@ -521,12 +556,19 @@ var Contract = /** @class */ (function () {
}).map(function (event) { return event.listener; });
};
Contract.prototype.removeAllListeners = function (eventName) {
var _this = this;
if (!this.provider) {
return this;
}
var eventFilter = this._getEventFilter(eventName);
this._events = this._events.filter(function (event) {
return event.eventFilter.eventTag !== eventFilter.eventTag;
// Keep all other events
if (event.eventFilter.eventTag !== eventFilter.eventTag) {
return true;
}
// Deregister this event from the provider and filter it out
_this.provider.removeListener(event.eventFilter.filter, event.wrappedListener);
return false;
});
return this;
};
@@ -620,7 +662,7 @@ var ContractFactory = /** @class */ (function () {
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');
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;

1521
dist/ethers.js vendored

File diff suppressed because it is too large Load Diff

2
dist/ethers.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

91
dist/ethers.types.txt vendored
View File

@@ -17,16 +17,16 @@ declare module 'ethers/ethers' {
import * as wordlists from 'ethers/wordlists';
import { platform } from 'ethers/utils/shims';
import { version } from 'ethers/_version';
import { ContractFunction, Event, EventFilter } from 'ethers/contract';
import { ContractFunction, ContractTransaction, Event, EventFilter } from 'ethers/contract';
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 };
export { Signer, Wallet, VoidSigner, getDefaultProvider, providers, Contract, ContractFactory, constants, errors, utils, wordlists, platform, version, ContractFunction, ContractTransaction, Event, EventFilter };
}
declare module 'ethers/contract' {
import { BigNumber } from 'ethers/utils/bignumber';
import { Indexed, Interface } from 'ethers/utils/interface';
import { UnsignedTransaction } from 'ethers/utils/transaction';
import { Provider } from 'ethers/providers/abstract-provider';
import { BlockTag, Provider } from 'ethers/providers/abstract-provider';
import { Signer } from 'ethers/abstract-signer';
import { Arrayish } from 'ethers/utils/bytes';
import { ParamType } from 'ethers/utils/abi-coder';
@@ -46,6 +46,12 @@ declare module 'ethers/contract' {
getTransaction: () => Promise<TransactionResponse>;
getTransactionReceipt: () => Promise<TransactionReceipt>;
}
export interface ContractReceipt extends TransactionReceipt {
events?: Array<Event>;
}
export interface ContractTransaction extends TransactionResponse {
wait(confirmations?: number): Promise<ContractReceipt>;
}
export class VoidSigner extends Signer {
readonly address: string;
constructor(address: string, provider: Provider);
@@ -71,6 +77,7 @@ declare module 'ethers/contract' {
readonly deployTransaction: TransactionResponse;
constructor(addressOrName: string, contractInterface: Array<string | ParamType> | string | Interface, signerOrProvider: Signer | Provider);
deployed(): Promise<Contract>;
_deployed(blockTag?: BlockTag): Promise<Contract>;
fallback(overrides?: TransactionRequest): Promise<TransactionResponse>;
connect(signerOrProvider: Signer | Provider | string): Contract;
attach(addressOrName: string): Contract;
@@ -146,7 +153,7 @@ declare module 'ethers/wallet' {
* Static methods to create Wallet instances.
*/
static createRandom(options?: any): Wallet;
static fromEncryptedJson(json: string, password: Arrayish, progressCallback: ProgressCallback): Promise<Wallet>;
static fromEncryptedJson(json: string, password: Arrayish, progressCallback?: ProgressCallback): Promise<Wallet>;
static fromMnemonic(mnemonic: string, path?: string, wordlist?: Wordlist): Wallet;
}
}
@@ -182,6 +189,10 @@ declare module 'ethers/errors' {
export function checkNew(self: any, kind: any): void;
export function checkArgumentCount(count: number, expectedCount: number, suffix?: string): void;
export function setCensorship(censorship: boolean, permanent?: boolean): void;
export function checkNormalize(): void;
export function setLogLevel(logLevel: string): void;
export function warn(...args: Array<any>): void;
export function info(...args: Array<any>): void;
}
declare module 'ethers/providers' {
@@ -203,7 +214,7 @@ declare module 'ethers/utils' {
import { getAddress, getContractAddress, getIcapAddress } from 'ethers/utils/address';
import * as base64 from 'ethers/utils/base64';
import { BigNumber, bigNumberify } from 'ethers/utils/bignumber';
import { arrayify, concat, hexDataSlice, hexDataLength, hexlify, hexStripZeros, hexZeroPad, joinSignature, padZeros, splitSignature, stripZeros } from 'ethers/utils/bytes';
import { arrayify, concat, hexDataSlice, hexDataLength, hexlify, hexStripZeros, hexZeroPad, isHexString, joinSignature, padZeros, splitSignature, stripZeros } from 'ethers/utils/bytes';
import { hashMessage, id, namehash } from 'ethers/utils/hash';
import * as HDNode from 'ethers/utils/hdnode';
import { Interface } from 'ethers/utils/interface';
@@ -213,10 +224,11 @@ declare module 'ethers/utils' {
import { keccak256 as solidityKeccak256, pack as solidityPack, sha256 as soliditySha256 } from 'ethers/utils/solidity';
import { randomBytes } from 'ethers/utils/random-bytes';
import { getNetwork } from 'ethers/utils/networks';
import { deepCopy, defineReadOnly, resolveProperties, shallowCopy } from 'ethers/utils/properties';
import { checkProperties, deepCopy, defineReadOnly, resolveProperties, shallowCopy } from 'ethers/utils/properties';
import * as RLP from 'ethers/utils/rlp';
import { computeAddress, computePublicKey, recoverAddress, recoverPublicKey, verifyMessage } from 'ethers/utils/secp256k1';
import { SigningKey } from 'ethers/utils/signing-key';
import { populateTransaction } from 'ethers/utils/transaction';
import { parse as parseTransaction, serialize as serializeTransaction } from 'ethers/utils/transaction';
import { formatBytes32String, parseBytes32String, toUtf8Bytes, toUtf8String } from 'ethers/utils/utf8';
import { commify, formatEther, parseEther, formatUnits, parseUnits } from 'ethers/utils/units';
@@ -232,7 +244,7 @@ declare module 'ethers/utils' {
import { ConnectionInfo, OnceBlockable, PollOptions } from 'ethers/utils/web';
import { EncryptOptions, ProgressCallback } from 'ethers/utils/secret-storage';
import { Wordlist } from 'ethers/utils/wordlist';
export { AbiCoder, defaultAbiCoder, formatSignature, formatParamType, parseSignature, parseParamType, RLP, fetchJson, getNetwork, deepCopy, defineReadOnly, resolveProperties, shallowCopy, arrayify, concat, padZeros, stripZeros, HDNode, SigningKey, Interface, base64, BigNumber, bigNumberify, hexlify, hexStripZeros, hexZeroPad, hexDataLength, hexDataSlice, toUtf8Bytes, toUtf8String, formatBytes32String, parseBytes32String, hashMessage, namehash, id, getAddress, getIcapAddress, getContractAddress, formatEther, parseEther, formatUnits, parseUnits, commify, keccak256, sha256, randomBytes, solidityPack, solidityKeccak256, soliditySha256, splitSignature, joinSignature, parseTransaction, serializeTransaction, getJsonWalletAddress, computeAddress, computePublicKey, recoverAddress, recoverPublicKey, verifyMessage, SupportedAlgorithms, UnicodeNormalizationForm, CoerceFunc, EventFragment, FunctionFragment, ParamType, BigNumberish, Arrayish, Hexable, Signature, Indexed, DeployDescription, EventDescription, FunctionDescription, LogDescription, TransactionDescription, Network, Networkish, Transaction, UnsignedTransaction, ConnectionInfo, OnceBlockable, PollOptions, EncryptOptions, ProgressCallback, Wordlist, };
export { AbiCoder, defaultAbiCoder, formatSignature, formatParamType, parseSignature, parseParamType, RLP, fetchJson, getNetwork, checkProperties, deepCopy, defineReadOnly, resolveProperties, shallowCopy, arrayify, concat, padZeros, stripZeros, HDNode, SigningKey, Interface, base64, BigNumber, bigNumberify, hexlify, isHexString, hexStripZeros, hexZeroPad, hexDataLength, hexDataSlice, toUtf8Bytes, toUtf8String, formatBytes32String, parseBytes32String, hashMessage, namehash, id, getAddress, getIcapAddress, getContractAddress, formatEther, parseEther, formatUnits, parseUnits, commify, keccak256, sha256, randomBytes, solidityPack, solidityKeccak256, soliditySha256, splitSignature, joinSignature, parseTransaction, populateTransaction, serializeTransaction, getJsonWalletAddress, computeAddress, computePublicKey, recoverAddress, recoverPublicKey, verifyMessage, SupportedAlgorithms, UnicodeNormalizationForm, CoerceFunc, EventFragment, FunctionFragment, ParamType, BigNumberish, Arrayish, Hexable, Signature, Indexed, DeployDescription, EventDescription, FunctionDescription, LogDescription, TransactionDescription, Network, Networkish, Transaction, UnsignedTransaction, ConnectionInfo, OnceBlockable, PollOptions, EncryptOptions, ProgressCallback, Wordlist, };
}
declare module 'ethers/wordlists' {
@@ -254,7 +266,7 @@ declare module 'ethers/utils/shims' {
}
declare module 'ethers/_version' {
export const version = "4.0.2";
export const version = "4.0.27";
}
declare module 'ethers/utils/bignumber' {
@@ -265,6 +277,7 @@ declare module 'ethers/utils/bignumber' {
constructor(value: BigNumberish);
fromTwos(value: number): BigNumber;
toTwos(value: number): BigNumber;
abs(): BigNumber;
add(other: BigNumberish): BigNumber;
sub(other: BigNumberish): BigNumber;
div(other: BigNumberish): BigNumber;
@@ -406,6 +419,7 @@ declare module 'ethers/utils/transaction' {
import { BigNumber } from 'ethers/utils/bignumber';
import { Arrayish, Signature } from 'ethers/utils/bytes';
import { BigNumberish } from 'ethers/utils/bignumber';
import { Provider } from 'ethers/providers/abstract-provider';
export type UnsignedTransaction = {
to?: string;
nonce?: number;
@@ -431,6 +445,7 @@ declare module 'ethers/utils/transaction' {
}
export function serialize(transaction: UnsignedTransaction, signature?: Arrayish | Signature): string;
export function parse(rawTransaction: Arrayish): Transaction;
export function populateTransaction(transaction: any, provider: Provider, from: string | Promise<string>): Promise<Transaction>;
}
declare module 'ethers/providers/abstract-provider' {
@@ -460,6 +475,11 @@ declare module 'ethers/providers/abstract-provider' {
address?: string;
topics?: Array<string | Array<string>>;
};
export type FilterByBlock = {
blockHash?: string;
address?: string;
topics?: Array<string | Array<string>>;
};
export interface Log {
blockNumber?: number;
blockHash?: string;
@@ -473,6 +493,8 @@ declare module 'ethers/providers/abstract-provider' {
logIndex?: number;
}
export interface TransactionReceipt {
to?: string;
from?: string;
contractAddress?: string;
transactionIndex?: number;
root?: string;
@@ -482,6 +504,7 @@ declare module 'ethers/providers/abstract-provider' {
transactionHash?: string;
logs?: Array<Log>;
blockNumber?: number;
confirmations?: number;
cumulativeGasUsed?: BigNumber;
byzantium: boolean;
status?: number;
@@ -489,7 +512,7 @@ declare module 'ethers/providers/abstract-provider' {
export type TransactionRequest = {
to?: string | Promise<string>;
from?: string | Promise<string>;
nonce?: number | string | Promise<number | string>;
nonce?: BigNumberish | Promise<BigNumberish>;
gasLimit?: BigNumberish | Promise<BigNumberish>;
gasPrice?: BigNumberish | Promise<BigNumberish>;
data?: Arrayish | Promise<Arrayish>;
@@ -500,9 +523,10 @@ declare module 'ethers/providers/abstract-provider' {
blockNumber?: number;
blockHash?: string;
timestamp?: number;
confirmations: number;
from: string;
raw?: string;
wait: (timeout?: number) => Promise<TransactionReceipt>;
wait: (confirmations?: number) => Promise<TransactionReceipt>;
}
export type EventType = string | Array<string> | Filter;
export type Listener = (...args: Array<any>) => void;
@@ -515,12 +539,12 @@ declare module 'ethers/providers/abstract-provider' {
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 call(transaction: TransactionRequest, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
abstract estimateGas(transaction: TransactionRequest): Promise<BigNumber>;
abstract getBlock(blockHashOrBlockTag: BlockTag | string | Promise<BlockTag | string>, includeTransactions?: boolean): Promise<Block>;
abstract getTransaction(transactionHash: string): Promise<TransactionResponse>;
abstract getTransactionReceipt(transactionHash: string): Promise<TransactionReceipt>;
abstract getLogs(filter: Filter): Promise<Array<Log>>;
abstract getLogs(filter: Filter | FilterByBlock): Promise<Array<Log>>;
abstract resolveName(name: string | Promise<string>): Promise<string>;
abstract lookupAddress(address: string | Promise<string>): Promise<string>;
abstract on(eventName: EventType, listener: Listener): Provider;
@@ -614,6 +638,9 @@ declare module 'ethers/utils/hdnode' {
export class HDNode {
readonly privateKey: string;
readonly publicKey: string;
readonly fingerprint: string;
readonly parentFingerprint: string;
readonly address: string;
readonly mnemonic: string;
readonly path: string;
readonly chainCode: string;
@@ -626,11 +653,14 @@ declare module 'ethers/utils/hdnode' {
* - fromMnemonic
* - fromSeed
*/
constructor(constructorGuard: any, privateKey: Arrayish, chainCode: Uint8Array, index: number, depth: number, mnemonic: string, path: string);
constructor(constructorGuard: any, privateKey: string, publicKey: string, parentFingerprint: string, chainCode: string, index: number, depth: number, mnemonic: string, path: string);
readonly extendedKey: string;
neuter(): HDNode;
derivePath(path: string): HDNode;
static isHDNode(value: any): value is HDNode;
}
export function fromMnemonic(mnemonic: string, wordlist?: Wordlist): HDNode;
export function fromExtendedKey(extendedKey: string): HDNode;
export function fromMnemonic(mnemonic: string, wordlist?: Wordlist, password?: string): HDNode;
export function fromSeed(seed: Arrayish): HDNode;
export function mnemonicToSeed(mnemonic: string, password?: string): string;
export function mnemonicToEntropy(mnemonic: string, wordlist?: Wordlist): string;
@@ -698,12 +728,14 @@ declare module 'ethers/utils/secret-storage' {
declare module 'ethers/providers/base-provider' {
import { BigNumber } from 'ethers/utils/bignumber';
import { Provider } from 'ethers/providers/abstract-provider';
import { Block, BlockTag, EventType, Filter, Listener, Log, TransactionReceipt, TransactionRequest, TransactionResponse } from 'ethers/providers/abstract-provider';
import { Block, BlockTag, EventType, Filter, FilterByBlock, Listener, Log, TransactionReceipt, TransactionRequest, TransactionResponse } from 'ethers/providers/abstract-provider';
import { BigNumberish } from 'ethers/utils/bignumber';
import { Transaction } from 'ethers/utils/transaction';
import { Network, Networkish } from 'ethers/utils/networks';
export class BaseProvider extends Provider {
protected _emitted: any;
protected _emitted: {
[eventName: string]: number | 'pending';
};
/**
* ready
*
@@ -721,7 +753,9 @@ declare module 'ethers/providers/base-provider' {
readonly blockNumber: number;
polling: boolean;
pollingInterval: number;
waitForTransaction(transactionHash: string, timeout?: number): Promise<TransactionReceipt>;
_getFastBlockNumber(): Promise<number>;
_setFastBlockNumber(blockNumber: number): void;
waitForTransaction(transactionHash: string, confirmations?: number): Promise<TransactionReceipt>;
getBlockNumber(): Promise<number>;
getGasPrice(): Promise<BigNumber>;
getBalance(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<BigNumber>;
@@ -730,12 +764,12 @@ declare module 'ethers/providers/base-provider' {
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>;
call(transaction: TransactionRequest, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
estimateGas(transaction: TransactionRequest): Promise<BigNumber>;
getBlock(blockHashOrBlockTag: BlockTag | string | Promise<BlockTag | string>, includeTransactions?: boolean): Promise<Block>;
getTransaction(transactionHash: string): Promise<TransactionResponse>;
getTransactionReceipt(transactionHash: string): Promise<TransactionReceipt>;
getLogs(filter: Filter): Promise<Array<Log>>;
getLogs(filter: Filter | FilterByBlock): Promise<Array<Log>>;
getEtherPrice(): Promise<number>;
resolveName(name: string | Promise<string>): Promise<string>;
lookupAddress(address: string | Promise<string>): Promise<string>;
@@ -750,7 +784,7 @@ declare module 'ethers/providers/base-provider' {
emit(eventName: EventType, ...args: Array<any>): boolean;
listenerCount(eventName?: EventType): number;
listeners(eventName: EventType): Array<Listener>;
removeAllListeners(eventName: EventType): Provider;
removeAllListeners(eventName?: EventType): Provider;
removeListener(eventName: EventType, listener: Listener): Provider;
}
}
@@ -794,7 +828,8 @@ declare module 'ethers/providers/infura-provider' {
import { Networkish } from 'ethers/utils/networks';
export class InfuraProvider extends JsonRpcProvider {
readonly apiAccessToken: string;
constructor(network?: Networkish, apiAccessToken?: string);
readonly projectId: string;
constructor(network?: Networkish, projectId?: string);
protected _startPending(): void;
getSigner(address?: string): JsonRpcSigner;
listAccounts(): Promise<Array<string>>;
@@ -815,6 +850,7 @@ declare module 'ethers/providers/json-rpc-provider' {
getAddress(): Promise<string>;
getBalance(blockTag?: BlockTag): Promise<BigNumber>;
getTransactionCount(blockTag?: BlockTag): Promise<number>;
sendUncheckedTransaction(transaction: TransactionRequest): Promise<string>;
sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse>;
signMessage(message: Arrayish | string): Promise<string>;
unlock(password: string): Promise<boolean>;
@@ -828,7 +864,11 @@ declare module 'ethers/providers/json-rpc-provider' {
perform(method: string, params: any): Promise<any>;
protected _startPending(): void;
protected _stopPending(): void;
static hexlifyTransaction(transaction: TransactionRequest): any;
static hexlifyTransaction(transaction: TransactionRequest, allowExtra?: {
[key: string]: boolean;
}): {
[key: string]: string;
};
}
}
@@ -886,6 +926,7 @@ declare module 'ethers/utils/keccak256' {
declare module 'ethers/utils/sha2' {
import { Arrayish } from 'ethers/utils/bytes';
export function ripemd160(data: Arrayish): string;
export function sha256(data: Arrayish): string;
export function sha512(data: Arrayish): string;
}
@@ -905,6 +946,7 @@ declare module 'ethers/utils/networks' {
name: string;
chainId: number;
ensAddress?: string;
_defaultProvider?: (providers: any) => any;
};
export type Networkish = Network | string | number;
/**
@@ -921,6 +963,9 @@ declare module 'ethers/utils/properties' {
export function setType(object: any, type: string): void;
export function isType(object: any, type: string): boolean;
export function resolveProperties(object: any): Promise<any>;
export function checkProperties(object: any, properties: {
[name: string]: boolean;
}): void;
export function shallowCopy(object: any): any;
export function deepCopy(object: any, frozen?: boolean): any;
export function inheritable(parent: any): (child: any) => void;
@@ -942,6 +987,7 @@ declare module 'ethers/utils/secp256k1' {
constructor(privateKey: Arrayish | string);
sign(digest: Arrayish | string): Signature;
computeSharedSecret(otherKey: Arrayish | string): string;
_addPoint(other: Arrayish | string): string;
}
export function computePublicKey(key: Arrayish | string, compressed?: boolean): string;
export function computeAddress(key: Arrayish | string): string;
@@ -995,6 +1041,7 @@ declare module 'ethers/utils/web' {
ceiling?: number;
interval?: number;
onceBlock?: OnceBlockable;
fastRetry?: number;
};
export function fetchJson(connection: string | ConnectionInfo, json: string, processFunc: (value: any) => any): Promise<any>;
export function poll(func: () => Promise<any>, options?: PollOptions): Promise<any>;

1
dist/shims.js vendored Normal file

File diff suppressed because one or more lines are too long

4
errors.d.ts vendored
View File

@@ -14,3 +14,7 @@ export declare function throwError(message: string, code: string, params: any):
export declare function checkNew(self: any, kind: any): void;
export declare function checkArgumentCount(count: number, expectedCount: number, suffix?: string): void;
export declare function setCensorship(censorship: boolean, permanent?: boolean): void;
export declare function checkNormalize(): void;
export declare function setLogLevel(logLevel: string): void;
export declare function warn(...args: Array<any>): void;
export declare function info(...args: Array<any>): void;

View File

@@ -18,7 +18,7 @@ exports.MISSING_NEW = 'MISSING_NEW';
// - 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
// - argument: The argument name that was invalid
// - value: The value of the argument
exports.INVALID_ARGUMENT = 'INVALID_ARGUMENT';
// Missing argument to a function:
@@ -102,9 +102,62 @@ function checkArgumentCount(count, expectedCount, suffix) {
exports.checkArgumentCount = checkArgumentCount;
function setCensorship(censorship, permanent) {
if (_permanentCensorErrors) {
throwError('error censorship permanent', exports.UNSUPPORTED_OPERATION, { operation: 'setCersorship' });
throwError('error censorship permanent', exports.UNSUPPORTED_OPERATION, { operation: 'setCensorship' });
}
_censorErrors = !!censorship;
_permanentCensorErrors = !!permanent;
}
exports.setCensorship = setCensorship;
function checkNormalize() {
try {
// Make sure all forms of normalization are supported
["NFD", "NFC", "NFKD", "NFKC"].forEach(function (form) {
try {
"test".normalize(form);
}
catch (error) {
throw new Error('missing ' + form);
}
});
if (String.fromCharCode(0xe9).normalize('NFD') !== String.fromCharCode(0x65, 0x0301)) {
throw new Error('broken implementation');
}
}
catch (error) {
throwError('platform missing String.prototype.normalize', exports.UNSUPPORTED_OPERATION, { operation: 'String.prototype.normalize', form: error.message });
}
}
exports.checkNormalize = checkNormalize;
var LogLevels = { debug: 1, "default": 2, info: 2, warn: 3, error: 4, off: 5 };
var LogLevel = LogLevels["default"];
function setLogLevel(logLevel) {
var level = LogLevels[logLevel];
if (level == null) {
warn("invliad log level - " + logLevel);
return;
}
LogLevel = level;
}
exports.setLogLevel = setLogLevel;
function log(logLevel, args) {
if (LogLevel > LogLevels[logLevel]) {
return;
}
console.log.apply(console, args);
}
function warn() {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
log("warn", args);
}
exports.warn = warn;
function info() {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
log("info", args);
}
exports.info = info;

4
ethers.d.ts vendored
View File

@@ -8,6 +8,6 @@ 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';
import { ContractFunction, ContractTransaction, Event, EventFilter } from './contract';
declare function getDefaultProvider(network?: utils.Network | string): providers.BaseProvider;
export { Signer, Wallet, VoidSigner, getDefaultProvider, providers, Contract, ContractFactory, constants, errors, utils, wordlists, platform, version, ContractFunction, Event, EventFilter };
export { Signer, Wallet, VoidSigner, getDefaultProvider, providers, Contract, ContractFactory, constants, errors, utils, wordlists, platform, version, ContractFunction, ContractTransaction, Event, EventFilter };

View File

@@ -36,9 +36,16 @@ exports.version = _version_1.version;
////////////////////////
// Helper Functions
function getDefaultProvider(network) {
return new providers.FallbackProvider([
new providers.InfuraProvider(network),
new providers.EtherscanProvider(network),
]);
if (network == null) {
network = 'homestead';
}
var n = utils.getNetwork(network);
if (!n || !n._defaultProvider) {
errors.throwError('unsupported getDefaultProvider network', errors.UNSUPPORTED_OPERATION, {
operation: 'getDefaultProvider',
network: network
});
}
return n._defaultProvider(providers);
}
exports.getDefaultProvider = getDefaultProvider;

View File

@@ -111,7 +111,7 @@ function taskBundle(name, options) {
"elliptic/package.json" : ellipticPackage,
// Remove RIPEMD160 and unneeded hashing algorithms
"hash.js/lib/hash/ripemd.js": "module.exports = {ripemd160: null}",
//"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,
@@ -152,7 +152,9 @@ function taskBundle(name, options) {
if (options.minify) {
result = result.pipe(buffer())
.pipe(sourcemaps.init({ loadMaps: true }))
.pipe(uglify())
.pipe(uglify({
output: { ascii_only: true }
}))
.pipe(sourcemaps.write('./'))
}
@@ -174,6 +176,30 @@ taskBundle("minified", { filename: "ethers.min.js", dest: 'dist', minify: true }
// Creates dist/ethers.min.js
taskBundle("minified-test", { filename: "ethers.min.js", dest: 'tests/dist', minify: true });
gulp.task('shims', function () {
var result = browserify({
basedir: '.',
debug: false,
entries: [ './tests/shims/index.js' ],
cache: { },
packageCache: {},
standalone: "_shims",
insertGlobalVars: {
process: function() { return; },
}
})
.bundle()
.pipe(source('shims.js'))
.pipe(buffer())
.pipe(uglify({
output: { ascii_only: true }
}))
.pipe(gulp.dest('dist'));
return result;
});
/*
// Dump the TypeScript definitions to dist/types/
gulp.task("types", function() {
@@ -265,7 +291,9 @@ function taskLang(locale) {
.bundle()
.pipe(source("wordlist-" + locale + ".js"))
.pipe(buffer())
.pipe(uglify())
.pipe(uglify({
output: { ascii_only: true }
}))
.pipe(gulp.dest("dist"));
});
}

77
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "ethers",
"version": "4.0.1",
"version": "4.0.27",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -69,9 +69,9 @@
"integrity": "sha512-8TqvB0ReZWwtcd3LXq3YSrBoLyXFgBX/sBZfGye9+YS8zH7/g+i6QRIuiDmwBoTzcQ/pk89nZYTYU4c5akKkzw=="
},
"JSONStream": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.3.tgz",
"integrity": "sha512-3Sp6WZZ/lXl+nTDoGpGWHEpTnnC6X5fnkolYZR6nwIfzbxxvA8utPWe1gCt7i0m9uVGsSz2IS8K8mJ7HmlduMg==",
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz",
"integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==",
"dev": true,
"requires": {
"jsonparse": "^1.2.0",
@@ -95,13 +95,10 @@
"dev": true
},
"acorn-dynamic-import": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz",
"integrity": "sha512-zVWV8Z8lislJoOKKqdNMOB+s6+XV5WERty8MnKBeFgwA+19XJjJHs2RP5dzM57FftIs+jQnRToLiWazKr6sSWg==",
"dev": true,
"requires": {
"acorn": "^5.0.0"
}
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz",
"integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==",
"dev": true
},
"acorn-jsx": {
"version": "4.1.1",
@@ -113,16 +110,31 @@
}
},
"acorn-node": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.5.2.tgz",
"integrity": "sha512-krFKvw/d1F17AN3XZbybIUzEY4YEPNiGo05AfP3dBlfVKrMHETKpgjpuZkSF8qDNt9UkQcqj7am8yJLseklCMg==",
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.6.2.tgz",
"integrity": "sha512-rIhNEZuNI8ibQcL7ANm/mGyPukIaZsRNX9psFNQURyJW0nu6k8wjSDld20z6v2mDBWqX13pIEnk9gGZJHIlEXg==",
"dev": true,
"requires": {
"acorn": "^5.7.1",
"acorn-dynamic-import": "^3.0.0",
"acorn": "^6.0.2",
"acorn-dynamic-import": "^4.0.0",
"acorn-walk": "^6.1.0",
"xtend": "^4.0.1"
},
"dependencies": {
"acorn": {
"version": "6.0.4",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.4.tgz",
"integrity": "sha512-VY4i5EKSKkofY2I+6QLTbTTN/UvEQPCo6eiwzzSaSWfpaDhOmStMCMod6wmuPciNq+XS0faCglFu2lHZpdHUtg==",
"dev": true
}
}
},
"acorn-walk": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.0.tgz",
"integrity": "sha512-ugTb7Lq7u4GfWSqqpwE0bGyoBZNMTok/zDBXxfEG0QM50jNlGhIWjRC1pPN7bvV1anhF+bs+/gNcRw+o55Evbg==",
"dev": true
},
"aes-js": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz",
@@ -783,9 +795,9 @@
"dev": true
},
"browserify": {
"version": "16.2.2",
"resolved": "https://registry.npmjs.org/browserify/-/browserify-16.2.2.tgz",
"integrity": "sha512-fMES05wq1Oukts6ksGUU2TMVHHp06LyQt0SIwbXIHm7waSrQmNBZePsU0iM/4f94zbvb/wHma+D1YrdzWYnF/A==",
"version": "16.2.3",
"resolved": "https://registry.npmjs.org/browserify/-/browserify-16.2.3.tgz",
"integrity": "sha512-zQt/Gd1+W+IY+h/xX2NYMW4orQWhqSwyV+xsblycTtpOuB27h1fZhhNQuipJ4t79ohw4P4mMem0jp/ZkISQtjQ==",
"dev": true,
"requires": {
"JSONStream": "^1.0.3",
@@ -927,9 +939,9 @@
}
},
"buffer": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.0.tgz",
"integrity": "sha512-nUJyfChH7PMJy75eRDCCKtszSEFokUNXC1hNVSe+o+VdcgvDPLs20k3v8UXI8ruRYAJiYtyRea8mYyqPxoHWDw==",
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz",
"integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==",
"dev": true,
"requires": {
"base64-js": "^1.0.2",
@@ -996,9 +1008,9 @@
}
},
"cached-path-relative": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.1.tgz",
"integrity": "sha1-0JxLUoAKpMB44t2BqGmqyQ0uVOc=",
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.2.tgz",
"integrity": "sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg==",
"dev": true
},
"caller-path": {
@@ -5288,9 +5300,9 @@
}
},
"pbkdf2": {
"version": "3.0.16",
"resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.16.tgz",
"integrity": "sha512-y4CXP3thSxqf7c0qmOF+9UeOTrifiVTIM+u7NWlq+PRsHbr7r7dpCmvzrZxa96JJUNi0Y5w9VqG5ZNeCVMoDcA==",
"version": "3.0.17",
"resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz",
"integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==",
"dev": true,
"requires": {
"create-hash": "^1.1.2",
@@ -5392,16 +5404,17 @@
}
},
"public-encrypt": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.2.tgz",
"integrity": "sha512-4kJ5Esocg8X3h8YgJsKAuoesBgB7mqH3eowiDzMUPKiRDDE7E/BqqZD1hnTByIaAFiwAw246YEltSq7tdrOH0Q==",
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
"integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==",
"dev": true,
"requires": {
"bn.js": "^4.1.0",
"browserify-rsa": "^4.0.0",
"create-hash": "^1.1.0",
"parse-asn1": "^5.0.0",
"randombytes": "^2.0.1"
"randombytes": "^2.0.1",
"safe-buffer": "^5.1.2"
}
},
"pump": {

View File

@@ -1,14 +1,14 @@
{
"name": "ethers",
"version": "4.0.2",
"version": "4.0.27",
"description": "Ethereum wallet library.",
"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": "npm run dist-version && npm run build && gulp default minified shims && npm run dist-types",
"dist-test": "gulp default-test minified-test shims",
"dist-bip39": "gulp bip39-es bip39-fr bip39-it bip39-ja bip39-ko bip39-zh",
"dist-types": "dts-bundle --name ethers --main ./index.d.ts --out ./dist/ethers.types.txt",
"dist-version": "node -e \"let v = require('./package.json').version; require('fs').writeFileSync('./src.ts/_version.ts', 'export const version = \\\"' + v +'\\\";\\n')\"",
@@ -31,7 +31,7 @@
"xmlhttprequest": "1.8.0"
},
"devDependencies": {
"browserify": "^16.2.2",
"browserify": "^16.2.3",
"browserify-zlib": "^0.2.0",
"dts-bundle": "^0.7.3",
"eslint": "^5.0.1",
@@ -53,7 +53,7 @@
"vinyl-source-stream": "^2.0.0",
"web3-providers-http": "1.0.0-beta.35"
},
"browser": "./dist/ethers.js",
"browser": "./dist/ethers.min.js",
"keywords": [
"ethereum",
"library",

View File

@@ -24,6 +24,11 @@ export declare type Filter = {
address?: string;
topics?: Array<string | Array<string>>;
};
export declare type FilterByBlock = {
blockHash?: string;
address?: string;
topics?: Array<string | Array<string>>;
};
export interface Log {
blockNumber?: number;
blockHash?: string;
@@ -37,6 +42,8 @@ export interface Log {
logIndex?: number;
}
export interface TransactionReceipt {
to?: string;
from?: string;
contractAddress?: string;
transactionIndex?: number;
root?: string;
@@ -46,6 +53,7 @@ export interface TransactionReceipt {
transactionHash?: string;
logs?: Array<Log>;
blockNumber?: number;
confirmations?: number;
cumulativeGasUsed?: BigNumber;
byzantium: boolean;
status?: number;
@@ -53,7 +61,7 @@ export interface TransactionReceipt {
export declare type TransactionRequest = {
to?: string | Promise<string>;
from?: string | Promise<string>;
nonce?: number | string | Promise<number | string>;
nonce?: BigNumberish | Promise<BigNumberish>;
gasLimit?: BigNumberish | Promise<BigNumberish>;
gasPrice?: BigNumberish | Promise<BigNumberish>;
data?: Arrayish | Promise<Arrayish>;
@@ -64,9 +72,10 @@ export interface TransactionResponse extends Transaction {
blockNumber?: number;
blockHash?: string;
timestamp?: number;
confirmations: number;
from: string;
raw?: string;
wait: (timeout?: number) => Promise<TransactionReceipt>;
wait: (confirmations?: number) => Promise<TransactionReceipt>;
}
export declare type EventType = string | Array<string> | Filter;
export declare type Listener = (...args: Array<any>) => void;
@@ -79,12 +88,12 @@ export declare abstract class Provider implements OnceBlockable {
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 call(transaction: TransactionRequest, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
abstract estimateGas(transaction: TransactionRequest): Promise<BigNumber>;
abstract getBlock(blockHashOrBlockTag: BlockTag | string | Promise<BlockTag | string>, includeTransactions?: boolean): Promise<Block>;
abstract getTransaction(transactionHash: string): Promise<TransactionResponse>;
abstract getTransactionReceipt(transactionHash: string): Promise<TransactionReceipt>;
abstract getLogs(filter: Filter): Promise<Array<Log>>;
abstract getLogs(filter: Filter | FilterByBlock): Promise<Array<Log>>;
abstract resolveName(name: string | Promise<string>): Promise<string>;
abstract lookupAddress(address: string | Promise<string>): Promise<string>;
abstract on(eventName: EventType, listener: Listener): Provider;

View File

@@ -1,17 +1,22 @@
import { BigNumber } from '../utils/bignumber';
import { Provider } from './abstract-provider';
import { Block, BlockTag, EventType, Filter, Listener, Log, TransactionReceipt, TransactionRequest, TransactionResponse } from './abstract-provider';
import { Block, BlockTag, EventType, Filter, FilterByBlock, Listener, Log, TransactionReceipt, TransactionRequest, TransactionResponse } from './abstract-provider';
import { BigNumberish } from '../utils/bignumber';
import { Transaction } from '../utils/transaction';
import { Network, Networkish } from '../utils/networks';
export declare class BaseProvider extends Provider {
private _network;
private _events;
protected _emitted: any;
protected _emitted: {
[eventName: string]: number | 'pending';
};
private _pollingInterval;
private _poller;
private _lastBlockNumber;
private _balances;
private _fastBlockNumber;
private _fastBlockNumberPromise;
private _fastQueryDate;
/**
* ready
*
@@ -30,7 +35,9 @@ export declare class BaseProvider extends Provider {
readonly blockNumber: number;
polling: boolean;
pollingInterval: number;
waitForTransaction(transactionHash: string, timeout?: number): Promise<TransactionReceipt>;
_getFastBlockNumber(): Promise<number>;
_setFastBlockNumber(blockNumber: number): void;
waitForTransaction(transactionHash: string, confirmations?: number): Promise<TransactionReceipt>;
getBlockNumber(): Promise<number>;
getGasPrice(): Promise<BigNumber>;
getBalance(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<BigNumber>;
@@ -39,12 +46,12 @@ export declare class BaseProvider extends Provider {
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>;
call(transaction: TransactionRequest, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
estimateGas(transaction: TransactionRequest): Promise<BigNumber>;
getBlock(blockHashOrBlockTag: BlockTag | string | Promise<BlockTag | string>, includeTransactions?: boolean): Promise<Block>;
getTransaction(transactionHash: string): Promise<TransactionResponse>;
getTransactionReceipt(transactionHash: string): Promise<TransactionReceipt>;
getLogs(filter: Filter): Promise<Array<Log>>;
getLogs(filter: Filter | FilterByBlock): Promise<Array<Log>>;
getEtherPrice(): Promise<number>;
private _resolveNames;
private _getResolver;
@@ -62,6 +69,6 @@ export declare class BaseProvider extends Provider {
emit(eventName: EventType, ...args: Array<any>): boolean;
listenerCount(eventName?: EventType): number;
listeners(eventName: EventType): Array<Listener>;
removeAllListeners(eventName: EventType): Provider;
removeAllListeners(eventName?: EventType): Provider;
removeListener(eventName: EventType, listener: Listener): Provider;
}

View File

@@ -79,9 +79,15 @@ function arrayOf(check) {
return result;
});
}
function checkHash(hash) {
if (typeof (hash) === 'string' && bytes_1.hexDataLength(hash) === 32) {
return hash.toLowerCase();
function checkHash(hash, requirePrefix) {
if (typeof (hash) === 'string') {
// geth-etc does add a "0x" prefix on receipt.root
if (!requirePrefix && hash.substring(0, 2) !== '0x') {
hash = '0x' + hash;
}
if (bytes_1.hexDataLength(hash) === 32) {
return hash.toLowerCase();
}
}
errors.throwError('invalid hash', errors.INVALID_ARGUMENT, { arg: 'hash', value: hash });
return null;
@@ -150,6 +156,7 @@ var formatTransaction = {
blockHash: allowNull(checkHash, null),
blockNumber: allowNull(checkNumber, null),
transactionIndex: allowNull(checkNumber, null),
confirmations: allowNull(checkNumber, null),
from: address_1.getAddress,
gasPrice: bignumber_1.bigNumberify,
gasLimit: bignumber_1.bigNumberify,
@@ -201,6 +208,10 @@ function checkTransactionResponse(transaction) {
}
var result = check(formatTransaction, transaction);
var networkId = transaction.networkId;
// geth-etc returns chainId
if (transaction.chainId != null && networkId == null && result.v == null) {
networkId = transaction.chainId;
}
if (bytes_1.isHexString(networkId)) {
networkId = bignumber_1.bigNumberify(networkId).toNumber();
}
@@ -269,6 +280,8 @@ function checkTransactionReceiptLog(log) {
return check(formatTransactionReceiptLog, log);
}
var formatTransactionReceipt = {
to: allowNull(address_1.getAddress, null),
from: allowNull(address_1.getAddress, null),
contractAddress: allowNull(address_1.getAddress, null),
transactionIndex: checkNumber,
root: allowNull(checkHash),
@@ -278,6 +291,7 @@ var formatTransactionReceipt = {
transactionHash: checkHash,
logs: arrayOf(checkTransactionReceiptLog),
blockNumber: checkNumber,
confirmations: allowNull(checkNumber, null),
cumulativeGasUsed: bignumber_1.bigNumberify,
status: allowNull(checkNumber)
};
@@ -312,7 +326,15 @@ var formatFilter = {
address: allowNull(address_1.getAddress, undefined),
topics: allowNull(checkTopics, undefined),
};
var formatFilterByBlock = {
blockHash: allowNull(checkHash, undefined),
address: allowNull(address_1.getAddress, undefined),
topics: allowNull(checkTopics, undefined),
};
function checkFilter(filter) {
if (filter && filter.blockHash) {
return check(formatFilterByBlock, filter);
}
return check(formatFilter, filter);
}
var formatLog = {
@@ -373,12 +395,12 @@ function getEventTag(eventName) {
return 'address:' + address_1.getAddress(eventName);
}
eventName = eventName.toLowerCase();
if (eventName === 'block' || eventName === 'pending' || eventName === 'error') {
return eventName;
}
else if (bytes_1.hexDataLength(eventName) === 32) {
if (bytes_1.hexDataLength(eventName) === 32) {
return 'tx:' + eventName;
}
if (eventName.indexOf(':') === -1) {
return eventName;
}
}
else if (Array.isArray(eventName)) {
return 'filter::' + serializeTopics(eventName);
@@ -388,6 +410,11 @@ function getEventTag(eventName) {
}
throw new Error('invalid event - ' + eventName);
}
//////////////////////////////
// Helper Object
function getTime() {
return (new Date()).getTime();
}
var BaseProvider = /** @class */ (function (_super) {
__extends(BaseProvider, _super);
function BaseProvider(network) {
@@ -398,6 +425,8 @@ var BaseProvider = /** @class */ (function (_super) {
properties_1.defineReadOnly(_this, '_network', network);
return network;
}));
// Squash any "unhandled promise" errors; the don't need to be handled
_this.ready.catch(function (error) { });
}
else {
var knownNetwork = networks_1.getNetwork((network == null) ? 'homestead' : network);
@@ -415,45 +444,62 @@ var BaseProvider = /** @class */ (function (_super) {
// Events being listened to
_this._events = [];
_this._pollingInterval = 4000;
// We use this to track recent emitted events; for example, if we emit a "block" of 100
// and we get a `getBlock(100)` request which would result in null, we should retry
// until we get a response. This provides devs with a consistent view. Similarly for
// transaction hashes.
_this._emitted = { block: _this._lastBlockNumber };
_this._emitted = { block: -2 };
_this._fastQueryDate = 0;
return _this;
}
BaseProvider.prototype._doPoll = function () {
var _this = this;
this.getBlockNumber().then(function (blockNumber) {
_this._setFastBlockNumber(blockNumber);
// If the block hasn't changed, meh.
if (blockNumber === _this._lastBlockNumber) {
return;
}
if (_this._lastBlockNumber === -2) {
_this._lastBlockNumber = blockNumber - 1;
// First polling cycle, trigger a "block" events
if (_this._emitted.block === -2) {
_this._emitted.block = blockNumber - 1;
}
// Notify all listener for each block that has passed
for (var i = _this._lastBlockNumber + 1; i <= blockNumber; i++) {
if (_this._emitted.block < i) {
_this._emitted.block = i;
for (var i = _this._emitted.block + 1; i <= blockNumber; i++) {
_this.emit('block', i);
}
// The emitted block was updated, check for obsolete events
if (_this._emitted.block !== blockNumber) {
_this._emitted.block = blockNumber;
Object.keys(_this._emitted).forEach(function (key) {
// The block event does not expire
if (key === 'block') {
return;
}
// The block we were at when we emitted this event
var eventBlockNumber = _this._emitted[key];
// We cannot garbage collect pending transactions or blocks here
// They should be garbage collected by the Provider when setting
// "pending" events
if (eventBlockNumber === 'pending') {
return;
}
// Evict any transaction hashes or block hashes over 12 blocks
// old, since they should not return null anyways
Object.keys(_this._emitted).forEach(function (key) {
if (key === 'block') {
return;
}
if (_this._emitted[key] > i + 12) {
delete _this._emitted[key];
}
});
}
_this.emit('block', i);
if (blockNumber - eventBlockNumber > 12) {
delete _this._emitted[key];
}
});
}
// First polling cycle
if (_this._lastBlockNumber === -2) {
_this._lastBlockNumber = blockNumber - 1;
}
// Sweep balances and remove addresses we no longer have events for
var newBalances = {};
// Find all transaction hashes we are waiting on
var uniqueEventTags = {};
_this._events.forEach(function (event) {
var comps = event.tag.split(':');
uniqueEventTags[event.tag] = true;
});
Object.keys(uniqueEventTags).forEach(function (tag) {
var comps = tag.split(':');
switch (comps[0]) {
case 'tx': {
var hash_2 = comps[1];
@@ -473,25 +519,27 @@ var BaseProvider = /** @class */ (function (_super) {
newBalances[address_2] = _this._balances[address_2];
}
_this.getBalance(address_2, 'latest').then(function (balance) {
var lastBalance = this._balances[address_2];
var lastBalance = _this._balances[address_2];
if (lastBalance && balance.eq(lastBalance)) {
return;
}
this._balances[address_2] = balance;
this.emit(address_2, balance);
_this._balances[address_2] = balance;
_this.emit(address_2, balance);
return null;
}).catch(function (error) { _this.emit('error', error); });
break;
}
case 'filter': {
var address = comps[1];
var topics = deserializeTopics(comps[2]);
var filter_1 = {
address: address,
address: comps[1],
fromBlock: _this._lastBlockNumber + 1,
toBlock: blockNumber,
topics: topics
};
if (!filter_1.address) {
delete filter_1.address;
}
_this.getLogs(filter_1).then(function (logs) {
if (logs.length === 0) {
return;
@@ -514,8 +562,10 @@ var BaseProvider = /** @class */ (function (_super) {
this.doPoll();
};
BaseProvider.prototype.resetEventsBlock = function (blockNumber) {
this._lastBlockNumber = blockNumber;
this._doPoll();
this._lastBlockNumber = blockNumber - 1;
if (this.polling) {
this._doPoll();
}
};
Object.defineProperty(BaseProvider.prototype, "network", {
get: function () {
@@ -529,10 +579,7 @@ var BaseProvider = /** @class */ (function (_super) {
};
Object.defineProperty(BaseProvider.prototype, "blockNumber", {
get: function () {
if (this._lastBlockNumber < 0) {
return null;
}
return this._lastBlockNumber;
return this._fastBlockNumber;
},
enumerable: true,
configurable: true
@@ -574,18 +621,56 @@ var BaseProvider = /** @class */ (function (_super) {
enumerable: true,
configurable: true
});
BaseProvider.prototype._getFastBlockNumber = function () {
var _this = this;
var now = getTime();
// Stale block number, request a newer value
if ((now - this._fastQueryDate) > 2 * this._pollingInterval) {
this._fastQueryDate = now;
this._fastBlockNumberPromise = this.getBlockNumber().then(function (blockNumber) {
if (_this._fastBlockNumber == null || blockNumber > _this._fastBlockNumber) {
_this._fastBlockNumber = blockNumber;
}
return _this._fastBlockNumber;
});
}
return this._fastBlockNumberPromise;
};
BaseProvider.prototype._setFastBlockNumber = function (blockNumber) {
// Older block, maybe a stale request
if (this._fastBlockNumber != null && blockNumber < this._fastBlockNumber) {
return;
}
// Update the time we updated the blocknumber
this._fastQueryDate = getTime();
// Newer block number, use it
if (this._fastBlockNumber == null || blockNumber > this._fastBlockNumber) {
this._fastBlockNumber = blockNumber;
this._fastBlockNumberPromise = Promise.resolve(blockNumber);
}
};
// @TODO: Add .poller which must be an event emitter with a 'start', 'stop' and 'block' event;
// this will be used once we move to the WebSocket or other alternatives to polling
BaseProvider.prototype.waitForTransaction = function (transactionHash, timeout) {
BaseProvider.prototype.waitForTransaction = function (transactionHash, confirmations) {
var _this = this;
return web_1.poll(function () {
return _this.getTransactionReceipt(transactionHash).then(function (receipt) {
if (receipt == null) {
return undefined;
}
if (confirmations == null) {
confirmations = 1;
}
return this.getTransactionReceipt(transactionHash).then(function (receipt) {
if (confirmations === 0 || (receipt && receipt.confirmations >= confirmations)) {
return receipt;
});
}, { onceBlock: this });
}
return (new Promise(function (resolve) {
var handler = function (receipt) {
if (receipt.confirmations < confirmations) {
return;
}
_this.removeListener(transactionHash, handler);
resolve(receipt);
};
_this.on(transactionHash, handler);
}));
});
};
BaseProvider.prototype.getBlockNumber = function () {
var _this = this;
@@ -595,6 +680,7 @@ var BaseProvider = /** @class */ (function (_super) {
if (value != result) {
throw new Error('invalid response - getBlockNumber');
}
_this._setFastBlockNumber(value);
return value;
});
});
@@ -688,7 +774,7 @@ var BaseProvider = /** @class */ (function (_super) {
// This should be called by any subclass wrapping a TransactionResponse
BaseProvider.prototype._wrapTransaction = function (tx, hash) {
var _this = this;
if (bytes_1.hexDataLength(hash) !== 32) {
if (hash != null && bytes_1.hexDataLength(hash) !== 32) {
throw new Error('invalid response - sendTransaction');
}
var result = tx;
@@ -696,13 +782,23 @@ var BaseProvider = /** @class */ (function (_super) {
if (hash != null && tx.hash !== hash) {
errors.throwError('Transaction hash mismatch from Provider.sendTransaction.', errors.UNKNOWN_ERROR, { expectedHash: tx.hash, returnedHash: hash });
}
this._emitted['t:' + tx.hash] = 'pending';
// @TODO: (confirmations? number, timeout? number)
result.wait = function () {
return _this.waitForTransaction(hash).then(function (receipt) {
result.wait = function (confirmations) {
// We know this transaction *must* exist (whether it gets mined is
// another story), so setting an emitted value forces us to
// wait even if the node returns null for the receipt
if (confirmations !== 0) {
_this._emitted['t:' + tx.hash] = 'pending';
}
return _this.waitForTransaction(tx.hash, confirmations).then(function (receipt) {
if (receipt == null && confirmations === 0) {
return null;
}
// No longer pending, allow the polling loop to garbage collect this
_this._emitted['t:' + tx.hash] = receipt.blockNumber;
if (receipt.status === 0) {
errors.throwError('transaction failed', errors.CALL_EXCEPTION, {
transactionHash: hash,
transactionHash: tx.hash,
transaction: tx
});
}
@@ -711,13 +807,14 @@ var BaseProvider = /** @class */ (function (_super) {
};
return result;
};
BaseProvider.prototype.call = function (transaction) {
BaseProvider.prototype.call = function (transaction, blockTag) {
var _this = this;
var tx = properties_1.shallowCopy(transaction);
return this.ready.then(function () {
return properties_1.resolveProperties(tx).then(function (tx) {
return properties_1.resolveProperties({ blockTag: blockTag, tx: tx }).then(function (_a) {
var blockTag = _a.blockTag, tx = _a.tx;
return _this._resolveNames(tx, ['to', 'from']).then(function (tx) {
var params = { transaction: checkTransactionRequest(tx) };
var params = { blockTag: checkBlockTag(blockTag), transaction: checkTransactionRequest(tx) };
return _this.perform('call', params).then(function (result) {
return bytes_1.hexlify(result);
});
@@ -751,12 +848,12 @@ var BaseProvider = /** @class */ (function (_super) {
return properties_1.resolveProperties({ blockHashOrBlockTag: blockHashOrBlockTag }).then(function (_a) {
var blockHashOrBlockTag = _a.blockHashOrBlockTag;
try {
var blockHash = bytes_1.hexlify(blockHashOrBlockTag);
if (bytes_1.hexDataLength(blockHash) === 32) {
var blockHash_1 = bytes_1.hexlify(blockHashOrBlockTag);
if (bytes_1.hexDataLength(blockHash_1) === 32) {
return web_1.poll(function () {
return _this.perform('getBlock', { blockHash: blockHash, includeTransactions: !!includeTransactions }).then(function (block) {
return _this.perform('getBlock', { blockHash: blockHash_1, includeTransactions: !!includeTransactions }).then(function (block) {
if (block == null) {
if (_this._emitted['b:' + blockHash] == null) {
if (_this._emitted['b:' + blockHash_1] == null) {
return null;
}
return undefined;
@@ -776,7 +873,7 @@ var BaseProvider = /** @class */ (function (_super) {
return web_1.poll(function () {
return _this.perform('getBlock', { blockTag: blockTag_1, includeTransactions: !!includeTransactions }).then(function (block) {
if (block == null) {
if (blockNumber_1 > _this._emitted.block) {
if (blockNumber_1 <= _this._emitted.block) {
return undefined;
}
return null;
@@ -795,7 +892,7 @@ var BaseProvider = /** @class */ (function (_super) {
return this.ready.then(function () {
return properties_1.resolveProperties({ transactionHash: transactionHash }).then(function (_a) {
var transactionHash = _a.transactionHash;
var params = { transactionHash: checkHash(transactionHash) };
var params = { transactionHash: checkHash(transactionHash, true) };
return web_1.poll(function () {
return _this.perform('getTransaction', params).then(function (result) {
if (result == null) {
@@ -804,7 +901,22 @@ var BaseProvider = /** @class */ (function (_super) {
}
return undefined;
}
return BaseProvider.checkTransactionResponse(result);
var tx = BaseProvider.checkTransactionResponse(result);
if (tx.blockNumber == null) {
tx.confirmations = 0;
}
else if (tx.confirmations == null) {
return _this._getFastBlockNumber().then(function (blockNumber) {
// Add the confirmations using the fast block number (pessimistic)
var confirmations = (blockNumber - tx.blockNumber) + 1;
if (confirmations <= 0) {
confirmations = 1;
}
tx.confirmations = confirmations;
return _this._wrapTransaction(tx);
});
}
return _this._wrapTransaction(tx);
});
}, { onceBlock: _this });
});
@@ -815,7 +927,7 @@ var BaseProvider = /** @class */ (function (_super) {
return this.ready.then(function () {
return properties_1.resolveProperties({ transactionHash: transactionHash }).then(function (_a) {
var transactionHash = _a.transactionHash;
var params = { transactionHash: checkHash(transactionHash) };
var params = { transactionHash: checkHash(transactionHash, true) };
return web_1.poll(function () {
return _this.perform('getTransactionReceipt', params).then(function (result) {
if (result == null) {
@@ -824,7 +936,26 @@ var BaseProvider = /** @class */ (function (_super) {
}
return undefined;
}
return checkTransactionReceipt(result);
// "geth-etc" returns receipts before they are ready
if (result.blockHash == null) {
return undefined;
}
var receipt = checkTransactionReceipt(result);
if (receipt.blockNumber == null) {
receipt.confirmations = 0;
}
else if (receipt.confirmations == null) {
return _this._getFastBlockNumber().then(function (blockNumber) {
// Add the confirmations using the fast block number (pessimistic)
var confirmations = (blockNumber - receipt.blockNumber) + 1;
if (confirmations <= 0) {
confirmations = 1;
}
receipt.confirmations = confirmations;
return receipt;
});
}
return receipt;
});
}, { onceBlock: _this });
});
@@ -975,7 +1106,7 @@ var BaseProvider = /** @class */ (function (_super) {
return null;
};
BaseProvider.prototype._startPending = function () {
console.log('WARNING: this provider does not support pending events');
errors.warn('WARNING: this provider does not support pending events');
};
BaseProvider.prototype._stopPending = function () {
};
@@ -1019,6 +1150,9 @@ var BaseProvider = /** @class */ (function (_super) {
result = true;
return !(event.once);
});
if (this.listenerCount() === 0) {
this.polling = false;
}
return result;
};
BaseProvider.prototype.listenerCount = function (eventName) {
@@ -1039,13 +1173,19 @@ var BaseProvider = /** @class */ (function (_super) {
});
};
BaseProvider.prototype.removeAllListeners = function (eventName) {
var eventTag = getEventTag(eventName);
this._events = this._events.filter(function (event) {
return (event.tag !== eventTag);
});
if (eventName === 'pending') {
if (eventName == null) {
this._events = [];
this._stopPending();
}
else {
var eventTag_1 = getEventTag(eventName);
this._events = this._events.filter(function (event) {
return (event.tag !== eventTag_1);
});
if (eventName === 'pending') {
this._stopPending();
}
}
if (this._events.length === 0) {
this.polling = false;
}
@@ -1055,13 +1195,13 @@ var BaseProvider = /** @class */ (function (_super) {
var found = false;
var eventTag = getEventTag(eventName);
this._events = this._events.filter(function (event) {
if (event.tag !== eventTag) {
if (event.tag !== eventTag || event.listener != listener) {
return true;
}
if (found) {
return true;
}
found = false;
found = true;
return false;
});
if (eventName === 'pending' && this.listenerCount('pending') === 0) {

View File

@@ -104,6 +104,9 @@ var EtherscanProvider = /** @class */ (function (_super) {
case 'kovan':
baseUrl = 'https://api-kovan.etherscan.io';
break;
case 'goerli':
baseUrl = 'https://api-goerli.etherscan.io';
break;
default:
throw new Error('unsupported network');
}
@@ -112,52 +115,65 @@ var EtherscanProvider = /** @class */ (function (_super) {
return _this;
}
EtherscanProvider.prototype.perform = function (method, params) {
//if (!params) { params = {}; }
var _this = this;
var url = this.baseUrl;
var apiKey = '';
if (this.apiKey) {
apiKey += '&apikey=' + this.apiKey;
}
var get = function (url, procFunc) {
return web_1.fetchJson(url, null, procFunc || getJsonResult).then(function (result) {
_this.emit('debug', {
action: 'perform',
request: url,
response: result,
provider: _this
});
return result;
});
};
switch (method) {
case 'getBlockNumber':
url += '/api?module=proxy&action=eth_blockNumber' + apiKey;
return web_1.fetchJson(url, null, getJsonResult);
return get(url);
case 'getGasPrice':
url += '/api?module=proxy&action=eth_gasPrice' + apiKey;
return web_1.fetchJson(url, null, getJsonResult);
return get(url);
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);
return get(url, 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);
return get(url);
case 'getCode':
url += '/api?module=proxy&action=eth_getCode&address=' + params.address;
url += '&tag=' + params.blockTag + apiKey;
return web_1.fetchJson(url, null, getJsonResult);
return get(url, getJsonResult);
case 'getStorageAt':
url += '/api?module=proxy&action=eth_getStorageAt&address=' + params.address;
url += '&position=' + params.position;
url += '&tag=' + params.blockTag + apiKey;
return web_1.fetchJson(url, null, getJsonResult);
return get(url, 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, {});
return get(url).catch(function (error) {
if (error.responseText) {
// "Insufficient funds. The account you tried to send transaction from does not have enough funds. Required 21464000000000 and got: 0"
if (error.responseText.toLowerCase().indexOf('insufficient funds') >= 0) {
errors.throwError('insufficient funds', errors.INSUFFICIENT_FUNDS, {});
}
// "Transaction with the same hash was already imported."
if (error.responseText.indexOf('same hash was already imported') >= 0) {
errors.throwError('nonce has already been used', errors.NONCE_EXPIRED, {});
}
// "Transaction gas price is too low. There is another transaction with same nonce in the queue. Try increasing the gas price or incrementing the nonce."
if (error.responseText.indexOf('another transaction with same nonce') >= 0) {
errors.throwError('replacement fee too low', errors.REPLACEMENT_UNDERPRICED, {});
}
}
throw error;
});
@@ -171,33 +187,39 @@ var EtherscanProvider = /** @class */ (function (_super) {
url += '&boolean=false';
}
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult);
return get(url);
}
throw new Error('getBlock by blockHash not implmeneted');
case 'getTransaction':
url += '/api?module=proxy&action=eth_getTransactionByHash&txhash=' + params.transactionHash;
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult);
return get(url);
case 'getTransactionReceipt':
url += '/api?module=proxy&action=eth_getTransactionReceipt&txhash=' + params.transactionHash;
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult);
case 'call':
return get(url);
case 'call': {
var transaction = getTransactionString(params.transaction);
if (transaction) {
transaction = '&' + transaction;
}
url += '/api?module=proxy&action=eth_call' + transaction;
//url += '&tag=' + params.blockTag + apiKey;
if (params.blockTag !== 'latest') {
throw new Error('EtherscanProvider does not support blockTag for call');
}
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult);
case 'estimateGas':
return get(url);
}
case 'estimateGas': {
var transaction = getTransactionString(params.transaction);
if (transaction) {
transaction = '&' + transaction;
}
url += '/api?module=proxy&action=eth_estimateGas&' + transaction;
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult);
return get(url);
}
case 'getLogs':
url += '/api?module=logs&action=getLogs';
try {
@@ -207,6 +229,16 @@ var EtherscanProvider = /** @class */ (function (_super) {
if (params.filter.toBlock) {
url += '&toBlock=' + checkLogTag(params.filter.toBlock);
}
if (params.filter.blockHash) {
try {
errors.throwError("Etherscan does not support blockHash filters", errors.UNSUPPORTED_OPERATION, {
operation: "getLogs(blockHash)"
});
}
catch (error) {
return Promise.reject(error);
}
}
if (params.filter.address) {
url += '&address=' + params.filter.address;
}
@@ -227,7 +259,7 @@ var EtherscanProvider = /** @class */ (function (_super) {
}
url += apiKey;
var self = this;
return web_1.fetchJson(url, null, getResult).then(function (logs) {
return get(url, getResult).then(function (logs) {
var txs = {};
var seq = Promise.resolve();
logs.forEach(function (log) {
@@ -256,7 +288,7 @@ var EtherscanProvider = /** @class */ (function (_super) {
}
url += '/api?module=stats&action=ethprice';
url += apiKey;
return web_1.fetchJson(url, null, getResult).then(function (result) {
return get(url, getResult).then(function (result) {
return parseFloat(result.ethusd);
});
default:
@@ -266,6 +298,7 @@ var EtherscanProvider = /** @class */ (function (_super) {
};
// @TODO: Allow startBlock and endBlock to be Promises
EtherscanProvider.prototype.getHistory = function (addressOrName, startBlock, endBlock) {
var _this = this;
var url = this.baseUrl;
var apiKey = '';
if (this.apiKey) {
@@ -283,6 +316,12 @@ var EtherscanProvider = /** @class */ (function (_super) {
url += '&endblock=' + endBlock;
url += '&sort=asc' + apiKey;
return web_1.fetchJson(url, null, getResult).then(function (result) {
_this.emit('debug', {
action: 'getHistory',
request: url,
response: result,
provider: _this
});
var output = [];
result.forEach(function (tx) {
['contractAddress', 'to'].forEach(function (key) {

View File

@@ -40,7 +40,8 @@ function checkNetworks(networks) {
// Matches!
if (check.name === network.name &&
check.chainId === network.chainId &&
check.ensAddress === network.ensAddress) {
((check.ensAddress === network.ensAddress) ||
(check.ensAddress == null && network.ensAddress == null))) {
return;
}
errors.throwError('provider mismatch', errors.INVALID_ARGUMENT, { arg: 'networks', value: networks });

View File

@@ -2,7 +2,8 @@ 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);
readonly projectId: string;
constructor(network?: Networkish, projectId?: string);
protected _startPending(): void;
getSigner(address?: string): JsonRpcSigner;
listAccounts(): Promise<Array<string>>;

View File

@@ -18,16 +18,21 @@ var __importStar = (this && this.__importStar) || function (mod) {
};
Object.defineProperty(exports, "__esModule", { value: true });
var json_rpc_provider_1 = require("./json-rpc-provider");
var bytes_1 = require("../utils/bytes");
var networks_1 = require("../utils/networks");
var properties_1 = require("../utils/properties");
var errors = __importStar(require("../errors"));
var defaultProjectId = "7d0d81d0919f4f05b9ab6634be01ee73";
var InfuraProvider = /** @class */ (function (_super) {
__extends(InfuraProvider, _super);
function InfuraProvider(network, apiAccessToken) {
function InfuraProvider(network, projectId) {
var _this = this;
network = networks_1.getNetwork((network == null) ? 'homestead' : network);
var standard = networks_1.getNetwork((network == null) ? 'homestead' : network);
if (projectId == null) {
projectId = defaultProjectId;
}
var host = null;
switch (network.name) {
switch (standard.name) {
case 'homestead':
host = 'mainnet.infura.io';
break;
@@ -37,23 +42,38 @@ var InfuraProvider = /** @class */ (function (_super) {
case 'rinkeby':
host = 'rinkeby.infura.io';
break;
case 'goerli':
host = 'goerli.infura.io';
break;
case 'kovan':
host = 'kovan.infura.io';
break;
default:
throw new Error('unsupported network');
errors.throwError('unsupported network', errors.INVALID_ARGUMENT, {
argument: "network",
value: network
});
}
// New-style Project ID
if (bytes_1.isHexString("0x" + projectId, 16)) {
_this = _super.call(this, 'https://' + host + '/v3/' + projectId, standard) || this;
properties_1.defineReadOnly(_this, 'apiAccessToken', null);
properties_1.defineReadOnly(_this, 'projectId', projectId);
// Legacy API Access Token
}
else {
_this = _super.call(this, 'https://' + host + '/' + projectId, standard) || this;
properties_1.defineReadOnly(_this, 'apiAccessToken', projectId);
properties_1.defineReadOnly(_this, 'projectId', null);
}
_this = _super.call(this, 'https://' + host + '/' + (apiAccessToken || ''), network) || this;
errors.checkNew(_this, InfuraProvider);
properties_1.defineReadOnly(_this, 'apiAccessToken', apiAccessToken || null);
return _this;
}
InfuraProvider.prototype._startPending = function () {
console.log('WARNING: INFURA does not support pending filters');
errors.warn('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;
return errors.throwError('INFURA does not support signing', errors.UNSUPPORTED_OPERATION, { operation: 'getSigner' });
};
InfuraProvider.prototype.listAccounts = function () {
return Promise.resolve([]);

View File

@@ -29,7 +29,9 @@ var IpcProvider = /** @class */ (function (_super) {
function IpcProvider(path, network) {
var _this = this;
if (path == null) {
errors.throwError('missing path', errors.MISSING_ARGUMENT, { arg: 'path' });
errors.throwError('missing path', errors.MISSING_ARGUMENT, {
argument: 'path'
});
}
_this = _super.call(this, 'ipc://' + path, network) || this;
errors.checkNew(_this, IpcProvider);
@@ -50,10 +52,14 @@ var IpcProvider = /** @class */ (function (_super) {
jsonrpc: "2.0"
});
return new Promise(function (resolve, reject) {
var response = Buffer.alloc(0);
var stream = net_1.default.connect(_this.path);
stream.on('data', function (data) {
response = Buffer.concat([response, data]);
});
stream.on("end", function () {
try {
resolve(JSON.parse(data.toString('utf8')).result);
resolve(JSON.parse(response.toString('utf8')).result);
// @TODO: Better pull apart the error
stream.destroy();
}
@@ -62,9 +68,6 @@ var IpcProvider = /** @class */ (function (_super) {
stream.destroy();
}
});
stream.on('end', function () {
stream.destroy();
});
stream.on('error', function (error) {
reject(error);
stream.destroy();

View File

@@ -13,6 +13,7 @@ export declare class JsonRpcSigner extends Signer {
getAddress(): Promise<string>;
getBalance(blockTag?: BlockTag): Promise<BigNumber>;
getTransactionCount(blockTag?: BlockTag): Promise<number>;
sendUncheckedTransaction(transaction: TransactionRequest): Promise<string>;
sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse>;
signMessage(message: Arrayish | string): Promise<string>;
unlock(password: string): Promise<boolean>;
@@ -27,5 +28,9 @@ export declare class JsonRpcProvider extends BaseProvider {
perform(method: string, params: any): Promise<any>;
protected _startPending(): void;
protected _stopPending(): void;
static hexlifyTransaction(transaction: TransactionRequest): any;
static hexlifyTransaction(transaction: TransactionRequest, allowExtra?: {
[key: string]: boolean;
}): {
[key: string]: string;
};
}

View File

@@ -20,13 +20,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
// See: https://github.com/ethereum/wiki/wiki/JSON-RPC
var base_provider_1 = require("./base-provider");
var abstract_signer_1 = require("../abstract-signer");
var errors = __importStar(require("../errors"));
var address_1 = require("../utils/address");
var bytes_1 = require("../utils/bytes");
var networks_1 = require("../utils/networks");
var properties_1 = require("../utils/properties");
var utf8_1 = require("../utils/utf8");
var web_1 = require("../utils/web");
var errors = __importStar(require("../errors"));
function timer(timeout) {
return new Promise(function (resolve) {
setTimeout(function () {
@@ -77,14 +77,6 @@ var JsonRpcSigner = /** @class */ (function (_super) {
}
return _this;
}
/* 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) {
@@ -104,50 +96,67 @@ var JsonRpcSigner = /** @class */ (function (_super) {
JsonRpcSigner.prototype.getTransactionCount = function (blockTag) {
return this.provider.getTransactionCount(this.getAddress(), blockTag);
};
JsonRpcSigner.prototype.sendUncheckedTransaction = function (transaction) {
var _this = this;
transaction = properties_1.shallowCopy(transaction);
var fromAddress = this.getAddress().then(function (address) {
if (address) {
address = address.toLowerCase();
}
return address;
});
// The JSON-RPC for eth_sendTransaction uses 90000 gas; if the user
// wishes to use this, it is easy to specify explicitly, otherwise
// we look it up for them.
if (transaction.gasLimit == null) {
var estimate = properties_1.shallowCopy(transaction);
estimate.from = fromAddress;
transaction.gasLimit = this.provider.estimateGas(estimate);
}
return Promise.all([
properties_1.resolveProperties(transaction),
fromAddress
]).then(function (results) {
var tx = results[0];
var hexTx = JsonRpcProvider.hexlifyTransaction(tx);
hexTx.from = results[1];
return _this.provider.send('eth_sendTransaction', [hexTx]).then(function (hash) {
return hash;
}, function (error) {
if (error.responseText) {
// See: JsonRpcProvider.sendTransaction (@TODO: Expose a ._throwError??)
if (error.responseText.indexOf('insufficient funds') >= 0) {
errors.throwError('insufficient funds', errors.INSUFFICIENT_FUNDS, {
transaction: tx
});
}
if (error.responseText.indexOf('nonce too low') >= 0) {
errors.throwError('nonce has already been used', errors.NONCE_EXPIRED, {
transaction: tx
});
}
if (error.responseText.indexOf('replacement transaction underpriced') >= 0) {
errors.throwError('replacement fee too low', errors.REPLACEMENT_UNDERPRICED, {
transaction: tx
});
}
}
throw error;
});
});
};
JsonRpcSigner.prototype.sendTransaction = function (transaction) {
var _this = this;
var tx = properties_1.shallowCopy(transaction);
if (tx.from == null) {
tx.from = this.getAddress().then(function (address) {
if (!address) {
return null;
}
return address.toLowerCase();
});
}
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;
return this.sendUncheckedTransaction(transaction).then(function (hash) {
return web_1.poll(function () {
return _this.provider.getTransaction(hash).then(function (tx) {
if (tx === null) {
return undefined;
}
return _this.provider._wrapTransaction(tx, hash);
});
}, 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
});
}
}, { fastRetry: 250, onceBlock: _this.provider }).catch(function (error) {
error.transactionHash = hash;
throw error;
});
});
@@ -169,6 +178,9 @@ var JsonRpcSigner = /** @class */ (function (_super) {
return JsonRpcSigner;
}(abstract_signer_1.Signer));
exports.JsonRpcSigner = JsonRpcSigner;
var allowedTransactionKeys = {
chainId: true, data: true, gasLimit: true, gasPrice: true, nonce: true, to: true, value: true
};
var JsonRpcProvider = /** @class */ (function (_super) {
__extends(JsonRpcProvider, _super);
function JsonRpcProvider(url, network) {
@@ -221,13 +233,22 @@ var JsonRpcProvider = /** @class */ (function (_super) {
});
};
JsonRpcProvider.prototype.send = function (method, params) {
var _this = this;
var request = {
method: method,
params: params,
id: 42,
jsonrpc: "2.0"
};
return web_1.fetchJson(this.connection, JSON.stringify(request), getResult);
return web_1.fetchJson(this.connection, JSON.stringify(request), getResult).then(function (result) {
_this.emit('debug', {
action: 'send',
request: request,
response: result,
provider: _this
});
return result;
});
};
JsonRpcProvider.prototype.perform = function (method, params) {
switch (method) {
@@ -245,17 +266,19 @@ var JsonRpcProvider = /** @class */ (function (_super) {
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, {});
if (error.responseText) {
// "insufficient funds for gas * price + value"
if (error.responseText.indexOf('insufficient funds') > 0) {
errors.throwError('insufficient funds', errors.INSUFFICIENT_FUNDS, {});
}
// "nonce too low"
if (error.responseText.indexOf('nonce too low') > 0) {
errors.throwError('nonce has already been used', errors.NONCE_EXPIRED, {});
}
// "replacement transaction underpriced"
if (error.responseText.indexOf('replacement transaction underpriced') > 0) {
errors.throwError('replacement fee too low', errors.REPLACEMENT_UNDERPRICED, {});
}
}
throw error;
});
@@ -272,9 +295,9 @@ var JsonRpcProvider = /** @class */ (function (_super) {
case 'getTransactionReceipt':
return this.send('eth_getTransactionReceipt', [params.transactionHash]);
case 'call':
return this.send('eth_call', [JsonRpcProvider.hexlifyTransaction(params.transaction), 'latest']);
return this.send('eth_call', [JsonRpcProvider.hexlifyTransaction(params.transaction, { from: true }), params.blockTag]);
case 'estimateGas':
return this.send('eth_estimateGas', [JsonRpcProvider.hexlifyTransaction(params.transaction)]);
return this.send('eth_estimateGas', [JsonRpcProvider.hexlifyTransaction(params.transaction, { from: true })]);
case 'getLogs':
if (params.filter && params.filter.address != null) {
params.filter.address = getLowerCase(params.filter.address);
@@ -301,6 +324,7 @@ var JsonRpcProvider = /** @class */ (function (_super) {
}
var seq = Promise.resolve();
hashes.forEach(function (hash) {
// @TODO: This should be garbage collected at some point... How? When?
self._emitted['t:' + hash.toLowerCase()] = 'pending';
seq = seq.then(function () {
return self.getTransaction(hash).then(function (tx) {
@@ -332,10 +356,21 @@ var JsonRpcProvider = /** @class */ (function (_super) {
// - gasLimit => gas
// - All values hexlified
// - All numeric values zero-striped
// @TODO: Not any, a dictionary of string to strings
JsonRpcProvider.hexlifyTransaction = function (transaction) {
// NOTE: This allows a TransactionRequest, but all values should be resolved
// before this is called
JsonRpcProvider.hexlifyTransaction = function (transaction, allowExtra) {
// Check only allowed properties are given
var allowed = properties_1.shallowCopy(allowedTransactionKeys);
if (allowExtra) {
for (var key in allowExtra) {
if (allowExtra[key]) {
allowed[key] = true;
}
}
}
properties_1.checkProperties(transaction, allowed);
var result = {};
// Some nodes (INFURA ropsten; INFURA mainnet is fine) don't like extra zeros.
// Some nodes (INFURA ropsten; INFURA mainnet is fine) don't like leading zeros.
['gasLimit', 'gasPrice', 'nonce', 'value'].forEach(function (key) {
if (transaction[key] == null) {
return;

View File

@@ -1 +1 @@
export const version = "4.0.2";
export const version = "4.0.27";

View File

@@ -16,7 +16,7 @@ import { UnsignedTransaction } from './utils/transaction';
///////////////////////////////
// Imported Abstracts
import { Provider } from './providers/abstract-provider';
import { BlockTag, Provider } from './providers/abstract-provider';
import { Signer } from './abstract-signer';
///////////////////////////////
@@ -148,14 +148,22 @@ 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 = {}
let tx: any = {}
let blockTag: BlockTag = null;
// If 1 extra parameter was passed in, it contains overrides
if (params.length === method.inputs.length + 1 && typeof(params[params.length - 1]) === 'object') {
tx = shallowCopy(params.pop());
if (tx.blockTag != null) {
blockTag = tx.blockTag;
}
delete tx.blockTag;
// Check for unexpected keys (e.g. using "gas" instead of "gasLimit")
for (var key in tx) {
for (let key in tx) {
if (!allowedTransactionKeys[key]) {
throw new Error('unknown transaction override ' + key);
}
@@ -173,8 +181,7 @@ function runMethod(contract: Contract, functionName: string, estimateOnly: boole
}
});
// Send to the contract address (after checking the contract is deployed)
tx.to = contract.deployed().then(() => {
tx.to = contract._deployed(blockTag).then(() => {
return contract.addressPromise;
});
@@ -202,8 +209,7 @@ function runMethod(contract: Contract, functionName: string, estimateOnly: boole
tx.from = contract.signer.getAddress()
}
return contract.provider.call(tx).then((value) => {
return contract.provider.call(tx, blockTag).then((value) => {
if ((hexDataLength(value) % 32) === 4 && hexDataSlice(value, 0, 4) === '0x08c379a0') {
let reason = defaultAbiCoder.decode([ 'string' ], hexDataSlice(value, 4));
errors.throwError('call revert exception', errors.CALL_EXCEPTION, {
@@ -272,7 +278,7 @@ function runMethod(contract: Contract, functionName: string, estimateOnly: boole
receipt.events = receipt.logs.map((log) => {
let event: Event = (<Event>deepCopy(log));
let parsed = this.interface.parseLog(log);
let parsed = contract.interface.parseLog(log);
if (parsed) {
event.args = parsed.values;
event.decode = parsed.decode;
@@ -280,12 +286,12 @@ function runMethod(contract: Contract, functionName: string, estimateOnly: boole
event.eventSignature = parsed.signature;
}
event.removeListener = () => { return this.provider; }
event.removeListener = () => { return contract.provider; }
event.getBlock = () => {
return this.provider.getBlock(receipt.blockHash);
return contract.provider.getBlock(receipt.blockHash);
}
event.getTransaction = () => {
return this.provider.getTransaction(receipt.transactionHash);
return contract.provider.getTransaction(receipt.transactionHash);
}
event.getTransactionReceipt = () => {
return Promise.resolve(receipt);
@@ -352,7 +358,7 @@ export class Contract {
// This is only set if the contract was created with a call to deploy
readonly deployTransaction: TransactionResponse;
private _deployed: Promise<Contract>;
private _deployedPromise: Promise<Contract>;
// https://github.com/Microsoft/TypeScript/issues/5453
// Once this issue is resolved (there are open PR) we can do this nicer
@@ -403,7 +409,6 @@ export class Contract {
if (address == null) { throw new Error('name not found'); }
return address;
}).catch((error: Error) => {
console.log('ERROR: Cannot find Contract - ' + addressOrName);
throw error;
}));
} else {
@@ -421,7 +426,7 @@ export class Contract {
if ((<any>this)[name] == null) {
defineReadOnly(this, name, run);
} else {
console.log('WARNING: Multiple definitions for ' + name);
errors.warn('WARNING: Multiple definitions for ' + name);
}
if (this.functions[name] == null) {
@@ -433,11 +438,15 @@ export class Contract {
// @TODO: Allow timeout?
deployed(): Promise<Contract> {
if (!this._deployed) {
return this._deployed();
}
_deployed(blockTag?: BlockTag): Promise<Contract> {
if (!this._deployedPromise) {
// If we were just deployed, we know the transaction we should occur in
if (this.deployTransaction) {
this._deployed = this.deployTransaction.wait().then(() => {
this._deployedPromise = this.deployTransaction.wait().then(() => {
return this;
});
@@ -446,7 +455,7 @@ export class Contract {
// up to that many blocks for getCode
// Otherwise, poll for our code to be deployed
this._deployed = this.provider.getCode(this.address).then((code) => {
this._deployedPromise = this.provider.getCode(this.address, blockTag).then((code) => {
if (code === '0x') {
errors.throwError('contract not deployed', errors.UNSUPPORTED_OPERATION, {
contractAddress: this.address,
@@ -458,7 +467,7 @@ export class Contract {
}
}
return this._deployed;
return this._deployedPromise;
}
// @TODO:
@@ -689,7 +698,15 @@ export class Contract {
let eventFilter = this._getEventFilter(eventName);
this._events = this._events.filter((event) => {
return event.eventFilter.eventTag !== eventFilter.eventTag
// Keep all other events
if (event.eventFilter.eventTag !== eventFilter.eventTag) {
return true;
}
// Deregister this event from the provider and filter it out
this.provider.removeListener(event.eventFilter.filter, event.wrappedListener);
return false;
});
return this;
@@ -791,7 +808,7 @@ export class ContractFactory {
});
// Make sure the call matches the constructor signature
errors.checkArgumentCount(args.length, this.interface.deployFunction.inputs.length, 'in Contract constructor');
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);

View File

@@ -23,7 +23,7 @@ export const MISSING_NEW = 'MISSING_NEW';
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
// - argument: The argument name that was invalid
// - value: The value of the argument
export const INVALID_ARGUMENT = 'INVALID_ARGUMENT';
@@ -116,9 +116,53 @@ export function checkArgumentCount(count: number, expectedCount: number, suffix?
export function setCensorship(censorship: boolean, permanent?: boolean): void {
if (_permanentCensorErrors) {
throwError('error censorship permanent', UNSUPPORTED_OPERATION, { operation: 'setCersorship' });
throwError('error censorship permanent', UNSUPPORTED_OPERATION, { operation: 'setCensorship' });
}
_censorErrors = !!censorship;
_permanentCensorErrors = !!permanent;
}
export function checkNormalize(): void {
try {
// Make sure all forms of normalization are supported
["NFD", "NFC", "NFKD", "NFKC"].forEach((form) => {
try {
"test".normalize(form);
} catch(error) {
throw new Error('missing ' + form);
}
});
if (String.fromCharCode(0xe9).normalize('NFD') !== String.fromCharCode(0x65, 0x0301)) {
throw new Error('broken implementation')
}
} catch (error) {
throwError('platform missing String.prototype.normalize', UNSUPPORTED_OPERATION, { operation: 'String.prototype.normalize', form: error.message });
}
}
const LogLevels: { [ name: string ]: number } = { debug: 1, "default": 2, info: 2, warn: 3, error: 4, off: 5 };
let LogLevel = LogLevels["default"];
export function setLogLevel(logLevel: string): void {
let level = LogLevels[logLevel];
if (level == null) {
warn("invliad log level - " + logLevel);
return;
}
LogLevel = level;
}
function log(logLevel: string, args: Array<any>): void {
if (LogLevel > LogLevels[logLevel]) { return; }
console.log.apply(console, args);
}
export function warn(...args: Array<any>): void {
log("warn", args);
}
export function info(...args: Array<any>): void {
log("info", args);
}

View File

@@ -33,10 +33,15 @@ import { ContractFunction, ContractTransaction, Event, EventFilter } from './con
// Helper Functions
function getDefaultProvider(network?: utils.Network | string): providers.BaseProvider {
return new providers.FallbackProvider([
new providers.InfuraProvider(network),
new providers.EtherscanProvider(network),
]);
if (network == null) { network = 'homestead'; }
let n = utils.getNetwork(network);
if (!n || !n._defaultProvider) {
errors.throwError('unsupported getDefaultProvider network', errors.UNSUPPORTED_OPERATION, {
operation: 'getDefaultProvider',
network: network
});
}
return n._defaultProvider(providers);
}

View File

@@ -41,6 +41,16 @@ export type Filter = {
topics?: Array<string | Array<string>>,
}
// @TODO: This is not supported as an EventType yet, as it will
// need some additional work to adhere to the serialized
// format for events. But we want to allow it for getLogs
// for now.
export type FilterByBlock = {
blockHash?: string,
address?: string,
topics?: Array<string | Array<string>>,
}
export interface Log {
blockNumber?: number;
blockHash?: string;
@@ -60,6 +70,8 @@ export interface Log {
}
export interface TransactionReceipt {
to?: string;
from?: string;
contractAddress?: string,
transactionIndex?: number,
root?: string,
@@ -78,7 +90,7 @@ export interface TransactionReceipt {
export type TransactionRequest = {
to?: string | Promise<string>,
from?: string | Promise<string>,
nonce?: number | string | Promise<number | string>,
nonce?: BigNumberish | Promise<BigNumberish>,
gasLimit?: BigNumberish | Promise<BigNumberish>,
gasPrice?: BigNumberish | Promise<BigNumberish>,
@@ -125,14 +137,14 @@ export abstract class Provider implements OnceBlockable {
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 call(transaction: TransactionRequest, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
abstract estimateGas(transaction: TransactionRequest): Promise<BigNumber>;
abstract getBlock(blockHashOrBlockTag: BlockTag | string | Promise<BlockTag | string>, includeTransactions?: boolean): Promise<Block>;
abstract getTransaction(transactionHash: string): Promise<TransactionResponse>;
abstract getTransactionReceipt(transactionHash: string): Promise<TransactionReceipt>;
abstract getLogs(filter: Filter): Promise<Array<Log>>;
abstract getLogs(filter: Filter | FilterByBlock): Promise<Array<Log>>;
abstract resolveName(name: string | Promise<string>): Promise<string>;
abstract lookupAddress(address: string | Promise<string>): Promise<string>;

View File

@@ -23,7 +23,7 @@ import { Provider } from './abstract-provider';
import {
Block, BlockTag,
EventType, Filter,
EventType, Filter, FilterByBlock,
Listener,
Log,
TransactionReceipt, TransactionRequest, TransactionResponse
@@ -38,10 +38,10 @@ import { Network, Networkish } from '../utils/networks';
// @TODO: not any?
function check(format: any, object: any): any {
var result: any = {};
for (var key in format) {
let result: any = {};
for (let key in format) {
try {
var value = format[key](object[key]);
let value = format[key](object[key]);
if (value !== undefined) { result[key] = value; }
} catch (error) {
error.checkKey = key;
@@ -72,7 +72,7 @@ function arrayOf(check: CheckFunc): CheckFunc {
return (function(array: any): Array<any> {
if (!Array.isArray(array)) { throw new Error('not an array'); }
var result: any = [];
let result: any = [];
array.forEach(function(value) {
result.push(check(value));
@@ -82,9 +82,13 @@ function arrayOf(check: CheckFunc): CheckFunc {
});
}
function checkHash(hash: any): string {
if (typeof(hash) === 'string' && hexDataLength(hash) === 32) {
return hash.toLowerCase();
function checkHash(hash: any, requirePrefix?: boolean): string {
if (typeof(hash) === 'string') {
// geth-etc does add a "0x" prefix on receipt.root
if (!requirePrefix && hash.substring(0, 2) !== '0x') { hash = '0x' + hash; }
if (hexDataLength(hash) === 32) {
return hash.toLowerCase();
}
}
errors.throwError('invalid hash', errors.INVALID_ARGUMENT, { arg: 'hash', value: hash });
return null;
@@ -204,7 +208,7 @@ function checkTransactionResponse(transaction: any): TransactionResponse {
// Very loose providers (e.g. TestRPC) don't provide a signature or raw
if (transaction.v && transaction.r && transaction.s) {
var raw = [
let raw = [
stripZeros(hexlify(transaction.nonce)),
stripZeros(hexlify(transaction.gasPrice)),
stripZeros(hexlify(transaction.gasLimit)),
@@ -220,10 +224,14 @@ function checkTransactionResponse(transaction: any): TransactionResponse {
}
}
let result = check(formatTransaction, transaction);
var result = check(formatTransaction, transaction);
let networkId = transaction.networkId;
var networkId = transaction.networkId;
// geth-etc returns chainId
if (transaction.chainId != null && networkId == null && result.v == null) {
networkId = transaction.chainId;
}
if (isHexString(networkId)) {
networkId = bigNumberify(networkId).toNumber();
@@ -306,6 +314,8 @@ function checkTransactionReceiptLog(log: any): any {
}
const formatTransactionReceipt = {
to: allowNull(getAddress, null),
from: allowNull(getAddress, null), // Gaanche does not populate this (#400)
contractAddress: allowNull(getAddress, null),
transactionIndex: checkNumber,
root: allowNull(checkHash),
@@ -324,7 +334,7 @@ function checkTransactionReceipt(transactionReceipt: any): TransactionReceipt {
//var status = transactionReceipt.status;
//var root = transactionReceipt.root;
var result: TransactionReceipt = check(formatTransactionReceipt, transactionReceipt);
let result: TransactionReceipt = check(formatTransactionReceipt, transactionReceipt);
result.logs.forEach(function(entry, index) {
if (entry.transactionLogIndex == null) {
entry.transactionLogIndex = index;
@@ -356,7 +366,16 @@ const formatFilter = {
topics: allowNull(checkTopics, undefined),
};
const formatFilterByBlock = {
blockHash: allowNull(checkHash, undefined),
address: allowNull(getAddress, undefined),
topics: allowNull(checkTopics, undefined),
};
function checkFilter(filter: any): any {
if (filter && filter.blockHash) {
return check(formatFilterByBlock, filter);
}
return check(formatFilter, filter);
}
@@ -425,13 +444,14 @@ function getEventTag(eventName: EventType): string {
eventName = eventName.toLowerCase();
if (eventName === 'block' || eventName === 'pending' || eventName === 'error') {
return eventName;
} else if (hexDataLength(eventName) === 32) {
if (hexDataLength(eventName) === 32) {
return 'tx:' + eventName;
}
if (eventName.indexOf(':') === -1) {
return eventName;
}
} else if (Array.isArray(eventName)) {
return 'filter::' + serializeTopics(eventName);
@@ -474,10 +494,21 @@ export class BaseProvider extends Provider {
private _network: Network;
private _events: Array<_Event>;
protected _emitted: any;
// To help mitigate the eventually conssitent nature of the blockchain
// we keep a mapping of events we emit. If we emit an event X, we expect
// that a user should be able to query for that event in the callback,
// if the node returns null, we stall the response until we get back a
// meaningful value, since we may be hitting a re-org, or a node that
// has not indexed the event yet.
// Events:
// - t:{hash} - Transaction hash
// - b:{hash} - BlockHash
// - block - The most recent emitted block
protected _emitted: { [ eventName: string ]: number | 'pending' };
private _pollingInterval: number;
private _poller: any; // @TODO: what does TypeScript thing setInterval returns?
private _poller: any; // @TODO: what does TypeScript think setInterval returns?
private _lastBlockNumber: number;
@@ -510,6 +541,9 @@ export class BaseProvider extends Provider {
return network;
}));
// Squash any "unhandled promise" errors; the don't need to be handled
this.ready.catch((error) => { });
} else {
let knownNetwork = getNetwork((network == null) ? 'homestead': network);
if (knownNetwork) {
@@ -531,11 +565,7 @@ export class BaseProvider extends Provider {
this._pollingInterval = 4000;
// We use this to track recent emitted events; for example, if we emit a "block" of 100
// and we get a `getBlock(100)` request which would result in null, we should retry
// until we get a response. This provides devs with a consistent view. Similarly for
// transaction hashes.
this._emitted = { block: this._lastBlockNumber };
this._emitted = { block: -2 };
this._fastQueryDate = 0;
}
@@ -547,32 +577,56 @@ export class BaseProvider extends Provider {
// If the block hasn't changed, meh.
if (blockNumber === this._lastBlockNumber) { return; }
if (this._lastBlockNumber === -2) { this._lastBlockNumber = blockNumber - 1; }
// First polling cycle, trigger a "block" events
if (this._emitted.block === -2) {
this._emitted.block = blockNumber - 1;
}
// Notify all listener for each block that has passed
for (var i = this._lastBlockNumber + 1; i <= blockNumber; i++) {
if (this._emitted.block < i) {
this._emitted.block = i;
// Evict any transaction hashes or block hashes over 12 blocks
// old, since they should not return null anyways
Object.keys(this._emitted).forEach((key) => {
if (key === 'block') { return; }
if (this._emitted[key] > i + 12) {
delete this._emitted[key];
}
});
}
for (let i = (<number>this._emitted.block) + 1; i <= blockNumber; i++) {
this.emit('block', i);
}
// The emitted block was updated, check for obsolete events
if ((<number>this._emitted.block) !== blockNumber) {
this._emitted.block = blockNumber;
Object.keys(this._emitted).forEach((key) => {
// The block event does not expire
if (key === 'block') { return; }
// The block we were at when we emitted this event
let eventBlockNumber = this._emitted[key];
// We cannot garbage collect pending transactions or blocks here
// They should be garbage collected by the Provider when setting
// "pending" events
if (eventBlockNumber === 'pending') { return; }
// Evict any transaction hashes or block hashes over 12 blocks
// old, since they should not return null anyways
if (blockNumber - eventBlockNumber > 12) {
delete this._emitted[key];
}
});
}
// First polling cycle
if (this._lastBlockNumber === -2) {
this._lastBlockNumber = blockNumber - 1;
}
// Sweep balances and remove addresses we no longer have events for
var newBalances: any = {};
let newBalances: any = {};
// Find all transaction hashes we are waiting on
let uniqueEventTags: { [ tag: string ]: boolean } = { };
this._events.forEach((event) => {
let comps = event.tag.split(':');
uniqueEventTags[event.tag] = true;
});
Object.keys(uniqueEventTags).forEach((tag) => {
let comps = tag.split(':');
switch (comps[0]) {
case 'tx': {
let hash = comps[1];
@@ -582,6 +636,7 @@ export class BaseProvider extends Provider {
this.emit(hash, receipt);
return null;
}).catch((error: Error) => { this.emit('error', error); });
break;
}
@@ -590,25 +645,27 @@ export class BaseProvider extends Provider {
if (this._balances[address]) {
newBalances[address] = this._balances[address];
}
this.getBalance(address, 'latest').then(function(balance) {
var lastBalance = this._balances[address];
this.getBalance(address, 'latest').then((balance) => {
let lastBalance = this._balances[address];
if (lastBalance && balance.eq(lastBalance)) { return; }
this._balances[address] = balance;
this.emit(address, balance);
return null;
}).catch((error: Error) => { this.emit('error', error); });
break;
}
case 'filter': {
let address = comps[1];
let topics = deserializeTopics(comps[2]);
let filter = {
address: address,
address: comps[1],
fromBlock: this._lastBlockNumber + 1,
toBlock: blockNumber,
topics: topics
}
if (!filter.address) { delete filter.address; }
this.getLogs(filter).then((logs) => {
if (logs.length === 0) { return; }
logs.forEach((log: Log) => {
@@ -629,12 +686,13 @@ export class BaseProvider extends Provider {
return null;
}).catch((error: Error) => { });
this.doPoll();
}
resetEventsBlock(blockNumber: number): void {
this._lastBlockNumber = blockNumber;
this._doPoll();
this._lastBlockNumber = blockNumber - 1;
if (this.polling) { this._doPoll(); }
}
get network(): Network {
@@ -646,8 +704,7 @@ export class BaseProvider extends Provider {
}
get blockNumber(): number {
if (this._lastBlockNumber < 0) { return null; }
return this._lastBlockNumber;
return this._fastBlockNumber;
}
get polling(): boolean {
@@ -718,13 +775,22 @@ export class BaseProvider extends Provider {
// this will be used once we move to the WebSocket or other alternatives to polling
waitForTransaction(transactionHash: string, confirmations?: number): Promise<TransactionReceipt> {
if (!confirmations) { confirmations = 1; }
return poll(() => {
return this.getTransactionReceipt(transactionHash).then((receipt) => {
if (receipt == null || receipt.confirmations < confirmations) { return undefined; }
if (confirmations == null) { confirmations = 1; }
return this.getTransactionReceipt(transactionHash).then((receipt) => {
if (confirmations === 0 || (receipt && receipt.confirmations >= confirmations)) {
return receipt;
});
}, { onceBlock: this });
}
return <Promise<TransactionReceipt>>(new Promise((resolve) => {
let handler = (receipt: TransactionReceipt) => {
if (receipt.confirmations < confirmations) { return; }
this.removeListener(transactionHash, handler);
resolve(receipt);
}
this.on(transactionHash, handler);
}));
});
}
getBlockNumber(): Promise<number> {
@@ -751,7 +817,7 @@ export class BaseProvider extends Provider {
return this.ready.then(() => {
return resolveProperties({ addressOrName: addressOrName, blockTag: blockTag }).then(({ addressOrName, blockTag }) => {
return this.resolveName(addressOrName).then((address) => {
var params = { address: address, blockTag: checkBlockTag(blockTag) };
let params = { address: address, blockTag: checkBlockTag(blockTag) };
return this.perform('getBalance', params).then((result) => {
return bigNumberify(result);
});
@@ -764,7 +830,7 @@ export class BaseProvider extends Provider {
return this.ready.then(() => {
return resolveProperties({ addressOrName: addressOrName, blockTag: blockTag }).then(({ addressOrName, blockTag }) => {
return this.resolveName(addressOrName).then((address) => {
var params = { address: address, blockTag: checkBlockTag(blockTag) };
let params = { address: address, blockTag: checkBlockTag(blockTag) };
return this.perform('getTransactionCount', params).then((result) => {
return bigNumberify(result).toNumber();
});
@@ -777,7 +843,7 @@ export class BaseProvider extends Provider {
return this.ready.then(() => {
return resolveProperties({ addressOrName: addressOrName, blockTag: blockTag }).then(({ addressOrName, blockTag }) => {
return this.resolveName(addressOrName).then((address) => {
var params = {address: address, blockTag: checkBlockTag(blockTag)};
let params = {address: address, blockTag: checkBlockTag(blockTag)};
return this.perform('getCode', params).then((result) => {
return hexlify(result);
});
@@ -790,7 +856,7 @@ export class BaseProvider extends Provider {
return this.ready.then(() => {
return resolveProperties({ addressOrName: addressOrName, position: position, blockTag: blockTag }).then(({ addressOrName, position, blockTag }) => {
return this.resolveName(addressOrName).then((address) => {
var params = {
let params = {
address: address,
blockTag: checkBlockTag(blockTag),
position: hexStripZeros(hexlify(position)),
@@ -806,7 +872,7 @@ export class BaseProvider extends Provider {
sendTransaction(signedTransaction: string | Promise<string>): Promise<TransactionResponse> {
return this.ready.then(() => {
return resolveProperties({ signedTransaction: signedTransaction }).then(({ signedTransaction }) => {
var params = { signedTransaction: hexlify(signedTransaction) };
let params = { signedTransaction: hexlify(signedTransaction) };
return this.perform('sendTransaction', params).then((hash) => {
return this._wrapTransaction(parseTransaction(signedTransaction), hash);
}, function (error) {
@@ -831,11 +897,22 @@ export class BaseProvider extends Provider {
errors.throwError('Transaction hash mismatch from Provider.sendTransaction.', errors.UNKNOWN_ERROR, { expectedHash: tx.hash, returnedHash: hash });
}
this._emitted['t:' + tx.hash] = 'pending';
// @TODO: (confirmations? number, timeout? number)
result.wait = (confirmations?: number) => {
// We know this transaction *must* exist (whether it gets mined is
// another story), so setting an emitted value forces us to
// wait even if the node returns null for the receipt
if (confirmations !== 0) {
this._emitted['t:' + tx.hash] = 'pending';
}
return this.waitForTransaction(tx.hash, confirmations).then((receipt) => {
if (receipt == null && confirmations === 0) { return null; }
// No longer pending, allow the polling loop to garbage collect this
this._emitted['t:' + tx.hash] = receipt.blockNumber;
if (receipt.status === 0) {
errors.throwError('transaction failed', errors.CALL_EXCEPTION, {
transactionHash: tx.hash,
@@ -850,12 +927,12 @@ export class BaseProvider extends Provider {
}
call(transaction: TransactionRequest): Promise<string> {
call(transaction: TransactionRequest, blockTag?: BlockTag | Promise<BlockTag>): Promise<string> {
let tx: TransactionRequest = shallowCopy(transaction);
return this.ready.then(() => {
return resolveProperties(tx).then((tx) => {
return resolveProperties({ blockTag: blockTag, tx: tx }).then(({ blockTag, tx }) => {
return this._resolveNames(tx, [ 'to', 'from' ]).then((tx) => {
var params = { transaction: checkTransactionRequest(tx) };
let params = { blockTag: checkBlockTag(blockTag), transaction: checkTransactionRequest(tx) };
return this.perform('call', params).then((result) => {
return hexlify(result);
});
@@ -876,7 +953,7 @@ export class BaseProvider extends Provider {
return this.ready.then(() => {
return resolveProperties(tx).then((tx) => {
return this._resolveNames(tx, [ 'to', 'from' ]).then((tx) => {
var params = { transaction: checkTransactionRequest(tx) };
let params = { transaction: checkTransactionRequest(tx) };
return this.perform('estimateGas', params).then((result) => {
return bigNumberify(result);
});
@@ -889,7 +966,7 @@ export class BaseProvider extends Provider {
return this.ready.then(() => {
return resolveProperties({ blockHashOrBlockTag: blockHashOrBlockTag }).then(({ blockHashOrBlockTag }) => {
try {
var blockHash = hexlify(blockHashOrBlockTag);
let blockHash = hexlify(blockHashOrBlockTag);
if (hexDataLength(blockHash) === 32) {
return poll(() => {
return this.perform('getBlock', { blockHash: blockHash, includeTransactions: !!includeTransactions }).then((block) => {
@@ -917,7 +994,7 @@ export class BaseProvider extends Provider {
return poll(() => {
return this.perform('getBlock', { blockTag: blockTag, includeTransactions: !!includeTransactions }).then((block) => {
if (block == null) {
if (blockNumber > this._emitted.block) {
if (blockNumber <= this._emitted.block) {
return undefined;
}
return null;
@@ -935,7 +1012,7 @@ export class BaseProvider extends Provider {
getTransaction(transactionHash: string): Promise<TransactionResponse> {
return this.ready.then(() => {
return resolveProperties({ transactionHash: transactionHash }).then(({ transactionHash }) => {
let params = { transactionHash: checkHash(transactionHash) };
let params = { transactionHash: checkHash(transactionHash, true) };
return poll(() => {
return this.perform('getTransaction', params).then((result) => {
if (result == null) {
@@ -972,7 +1049,7 @@ export class BaseProvider extends Provider {
getTransactionReceipt(transactionHash: string): Promise<TransactionReceipt> {
return this.ready.then(() => {
return resolveProperties({ transactionHash: transactionHash }).then(({ transactionHash }) => {
let params = { transactionHash: checkHash(transactionHash) };
let params = { transactionHash: checkHash(transactionHash, true) };
return poll(() => {
return this.perform('getTransactionReceipt', params).then((result) => {
if (result == null) {
@@ -1009,11 +1086,11 @@ export class BaseProvider extends Provider {
});
}
getLogs(filter: Filter): Promise<Array<Log>> {
getLogs(filter: Filter | FilterByBlock): Promise<Array<Log>> {
return this.ready.then(() => {
return resolveProperties(filter).then((filter) => {
return this._resolveNames(filter, ['address']).then((filter) => {
var params = { filter: checkFilter(filter) };
let params = { filter: checkFilter(filter) };
return this.perform('getLogs', params).then((result) => {
return arrayOf(checkLog)(result);
});
@@ -1033,9 +1110,9 @@ export class BaseProvider extends Provider {
// @TODO: Could probably use resolveProperties instead?
private _resolveNames(object: any, keys: Array<string>): Promise<{ [key: string]: string }> {
var promises: Array<Promise<void>> = [];
let promises: Array<Promise<void>> = [];
var result: { [key: string ]: string } = shallowCopy(object);
let result: { [key: string ]: string } = shallowCopy(object);
keys.forEach(function(key) {
if (result[key] == null) { return; }
@@ -1062,8 +1139,8 @@ export class BaseProvider extends Provider {
}
// keccak256('resolver(bytes32)')
var data = '0x0178b8bf' + namehash(name).substring(2);
var transaction = { to: network.ensAddress, data: data };
let data = '0x0178b8bf' + namehash(name).substring(2);
let transaction = { to: network.ensAddress, data: data };
return this.call(transaction).then((data) => {
@@ -1088,22 +1165,22 @@ export class BaseProvider extends Provider {
return Promise.resolve(getAddress(name));
} catch (error) { }
var self = this;
let self = this;
var nodeHash = namehash(name);
let nodeHash = namehash(name);
// Get the addr from the resovler
return this._getResolver(name).then(function(resolverAddress) {
// keccak256('addr(bytes32)')
var data = '0x3b3b57de' + nodeHash.substring(2);
var transaction = { to: resolverAddress, data: data };
let data = '0x3b3b57de' + nodeHash.substring(2);
let transaction = { to: resolverAddress, data: data };
return self.call(transaction);
// extract the address from the data
}).then(function(data) {
if (hexDataLength(data) !== 32) { return null; }
var address = getAddress(hexDataSlice(data, 12));
let address = getAddress(hexDataSlice(data, 12));
if (address === '0x0000000000000000000000000000000000000000') { return null; }
return address;
});
@@ -1118,17 +1195,17 @@ export class BaseProvider extends Provider {
address = getAddress(address);
var name = address.substring(2) + '.addr.reverse'
var nodehash = namehash(name);
let name = address.substring(2) + '.addr.reverse'
let nodehash = namehash(name);
var self = this;
let self = this;
return this._getResolver(name).then(function(resolverAddress) {
if (!resolverAddress) { return null; }
// keccak('name(bytes32)')
var data = '0x691f3431' + nodehash.substring(2);
var transaction = { to: resolverAddress, data: data };
let data = '0x691f3431' + nodehash.substring(2);
let transaction = { to: resolverAddress, data: data };
return self.call(transaction);
}).then(function(data) {
@@ -1140,12 +1217,12 @@ export class BaseProvider extends Provider {
data = data.substring(64);
if (data.length < 64) { return null; }
var length = bigNumberify('0x' + data.substring(0, 64)).toNumber();
let length = bigNumberify('0x' + data.substring(0, 64)).toNumber();
data = data.substring(64);
if (2 * length > data.length) { return null; }
var name = toUtf8String('0x' + data.substring(0, 2 * length));
let name = toUtf8String('0x' + data.substring(0, 2 * length));
// Make sure the reverse record matches the foward record
return self.resolveName(name).then(function(addr) {
@@ -1169,7 +1246,7 @@ export class BaseProvider extends Provider {
}
protected _startPending(): void {
console.log('WARNING: this provider does not support pending events');
errors.warn('WARNING: this provider does not support pending events');
}
protected _stopPending(): void {
@@ -1212,6 +1289,8 @@ export class BaseProvider extends Provider {
return !(event.once);
});
if (this.listenerCount() === 0) { this.polling = false; }
return result;
}
@@ -1233,13 +1312,18 @@ export class BaseProvider extends Provider {
});
}
removeAllListeners(eventName: EventType): Provider {
let eventTag = getEventTag(eventName);
this._events = this._events.filter((event) => {
return (event.tag !== eventTag);
});
removeAllListeners(eventName?: EventType): Provider {
if (eventName == null) {
this._events = [ ];
this._stopPending();
} else {
let eventTag = getEventTag(eventName);
this._events = this._events.filter((event) => {
return (event.tag !== eventTag);
});
if (eventName === 'pending') { this._stopPending(); }
}
if (eventName === 'pending') { this._stopPending(); }
if (this._events.length === 0) { this.polling = false; }
return this;
@@ -1250,9 +1334,9 @@ export class BaseProvider extends Provider {
let eventTag = getEventTag(eventName);
this._events = this._events.filter((event) => {
if (event.tag !== eventTag) { return true; }
if (event.tag !== eventTag || event.listener != listener) { return true; }
if (found) { return true; }
found = false;
found = true;
return false;
});
@@ -1261,6 +1345,7 @@ export class BaseProvider extends Provider {
return this;
}
}
defineReadOnly(Provider, 'inherits', inheritable(Provider));

View File

@@ -98,6 +98,9 @@ export class EtherscanProvider extends BaseProvider{
case 'kovan':
baseUrl = 'https://api-kovan.etherscan.io';
break;
case 'goerli':
baseUrl = 'https://api-goerli.etherscan.io';
break;
default:
throw new Error('unsupported network');
}
@@ -108,50 +111,60 @@ export class EtherscanProvider extends BaseProvider{
perform(method: string, params: any) {
//if (!params) { params = {}; }
var url = this.baseUrl;
let url = this.baseUrl;
let apiKey = '';
if (this.apiKey) { apiKey += '&apikey=' + this.apiKey; }
let get = (url: string, procFunc?: (value: any) => any) => {
return fetchJson(url, null, procFunc || getJsonResult).then((result) => {
this.emit('debug', {
action: 'perform',
request: url,
response: result,
provider: this
});
return result;
});
};
switch (method) {
case 'getBlockNumber':
url += '/api?module=proxy&action=eth_blockNumber' + apiKey;
return fetchJson(url, null, getJsonResult);
return get(url);
case 'getGasPrice':
url += '/api?module=proxy&action=eth_gasPrice' + apiKey;
return fetchJson(url, null, getJsonResult);
return get(url);
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);
return get(url, getResult);
case 'getTransactionCount':
url += '/api?module=proxy&action=eth_getTransactionCount&address=' + params.address;
url += '&tag=' + params.blockTag + apiKey;
return fetchJson(url, null, getJsonResult);
return get(url);
case 'getCode':
url += '/api?module=proxy&action=eth_getCode&address=' + params.address;
url += '&tag=' + params.blockTag + apiKey;
return fetchJson(url, null, getJsonResult);
return get(url, getJsonResult);
case 'getStorageAt':
url += '/api?module=proxy&action=eth_getStorageAt&address=' + params.address;
url += '&position=' + params.position;
url += '&tag=' + params.blockTag + apiKey;
return fetchJson(url, null, getJsonResult);
return get(url, getJsonResult);
case 'sendTransaction':
url += '/api?module=proxy&action=eth_sendRawTransaction&hex=' + params.signedTransaction;
url += apiKey;
return fetchJson(url, null, getJsonResult).catch((error) => {
return get(url).catch((error) => {
if (error.responseText) {
// "Insufficient funds. The account you tried to send transaction from does not have enough funds. Required 21464000000000 and got: 0"
if (error.responseText.toLowerCase().indexOf('insufficient funds') >= 0) {
@@ -178,34 +191,40 @@ export class EtherscanProvider extends BaseProvider{
url += '&boolean=false';
}
url += apiKey;
return fetchJson(url, null, getJsonResult);
return get(url);
}
throw new Error('getBlock by blockHash not implmeneted');
case 'getTransaction':
url += '/api?module=proxy&action=eth_getTransactionByHash&txhash=' + params.transactionHash;
url += apiKey;
return fetchJson(url, null, getJsonResult);
return get(url);
case 'getTransactionReceipt':
url += '/api?module=proxy&action=eth_getTransactionReceipt&txhash=' + params.transactionHash;
url += apiKey;
return fetchJson(url, null, getJsonResult);
return get(url);
case 'call':
var transaction = getTransactionString(params.transaction);
case 'call': {
let transaction = getTransactionString(params.transaction);
if (transaction) { transaction = '&' + transaction; }
url += '/api?module=proxy&action=eth_call' + transaction;
//url += '&tag=' + params.blockTag + apiKey;
if (params.blockTag !== 'latest') {
throw new Error('EtherscanProvider does not support blockTag for call');
}
url += apiKey;
return fetchJson(url, null, getJsonResult);
return get(url);
}
case 'estimateGas':
var transaction = getTransactionString(params.transaction);
case 'estimateGas': {
let transaction = getTransactionString(params.transaction);
if (transaction) { transaction = '&' + transaction; }
url += '/api?module=proxy&action=eth_estimateGas&' + transaction;
url += apiKey;
return fetchJson(url, null, getJsonResult);
return get(url);
}
case 'getLogs':
url += '/api?module=logs&action=getLogs';
@@ -218,6 +237,16 @@ export class EtherscanProvider extends BaseProvider{
url += '&toBlock=' + checkLogTag(params.filter.toBlock);
}
if (params.filter.blockHash) {
try {
errors.throwError("Etherscan does not support blockHash filters", errors.UNSUPPORTED_OPERATION, {
operation: "getLogs(blockHash)"
});
} catch (error) {
return Promise.reject(error);
}
}
if (params.filter.address) {
url += '&address=' + params.filter.address;
}
@@ -227,7 +256,7 @@ export class EtherscanProvider extends BaseProvider{
if (params.filter.topics.length > 1) {
throw new Error('unsupported topic format');
}
var topic0 = params.filter.topics[0];
let topic0 = params.filter.topics[0];
if (typeof(topic0) !== 'string' || topic0.length !== 66) {
throw new Error('unsupported topic0 format');
}
@@ -240,7 +269,7 @@ export class EtherscanProvider extends BaseProvider{
url += apiKey;
var self = this;
return fetchJson(url, null, getResult).then(function(logs: Array<any>) {
return get(url, getResult).then(function(logs: Array<any>) {
var txs: { [hash: string]: string } = {};
var seq = Promise.resolve();
@@ -268,7 +297,7 @@ export class EtherscanProvider extends BaseProvider{
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 get(url, getResult).then(function(result) {
return parseFloat(result.ethusd);
});
@@ -297,6 +326,12 @@ export class EtherscanProvider extends BaseProvider{
url += '&sort=asc' + apiKey;
return fetchJson(url, null, getResult).then((result: Array<any>) => {
this.emit('debug', {
action: 'getHistory',
request: url,
response: result,
provider: this
});
var output: Array<TransactionResponse> = [];
result.forEach((tx) => {
['contractAddress', 'to'].forEach(function(key) {

View File

@@ -32,7 +32,8 @@ function checkNetworks(networks: Array<Network>): boolean {
// Matches!
if (check.name === network.name &&
check.chainId === network.chainId &&
check.ensAddress === network.ensAddress) { return; }
((check.ensAddress === network.ensAddress) ||
(check.ensAddress == null && network.ensAddress == null))) { return; }
errors.throwError(
'provider mismatch',

View File

@@ -2,6 +2,7 @@
import { JsonRpcProvider, JsonRpcSigner } from './json-rpc-provider';
import { isHexString } from "../utils/bytes";
import { getNetwork } from '../utils/networks';
import { defineReadOnly } from '../utils/properties';
@@ -10,14 +11,18 @@ import { Networkish } from '../utils/networks';
import * as errors from '../errors';
const defaultProjectId = "7d0d81d0919f4f05b9ab6634be01ee73";
export class InfuraProvider extends JsonRpcProvider {
readonly apiAccessToken: string;
readonly projectId: string;
constructor(network?: Networkish, apiAccessToken?: string) {
network = getNetwork((network == null) ? 'homestead': network);
constructor(network?: Networkish, projectId?: string) {
let standard = getNetwork((network == null) ? 'homestead': network);
if (projectId == null) { projectId = defaultProjectId; }
var host = null;
switch(network.name) {
let host = null;
switch(standard.name) {
case 'homestead':
host = 'mainnet.infura.io';
break;
@@ -27,30 +32,45 @@ export class InfuraProvider extends JsonRpcProvider {
case 'rinkeby':
host = 'rinkeby.infura.io';
break;
case 'goerli':
host = 'goerli.infura.io';
break;
case 'kovan':
host = 'kovan.infura.io';
break;
default:
throw new Error('unsupported network');
errors.throwError('unsupported network', errors.INVALID_ARGUMENT, {
argument: "network",
value: network
});
}
super('https://' + host + '/' + (apiAccessToken || ''), network);
errors.checkNew(this, InfuraProvider);
// New-style Project ID
if (isHexString("0x" + projectId, 16)) {
super('https://' + host + '/v3/' + projectId, standard);
defineReadOnly(this, 'apiAccessToken', null);
defineReadOnly(this, 'projectId', projectId);
defineReadOnly(this, 'apiAccessToken', apiAccessToken || null);
// Legacy API Access Token
} else {
super('https://' + host + '/' + projectId, standard);
defineReadOnly(this, 'apiAccessToken', projectId);
defineReadOnly(this, 'projectId', null);
}
errors.checkNew(this, InfuraProvider);
}
protected _startPending(): void {
console.log('WARNING: INFURA does not support pending filters');
errors.warn('WARNING: INFURA does not support pending filters');
}
getSigner(address?: string): JsonRpcSigner {
errors.throwError(
return errors.throwError(
'INFURA does not support signing',
errors.UNSUPPORTED_OPERATION,
{ operation: 'getSigner' }
);
return null;
}
listAccounts(): Promise<Array<string>> {

View File

@@ -1,3 +1,4 @@
"use strict";
import net from 'net';
@@ -10,12 +11,15 @@ 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' });
errors.throwError('missing path', errors.MISSING_ARGUMENT, {
argument: 'path'
});
}
super('ipc://' + path, network);
@@ -32,7 +36,7 @@ export class IpcProvider extends JsonRpcProvider {
// advantage we are aiming for now is security. This simplifies
// multiplexing requests (since we do not need to multiplex).
var payload = JSON.stringify({
let payload = JSON.stringify({
method: method,
params: params,
id: 42,
@@ -40,10 +44,17 @@ export class IpcProvider extends JsonRpcProvider {
});
return new Promise((resolve, reject) => {
var stream = net.connect(this.path);
stream.on('data', function(data) {
let response = Buffer.alloc(0);
let stream = net.connect(this.path);
stream.on('data', (data) => {
response = Buffer.concat([ response, data ]);
});
stream.on("end", () => {
try {
resolve(JSON.parse(data.toString('utf8')).result);
resolve(JSON.parse(response.toString('utf8')).result);
// @TODO: Better pull apart the error
stream.destroy();
} catch (error) {
@@ -52,14 +63,11 @@ export class IpcProvider extends JsonRpcProvider {
}
});
stream.on('end', function() {
stream.destroy();
});
stream.on('error', function(error) {
stream.on('error', (error) => {
reject(error);
stream.destroy();
});
stream.write(payload);
stream.end();
});

View File

@@ -6,11 +6,13 @@ import { BaseProvider } from './base-provider';
import { Signer } from '../abstract-signer';
import * as errors from '../errors';
import { getAddress } from '../utils/address';
import { BigNumber } from '../utils/bignumber';
import { hexlify, hexStripZeros } from '../utils/bytes';
import { getNetwork } from '../utils/networks';
import { defineReadOnly, resolveProperties, shallowCopy } from '../utils/properties';
import { checkProperties, defineReadOnly, resolveProperties, shallowCopy } from '../utils/properties';
import { toUtf8Bytes } from '../utils/utf8';
import { fetchJson, poll } from '../utils/web';
@@ -18,9 +20,8 @@ import { fetchJson, poll } from '../utils/web';
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';
import { BlockTag, TransactionRequest, TransactionResponse } from '../providers/abstract-provider';
function timer(timeout: number): Promise<any> {
return new Promise(function(resolve) {
@@ -33,7 +34,7 @@ function timer(timeout: number): Promise<any> {
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);
let error: any = new Error(payload.error.message);
error.code = payload.error.code;
error.data = payload.error.data;
throw error;
@@ -49,6 +50,11 @@ function getLowerCase(value: string): string {
const _constructorGuard = {};
// Some environments (Trust Wallet and company) use a global map
// to track JSON-RPC ID, so we try to keep IDs unique across all
// connections. See #489.
let _nextId = 42;
export class JsonRpcSigner extends Signer {
readonly provider: JsonRpcProvider;
private _index: number;
@@ -78,15 +84,6 @@ export class JsonRpcSigner extends Signer {
}
}
/* 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);
@@ -109,31 +106,32 @@ export class JsonRpcSigner extends Signer {
return this.provider.getTransactionCount(this.getAddress(), blockTag);
}
sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse> {
let tx: TransactionRequest = shallowCopy(transaction);
sendUncheckedTransaction(transaction: TransactionRequest): Promise<string> {
transaction = shallowCopy(transaction);
if (tx.from == null) {
tx.from = this.getAddress().then((address) => {
if (!address) { return null; }
return address.toLowerCase();
});
}
let fromAddress = this.getAddress().then((address) => {
if (address) { address = address.toLowerCase(); }
return address;
});
// The JSON-RPC for eth_sendTransaction uses 90000 gas; if the user
// wishes to use this, it is easy to specify explicitly, otherwise
// we look it up for them.
if (transaction.gasLimit == null) {
tx.gasLimit = this.provider.estimateGas(tx);
let estimate = shallowCopy(transaction);
estimate.from = fromAddress;
transaction.gasLimit = this.provider.estimateGas(estimate);
}
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;
});
return Promise.all([
resolveProperties(transaction),
fromAddress
]).then((results) => {
let tx = results[0];
let hexTx = JsonRpcProvider.hexlifyTransaction(tx);
hexTx.from = results[1];
return this.provider.send('eth_sendTransaction', [ hexTx ]).then((hash) => {
return hash;
}, (error) => {
if (error.responseText) {
// See: JsonRpcProvider.sendTransaction (@TODO: Expose a ._throwError??)
@@ -158,8 +156,22 @@ export class JsonRpcSigner extends Signer {
});
}
sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse> {
return this.sendUncheckedTransaction(transaction).then((hash) => {
return poll(() => {
return this.provider.getTransaction(hash).then((tx: TransactionResponse) => {
if (tx === null) { return undefined; }
return this.provider._wrapTransaction(tx, hash);
});
}, { fastRetry: 250, onceBlock: this.provider }).catch((error: Error) => {
(<any>error).transactionHash = hash;
throw error;
});
});
}
signMessage(message: Arrayish | string): Promise<string> {
var data = ((typeof(message) === 'string') ? toUtf8Bytes(message): message);
let data = ((typeof(message) === 'string') ? toUtf8Bytes(message): message);
return this.getAddress().then((address) => {
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign
@@ -168,7 +180,7 @@ export class JsonRpcSigner extends Signer {
}
unlock(password: string): Promise<boolean> {
var provider = this.provider;
let provider = this.provider;
return this.getAddress().then(function(address) {
return provider.send('personal_unlockAccount', [ address.toLowerCase(), password, null ]);
@@ -176,6 +188,10 @@ export class JsonRpcSigner extends Signer {
}
}
const allowedTransactionKeys: { [ key: string ]: boolean } = {
chainId: true, data: true, gasLimit: true, gasPrice:true, nonce: true, to: true, value: true
}
export class JsonRpcProvider extends BaseProvider {
readonly connection: ConnectionInfo;
@@ -236,14 +252,22 @@ export class JsonRpcProvider extends BaseProvider {
}
send(method: string, params: any): Promise<any> {
var request = {
let request = {
method: method,
params: params,
id: 42,
id: (_nextId++),
jsonrpc: "2.0"
};
return fetchJson(this.connection, JSON.stringify(request), getResult);
return fetchJson(this.connection, JSON.stringify(request), getResult).then((result) => {
this.emit('debug', {
action: 'send',
request: request,
response: result,
provider: this
});
return result;
});
}
perform(method: string, params: any): Promise<any> {
@@ -300,10 +324,10 @@ export class JsonRpcProvider extends BaseProvider {
return this.send('eth_getTransactionReceipt', [ params.transactionHash ]);
case 'call':
return this.send('eth_call', [ JsonRpcProvider.hexlifyTransaction(params.transaction), 'latest' ]);
return this.send('eth_call', [ JsonRpcProvider.hexlifyTransaction(params.transaction, { from: true }), params.blockTag ]);
case 'estimateGas':
return this.send('eth_estimateGas', [ JsonRpcProvider.hexlifyTransaction(params.transaction) ]);
return this.send('eth_estimateGas', [ JsonRpcProvider.hexlifyTransaction(params.transaction, { from: true }) ]);
case 'getLogs':
if (params.filter && params.filter.address != null) {
@@ -321,9 +345,9 @@ export class JsonRpcProvider extends BaseProvider {
protected _startPending(): void {
if (this._pendingFilter != null) { return; }
var self = this;
let self = this;
var pendingFilter: Promise<number> = this.send('eth_newPendingTransactionFilter', []);
let pendingFilter: Promise<number> = this.send('eth_newPendingTransactionFilter', []);
this._pendingFilter = pendingFilter;
pendingFilter.then(function(filterId) {
@@ -331,8 +355,9 @@ export class JsonRpcProvider extends BaseProvider {
self.send('eth_getFilterChanges', [ filterId ]).then(function(hashes: Array<string>) {
if (self._pendingFilter != pendingFilter) { return null; }
var seq = Promise.resolve();
let seq = Promise.resolve();
hashes.forEach(function(hash) {
// @TODO: This should be garbage collected at some point... How? When?
self._emitted['t:' + hash.toLowerCase()] = 'pending';
seq = seq.then(function() {
return self.getTransaction(hash).then(function(tx) {
@@ -369,11 +394,22 @@ export class JsonRpcProvider extends BaseProvider {
// - 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 = {};
// NOTE: This allows a TransactionRequest, but all values should be resolved
// before this is called
static hexlifyTransaction(transaction: TransactionRequest, allowExtra?: { [key: string]: boolean }): { [key: string]: string } {
// Some nodes (INFURA ropsten; INFURA mainnet is fine) don't like extra zeros.
// Check only allowed properties are given
let allowed = shallowCopy(allowedTransactionKeys);
if (allowExtra) {
for (let key in allowExtra) {
if (allowExtra[key]) { allowed[key] = true; }
}
}
checkProperties(transaction, allowed);
let result: { [key: string]: string } = {};
// Some nodes (INFURA ropsten; INFURA mainnet is fine) don't like leading zeros.
['gasLimit', 'gasPrice', 'nonce', 'value'].forEach(function(key) {
if ((<any>transaction)[key] == null) { return; }
let value = hexStripZeros(hexlify((<any>transaction)[key]));
@@ -384,7 +420,7 @@ export class JsonRpcProvider extends BaseProvider {
['from', 'to', 'data'].forEach(function(key) {
if ((<any>transaction)[key] == null) { return; }
result[key] = hexlify((<any>transaction)[key]);
});
});
return result;
}

View File

@@ -107,9 +107,12 @@ type ParseNode = {
function parseParam(param: string, allowIndexed?: boolean): ParamType {
let originalParam = param;
function throwError(i: number) {
throw new Error('unexpected character "' + param[i] + '" at position ' + i + ' in "' + param + '"');
throw new Error('unexpected character "' + originalParam[i] + '" at position ' + i + ' in "' + originalParam + '"');
}
param = param.replace(/\s/g, ' ');
var parent: ParseNode = { type: '', name: '', state: { allowType: true } };
var node = parent;
@@ -263,7 +266,7 @@ function parseSignatureEvent(fragment: string): EventFragment {
case '':
break;
default:
console.log('unknown modifier: ' + modifier);
errors.info('unknown modifier: ' + modifier);
}
});
@@ -333,7 +336,7 @@ function parseSignatureFunction(fragment: string): FunctionFragment {
case '':
break;
default:
console.log('unknown modifier: ' + modifier);
errors.info('unknown modifier: ' + modifier);
}
});
@@ -380,6 +383,7 @@ export function formatSignature(fragment: EventFragment | FunctionFragment): str
export function parseSignature(fragment: string): EventFragment | FunctionFragment {
if(typeof(fragment) === 'string') {
// Make sure the "returns" is surrounded by a space and all whitespace is exactly one space
fragment = fragment.replace(/\s/g, ' ');
fragment = fragment.replace(/\(/g, ' (').replace(/\)/g, ') ').replace(/\s+/g, ' ');
fragment = fragment.trim();
@@ -844,7 +848,7 @@ class CoderArray extends Coder {
result = uint256Coder.encode(count);
}
errors.checkArgumentCount(count, value.length, 'in coder array' + (this.localName? (" "+ this.localName): ""));
errors.checkArgumentCount(count, value.length, ' in coder array' + (this.localName? (" "+ this.localName): ""));
var coders = [];
for (var i = 0; i < value.length; i++) { coders.push(this.coder); }

143
src.ts/utils/basex.ts Normal file
View File

@@ -0,0 +1,143 @@
/**
* var basex = require('base-x');
*
* This implementation is heavily based on base-x. The main reason to
* deviate was to prevent the dependency of Buffer.
*
* Contributors:
*
* base-x encoding
* Forked from https://github.com/cryptocoinjs/bs58
* Originally written by Mike Hearn for BitcoinJ
* Copyright (c) 2011 Google Inc
* Ported to JavaScript by Stefan Thomas
* Merged Buffer refactorings from base58-native by Stephen Pair
* Copyright (c) 2013 BitPay Inc
*
* The MIT License (MIT)
*
* Copyright base-x contributors (c) 2016
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
import { arrayify, Arrayish } from "./bytes";
import { defineReadOnly } from "./properties";
export class BaseX {
readonly alphabet: string;
readonly base: number;
private _alphabetMap: { [ character: string ]: number };
private _leader: string;
constructor(alphabet: string) {
defineReadOnly(this, "alphabet", alphabet);
defineReadOnly(this, "base", alphabet.length);
defineReadOnly(this, "_alphabetMap", { });
defineReadOnly(this, "_leader", alphabet.charAt(0));
// pre-compute lookup table
for (let i = 0; i < alphabet.length; i++) {
this._alphabetMap[alphabet.charAt(i)] = i;
}
}
encode(value: Arrayish | string): string {
let source = arrayify(value);
if (source.length === 0) { return ''; }
let digits = [ 0 ]
for (let i = 0; i < source.length; ++i) {
let carry = source[i];
for (let j = 0; j < digits.length; ++j) {
carry += digits[j] << 8;
digits[j] = carry % this.base;
carry = (carry / this.base) | 0;
}
while (carry > 0) {
digits.push(carry % this.base);
carry = (carry / this.base) | 0;
}
}
let string = ''
// deal with leading zeros
for (let k = 0; source[k] === 0 && k < source.length - 1; ++k) {
string += this._leader;
}
// convert digits to a string
for (let q = digits.length - 1; q >= 0; --q) {
string += this.alphabet[digits[q]];
}
return string;
}
decode(value: string): Uint8Array {
if (typeof(value) !== 'string') {
throw new TypeError('Expected String');
}
let bytes: Array<number> = [];
if (value.length === 0) { return new Uint8Array(bytes); }
bytes.push(0);
for (let i = 0; i < value.length; i++) {
let byte = this._alphabetMap[value[i]];
if (byte === undefined) {
throw new Error('Non-base' + this.base + ' character');
}
let carry = byte;
for (let j = 0; j < bytes.length; ++j) {
carry += bytes[j] * this.base;
bytes[j] = carry & 0xff;
carry >>= 8;
}
while (carry > 0) {
bytes.push(carry & 0xff);
carry >>= 8;
}
}
// deal with leading zeros
for (let k = 0; value[k] === this._leader && k < value.length - 1; ++k) {
bytes.push(0)
}
return arrayify(new Uint8Array(bytes.reverse()))
}
}
const Base32 = new BaseX("abcdefghijklmnopqrstuvwxyz234567");
const Base58 = new BaseX("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz");
export { Base32, Base58 };
//console.log(Base58.decode("Qmd2V777o5XvJbYMeMb8k2nU5f8d3ciUQ5YpYuWhzv8iDj"))
//console.log(Base58.encode(Base58.decode("Qmd2V777o5XvJbYMeMb8k2nU5f8d3ciUQ5YpYuWhzv8iDj")))

View File

@@ -47,7 +47,6 @@ function _bnify(value: BigNumber): BN.BN {
return new BN.BN(hex.substring(2), 16);
}
export type BigNumberish = BigNumber | string | number | Arrayish;
export class BigNumber implements Hexable {
@@ -108,6 +107,13 @@ export class BigNumber implements Hexable {
return toBigNumber(_bnify(this).toTwos(value));
}
abs(): BigNumber {
if (this._hex[0] === '-') {
return toBigNumber(_bnify(this).mul(BN_1));
}
return this;
}
add(other: BigNumberish): BigNumber {
return toBigNumber(_bnify(this).add(toBN(other)));
}

View File

@@ -40,7 +40,7 @@ function addSlice(array: Uint8Array): Uint8Array {
array.slice = function() {
var args = Array.prototype.slice.call(arguments);
return new Uint8Array(Array.prototype.slice.apply(array, args));
return addSlice(new Uint8Array(Array.prototype.slice.apply(array, args)));
}
return array;
@@ -168,6 +168,15 @@ export function hexlify(value: Arrayish | Hexable | number): string {
errors.throwError('cannot hexlify negative value', errors.INVALID_ARGUMENT, { arg: 'value', value: value });
}
// @TODO: Roll this into the above error as a numeric fault (overflow); next version, not backward compatible
// We can about (value == MAX_INT) to as well, since that may indicate we underflowed already
if (value >= 9007199254740991) {
errors.throwError("out-of-range", errors.NUMERIC_FAULT, {
operartion: "hexlify",
fault: "out-of-safe-range"
});
}
var hex = '';
while (value) {
hex = HexCharacters[value & 0x0f] + hex;

View File

@@ -1,5 +1,7 @@
'use strict';
import * as errors from '../errors';
import { concat, hexlify } from './bytes';
import { toUtf8Bytes } from './utf8';
import { keccak256 } from './keccak256';
@@ -11,11 +13,18 @@ import { Arrayish } from './bytes';
///////////////////////////////
var Zeros = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
var Partition = new RegExp("^((.*)\\.)?([^.]+)$");
var UseSTD3ASCIIRules = new RegExp("^[a-z0-9.-]*$");
const Zeros = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
const Partition = new RegExp("^((.*)\\.)?([^.]+)$");
const UseSTD3ASCIIRules = new RegExp("^[a-z0-9.-]*$");
export function namehash(name: string): string {
if (typeof(name) !== 'string') {
errors.throwError('invalid address - ' + String(name), errors.INVALID_ARGUMENT, {
argument: 'name',
value: name
});
}
name = name.toLowerCase();
// Supporting the full UTF-8 space requires additional (and large)
@@ -23,13 +32,16 @@ export function namehash(name: string): string {
// It should be fairly easy in the future to support systems with
// String.normalize, but that is future work.
if (!name.match(UseSTD3ASCIIRules)) {
throw new Error('contains invalid UseSTD3ASCIIRules characters');
errors.throwError('contains invalid UseSTD3ASCIIRules characters', errors.INVALID_ARGUMENT, {
argument: 'name',
value: name
});
}
var result: string | Uint8Array = Zeros;
let result: string | Uint8Array = Zeros;
while (name.length) {
var partition = name.match(Partition);
var label = toUtf8Bytes(partition[3]);
let partition = name.match(Partition);
let label = toUtf8Bytes(partition[3]);
result = keccak256(concat([result, keccak256(label)]));
name = partition[2] || '';
@@ -44,11 +56,10 @@ export function id(text: string): string {
}
export function hashMessage(message: Arrayish | string): string {
var payload = concat([
return keccak256(concat([
toUtf8Bytes('\x19Ethereum Signed Message:\n'),
toUtf8Bytes(String(message.length)),
((typeof(message) === 'string') ? toUtf8Bytes(message): message)
]);
return keccak256(payload);
]));
}

View File

@@ -1,4 +1,4 @@
'use strict';
"use strict";
// See: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
// See: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki
@@ -13,14 +13,15 @@ import { langEn } from '../wordlists/lang-en';
//import { register } from '../wordlists/wordlist';
//register(langEn);
import { arrayify, hexlify } from './bytes';
import { bigNumberify } from './bignumber';
import { Base58 } from "./basex";
import { arrayify, concat, hexDataSlice, hexZeroPad, hexlify } from './bytes';
import { BigNumber, bigNumberify } from './bignumber';
import { toUtf8Bytes, UnicodeNormalizationForm } from './utf8';
import { pbkdf2 } from './pbkdf2';
import { computeHmac, SupportedAlgorithms } from './hmac';
import { defineReadOnly, isType, setType } from './properties';
import { KeyPair } from './secp256k1';
import { sha256 } from './sha2';
import { computeAddress, KeyPair } from './secp256k1';
import { ripemd160, sha256 } from './sha2';
const N = bigNumberify("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141");
@@ -30,9 +31,9 @@ import { Wordlist } from './wordlist';
// "Bitcoin seed"
var MasterSecret = toUtf8Bytes('Bitcoin seed');
const MasterSecret = toUtf8Bytes('Bitcoin seed');
var HardenedBit = 0x80000000;
const HardenedBit = 0x80000000;
// Returns a byte with the MSB bits set
function getUpperMask(bits: number): number {
@@ -44,16 +45,28 @@ function getLowerMask(bits: number): number {
return (1 << bits) - 1;
}
function bytes32(value: Arrayish | BigNumber | number): string {
return hexZeroPad(hexlify(value), 32);
}
function base58check(data: Uint8Array): string {
let checksum = hexDataSlice(sha256(sha256(data)), 0, 4);
return Base58.encode(concat([ data, checksum ]));
}
const _constructorGuard: any = {};
export const defaultPath = "m/44'/60'/0'/0/0";
export class HDNode {
private readonly keyPair: KeyPair;
readonly privateKey: string;
readonly publicKey: string;
readonly fingerprint: string;
readonly parentFingerprint: string;
readonly address: string;
readonly mnemonic: string;
readonly path: string;
@@ -69,19 +82,28 @@ export class HDNode {
* - fromMnemonic
* - fromSeed
*/
constructor(constructorGuard: any, privateKey: Arrayish, chainCode: Uint8Array, index: number, depth: number, mnemonic: string, path: string) {
constructor(constructorGuard: any, privateKey: string, publicKey: string, parentFingerprint: string, chainCode: string, index: number, depth: number, mnemonic: string, path: string) {
errors.checkNew(this, HDNode);
if (constructorGuard !== _constructorGuard) {
throw new Error('HDNode constructor cannot be called directly');
}
defineReadOnly(this, 'keyPair', new KeyPair(privateKey));
if (privateKey) {
let keyPair = new KeyPair(privateKey);
defineReadOnly(this, 'privateKey', keyPair.privateKey);
defineReadOnly(this, 'publicKey', keyPair.compressedPublicKey);
} else {
defineReadOnly(this, 'privateKey', null);
defineReadOnly(this, 'publicKey', hexlify(publicKey));
}
defineReadOnly(this, 'privateKey', this.keyPair.privateKey);
defineReadOnly(this, 'publicKey', this.keyPair.compressedPublicKey);
defineReadOnly(this, 'parentFingerprint', parentFingerprint);
defineReadOnly(this, 'fingerprint', hexDataSlice(ripemd160(sha256(this.publicKey)), 0, 4));
defineReadOnly(this, 'chainCode', hexlify(chainCode));
defineReadOnly(this, 'address', computeAddress(this.publicKey));
defineReadOnly(this, 'chainCode', chainCode);
defineReadOnly(this, 'index', index);
defineReadOnly(this, 'depth', depth);
@@ -92,22 +114,43 @@ export class HDNode {
setType(this, 'HDNode');
}
get extendedKey(): string {
// We only support the mainnet values for now, but if anyone needs
// testnet values, let me know. I believe current senitment is that
// we should always use mainnet, and use BIP-44 to derive the network
// - Mainnet: public=0x0488B21E, private=0x0488ADE4
// - Testnet: public=0x043587CF, private=0x04358394
if (this.depth >= 256) { throw new Error("Depth too large!"); }
return base58check(concat([
((this.privateKey != null) ? "0x0488ADE4": "0x0488B21E"),
hexlify(this.depth),
this.parentFingerprint,
hexZeroPad(hexlify(this.index), 4),
this.chainCode,
((this.privateKey != null) ? concat([ "0x00", this.privateKey ]): this.publicKey),
]));
}
neuter(): HDNode {
return new HDNode(_constructorGuard, null, this.publicKey, this.parentFingerprint, this.chainCode, this.index, this.depth, null, this.path);
}
private _derive(index: number): HDNode {
// Public parent key -> public child key
if (!this.privateKey) {
if (index >= HardenedBit) { throw new Error('cannot derive child of neutered node'); }
throw new Error('not implemented');
}
var data = new Uint8Array(37);
if (index > 0xffffffff) { throw new Error("invalid index - " + String(index)); }
// Base path
var mnemonic = this.mnemonic;
var path = this.path;
if (path) { path += '/' + index; }
let path = this.path;
if (path) { path += '/' + (index & ~HardenedBit); }
let data = new Uint8Array(37);
if (index & HardenedBit) {
if (!this.privateKey) {
throw new Error('cannot derive child of neutered node');
}
// Data = 0x00 || ser_256(k_par)
data.set(arrayify(this.privateKey), 1);
@@ -116,39 +159,50 @@ export class HDNode {
} else {
// Data = ser_p(point(k_par))
data.set(this.keyPair.publicKeyBytes);
data.set(arrayify(this.publicKey));
}
// Data += ser_32(i)
for (var i = 24; i >= 0; i -= 8) { data[33 + (i >> 3)] = ((index >> (24 - i)) & 0xff); }
for (let i = 24; i >= 0; i -= 8) { data[33 + (i >> 3)] = ((index >> (24 - i)) & 0xff); }
var I = computeHmac(SupportedAlgorithms.sha512, this.chainCode, data);
var IL = bigNumberify(I.slice(0, 32));
var IR = I.slice(32);
let I = computeHmac(SupportedAlgorithms.sha512, this.chainCode, data);
let IL = I.slice(0, 32);
let IR = I.slice(32);
var ki = IL.add(this.keyPair.privateKey).mod(N);
// The private key
return new HDNode(_constructorGuard, arrayify(ki), IR, index, this.depth + 1, mnemonic, path);
let ki: string = null
// The public key
let Ki: string = null;
if (this.privateKey) {
ki = bytes32(bigNumberify(IL).add(this.privateKey).mod(N));
} else {
let ek = new KeyPair(hexlify(IL));
Ki = ek._addPoint(this.publicKey);
}
return new HDNode(_constructorGuard, ki, Ki, this.fingerprint, bytes32(IR), index, this.depth + 1, this.mnemonic, path);
}
derivePath(path: string): HDNode {
var components = path.split('/');
let components = path.split('/');
if (components.length === 0 || (components[0] === 'm' && this.depth !== 0)) {
throw new Error('invalid path');
throw new Error('invalid path - ' + path);
}
if (components[0] === 'm') { components.shift(); }
var result: HDNode = this;
for (var i = 0; i < components.length; i++) {
var component = components[i];
let result: HDNode = this;
for (let i = 0; i < components.length; i++) {
let component = components[i];
if (component.match(/^[0-9]+'$/)) {
var index = parseInt(component.substring(0, component.length - 1));
let index = parseInt(component.substring(0, component.length - 1));
if (index >= HardenedBit) { throw new Error('invalid path index - ' + component); }
result = result._derive(HardenedBit + index);
} else if (component.match(/^[0-9]+$/)) {
var index = parseInt(component);
let index = parseInt(component);
if (index >= HardenedBit) { throw new Error('invalid path index - ' + component); }
result = result._derive(index);
} else {
@@ -164,22 +218,53 @@ export class HDNode {
}
}
export function fromExtendedKey(extendedKey: string): HDNode {
let bytes = Base58.decode(extendedKey);
if (bytes.length !== 82 || base58check(bytes.slice(0, 78)) !== extendedKey) {
errors.throwError("invalid extended key", errors.INVALID_ARGUMENT, {
argument: "extendedKey",
value: "[REDACTED]"
});
}
let depth = bytes[4];
let parentFingerprint = hexlify(bytes.slice(5, 9));
let index = parseInt(hexlify(bytes.slice(9, 13)).substring(2), 16);
let chainCode = hexlify(bytes.slice(13, 45));
let key = bytes.slice(45, 78);
switch (hexlify(bytes.slice(0, 4))) {
// Public Key
case "0x0488b21e": case "0x043587cf":
return new HDNode(_constructorGuard, null, hexlify(key), parentFingerprint, chainCode, index, depth, null, null);
// Private Key
case "0x0488ade4": case "0x04358394 ":
if (key[0] !== 0) { break; }
return new HDNode(_constructorGuard, hexlify(key.slice(1)), null, parentFingerprint, chainCode, index, depth, null, null);
}
return errors.throwError("invalid extended key", errors.INVALID_ARGUMENT, {
argument: "extendedKey",
value: "[REDACTED]"
});
}
function _fromSeed(seed: Arrayish, mnemonic: string): HDNode {
let seedArray: Uint8Array = arrayify(seed);
if (seedArray.length < 16 || seedArray.length > 64) { throw new Error('invalid seed'); }
var I: Uint8Array = arrayify(computeHmac(SupportedAlgorithms.sha512, MasterSecret, seedArray));
let I: Uint8Array = arrayify(computeHmac(SupportedAlgorithms.sha512, MasterSecret, seedArray));
return new HDNode(_constructorGuard, I.slice(0, 32), I.slice(32), 0, 0, mnemonic, 'm');
return new HDNode(_constructorGuard, bytes32(I.slice(0, 32)), null, "0x00000000", bytes32(I.slice(32)), 0, 0, mnemonic, 'm');
}
export function fromMnemonic(mnemonic: string, wordlist?: Wordlist): HDNode {
export function fromMnemonic(mnemonic: string, wordlist?: Wordlist, password?: string): HDNode {
// Check that the checksum s valid (will throw an error)
mnemonicToEntropy(mnemonic, wordlist);
return _fromSeed(mnemonicToSeed(mnemonic), mnemonic);
return _fromSeed(mnemonicToSeed(mnemonic, password), mnemonic);
}
export function fromSeed(seed: Arrayish): HDNode {
@@ -189,7 +274,7 @@ export function fromSeed(seed: Arrayish): HDNode {
export function mnemonicToSeed(mnemonic: string, password?: string): string {
if (!password) { password = ''; }
var salt = toUtf8Bytes('mnemonic' + password, UnicodeNormalizationForm.NFKD);
let salt = toUtf8Bytes('mnemonic' + password, UnicodeNormalizationForm.NFKD);
return hexlify(pbkdf2(toUtf8Bytes(mnemonic, UnicodeNormalizationForm.NFKD), salt, 2048, 64, 'sha512'));
}
@@ -197,17 +282,19 @@ export function mnemonicToSeed(mnemonic: string, password?: string): string {
export function mnemonicToEntropy(mnemonic: string, wordlist?: Wordlist): string {
if (!wordlist) { wordlist = langEn; }
var words = wordlist.split(mnemonic);
errors.checkNormalize();
let words = wordlist.split(mnemonic);
if ((words.length % 3) !== 0) { throw new Error('invalid mnemonic'); }
var entropy = arrayify(new Uint8Array(Math.ceil(11 * words.length / 8)));
let entropy = arrayify(new Uint8Array(Math.ceil(11 * words.length / 8)));
var offset = 0;
for (var i = 0; i < words.length; i++) {
var index = wordlist.getWordIndex(words[i].normalize('NFKD'));
let offset = 0;
for (let i = 0; i < words.length; i++) {
let index = wordlist.getWordIndex(words[i].normalize('NFKD'));
if (index === -1) { throw new Error('invalid mnemonic'); }
for (var bit = 0; bit < 11; bit++) {
for (let bit = 0; bit < 11; bit++) {
if (index & (1 << (10 - bit))) {
entropy[offset >> 3] |= (1 << (7 - (offset % 8)));
}
@@ -215,12 +302,12 @@ export function mnemonicToEntropy(mnemonic: string, wordlist?: Wordlist): string
}
}
var entropyBits = 32 * words.length / 3;
let entropyBits = 32 * words.length / 3;
var checksumBits = words.length / 3;
var checksumMask = getUpperMask(checksumBits);
let checksumBits = words.length / 3;
let checksumMask = getUpperMask(checksumBits);
var checksum = arrayify(sha256(entropy.slice(0, entropyBits / 8)))[0];
let checksum = arrayify(sha256(entropy.slice(0, entropyBits / 8)))[0];
checksum &= checksumMask;
if (checksum !== (entropy[entropy.length - 1] & checksumMask)) {
@@ -237,10 +324,10 @@ export function entropyToMnemonic(entropy: Arrayish, wordlist?: Wordlist): strin
throw new Error('invalid entropy');
}
var indices: Array<number> = [ 0 ];
let indices: Array<number> = [ 0 ];
var remainingBits = 11;
for (var i = 0; i < entropy.length; i++) {
let remainingBits = 11;
for (let i = 0; i < entropy.length; i++) {
// Consume the whole byte (with still more to go)
if (remainingBits > 8) {
@@ -262,8 +349,8 @@ export function entropyToMnemonic(entropy: Arrayish, wordlist?: Wordlist): strin
}
// Compute the checksum bits
var checksum = arrayify(sha256(entropy))[0];
var checksumBits = entropy.length / 4;
let checksum = arrayify(sha256(entropy))[0];
let checksumBits = entropy.length / 4;
checksum &= getUpperMask(checksumBits);
// Shift the checksum into the word indices

View File

@@ -4,7 +4,7 @@ import { AbiCoder, defaultAbiCoder, formatSignature, formatParamType, parseSigna
import { getAddress, getContractAddress, getIcapAddress } from './address';
import * as base64 from './base64';
import { BigNumber, bigNumberify } from './bignumber';
import { arrayify, concat, hexDataSlice, hexDataLength, hexlify, hexStripZeros, hexZeroPad, joinSignature, padZeros, splitSignature, stripZeros } from './bytes';
import { arrayify, concat, hexDataSlice, hexDataLength, hexlify, hexStripZeros, hexZeroPad, isHexString, joinSignature, padZeros, splitSignature, stripZeros } from './bytes';
import { hashMessage, id, namehash } from './hash';
import * as HDNode from './hdnode';
import { Interface } from './interface';
@@ -14,10 +14,11 @@ import { sha256 } from './sha2';
import { keccak256 as solidityKeccak256, pack as solidityPack, sha256 as soliditySha256 } from './solidity';
import { randomBytes } from './random-bytes';
import { getNetwork } from './networks';
import { deepCopy, defineReadOnly, resolveProperties, shallowCopy } from './properties';
import { checkProperties, deepCopy, defineReadOnly, resolveProperties, shallowCopy } from './properties';
import * as RLP from './rlp';
import { computeAddress, computePublicKey, recoverAddress, recoverPublicKey, verifyMessage } from './secp256k1';
import { SigningKey } from './signing-key';
import { populateTransaction } from './transaction';
import { parse as parseTransaction, serialize as serializeTransaction } from './transaction';
import { formatBytes32String, parseBytes32String, toUtf8Bytes, toUtf8String } from './utf8';
import { commify, formatEther, parseEther, formatUnits, parseUnits } from './units';
@@ -60,6 +61,7 @@ export {
fetchJson,
getNetwork,
checkProperties,
deepCopy,
defineReadOnly,
resolveProperties,
@@ -82,6 +84,7 @@ export {
bigNumberify,
hexlify,
isHexString,
hexStripZeros,
hexZeroPad,
hexDataLength,
@@ -122,6 +125,7 @@ export {
joinSignature,
parseTransaction,
populateTransaction,
serializeTransaction,
getJsonWalletAddress,

View File

@@ -103,7 +103,7 @@ class _DeployDescription extends Description implements DeployDescription {
});
}
errors.checkArgumentCount(params.length, this.inputs.length, 'in Interface constructor');
errors.checkArgumentCount(params.length, this.inputs.length, ' in Interface constructor');
try {
return (bytecode + defaultAbiCoder.encode(this.inputs, params).substring(2));
@@ -132,7 +132,7 @@ class _FunctionDescription extends Description implements FunctionDescription {
readonly gas: BigNumber;
encode(params: Array<any>): string {
errors.checkArgumentCount(params.length, this.inputs.length, 'in interface function ' + this.name);
errors.checkArgumentCount(params.length, this.inputs.length, ' in interface function ' + this.name);
try {
return this.sighash + defaultAbiCoder.encode(this.inputs, params).substring(2);
@@ -182,19 +182,21 @@ class _EventDescription extends Description implements EventDescription {
let topics: Array<string> = [];
if (!this.anonymous) { topics.push(this.topic); }
params.forEach((arg, index) => {
if (arg === null) {
topics.push(null);
return;
}
let param = this.inputs[index];
if (!param.indexed) {
errors.throwError('cannot filter non-indexed parameters; must be null', errors.INVALID_ARGUMENT, { argument: (param.name || index), value: arg });
if (arg != null) {
errors.throwError('cannot filter non-indexed parameters; must be null', errors.INVALID_ARGUMENT, { argument: (param.name || index), value: arg });
}
return;
}
if (param.type === 'string') {
if (arg == null) {
topics.push(null);
} else if (param.type === 'string') {
topics.push(id(arg));
} else if (param.type === 'bytes') {
topics.push(keccak256(arg));
@@ -318,13 +320,18 @@ function addMethod(method: any): void {
payable: (method.payable == null || !!method.payable),
type: ((method.constant) ? 'call': 'transaction'),
name: method.name,
signature: signature,
sighash: sighash,
});
// Expose the first (and hopefully unique named function
if (method.name && this.functions[method.name] == null) {
defineReadOnly(this.functions, method.name, description);
// Expose the first (and hopefully unique named function)
if (method.name) {
if (this.functions[method.name] == null) {
defineReadOnly(this.functions, method.name, description);
} else {
errors.warn('WARNING: Multiple definitions for ' + method.name);
}
}
// Expose all methods by their signature, for overloaded functions
@@ -365,7 +372,7 @@ function addMethod(method: any): void {
break;
default:
console.log('WARNING: unsupported ABI type - ' + method.type);
errors.warn('WARNING: unsupported ABI type - ' + method.type);
break;
}
}
@@ -431,10 +438,10 @@ export class Interface {
return new _TransactionDescription({
args: result,
decode: func.decode,
name: name,
name: func.name,
signature: func.signature,
sighash: func.sighash,
value: bigNumberify(tx.value || null),
value: bigNumberify(tx.value || '0'),
});
}
}

View File

@@ -7,33 +7,69 @@ export type Network = {
name: string,
chainId: number,
ensAddress?: string,
_defaultProvider?: (providers: any) => any
}
export type Networkish = Network | string | number;
function ethDefaultProvider(network: string): (providers: any) => any {
return function(providers: any): any {
let providerList: Array<any> = [];
if (providers.InfuraProvider) {
providerList.push(new providers.InfuraProvider(network));
}
if (providers.EtherscanProvider) {
providerList.push(new providers.EtherscanProvider(network));
}
if (providerList.length === 0) { return null; }
if (providers.FallbackProvider) {
return new providers.FallbackProvider(providerList);;
}
return providerList[0];
}
}
function etcDefaultProvider(url: string, network: string): (providers: any) => any {
return function(providers: any): any {
if (providers.JsonRpcProvider) {
return new providers.JsonRpcProvider(url, network);
}
return null;
}
}
const homestead: Network = {
chainId: 1,
ensAddress: "0x314159265dd8dbb310642f98f50c066173c1259b",
name: "homestead"
name: "homestead",
_defaultProvider: ethDefaultProvider('homestead')
};
const ropsten: Network = {
chainId: 3,
ensAddress: "0x112234455c3a32fd11230c42e7bccd4a84e02010",
name: "ropsten"
name: "ropsten",
_defaultProvider: ethDefaultProvider('ropsten')
};
const networks: { [name: string]: { chainId: number, ensAddress?: string } } = {
const networks: { [name: string]: Network } = {
unspecified: {
chainId: 0
chainId: 0,
name: 'unspecified'
},
homestead: homestead,
mainnet: homestead,
morden: {
chainId: 2
chainId: 2,
name: 'morden'
},
ropsten: ropsten,
@@ -41,19 +77,34 @@ const networks: { [name: string]: { chainId: number, ensAddress?: string } } = {
rinkeby: {
chainId: 4,
ensAddress: "0xe7410170f87102DF0055eB195163A03B7F2Bff4A"
ensAddress: "0xe7410170f87102DF0055eB195163A03B7F2Bff4A",
name: 'rinkeby',
_defaultProvider: ethDefaultProvider('rinkeby')
},
goerli: {
chainId: 5,
ensAddress: "0x112234455c3a32fd11230c42e7bccd4a84e02010",
name: "goerli",
_defaultProvider: ethDefaultProvider('goerli')
},
kovan: {
chainId: 42
chainId: 42,
name: 'kovan',
_defaultProvider: ethDefaultProvider('kovan')
},
classic: {
chainId: 61
chainId: 61,
name: 'classic',
_defaultProvider: etcDefaultProvider('https://web3.gastracker.io', 'classic')
},
classicTestnet: {
chainId: 62
chainId: 62,
name: 'classicTestnet',
_defaultProvider: etcDefaultProvider('https://web3.gastracker.io/morden', 'classicTestnet')
}
}
@@ -64,17 +115,18 @@ const networks: { [name: string]: { chainId: number, ensAddress?: string } } = {
* and verifies a network is a valid Network..
*/
export function getNetwork(network: Networkish): Network {
// No network (null) or unspecified (chainId = 0)
if (!network) { return null; }
// No network (null)
if (network == null) { return null; }
if (typeof(network) === 'number') {
for (var name in networks) {
for (let name in networks) {
let n = networks[name];
if (n.chainId === network) {
return {
name: name,
name: n.name,
chainId: n.chainId,
ensAddress: n.ensAddress
ensAddress: (n.ensAddress || null),
_defaultProvider: (n._defaultProvider || null)
};
}
}
@@ -89,9 +141,10 @@ export function getNetwork(network: Networkish): Network {
let n = networks[network];
if (n == null) { return null; }
return {
name: network,
name: n.name,
chainId: n.chainId,
ensAddress: n.ensAddress
ensAddress: n.ensAddress,
_defaultProvider: (n._defaultProvider || null)
};
}
@@ -110,10 +163,11 @@ export function getNetwork(network: Networkish): Network {
errors.throwError('network chainId mismatch', errors.INVALID_ARGUMENT, { arg: 'network', value: network });
}
// Standard Network
// Standard Network (allow overriding the ENS address)
return {
name: network.name,
chainId: n.chainId,
ensAddress: n.ensAddress
ensAddress: (network.ensAddress || n.ensAddress || null),
_defaultProvider: (network._defaultProvider || n._defaultProvider || null)
};
}

View File

@@ -1,5 +1,7 @@
'use strict';
import * as errors from '../errors';
export function defineReadOnly(object: any, name: string, value: any): void {
Object.defineProperty(object, name, {
enumerable: true,
@@ -42,6 +44,25 @@ export function resolveProperties(object: any): Promise<any> {
});
}
export function checkProperties(object: any, properties: { [ name: string ]: boolean }): void {
if (!object || typeof(object) !== 'object') {
errors.throwError('invalid object', errors.INVALID_ARGUMENT, {
argument: 'object',
value: object
});
}
Object.keys(object).forEach((key) => {
if (!properties[key]) {
errors.throwError('invalid object key - ' + key, errors.INVALID_ARGUMENT, {
argument: 'transaction',
value: object,
key: key
});
}
});
}
export function shallowCopy(object: any): any {
let result: any = {};
for (var key in object) { result[key] = object[key]; }

View File

@@ -62,6 +62,12 @@ export class KeyPair {
let otherKeyPair = getCurve().keyFromPublic(arrayify(computePublicKey(otherKey)));
return hexZeroPad('0x' + keyPair.derive(otherKeyPair.getPublic()).toString(16), 32);
}
_addPoint(other: Arrayish | string): string {
let p0 = getCurve().keyFromPublic(arrayify(this.publicKey));
let p1 = getCurve().keyFromPublic(arrayify(other));
return "0x" + p0.pub.add(p1.pub).encodeCompressed("hex");
}
}
export function computePublicKey(key: Arrayish | string, compressed?: boolean): string {

View File

@@ -438,6 +438,7 @@ export function encrypt(privateKey: Arrayish | SigningKey, password: Arrayish |
gethFilename: ('UTC--' + timestamp + '--' + data.address),
mnemonicCounter: hexlify(mnemonicIv).substring(2),
mnemonicCiphertext: hexlify(mnemonicCiphertext).substring(2),
path: path,
version: "0.1"
};
}

View File

@@ -7,6 +7,10 @@ import { arrayify } from './bytes';
// Types
import { Arrayish } from './bytes';
export function ripemd160(data: Arrayish): string {
return '0x' + (hash.ripemd160().update(arrayify(data)).digest('hex'));
}
export function sha256(data: Arrayish): string {
return '0x' + (hash.sha256().update(arrayify(data)).digest('hex'));
}

View File

@@ -9,6 +9,7 @@ import { getAddress } from './address';
import { BigNumber, bigNumberify } from './bignumber';
import { arrayify, hexlify, hexZeroPad, splitSignature, stripZeros, } from './bytes';
import { keccak256 } from './keccak256';
import { checkProperties, resolveProperties, shallowCopy } from './properties';
import * as RLP from './rlp';
@@ -19,6 +20,8 @@ import * as RLP from './rlp';
import { Arrayish, Signature } from './bytes';
import { BigNumberish } from './bignumber';
import { Provider } from '../providers/abstract-provider';
///////////////////////////////
// Exported Types
@@ -65,7 +68,7 @@ function handleNumber(value: string): BigNumber {
return bigNumberify(value);
}
var transactionFields = [
const transactionFields = [
{ name: 'nonce', maxLength: 32 },
{ name: 'gasPrice', maxLength: 32 },
{ name: 'gasLimit', maxLength: 32 },
@@ -74,8 +77,14 @@ var transactionFields = [
{ name: 'data' },
];
const allowedTransactionKeys: { [ key: string ]: boolean } = {
chainId: true, data: true, gasLimit: true, gasPrice:true, nonce: true, to: true, value: true
}
export function serialize(transaction: UnsignedTransaction, signature?: Arrayish | Signature): string {
var raw: Array<string | Uint8Array> = [];
checkProperties(transaction, allowedTransactionKeys);
let raw: Array<string | Uint8Array> = [];
transactionFields.forEach(function(fieldInfo) {
let value = (<any>transaction)[fieldInfo.name] || ([]);
@@ -115,7 +124,7 @@ export function serialize(transaction: UnsignedTransaction, signature?: Arrayish
let sig = splitSignature(signature);
// We pushed a chainId and null r, s on for hashing only; remove those
var v = 27 + sig.recoveryParam
let v = 27 + sig.recoveryParam
if (raw.length === 9) {
raw.pop();
raw.pop();
@@ -153,7 +162,7 @@ export function parse(rawTransaction: Arrayish): Transaction {
tx.v = bigNumberify(transaction[6]).toNumber();
} catch (error) {
console.log(error);
errors.info(error);
return tx;
}
@@ -171,7 +180,7 @@ export function parse(rawTransaction: Arrayish): Transaction {
tx.chainId = Math.floor((tx.v - 35) / 2);
if (tx.chainId < 0) { tx.chainId = 0; }
var recoveryParam = tx.v - 27;
let recoveryParam = tx.v - 27;
let raw = transaction.slice(0, 6);
@@ -182,11 +191,11 @@ export function parse(rawTransaction: Arrayish): Transaction {
recoveryParam -= tx.chainId * 2 + 8;
}
var digest = keccak256(RLP.encode(raw));
let digest = keccak256(RLP.encode(raw));
try {
tx.from = recoverAddress(digest, { r: hexlify(tx.r), s: hexlify(tx.s), recoveryParam: recoveryParam });
} catch (error) {
console.log(error);
errors.info(error);
}
tx.hash = keccak256(rawTransaction);
@@ -194,3 +203,41 @@ export function parse(rawTransaction: Arrayish): Transaction {
return tx;
}
export function populateTransaction(transaction: any, provider: Provider, from: string | Promise<string>): Promise<Transaction> {
if (!Provider.isProvider(provider)) {
errors.throwError('missing provider', errors.INVALID_ARGUMENT, {
argument: 'provider',
value: provider
});
}
checkProperties(transaction, allowedTransactionKeys);
let tx = shallowCopy(transaction);
if (tx.to != null) {
tx.to = provider.resolveName(tx.to);
}
if (tx.gasPrice == null) {
tx.gasPrice = provider.getGasPrice();
}
if (tx.nonce == null) {
tx.nonce = provider.getTransactionCount(from);
}
if (tx.gasLimit == null) {
let estimate = shallowCopy(tx);
estimate.from = from;
tx.gasLimit = provider.estimateGas(estimate);
}
if (tx.chainId == null) {
tx.chainId = provider.getNetwork().then((network) => network.chainId);
}
return resolveProperties(tx);
}

View File

@@ -1,7 +1,7 @@
'use strict';
import { HashZero } from '../constants';
import { checkNormalize } from '../errors';
import { arrayify, concat, hexlify } from './bytes';
///////////////////////////////
@@ -23,6 +23,7 @@ export enum UnicodeNormalizationForm {
export function toUtf8Bytes(str: string, form: UnicodeNormalizationForm = UnicodeNormalizationForm.current): Uint8Array {
if (form != UnicodeNormalizationForm.current) {
checkNormalize();
str = str.normalize(form);
}

View File

@@ -3,6 +3,7 @@
import { XMLHttpRequest } from 'xmlhttprequest';
import { encode as base64Encode } from './base64';
import { shallowCopy } from './properties';
import { toUtf8Bytes } from './utf8';
import * as errors from '../errors';
@@ -27,7 +28,8 @@ export type PollOptions = {
floor?: number,
ceiling?: number,
interval?: number,
onceBlock?: OnceBlockable
onceBlock?: OnceBlockable,
fastRetry?: number
};
@@ -134,6 +136,9 @@ export function fetchJson(connection: string | ConnectionInfo, json: string, pro
let jsonError: any = new Error('invalid json response');
jsonError.orginialError = error;
jsonError.responseText = request.responseText;
if (json != null) {
jsonError.requestBody = json;
}
jsonError.url = url;
reject(jsonError);
return;
@@ -162,7 +167,7 @@ export function fetchJson(connection: string | ConnectionInfo, json: string, pro
}
try {
if (json) {
if (json != null) {
request.send(json);
} else {
request.send();
@@ -180,6 +185,7 @@ export function fetchJson(connection: string | ConnectionInfo, json: string, pro
export function poll(func: () => Promise<any>, options?: PollOptions): Promise<any> {
if (!options) { options = {}; }
options = shallowCopy(options);
if (options.floor == null) { options.floor = 0; }
if (options.ceiling == null) { options.ceiling = 10000; }
if (options.interval == null) { options.interval = 250; }
@@ -203,6 +209,8 @@ export function poll(func: () => Promise<any>, options?: PollOptions): Promise<a
}, options.timeout)
}
let fastTimeout = options.fastRetry || null;
let attempt = 0;
function check() {
return func().then(function(result) {
@@ -222,6 +230,13 @@ export function poll(func: () => Promise<any>, options?: PollOptions): Promise<a
if (timeout < options.floor) { timeout = options.floor; }
if (timeout > options.ceiling) { timeout = options.ceiling; }
// Fast Timeout, means we quickly try again the first time
if (fastTimeout) {
attempt--;
timeout = fastTimeout;
fastTimeout = null;
}
setTimeout(check, timeout);
}

View File

@@ -10,7 +10,7 @@ import { defineReadOnly, resolveProperties, shallowCopy } from './utils/properti
import { randomBytes } from './utils/random-bytes';
import * as secretStorage from './utils/secret-storage';
import { SigningKey } from './utils/signing-key';
import { serialize as serializeTransaction } from './utils/transaction';
import { populateTransaction, serialize as serializeTransaction } from './utils/transaction';
import { Wordlist } from './utils/wordlist';
// Imported Abstracts
@@ -24,7 +24,6 @@ import { BlockTag, TransactionRequest, TransactionResponse } from './providers/a
import * as errors from './errors';
export class Wallet extends AbstractSigner {
readonly provider: Provider;
@@ -48,7 +47,7 @@ export class Wallet extends AbstractSigner {
get address(): string { return this.signingKey.address; }
get mnemonic(): string { return this.signingKey.mnemonic; }
get path(): string { return this.signingKey.mnemonic; }
get path(): string { return this.signingKey.path; }
get privateKey(): string { return this.signingKey.privateKey; }
@@ -63,7 +62,6 @@ export class Wallet extends AbstractSigner {
return new Wallet(this.signingKey, provider);
}
getAddress(): Promise<string> {
return Promise.resolve(this.address);
}
@@ -72,7 +70,7 @@ export class Wallet extends AbstractSigner {
return resolveProperties(transaction).then((tx) => {
let rawTx = serializeTransaction(tx);
let signature = this.signingKey.signDigest(keccak256(rawTx));
return Promise.resolve(serializeTransaction(tx, signature));
return serializeTransaction(tx, signature);
});
}
@@ -94,34 +92,16 @@ export class Wallet extends AbstractSigner {
sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse> {
if (!this.provider) { throw new Error('missing provider'); }
if (!transaction || typeof(transaction) !== 'object') {
throw new Error('invalid transaction object');
if (transaction.nonce == null) {
transaction = shallowCopy(transaction);
transaction.nonce = this.getTransactionCount("pending");
}
var tx = shallowCopy(transaction);
if (tx.to != null) {
tx.to = this.provider.resolveName(tx.to);
}
if (tx.gasLimit == null) {
tx.from = this.getAddress();
tx.gasLimit = this.provider.estimateGas(tx);
}
if (tx.gasPrice == null) {
tx.gasPrice = this.provider.getGasPrice();
}
if (tx.nonce == null) {
tx.nonce = this.getTransactionCount();
}
if (tx.chainId == null) {
tx.chainId = this.provider.getNetwork().then((network) => network.chainId);
}
return this.provider.sendTransaction(this.sign(tx));
return populateTransaction(transaction, this.provider, this.address).then((tx) => {
return this.sign(tx).then((signedTransaction) => {
return this.provider.sendTransaction(signedTransaction);
});
});
}
encrypt(password: Arrayish | string, options?: any, progressCallback?: ProgressCallback): Promise<string> {
@@ -165,7 +145,7 @@ export class Wallet extends AbstractSigner {
return Wallet.fromMnemonic(mnemonic, options.path, options.locale);
}
static fromEncryptedJson(json: string, password: Arrayish, progressCallback: ProgressCallback): Promise<Wallet> {
static fromEncryptedJson(json: string, password: Arrayish, progressCallback?: ProgressCallback): Promise<Wallet> {
if (isCrowdsaleWallet(json)) {
try {
if (progressCallback) { progressCallback(0); }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -132,9 +132,7 @@ class LangJa extends Wordlist {
}
split(mnemonic: string): Array<string> {
if (!mnemonic.normalize) {
errors.throwError('Japanese is unsupported on this platform; missing String.prototype.normalize', errors.UNSUPPORTED_OPERATION, { operation: 'String.prototype.normalize' });
}
errors.checkNormalize();
return mnemonic.split(/(?:\u3000| )+/g);
}

55
tests/local-tests.js Normal file
View File

@@ -0,0 +1,55 @@
// These are test cases that cannot be run on Travis CI, but running them locally can
// help prevent certain bugs from getting committed.
var assert = require('assert');
var ethers = require('..');
var wallet = new ethers.Wallet("0x0123456789012345678901234567890123456789012345678901234567890123");
describe("Local JSON-RPC", function() {
// https://github.com/ethers-io/ethers.js/issues/306
it ("sends a transaction", function() {
this.timeout(10000);
var provider = new ethers.providers.JsonRpcProvider();
var signer = provider.getSigner(1);
return signer.sendTransaction({
to: wallet.address,
value: 1
}).then(function(tx) {
console.log(tx);
return tx.wait().then(() => {
console.log("Mined", provider);
});
}, function(error) {
console.log(error);
assert.ok(false, "throws an error");
});
});
it("sends a wallet transactin", function() {
this.timeout(10000);
var provider = new ethers.providers.JsonRpcProvider();
var signer = wallet.connect(provider);
return signer.sendTransaction({
to: provider.getSigner(1).getAddress(),
value: 2
}).then(function(tx) {
console.log(tx);
return tx.wait().then(() => {
console.log("Mined");
});
}, function(error) {
console.log(error);
assert.ok(false, "throws an error");
});
});
});

View File

@@ -32,6 +32,9 @@ function getHD(seed) {
path: 'm',
privateKey: '0x' + privateKey.toString('hex'),
address: '0x' + ethereumUtil.privateToAddress(privateKey).toString('hex'),
parentFingerprint: rootNode.parentFingerprint,
xpriv: rootNode.toBase58(),
xpub: rootNode.neutered().toBase58(),
}];
for (var j = 0; j < 5; j++) {
@@ -42,6 +45,9 @@ function getHD(seed) {
path: path,
privateKey: '0x' + privateKey.toString('hex'),
address: '0x' + ethereumUtil.privateToAddress(privateKey).toString('hex'),
parentFingerprint: node.parentFingerprint,
xpriv: node.toBase58(),
xpub: node.neutered().toBase58(),
});
}
@@ -96,6 +102,8 @@ Testcases['axic'] = {
path: "m/44'/60'/0'/0/0",
address: '0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9',
privateKey: '0xb96e9ccb774cc33213cbcb2c69d3cdae17b0fe4888a1ccd343cbd1a17fd98b18',
xpriv: "xprvA2xEQ2iTe9QB22rvf5cbfpUxEBmMdvc7stEFxLhiMXmdLrwLbqugPCHRZiRfEq2puC5vTgwyFneV38hppF8oTf9aoaUv7M8u2XvnACTe6r4",
xpub: "xpub6FwaoYFMUWxUEWwPm79c2xRgnDbr3PKyF79rkj7KusJcDfGV9PDvvzbuQz32JYu3y2EpqY7xUag5Zw89YXokCKVtWLrfJ1RDUAYLLzTR8En"
}
]
}

99
tests/shims/base64.js Normal file
View File

@@ -0,0 +1,99 @@
/**
* See: https://github.com/MaxArt2501/base64-js
* The MIT License (MIT)
*
* Copyright (c) 2014 MaxArt2501
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define([], function() {factory(root);});
} else factory(root);
// node.js has always supported base64 conversions, while browsers that support
// web workers support base64 too, but you may never know.
})(typeof exports !== "undefined" ? exports : this, function(root) {
if (root.atob) {
// Some browsers' implementation of atob doesn't support whitespaces
// in the encoded string (notably, IE). This wraps the native atob
// in a function that strips the whitespaces.
// The original function can be retrieved in atob.original
try {
root.atob(" ");
} catch(e) {
root.atob = (function(atob) {
var func = function(string) {
return atob(String(string).replace(/[\t\n\f\r ]+/g, ""));
};
func.original = atob;
return func;
})(root.atob);
}
return;
}
// base64 character set, plus padding character (=)
var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
// Regular expression to check formal correctness of base64 encoded strings
b64re = /^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/;
root.btoa = function(string) {
string = String(string);
var bitmap, a, b, c,
result = "", i = 0,
rest = string.length % 3; // To determine the final padding
for (; i < string.length;) {
if ((a = string.charCodeAt(i++)) > 255
|| (b = string.charCodeAt(i++)) > 255
|| (c = string.charCodeAt(i++)) > 255)
throw new TypeError("Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.");
bitmap = (a << 16) | (b << 8) | c;
result += b64.charAt(bitmap >> 18 & 63) + b64.charAt(bitmap >> 12 & 63)
+ b64.charAt(bitmap >> 6 & 63) + b64.charAt(bitmap & 63);
}
// If there's need of padding, replace the last 'A's with equal signs
return rest ? result.slice(0, rest - 3) + "===".substring(rest) : result;
};
root.atob = function(string) {
// atob can work with strings with whitespaces, even inside the encoded part,
// but only \t, \n, \f, \r and ' ', which can be stripped.
string = String(string).replace(/[\t\n\f\r ]+/g, "");
if (!b64re.test(string))
throw new TypeError("Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.");
// Adding the padding if missing, for semplicity
string += "==".slice(2 - (string.length & 3));
var bitmap, result = "", r1, r2, i = 0;
for (; i < string.length;) {
bitmap = b64.indexOf(string.charAt(i++)) << 18 | b64.indexOf(string.charAt(i++)) << 12
| (r1 = b64.indexOf(string.charAt(i++))) << 6 | (r2 = b64.indexOf(string.charAt(i++)));
result += r1 === 64 ? String.fromCharCode(bitmap >> 16 & 255)
: r2 === 64 ? String.fromCharCode(bitmap >> 16 & 255, bitmap >> 8 & 255)
: String.fromCharCode(bitmap >> 16 & 255, bitmap >> 8 & 255, bitmap & 255);
}
return result;
};
});

73
tests/shims/index.js Normal file
View File

@@ -0,0 +1,73 @@
'use strict';
var shims = [];
// Shim String.prototype.normalize
try {
var missing = [];
// Some platforms are missing certain normalization forms
var forms = ["NFD", "NFC", "NFKD", "NFKC"];
for (var i = 0; i < forms.length; i++) {
try {
"test".normalize(forms[i]);
} catch(error) {
missing.push(forms[i]);
}
}
if (missing.length) {
shims.push("String.prototype.normalize (missing: " + missing.join(", ") + ")");
throw new Error('bad normalize');
}
// Some platforms have a native normalize, but it is broken; so we force our shim
if (String.fromCharCode(0xe9).normalize('NFD') !== String.fromCharCode(0x65, 0x0301)) {
shims.push("String.prototype.normalize (broken)");
throw new Error('bad normalize');
}
} catch (error) {
var unorm = require('./unorm.js');
String.prototype.normalize = function(form) {
var func = unorm[(form || 'NFC').toLowerCase()];
if (!func) { throw new RangeError('invalid form - ' + form); }
return func(this);
}
}
// Shim atob and btoa
var base64 = require('./base64.js');
if (!global.atob) {
shims.push("atob");
global.atob = base64.atob;
}
if (!global.btoa) {
shims.push("btoa");
global.btoa = base64.btoa;
}
// Shim Promise
// @TODO: Check first?
var promise = require('./es6-promise.auto.js');
// Shim ArrayBuffer.isView
if (!ArrayBuffer.isView) {
shims.push("ArrayBuffer.isView");
ArrayBuffer.isView = function(obj) {
// @TODO: This should probably check various instanceof aswell
return !!(obj.buffer);
}
}
// Shim nextTick
if (!global.nextTick) {
shims.push("nextTick");
global.nextTick = function (callback) { setTimeout(callback, 0); }
}
if (shims.length) {
console.log("Shims Injected:");
for (var i = 0; i < shims.length; i++) {
console.log(' - ' + shims[i]);
}
}

View File

@@ -231,6 +231,36 @@ describe('Test Interface Signatures', function() {
'derived the correct signature hash');
})
});
it('derives correct description for human-readable ABI', function() {
var iface = new Interface([ "function transfer(address from, uint amount)" ]);
[
"transfer",
"transfer(address,uint256)"
].forEach(function(key) {
var descr = iface.functions[key];
assert.equal(descr.name, "transfer", "incorrect name key - " + key);
assert.equal(descr.signature, "transfer(address,uint256)", "incorrect signature key - " + key);
assert.equal(descr.sighash, "0xa9059cbb", "incorrect sighash key - " + key);
});
});
// See: https://github.com/ethers-io/ethers.js/issues/370
it ('parses transaction function', function() {
var iface = new Interface([ "function transfer(address from, uint amount)" ]);
// Transaction: 0x820cc57bc77be44d8f4f024a18e18f64a8b6e62a82a3d7897db5970dbe181ba1
var rawTx = "0xf8aa028502540be4008316e36094334eec1482109bd802d9e72a447848de3bcc106380b844a9059cbb000000000000000000000000851b9167b7cbf772d38efaf89705b35022880a070000000000000000000000000000000000000000000000000de0b6b3a764000026a03200bf26e5f10f7eda59c0aad9adc2334dda79e785b9b004342524d97a66fca9a0450b07a4dc450bb472e08f8370350fa365fcef6db1a95309ae4c06c9d0748092";
var tx = ethers.utils.parseTransaction(rawTx);
var descr = iface.parseTransaction(tx);
assert.equal(descr.args[0], '0x851b9167B7cbf772D38eFaf89705b35022880A07', 'parsed tx - args[0]');
assert.equal(descr.args[1].toString(), '1000000000000000000', 'parsed tx - args[1]');
assert.equal(descr.name, 'transfer', 'parsed tx - name');
assert.equal(descr.signature, 'transfer(address,uint256)', 'parsed tx - signature');
assert.equal(descr.sighash, '0xa9059cbb', 'parsed tx - sighash');
assert.equal(descr.value.toString(), '0', 'parsed tx - value');
});
});
describe('Test Number Coder', function() {
@@ -441,3 +471,72 @@ describe('Test Fixed Bytes Coder', function() {
});
});
});
describe('Test Filters', function() {
// @TODO: Add a LOT more tests here
function doTest(test) {
it(test.name, function() {
var iface = new ethers.utils.Interface([ test.signature ]);
var eventDescription = iface.events[test.event];
var filter = eventDescription.encodeTopics(test.args);
assert.equal(filter.length, test.expected.length, 'filter length matches - ' + test.name);
filter.forEach(function(expected, index) {
assert.equal(expected, test.expected[index], 'signature topic matches - ' + index + ' - ' + test.name);
});
});
}
var Tests = [
// Skips null in non-indexed fields
// See: https://github.com/ethers-io/ethers.js/issues/305
{
name: "creates correct filters for null non-indexed fields",
args: [ null, 2, null, null ],
event: "LogSomething",
signature: "event LogSomething(int hup, int indexed two, bool three, address indexed four)",
expected: [
"0xf6b983969813047dce97b9ff8a48cfb0a13306eb2caae2ef186b280bc27491c8",
"0x0000000000000000000000000000000000000000000000000000000000000002"
]
},
// https://etherscan.io/tx/0x820cc57bc77be44d8f4f024a18e18f64a8b6e62a82a3d7897db5970dbe181ba1
{
name: "transfer filtering from",
args: [
"0x59DEa134510ebce4a0c7146595dc8A61Eb9D0D79"
],
event: "Transfer",
signature: "event Transfer(address indexed from, address indexed to, uint value)",
expected: [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x00000000000000000000000059dea134510ebce4a0c7146595dc8a61eb9d0d79"
]
},
{
name: "transfer filtering to",
args: [
null,
"0x851b9167B7cbf772D38eFaf89705b35022880A07"
],
event: "Transfer",
signature: "event Transfer(address indexed from, address indexed to, uint value)",
expected: [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
null,
"0x000000000000000000000000851b9167b7cbf772d38efaf89705b35022880a07"
]
}
];
Tests.forEach(function(test) {
doTest(test);
});
});

View File

@@ -9,19 +9,76 @@ describe('Test HD Node Derivation', function(test) {
var tests = utils.loadTests('hdnode');
tests.forEach(function(test) {
it('Derives the HD nodes - ' + test.name, function() {
it('Derives the HD nodes - ' + test.name, function() {
this.timeout(10000);
var rootNode = new ethers.utils.HDNode.fromSeed(test.seed);
test.hdnodes.forEach(function(nodeTest) {
//var rootNode = new ethers.utils.HDNode.fromSeed(test.seed);
var rootNode = new ethers.utils.HDNode.fromMnemonic(test.mnemonic, null, test.password || null);
test.hdnodes.forEach(function(nodeTest) {
var node = rootNode.derivePath(nodeTest.path);
assert.equal(node.privateKey, nodeTest.privateKey,
'Generates privateKey - ' + nodeTest.privateKey);
assert.equal(node.extendedKey, nodeTest.xpriv,
"Child Extended privateKey - " + nodeTest.privateKey);
assert.equal(node.neuter().extendedKey, nodeTest.xpub,
"Child Extended privateKey - " + nodeTest.privateKey);
var wallet = new ethers.Wallet(node.privateKey);
assert.equal(wallet.address.toLowerCase(), nodeTest.address,
'Generates address - ' + nodeTest.privateKey);
assert.equal(node.address, (new ethers.Wallet(node)).address,
'HDNode address matches - ' + nodeTest.privateKey);
// Test public extended key derivation
var lastHardened = nodeTest.path.match(/^(.*)'([^']*)$/);
if (lastHardened && lastHardened[2].trim() !== "") {
// Derive as far as we can for hardened, then derive the remaining from neutered
var hardNode = rootNode.derivePath(lastHardened[1] + "'");
var neutered = hardNode.neuter();
var nodeXpriv = ethers.utils.HDNode.fromExtendedKey(hardNode.extendedKey);
nodeXpriv = nodeXpriv.derivePath(lastHardened[2].substring(1));
var nodeXpub = ethers.utils.HDNode.fromExtendedKey(neutered.extendedKey);
nodeXpub = nodeXpub.derivePath(lastHardened[2].substring(1));
assert.equal(neutered.privateKey, null,
'Neutered HDNode privateKey null - ' + nodeTest.privateKey);
assert.equal(neutered.xpriv, null,
'Neutered HDNode xpriv null - ' + nodeTest.privateKey);
neutered = neutered.derivePath(lastHardened[2].substring(1));
assert.equal(neutered.address.toLowerCase(), nodeTest.address,
'Derived Neutered HDNode address matches - ' + nodeTest.privateKey);
assert.equal(neutered.xpub, node.xpub,
'Derived Neutered HDNode xpub matches - ' + nodeTest.privateKey);
assert.equal(neutered.privateKey, null,
'Derived Neutered HDNode privateKey null - ' + nodeTest.privateKey);
assert.equal(neutered.xpriv, null,
'Neutered HDNode xpriv null - ' + nodeTest.privateKey);
// Test extended key derivation
assert.equal(nodeXpub.xpriv, null,
'Serialized Neutered HDNode xpriv null - ' + nodeTest.privateKey);
assert.equal(nodeXpriv.extendedKey, node.extendedKey,
'Serialized HDNode xpriv matches - ' + nodeTest.privateKey);
assert.equal(nodeXpub.extendedKey, neutered.extendedKey,
'Serialized Neutered HDNode xpub matches - ' + nodeTest.privateKey);
}
// Test serialization
var deserializedNode = ethers.utils.HDNode.fromExtendedKey(nodeTest.xpriv);
assert.equal(deserializedNode.extendedKey, nodeTest.xpriv,
'Neutered HDNode xpriv null - ' + nodeTest.privateKey);
assert.equal(deserializedNode.neuter().extendedKey, nodeTest.xpub,
'Neutered HDNode xpriv null - ' + nodeTest.privateKey);
});
});
});
@@ -64,6 +121,8 @@ function testEasySeed(lang, locale) {
}
testEasySeed(ethers.wordlists.en, 'en');
testEasySeed(ethers.wordlists.es, 'es');
testEasySeed(ethers.wordlists.fr, 'fr');
testEasySeed(ethers.wordlists.ja, 'ja');
testEasySeed(ethers.wordlists.zh_cn, 'zh_cn');
testEasySeed(ethers.wordlists.zh_tw, 'zh_tw');

View File

@@ -53,6 +53,7 @@ var blockchainData = {
blockNumber: 0x3c92b5,
contractAddress: null,
cumulativeGasUsed: 0x1cca2e,
from: "0x18C6045651826824FEBBD39d8560584078d1b247",
gasUsed:0x14bb7,
logs: [
{
@@ -78,11 +79,12 @@ var blockchainData = {
transactionLogIndex: 0x1
}
],
"logsBloom": "0x00000000000000040000000000100000010000000000000040000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000200000010000000004000000000000000000000000000000000002000000000000000000000000400000000020000000000000000000000000000000000000004000000000000000000000000000000000000000000000000801000000000000000000000020000000000040000000040000000000000000002000000004000000000000000000000000000000000000000000000010000000000000000000000000000000000200000000000000000",
"root": "0x9b550a9a640ce50331b64504ef87aaa7e2aaf97344acb6ff111f879b319d2590",
"status": null,
"transactionHash": "0xc6fcb7d00d536e659a4559d2de29afa9e364094438fef3e72ba80728ce1cb616",
"transactionIndex": 0x39
logsBloom: "0x00000000000000040000000000100000010000000000000040000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000200000010000000004000000000000000000000000000000000002000000000000000000000000400000000020000000000000000000000000000000000000004000000000000000000000000000000000000000000000000801000000000000000000000020000000000040000000040000000000000000002000000004000000000000000000000000000000000000000000000010000000000000000000000000000000000200000000000000000",
root: "0x9b550a9a640ce50331b64504ef87aaa7e2aaf97344acb6ff111f879b319d2590",
status: null,
to: "0x6090A6e47849629b7245Dfa1Ca21D94cd15878Ef",
transactionHash: "0xc6fcb7d00d536e659a4559d2de29afa9e364094438fef3e72ba80728ce1cb616",
transactionIndex: 0x39,
},
transactionReceiptByzantium: {
byzantium: true,
@@ -90,6 +92,7 @@ var blockchainData = {
blockNumber: 0x444f76,
contractAddress: null,
cumulativeGasUsed: 0x15bfe7,
from: "0x18C6045651826824FEBBD39d8560584078d1b247",
gasUsed: 0x1b968,
logs: [
{
@@ -106,6 +109,7 @@ var blockchainData = {
],
logsBloom: "0x00000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000200000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000800000000000000000800000000000000000000000000000000000000",
status:1,
to: "0xb90E64082D00437e65A76d4c8187596BC213480a",
transactionHash: "0x7f1c6a58dc880438236d0b0a4ae166e9e9a038dbea8ec074149bd8b176332cac",
transactionIndex: 0x1e
}
@@ -170,6 +174,7 @@ var blockchainData = {
blockNumber: 0x1564d8,
contractAddress: null,
cumulativeGasUsed: bigNumberify("0x80b9"),
from: "0xb346D5019EeafC028CfC01A5f789399C2314ae8D",
gasUsed: bigNumberify("0x80b9"),
logs: [
{
@@ -186,6 +191,7 @@ var blockchainData = {
],
logsBloom: "0x00000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000010000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
root: "0xf1c3506ab619ac1b5e8f1ca355b16d6b9a1b7436b2960b0e9ec9a91f4238b5cc",
to: "0x6fC21092DA55B392b045eD78F4732bff3C580e2c",
transactionHash: "0x55c477790b105e69e98afadf0505cbda606414b0187356137132bf24945016ce",
transactionIndex: 0x0
},
@@ -195,6 +201,7 @@ var blockchainData = {
blockNumber: 0x1e1e3b,
contractAddress: null,
cumulativeGasUsed: bigNumberify("0x4142f"),
from: "0xdc8F20170C0946ACCF9627b3EB1513CFD1c0499f",
gasUsed: bigNumberify("0x1eb6d"),
logs:[
{
@@ -211,10 +218,44 @@ var blockchainData = {
],
logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000080000000202000000",
status: 1,
to: "0xB70560a43A9aBf6ea2016F40a3e84B8821E134c5",
transactionHash: "0xf724f1d6813f13fb523c5f6af6261d06d41138dd094fff723e09fb0f893f03e6",
transactionIndex: 0x2
},
},
goerli: {
balance: {
address: "0x06B5955A67D827CDF91823E3bB8F069e6c89c1D6",
balance: bigNumberify("314159000000000000")
},
block3: {
hash: '0xd5daa825732729bb0d2fd187a1b888e6bfc890f1fc5333984740d9052afb2920',
parentHash: '0xe675f1362d82cdd1ec260b16fb046c17f61d8a84808150f5d715ccce775f575e',
number: 3,
timestamp: 1548947483,
difficulty: 2,
gasLimit: bigNumberify('10455073'),
gasUsed: bigNumberify('0'),
miner: '0x0000000000000000000000000000000000000000',
extraData: '0x506172697479205465636820417574686f7269747900000000000000000000002822e1b202411c38084d96c84302b8361ec4840a51cd2fad9cb4bd9921cad7e64bc2e5dc7b41f3f75b33358be3aec718cf4d4317ace940e01b3581a95c9259ac01',
transactions: []
},
transactionReceipt: {
blockHash: '0x2384e8e8bdcf6eb87ec7c138fa503ac34adb32cac817e4b35f14d4339eaa1993',
blockNumber: 47464,
byzantium: true,
contractAddress: null,
cumulativeGasUsed: bigNumberify(21000),
from: '0x8c1e1e5b47980D214965f3bd8ea34C413E120ae4',
gasUsed: bigNumberify(21000),
logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
to: '0x58Bb4221245461E1d4cf886f18a01E3Df40Bd359',
transactionHash: '0xec8b1ac5d787f36c738cc7793fec606283b41f1efa69df4ae6b2a014dcd12797',
transactionIndex: 0,
logs: [],
status: 1
}
}
}
blockchainData['default'] = blockchainData.homestead;
@@ -370,7 +411,7 @@ function testProvider(providerName, networkName) {
});
}
['default', 'homestead', 'ropsten', 'rinkeby', 'kovan'].forEach(function(networkName) {
['default', 'homestead', 'ropsten', 'rinkeby', 'kovan', 'goerli'].forEach(function(networkName) {
['getDefaultProvider', 'InfuraProvider', 'EtherscanProvider', 'Web3Provider'].forEach(function(providerName) {
// @TODO: Remove this! Temporary because Etherscan is down

View File

@@ -327,3 +327,41 @@ describe('Test Bytes32String coder', function() {
assert.equal(str2, str, "parsed correctly");
});
});
describe('Test BigNumber', function() {
it("computes absoltue values", function() {
function testAbs(test) {
var value = ethers.utils.bigNumberify(test.value);
var expected = ethers.utils.bigNumberify(test.expected);
assert.ok(value.abs().eq(expected), 'BigNumber.abs - ' + test.value);
}
[
{ value: "0x0", expected: "0x0" },
{ value: "-0x0", expected: "0x0" },
{ value: "0x5", expected: "0x5" },
{ value: "-0x5", expected: "0x5" },
{ value: "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", expected: "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" },
{ value: "-0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", expected: "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" },
{ value: "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", expected: "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" },
{ value: "-0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", expected: "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" },
].forEach(testAbs);
});
});
describe("Hexlify", function() {
it("hexlify on string of unsafe number", function() {
assert(ethers.utils.hexlify(ethers.utils.bigNumberify("9985956830000000000")), "0x8a953ed43a892c00", "hexlify on large BigNumber");
});
[9007199254740991, 9985956830000000000].forEach(function(value) {
it('hexlify fails on unsafe number - ' + value, function() {
assert.throws(function() {
var result = ethers.utils.hexlify(value);
console.log('Result', result);
}, function(error) {
return (error.code === "NUMERIC_FAULT" && error.fault === "out-of-safe-range");
}, "hexlify throws on out-of-range value - " + value);
});
});
});

View File

@@ -31,7 +31,7 @@ describe('Test JSON Wallets', function() {
// A few extra test cases to test encrypting/decrypting
['one', 'two', 'three'].forEach(function(i) {
var password = 'foobar' + i;
var wallet = new Wallet(utils.randomHexString('test-' + i, 32));
var wallet = Wallet.createRandom({ path: "m/56'/82", extraEntropy: utils.randomHexString('test-' + i, 32) });
it('encrypts and decrypts a random wallet - ' + i, function() {
this.timeout(1200000);
@@ -39,6 +39,10 @@ describe('Test JSON Wallets', function() {
return Wallet.fromEncryptedJson(json, password).then(function(decryptedWallet) {
assert.equal(decryptedWallet.address, wallet.address,
'decrypted wallet - ' + wallet.privateKey);
assert.equal(decryptedWallet.mnemonic, wallet.mnemonic,
"decrypted wallet menonic - " + wallet.privateKey);
assert.equal(decryptedWallet.path, wallet.path,
"decrypted wallet path - " + wallet.privateKey);
return decryptedWallet.encrypt(password).then(function(encryptedWallet) {
var parsedWallet = JSON.parse(encryptedWallet);
assert.equal(decryptedWallet.address.toLowerCase().substring(2), parsedWallet.address,

View File

@@ -11,37 +11,7 @@
<body>
<div id="mocha"></div>
<script src="../node_modules/mocha/mocha.js"></script>
<!--
Shim for PhantomJS: Promise
See: https://github.com/stefanpenner/es6-promise
-->
<script src="./dist/es6-promise.auto.js"></script>
<!--
Shim for String.prototype.normalize
See: https://github.com/walling/unorm
-->
<script src="./dist/unorm.js"></script>
<!--
Shims for PhantomJS
-->
<script type="text/javascript">
// ArrayBuffer.isView
if (!ArrayBuffer.isView) {
ArrayBuffer.isView = function(obj) {
// @TODO: This should probably check various instanceof aswell
return !!(obj.buffer);
}
}
// nextTick
if (!window.nextTick) {
window.nextTick = function (callback) { setTimeout(callback, 0); }
}
</script>
<script src="../dist/shims.js"></script>
<!-- Inject the mocha describe and it functions -->
<script type="text/javascript">

Binary file not shown.

8
thirdparty.d.ts vendored
View File

@@ -65,6 +65,11 @@ declare module "elliptic" {
recoveryParam: number
}
interface Point {
add(point: Point): Point;
encodeCompressed(enc: string): string
}
interface KeyPair {
sign(message: Uint8Array, options: { canonical?: boolean }): Signature;
getPublic(compressed: boolean, encoding?: string): string;
@@ -72,6 +77,7 @@ declare module "elliptic" {
getPrivate(encoding?: string): string;
encode(encoding: string, compressed: boolean): string;
derive(publicKey: BN): BN;
pub: Point;
priv: BN;
}
@@ -83,6 +89,8 @@ declare module "elliptic" {
keyFromPublic(publicKey: string | Uint8Array): KeyPair;
keyFromPrivate(privateKey: string | Uint8Array): KeyPair;
recoverPubKey(data: Uint8Array, signature: BasicSignature, recoveryParam: number): KeyPair;
// curve: Curve;
}
}

View File

@@ -51,9 +51,11 @@ function verifyType(type) {
return type;
}
function parseParam(param, allowIndexed) {
var originalParam = param;
function throwError(i) {
throw new Error('unexpected character "' + param[i] + '" at position ' + i + ' in "' + param + '"');
throw new Error('unexpected character "' + originalParam[i] + '" at position ' + i + ' in "' + originalParam + '"');
}
param = param.replace(/\s/g, ' ');
var parent = { type: '', name: '', state: { allowType: true } };
var node = parent;
for (var i = 0; i < param.length; i++) {
@@ -194,7 +196,7 @@ function parseSignatureEvent(fragment) {
case '':
break;
default:
console.log('unknown modifier: ' + modifier);
errors.info('unknown modifier: ' + modifier);
}
});
if (abi.name && !abi.name.match(regexIdentifier)) {
@@ -258,7 +260,7 @@ function parseSignatureFunction(fragment) {
case '':
break;
default:
console.log('unknown modifier: ' + modifier);
errors.info('unknown modifier: ' + modifier);
}
});
// We have outputs
@@ -298,6 +300,7 @@ exports.formatSignature = formatSignature;
function parseSignature(fragment) {
if (typeof (fragment) === 'string') {
// Make sure the "returns" is surrounded by a space and all whitespace is exactly one space
fragment = fragment.replace(/\s/g, ' ');
fragment = fragment.replace(/\(/g, ' (').replace(/\)/g, ') ').replace(/\s+/g, ' ');
fragment = fragment.trim();
if (fragment.substring(0, 6) === 'event ') {
@@ -733,7 +736,7 @@ var CoderArray = /** @class */ (function (_super) {
count = value.length;
result = uint256Coder.encode(count);
}
errors.checkArgumentCount(count, value.length, 'in coder array' + (this.localName ? (" " + this.localName) : ""));
errors.checkArgumentCount(count, value.length, ' in coder array' + (this.localName ? (" " + this.localName) : ""));
var coders = [];
for (var i = 0; i < value.length; i++) {
coders.push(this.coder);

52
utils/basex.d.ts vendored Normal file
View File

@@ -0,0 +1,52 @@
/**
* var basex = require('base-x');
*
* This implementation is heavily based on base-x. The main reason to
* deviate was to prevent the dependency of Buffer.
*
* Contributors:
*
* base-x encoding
* Forked from https://github.com/cryptocoinjs/bs58
* Originally written by Mike Hearn for BitcoinJ
* Copyright (c) 2011 Google Inc
* Ported to JavaScript by Stefan Thomas
* Merged Buffer refactorings from base58-native by Stephen Pair
* Copyright (c) 2013 BitPay Inc
*
* The MIT License (MIT)
*
* Copyright base-x contributors (c) 2016
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
import { Arrayish } from "./bytes";
export declare class BaseX {
readonly alphabet: string;
readonly base: number;
private _alphabetMap;
private _leader;
constructor(alphabet: string);
encode(value: Arrayish | string): string;
decode(value: string): Uint8Array;
}
declare const Base32: BaseX;
declare const Base58: BaseX;
export { Base32, Base58 };

123
utils/basex.js Normal file
View File

@@ -0,0 +1,123 @@
"use strict";
/**
* var basex = require('base-x');
*
* This implementation is heavily based on base-x. The main reason to
* deviate was to prevent the dependency of Buffer.
*
* Contributors:
*
* base-x encoding
* Forked from https://github.com/cryptocoinjs/bs58
* Originally written by Mike Hearn for BitcoinJ
* Copyright (c) 2011 Google Inc
* Ported to JavaScript by Stefan Thomas
* Merged Buffer refactorings from base58-native by Stephen Pair
* Copyright (c) 2013 BitPay Inc
*
* The MIT License (MIT)
*
* Copyright base-x contributors (c) 2016
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
Object.defineProperty(exports, "__esModule", { value: true });
var bytes_1 = require("./bytes");
var properties_1 = require("./properties");
var BaseX = /** @class */ (function () {
function BaseX(alphabet) {
properties_1.defineReadOnly(this, "alphabet", alphabet);
properties_1.defineReadOnly(this, "base", alphabet.length);
properties_1.defineReadOnly(this, "_alphabetMap", {});
properties_1.defineReadOnly(this, "_leader", alphabet.charAt(0));
// pre-compute lookup table
for (var i = 0; i < alphabet.length; i++) {
this._alphabetMap[alphabet.charAt(i)] = i;
}
}
BaseX.prototype.encode = function (value) {
var source = bytes_1.arrayify(value);
if (source.length === 0) {
return '';
}
var digits = [0];
for (var i = 0; i < source.length; ++i) {
var carry = source[i];
for (var j = 0; j < digits.length; ++j) {
carry += digits[j] << 8;
digits[j] = carry % this.base;
carry = (carry / this.base) | 0;
}
while (carry > 0) {
digits.push(carry % this.base);
carry = (carry / this.base) | 0;
}
}
var string = '';
// deal with leading zeros
for (var k = 0; source[k] === 0 && k < source.length - 1; ++k) {
string += this._leader;
}
// convert digits to a string
for (var q = digits.length - 1; q >= 0; --q) {
string += this.alphabet[digits[q]];
}
return string;
};
BaseX.prototype.decode = function (value) {
if (typeof (value) !== 'string') {
throw new TypeError('Expected String');
}
var bytes = [];
if (value.length === 0) {
return new Uint8Array(bytes);
}
bytes.push(0);
for (var i = 0; i < value.length; i++) {
var byte = this._alphabetMap[value[i]];
if (byte === undefined) {
throw new Error('Non-base' + this.base + ' character');
}
var carry = byte;
for (var j = 0; j < bytes.length; ++j) {
carry += bytes[j] * this.base;
bytes[j] = carry & 0xff;
carry >>= 8;
}
while (carry > 0) {
bytes.push(carry & 0xff);
carry >>= 8;
}
}
// deal with leading zeros
for (var k = 0; value[k] === this._leader && k < value.length - 1; ++k) {
bytes.push(0);
}
return bytes_1.arrayify(new Uint8Array(bytes.reverse()));
};
return BaseX;
}());
exports.BaseX = BaseX;
var Base32 = new BaseX("abcdefghijklmnopqrstuvwxyz234567");
exports.Base32 = Base32;
var Base58 = new BaseX("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz");
exports.Base58 = Base58;
//console.log(Base58.decode("Qmd2V777o5XvJbYMeMb8k2nU5f8d3ciUQ5YpYuWhzv8iDj"))
//console.log(Base58.encode(Base58.decode("Qmd2V777o5XvJbYMeMb8k2nU5f8d3ciUQ5YpYuWhzv8iDj")))

View File

@@ -6,6 +6,7 @@ export declare class BigNumber implements Hexable {
constructor(value: BigNumberish);
fromTwos(value: number): BigNumber;
toTwos(value: number): BigNumber;
abs(): BigNumber;
add(other: BigNumberish): BigNumber;
sub(other: BigNumberish): BigNumber;
div(other: BigNumberish): BigNumber;

View File

@@ -105,6 +105,12 @@ var BigNumber = /** @class */ (function () {
BigNumber.prototype.toTwos = function (value) {
return toBigNumber(_bnify(this).toTwos(value));
};
BigNumber.prototype.abs = function () {
if (this._hex[0] === '-') {
return toBigNumber(_bnify(this).mul(BN_1));
}
return this;
};
BigNumber.prototype.add = function (other) {
return toBigNumber(_bnify(this).add(toBN(other)));
};

View File

@@ -23,7 +23,7 @@ function addSlice(array) {
}
array.slice = function () {
var args = Array.prototype.slice.call(arguments);
return new Uint8Array(Array.prototype.slice.apply(array, args));
return addSlice(new Uint8Array(Array.prototype.slice.apply(array, args)));
};
return array;
}
@@ -135,6 +135,14 @@ function hexlify(value) {
if (value < 0) {
errors.throwError('cannot hexlify negative value', errors.INVALID_ARGUMENT, { arg: 'value', value: value });
}
// @TODO: Roll this into the above error as a numeric fault (overflow); next version, not backward compatible
// We can about (value == MAX_INT) to as well, since that may indicate we underflowed already
if (value >= 9007199254740991) {
errors.throwError("out-of-range", errors.NUMERIC_FAULT, {
operartion: "hexlify",
fault: "out-of-safe-range"
});
}
var hex = '';
while (value) {
hex = HexCharacters[value & 0x0f] + hex;

View File

@@ -1,5 +1,13 @@
'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 errors = __importStar(require("../errors"));
var bytes_1 = require("./bytes");
var utf8_1 = require("./utf8");
var keccak256_1 = require("./keccak256");
@@ -8,13 +16,22 @@ var Zeros = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
var Partition = new RegExp("^((.*)\\.)?([^.]+)$");
var UseSTD3ASCIIRules = new RegExp("^[a-z0-9.-]*$");
function namehash(name) {
if (typeof (name) !== 'string') {
errors.throwError('invalid address - ' + String(name), errors.INVALID_ARGUMENT, {
argument: 'name',
value: name
});
}
name = name.toLowerCase();
// Supporting the full UTF-8 space requires additional (and large)
// libraries, so for now we simply do not support them.
// It should be fairly easy in the future to support systems with
// String.normalize, but that is future work.
if (!name.match(UseSTD3ASCIIRules)) {
throw new Error('contains invalid UseSTD3ASCIIRules characters');
errors.throwError('contains invalid UseSTD3ASCIIRules characters', errors.INVALID_ARGUMENT, {
argument: 'name',
value: name
});
}
var result = Zeros;
while (name.length) {
@@ -31,11 +48,10 @@ function id(text) {
}
exports.id = id;
function hashMessage(message) {
var payload = bytes_1.concat([
return keccak256_1.keccak256(bytes_1.concat([
utf8_1.toUtf8Bytes('\x19Ethereum Signed Message:\n'),
utf8_1.toUtf8Bytes(String(message.length)),
((typeof (message) === 'string') ? utf8_1.toUtf8Bytes(message) : message)
]);
return keccak256_1.keccak256(payload);
]));
}
exports.hashMessage = hashMessage;

11
utils/hdnode.d.ts vendored
View File

@@ -2,9 +2,11 @@ import { Arrayish } from './bytes';
import { Wordlist } from './wordlist';
export declare const defaultPath = "m/44'/60'/0'/0/0";
export declare class HDNode {
private readonly keyPair;
readonly privateKey: string;
readonly publicKey: string;
readonly fingerprint: string;
readonly parentFingerprint: string;
readonly address: string;
readonly mnemonic: string;
readonly path: string;
readonly chainCode: string;
@@ -17,12 +19,15 @@ export declare class HDNode {
* - fromMnemonic
* - fromSeed
*/
constructor(constructorGuard: any, privateKey: Arrayish, chainCode: Uint8Array, index: number, depth: number, mnemonic: string, path: string);
constructor(constructorGuard: any, privateKey: string, publicKey: string, parentFingerprint: string, chainCode: string, index: number, depth: number, mnemonic: string, path: string);
readonly extendedKey: string;
neuter(): HDNode;
private _derive;
derivePath(path: string): HDNode;
static isHDNode(value: any): value is HDNode;
}
export declare function fromMnemonic(mnemonic: string, wordlist?: Wordlist): HDNode;
export declare function fromExtendedKey(extendedKey: string): HDNode;
export declare function fromMnemonic(mnemonic: string, wordlist?: Wordlist, password?: string): HDNode;
export declare function fromSeed(seed: Arrayish): HDNode;
export declare function mnemonicToSeed(mnemonic: string, password?: string): string;
export declare function mnemonicToEntropy(mnemonic: string, wordlist?: Wordlist): string;

View File

@@ -1,4 +1,4 @@
'use strict';
"use strict";
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
@@ -16,6 +16,7 @@ var lang_en_1 = require("../wordlists/lang-en");
// Automatically register English?
//import { register } from '../wordlists/wordlist';
//register(langEn);
var basex_1 = require("./basex");
var bytes_1 = require("./bytes");
var bignumber_1 = require("./bignumber");
var utf8_1 = require("./utf8");
@@ -36,6 +37,13 @@ function getUpperMask(bits) {
function getLowerMask(bits) {
return (1 << bits) - 1;
}
function bytes32(value) {
return bytes_1.hexZeroPad(bytes_1.hexlify(value), 32);
}
function base58check(data) {
var checksum = bytes_1.hexDataSlice(sha2_1.sha256(sha2_1.sha256(data)), 0, 4);
return basex_1.Base58.encode(bytes_1.concat([data, checksum]));
}
var _constructorGuard = {};
exports.defaultPath = "m/44'/60'/0'/0/0";
var HDNode = /** @class */ (function () {
@@ -46,37 +54,69 @@ var HDNode = /** @class */ (function () {
* - fromMnemonic
* - fromSeed
*/
function HDNode(constructorGuard, privateKey, chainCode, index, depth, mnemonic, path) {
function HDNode(constructorGuard, privateKey, publicKey, parentFingerprint, chainCode, index, depth, mnemonic, path) {
errors.checkNew(this, HDNode);
if (constructorGuard !== _constructorGuard) {
throw new Error('HDNode constructor cannot be called directly');
}
properties_1.defineReadOnly(this, 'keyPair', new secp256k1_1.KeyPair(privateKey));
properties_1.defineReadOnly(this, 'privateKey', this.keyPair.privateKey);
properties_1.defineReadOnly(this, 'publicKey', this.keyPair.compressedPublicKey);
properties_1.defineReadOnly(this, 'chainCode', bytes_1.hexlify(chainCode));
if (privateKey) {
var keyPair = new secp256k1_1.KeyPair(privateKey);
properties_1.defineReadOnly(this, 'privateKey', keyPair.privateKey);
properties_1.defineReadOnly(this, 'publicKey', keyPair.compressedPublicKey);
}
else {
properties_1.defineReadOnly(this, 'privateKey', null);
properties_1.defineReadOnly(this, 'publicKey', bytes_1.hexlify(publicKey));
}
properties_1.defineReadOnly(this, 'parentFingerprint', parentFingerprint);
properties_1.defineReadOnly(this, 'fingerprint', bytes_1.hexDataSlice(sha2_1.ripemd160(sha2_1.sha256(this.publicKey)), 0, 4));
properties_1.defineReadOnly(this, 'address', secp256k1_1.computeAddress(this.publicKey));
properties_1.defineReadOnly(this, 'chainCode', chainCode);
properties_1.defineReadOnly(this, 'index', index);
properties_1.defineReadOnly(this, 'depth', depth);
properties_1.defineReadOnly(this, 'mnemonic', mnemonic);
properties_1.defineReadOnly(this, 'path', path);
properties_1.setType(this, 'HDNode');
}
HDNode.prototype._derive = function (index) {
// Public parent key -> public child key
if (!this.privateKey) {
if (index >= HardenedBit) {
throw new Error('cannot derive child of neutered node');
Object.defineProperty(HDNode.prototype, "extendedKey", {
get: function () {
// We only support the mainnet values for now, but if anyone needs
// testnet values, let me know. I believe current senitment is that
// we should always use mainnet, and use BIP-44 to derive the network
// - Mainnet: public=0x0488B21E, private=0x0488ADE4
// - Testnet: public=0x043587CF, private=0x04358394
if (this.depth >= 256) {
throw new Error("Depth too large!");
}
throw new Error('not implemented');
return base58check(bytes_1.concat([
((this.privateKey != null) ? "0x0488ADE4" : "0x0488B21E"),
bytes_1.hexlify(this.depth),
this.parentFingerprint,
bytes_1.hexZeroPad(bytes_1.hexlify(this.index), 4),
this.chainCode,
((this.privateKey != null) ? bytes_1.concat(["0x00", this.privateKey]) : this.publicKey),
]));
},
enumerable: true,
configurable: true
});
HDNode.prototype.neuter = function () {
return new HDNode(_constructorGuard, null, this.publicKey, this.parentFingerprint, this.chainCode, this.index, this.depth, null, this.path);
};
HDNode.prototype._derive = function (index) {
if (index > 0xffffffff) {
throw new Error("invalid index - " + String(index));
}
var data = new Uint8Array(37);
// Base path
var mnemonic = this.mnemonic;
var path = this.path;
if (path) {
path += '/' + index;
path += '/' + (index & ~HardenedBit);
}
var data = new Uint8Array(37);
if (index & HardenedBit) {
if (!this.privateKey) {
throw new Error('cannot derive child of neutered node');
}
// Data = 0x00 || ser_256(k_par)
data.set(bytes_1.arrayify(this.privateKey), 1);
// Hardened path
@@ -86,22 +126,32 @@ var HDNode = /** @class */ (function () {
}
else {
// Data = ser_p(point(k_par))
data.set(this.keyPair.publicKeyBytes);
data.set(bytes_1.arrayify(this.publicKey));
}
// Data += ser_32(i)
for (var i = 24; i >= 0; i -= 8) {
data[33 + (i >> 3)] = ((index >> (24 - i)) & 0xff);
}
var I = hmac_1.computeHmac(hmac_1.SupportedAlgorithms.sha512, this.chainCode, data);
var IL = bignumber_1.bigNumberify(I.slice(0, 32));
var IL = I.slice(0, 32);
var IR = I.slice(32);
var ki = IL.add(this.keyPair.privateKey).mod(N);
return new HDNode(_constructorGuard, bytes_1.arrayify(ki), IR, index, this.depth + 1, mnemonic, path);
// The private key
var ki = null;
// The public key
var Ki = null;
if (this.privateKey) {
ki = bytes32(bignumber_1.bigNumberify(IL).add(this.privateKey).mod(N));
}
else {
var ek = new secp256k1_1.KeyPair(bytes_1.hexlify(IL));
Ki = ek._addPoint(this.publicKey);
}
return new HDNode(_constructorGuard, ki, Ki, this.fingerprint, bytes32(IR), index, this.depth + 1, this.mnemonic, path);
};
HDNode.prototype.derivePath = function (path) {
var components = path.split('/');
if (components.length === 0 || (components[0] === 'm' && this.depth !== 0)) {
throw new Error('invalid path');
throw new Error('invalid path - ' + path);
}
if (components[0] === 'm') {
components.shift();
@@ -135,18 +185,50 @@ var HDNode = /** @class */ (function () {
return HDNode;
}());
exports.HDNode = HDNode;
function fromExtendedKey(extendedKey) {
var bytes = basex_1.Base58.decode(extendedKey);
if (bytes.length !== 82 || base58check(bytes.slice(0, 78)) !== extendedKey) {
errors.throwError("invalid extended key", errors.INVALID_ARGUMENT, {
argument: "extendedKey",
value: "[REDACTED]"
});
}
var depth = bytes[4];
var parentFingerprint = bytes_1.hexlify(bytes.slice(5, 9));
var index = parseInt(bytes_1.hexlify(bytes.slice(9, 13)).substring(2), 16);
var chainCode = bytes_1.hexlify(bytes.slice(13, 45));
var key = bytes.slice(45, 78);
switch (bytes_1.hexlify(bytes.slice(0, 4))) {
// Public Key
case "0x0488b21e":
case "0x043587cf":
return new HDNode(_constructorGuard, null, bytes_1.hexlify(key), parentFingerprint, chainCode, index, depth, null, null);
// Private Key
case "0x0488ade4":
case "0x04358394 ":
if (key[0] !== 0) {
break;
}
return new HDNode(_constructorGuard, bytes_1.hexlify(key.slice(1)), null, parentFingerprint, chainCode, index, depth, null, null);
}
return errors.throwError("invalid extended key", errors.INVALID_ARGUMENT, {
argument: "extendedKey",
value: "[REDACTED]"
});
}
exports.fromExtendedKey = fromExtendedKey;
function _fromSeed(seed, mnemonic) {
var seedArray = bytes_1.arrayify(seed);
if (seedArray.length < 16 || seedArray.length > 64) {
throw new Error('invalid seed');
}
var I = bytes_1.arrayify(hmac_1.computeHmac(hmac_1.SupportedAlgorithms.sha512, MasterSecret, seedArray));
return new HDNode(_constructorGuard, I.slice(0, 32), I.slice(32), 0, 0, mnemonic, 'm');
return new HDNode(_constructorGuard, bytes32(I.slice(0, 32)), null, "0x00000000", bytes32(I.slice(32)), 0, 0, mnemonic, 'm');
}
function fromMnemonic(mnemonic, wordlist) {
function fromMnemonic(mnemonic, wordlist, password) {
// Check that the checksum s valid (will throw an error)
mnemonicToEntropy(mnemonic, wordlist);
return _fromSeed(mnemonicToSeed(mnemonic), mnemonic);
return _fromSeed(mnemonicToSeed(mnemonic, password), mnemonic);
}
exports.fromMnemonic = fromMnemonic;
function fromSeed(seed) {
@@ -165,6 +247,7 @@ function mnemonicToEntropy(mnemonic, wordlist) {
if (!wordlist) {
wordlist = lang_en_1.langEn;
}
errors.checkNormalize();
var words = wordlist.split(mnemonic);
if ((words.length % 3) !== 0) {
throw new Error('invalid mnemonic');

7
utils/index.d.ts vendored
View File

@@ -2,7 +2,7 @@ import { AbiCoder, defaultAbiCoder, formatSignature, formatParamType, parseSigna
import { getAddress, getContractAddress, getIcapAddress } from './address';
import * as base64 from './base64';
import { BigNumber, bigNumberify } from './bignumber';
import { arrayify, concat, hexDataSlice, hexDataLength, hexlify, hexStripZeros, hexZeroPad, joinSignature, padZeros, splitSignature, stripZeros } from './bytes';
import { arrayify, concat, hexDataSlice, hexDataLength, hexlify, hexStripZeros, hexZeroPad, isHexString, joinSignature, padZeros, splitSignature, stripZeros } from './bytes';
import { hashMessage, id, namehash } from './hash';
import * as HDNode from './hdnode';
import { Interface } from './interface';
@@ -12,10 +12,11 @@ import { sha256 } from './sha2';
import { keccak256 as solidityKeccak256, pack as solidityPack, sha256 as soliditySha256 } from './solidity';
import { randomBytes } from './random-bytes';
import { getNetwork } from './networks';
import { deepCopy, defineReadOnly, resolveProperties, shallowCopy } from './properties';
import { checkProperties, deepCopy, defineReadOnly, resolveProperties, shallowCopy } from './properties';
import * as RLP from './rlp';
import { computeAddress, computePublicKey, recoverAddress, recoverPublicKey, verifyMessage } from './secp256k1';
import { SigningKey } from './signing-key';
import { populateTransaction } from './transaction';
import { parse as parseTransaction, serialize as serializeTransaction } from './transaction';
import { formatBytes32String, parseBytes32String, toUtf8Bytes, toUtf8String } from './utf8';
import { commify, formatEther, parseEther, formatUnits, parseUnits } from './units';
@@ -31,4 +32,4 @@ import { Transaction, UnsignedTransaction } from './transaction';
import { ConnectionInfo, OnceBlockable, PollOptions } from './web';
import { EncryptOptions, ProgressCallback } from './secret-storage';
import { Wordlist } from './wordlist';
export { AbiCoder, defaultAbiCoder, formatSignature, formatParamType, parseSignature, parseParamType, RLP, fetchJson, getNetwork, deepCopy, defineReadOnly, resolveProperties, shallowCopy, arrayify, concat, padZeros, stripZeros, HDNode, SigningKey, Interface, base64, BigNumber, bigNumberify, hexlify, hexStripZeros, hexZeroPad, hexDataLength, hexDataSlice, toUtf8Bytes, toUtf8String, formatBytes32String, parseBytes32String, hashMessage, namehash, id, getAddress, getIcapAddress, getContractAddress, formatEther, parseEther, formatUnits, parseUnits, commify, keccak256, sha256, randomBytes, solidityPack, solidityKeccak256, soliditySha256, splitSignature, joinSignature, parseTransaction, serializeTransaction, getJsonWalletAddress, computeAddress, computePublicKey, recoverAddress, recoverPublicKey, verifyMessage, SupportedAlgorithms, UnicodeNormalizationForm, CoerceFunc, EventFragment, FunctionFragment, ParamType, BigNumberish, Arrayish, Hexable, Signature, Indexed, DeployDescription, EventDescription, FunctionDescription, LogDescription, TransactionDescription, Network, Networkish, Transaction, UnsignedTransaction, ConnectionInfo, OnceBlockable, PollOptions, EncryptOptions, ProgressCallback, Wordlist, };
export { AbiCoder, defaultAbiCoder, formatSignature, formatParamType, parseSignature, parseParamType, RLP, fetchJson, getNetwork, checkProperties, deepCopy, defineReadOnly, resolveProperties, shallowCopy, arrayify, concat, padZeros, stripZeros, HDNode, SigningKey, Interface, base64, BigNumber, bigNumberify, hexlify, isHexString, hexStripZeros, hexZeroPad, hexDataLength, hexDataSlice, toUtf8Bytes, toUtf8String, formatBytes32String, parseBytes32String, hashMessage, namehash, id, getAddress, getIcapAddress, getContractAddress, formatEther, parseEther, formatUnits, parseUnits, commify, keccak256, sha256, randomBytes, solidityPack, solidityKeccak256, soliditySha256, splitSignature, joinSignature, parseTransaction, populateTransaction, serializeTransaction, getJsonWalletAddress, computeAddress, computePublicKey, recoverAddress, recoverPublicKey, verifyMessage, SupportedAlgorithms, UnicodeNormalizationForm, CoerceFunc, EventFragment, FunctionFragment, ParamType, BigNumberish, Arrayish, Hexable, Signature, Indexed, DeployDescription, EventDescription, FunctionDescription, LogDescription, TransactionDescription, Network, Networkish, Transaction, UnsignedTransaction, ConnectionInfo, OnceBlockable, PollOptions, EncryptOptions, ProgressCallback, Wordlist, };

View File

@@ -31,6 +31,7 @@ exports.hexDataLength = bytes_1.hexDataLength;
exports.hexlify = bytes_1.hexlify;
exports.hexStripZeros = bytes_1.hexStripZeros;
exports.hexZeroPad = bytes_1.hexZeroPad;
exports.isHexString = bytes_1.isHexString;
exports.joinSignature = bytes_1.joinSignature;
exports.padZeros = bytes_1.padZeros;
exports.splitSignature = bytes_1.splitSignature;
@@ -58,6 +59,7 @@ exports.randomBytes = random_bytes_1.randomBytes;
var networks_1 = require("./networks");
exports.getNetwork = networks_1.getNetwork;
var properties_1 = require("./properties");
exports.checkProperties = properties_1.checkProperties;
exports.deepCopy = properties_1.deepCopy;
exports.defineReadOnly = properties_1.defineReadOnly;
exports.resolveProperties = properties_1.resolveProperties;
@@ -73,8 +75,10 @@ exports.verifyMessage = secp256k1_1.verifyMessage;
var signing_key_1 = require("./signing-key");
exports.SigningKey = signing_key_1.SigningKey;
var transaction_1 = require("./transaction");
exports.parseTransaction = transaction_1.parse;
exports.serializeTransaction = transaction_1.serialize;
exports.populateTransaction = transaction_1.populateTransaction;
var transaction_2 = require("./transaction");
exports.parseTransaction = transaction_2.parse;
exports.serializeTransaction = transaction_2.serialize;
var utf8_1 = require("./utf8");
exports.formatBytes32String = utf8_1.formatBytes32String;
exports.parseBytes32String = utf8_1.parseBytes32String;

View File

@@ -56,7 +56,7 @@ var _DeployDescription = /** @class */ (function (_super) {
value: bytecode
});
}
errors.checkArgumentCount(params.length, this.inputs.length, 'in Interface constructor');
errors.checkArgumentCount(params.length, this.inputs.length, ' in Interface constructor');
try {
return (bytecode + abi_coder_1.defaultAbiCoder.encode(this.inputs, params).substring(2));
}
@@ -77,7 +77,7 @@ var _FunctionDescription = /** @class */ (function (_super) {
return _super !== null && _super.apply(this, arguments) || this;
}
_FunctionDescription.prototype.encode = function (params) {
errors.checkArgumentCount(params.length, this.inputs.length, 'in interface function ' + this.name);
errors.checkArgumentCount(params.length, this.inputs.length, ' in interface function ' + this.name);
try {
return this.sighash + abi_coder_1.defaultAbiCoder.encode(this.inputs, params).substring(2);
}
@@ -128,15 +128,17 @@ var _EventDescription = /** @class */ (function (_super) {
topics.push(this.topic);
}
params.forEach(function (arg, index) {
if (arg === null) {
topics.push(null);
return;
}
var param = _this.inputs[index];
if (!param.indexed) {
errors.throwError('cannot filter non-indexed parameters; must be null', errors.INVALID_ARGUMENT, { argument: (param.name || index), value: arg });
if (arg != null) {
errors.throwError('cannot filter non-indexed parameters; must be null', errors.INVALID_ARGUMENT, { argument: (param.name || index), value: arg });
}
return;
}
if (param.type === 'string') {
if (arg == null) {
topics.push(null);
}
else if (param.type === 'string') {
topics.push(hash_1.id(arg));
}
else if (param.type === 'bytes') {
@@ -247,12 +249,18 @@ function addMethod(method) {
gas: method.gas,
payable: (method.payable == null || !!method.payable),
type: ((method.constant) ? 'call' : 'transaction'),
name: method.name,
signature: signature,
sighash: sighash,
});
// Expose the first (and hopefully unique named function
if (method.name && this.functions[method.name] == null) {
properties_1.defineReadOnly(this.functions, method.name, description);
// Expose the first (and hopefully unique named function)
if (method.name) {
if (this.functions[method.name] == null) {
properties_1.defineReadOnly(this.functions, method.name, description);
}
else {
errors.warn('WARNING: Multiple definitions for ' + method.name);
}
}
// Expose all methods by their signature, for overloaded functions
if (this.functions[description.signature] == null) {
@@ -283,7 +291,7 @@ function addMethod(method) {
// Nothing to do for fallback
break;
default:
console.log('WARNING: unsupported ABI type - ' + method.type);
errors.warn('WARNING: unsupported ABI type - ' + method.type);
break;
}
}
@@ -337,10 +345,10 @@ var Interface = /** @class */ (function () {
return new _TransactionDescription({
args: result,
decode: func.decode,
name: name,
name: func.name,
signature: func.signature,
sighash: func.sighash,
value: bignumber_1.bigNumberify(tx.value || null),
value: bignumber_1.bigNumberify(tx.value || '0'),
});
}
}

1
utils/networks.d.ts vendored
View File

@@ -2,6 +2,7 @@ export declare type Network = {
name: string;
chainId: number;
ensAddress?: string;
_defaultProvider?: (providers: any) => any;
};
export declare type Networkish = Network | string | number;
/**

View File

@@ -8,39 +8,84 @@ var __importStar = (this && this.__importStar) || function (mod) {
};
Object.defineProperty(exports, "__esModule", { value: true });
var errors = __importStar(require("../errors"));
function ethDefaultProvider(network) {
return function (providers) {
var providerList = [];
if (providers.InfuraProvider) {
providerList.push(new providers.InfuraProvider(network));
}
if (providers.EtherscanProvider) {
providerList.push(new providers.EtherscanProvider(network));
}
if (providerList.length === 0) {
return null;
}
if (providers.FallbackProvider) {
return new providers.FallbackProvider(providerList);
;
}
return providerList[0];
};
}
function etcDefaultProvider(url, network) {
return function (providers) {
if (providers.JsonRpcProvider) {
return new providers.JsonRpcProvider(url, network);
}
return null;
};
}
var homestead = {
chainId: 1,
ensAddress: "0x314159265dd8dbb310642f98f50c066173c1259b",
name: "homestead"
name: "homestead",
_defaultProvider: ethDefaultProvider('homestead')
};
var ropsten = {
chainId: 3,
ensAddress: "0x112234455c3a32fd11230c42e7bccd4a84e02010",
name: "ropsten"
name: "ropsten",
_defaultProvider: ethDefaultProvider('ropsten')
};
var networks = {
unspecified: {
chainId: 0
chainId: 0,
name: 'unspecified'
},
homestead: homestead,
mainnet: homestead,
morden: {
chainId: 2
chainId: 2,
name: 'morden'
},
ropsten: ropsten,
testnet: ropsten,
rinkeby: {
chainId: 4,
ensAddress: "0xe7410170f87102DF0055eB195163A03B7F2Bff4A"
ensAddress: "0xe7410170f87102DF0055eB195163A03B7F2Bff4A",
name: 'rinkeby',
_defaultProvider: ethDefaultProvider('rinkeby')
},
goerli: {
chainId: 5,
ensAddress: "0x112234455c3a32fd11230c42e7bccd4a84e02010",
name: "goerli",
_defaultProvider: ethDefaultProvider('goerli')
},
kovan: {
chainId: 42
chainId: 42,
name: 'kovan',
_defaultProvider: ethDefaultProvider('kovan')
},
classic: {
chainId: 61
chainId: 61,
name: 'classic',
_defaultProvider: etcDefaultProvider('https://web3.gastracker.io', 'classic')
},
classicTestnet: {
chainId: 62
chainId: 62,
name: 'classicTestnet',
_defaultProvider: etcDefaultProvider('https://web3.gastracker.io/morden', 'classicTestnet')
}
};
/**
@@ -50,18 +95,19 @@ var networks = {
* and verifies a network is a valid Network..
*/
function getNetwork(network) {
// No network (null) or unspecified (chainId = 0)
if (!network) {
// No network (null)
if (network == null) {
return null;
}
if (typeof (network) === 'number') {
for (var name in networks) {
var n_1 = networks[name];
for (var name_1 in networks) {
var n_1 = networks[name_1];
if (n_1.chainId === network) {
return {
name: name,
name: n_1.name,
chainId: n_1.chainId,
ensAddress: n_1.ensAddress
ensAddress: (n_1.ensAddress || null),
_defaultProvider: (n_1._defaultProvider || null)
};
}
}
@@ -76,9 +122,10 @@ function getNetwork(network) {
return null;
}
return {
name: network,
name: n_2.name,
chainId: n_2.chainId,
ensAddress: n_2.ensAddress
ensAddress: n_2.ensAddress,
_defaultProvider: (n_2._defaultProvider || null)
};
}
var n = networks[network.name];
@@ -93,11 +140,12 @@ function getNetwork(network) {
if (network.chainId !== 0 && network.chainId !== n.chainId) {
errors.throwError('network chainId mismatch', errors.INVALID_ARGUMENT, { arg: 'network', value: network });
}
// Standard Network
// Standard Network (allow overriding the ENS address)
return {
name: network.name,
chainId: n.chainId,
ensAddress: n.ensAddress
ensAddress: (network.ensAddress || n.ensAddress || null),
_defaultProvider: (network._defaultProvider || n._defaultProvider || null)
};
}
exports.getNetwork = getNetwork;

View File

@@ -2,6 +2,9 @@ export declare function defineReadOnly(object: any, name: string, value: any): v
export declare function setType(object: any, type: string): void;
export declare function isType(object: any, type: string): boolean;
export declare function resolveProperties(object: any): Promise<any>;
export declare function checkProperties(object: any, properties: {
[name: string]: boolean;
}): void;
export declare function shallowCopy(object: any): any;
export declare function deepCopy(object: any, frozen?: boolean): any;
export declare function inheritable(parent: any): (child: any) => void;

View File

@@ -1,5 +1,13 @@
'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 errors = __importStar(require("../errors"));
function defineReadOnly(object, name, value) {
Object.defineProperty(object, name, {
enumerable: true,
@@ -38,6 +46,24 @@ function resolveProperties(object) {
});
}
exports.resolveProperties = resolveProperties;
function checkProperties(object, properties) {
if (!object || typeof (object) !== 'object') {
errors.throwError('invalid object', errors.INVALID_ARGUMENT, {
argument: 'object',
value: object
});
}
Object.keys(object).forEach(function (key) {
if (!properties[key]) {
errors.throwError('invalid object key - ' + key, errors.INVALID_ARGUMENT, {
argument: 'transaction',
value: object,
key: key
});
}
});
}
exports.checkProperties = checkProperties;
function shallowCopy(object) {
var result = {};
for (var key in object) {

View File

@@ -7,6 +7,7 @@ export declare class KeyPair {
constructor(privateKey: Arrayish | string);
sign(digest: Arrayish | string): Signature;
computeSharedSecret(otherKey: Arrayish | string): string;
_addPoint(other: Arrayish | string): string;
}
export declare function computePublicKey(key: Arrayish | string, compressed?: boolean): string;
export declare function computeAddress(key: Arrayish | string): string;

View File

@@ -45,6 +45,11 @@ var KeyPair = /** @class */ (function () {
var otherKeyPair = getCurve().keyFromPublic(bytes_1.arrayify(computePublicKey(otherKey)));
return bytes_1.hexZeroPad('0x' + keyPair.derive(otherKeyPair.getPublic()).toString(16), 32);
};
KeyPair.prototype._addPoint = function (other) {
var p0 = getCurve().keyFromPublic(bytes_1.arrayify(this.publicKey));
var p1 = getCurve().keyFromPublic(bytes_1.arrayify(other));
return "0x" + p0.pub.add(p1.pub).encodeCompressed("hex");
};
return KeyPair;
}());
exports.KeyPair = KeyPair;

View File

@@ -384,6 +384,7 @@ function encrypt(privateKey, password, options, progressCallback) {
gethFilename: ('UTC--' + timestamp + '--' + data.address),
mnemonicCounter: bytes_1.hexlify(mnemonicIv).substring(2),
mnemonicCiphertext: bytes_1.hexlify(mnemonicCiphertext).substring(2),
path: path,
version: "0.1"
};
}

1
utils/sha2.d.ts vendored
View File

@@ -1,3 +1,4 @@
import { Arrayish } from './bytes';
export declare function ripemd160(data: Arrayish): string;
export declare function sha256(data: Arrayish): string;
export declare function sha512(data: Arrayish): string;

View File

@@ -5,6 +5,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
Object.defineProperty(exports, "__esModule", { value: true });
var hash_js_1 = __importDefault(require("hash.js"));
var bytes_1 = require("./bytes");
function ripemd160(data) {
return '0x' + (hash_js_1.default.ripemd160().update(bytes_1.arrayify(data)).digest('hex'));
}
exports.ripemd160 = ripemd160;
function sha256(data) {
return '0x' + (hash_js_1.default.sha256().update(bytes_1.arrayify(data)).digest('hex'));
}

View File

@@ -1,6 +1,7 @@
import { BigNumber } from './bignumber';
import { Arrayish, Signature } from './bytes';
import { BigNumberish } from './bignumber';
import { Provider } from '../providers/abstract-provider';
export declare type UnsignedTransaction = {
to?: string;
nonce?: number;
@@ -26,3 +27,4 @@ export interface Transaction {
}
export declare function serialize(transaction: UnsignedTransaction, signature?: Arrayish | Signature): string;
export declare function parse(rawTransaction: Arrayish): Transaction;
export declare function populateTransaction(transaction: any, provider: Provider, from: string | Promise<string>): Promise<Transaction>;

View File

@@ -14,7 +14,9 @@ var address_1 = require("./address");
var bignumber_1 = require("./bignumber");
var bytes_1 = require("./bytes");
var keccak256_1 = require("./keccak256");
var properties_1 = require("./properties");
var RLP = __importStar(require("./rlp"));
var abstract_provider_1 = require("../providers/abstract-provider");
///////////////////////////////
function handleAddress(value) {
if (value === '0x') {
@@ -36,7 +38,11 @@ var transactionFields = [
{ name: 'value', maxLength: 32 },
{ name: 'data' },
];
var allowedTransactionKeys = {
chainId: true, data: true, gasLimit: true, gasPrice: true, nonce: true, to: true, value: true
};
function serialize(transaction, signature) {
properties_1.checkProperties(transaction, allowedTransactionKeys);
var raw = [];
transactionFields.forEach(function (fieldInfo) {
var value = transaction[fieldInfo.name] || ([]);
@@ -103,7 +109,7 @@ function parse(rawTransaction) {
tx.v = bignumber_1.bigNumberify(transaction[6]).toNumber();
}
catch (error) {
console.log(error);
errors.info(error);
return tx;
}
tx.r = bytes_1.hexZeroPad(transaction[7], 32);
@@ -132,10 +138,39 @@ function parse(rawTransaction) {
tx.from = secp256k1_1.recoverAddress(digest, { r: bytes_1.hexlify(tx.r), s: bytes_1.hexlify(tx.s), recoveryParam: recoveryParam });
}
catch (error) {
console.log(error);
errors.info(error);
}
tx.hash = keccak256_1.keccak256(rawTransaction);
}
return tx;
}
exports.parse = parse;
function populateTransaction(transaction, provider, from) {
if (!abstract_provider_1.Provider.isProvider(provider)) {
errors.throwError('missing provider', errors.INVALID_ARGUMENT, {
argument: 'provider',
value: provider
});
}
properties_1.checkProperties(transaction, allowedTransactionKeys);
var tx = properties_1.shallowCopy(transaction);
if (tx.to != null) {
tx.to = provider.resolveName(tx.to);
}
if (tx.gasPrice == null) {
tx.gasPrice = provider.getGasPrice();
}
if (tx.nonce == null) {
tx.nonce = provider.getTransactionCount(from);
}
if (tx.gasLimit == null) {
var estimate = properties_1.shallowCopy(tx);
estimate.from = from;
tx.gasLimit = provider.estimateGas(estimate);
}
if (tx.chainId == null) {
tx.chainId = provider.getNetwork().then(function (network) { return network.chainId; });
}
return properties_1.resolveProperties(tx);
}
exports.populateTransaction = populateTransaction;

View File

@@ -1,6 +1,7 @@
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
var constants_1 = require("../constants");
var errors_1 = require("../errors");
var bytes_1 = require("./bytes");
///////////////////////////////
var UnicodeNormalizationForm;
@@ -16,6 +17,7 @@ var UnicodeNormalizationForm;
function toUtf8Bytes(str, form) {
if (form === void 0) { form = UnicodeNormalizationForm.current; }
if (form != UnicodeNormalizationForm.current) {
errors_1.checkNormalize();
str = str.normalize(form);
}
var result = [];

1
utils/web.d.ts vendored
View File

@@ -17,6 +17,7 @@ export declare type PollOptions = {
ceiling?: number;
interval?: number;
onceBlock?: OnceBlockable;
fastRetry?: number;
};
export declare function fetchJson(connection: string | ConnectionInfo, json: string, processFunc: (value: any) => any): Promise<any>;
export declare function poll(func: () => Promise<any>, options?: PollOptions): Promise<any>;

View File

@@ -9,6 +9,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
Object.defineProperty(exports, "__esModule", { value: true });
var xmlhttprequest_1 = require("xmlhttprequest");
var base64_1 = require("./base64");
var properties_1 = require("./properties");
var utf8_1 = require("./utf8");
var errors = __importStar(require("../errors"));
function fetchJson(connection, json, processFunc) {
@@ -82,6 +83,9 @@ function fetchJson(connection, json, processFunc) {
// @TODO: not any!
var error = new Error('invalid response - ' + request.status);
error.statusCode = request.status;
if (request.responseText) {
error.responseText = request.responseText;
}
reject(error);
return;
}
@@ -95,6 +99,9 @@ function fetchJson(connection, json, processFunc) {
var jsonError = new Error('invalid json response');
jsonError.orginialError = error;
jsonError.responseText = request.responseText;
if (json != null) {
jsonError.requestBody = json;
}
jsonError.url = url;
reject(jsonError);
return;
@@ -120,7 +127,7 @@ function fetchJson(connection, json, processFunc) {
reject(error);
};
try {
if (json) {
if (json != null) {
request.send(json);
}
else {
@@ -141,6 +148,7 @@ function poll(func, options) {
if (!options) {
options = {};
}
options = properties_1.shallowCopy(options);
if (options.floor == null) {
options.floor = 0;
}
@@ -171,6 +179,7 @@ function poll(func, options) {
}
}, options.timeout);
}
var fastTimeout = options.fastRetry || null;
var attempt = 0;
function check() {
return func().then(function (result) {
@@ -193,6 +202,12 @@ function poll(func, options) {
if (timeout > options.ceiling) {
timeout = options.ceiling;
}
// Fast Timeout, means we quickly try again the first time
if (fastTimeout) {
attempt--;
timeout = fastTimeout;
fastTimeout = null;
}
setTimeout(check, timeout);
}
return null;

2
wallet.d.ts vendored
View File

@@ -30,6 +30,6 @@ export declare class Wallet extends AbstractSigner {
* Static methods to create Wallet instances.
*/
static createRandom(options?: any): Wallet;
static fromEncryptedJson(json: string, password: Arrayish, progressCallback: ProgressCallback): Promise<Wallet>;
static fromEncryptedJson(json: string, password: Arrayish, progressCallback?: ProgressCallback): Promise<Wallet>;
static fromMnemonic(mnemonic: string, path?: string, wordlist?: Wordlist): Wallet;
}

View File

@@ -57,7 +57,7 @@ var Wallet = /** @class */ (function (_super) {
configurable: true
});
Object.defineProperty(Wallet.prototype, "path", {
get: function () { return this.signingKey.mnemonic; },
get: function () { return this.signingKey.path; },
enumerable: true,
configurable: true
});
@@ -83,7 +83,7 @@ var Wallet = /** @class */ (function (_super) {
return properties_1.resolveProperties(transaction).then(function (tx) {
var rawTx = transaction_1.serialize(tx);
var signature = _this.signingKey.signDigest(keccak256_1.keccak256(rawTx));
return Promise.resolve(transaction_1.serialize(tx, signature));
return transaction_1.serialize(tx, signature);
});
};
Wallet.prototype.signMessage = function (message) {
@@ -102,30 +102,19 @@ var Wallet = /** @class */ (function (_super) {
return this.provider.getTransactionCount(this.address, blockTag);
};
Wallet.prototype.sendTransaction = function (transaction) {
var _this = this;
if (!this.provider) {
throw new Error('missing provider');
}
if (!transaction || typeof (transaction) !== 'object') {
throw new Error('invalid transaction object');
if (transaction.nonce == null) {
transaction = properties_1.shallowCopy(transaction);
transaction.nonce = this.getTransactionCount("pending");
}
var tx = properties_1.shallowCopy(transaction);
if (tx.to != null) {
tx.to = this.provider.resolveName(tx.to);
}
if (tx.gasLimit == null) {
tx.from = this.getAddress();
tx.gasLimit = this.provider.estimateGas(tx);
}
if (tx.gasPrice == null) {
tx.gasPrice = this.provider.getGasPrice();
}
if (tx.nonce == null) {
tx.nonce = this.getTransactionCount();
}
if (tx.chainId == null) {
tx.chainId = this.provider.getNetwork().then(function (network) { return network.chainId; });
}
return this.provider.sendTransaction(this.sign(tx));
return transaction_1.populateTransaction(transaction, this.provider, this.address).then(function (tx) {
return _this.sign(tx).then(function (signedTransaction) {
return _this.provider.sendTransaction(signedTransaction);
});
});
};
Wallet.prototype.encrypt = function (password, options, progressCallback) {
if (typeof (options) === 'function' && !progressCallback) {

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