Compare commits

...

66 Commits

Author SHA1 Message Date
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
84 changed files with 2587 additions and 646 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.10";
export declare const version = "4.0.26";

View File

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

5
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';
@@ -50,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

@@ -106,7 +106,6 @@ function resolveAddresses(provider, value, paramType) {
function runMethod(contract, functionName, estimateOnly) {
var method = contract.interface.functions[functionName];
return function () {
var _this = this;
var params = [];
for (var _i = 0; _i < arguments.length; _i++) {
params[_i] = arguments[_i];
@@ -136,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) {
@@ -218,19 +216,19 @@ function runMethod(contract, functionName, estimateOnly) {
return wait(confirmations).then(function (receipt) {
receipt.events = receipt.logs.map(function (log) {
var event = properties_1.deepCopy(log);
var parsed = _this.interface.parseLog(log);
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 _this.provider; };
event.removeListener = function () { return contract.provider; };
event.getBlock = function () {
return _this.provider.getBlock(receipt.blockHash);
return contract.provider.getBlock(receipt.blockHash);
};
event.getTransaction = function () {
return _this.provider.getTransaction(receipt.transactionHash);
return contract.provider.getTransaction(receipt.transactionHash);
};
event.getTransactionReceipt = function () {
return Promise.resolve(receipt);
@@ -305,7 +303,6 @@ var Contract = /** @class */ (function () {
}
return address;
}).catch(function (error) {
console.log('ERROR: Cannot find Contract - ' + addressOrName);
throw error;
}));
}
@@ -324,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);
@@ -334,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;
});
}
@@ -346,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,
@@ -357,7 +357,7 @@ var Contract = /** @class */ (function () {
});
}
}
return this._deployed;
return this._deployedPromise;
};
// @TODO:
// estimateFallback(overrides?: TransactionRequest): Promise<BigNumber>
@@ -556,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;
};
@@ -655,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;

1064
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

51
dist/ethers.types.txt vendored
View File

@@ -26,7 +26,7 @@ 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';
@@ -77,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;
@@ -189,6 +190,9 @@ declare module 'ethers/errors' {
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' {
@@ -210,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';
@@ -240,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, checkProperties, 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, 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, };
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' {
@@ -262,7 +266,7 @@ declare module 'ethers/utils/shims' {
}
declare module 'ethers/_version' {
export const version = "4.0.10";
export const version = "4.0.26";
}
declare module 'ethers/utils/bignumber' {
@@ -273,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;
@@ -470,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;
@@ -483,6 +493,8 @@ declare module 'ethers/providers/abstract-provider' {
logIndex?: number;
}
export interface TransactionReceipt {
to?: string;
from?: string;
contractAddress?: string;
transactionIndex?: number;
root?: string;
@@ -500,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>;
@@ -532,7 +544,7 @@ declare module 'ethers/providers/abstract-provider' {
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;
@@ -626,6 +638,8 @@ 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;
@@ -639,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;
@@ -711,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
*
@@ -750,7 +769,7 @@ declare module 'ethers/providers/base-provider' {
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>;
@@ -765,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;
}
}
@@ -809,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>>;
@@ -830,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>;
@@ -905,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;
}
@@ -924,6 +946,7 @@ declare module 'ethers/utils/networks' {
name: string;
chainId: number;
ensAddress?: string;
_defaultProvider?: (providers: any) => any;
};
export type Networkish = Network | string | number;
/**
@@ -964,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;
@@ -1017,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>;

2
dist/shims.js vendored

File diff suppressed because one or more lines are too long

3
errors.d.ts vendored
View File

@@ -15,3 +15,6 @@ 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,7 +102,7 @@ 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;
@@ -110,12 +110,54 @@ function setCensorship(censorship, 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');
throw new Error('broken implementation');
}
}
catch (error) {
throwError('platform missing String.prototype.normalize', exports.UNSUPPORTED_OPERATION, { operation: 'String.prototype.normalize' });
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;

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,

2
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "ethers",
"version": "4.0.10",
"version": "4.0.27",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "ethers",
"version": "4.0.10",
"version": "4.0.27",
"description": "Ethereum wallet library.",
"main": "./index.js",
"types": "./index.d.ts",

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;
@@ -54,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>;
@@ -86,7 +93,7 @@ export declare abstract class Provider implements OnceBlockable {
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,13 +1,15 @@
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;
@@ -49,7 +51,7 @@ export declare class BaseProvider extends Provider {
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;
@@ -67,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;
@@ -202,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();
}
@@ -270,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),
@@ -314,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 = {
@@ -405,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);
@@ -422,11 +444,7 @@ 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;
}
@@ -438,34 +456,50 @@ var BaseProvider = /** @class */ (function (_super) {
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;
}
var _loop_1 = function (i) {
if (_this._emitted.block < i) {
_this._emitted.block = i;
// Notify all listener for each block that has passed
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);
};
// Notify all listener for each block that has passed
for (var i = _this._lastBlockNumber + 1; i <= blockNumber; i++) {
_loop_1(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];
@@ -485,12 +519,12 @@ 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;
@@ -528,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 () {
@@ -543,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
@@ -620,17 +653,24 @@ var BaseProvider = /** @class */ (function (_super) {
// this will be used once we move to the WebSocket or other alternatives to polling
BaseProvider.prototype.waitForTransaction = function (transactionHash, confirmations) {
var _this = this;
if (!confirmations) {
if (confirmations == null) {
confirmations = 1;
}
return web_1.poll(function () {
return _this.getTransactionReceipt(transactionHash).then(function (receipt) {
if (receipt == null || receipt.confirmations < confirmations) {
return undefined;
}
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;
@@ -742,10 +782,20 @@ 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 (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: tx.hash,
@@ -823,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;
@@ -842,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) {
@@ -877,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) {
@@ -1056,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 () {
};
@@ -1123,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;
}
@@ -1139,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');
}
@@ -226,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;
}

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;
@@ -41,19 +46,31 @@ var InfuraProvider = /** @class */ (function (_super) {
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>;

View File

@@ -25,7 +25,6 @@ var address_1 = require("../utils/address");
var bytes_1 = require("../utils/bytes");
var networks_1 = require("../utils/networks");
var properties_1 = require("../utils/properties");
var transaction_1 = require("../utils/transaction");
var utf8_1 = require("../utils/utf8");
var web_1 = require("../utils/web");
function timer(timeout) {
@@ -97,31 +96,32 @@ var JsonRpcSigner = /** @class */ (function (_super) {
JsonRpcSigner.prototype.getTransactionCount = function (blockTag) {
return this.provider.getTransactionCount(this.getAddress(), blockTag);
};
JsonRpcSigner.prototype.sendTransaction = function (transaction) {
JsonRpcSigner.prototype.sendUncheckedTransaction = function (transaction) {
var _this = this;
// Once populateTransaction resolves, the from address will be populated from getAddress
var from = null;
var getAddress = this.getAddress().then(function (address) {
transaction = properties_1.shallowCopy(transaction);
var fromAddress = this.getAddress().then(function (address) {
if (address) {
from = address.toLowerCase();
address = address.toLowerCase();
}
return from;
return address;
});
return transaction_1.populateTransaction(transaction, this.provider, getAddress).then(function (tx) {
// 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 = from;
hexTx.from = results[1];
return _this.provider.send('eth_sendTransaction', [hexTx]).then(function (hash) {
return web_1.poll(function () {
return _this.provider.getTransaction(hash).then(function (tx) {
if (tx === null) {
return undefined;
}
return _this.provider._wrapTransaction(tx, hash);
});
}, { onceBlock: _this.provider }).catch(function (error) {
error.transactionHash = hash;
throw error;
});
return hash;
}, function (error) {
if (error.responseText) {
// See: JsonRpcProvider.sendTransaction (@TODO: Expose a ._throwError??)
@@ -145,6 +145,22 @@ var JsonRpcSigner = /** @class */ (function (_super) {
});
});
};
JsonRpcSigner.prototype.sendTransaction = function (transaction) {
var _this = this;
return this.sendUncheckedTransaction(transaction).then(function (hash) {
return web_1.poll(function () {
return _this.provider.getTransaction(hash).then(function (tx) {
if (tx === null) {
return undefined;
}
return _this.provider._wrapTransaction(tx, hash);
});
}, { fastRetry: 250, onceBlock: _this.provider }).catch(function (error) {
error.transactionHash = hash;
throw error;
});
});
};
JsonRpcSigner.prototype.signMessage = function (message) {
var _this = this;
var data = ((typeof (message) === 'string') ? utf8_1.toUtf8Bytes(message) : message);
@@ -308,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) {

View File

@@ -1 +1 @@
export const version = "4.0.10";
export const version = "4.0.26";

View File

@@ -181,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;
});
@@ -211,7 +210,6 @@ function runMethod(contract: Contract, functionName: string, estimateOnly: boole
}
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, {
@@ -280,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;
@@ -288,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);
@@ -360,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
@@ -411,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 {
@@ -429,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) {
@@ -441,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;
});
@@ -454,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,
@@ -466,7 +467,7 @@ export class Contract {
}
}
return this._deployed;
return this._deployedPromise;
}
// @TODO:
@@ -697,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;

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,7 +116,7 @@ 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;
@@ -125,10 +125,44 @@ export function setCensorship(censorship: boolean, permanent?: boolean): void {
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')
throw new Error('broken implementation')
}
} catch (error) {
throwError('platform missing String.prototype.normalize', UNSUPPORTED_OPERATION, { operation: 'String.prototype.normalize' });
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>,
@@ -132,7 +144,7 @@ export abstract class Provider implements OnceBlockable {
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
@@ -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;
@@ -220,11 +224,15 @@ function checkTransactionResponse(transaction: any): TransactionResponse {
}
}
let result = check(formatTransaction, transaction);
let 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),
@@ -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);
}
@@ -522,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) {
@@ -598,8 +620,13 @@ export class BaseProvider extends Provider {
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];
@@ -749,14 +776,21 @@ export class BaseProvider extends Provider {
waitForTransaction(transactionHash: string, confirmations?: number): Promise<TransactionReceipt> {
if (confirmations == null) { confirmations = 1; }
return poll(() => {
return this.getTransactionReceipt(transactionHash).then((receipt) => {
if (receipt == null && confirmations !== 0) {
if (receipt.confirmations < confirmations) { return undefined; }
}
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> {
@@ -874,6 +908,7 @@ export class BaseProvider extends Provider {
}
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;
@@ -977,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) {
@@ -1014,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) {
@@ -1051,7 +1086,7 @@ 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) => {
@@ -1211,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 {
@@ -1277,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;
@@ -1294,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;
});

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

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

@@ -12,8 +12,7 @@ import { getAddress } from '../utils/address';
import { BigNumber } from '../utils/bignumber';
import { hexlify, hexStripZeros } from '../utils/bytes';
import { getNetwork } from '../utils/networks';
import { checkProperties, defineReadOnly, shallowCopy } from '../utils/properties';
import { populateTransaction } from '../utils/transaction';
import { checkProperties, defineReadOnly, resolveProperties, shallowCopy } from '../utils/properties';
import { toUtf8Bytes } from '../utils/utf8';
import { fetchJson, poll } from '../utils/web';
@@ -35,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;
@@ -102,28 +101,32 @@ export class JsonRpcSigner extends Signer {
return this.provider.getTransactionCount(this.getAddress(), blockTag);
}
sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse> {
sendUncheckedTransaction(transaction: TransactionRequest): Promise<string> {
transaction = shallowCopy(transaction);
// Once populateTransaction resolves, the from address will be populated from getAddress
let from: string = null;
let getAddress = this.getAddress().then((address) => {
if (address) { from = address.toLowerCase(); }
return from;
let fromAddress = this.getAddress().then((address) => {
if (address) { address = address.toLowerCase(); }
return address;
});
return populateTransaction(transaction, this.provider, getAddress).then((tx) => {
// The JSON-RPC for eth_sendTransaction uses 90000 gas; if the user
// wishes to use this, it is easy to specify explicitly, otherwise
// we look it up for them.
if (transaction.gasLimit == null) {
let estimate = shallowCopy(transaction);
estimate.from = fromAddress;
transaction.gasLimit = this.provider.estimateGas(estimate);
}
return Promise.all([
resolveProperties(transaction),
fromAddress
]).then((results) => {
let tx = results[0];
let hexTx = JsonRpcProvider.hexlifyTransaction(tx);
hexTx.from = from;
hexTx.from = results[1];
return this.provider.send('eth_sendTransaction', [ hexTx ]).then((hash) => {
return poll(() => {
return this.provider.getTransaction(hash).then((tx: TransactionResponse) => {
if (tx === null) { return undefined; }
return this.provider._wrapTransaction(tx, hash);
});
}, { onceBlock: this.provider }).catch((error: Error) => {
(<any>error).transactionHash = hash;
throw error;
});
return hash;
}, (error) => {
if (error.responseText) {
// See: JsonRpcProvider.sendTransaction (@TODO: Expose a ._throwError??)
@@ -148,8 +151,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
@@ -158,7 +175,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 ]);
@@ -323,9 +340,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) {
@@ -333,7 +350,7 @@ 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';

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

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 { computeAddress, KeyPair } from './secp256k1';
import { sha256 } from './sha2';
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,26 @@ 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;
@@ -71,21 +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, 'address', computeAddress(this.publicKey));
defineReadOnly(this, 'chainCode', hexlify(chainCode));
defineReadOnly(this, 'chainCode', chainCode);
defineReadOnly(this, 'index', index);
defineReadOnly(this, 'depth', depth);
@@ -96,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);
@@ -120,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 {
@@ -168,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 {
@@ -193,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'));
}
@@ -202,18 +283,18 @@ export function mnemonicToEntropy(mnemonic: string, wordlist?: Wordlist): string
if (!wordlist) { wordlist = langEn; }
errors.checkNormalize();
var words = wordlist.split(mnemonic);
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)));
}
@@ -221,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)) {
@@ -243,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) {
@@ -268,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';
@@ -84,6 +84,7 @@ export {
bigNumberify,
hexlify,
isHexString,
hexStripZeros,
hexZeroPad,
hexDataLength,

View File

@@ -320,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
@@ -367,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;
}
}
@@ -433,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

@@ -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

@@ -162,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;
}
@@ -195,7 +195,7 @@ export function parse(rawTransaction: Arrayish): Transaction {
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);

View File

@@ -28,7 +28,8 @@ export type PollOptions = {
floor?: number,
ceiling?: number,
interval?: number,
onceBlock?: OnceBlockable
onceBlock?: OnceBlockable,
fastRetry?: number
};
@@ -135,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;
@@ -163,7 +167,7 @@ export function fetchJson(connection: string | ConnectionInfo, json: string, pro
}
try {
if (json) {
if (json != null) {
request.send(json);
} else {
request.send();
@@ -205,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) {
@@ -224,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

@@ -47,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; }
@@ -62,7 +62,6 @@ export class Wallet extends AbstractSigner {
return new Wallet(this.signingKey, provider);
}
getAddress(): Promise<string> {
return Promise.resolve(this.address);
}
@@ -91,6 +90,13 @@ export class Wallet extends AbstractSigner {
}
sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse> {
if (!this.provider) { throw new Error('missing provider'); }
if (transaction.nonce == null) {
transaction = shallowCopy(transaction);
transaction.nonce = this.getTransactionCount("pending");
}
return populateTransaction(transaction, this.provider, this.address).then((tx) => {
return this.sign(tx).then((signedTransaction) => {
return this.provider.sendTransaction(signedTransaction);

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"
}
]
}

View File

@@ -1,14 +1,33 @@
'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');
console.log("Broken String.prototype.normalize... Forcing shim.");
String.prototype.normalize = function(form) {
var func = unorm[(form || 'NFC').toLowerCase()];
if (!func) { throw new RangeError('invalid form - ' + form); }
@@ -18,14 +37,22 @@ try {
// Shim atob and btoa
var base64 = require('./base64.js');
if (!global.atob) { global.atob = base64.atob; }
if (!global.btoa) { global.btoa = base64.btoa; }
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);
@@ -34,6 +61,13 @@ if (!ArrayBuffer.isView) {
// 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() {

View File

@@ -9,21 +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);
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);
});
});
});

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,

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;

10
utils/hdnode.d.ts vendored
View File

@@ -2,9 +2,10 @@ 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;
@@ -18,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,38 +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);
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', bytes_1.hexlify(chainCode));
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
@@ -87,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();
@@ -136,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) {

4
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';
@@ -32,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, checkProperties, 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, 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, };
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;

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);
}
@@ -249,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) {
@@ -285,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;
}
}
@@ -339,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,89 @@ 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: function (providers) {
if (providers.EtherscanProvider) {
return new providers.EtherscanProvider("goerli");
}
return null;
}
},
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 +100,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 +127,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 +145,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

@@ -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

@@ -109,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);
@@ -138,7 +138,7 @@ 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);
}

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

@@ -99,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;
@@ -124,7 +127,7 @@ function fetchJson(connection, json, processFunc) {
reject(error);
};
try {
if (json) {
if (json != null) {
request.send(json);
}
else {
@@ -176,6 +179,7 @@ function poll(func, options) {
}
}, options.timeout);
}
var fastTimeout = options.fastRetry || null;
var attempt = 0;
function check() {
return func().then(function (result) {
@@ -198,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;

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
});
@@ -103,6 +103,13 @@ var Wallet = /** @class */ (function (_super) {
};
Wallet.prototype.sendTransaction = function (transaction) {
var _this = this;
if (!this.provider) {
throw new Error('missing provider');
}
if (transaction.nonce == null) {
transaction = properties_1.shallowCopy(transaction);
transaction.nonce = this.getTransactionCount("pending");
}
return transaction_1.populateTransaction(transaction, this.provider, this.address).then(function (tx) {
return _this.sign(tx).then(function (signedTransaction) {
return _this.provider.sendTransaction(signedTransaction);