Compare commits

...

59 Commits

Author SHA1 Message Date
Richard Moore
b5f720ace6 Ganache does not include from in receipts (#400). 2019-01-24 16:52:24 -05:00
Richard Moore
9f201c386e Updated dist files. 2019-01-23 16:30:12 -05:00
Richard Moore
700dd34137 Added to and from for Transaction Receipts (#398). 2019-01-23 16:25:49 -05:00
Richard Moore
f2dd977de4 Added v3 INFURA end-points to InfuraProvider (#286). 2019-01-23 16:04:54 -05:00
Richard Moore
5f013216c5 Fixed long-response bug in IpcProvider (#384). 2019-01-21 17:03:52 -05:00
Richard Moore
eac0805435 Updated dist files. 2019-01-17 16:34:17 -05:00
Richard Moore
e5bee7e5a3 Fixed path for x-ethers metadata and wallet (#). 2019-01-17 16:32:51 -05:00
Richard Moore
442553620a Updated dist files. 2019-01-15 16:01:45 -05:00
Richard Moore
6d08968b87 Fixed contract removeAllListeners which did not clean up the event loop properly (#391). 2019-01-15 15:58:42 -05:00
Richard Moore
c2ce59f95e Updated dist files. 2018-12-27 15:53:00 -05:00
Richard Moore
f3ec27b95f Added customizable log levels to quiet warnings (#379). 2018-12-27 15:48:38 -05:00
Richard Moore
c88cb5ea90 Updated dist files. 2018-12-14 18:39:46 -05:00
Richard Moore
99a21660ab Allow unchecked transactions which will remain unwrapped for the JsonRpcSigner (#340). 2018-12-14 18:36:24 -05:00
Richard Moore
4bc62a1e8a Make it easier for sub-classes of Wallet to manage nonces. 2018-12-14 18:32:48 -05:00
Richard Moore
26eb6cc01a Updated dist files. 2018-12-12 16:59:25 -05:00
Richard Moore
bcba17a9e7 Allow nonce to be a BigNumber (#228). 2018-12-12 16:56:50 -05:00
Richard Moore
918b66bc2e Fixed typo in error strings (#376). 2018-12-12 16:31:23 -05:00
Richard Moore
152d672278 Add isHexString to exported utils (#367). 2018-12-12 16:23:39 -05:00
Richard Moore
51fb472809 Add abs method to BigNumber (#375). 2018-12-12 16:10:28 -05:00
Richard Moore
66440b8542 Better error messages for namehash (#364). 2018-12-10 17:02:02 -05:00
Richard Moore
fefdd51084 Updated dist files. 2018-12-08 18:48:53 -05:00
Richard Moore
6ca1d77298 Fixed function name in parsed transactions (#370). 2018-12-08 18:46:29 -05:00
Richard Moore
4f6748ec4c Include request body in web errors. 2018-12-05 04:19:34 -05:00
Richard Moore
f56fc572f1 Squashed unhandled promise exception for Providers that are never used (#362). 2018-12-05 04:18:47 -05:00
Richard Moore
16fdf6b621 Added gas estimation back into JsonRpcSigner (#365). 2018-12-05 03:32:24 -05:00
Richard Moore
a863037ca3 Updated dist files. 2018-12-04 17:16:42 -05:00
Richard Moore
2d854bd94c Do not fill in implicit values for JSON-RPC based signers (#335). 2018-12-04 17:13:55 -05:00
Richard Moore
9565c28a91 More relaxed transaction parsing (#357). 2018-12-04 16:31:18 -05:00
Richard Moore
bc457bb3bd Allow any whitespace characters in human-readable ABI (#360). 2018-12-04 16:14:57 -05:00
Richard Moore
db383a3121 Updated dist files. 2018-11-27 17:32:47 -05:00
Richard Moore
3f76f603d9 Fixed contract proxied tx.wait receipt properties (#355). 2018-11-27 17:32:05 -05:00
Richard Moore
68304848ae Updated dist files. 2018-11-27 16:03:39 -05:00
Richard Moore
bb6bc4cac3 Check for partially-working normalize support. 2018-11-27 15:59:14 -05:00
Richard Moore
ef8b9c36ef Support for platforms where UTF-8 is only half broken. 2018-11-27 15:56:50 -05:00
Richard Moore
e6c943d01f Updated dist files. 2018-11-21 16:24:40 -05:00
Richard Moore
31d3ee899f Throw exception instead of returning null for getDefaultProvider (#351). 2018-11-21 16:23:44 -05:00
Richard Moore
98143a845b Updated dist files. 2018-11-20 15:45:47 -05:00
Richard Moore
bffc557be1 Added default provider support for Ethereum classic (#351). 2018-11-20 15:41:12 -05:00
Richard Moore
09208fa8fe Updated dist files. 2018-11-13 07:50:04 -05:00
Richard Moore
048c571d3d Fixed 0 confirmation waiting (#346). 2018-11-13 07:48:37 -05:00
Richard Moore
24757f1064 Updated dist files. 2018-11-12 17:27:47 -05:00
Richard Moore
88f2f51266 Fix spacing in checkArgument errors (#318). 2018-11-12 17:22:18 -05:00
Richard Moore
93152ef863 Do not replay block events when the provider event block is reset (#343). 2018-11-12 17:17:43 -05:00
Richard Moore
09b698b0a9 Updated dist files. 2018-11-09 14:42:29 -05:00
Richard Moore
478aaf9619 Force unorm shim when String.prototype.normalize is broken (#338). 2018-11-09 14:36:21 -05:00
Richard Moore
fad902b438 Better error message when normalize is missing. 2018-11-09 14:34:14 -05:00
Richard Moore
7bfaf292db Added shims for React-Native support. 2018-11-08 18:25:16 -05:00
Richard Moore
be0488a1a0 Updated dist files. 2018-11-08 16:03:33 -05:00
Richard Moore
28a52cd485 Fix for when blockTag is specified as a null equivalent value in contract overrides (#329). 2018-11-08 16:01:48 -05:00
Richard Moore
3a19f43844 Added "debug" event for providers; do not depend on the format as it may change, but this should help debugging in most cases (#320). 2018-11-08 15:59:30 -05:00
Richard Moore
4852e837d2 Fix for Kovan filters without an address (#339). 2018-11-08 15:56:35 -05:00
Richard Moore
fa68385cfe Updated dist files. 2018-10-14 19:05:38 -04:00
Richard Moore
d54609a458 Fixed lingering polling timer when no events left to process in a provider. 2018-10-14 19:01:53 -04:00
Richard Moore
f682861e0b Fixed utils.poll from mutating passed variables. 2018-10-14 19:01:09 -04:00
Richard Moore
023a20ff47 Fixed and refactored populating transaction values for signers (#306). 2018-10-14 19:00:15 -04:00
Richard Moore
e39cd84923 Fixed test cases for phantomjs (must use ES3 syntax). 2018-10-13 17:27:19 -04:00
Richard Moore
5020897f10 Updated dist files. 2018-10-13 17:19:07 -04:00
Richard Moore
6ac2d923b7 Fixed filtering with null non-indexed parameters (#305). 2018-10-13 17:17:02 -04:00
Richard Moore
6996dd86f4 Updated dist files. 2018-10-11 16:50:52 -04:00
82 changed files with 2174 additions and 802 deletions

2
_version.d.ts vendored
View File

@@ -1 +1 @@
export declare const version = "4.0.4";
export declare const version = "4.0.22";

View File

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

View File

@@ -106,15 +106,19 @@ 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];
}
var tx = {};
var blockTag = null;
// If 1 extra parameter was passed in, it contains overrides
if (params.length === method.inputs.length + 1 && typeof (params[params.length - 1]) === 'object') {
tx = properties_1.shallowCopy(params.pop());
if (tx.blockTag != null) {
blockTag = tx.blockTag;
}
delete tx.blockTag;
// Check for unexpected keys (e.g. using "gas" instead of "gasLimit")
for (var key in tx) {
if (!allowedTransactionKeys[key]) {
@@ -154,7 +158,7 @@ function runMethod(contract, functionName, estimateOnly) {
if (tx.from == null && contract.signer) {
tx.from = contract.signer.getAddress();
}
return contract.provider.call(tx).then(function (value) {
return contract.provider.call(tx, blockTag).then(function (value) {
if ((bytes_1.hexDataLength(value) % 32) === 4 && bytes_1.hexDataSlice(value, 0, 4) === '0x08c379a0') {
var reason = abi_coder_1.defaultAbiCoder.decode(['string'], bytes_1.hexDataSlice(value, 4));
errors.throwError('call revert exception', errors.CALL_EXCEPTION, {
@@ -213,19 +217,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);
@@ -300,7 +304,6 @@ var Contract = /** @class */ (function () {
}
return address;
}).catch(function (error) {
console.log('ERROR: Cannot find Contract - ' + addressOrName);
throw error;
}));
}
@@ -319,7 +322,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);
@@ -551,12 +554,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;
};
@@ -650,7 +660,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;

740
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

42
dist/ethers.types.txt vendored
View File

@@ -188,6 +188,10 @@ declare module 'ethers/errors' {
export function checkNew(self: any, kind: any): void;
export function checkArgumentCount(count: number, expectedCount: number, suffix?: string): void;
export function setCensorship(censorship: boolean, permanent?: boolean): void;
export function checkNormalize(): void;
export function setLogLevel(logLevel: string): void;
export function warn(...args: Array<any>): void;
export function info(...args: Array<any>): void;
}
declare module 'ethers/providers' {
@@ -209,7 +213,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';
@@ -219,10 +223,11 @@ declare module 'ethers/utils' {
import { keccak256 as solidityKeccak256, pack as solidityPack, sha256 as soliditySha256 } from 'ethers/utils/solidity';
import { randomBytes } from 'ethers/utils/random-bytes';
import { getNetwork } from 'ethers/utils/networks';
import { deepCopy, defineReadOnly, resolveProperties, shallowCopy } from 'ethers/utils/properties';
import { checkProperties, deepCopy, defineReadOnly, resolveProperties, shallowCopy } from 'ethers/utils/properties';
import * as RLP from 'ethers/utils/rlp';
import { computeAddress, computePublicKey, recoverAddress, recoverPublicKey, verifyMessage } from 'ethers/utils/secp256k1';
import { SigningKey } from 'ethers/utils/signing-key';
import { populateTransaction } from 'ethers/utils/transaction';
import { parse as parseTransaction, serialize as serializeTransaction } from 'ethers/utils/transaction';
import { formatBytes32String, parseBytes32String, toUtf8Bytes, toUtf8String } from 'ethers/utils/utf8';
import { commify, formatEther, parseEther, formatUnits, parseUnits } from 'ethers/utils/units';
@@ -238,7 +243,7 @@ declare module 'ethers/utils' {
import { ConnectionInfo, OnceBlockable, PollOptions } from 'ethers/utils/web';
import { EncryptOptions, ProgressCallback } from 'ethers/utils/secret-storage';
import { Wordlist } from 'ethers/utils/wordlist';
export { AbiCoder, defaultAbiCoder, formatSignature, formatParamType, parseSignature, parseParamType, RLP, fetchJson, getNetwork, deepCopy, defineReadOnly, resolveProperties, shallowCopy, arrayify, concat, padZeros, stripZeros, HDNode, SigningKey, Interface, base64, BigNumber, bigNumberify, hexlify, hexStripZeros, hexZeroPad, hexDataLength, hexDataSlice, toUtf8Bytes, toUtf8String, formatBytes32String, parseBytes32String, hashMessage, namehash, id, getAddress, getIcapAddress, getContractAddress, formatEther, parseEther, formatUnits, parseUnits, commify, keccak256, sha256, randomBytes, solidityPack, solidityKeccak256, soliditySha256, splitSignature, joinSignature, parseTransaction, serializeTransaction, getJsonWalletAddress, computeAddress, computePublicKey, recoverAddress, recoverPublicKey, verifyMessage, SupportedAlgorithms, UnicodeNormalizationForm, CoerceFunc, EventFragment, FunctionFragment, ParamType, BigNumberish, Arrayish, Hexable, Signature, Indexed, DeployDescription, EventDescription, FunctionDescription, LogDescription, TransactionDescription, Network, Networkish, Transaction, UnsignedTransaction, ConnectionInfo, OnceBlockable, PollOptions, EncryptOptions, ProgressCallback, Wordlist, };
export { AbiCoder, defaultAbiCoder, formatSignature, formatParamType, parseSignature, parseParamType, RLP, fetchJson, getNetwork, checkProperties, deepCopy, defineReadOnly, resolveProperties, shallowCopy, arrayify, concat, padZeros, stripZeros, HDNode, SigningKey, Interface, base64, BigNumber, bigNumberify, hexlify, isHexString, hexStripZeros, hexZeroPad, hexDataLength, hexDataSlice, toUtf8Bytes, toUtf8String, formatBytes32String, parseBytes32String, hashMessage, namehash, id, getAddress, getIcapAddress, getContractAddress, formatEther, parseEther, formatUnits, parseUnits, commify, keccak256, sha256, randomBytes, solidityPack, solidityKeccak256, soliditySha256, splitSignature, joinSignature, parseTransaction, populateTransaction, serializeTransaction, getJsonWalletAddress, computeAddress, computePublicKey, recoverAddress, recoverPublicKey, verifyMessage, SupportedAlgorithms, UnicodeNormalizationForm, CoerceFunc, EventFragment, FunctionFragment, ParamType, BigNumberish, Arrayish, Hexable, Signature, Indexed, DeployDescription, EventDescription, FunctionDescription, LogDescription, TransactionDescription, Network, Networkish, Transaction, UnsignedTransaction, ConnectionInfo, OnceBlockable, PollOptions, EncryptOptions, ProgressCallback, Wordlist, };
}
declare module 'ethers/wordlists' {
@@ -260,7 +265,7 @@ declare module 'ethers/utils/shims' {
}
declare module 'ethers/_version' {
export const version = "4.0.4";
export const version = "4.0.22";
}
declare module 'ethers/utils/bignumber' {
@@ -271,6 +276,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;
@@ -412,6 +418,7 @@ declare module 'ethers/utils/transaction' {
import { BigNumber } from 'ethers/utils/bignumber';
import { Arrayish, Signature } from 'ethers/utils/bytes';
import { BigNumberish } from 'ethers/utils/bignumber';
import { Provider } from 'ethers/providers/abstract-provider';
export type UnsignedTransaction = {
to?: string;
nonce?: number;
@@ -437,6 +444,7 @@ declare module 'ethers/utils/transaction' {
}
export function serialize(transaction: UnsignedTransaction, signature?: Arrayish | Signature): string;
export function parse(rawTransaction: Arrayish): Transaction;
export function populateTransaction(transaction: any, provider: Provider, from: string | Promise<string>): Promise<Transaction>;
}
declare module 'ethers/providers/abstract-provider' {
@@ -479,6 +487,8 @@ declare module 'ethers/providers/abstract-provider' {
logIndex?: number;
}
export interface TransactionReceipt {
to?: string;
from?: string;
contractAddress?: string;
transactionIndex?: number;
root?: string;
@@ -496,7 +506,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>;
@@ -523,7 +533,7 @@ declare module 'ethers/providers/abstract-provider' {
abstract getCode(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
abstract getStorageAt(addressOrName: string | Promise<string>, position: BigNumberish | Promise<BigNumberish>, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
abstract sendTransaction(signedTransaction: string | Promise<string>): Promise<TransactionResponse>;
abstract call(transaction: TransactionRequest): Promise<string>;
abstract call(transaction: TransactionRequest, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
abstract estimateGas(transaction: TransactionRequest): Promise<BigNumber>;
abstract getBlock(blockHashOrBlockTag: BlockTag | string | Promise<BlockTag | string>, includeTransactions?: boolean): Promise<Block>;
abstract getTransaction(transactionHash: string): Promise<TransactionResponse>;
@@ -712,7 +722,9 @@ declare module 'ethers/providers/base-provider' {
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
*
@@ -741,7 +753,7 @@ declare module 'ethers/providers/base-provider' {
getStorageAt(addressOrName: string | Promise<string>, position: BigNumberish | Promise<BigNumberish>, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
sendTransaction(signedTransaction: string | Promise<string>): Promise<TransactionResponse>;
_wrapTransaction(tx: Transaction, hash?: string): TransactionResponse;
call(transaction: TransactionRequest): Promise<string>;
call(transaction: TransactionRequest, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
estimateGas(transaction: TransactionRequest): Promise<BigNumber>;
getBlock(blockHashOrBlockTag: BlockTag | string | Promise<BlockTag | string>, includeTransactions?: boolean): Promise<Block>;
getTransaction(transactionHash: string): Promise<TransactionResponse>;
@@ -805,7 +817,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>>;
@@ -826,6 +839,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>;
@@ -839,7 +853,11 @@ declare module 'ethers/providers/json-rpc-provider' {
perform(method: string, params: any): Promise<any>;
protected _startPending(): void;
protected _stopPending(): void;
static hexlifyTransaction(transaction: TransactionRequest): any;
static hexlifyTransaction(transaction: TransactionRequest, allowExtra?: {
[key: string]: boolean;
}): {
[key: string]: string;
};
}
}
@@ -916,6 +934,7 @@ declare module 'ethers/utils/networks' {
name: string;
chainId: number;
ensAddress?: string;
_defaultProvider?: (providers: any) => any;
};
export type Networkish = Network | string | number;
/**
@@ -932,6 +951,9 @@ declare module 'ethers/utils/properties' {
export function setType(object: any, type: string): void;
export function isType(object: any, type: string): boolean;
export function resolveProperties(object: any): Promise<any>;
export function checkProperties(object: any, properties: {
[name: string]: boolean;
}): void;
export function shallowCopy(object: any): any;
export function deepCopy(object: any, frozen?: boolean): any;
export function inheritable(parent: any): (child: any) => void;

1
dist/shims.js vendored Normal file

File diff suppressed because one or more lines are too long

4
errors.d.ts vendored
View File

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

View File

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

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

@@ -152,7 +152,9 @@ function taskBundle(name, options) {
if (options.minify) {
result = result.pipe(buffer())
.pipe(sourcemaps.init({ loadMaps: true }))
.pipe(uglify())
.pipe(uglify({
output: { ascii_only: true }
}))
.pipe(sourcemaps.write('./'))
}
@@ -174,6 +176,30 @@ taskBundle("minified", { filename: "ethers.min.js", dest: 'dist', minify: true }
// Creates dist/ethers.min.js
taskBundle("minified-test", { filename: "ethers.min.js", dest: 'tests/dist', minify: true });
gulp.task('shims', function () {
var result = browserify({
basedir: '.',
debug: false,
entries: [ './tests/shims/index.js' ],
cache: { },
packageCache: {},
standalone: "_shims",
insertGlobalVars: {
process: function() { return; },
}
})
.bundle()
.pipe(source('shims.js'))
.pipe(buffer())
.pipe(uglify({
output: { ascii_only: true }
}))
.pipe(gulp.dest('dist'));
return result;
});
/*
// Dump the TypeScript definitions to dist/types/
gulp.task("types", function() {
@@ -265,7 +291,9 @@ function taskLang(locale) {
.bundle()
.pipe(source("wordlist-" + locale + ".js"))
.pipe(buffer())
.pipe(uglify())
.pipe(uglify({
output: { ascii_only: true }
}))
.pipe(gulp.dest("dist"));
});
}

77
package-lock.json generated
View File

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

View File

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

View File

@@ -37,6 +37,8 @@ export interface Log {
logIndex?: number;
}
export interface TransactionReceipt {
to?: string;
from?: string;
contractAddress?: string;
transactionIndex?: number;
root?: string;
@@ -54,7 +56,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>;
@@ -81,7 +83,7 @@ export declare abstract class Provider implements OnceBlockable {
abstract getCode(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
abstract getStorageAt(addressOrName: string | Promise<string>, position: BigNumberish | Promise<BigNumberish>, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
abstract sendTransaction(signedTransaction: string | Promise<string>): Promise<TransactionResponse>;
abstract call(transaction: TransactionRequest): Promise<string>;
abstract call(transaction: TransactionRequest, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
abstract estimateGas(transaction: TransactionRequest): Promise<BigNumber>;
abstract getBlock(blockHashOrBlockTag: BlockTag | string | Promise<BlockTag | string>, includeTransactions?: boolean): Promise<Block>;
abstract getTransaction(transactionHash: string): Promise<TransactionResponse>;

View File

@@ -7,7 +7,9 @@ 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;
@@ -44,7 +46,7 @@ export declare class BaseProvider extends Provider {
getStorageAt(addressOrName: string | Promise<string>, position: BigNumberish | Promise<BigNumberish>, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
sendTransaction(signedTransaction: string | Promise<string>): Promise<TransactionResponse>;
_wrapTransaction(tx: Transaction, hash?: string): TransactionResponse;
call(transaction: TransactionRequest): Promise<string>;
call(transaction: TransactionRequest, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
estimateGas(transaction: TransactionRequest): Promise<BigNumber>;
getBlock(blockHashOrBlockTag: BlockTag | string | Promise<BlockTag | string>, includeTransactions?: boolean): Promise<Block>;
getTransaction(transactionHash: string): Promise<TransactionResponse>;

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: address_1.getAddress,
contractAddress: allowNull(address_1.getAddress, null),
transactionIndex: checkNumber,
root: allowNull(checkHash),
@@ -375,12 +387,12 @@ function getEventTag(eventName) {
return 'address:' + address_1.getAddress(eventName);
}
eventName = eventName.toLowerCase();
if (eventName === 'block' || eventName === 'pending' || eventName === 'error') {
return eventName;
}
else if (bytes_1.hexDataLength(eventName) === 32) {
if (bytes_1.hexDataLength(eventName) === 32) {
return 'tx:' + eventName;
}
if (eventName.indexOf(':') === -1) {
return eventName;
}
}
else if (Array.isArray(eventName)) {
return 'filter::' + serializeTopics(eventName);
@@ -405,6 +417,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 +436,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,25 +448,40 @@ 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;
}
// Notify all listener for each block that has passed
for (var i = _this._lastBlockNumber + 1; i <= blockNumber; i++) {
if (_this._emitted.block < i) {
_this._emitted.block = i;
for (var i = _this._emitted.block + 1; i <= blockNumber; i++) {
_this.emit('block', i);
}
// The emitted block was updated, check for obsolete events
if (_this._emitted.block !== blockNumber) {
_this._emitted.block = blockNumber;
Object.keys(_this._emitted).forEach(function (key) {
// The block event does not expire
if (key === 'block') {
return;
}
// The block we were at when we emitted this event
var eventBlockNumber = _this._emitted[key];
// We cannot garbage collect pending transactions or blocks here
// They should be garbage collected by the Provider when setting
// "pending" events
if (eventBlockNumber === 'pending') {
return;
}
// Evict any transaction hashes or block hashes over 12 blocks
// old, since they should not return null anyways
Object.keys(_this._emitted).forEach(function (key) {
if (key === 'block') {
return;
}
if (_this._emitted[key] > i + 12) {
delete _this._emitted[key];
}
});
}
_this.emit('block', i);
if (blockNumber - eventBlockNumber > 12) {
delete _this._emitted[key];
}
});
}
// First polling cycle
if (_this._lastBlockNumber === -2) {
_this._lastBlockNumber = blockNumber - 1;
}
// Sweep balances and remove addresses we no longer have events for
var newBalances = {};
@@ -482,25 +507,27 @@ var BaseProvider = /** @class */ (function (_super) {
newBalances[address_2] = _this._balances[address_2];
}
_this.getBalance(address_2, 'latest').then(function (balance) {
var lastBalance = this._balances[address_2];
var lastBalance = _this._balances[address_2];
if (lastBalance && balance.eq(lastBalance)) {
return;
}
this._balances[address_2] = balance;
this.emit(address_2, balance);
_this._balances[address_2] = balance;
_this.emit(address_2, balance);
return null;
}).catch(function (error) { _this.emit('error', error); });
break;
}
case 'filter': {
var address = comps[1];
var topics = deserializeTopics(comps[2]);
var filter_1 = {
address: address,
address: comps[1],
fromBlock: _this._lastBlockNumber + 1,
toBlock: blockNumber,
topics: topics
};
if (!filter_1.address) {
delete filter_1.address;
}
_this.getLogs(filter_1).then(function (logs) {
if (logs.length === 0) {
return;
@@ -523,8 +550,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 () {
@@ -538,10 +567,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
@@ -615,11 +641,14 @@ 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 (confirmations === 0) {
return receipt;
}
if (receipt == null || receipt.confirmations < confirmations) {
return undefined;
}
@@ -737,10 +766,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,
@@ -752,13 +791,14 @@ var BaseProvider = /** @class */ (function (_super) {
};
return result;
};
BaseProvider.prototype.call = function (transaction) {
BaseProvider.prototype.call = function (transaction, blockTag) {
var _this = this;
var tx = properties_1.shallowCopy(transaction);
return this.ready.then(function () {
return properties_1.resolveProperties(tx).then(function (tx) {
return properties_1.resolveProperties({ blockTag: blockTag, tx: tx }).then(function (_a) {
var blockTag = _a.blockTag, tx = _a.tx;
return _this._resolveNames(tx, ['to', 'from']).then(function (tx) {
var params = { transaction: checkTransactionRequest(tx) };
var params = { blockTag: checkBlockTag(blockTag), transaction: checkTransactionRequest(tx) };
return _this.perform('call', params).then(function (result) {
return bytes_1.hexlify(result);
});
@@ -792,12 +832,12 @@ var BaseProvider = /** @class */ (function (_super) {
return properties_1.resolveProperties({ blockHashOrBlockTag: blockHashOrBlockTag }).then(function (_a) {
var blockHashOrBlockTag = _a.blockHashOrBlockTag;
try {
var blockHash = bytes_1.hexlify(blockHashOrBlockTag);
if (bytes_1.hexDataLength(blockHash) === 32) {
var blockHash_1 = bytes_1.hexlify(blockHashOrBlockTag);
if (bytes_1.hexDataLength(blockHash_1) === 32) {
return web_1.poll(function () {
return _this.perform('getBlock', { blockHash: blockHash, includeTransactions: !!includeTransactions }).then(function (block) {
return _this.perform('getBlock', { blockHash: blockHash_1, includeTransactions: !!includeTransactions }).then(function (block) {
if (block == null) {
if (_this._emitted['b:' + blockHash] == null) {
if (_this._emitted['b:' + blockHash_1] == null) {
return null;
}
return undefined;
@@ -817,7 +857,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;
@@ -836,7 +876,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) {
@@ -871,7 +911,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) {
@@ -1050,7 +1090,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 () {
};
@@ -1094,6 +1134,9 @@ var BaseProvider = /** @class */ (function (_super) {
result = true;
return !(event.once);
});
if (this.listenerCount() === 0) {
this.polling = false;
}
return result;
};
BaseProvider.prototype.listenerCount = function (eventName) {

View File

@@ -112,41 +112,52 @@ var EtherscanProvider = /** @class */ (function (_super) {
return _this;
}
EtherscanProvider.prototype.perform = function (method, params) {
//if (!params) { params = {}; }
var _this = this;
var url = this.baseUrl;
var apiKey = '';
if (this.apiKey) {
apiKey += '&apikey=' + this.apiKey;
}
var get = function (url, procFunc) {
return web_1.fetchJson(url, null, procFunc || getJsonResult).then(function (result) {
_this.emit('debug', {
action: 'perform',
request: url,
response: result,
provider: _this
});
return result;
});
};
switch (method) {
case 'getBlockNumber':
url += '/api?module=proxy&action=eth_blockNumber' + apiKey;
return web_1.fetchJson(url, null, getJsonResult);
return get(url);
case 'getGasPrice':
url += '/api?module=proxy&action=eth_gasPrice' + apiKey;
return web_1.fetchJson(url, null, getJsonResult);
return get(url);
case 'getBalance':
// Returns base-10 result
url += '/api?module=account&action=balance&address=' + params.address;
url += '&tag=' + params.blockTag + apiKey;
return web_1.fetchJson(url, null, getResult);
return get(url, getResult);
case 'getTransactionCount':
url += '/api?module=proxy&action=eth_getTransactionCount&address=' + params.address;
url += '&tag=' + params.blockTag + apiKey;
return web_1.fetchJson(url, null, getJsonResult);
return get(url);
case 'getCode':
url += '/api?module=proxy&action=eth_getCode&address=' + params.address;
url += '&tag=' + params.blockTag + apiKey;
return web_1.fetchJson(url, null, getJsonResult);
return get(url, getJsonResult);
case 'getStorageAt':
url += '/api?module=proxy&action=eth_getStorageAt&address=' + params.address;
url += '&position=' + params.position;
url += '&tag=' + params.blockTag + apiKey;
return web_1.fetchJson(url, null, getJsonResult);
return get(url, getJsonResult);
case 'sendTransaction':
url += '/api?module=proxy&action=eth_sendRawTransaction&hex=' + params.signedTransaction;
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult).catch(function (error) {
return get(url).catch(function (error) {
if (error.responseText) {
// "Insufficient funds. The account you tried to send transaction from does not have enough funds. Required 21464000000000 and got: 0"
if (error.responseText.toLowerCase().indexOf('insufficient funds') >= 0) {
@@ -173,33 +184,39 @@ var EtherscanProvider = /** @class */ (function (_super) {
url += '&boolean=false';
}
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult);
return get(url);
}
throw new Error('getBlock by blockHash not implmeneted');
case 'getTransaction':
url += '/api?module=proxy&action=eth_getTransactionByHash&txhash=' + params.transactionHash;
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult);
return get(url);
case 'getTransactionReceipt':
url += '/api?module=proxy&action=eth_getTransactionReceipt&txhash=' + params.transactionHash;
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult);
case 'call':
return get(url);
case 'call': {
var transaction = getTransactionString(params.transaction);
if (transaction) {
transaction = '&' + transaction;
}
url += '/api?module=proxy&action=eth_call' + transaction;
//url += '&tag=' + params.blockTag + apiKey;
if (params.blockTag !== 'latest') {
throw new Error('EtherscanProvider does not support blockTag for call');
}
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult);
case 'estimateGas':
return get(url);
}
case 'estimateGas': {
var transaction = getTransactionString(params.transaction);
if (transaction) {
transaction = '&' + transaction;
}
url += '/api?module=proxy&action=eth_estimateGas&' + transaction;
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult);
return get(url);
}
case 'getLogs':
url += '/api?module=logs&action=getLogs';
try {
@@ -229,7 +246,7 @@ var EtherscanProvider = /** @class */ (function (_super) {
}
url += apiKey;
var self = this;
return web_1.fetchJson(url, null, getResult).then(function (logs) {
return get(url, getResult).then(function (logs) {
var txs = {};
var seq = Promise.resolve();
logs.forEach(function (log) {
@@ -258,7 +275,7 @@ var EtherscanProvider = /** @class */ (function (_super) {
}
url += '/api?module=stats&action=ethprice';
url += apiKey;
return web_1.fetchJson(url, null, getResult).then(function (result) {
return get(url, getResult).then(function (result) {
return parseFloat(result.ethusd);
});
default:
@@ -268,6 +285,7 @@ var EtherscanProvider = /** @class */ (function (_super) {
};
// @TODO: Allow startBlock and endBlock to be Promises
EtherscanProvider.prototype.getHistory = function (addressOrName, startBlock, endBlock) {
var _this = this;
var url = this.baseUrl;
var apiKey = '';
if (this.apiKey) {
@@ -285,6 +303,12 @@ var EtherscanProvider = /** @class */ (function (_super) {
url += '&endblock=' + endBlock;
url += '&sort=asc' + apiKey;
return web_1.fetchJson(url, null, getResult).then(function (result) {
_this.emit('debug', {
action: 'getHistory',
request: url,
response: result,
provider: _this
});
var output = [];
result.forEach(function (tx) {
['contractAddress', 'to'].forEach(function (key) {

View File

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

View File

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

View File

@@ -18,16 +18,21 @@ var __importStar = (this && this.__importStar) || function (mod) {
};
Object.defineProperty(exports, "__esModule", { value: true });
var json_rpc_provider_1 = require("./json-rpc-provider");
var bytes_1 = require("../utils/bytes");
var networks_1 = require("../utils/networks");
var properties_1 = require("../utils/properties");
var errors = __importStar(require("../errors"));
var defaultProjectId = "7d0d81d0919f4f05b9ab6634be01ee73";
var InfuraProvider = /** @class */ (function (_super) {
__extends(InfuraProvider, _super);
function InfuraProvider(network, apiAccessToken) {
function InfuraProvider(network, projectId) {
var _this = this;
network = networks_1.getNetwork((network == null) ? 'homestead' : network);
var standard = networks_1.getNetwork((network == null) ? 'homestead' : network);
if (projectId == null) {
projectId = defaultProjectId;
}
var host = null;
switch (network.name) {
switch (standard.name) {
case 'homestead':
host = 'mainnet.infura.io';
break;
@@ -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>;
@@ -27,5 +28,9 @@ export declare class JsonRpcProvider extends BaseProvider {
perform(method: string, params: any): Promise<any>;
protected _startPending(): void;
protected _stopPending(): void;
static hexlifyTransaction(transaction: TransactionRequest): any;
static hexlifyTransaction(transaction: TransactionRequest, allowExtra?: {
[key: string]: boolean;
}): {
[key: string]: string;
};
}

View File

@@ -20,13 +20,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
// See: https://github.com/ethereum/wiki/wiki/JSON-RPC
var base_provider_1 = require("./base-provider");
var abstract_signer_1 = require("../abstract-signer");
var errors = __importStar(require("../errors"));
var address_1 = require("../utils/address");
var bytes_1 = require("../utils/bytes");
var networks_1 = require("../utils/networks");
var properties_1 = require("../utils/properties");
var utf8_1 = require("../utils/utf8");
var web_1 = require("../utils/web");
var errors = __importStar(require("../errors"));
function timer(timeout) {
return new Promise(function (resolve) {
setTimeout(function () {
@@ -77,14 +77,6 @@ var JsonRpcSigner = /** @class */ (function (_super) {
}
return _this;
}
/* May add back in the future; for now it is considered confusing. :)
get address(): string {
if (!this._address) {
errors.throwError('no sync sync address available; use getAddress', errors.UNSUPPORTED_OPERATION, { operation: 'address' });
}
return this._address
}
*/
JsonRpcSigner.prototype.getAddress = function () {
var _this = this;
if (this._address) {
@@ -104,33 +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;
var tx = properties_1.shallowCopy(transaction);
if (tx.from == null) {
tx.from = this.getAddress().then(function (address) {
if (!address) {
return null;
}
return address.toLowerCase();
});
}
transaction = properties_1.shallowCopy(transaction);
var fromAddress = this.getAddress().then(function (address) {
if (address) {
address = address.toLowerCase();
}
return address;
});
// The JSON-RPC for eth_sendTransaction uses 90000 gas; if the user
// wishes to use this, it is easy to specify explicitly, otherwise
// we look it up for them.
if (transaction.gasLimit == null) {
tx.gasLimit = this.provider.estimateGas(tx);
var estimate = properties_1.shallowCopy(transaction);
estimate.from = fromAddress;
transaction.gasLimit = this.provider.estimateGas(estimate);
}
return properties_1.resolveProperties(tx).then(function (tx) {
return _this.provider.send('eth_sendTransaction', [JsonRpcProvider.hexlifyTransaction(tx)]).then(function (hash) {
return web_1.poll(function () {
return _this.provider.getTransaction(hash).then(function (tx) {
if (tx === null) {
return undefined;
}
return _this.provider._wrapTransaction(tx, hash);
});
}, { onceBlock: _this.provider }).catch(function (error) {
error.transactionHash = hash;
throw error;
});
return Promise.all([
properties_1.resolveProperties(transaction),
fromAddress
]).then(function (results) {
var tx = results[0];
var hexTx = JsonRpcProvider.hexlifyTransaction(tx);
hexTx.from = results[1];
return _this.provider.send('eth_sendTransaction', [hexTx]).then(function (hash) {
return hash;
}, function (error) {
if (error.responseText) {
// See: JsonRpcProvider.sendTransaction (@TODO: Expose a ._throwError??)
@@ -154,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);
});
}, { 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);
@@ -171,6 +178,9 @@ var JsonRpcSigner = /** @class */ (function (_super) {
return JsonRpcSigner;
}(abstract_signer_1.Signer));
exports.JsonRpcSigner = JsonRpcSigner;
var allowedTransactionKeys = {
chainId: true, data: true, gasLimit: true, gasPrice: true, nonce: true, to: true, value: true
};
var JsonRpcProvider = /** @class */ (function (_super) {
__extends(JsonRpcProvider, _super);
function JsonRpcProvider(url, network) {
@@ -223,13 +233,22 @@ var JsonRpcProvider = /** @class */ (function (_super) {
});
};
JsonRpcProvider.prototype.send = function (method, params) {
var _this = this;
var request = {
method: method,
params: params,
id: 42,
jsonrpc: "2.0"
};
return web_1.fetchJson(this.connection, JSON.stringify(request), getResult);
return web_1.fetchJson(this.connection, JSON.stringify(request), getResult).then(function (result) {
_this.emit('debug', {
action: 'send',
request: request,
response: result,
provider: _this
});
return result;
});
};
JsonRpcProvider.prototype.perform = function (method, params) {
switch (method) {
@@ -276,9 +295,9 @@ var JsonRpcProvider = /** @class */ (function (_super) {
case 'getTransactionReceipt':
return this.send('eth_getTransactionReceipt', [params.transactionHash]);
case 'call':
return this.send('eth_call', [JsonRpcProvider.hexlifyTransaction(params.transaction), 'latest']);
return this.send('eth_call', [JsonRpcProvider.hexlifyTransaction(params.transaction, { from: true }), params.blockTag]);
case 'estimateGas':
return this.send('eth_estimateGas', [JsonRpcProvider.hexlifyTransaction(params.transaction)]);
return this.send('eth_estimateGas', [JsonRpcProvider.hexlifyTransaction(params.transaction, { from: true })]);
case 'getLogs':
if (params.filter && params.filter.address != null) {
params.filter.address = getLowerCase(params.filter.address);
@@ -305,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) {
@@ -336,10 +356,21 @@ var JsonRpcProvider = /** @class */ (function (_super) {
// - gasLimit => gas
// - All values hexlified
// - All numeric values zero-striped
// @TODO: Not any, a dictionary of string to strings
JsonRpcProvider.hexlifyTransaction = function (transaction) {
// NOTE: This allows a TransactionRequest, but all values should be resolved
// before this is called
JsonRpcProvider.hexlifyTransaction = function (transaction, allowExtra) {
// Check only allowed properties are given
var allowed = properties_1.shallowCopy(allowedTransactionKeys);
if (allowExtra) {
for (var key in allowExtra) {
if (allowExtra[key]) {
allowed[key] = true;
}
}
}
properties_1.checkProperties(transaction, allowed);
var result = {};
// Some nodes (INFURA ropsten; INFURA mainnet is fine) don't like extra zeros.
// Some nodes (INFURA ropsten; INFURA mainnet is fine) don't like leading zeros.
['gasLimit', 'gasPrice', 'nonce', 'value'].forEach(function (key) {
if (transaction[key] == null) {
return;

View File

@@ -1 +1 @@
export const version = "4.0.4";
export const version = "4.0.22";

View File

@@ -158,9 +158,10 @@ function runMethod(contract: Contract, functionName: string, estimateOnly: boole
if (tx.blockTag != null) {
blockTag = tx.blockTag;
delete tx.blockTag;
}
delete tx.blockTag;
// Check for unexpected keys (e.g. using "gas" instead of "gasLimit")
for (let key in tx) {
if (!allowedTransactionKeys[key]) {
@@ -279,7 +280,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;
@@ -287,12 +288,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);
@@ -410,7 +411,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 {
@@ -428,7 +428,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) {
@@ -696,7 +696,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;
@@ -798,7 +806,7 @@ export class ContractFactory {
});
// Make sure the call matches the constructor signature
errors.checkArgumentCount(args.length, this.interface.deployFunction.inputs.length, 'in Contract constructor');
errors.checkArgumentCount(args.length, this.interface.deployFunction.inputs.length, ' in Contract constructor');
// Set the data to the bytecode + the encoded constructor arguments
tx.data = this.interface.deployFunction.encode(this.bytecode, args);

View File

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

View File

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

View File

@@ -60,6 +60,8 @@ export interface Log {
}
export interface TransactionReceipt {
to?: string;
from?: string;
contractAddress?: string,
transactionIndex?: number,
root?: string,
@@ -78,7 +80,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>,

View File

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

View File

@@ -113,43 +113,55 @@ export class EtherscanProvider extends BaseProvider{
let apiKey = '';
if (this.apiKey) { apiKey += '&apikey=' + this.apiKey; }
let get = (url: string, procFunc?: (value: any) => any) => {
return fetchJson(url, null, procFunc || getJsonResult).then((result) => {
this.emit('debug', {
action: 'perform',
request: url,
response: result,
provider: this
});
return result;
});
};
switch (method) {
case 'getBlockNumber':
url += '/api?module=proxy&action=eth_blockNumber' + apiKey;
return fetchJson(url, null, getJsonResult);
return get(url);
case 'getGasPrice':
url += '/api?module=proxy&action=eth_gasPrice' + apiKey;
return fetchJson(url, null, getJsonResult);
return get(url);
case 'getBalance':
// Returns base-10 result
url += '/api?module=account&action=balance&address=' + params.address;
url += '&tag=' + params.blockTag + apiKey;
return fetchJson(url, null, getResult);
return get(url, getResult);
case 'getTransactionCount':
url += '/api?module=proxy&action=eth_getTransactionCount&address=' + params.address;
url += '&tag=' + params.blockTag + apiKey;
return fetchJson(url, null, getJsonResult);
return get(url);
case 'getCode':
url += '/api?module=proxy&action=eth_getCode&address=' + params.address;
url += '&tag=' + params.blockTag + apiKey;
return fetchJson(url, null, getJsonResult);
return get(url, getJsonResult);
case 'getStorageAt':
url += '/api?module=proxy&action=eth_getStorageAt&address=' + params.address;
url += '&position=' + params.position;
url += '&tag=' + params.blockTag + apiKey;
return fetchJson(url, null, getJsonResult);
return get(url, getJsonResult);
case 'sendTransaction':
url += '/api?module=proxy&action=eth_sendRawTransaction&hex=' + params.signedTransaction;
url += apiKey;
return fetchJson(url, null, getJsonResult).catch((error) => {
return get(url).catch((error) => {
if (error.responseText) {
// "Insufficient funds. The account you tried to send transaction from does not have enough funds. Required 21464000000000 and got: 0"
if (error.responseText.toLowerCase().indexOf('insufficient funds') >= 0) {
@@ -176,19 +188,19 @@ export class EtherscanProvider extends BaseProvider{
url += '&boolean=false';
}
url += apiKey;
return fetchJson(url, null, getJsonResult);
return get(url);
}
throw new Error('getBlock by blockHash not implmeneted');
case 'getTransaction':
url += '/api?module=proxy&action=eth_getTransactionByHash&txhash=' + params.transactionHash;
url += apiKey;
return fetchJson(url, null, getJsonResult);
return get(url);
case 'getTransactionReceipt':
url += '/api?module=proxy&action=eth_getTransactionReceipt&txhash=' + params.transactionHash;
url += apiKey;
return fetchJson(url, null, getJsonResult);
return get(url);
case 'call': {
@@ -200,7 +212,7 @@ export class EtherscanProvider extends BaseProvider{
throw new Error('EtherscanProvider does not support blockTag for call');
}
url += apiKey;
return fetchJson(url, null, getJsonResult);
return get(url);
}
case 'estimateGas': {
@@ -208,7 +220,7 @@ export class EtherscanProvider extends BaseProvider{
if (transaction) { transaction = '&' + transaction; }
url += '/api?module=proxy&action=eth_estimateGas&' + transaction;
url += apiKey;
return fetchJson(url, null, getJsonResult);
return get(url);
}
case 'getLogs':
@@ -244,7 +256,7 @@ export class EtherscanProvider extends BaseProvider{
url += apiKey;
var self = this;
return fetchJson(url, null, getResult).then(function(logs: Array<any>) {
return get(url, getResult).then(function(logs: Array<any>) {
var txs: { [hash: string]: string } = {};
var seq = Promise.resolve();
@@ -272,7 +284,7 @@ export class EtherscanProvider extends BaseProvider{
if (this.network.name !== 'homestead') { return Promise.resolve(0.0); }
url += '/api?module=stats&action=ethprice';
url += apiKey;
return fetchJson(url, null, getResult).then(function(result) {
return get(url, getResult).then(function(result) {
return parseFloat(result.ethusd);
});
@@ -301,6 +313,12 @@ export class EtherscanProvider extends BaseProvider{
url += '&sort=asc' + apiKey;
return fetchJson(url, null, getResult).then((result: Array<any>) => {
this.emit('debug', {
action: 'getHistory',
request: url,
response: result,
provider: this
});
var output: Array<TransactionResponse> = [];
result.forEach((tx) => {
['contractAddress', 'to'].forEach(function(key) {

View File

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

View File

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

View File

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

View File

@@ -6,11 +6,13 @@ import { BaseProvider } from './base-provider';
import { Signer } from '../abstract-signer';
import * as errors from '../errors';
import { getAddress } from '../utils/address';
import { BigNumber } from '../utils/bignumber';
import { hexlify, hexStripZeros } from '../utils/bytes';
import { getNetwork } from '../utils/networks';
import { defineReadOnly, resolveProperties, shallowCopy } from '../utils/properties';
import { checkProperties, defineReadOnly, resolveProperties, shallowCopy } from '../utils/properties';
import { toUtf8Bytes } from '../utils/utf8';
import { fetchJson, poll } from '../utils/web';
@@ -18,9 +20,8 @@ import { fetchJson, poll } from '../utils/web';
import { Arrayish } from '../utils/bytes';
import { Network, Networkish } from '../utils/networks';
import { ConnectionInfo } from '../utils/web';
import { BlockTag, TransactionRequest, TransactionResponse } from '../providers/abstract-provider';
import * as errors from '../errors';
import { BlockTag, TransactionRequest, TransactionResponse } from '../providers/abstract-provider';
function timer(timeout: number): Promise<any> {
return new Promise(function(resolve) {
@@ -33,7 +34,7 @@ function timer(timeout: number): Promise<any> {
function getResult(payload: { error?: { code?: number, data?: any, message?: string }, result?: any }): any {
if (payload.error) {
// @TODO: not any
var error: any = new Error(payload.error.message);
let error: any = new Error(payload.error.message);
error.code = payload.error.code;
error.data = payload.error.data;
throw error;
@@ -78,15 +79,6 @@ export class JsonRpcSigner extends Signer {
}
}
/* May add back in the future; for now it is considered confusing. :)
get address(): string {
if (!this._address) {
errors.throwError('no sync sync address available; use getAddress', errors.UNSUPPORTED_OPERATION, { operation: 'address' });
}
return this._address
}
*/
getAddress(): Promise<string> {
if (this._address) {
return Promise.resolve(this._address);
@@ -109,31 +101,32 @@ export class JsonRpcSigner extends Signer {
return this.provider.getTransactionCount(this.getAddress(), blockTag);
}
sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse> {
let tx: TransactionRequest = shallowCopy(transaction);
sendUncheckedTransaction(transaction: TransactionRequest): Promise<string> {
transaction = shallowCopy(transaction);
if (tx.from == null) {
tx.from = this.getAddress().then((address) => {
if (!address) { return null; }
return address.toLowerCase();
});
}
let fromAddress = this.getAddress().then((address) => {
if (address) { address = address.toLowerCase(); }
return address;
});
// The JSON-RPC for eth_sendTransaction uses 90000 gas; if the user
// wishes to use this, it is easy to specify explicitly, otherwise
// we look it up for them.
if (transaction.gasLimit == null) {
tx.gasLimit = this.provider.estimateGas(tx);
let estimate = shallowCopy(transaction);
estimate.from = fromAddress;
transaction.gasLimit = this.provider.estimateGas(estimate);
}
return resolveProperties(tx).then((tx) => {
return this.provider.send('eth_sendTransaction', [ JsonRpcProvider.hexlifyTransaction(tx) ]).then((hash) => {
return poll(() => {
return this.provider.getTransaction(hash).then((tx: TransactionResponse) => {
if (tx === null) { return undefined; }
return this.provider._wrapTransaction(tx, hash);
});
}, { onceBlock: this.provider }).catch((error: Error) => {
(<any>error).transactionHash = hash;
throw error;
});
return Promise.all([
resolveProperties(transaction),
fromAddress
]).then((results) => {
let tx = results[0];
let hexTx = JsonRpcProvider.hexlifyTransaction(tx);
hexTx.from = results[1];
return this.provider.send('eth_sendTransaction', [ hexTx ]).then((hash) => {
return hash;
}, (error) => {
if (error.responseText) {
// See: JsonRpcProvider.sendTransaction (@TODO: Expose a ._throwError??)
@@ -158,8 +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);
});
}, { onceBlock: this.provider }).catch((error: Error) => {
(<any>error).transactionHash = hash;
throw error;
});
});
}
signMessage(message: Arrayish | string): Promise<string> {
var data = ((typeof(message) === 'string') ? toUtf8Bytes(message): message);
let data = ((typeof(message) === 'string') ? toUtf8Bytes(message): message);
return this.getAddress().then((address) => {
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign
@@ -168,7 +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 ]);
@@ -240,14 +247,22 @@ export class JsonRpcProvider extends BaseProvider {
}
send(method: string, params: any): Promise<any> {
var request = {
let request = {
method: method,
params: params,
id: 42,
jsonrpc: "2.0"
};
return fetchJson(this.connection, JSON.stringify(request), getResult);
return fetchJson(this.connection, JSON.stringify(request), getResult).then((result) => {
this.emit('debug', {
action: 'send',
request: request,
response: result,
provider: this
});
return result;
});
}
perform(method: string, params: any): Promise<any> {
@@ -325,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) {
@@ -335,8 +350,9 @@ export class JsonRpcProvider extends BaseProvider {
self.send('eth_getFilterChanges', [ filterId ]).then(function(hashes: Array<string>) {
if (self._pendingFilter != pendingFilter) { return null; }
var seq = Promise.resolve();
let seq = Promise.resolve();
hashes.forEach(function(hash) {
// @TODO: This should be garbage collected at some point... How? When?
self._emitted['t:' + hash.toLowerCase()] = 'pending';
seq = seq.then(function() {
return self.getTransaction(hash).then(function(tx) {
@@ -376,21 +392,19 @@ export class JsonRpcProvider extends BaseProvider {
// NOTE: This allows a TransactionRequest, but all values should be resolved
// before this is called
static hexlifyTransaction(transaction: TransactionRequest, allowExtra?: { [key: string]: boolean }): { [key: string]: string } {
if (!allowExtra) { allowExtra = {}; }
for (let key in transaction) {
if (!allowedTransactionKeys[key] && !allowExtra[key]) {
errors.throwError('invalid key - ' + key, errors.INVALID_ARGUMENT, {
argument: 'transaction',
value: transaction,
key: key
});
// Check only allowed properties are given
let allowed = shallowCopy(allowedTransactionKeys);
if (allowExtra) {
for (let key in allowExtra) {
if (allowExtra[key]) { allowed[key] = true; }
}
}
checkProperties(transaction, allowed);
let result: { [key: string]: string } = {};
// Some nodes (INFURA ropsten; INFURA mainnet is fine) don't like extra zeros.
// Some nodes (INFURA ropsten; INFURA mainnet is fine) don't like leading zeros.
['gasLimit', 'gasPrice', 'nonce', 'value'].forEach(function(key) {
if ((<any>transaction)[key] == null) { return; }
let value = hexStripZeros(hexlify((<any>transaction)[key]));

View File

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

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

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

@@ -109,7 +109,7 @@ export class HDNode {
// Base path
var mnemonic = this.mnemonic;
var path = this.path;
if (path) { path += '/' + index; }
if (path) { path += '/' + (index & ~HardenedBit); }
if (index & HardenedBit) {
// Data = 0x00 || ser_256(k_par)
@@ -201,6 +201,8 @@ export function mnemonicToSeed(mnemonic: string, password?: string): string {
export function mnemonicToEntropy(mnemonic: string, wordlist?: Wordlist): string {
if (!wordlist) { wordlist = langEn; }
errors.checkNormalize();
var words = wordlist.split(mnemonic);
if ((words.length % 3) !== 0) { throw new Error('invalid mnemonic'); }

View File

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

View File

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

View File

@@ -7,33 +7,69 @@ export type Network = {
name: string,
chainId: number,
ensAddress?: string,
_defaultProvider?: (providers: any) => any
}
export type Networkish = Network | string | number;
function ethDefaultProvider(network: string): (providers: any) => any {
return function(providers: any): any {
let providerList: Array<any> = [];
if (providers.InfuraProvider) {
providerList.push(new providers.InfuraProvider(network));
}
if (providers.EtherscanProvider) {
providerList.push(new providers.EtherscanProvider(network));
}
if (providerList.length === 0) { return null; }
if (providers.FallbackProvider) {
return new providers.FallbackProvider(providerList);;
}
return providerList[0];
}
}
function etcDefaultProvider(url: string, network: string): (providers: any) => any {
return function(providers: any): any {
if (providers.JsonRpcProvider) {
return new providers.JsonRpcProvider(url, network);
}
return null;
}
}
const homestead: Network = {
chainId: 1,
ensAddress: "0x314159265dd8dbb310642f98f50c066173c1259b",
name: "homestead"
name: "homestead",
_defaultProvider: ethDefaultProvider('homestead')
};
const ropsten: Network = {
chainId: 3,
ensAddress: "0x112234455c3a32fd11230c42e7bccd4a84e02010",
name: "ropsten"
name: "ropsten",
_defaultProvider: ethDefaultProvider('ropsten')
};
const networks: { [name: string]: { chainId: number, ensAddress?: string } } = {
const networks: { [name: string]: Network } = {
unspecified: {
chainId: 0
chainId: 0,
name: 'unspecified'
},
homestead: homestead,
mainnet: homestead,
morden: {
chainId: 2
chainId: 2,
name: 'morden'
},
ropsten: ropsten,
@@ -41,19 +77,27 @@ const networks: { [name: string]: { chainId: number, ensAddress?: string } } = {
rinkeby: {
chainId: 4,
ensAddress: "0xe7410170f87102DF0055eB195163A03B7F2Bff4A"
ensAddress: "0xe7410170f87102DF0055eB195163A03B7F2Bff4A",
name: 'rinkeby',
_defaultProvider: ethDefaultProvider('rinkeby')
},
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 +108,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 +134,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 +156,11 @@ export function getNetwork(network: Networkish): Network {
errors.throwError('network chainId mismatch', errors.INVALID_ARGUMENT, { arg: 'network', value: network });
}
// Standard Network
// Standard Network (allow overriding the ENS address)
return {
name: network.name,
chainId: n.chainId,
ensAddress: n.ensAddress
ensAddress: (network.ensAddress || n.ensAddress || null),
_defaultProvider: (network._defaultProvider || n._defaultProvider || null)
};
}

View File

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

View File

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

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

View File

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

View File

@@ -3,6 +3,7 @@
import { XMLHttpRequest } from 'xmlhttprequest';
import { encode as base64Encode } from './base64';
import { shallowCopy } from './properties';
import { toUtf8Bytes } from './utf8';
import * as errors from '../errors';
@@ -134,6 +135,9 @@ export function fetchJson(connection: string | ConnectionInfo, json: string, pro
let jsonError: any = new Error('invalid json response');
jsonError.orginialError = error;
jsonError.responseText = request.responseText;
if (json != null) {
jsonError.requestBody = json;
}
jsonError.url = url;
reject(jsonError);
return;
@@ -162,7 +166,7 @@ export function fetchJson(connection: string | ConnectionInfo, json: string, pro
}
try {
if (json) {
if (json != null) {
request.send(json);
} else {
request.send();
@@ -180,6 +184,7 @@ export function fetchJson(connection: string | ConnectionInfo, json: string, pro
export function poll(func: () => Promise<any>, options?: PollOptions): Promise<any> {
if (!options) { options = {}; }
options = shallowCopy(options);
if (options.floor == null) { options.floor = 0; }
if (options.ceiling == null) { options.ceiling = 10000; }
if (options.interval == null) { options.interval = 250; }

View File

@@ -10,7 +10,7 @@ import { defineReadOnly, resolveProperties, shallowCopy } from './utils/properti
import { randomBytes } from './utils/random-bytes';
import * as secretStorage from './utils/secret-storage';
import { SigningKey } from './utils/signing-key';
import { serialize as serializeTransaction } from './utils/transaction';
import { populateTransaction, serialize as serializeTransaction } from './utils/transaction';
import { Wordlist } from './utils/wordlist';
// Imported Abstracts
@@ -24,10 +24,6 @@ import { BlockTag, TransactionRequest, TransactionResponse } from './providers/a
import * as errors from './errors';
const allowedTransactionKeys: { [ key: string ]: boolean } = {
chainId: true, data: true, gasLimit: true, gasPrice:true, nonce: true, to: true, value: true
}
export class Wallet extends AbstractSigner {
readonly provider: Provider;
@@ -51,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; }
@@ -66,25 +62,15 @@ export class Wallet extends AbstractSigner {
return new Wallet(this.signingKey, provider);
}
getAddress(): Promise<string> {
return Promise.resolve(this.address);
}
sign(transaction: TransactionRequest): Promise<string> {
for (let key in transaction) {
if (!allowedTransactionKeys[key]) {
errors.throwError('unsupported transaction property - ' + key, errors.INVALID_ARGUMENT, {
argument: 'transaction',
value: transaction,
key: key
});
}
}
return resolveProperties(transaction).then((tx) => {
let rawTx = serializeTransaction(tx);
let signature = this.signingKey.signDigest(keccak256(rawTx));
return Promise.resolve(serializeTransaction(tx, signature));
return serializeTransaction(tx, signature);
});
}
@@ -106,35 +92,16 @@ export class Wallet extends AbstractSigner {
sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse> {
if (!this.provider) { throw new Error('missing provider'); }
if (!transaction || typeof(transaction) !== 'object') {
throw new Error('invalid transaction object');
if (transaction.nonce == null) {
transaction = shallowCopy(transaction);
transaction.nonce = this.getTransactionCount("pending");
}
let tx = shallowCopy(transaction);
if (tx.to != null) {
tx.to = this.provider.resolveName(tx.to);
}
if (tx.gasPrice == null) {
tx.gasPrice = this.provider.getGasPrice();
}
if (tx.nonce == null) {
tx.nonce = this.getTransactionCount();
}
if (tx.gasLimit == null) {
let estimate = shallowCopy(tx);
estimate.from = this.getAddress();
tx.gasLimit = this.provider.estimateGas(estimate);
}
if (tx.chainId == null) {
tx.chainId = this.provider.getNetwork().then((network) => network.chainId);
}
return this.provider.sendTransaction(this.sign(tx));
return populateTransaction(transaction, this.provider, this.address).then((tx) => {
return this.sign(tx).then((signedTransaction) => {
return this.provider.sendTransaction(signedTransaction);
});
});
}
encrypt(password: Arrayish | string, options?: any, progressCallback?: ProgressCallback): Promise<string> {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

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

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

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

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

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

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

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

View File

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

View File

@@ -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,6 +218,7 @@ var blockchainData = {
],
logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000080000000202000000",
status: 1,
to: "0xB70560a43A9aBf6ea2016F40a3e84B8821E134c5",
transactionHash: "0xf724f1d6813f13fb523c5f6af6261d06d41138dd094fff723e09fb0f893f03e6",
transactionIndex: 0x2
},

View File

@@ -327,3 +327,24 @@ 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);
});
});

View File

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

View File

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

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

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

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

View File

@@ -75,7 +75,7 @@ var HDNode = /** @class */ (function () {
var mnemonic = this.mnemonic;
var path = this.path;
if (path) {
path += '/' + index;
path += '/' + (index & ~HardenedBit);
}
if (index & HardenedBit) {
// Data = 0x00 || ser_256(k_par)
@@ -166,6 +166,7 @@ function mnemonicToEntropy(mnemonic, wordlist) {
if (!wordlist) {
wordlist = lang_en_1.langEn;
}
errors.checkNormalize();
var words = wordlist.split(mnemonic);
if ((words.length % 3) !== 0) {
throw new Error('invalid mnemonic');

7
utils/index.d.ts vendored
View File

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

View File

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

View File

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

1
utils/networks.d.ts vendored
View File

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

View File

@@ -8,39 +8,78 @@ 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')
},
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 +89,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 +116,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 +134,12 @@ function getNetwork(network) {
if (network.chainId !== 0 && network.chainId !== n.chainId) {
errors.throwError('network chainId mismatch', errors.INVALID_ARGUMENT, { arg: 'network', value: network });
}
// Standard Network
// Standard Network (allow overriding the ENS address)
return {
name: network.name,
chainId: n.chainId,
ensAddress: n.ensAddress
ensAddress: (network.ensAddress || n.ensAddress || null),
_defaultProvider: (network._defaultProvider || n._defaultProvider || null)
};
}
exports.getNetwork = getNetwork;

View File

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

View File

@@ -1,5 +1,13 @@
'use strict';
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
var errors = __importStar(require("../errors"));
function defineReadOnly(object, name, value) {
Object.defineProperty(object, name, {
enumerable: true,
@@ -38,6 +46,24 @@ function resolveProperties(object) {
});
}
exports.resolveProperties = resolveProperties;
function checkProperties(object, properties) {
if (!object || typeof (object) !== 'object') {
errors.throwError('invalid object', errors.INVALID_ARGUMENT, {
argument: 'object',
value: object
});
}
Object.keys(object).forEach(function (key) {
if (!properties[key]) {
errors.throwError('invalid object key - ' + key, errors.INVALID_ARGUMENT, {
argument: 'transaction',
value: object,
key: key
});
}
});
}
exports.checkProperties = checkProperties;
function shallowCopy(object) {
var result = {};
for (var key in object) {

View File

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

View File

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

View File

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

View File

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

View File

@@ -9,6 +9,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
Object.defineProperty(exports, "__esModule", { value: true });
var xmlhttprequest_1 = require("xmlhttprequest");
var base64_1 = require("./base64");
var properties_1 = require("./properties");
var utf8_1 = require("./utf8");
var errors = __importStar(require("../errors"));
function fetchJson(connection, json, processFunc) {
@@ -98,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;
@@ -123,7 +127,7 @@ function fetchJson(connection, json, processFunc) {
reject(error);
};
try {
if (json) {
if (json != null) {
request.send(json);
}
else {
@@ -144,6 +148,7 @@ function poll(func, options) {
if (!options) {
options = {};
}
options = properties_1.shallowCopy(options);
if (options.floor == null) {
options.floor = 0;
}

View File

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -131,9 +131,7 @@ var LangJa = /** @class */ (function (_super) {
return wordlist.indexOf(word);
};
LangJa.prototype.split = function (mnemonic) {
if (!mnemonic.normalize) {
errors.throwError('Japanese is unsupported on this platform; missing String.prototype.normalize', errors.UNSUPPORTED_OPERATION, { operation: 'String.prototype.normalize' });
}
errors.checkNormalize();
return mnemonic.split(/(?:\u3000| )+/g);
};
LangJa.prototype.join = function (words) {