2
0
mirror of synced 2025-02-24 20:18:07 +00:00

Refactored types for TypeScript and to remove circular dependencies.

This commit is contained in:
Richard Moore 2018-07-14 17:19:08 -04:00
parent 3f9f0e02e5
commit 5f3ceec6f9
No known key found for this signature in database
GPG Key ID: 525F70A6FCABC295
23 changed files with 552 additions and 370 deletions

View File

@ -2,16 +2,17 @@
import { EventDescription, Interface } from './interface';
import { Block, Log, Provider, TransactionReceipt, TransactionRequest, TransactionResponse } from '../providers/provider';
import { Signer } from '../wallet/wallet';
import { defaultAbiCoder, formatSignature, ParamType, parseSignature } from '../utils/abi-coder';
import { getContractAddress } from '../utils/address';
import { getAddress, getContractAddress } from '../utils/address';
import { BigNumber, ConstantZero } from '../utils/bignumber';
import { hexDataLength, hexDataSlice, isHexString } from '../utils/bytes';
import { defineReadOnly, jsonCopy, shallowCopy } from '../utils/properties';
import { poll } from '../utils/web';
import { MinimalProvider, Signer } from '../utils/types';
import { EventFilter, Event, Listener, Log, TransactionRequest, TransactionResponse } from '../utils/types';
export { EventFilter, Event, Listener, Log, TransactionRequest, TransactionResponse };
import * as errors from '../utils/errors';
var allowedTransactionKeys: { [ key: string ]: boolean } = {
@ -21,7 +22,7 @@ var allowedTransactionKeys: { [ key: string ]: boolean } = {
// Recursively replaces ENS names with promises to resolve the name and
// stalls until all promises have returned
// @TODO: Expand this to resolve any promises too
function resolveAddresses(provider: Provider, value: any, paramType: ParamType | Array<ParamType>): Promise<any> {
function resolveAddresses(provider: MinimalProvider, value: any, paramType: ParamType | Array<ParamType>): Promise<any> {
if (Array.isArray(paramType)) {
var promises: Array<Promise<string>> = [];
paramType.forEach((paramType, index) => {
@ -171,32 +172,6 @@ function runMethod(contract: Contract, functionName: string, estimateOnly: boole
}
}
export type Listener = (...args: Array<any>) => void;
export type EventFilter = {
address?: string;
topics?: Array<string>;
// @TODO: Support OR-style topcis; backwards compatible to make this change
//topics?: Array<string | Array<string>>
};
export type ContractEstimate = (...params: Array<any>) => Promise<BigNumber>;
export type ContractFunction = (...params: Array<any>) => Promise<any>;
export type ContractFilter = (...params: Array<any>) => EventFilter;
export interface Event extends Log {
args: Array<any>;
decode: (data: string, topics?: Array<string>) => any;
event: string;
eventSignature: string;
removeListener: () => void;
getBlock: () => Promise<Block>;
getTransaction: () => Promise<TransactionResponse>;
getTransactionReceipt: () => Promise<TransactionReceipt>;
}
function getEventTag(filter: EventFilter): string {
return (filter.address || '') + (filter.topics ? filter.topics.join(':'): '');
}
@ -220,32 +195,28 @@ type _Event = {
};
export type ErrorCallback = (error: Error) => void;
export type Contractish = Array<string | ParamType> | Interface | string;
export class Contract {
readonly address: string;
readonly interface: Interface;
readonly signer: Signer;
readonly provider: Provider;
readonly provider: MinimalProvider;
readonly estimate: Bucket<ContractEstimate>;
readonly functions: Bucket<ContractFunction>;
readonly estimate: Bucket<(...params: Array<any>) => Promise<BigNumber>>;
readonly functions: Bucket<(...params: Array<any>) => Promise<any>>;
readonly filters: Bucket<ContractFilter>;
readonly filters: Bucket<(...params: Array<any>) => EventFilter>;
readonly addressPromise: Promise<string>;
// This is only set if the contract was created with a call to deploy
readonly deployTransaction: TransactionResponse;
private _onerror: ErrorCallback;
// https://github.com/Microsoft/TypeScript/issues/5453
// Once this issue is resolved (there are open PR) we can do this nicer
// by making addressOrName default to null for 2 operand calls. :)
constructor(addressOrName: string, contractInterface: Contractish, signerOrProvider: Signer | Provider) {
constructor(addressOrName: string, contractInterface: Array<string | ParamType> | string | Interface, signerOrProvider: Signer | MinimalProvider) {
errors.checkNew(this, Contract);
// @TODO: Maybe still check the addressOrName looks like a valid address or name?
@ -259,7 +230,7 @@ export class Contract {
if (signerOrProvider instanceof Signer) {
defineReadOnly(this, 'provider', signerOrProvider.provider);
defineReadOnly(this, 'signer', signerOrProvider);
} else if (signerOrProvider instanceof Provider) {
} else if (signerOrProvider instanceof MinimalProvider) {
defineReadOnly(this, 'provider', signerOrProvider);
defineReadOnly(this, 'signer', null);
} else {
@ -291,13 +262,21 @@ export class Contract {
this._events = [];
defineReadOnly(this, 'address', addressOrName);
defineReadOnly(this, 'addressPromise', this.provider.resolveName(addressOrName).then((address) => {
if (address == null) { throw new Error('name not found'); }
return address;
}).catch((error: Error) => {
console.log('ERROR: Cannot find Contract - ' + addressOrName);
throw error;
}));
if (this.provider) {
defineReadOnly(this, 'addressPromise', this.provider.resolveName(addressOrName).then((address) => {
if (address == null) { throw new Error('name not found'); }
return address;
}).catch((error: Error) => {
console.log('ERROR: Cannot find Contract - ' + addressOrName);
throw error;
}));
} else {
try {
defineReadOnly(this, 'addressPromise', Promise.resolve(getAddress(addressOrName)));
} catch (error) {
errors.throwError('provider is required to use non-address contract address', errors.INVALID_ARGUMENT, { argument: 'addressOrName', value: addressOrName });
}
}
Object.keys(this.interface.functions).forEach((name) => {
var run = runMethod(this, name, false);
@ -315,14 +294,9 @@ export class Contract {
});
}
get onerror() { return this._onerror; }
set onerror(callback: ErrorCallback) {
this._onerror = callback;
}
// @TODO: Allow timeout?
deployed(): Promise<Contract> {
// If we were just deployed, we know the transaction we should occur in
if (this.deployTransaction) {
return this.deployTransaction.wait().then(() => {
@ -362,7 +336,7 @@ export class Contract {
}
// Reconnect to a different signer or provider
connect(signerOrProvider: Signer | Provider): Contract {
connect(signerOrProvider: Signer | MinimalProvider): Contract {
return new Contract(this.address, this.interface, signerOrProvider);
}

View File

@ -10,9 +10,19 @@ import { id } from '../utils/hash';
import { keccak256 } from '../utils/keccak256';
import { defineReadOnly, defineFrozen } from '../utils/properties';
import { DeployDescription, EventDescription, FunctionDescription, Indexed } from '../utils/types';
export { DeployDescription, EventDescription, FunctionDescription, Indexed };
import * as errors from '../utils/errors';
export class Description {
class _Indexed extends Indexed {
constructor(hash: string) {
super();
defineReadOnly(this, 'hash', hash);
}
}
class _Description {
readonly type: string;
constructor(info: any) {
for (var key in info) {
@ -26,11 +36,8 @@ export class Description {
}
}
export class Indexed extends Description {
readonly hash: string;
}
export class DeployDescription extends Description {
class _DeployDescription extends _Description implements DeployDescription {
readonly type: "deploy";
readonly inputs: Array<ParamType>;
readonly payable: boolean;
@ -58,7 +65,8 @@ export class DeployDescription extends Description {
}
}
export class FunctionDescription extends Description {
class _FunctionDescription extends _Description implements FunctionDescription {
readonly type: "call" | "transaction";
readonly name: string;
readonly signature: string;
readonly sighash: string;
@ -98,12 +106,13 @@ export class FunctionDescription extends Description {
}
}
class Result extends Description {
class Result extends _Description {
[key: string]: any;
[key: number]: any;
}
export class EventDescription extends Description {
class _EventDescription extends _Description implements EventDescription {
readonly type: "event";
readonly name: string;
readonly signature: string;
@ -190,10 +199,11 @@ export class EventDescription extends Description {
this.inputs.forEach(function(input, index) {
if (input.indexed) {
if (topics == null) {
result[index] = new Indexed({ type: 'indexed', hash: null });
result[index] = new _Indexed(null);
} else if (inputDynamic[index]) {
result[index] = new Indexed({ type: 'indexed', hash: resultIndexed[indexedIndex++] });
result[index] = new _Indexed(resultIndexed[indexedIndex++]);
} else {
result[index] = resultIndexed[indexedIndex++];
}
@ -209,7 +219,7 @@ export class EventDescription extends Description {
}
}
class TransactionDescription extends Description {
class TransactionDescription extends _Description {
readonly name: string;
readonly args: Array<any>;
readonly signature: string;
@ -218,7 +228,7 @@ class TransactionDescription extends Description {
readonly value: BigNumber;
}
class LogDescription extends Description {
class LogDescription extends _Description {
readonly name: string;
readonly signature: string;
readonly topic: string;
@ -229,10 +239,10 @@ class LogDescription extends Description {
function addMethod(method: any): void {
switch (method.type) {
case 'constructor': {
let description = new DeployDescription({
let description = new _DeployDescription({
inputs: method.inputs,
payable: (method.payable == null || !!method.payable),
type: 'deploy'
type: "deploy"
});
if (!this.deployFunction) { this.deployFunction = description; }
@ -244,7 +254,7 @@ function addMethod(method: any): void {
let signature = formatSignature(method).replace(/tuple/g, '');
let sighash = id(signature).substring(0, 10);
let description = new FunctionDescription({
let description = new _FunctionDescription({
inputs: method.inputs,
outputs: method.outputs,
@ -271,7 +281,7 @@ function addMethod(method: any): void {
case 'event': {
let signature = formatSignature(method).replace(/tuple/g, '');
let description = new EventDescription({
let description = new _EventDescription({
name: method.name,
signature: signature,

View File

@ -5,10 +5,11 @@ import {} from './utils/shims';
import { Contract, Interface } from './contracts';
import providers from './providers';
import * as providers from './providers';
import * as errors from './utils/errors';
import { getNetwork } from './providers/networks';
import utils from './utils';
import * as types from './utils/types';
import * as utils from './utils';
import { HDNode, SigningKey, Wallet } from './wallet';
import * as wordlists from './wordlists';
@ -28,6 +29,8 @@ export {
getNetwork,
providers,
types,
errors,
constants,
utils,
@ -49,6 +52,8 @@ export default {
getNetwork,
providers,
types,
errors,
constants,
utils,

View File

@ -1,9 +1,9 @@
import { BlockTag, checkTransactionResponse, Provider, TransactionRequest, TransactionResponse } from './provider';
import { Networkish } from './networks';
import { Provider } from './provider';
import { hexlify, hexStripZeros } from '../utils/bytes';
import { defineReadOnly } from '../utils/properties';
import { BlockTag, Networkish, TransactionRequest, TransactionResponse } from '../utils/types';
import { fetchJson } from '../utils/web';
import * as errors from '../utils/errors';
@ -278,7 +278,7 @@ export class EtherscanProvider extends Provider{
if (tx.creates == null && tx.contractAddress != null) {
tx.creates = tx.contractAddress;
}
let item = checkTransactionResponse(tx);
let item = Provider.checkTransactionResponse(tx);
if (tx.timeStamp) { item.timestamp = parseInt(tx.timeStamp); }
output.push(item);
});

View File

@ -1,8 +1,9 @@
'use strict';
import { Network } from './networks';
import { Provider } from './provider';
import { Network } from '../utils/types';
import * as errors from '../utils/errors';
// Returns:

View File

@ -2,15 +2,15 @@
import { Provider } from './provider';
import { Network } from './networks';
import { EtherscanProvider } from './etherscan-provider';
import { FallbackProvider } from './fallback-provider';
import { IpcProvider } from './ipc-provider';
import { InfuraProvider } from './infura-provider';
import { JsonRpcProvider } from './json-rpc-provider';
import { JsonRpcProvider, JsonRpcSigner } from './json-rpc-provider';
import { Web3Provider } from './web3-provider';
import { Network } from '../utils/types';
function getDefaultProvider(network?: Network | string): FallbackProvider {
return new FallbackProvider([
new InfuraProvider(network),
@ -29,7 +29,9 @@ export {
JsonRpcProvider,
Web3Provider,
IpcProvider
IpcProvider,
JsonRpcSigner
};
export default {
@ -43,5 +45,7 @@ export default {
JsonRpcProvider,
Web3Provider,
IpcProvider
IpcProvider,
JsonRpcSigner
};

View File

@ -1,9 +1,10 @@
'use strict';
import { JsonRpcProvider, JsonRpcSigner } from './json-rpc-provider';
import { getNetwork, Networkish } from './networks';
import { getNetwork } from './networks';
import { defineReadOnly } from '../utils/properties';
import { Networkish } from '../utils/types';
import * as errors from '../utils/errors';

View File

@ -2,8 +2,8 @@
import net from 'net';
import { JsonRpcProvider } from './json-rpc-provider';
import { Networkish } from './networks';
import { Networkish } from '../utils/types';
import { defineReadOnly } from '../utils/properties';
import * as errors from '../utils/errors';

View File

@ -2,14 +2,14 @@
// See: https://github.com/ethereum/wiki/wiki/JSON-RPC
import { getNetwork, Network, Networkish } from './networks';
import { BlockTag, Provider, TransactionRequest, TransactionResponse } from './provider';
import { Signer } from '../wallet/wallet';
import { getNetwork } from './networks';
import { Provider } from './provider';
import { getAddress } from '../utils/address';
import { BigNumber } from '../utils/bignumber';
import { Arrayish, hexlify, hexStripZeros } from '../utils/bytes';
import { defineReadOnly, resolveProperties, shallowCopy } from '../utils/properties';
import { BlockTag, Network, Networkish, Signer, TransactionRequest, TransactionResponse } from '../utils/types';
import { toUtf8Bytes } from '../utils/utf8';
import { ConnectionInfo, fetchJson, poll } from '../utils/web';
@ -35,30 +35,6 @@ function getResult(payload: { error?: { code?: number, data?: any, message?: str
return payload.result;
}
// Convert an ethers.js transaction into a JSON-RPC transaction
// - gasLimit => gas
// - All values hexlified
// - All numeric values zero-striped
// @TODO: Not any, a dictionary of string to strings
export function hexlifyTransaction(transaction: TransactionRequest): any {
var result: any = {};
// Some nodes (INFURA ropsten; INFURA mainnet is fine) don't like extra zeros.
['gasLimit', 'gasPrice', 'nonce', 'value'].forEach(function(key) {
if ((<any>transaction)[key] == null) { return; }
let value = hexStripZeros(hexlify((<any>transaction)[key]));
if (key === 'gasLimit') { key = 'gas'; }
result[key] = value;
});
['from', 'to', 'data'].forEach(function(key) {
if ((<any>transaction)[key] == null) { return; }
result[key] = hexlify((<any>transaction)[key]);
});
return result;
}
function getLowerCase(value: string): string {
if (value) { return value.toLowerCase(); }
return value;
@ -119,7 +95,7 @@ export class JsonRpcSigner extends Signer {
}
return resolveProperties(tx).then((tx) => {
tx = hexlifyTransaction(tx);
tx = JsonRpcProvider.hexlifyTransaction(tx);
return this.provider.send('eth_sendTransaction', [ tx ]).then((hash) => {
return poll(() => {
return this.provider.getTransaction(hash).then((tx: TransactionResponse) => {
@ -260,10 +236,10 @@ export class JsonRpcProvider extends Provider {
return this.send('eth_getTransactionReceipt', [ params.transactionHash ]);
case 'call':
return this.send('eth_call', [ hexlifyTransaction(params.transaction), 'latest' ]);
return this.send('eth_call', [ JsonRpcProvider.hexlifyTransaction(params.transaction), 'latest' ]);
case 'estimateGas':
return this.send('eth_estimateGas', [ hexlifyTransaction(params.transaction) ]);
return this.send('eth_estimateGas', [ JsonRpcProvider.hexlifyTransaction(params.transaction) ]);
case 'getLogs':
if (params.filter && params.filter.address != null) {
@ -324,4 +300,28 @@ export class JsonRpcProvider extends Provider {
_stopPending(): void {
this._pendingFilter = null;
}
// Convert an ethers.js transaction into a JSON-RPC transaction
// - gasLimit => gas
// - All values hexlified
// - All numeric values zero-striped
// @TODO: Not any, a dictionary of string to strings
static hexlifyTransaction(transaction: TransactionRequest): any {
var result: any = {};
// Some nodes (INFURA ropsten; INFURA mainnet is fine) don't like extra zeros.
['gasLimit', 'gasPrice', 'nonce', 'value'].forEach(function(key) {
if ((<any>transaction)[key] == null) { return; }
let value = hexStripZeros(hexlify((<any>transaction)[key]));
if (key === 'gasLimit') { key = 'gas'; }
result[key] = value;
});
['from', 'to', 'data'].forEach(function(key) {
if ((<any>transaction)[key] == null) { return; }
result[key] = hexlify((<any>transaction)[key]);
});
return result;
}
}

View File

@ -1,15 +1,10 @@
'use strict';
import { Network, Networkish } from '../utils/types';
export { Network, Networkish };
import * as errors from '../utils/errors';
export type Network = {
name: string,
chainId: number,
ensAddress?: string,
}
export type Networkish = Network | string | number;
const homestead = {
chainId: 1,

View File

@ -4,7 +4,7 @@ import { getNetwork, Network, Networkish } from './networks';
import { getAddress, getContractAddress } from '../utils/address';
import { BigNumber, bigNumberify, BigNumberish } from '../utils/bignumber';
import { Arrayish, hexDataLength, hexDataSlice, hexlify, hexStripZeros, isHexString, stripZeros } from '../utils/bytes';
import { hexDataLength, hexDataSlice, hexlify, hexStripZeros, isHexString, stripZeros } from '../utils/bytes';
import { namehash } from '../utils/hash';
import { defineReadOnly, resolveProperties, shallowCopy } from '../utils/properties';
import { encode as rlpEncode } from '../utils/rlp';
@ -12,102 +12,13 @@ import { parse as parseTransaction, Transaction } from '../utils/transaction';
import { toUtf8String } from '../utils/utf8';
import { poll } from '../utils/web';
import { MinimalProvider } from '../utils/types';
import { Block, BlockTag, EventType, Filter, Listener, Log, TransactionReceipt, TransactionRequest, TransactionResponse } from '../utils/types';
export { Block, BlockTag, EventType, Filter, Listener, Log, TransactionReceipt, TransactionRequest, TransactionResponse };
import * as errors from '../utils/errors';
//////////////////////////////
// Exported Types
export type BlockTag = string | number;
export interface Block {
hash: string;
parentHash: string;
number: number;
timestamp: number;
nonce: string;
difficulty: number;
gasLimit: BigNumber;
gasUsed: BigNumber;
miner: string;
extraData: string;
transactions: Array<string>;
}
export type TransactionRequest = {
to?: string | Promise<string>,
from?: string | Promise<string>,
nonce?: number | string | Promise<number | string>,
gasLimit?: BigNumberish | Promise<BigNumberish>,
gasPrice?: BigNumberish | Promise<BigNumberish>,
data?: Arrayish | Promise<Arrayish>,
value?: BigNumberish | Promise<BigNumberish>,
chainId?: number | Promise<number>,
}
export interface TransactionResponse extends Transaction {
// Only if a transaction has been mined
blockNumber?: number,
blockHash?: string,
timestamp?: number,
// Not optional (as it is in Transaction)
from: string;
// The raw transaction
raw?: string,
// This function waits until the transaction has been mined
wait: (timeout?: number) => Promise<TransactionReceipt>
};
export interface TransactionReceipt {
contractAddress?: string,
transactionIndex?: number,
root?: string,
gasUsed?: BigNumber,
logsBloom?: string,
blockHash?: string,
transactionHash?: string,
logs?: Array<Log>,
blockNumber?: number,
cumulativeGasUsed?: BigNumber,
byzantium: boolean,
status?: number // @TOOD: Check 0 or 1?
};
export type Filter = {
fromBlock?: BlockTag,
toBlock?: BlockTag,
address?: string,
topics?: Array<string | Array<string>>,
}
export interface Log {
blockNumber?: number;
blockHash?: string;
transactionIndex?: number;
removed?: boolean;
transactionLogIndex?: number,
address: string;
data: string;
topics: Array<string>;
transactionHash?: string;
logIndex?: number;
}
//////////////////////////////
// Request and Response Checking
@ -282,7 +193,7 @@ var formatTransaction = {
raw: allowNull(hexlify),
};
export function checkTransactionResponse(transaction: any): TransactionResponse {
function checkTransactionResponse(transaction: any): TransactionResponse {
// Rename gas to gasLimit
if (transaction.gas != null && transaction.gasLimit == null) {
@ -594,8 +505,6 @@ export class ProviderSigner extends Signer {
}
*/
export type Listener = (...args: Array<any>) => void;
/**
* EventType
* - "block"
@ -607,15 +516,13 @@ export type Listener = (...args: Array<any>) => void;
* - transaction hash
*/
export type EventType = string | Array<string> | Filter;
type _Event = {
listener: Listener;
once: boolean;
tag: string;
}
export class Provider {
export class Provider extends MinimalProvider {
private _network: Network;
private _events: Array<_Event>;
@ -642,6 +549,7 @@ export class Provider {
protected ready: Promise<Network>;
constructor(network: Networkish | Promise<Network>) {
super();
errors.checkNew(this, Provider);
if (network instanceof Promise) {
@ -1040,7 +948,7 @@ export class Provider {
}
return undefined;
}
return checkTransactionResponse(result);
return Provider.checkTransactionResponse(result);
});
}, { onceBlock: this });
});
@ -1213,6 +1121,10 @@ export class Provider {
});
}
static checkTransactionResponse(transaction: any): TransactionResponse {
return checkTransactionResponse(transaction);
}
doPoll(): void {
}

View File

@ -14,13 +14,11 @@ utils.defineProperty(Web3Signer, 'onchange', {
});
*/
export type Callback = (error: any, response: any) => void;
export type AsyncProvider = {
isMetaMask: boolean;
host?: string;
path?: string;
sendAsync: (request: any, callback: Callback) => void
sendAsync: (request: any, callback: (error: any, response: any) => void) => void
}
export class Web3Provider extends JsonRpcProvider {

View File

@ -8,16 +8,15 @@ import { arrayify, Arrayish, concat, hexlify, padZeros } from './bytes';
import { toUtf8Bytes, toUtf8String } from './utf8';
import { defineReadOnly, jsonCopy } from './properties';
import { CoerceFunc, EventFragment, FunctionFragment, ParamType }from './types';
export { CoerceFunc, EventFragment, FunctionFragment, ParamType }
import * as errors from './errors';
const paramTypeBytes = new RegExp(/^bytes([0-9]*)$/);
const paramTypeNumber = new RegExp(/^(u?int)([0-9]*)$/);
const paramTypeArray = new RegExp(/^(.*)\[([0-9]*)\]$/);
// @TODO: Add enum for param types
export type CoerceFunc = (type: string, value: any) => any;
export type ParamType = { name?: string, type: string, indexed?: boolean, components?: Array<any> };
export const defaultCoerceFunc: CoerceFunc = function(type: string, value: any): any {
var match = type.match(paramTypeNumber)
if (match && parseInt(match[2]) <= 48) { return value.toNumber(); }
@ -188,29 +187,6 @@ function parseParam(param: string, allowIndexed?: boolean): ParamType {
return (<ParamType>parent);
}
// @TODO: should this just be a combined Fragment?
export type EventFragment = {
type: string
name: string,
anonymous: boolean,
inputs: Array<ParamType>,
};
export type FunctionFragment = {
type: string
name: string,
constant: boolean,
inputs: Array<ParamType>,
outputs: Array<ParamType>,
payable: boolean,
stateMutability: string,
};
// @TODO: Better return type
function parseSignatureEvent(fragment: string): EventFragment {

View File

@ -10,14 +10,16 @@
import BN from 'bn.js';
import { Arrayish, hexlify, isArrayish, isHexString } from './bytes';
import { hexlify, isArrayish, isHexString } from './bytes';
import { defineReadOnly } from './properties';
import { BigNumber, BigNumberish } from './types';
export { BigNumber, BigNumberish };
import * as errors from './errors';
const BN_1 = new BN.BN(-1);
export type BigNumberish = BigNumber | string | number | Arrayish;
function toHex(bn: BN.BN): string {
let value = bn.toString(16);
if (value[0] === '-') {
@ -38,29 +40,6 @@ function toBigNumber(bn: BN.BN): _BigNumber {
return new _BigNumber(toHex(bn));
}
export interface BigNumber {
fromTwos(value: number): BigNumber;
toTwos(value: number): BigNumber;
add(other: BigNumberish): BigNumber;
sub(other: BigNumberish): BigNumber;
div(other: BigNumberish): BigNumber;
mul(other: BigNumberish): BigNumber;
mod(other: BigNumberish): BigNumber;
pow(other: BigNumberish): BigNumber;
maskn(value: number): BigNumber;
eq(other: BigNumberish): boolean;
lt(other: BigNumberish): boolean;
lte(other: BigNumberish): boolean;
gt(other: BigNumberish): boolean;
gte(other: BigNumberish): boolean;
isZero(): boolean;
toNumber(): number;
toString(): string;
toHexString(): string;
};
class _BigNumber implements BigNumber {
private readonly _hex: string;

View File

@ -5,14 +5,15 @@
import { BigNumber } from './bignumber';
import { Arrayish, Signature } from './types';
export { Arrayish, Signature };
import errors = require('./errors');
export const AddressZero = '0x0000000000000000000000000000000000000000';
export const HashZero = '0x0000000000000000000000000000000000000000000000000000000000000000';
export type Arrayish = string | ArrayLike<number>;
function isBigNumber(value: any): value is BigNumber {
return !!value._bn;
@ -245,13 +246,6 @@ function isSignature(value: any): value is Signature {
return (value && value.r != null && value.s != null);
}
export interface Signature {
r: string;
s: string;
recoveryParam: number;
v?: number;
}
export function splitSignature(signature: Arrayish | Signature): Signature {
let v = 0;
let r = '0x', s = '0x';

View File

@ -4,16 +4,16 @@ import { createHmac } from 'crypto';
import { arrayify, Arrayish } from './bytes';
import * as errors from './errors';
import { SupportedAlgorithms } from './types';
export { SupportedAlgorithms };
export type SupportedAlgorithms = 'sha256' | 'sha512';
import * as errors from './errors';
const supportedAlgorithms = { sha256: true, sha512: true };
export function computeHmac(algorithm: SupportedAlgorithms, key: Arrayish, data: Arrayish): Uint8Array {
if (!supportedAlgorithms[algorithm]) {
errors.throwError('unsupported algorithm ' + algorithm, errors.UNSUPPORTED_OPERATION, { operation: 'hmac', algorithm: algorithm });
}
//return arrayify(_hmac(_hash[algorithm], arrayify(key)).update(arrayify(data)).digest());
return arrayify(createHmac(algorithm, new Buffer(arrayify(key))).update(new Buffer(arrayify(data))).digest());
}

View File

@ -1,11 +1,14 @@
import { getAddress } from './address';
import { BigNumber, bigNumberify, BigNumberish, ConstantZero } from './bignumber';
import { BigNumber, bigNumberify, ConstantZero } from './bignumber';
import { arrayify, Arrayish, hexlify, hexZeroPad, Signature, splitSignature, stripZeros, } from './bytes';
import { keccak256 } from './keccak256';
import * as RLP from './rlp';
import { Transaction, UnsignedTransaction } from './types';
export { Transaction, UnsignedTransaction };
import * as errors from './errors';
/* !!!!!!!!!!!!!!!!!!!!!!!! IMPORTANT !!!!!!!!!!!!!!!!!!!!!!!
@ -17,38 +20,6 @@ import * as errors from './errors';
*
*/
export type UnsignedTransaction = {
to?: string;
nonce?: number;
gasLimit?: BigNumberish;
gasPrice?: BigNumberish;
data?: Arrayish;
value?: BigNumberish;
chainId?: number;
}
export interface Transaction {
hash?: string;
to?: string;
from?: string;
nonce: number;
gasLimit: BigNumber;
gasPrice: BigNumber;
data: string;
value: BigNumber;
chainId: number;
r?: string;
s?: string;
v?: number;
}
function handleAddress(value: string): string {
if (value === '0x') { return null; }
return getAddress(value);

383
src.ts/utils/types.ts Normal file
View File

@ -0,0 +1,383 @@
///////////////////////////////
// Bytes
export type Arrayish = string | ArrayLike<number>;
///////////////////////////////
// BigNumber
export interface BigNumber {
fromTwos(value: number): BigNumber;
toTwos(value: number): BigNumber;
add(other: BigNumberish): BigNumber;
sub(other: BigNumberish): BigNumber;
div(other: BigNumberish): BigNumber;
mul(other: BigNumberish): BigNumber;
mod(other: BigNumberish): BigNumber;
pow(other: BigNumberish): BigNumber;
maskn(value: number): BigNumber;
eq(other: BigNumberish): boolean;
lt(other: BigNumberish): boolean;
lte(other: BigNumberish): boolean;
gt(other: BigNumberish): boolean;
gte(other: BigNumberish): boolean;
isZero(): boolean;
toNumber(): number;
toString(): string;
toHexString(): string;
};
export type BigNumberish = BigNumber | string | number | Arrayish;
///////////////////////////////
// Connection
export type ConnectionInfo = {
url: string,
user?: string,
password?: string,
allowInsecure?: boolean
};
///////////////////////////////
// Crypto
export type SupportedAlgorithms = 'sha256' | 'sha512';
export interface Signature {
r: string;
s: string;
recoveryParam: number;
v?: number;
}
///////////////////////////////
// Network
export type Network = {
name: string,
chainId: number,
ensAddress?: string,
}
export type Networkish = Network | string | number;
///////////////////////////////
// ABI Coder
export type CoerceFunc = (type: string, value: any) => any;
export type ParamType = {
name?: string,
type: string,
indexed?: boolean,
components?: Array<any>
};
// @TODO: should this just be a combined Fragment?
export type EventFragment = {
type: string
name: string,
anonymous: boolean,
inputs: Array<ParamType>,
};
export type FunctionFragment = {
type: string
name: string,
constant: boolean,
inputs: Array<ParamType>,
outputs: Array<ParamType>,
payable: boolean,
stateMutability: string,
};
///////////////////////////////
// Transactions
export type UnsignedTransaction = {
to?: string;
nonce?: number;
gasLimit?: BigNumberish;
gasPrice?: BigNumberish;
data?: Arrayish;
value?: BigNumberish;
chainId?: number;
}
export interface Transaction {
hash?: string;
to?: string;
from?: string;
nonce: number;
gasLimit: BigNumber;
gasPrice: BigNumber;
data: string;
value: BigNumber;
chainId: number;
r?: string;
s?: string;
v?: number;
}
///////////////////////////////
// Blockchain
export type BlockTag = string | number;
export interface Block {
hash: string;
parentHash: string;
number: number;
timestamp: number;
nonce: string;
difficulty: number;
gasLimit: BigNumber;
gasUsed: BigNumber;
miner: string;
extraData: string;
transactions: Array<string>;
}
export type Filter = {
fromBlock?: BlockTag,
toBlock?: BlockTag,
address?: string,
topics?: Array<string | Array<string>>,
}
export interface Log {
blockNumber?: number;
blockHash?: string;
transactionIndex?: number;
removed?: boolean;
transactionLogIndex?: number,
address: string;
data: string;
topics: Array<string>;
transactionHash?: string;
logIndex?: number;
}
export interface TransactionReceipt {
contractAddress?: string,
transactionIndex?: number,
root?: string,
gasUsed?: BigNumber,
logsBloom?: string,
blockHash?: string,
transactionHash?: string,
logs?: Array<Log>,
blockNumber?: number,
cumulativeGasUsed?: BigNumber,
byzantium: boolean,
status?: number
};
export type TransactionRequest = {
to?: string | Promise<string>,
from?: string | Promise<string>,
nonce?: number | string | Promise<number | string>,
gasLimit?: BigNumberish | Promise<BigNumberish>,
gasPrice?: BigNumberish | Promise<BigNumberish>,
data?: Arrayish | Promise<Arrayish>,
value?: BigNumberish | Promise<BigNumberish>,
chainId?: number | Promise<number>,
}
export interface TransactionResponse extends Transaction {
// Only if a transaction has been mined
blockNumber?: number,
blockHash?: string,
timestamp?: number,
// Not optional (as it is in Transaction)
from: string;
// The raw transaction
raw?: string,
// This function waits until the transaction has been mined
wait: (timeout?: number) => Promise<TransactionReceipt>
};
///////////////////////////////
// Interface
export class Indexed {
hash: string;
}
export interface DeployDescription {
type: "deploy";
inputs: Array<ParamType>;
payable: boolean;
encode(bytecode: string, params: Array<any>): string;
}
export interface FunctionDescription {
type: "call" | "transaction";
name: string;
signature: string;
sighash: string;
inputs: Array<ParamType>;
outputs: Array<ParamType>;
payable: boolean;
encode(params: Array<any>): string;
decode(data: string): any;
}
export interface EventDescription {
type: "event";
name: string;
signature: string;
inputs: Array<ParamType>;
anonymous: boolean;
topic: string;
encodeTopics(params: Array<any>): Array<string>;
decode(data: string, topics?: Array<string>): any;
}
///////////////////////////////
// Contract
export type EventFilter = {
address?: string;
topics?: Array<string>;
// @TODO: Support OR-style topcis; backwards compatible to make this change
//topics?: Array<string | Array<string>>
};
// The (n + 1)th parameter passed to contract event callbacks
export interface Event extends Log {
args: Array<any>;
decode: (data: string, topics?: Array<string>) => any;
event: string;
eventSignature: string;
removeListener: () => void;
getBlock: () => Promise<Block>;
getTransaction: () => Promise<TransactionResponse>;
getTransactionReceipt: () => Promise<TransactionReceipt>;
}
///////////////////////////////
// Provider
export type EventType = string | Array<string> | Filter;
export type Listener = (...args: Array<any>) => void;
/**
* Provider
*
* Note: We use an abstract class so we can use instanceof to determine if an
* object is a Provider.
*/
export abstract class MinimalProvider {
abstract getNetwork(): Promise<Network>;
abstract getBlockNumber(): Promise<number>;
abstract getGasPrice(): Promise<BigNumber>;
abstract getBalance(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<BigNumber>;
abstract getTransactionCount(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<number>;
abstract getCode(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<string> ;
abstract getStorageAt(addressOrName: string | Promise<string>, position: BigNumberish | Promise<BigNumberish>, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
abstract sendTransaction(signedTransaction: string | Promise<string>): Promise<TransactionResponse>;
abstract call(transaction: TransactionRequest): Promise<string>;
abstract estimateGas(transaction: TransactionRequest): Promise<BigNumber>;
abstract getBlock(blockHashOrBlockTag: BlockTag | string | Promise<BlockTag | string>): Promise<Block>;
abstract getTransaction(transactionHash: string): Promise<TransactionResponse>;
abstract getTransactionReceipt(transactionHash: string): Promise<TransactionReceipt>;
abstract getLogs(filter: Filter): Promise<Array<Log>>;
abstract resolveName(name: string | Promise<string>): Promise<string>;
abstract lookupAddress(address: string | Promise<string>): Promise<string>;
abstract on(eventName: EventType, listener: Listener): MinimalProvider;
abstract once(eventName: EventType, listener: Listener): MinimalProvider;
abstract listenerCount(eventName?: EventType): number;
abstract listeners(eventName: EventType): Array<Listener>;
abstract removeAllListeners(eventName: EventType): MinimalProvider;
abstract removeListener(eventName: EventType, listener: Listener): MinimalProvider;
// @TODO: This *could* be implemented here, but would pull in events...
abstract waitForTransaction(transactionHash: string, timeout?: number): Promise<TransactionReceipt>;
}
///////////////////////////////
// Signer
export type ProgressCallback = (percent: number) => void;
export type EncryptOptions = {
iv?: Arrayish;
entropy?: Arrayish;
mnemonic?: string;
path?: string;
client?: string;
salt?: Arrayish;
uuid?: string;
scrypt?: {
N?: number;
r?: number;
p?: number;
}
}
/**
* Signer
*
* Note: We use an abstract class so we can use instanceof to determine if an
* object is a Signer.
*/
export abstract class Signer {
provider?: MinimalProvider;
abstract getAddress(): Promise<string>
abstract signMessage(transaction: Arrayish | string): Promise<string>;
abstract sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse>;
}

View File

@ -111,6 +111,10 @@ export function parseUnits(value: string, unitType?: string | number): BigNumber
// Remove commas
var value = value.replace(/,/g,'');
if (unitInfo.decimals === 0) {
return bigNumberify(value);
}
// Is it negative?
var negative = (value.substring(0, 1) === '-');
if (negative) { value = value.substring(1); }

View File

@ -1,22 +1,18 @@
'use strict';
import { XMLHttpRequest } from 'xmlhttprequest';
import { toUtf8Bytes } from './utf8';
import { encode as base64Encode } from './base64';
export type ConnectionInfo = {
url: string,
user?: string,
password?: string,
allowInsecure?: boolean
};
import { encode as base64Encode } from './base64';
import { toUtf8Bytes } from './utf8';
import { ConnectionInfo } from './types';
export { ConnectionInfo };
import * as errors from './errors';
export type ProcessFunc = (value: any) => any;
type Header = { key: string, value: string };
export function fetchJson(connection: string | ConnectionInfo, json: string, processFunc: ProcessFunc): Promise<any> {
export function fetchJson(connection: string | ConnectionInfo, json: string, processFunc: (value: any) => any): Promise<any> {
let headers: Array<Header> = [ ];
let url: string = null;

View File

@ -4,6 +4,7 @@ import { Wallet } from './wallet';
import * as HDNode from './hdnode';
import { SigningKey } from './signing-key';
export { HDNode, SigningKey, Wallet };
export default { HDNode, SigningKey, Wallet };

View File

@ -4,21 +4,17 @@ import aes from 'aes-js';
import scrypt from 'scrypt-js';
import uuid from 'uuid';
import { SigningKey } from './signing-key';
import * as HDNode from './hdnode';
import { getAddress } from '../utils/address';
import { arrayify, Arrayish, concat, hexlify } from '../utils/bytes';
import { pbkdf2 } from '../utils/pbkdf2';
import { keccak256 } from '../utils/keccak256';
import { toUtf8Bytes, UnicodeNormalizationForm } from '../utils/utf8';
import { randomBytes } from '../utils/random-bytes';
import { SigningKey } from './signing-key';
import * as HDNode from './hdnode';
export interface ProgressCallback {
(percent: number): void
}
import { EncryptOptions, ProgressCallback } from '../utils/types';
function looseArrayify(hexString: string): Uint8Array {
if (typeof(hexString) === 'string' && hexString.substring(0, 2) !== '0x') {
@ -230,6 +226,7 @@ export function decrypt(json: string, password: Arrayish, progressCallback?: Pro
return;
}
if (progressCallback) { progressCallback(0); }
scrypt(passwordBytes, salt, N, r, p, 64, function(error, progress, key) {
if (error) {
error.progress = progress;
@ -288,21 +285,6 @@ export function decrypt(json: string, password: Arrayish, progressCallback?: Pro
});
}
export type EncryptOptions = {
iv?: Arrayish;
entropy?: Arrayish;
mnemonic?: string;
path?: string;
client?: string;
salt?: Arrayish;
uuid?: string;
scrypt?: {
N?: number;
r?: number;
p?: number;
}
}
export function encrypt(privateKey: Arrayish | SigningKey, password: Arrayish | string, options?: EncryptOptions, progressCallback?: ProgressCallback): Promise<string> {
// the options are optional, so adjust the call as needed
@ -382,6 +364,7 @@ export function encrypt(privateKey: Arrayish | SigningKey, password: Arrayish |
}
return new Promise(function(resolve, reject) {
if (progressCallback) { progressCallback(0); }
// We take 64 bytes:
// - 32 bytes As normal for the Web3 secret storage (derivedKey, macPrefix)

View File

@ -2,10 +2,9 @@
import { defaultPath, entropyToMnemonic, fromMnemonic, HDNode } from './hdnode';
import * as secretStorage from './secret-storage';
import { ProgressCallback } from './secret-storage';
import { SigningKey } from './signing-key';
import { BlockTag, Provider, TransactionRequest, TransactionResponse } from '../providers/provider';
import { Provider } from '../providers/provider';
import { Wordlist } from '../wordlists/wordlist';
import { BigNumber } from '../utils/bignumber';
@ -17,17 +16,11 @@ import { randomBytes } from '../utils/random-bytes';
import { recoverAddress } from '../utils/secp256k1';
import { serialize as serializeTransaction } from '../utils/transaction';
import { BlockTag, ProgressCallback, Signer, TransactionRequest, TransactionResponse } from '../utils/types';
export { BlockTag, ProgressCallback, Signer, TransactionRequest, TransactionResponse };
import * as errors from '../utils/errors';
export abstract class Signer {
provider?: Provider;
abstract getAddress(): Promise<string>
abstract signMessage(transaction: Arrayish | string): Promise<string>;
abstract sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse>;
}
export class Wallet extends Signer {
@ -171,7 +164,9 @@ export class Wallet extends Signer {
static fromEncryptedJson(json: string, password: Arrayish, progressCallback: ProgressCallback): Promise<Wallet> {
if (secretStorage.isCrowdsaleWallet(json)) {
try {
if (progressCallback) { progressCallback(0); }
let privateKey = secretStorage.decryptCrowdsale(json, password);
if (progressCallback) { progressCallback(1); }
return Promise.resolve(new Wallet(privateKey));
} catch (error) {
return Promise.reject(error);