Refactor Web3Wrapper to no longer use Web3.js & add more test coverage
This commit is contained in:
@@ -67,10 +67,13 @@
|
||||
"@0xproject/assert": "^0.2.12",
|
||||
"@0xproject/typescript-typings": "^0.4.1",
|
||||
"@0xproject/utils": "^0.7.1",
|
||||
"bn.js": "^4.11.8",
|
||||
"ethereum-types": "^0.0.1",
|
||||
"ethereumjs-util": "^5.1.1",
|
||||
"ethers": "3.0.22",
|
||||
"lodash": "^4.17.4",
|
||||
"web3": "^0.20.0"
|
||||
"web3": "^0.20.0",
|
||||
"web3-utils": "^1.0.0-beta.34"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
8
packages/web3-wrapper/src/globals.d.ts
vendored
8
packages/web3-wrapper/src/globals.d.ts
vendored
@@ -1,3 +1,11 @@
|
||||
declare module 'web3-utils' {
|
||||
import * as BigNumber from 'bignumber.js';
|
||||
|
||||
const toHex: (val: any) => string;
|
||||
const isHexStrict: (val: any) => boolean;
|
||||
const toDecimal: (val: any) => number;
|
||||
}
|
||||
|
||||
declare module '*.json' {
|
||||
const json: any;
|
||||
/* tslint:disable */
|
||||
|
||||
177
packages/web3-wrapper/src/marshaller.ts
Normal file
177
packages/web3-wrapper/src/marshaller.ts
Normal file
@@ -0,0 +1,177 @@
|
||||
import { addressUtils, BigNumber } from '@0xproject/utils';
|
||||
import {
|
||||
BlockParam,
|
||||
BlockParamLiteral,
|
||||
BlockWithoutTransactionData,
|
||||
BlockWithTransactionData,
|
||||
CallData,
|
||||
CallTxDataBase,
|
||||
LogEntry,
|
||||
RawLogEntry,
|
||||
Transaction,
|
||||
TxData,
|
||||
} from 'ethereum-types';
|
||||
import ethUtil = require('ethereumjs-util');
|
||||
import * as _ from 'lodash';
|
||||
import web3Utils = require('web3-utils');
|
||||
|
||||
import { utils } from './utils';
|
||||
|
||||
import {
|
||||
AbstractBlockRPC,
|
||||
BlockWithoutTransactionDataRPC,
|
||||
BlockWithTransactionDataRPC,
|
||||
CallDataRPC,
|
||||
CallTxDataBaseRPC,
|
||||
TransactionRPC,
|
||||
TxDataRPC,
|
||||
} from './types';
|
||||
|
||||
export const marshaller = {
|
||||
unmarshalIntoBlockWithoutTransactionData(
|
||||
blockWithHexValues: BlockWithoutTransactionDataRPC,
|
||||
): BlockWithoutTransactionData {
|
||||
const block = {
|
||||
...blockWithHexValues,
|
||||
gasLimit: web3Utils.toDecimal(blockWithHexValues.gasLimit),
|
||||
gasUsed: web3Utils.toDecimal(blockWithHexValues.gasUsed),
|
||||
size: web3Utils.toDecimal(blockWithHexValues.size),
|
||||
timestamp: web3Utils.toDecimal(blockWithHexValues.timestamp),
|
||||
number: _.isNull(blockWithHexValues.number) ? null : web3Utils.toDecimal(blockWithHexValues.number),
|
||||
difficulty: this._convertAmountToBigNumber(blockWithHexValues.difficulty),
|
||||
totalDifficulty: this._convertAmountToBigNumber(blockWithHexValues.totalDifficulty),
|
||||
};
|
||||
return block;
|
||||
},
|
||||
unmarshalIntoBlockWithTransactionData(blockWithHexValues: BlockWithTransactionDataRPC): BlockWithTransactionData {
|
||||
const block = {
|
||||
...blockWithHexValues,
|
||||
gasLimit: web3Utils.toDecimal(blockWithHexValues.gasLimit),
|
||||
gasUsed: web3Utils.toDecimal(blockWithHexValues.gasUsed),
|
||||
size: web3Utils.toDecimal(blockWithHexValues.size),
|
||||
timestamp: web3Utils.toDecimal(blockWithHexValues.timestamp),
|
||||
number: _.isNull(blockWithHexValues.number) ? null : web3Utils.toDecimal(blockWithHexValues.number),
|
||||
difficulty: this._convertAmountToBigNumber(blockWithHexValues.difficulty),
|
||||
totalDifficulty: this._convertAmountToBigNumber(blockWithHexValues.totalDifficulty),
|
||||
transactions: [] as Transaction[],
|
||||
};
|
||||
block.transactions = _.map(blockWithHexValues.transactions, (tx: TransactionRPC) => {
|
||||
const transaction = this.unmarshalTransaction(tx);
|
||||
return transaction;
|
||||
});
|
||||
return block;
|
||||
},
|
||||
unmarshalTransaction(txRpc: TransactionRPC): Transaction {
|
||||
const tx = {
|
||||
...txRpc,
|
||||
blockNumber: !_.isNull(txRpc.blockNumber) ? web3Utils.toDecimal(txRpc.blockNumber) : null,
|
||||
transactionIndex: !_.isNull(txRpc.transactionIndex) ? web3Utils.toDecimal(txRpc.transactionIndex) : null,
|
||||
nonce: web3Utils.toDecimal(txRpc.nonce),
|
||||
gas: web3Utils.toDecimal(txRpc.gas),
|
||||
gasPrice: this._convertAmountToBigNumber(txRpc.gasPrice),
|
||||
value: this._convertAmountToBigNumber(txRpc.value),
|
||||
};
|
||||
return tx;
|
||||
},
|
||||
marshalTxData(txData: Partial<TxData>): Partial<TxDataRPC> {
|
||||
if (_.isUndefined(txData.from)) {
|
||||
throw new Error(`txData is missing required "from" address.`);
|
||||
}
|
||||
const callTxDataBase = {
|
||||
...txData,
|
||||
};
|
||||
delete callTxDataBase.from;
|
||||
const callTxDataBaseRPC = this._marshalCallTxDataBase(callTxDataBase);
|
||||
const txDataRPC = {
|
||||
...callTxDataBaseRPC,
|
||||
from: this.marshalAddress(txData.from),
|
||||
};
|
||||
const prunableIfUndefined = ['gasPrice', 'gas', 'value', 'nonce'];
|
||||
_.each(txDataRPC, (value: any, key: string) => {
|
||||
if (_.isUndefined(value) && _.includes(prunableIfUndefined, key)) {
|
||||
delete (txDataRPC as any)[key];
|
||||
}
|
||||
});
|
||||
return txDataRPC;
|
||||
},
|
||||
marshalCallData(callData: Partial<CallData>): Partial<CallDataRPC> {
|
||||
const callTxDataBase = {
|
||||
...callData,
|
||||
};
|
||||
delete callTxDataBase.from;
|
||||
const callTxDataBaseRPC = this._marshalCallTxDataBase(callTxDataBase);
|
||||
const callDataRPC = {
|
||||
...callTxDataBaseRPC,
|
||||
from: _.isUndefined(callData.from) ? undefined : this.marshalAddress(callData.from),
|
||||
};
|
||||
return callDataRPC;
|
||||
},
|
||||
marshalAddress(address: string): string {
|
||||
if (addressUtils.isAddress(address)) {
|
||||
return ethUtil.addHexPrefix(address);
|
||||
}
|
||||
throw new Error(`Invalid address encountered: ${address}`);
|
||||
},
|
||||
marshalBlockParam(blockParam: BlockParam | string | number | undefined): string | undefined {
|
||||
if (_.isUndefined(blockParam)) {
|
||||
return BlockParamLiteral.Latest;
|
||||
}
|
||||
const encodedBlockParam = _.isNumber(blockParam) ? web3Utils.toHex(blockParam) : blockParam;
|
||||
return encodedBlockParam;
|
||||
},
|
||||
unmarshalLog(rawLog: RawLogEntry): LogEntry {
|
||||
const formattedLog = {
|
||||
...rawLog,
|
||||
logIndex: this.convertHexToNumberOrNull(rawLog.logIndex),
|
||||
blockNumber: this.convertHexToNumberOrNull(rawLog.blockNumber),
|
||||
transactionIndex: this.convertHexToNumberOrNull(rawLog.transactionIndex),
|
||||
};
|
||||
return formattedLog;
|
||||
},
|
||||
_marshalCallTxDataBase(callTxDataBase: Partial<CallTxDataBase>): Partial<CallTxDataBaseRPC> {
|
||||
const callTxDataBaseRPC = {
|
||||
...callTxDataBase,
|
||||
to: _.isUndefined(callTxDataBase.to) ? undefined : this.marshalAddress(callTxDataBase.to),
|
||||
gasPrice: _.isUndefined(callTxDataBase.gasPrice)
|
||||
? undefined
|
||||
: this._encodeAmountAsHexString(callTxDataBase.gasPrice),
|
||||
gas: _.isUndefined(callTxDataBase.gas) ? undefined : this._encodeAmountAsHexString(callTxDataBase.gas),
|
||||
value: _.isUndefined(callTxDataBase.value)
|
||||
? undefined
|
||||
: this._encodeAmountAsHexString(callTxDataBase.value),
|
||||
nonce: _.isUndefined(callTxDataBase.nonce)
|
||||
? undefined
|
||||
: this._encodeAmountAsHexString(callTxDataBase.nonce),
|
||||
};
|
||||
|
||||
return callTxDataBaseRPC;
|
||||
},
|
||||
convertHexToNumberOrNull(hex: string | null): number | null {
|
||||
if (_.isNull(hex)) {
|
||||
return null;
|
||||
}
|
||||
const decimal = web3Utils.toDecimal(hex);
|
||||
return decimal;
|
||||
},
|
||||
_convertAmountToBigNumber(value: string | number | BigNumber): BigNumber {
|
||||
const num = value || 0;
|
||||
const isBigNumber = utils.isBigNumber(num);
|
||||
if (isBigNumber) {
|
||||
return num as BigNumber;
|
||||
}
|
||||
|
||||
if (_.isString(num) && (num.indexOf('0x') === 0 || num.indexOf('-0x') === 0)) {
|
||||
return new BigNumber(num.replace('0x', ''), 16);
|
||||
}
|
||||
|
||||
const baseTen = 10;
|
||||
return new BigNumber((num as number).toString(baseTen), baseTen);
|
||||
},
|
||||
_encodeAmountAsHexString(value: string | number | BigNumber): string {
|
||||
const valueBigNumber = this._convertAmountToBigNumber(value);
|
||||
const hexBase = 16;
|
||||
const valueHex = valueBigNumber.toString(hexBase);
|
||||
|
||||
return valueBigNumber.lessThan(0) ? '-0x' + valueHex.substr(1) : '0x' + valueHex;
|
||||
},
|
||||
};
|
||||
@@ -1,3 +1,59 @@
|
||||
export enum Web3WrapperErrors {
|
||||
TransactionMiningTimeout = 'TRANSACTION_MINING_TIMEOUT',
|
||||
}
|
||||
|
||||
export interface AbstractBlockRPC {
|
||||
number: string | null;
|
||||
hash: string | null;
|
||||
parentHash: string;
|
||||
nonce: string | null;
|
||||
sha3Uncles: string;
|
||||
logsBloom: string | null;
|
||||
transactionsRoot: string;
|
||||
stateRoot: string;
|
||||
miner: string;
|
||||
difficulty: string;
|
||||
totalDifficulty: string;
|
||||
extraData: string;
|
||||
size: string;
|
||||
gasLimit: string;
|
||||
gasUsed: string;
|
||||
timestamp: string;
|
||||
uncles: string[];
|
||||
}
|
||||
export interface BlockWithoutTransactionDataRPC extends AbstractBlockRPC {
|
||||
transactions: string[];
|
||||
}
|
||||
export interface BlockWithTransactionDataRPC extends AbstractBlockRPC {
|
||||
transactions: TransactionRPC[];
|
||||
}
|
||||
export interface TransactionRPC {
|
||||
hash: string;
|
||||
nonce: number;
|
||||
blockHash: string | null;
|
||||
blockNumber: string | null;
|
||||
transactionIndex: string | null;
|
||||
from: string;
|
||||
to: string | null;
|
||||
value: string;
|
||||
gasPrice: string;
|
||||
gas: string;
|
||||
input: string;
|
||||
}
|
||||
|
||||
export interface CallTxDataBaseRPC {
|
||||
to?: string;
|
||||
value?: string;
|
||||
gas?: string;
|
||||
gasPrice?: string;
|
||||
data?: string;
|
||||
nonce?: string;
|
||||
}
|
||||
|
||||
export interface TxDataRPC extends CallTxDataBaseRPC {
|
||||
from: string;
|
||||
}
|
||||
|
||||
export interface CallDataRPC extends CallTxDataBaseRPC {
|
||||
from?: string;
|
||||
}
|
||||
|
||||
8
packages/web3-wrapper/src/utils.ts
Normal file
8
packages/web3-wrapper/src/utils.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import * as _ from 'lodash';
|
||||
|
||||
export const utils = {
|
||||
isBigNumber(value: any): boolean {
|
||||
const isBigNumber = _.isObject(value) && (value as any).isBigNumber;
|
||||
return isBigNumber;
|
||||
},
|
||||
};
|
||||
@@ -20,9 +20,15 @@ import {
|
||||
TxData,
|
||||
} from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
import * as Web3 from 'web3';
|
||||
import * as web3Utils from 'web3-utils';
|
||||
|
||||
import { Web3WrapperErrors } from './types';
|
||||
import { marshaller } from './marshaller';
|
||||
import {
|
||||
BlockWithoutTransactionDataRPC,
|
||||
BlockWithTransactionDataRPC,
|
||||
TransactionRPC,
|
||||
Web3WrapperErrors,
|
||||
} from './types';
|
||||
|
||||
const BASE_TEN = 10;
|
||||
|
||||
@@ -34,7 +40,7 @@ export const uniqueVersionIds = {
|
||||
};
|
||||
|
||||
/**
|
||||
* A wrapper around the Web3.js 0.x library that provides a consistent, clean promise-based interface.
|
||||
* An alternative to the Web3.js library that provides a consistent, clean, promise-based interface.
|
||||
*/
|
||||
export class Web3Wrapper {
|
||||
/**
|
||||
@@ -42,7 +48,7 @@ export class Web3Wrapper {
|
||||
*/
|
||||
public isZeroExWeb3Wrapper = true;
|
||||
public abiDecoder: AbiDecoder;
|
||||
private _web3: Web3;
|
||||
private _provider: Provider;
|
||||
private _txDefaults: Partial<TxData>;
|
||||
private _jsonRpcRequestId: number;
|
||||
/**
|
||||
@@ -117,6 +123,20 @@ export class Web3Wrapper {
|
||||
}
|
||||
}
|
||||
}
|
||||
private static _normalizeTxReceiptStatus(status: undefined | null | string | 0 | 1): null | 0 | 1 {
|
||||
// Transaction status might have four values
|
||||
// undefined - Testrpc and other old clients
|
||||
// null - New clients on old transactions
|
||||
// number - Parity
|
||||
// hex - Geth
|
||||
if (_.isString(status)) {
|
||||
return web3Utils.toDecimal(status) as 0 | 1;
|
||||
} else if (_.isUndefined(status)) {
|
||||
return null;
|
||||
} else {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Instantiates a new Web3Wrapper.
|
||||
* @param provider The Web3 provider instance you would like the Web3Wrapper to use for interacting with
|
||||
@@ -133,8 +153,7 @@ export class Web3Wrapper {
|
||||
(provider as any).sendAsync = (provider as any).send;
|
||||
}
|
||||
this.abiDecoder = new AbiDecoder([]);
|
||||
this._web3 = new Web3();
|
||||
this._web3.setProvider(provider);
|
||||
this._provider = provider;
|
||||
this._txDefaults = txDefaults || {};
|
||||
this._jsonRpcRequestId = 0;
|
||||
}
|
||||
@@ -150,7 +169,7 @@ export class Web3Wrapper {
|
||||
* @return Web3 provider instance
|
||||
*/
|
||||
public getProvider(): Provider {
|
||||
return this._web3.currentProvider;
|
||||
return this._provider;
|
||||
}
|
||||
/**
|
||||
* Update the used Web3 provider
|
||||
@@ -158,7 +177,7 @@ export class Web3Wrapper {
|
||||
*/
|
||||
public setProvider(provider: Provider): void {
|
||||
assert.isWeb3Provider('provider', provider);
|
||||
this._web3.setProvider(provider);
|
||||
this._provider = provider;
|
||||
}
|
||||
/**
|
||||
* Check whether an address is available through the backing provider. This can be
|
||||
@@ -196,9 +215,13 @@ export class Web3Wrapper {
|
||||
* @returns The transaction receipt, including it's status (0: failed, 1: succeeded or undefined: not found)
|
||||
*/
|
||||
public async getTransactionReceiptAsync(txHash: string): Promise<TransactionReceipt> {
|
||||
const transactionReceipt = await promisify<TransactionReceipt>(this._web3.eth.getTransactionReceipt)(txHash);
|
||||
assert.isHexString('txHash', txHash);
|
||||
const transactionReceipt = await this._sendRawPayloadAsync<TransactionReceipt>({
|
||||
method: 'eth_getTransactionReceipt',
|
||||
params: [txHash],
|
||||
});
|
||||
if (!_.isNull(transactionReceipt)) {
|
||||
transactionReceipt.status = this._normalizeTxReceiptStatus(transactionReceipt.status);
|
||||
transactionReceipt.status = Web3Wrapper._normalizeTxReceiptStatus(transactionReceipt.status);
|
||||
}
|
||||
return transactionReceipt;
|
||||
}
|
||||
@@ -233,9 +256,17 @@ export class Web3Wrapper {
|
||||
* @param address Address of the contract
|
||||
* @return Code of the contract
|
||||
*/
|
||||
public async getContractCodeAsync(address: string): Promise<string> {
|
||||
public async getContractCodeAsync(address: string, defaultBlock?: BlockParam): Promise<string> {
|
||||
assert.isETHAddressHex('address', address);
|
||||
const code = await promisify<string>(this._web3.eth.getCode)(address);
|
||||
if (!_.isUndefined(defaultBlock)) {
|
||||
Web3Wrapper._assertBlockParam(defaultBlock);
|
||||
}
|
||||
const marshalledDefaultBlock = marshaller.marshalBlockParam(defaultBlock);
|
||||
const encodedAddress = marshaller.marshalAddress(address);
|
||||
const code = await this._sendRawPayloadAsync<string>({
|
||||
method: 'eth_getCode',
|
||||
params: [encodedAddress, marshalledDefaultBlock],
|
||||
});
|
||||
return code;
|
||||
}
|
||||
/**
|
||||
@@ -259,9 +290,13 @@ export class Web3Wrapper {
|
||||
* @returns Signature string (might be VRS or RSV depending on the Signer)
|
||||
*/
|
||||
public async signMessageAsync(address: string, message: string): Promise<string> {
|
||||
assert.isETHAddressHex('address', address);
|
||||
assert.isETHAddressHex('address', address);
|
||||
assert.isString('message', message); // TODO: Should this be stricter? Hex string?
|
||||
const signData = await promisify<string>(this._web3.eth.sign)(address, message);
|
||||
const signData = await this._sendRawPayloadAsync<string>({
|
||||
method: 'eth_sign',
|
||||
params: [address, message],
|
||||
});
|
||||
return signData;
|
||||
}
|
||||
/**
|
||||
@@ -269,8 +304,12 @@ export class Web3Wrapper {
|
||||
* @returns Block number
|
||||
*/
|
||||
public async getBlockNumberAsync(): Promise<number> {
|
||||
const blockNumber = await promisify<number>(this._web3.eth.getBlockNumber)();
|
||||
return blockNumber;
|
||||
const blockNumberHex = await this._sendRawPayloadAsync<string>({
|
||||
method: 'eth_blockNumber',
|
||||
params: [],
|
||||
});
|
||||
const blockNumber = marshaller.convertHexToNumberOrNull(blockNumberHex);
|
||||
return blockNumber as number;
|
||||
}
|
||||
/**
|
||||
* Fetch a specific Ethereum block without transaction data
|
||||
@@ -279,10 +318,17 @@ export class Web3Wrapper {
|
||||
*/
|
||||
public async getBlockAsync(blockParam: string | BlockParam): Promise<BlockWithoutTransactionData> {
|
||||
Web3Wrapper._assertBlockParamOrString(blockParam);
|
||||
const encodedBlockParam = marshaller.marshalBlockParam(blockParam);
|
||||
const method = web3Utils.isHexStrict(blockParam) ? 'eth_getBlockByHash' : 'eth_getBlockByNumber';
|
||||
const shouldIncludeTransactionData = false;
|
||||
const blockWithoutTransactionData = await promisify<BlockWithoutTransactionData>(this._web3.eth.getBlock)(
|
||||
blockParam,
|
||||
shouldIncludeTransactionData,
|
||||
const blockWithoutTransactionDataWithHexValues = await this._sendRawPayloadAsync<
|
||||
BlockWithoutTransactionDataRPC
|
||||
>({
|
||||
method,
|
||||
params: [encodedBlockParam, shouldIncludeTransactionData],
|
||||
});
|
||||
const blockWithoutTransactionData = marshaller.unmarshalIntoBlockWithoutTransactionData(
|
||||
blockWithoutTransactionDataWithHexValues,
|
||||
);
|
||||
return blockWithoutTransactionData;
|
||||
}
|
||||
@@ -293,12 +339,20 @@ export class Web3Wrapper {
|
||||
*/
|
||||
public async getBlockWithTransactionDataAsync(blockParam: string | BlockParam): Promise<BlockWithTransactionData> {
|
||||
Web3Wrapper._assertBlockParamOrString(blockParam);
|
||||
let encodedBlockParam = blockParam;
|
||||
if (_.isNumber(blockParam)) {
|
||||
encodedBlockParam = web3Utils.toHex(blockParam);
|
||||
}
|
||||
const method = web3Utils.isHexStrict(blockParam) ? 'eth_getBlockByHash' : 'eth_getBlockByNumber';
|
||||
const shouldIncludeTransactionData = true;
|
||||
const blockWithTransactionData = await promisify<BlockWithTransactionData>(this._web3.eth.getBlock)(
|
||||
blockParam,
|
||||
shouldIncludeTransactionData,
|
||||
const blockWithTransactionDataWithHexValues = await this._sendRawPayloadAsync<BlockWithTransactionDataRPC>({
|
||||
method,
|
||||
params: [encodedBlockParam, shouldIncludeTransactionData],
|
||||
});
|
||||
const blockWithoutTransactionData = marshaller.unmarshalIntoBlockWithTransactionData(
|
||||
blockWithTransactionDataWithHexValues,
|
||||
);
|
||||
return blockWithTransactionData;
|
||||
return blockWithoutTransactionData;
|
||||
}
|
||||
/**
|
||||
* Fetch a block's timestamp
|
||||
@@ -315,7 +369,10 @@ export class Web3Wrapper {
|
||||
* @returns Available user addresses
|
||||
*/
|
||||
public async getAvailableAddressesAsync(): Promise<string[]> {
|
||||
const addresses = await promisify<string[]>(this._web3.eth.getAccounts)();
|
||||
const addresses = await this._sendRawPayloadAsync<string>({
|
||||
method: 'eth_accounts',
|
||||
params: [],
|
||||
});
|
||||
const normalizedAddresses = _.map(addresses, address => address.toLowerCase());
|
||||
return normalizedAddresses;
|
||||
}
|
||||
@@ -368,11 +425,11 @@ export class Web3Wrapper {
|
||||
public async getLogsAsync(filter: FilterObject): Promise<LogEntry[]> {
|
||||
let fromBlock = filter.fromBlock;
|
||||
if (_.isNumber(fromBlock)) {
|
||||
fromBlock = this._web3.toHex(fromBlock);
|
||||
fromBlock = web3Utils.toHex(fromBlock);
|
||||
}
|
||||
let toBlock = filter.toBlock;
|
||||
if (_.isNumber(toBlock)) {
|
||||
toBlock = this._web3.toHex(toBlock);
|
||||
toBlock = web3Utils.toHex(toBlock);
|
||||
}
|
||||
const serializedFilter = {
|
||||
...filter,
|
||||
@@ -380,12 +437,11 @@ export class Web3Wrapper {
|
||||
toBlock,
|
||||
};
|
||||
const payload = {
|
||||
jsonrpc: '2.0',
|
||||
method: 'eth_getLogs',
|
||||
params: [serializedFilter],
|
||||
};
|
||||
const rawLogs = await this._sendRawPayloadAsync<RawLogEntry[]>(payload);
|
||||
const formattedLogs = _.map(rawLogs, this._formatLog.bind(this));
|
||||
const formattedLogs = _.map(rawLogs, marshaller.unmarshalLog.bind(marshaller));
|
||||
return formattedLogs;
|
||||
}
|
||||
/**
|
||||
@@ -394,7 +450,9 @@ export class Web3Wrapper {
|
||||
* @returns Estimated gas cost
|
||||
*/
|
||||
public async estimateGasAsync(txData: Partial<TxData>): Promise<number> {
|
||||
const gas = await promisify<number>(this._web3.eth.estimateGas)(txData);
|
||||
const txDataHex = marshaller.marshalTxData(txData);
|
||||
const gasHex = await this._sendRawPayloadAsync<string>({ method: 'eth_estimateGas', params: [txDataHex] });
|
||||
const gas = web3Utils.toDecimal(gasHex);
|
||||
return gas;
|
||||
}
|
||||
/**
|
||||
@@ -407,7 +465,12 @@ export class Web3Wrapper {
|
||||
if (!_.isUndefined(defaultBlock)) {
|
||||
Web3Wrapper._assertBlockParam(defaultBlock);
|
||||
}
|
||||
const rawCallResult = await promisify<string>(this._web3.eth.call)(callData, defaultBlock);
|
||||
const marshalledDefaultBlock = marshaller.marshalBlockParam(defaultBlock);
|
||||
const callDataHex = marshaller.marshalCallData(callData);
|
||||
const rawCallResult = await this._sendRawPayloadAsync<string>({
|
||||
method: 'eth_call',
|
||||
params: [callDataHex, marshalledDefaultBlock],
|
||||
});
|
||||
if (rawCallResult === '0x') {
|
||||
throw new Error('Contract call failed (returned null)');
|
||||
}
|
||||
@@ -419,7 +482,8 @@ export class Web3Wrapper {
|
||||
* @returns Transaction hash
|
||||
*/
|
||||
public async sendTransactionAsync(txData: TxData): Promise<string> {
|
||||
const txHash = await promisify<string>(this._web3.eth.sendTransaction)(txData);
|
||||
const txDataHex = marshaller.marshalTxData(txData);
|
||||
const txHash = await this._sendRawPayloadAsync<string>({ method: 'eth_sendTransaction', params: [txDataHex] });
|
||||
return txHash;
|
||||
}
|
||||
/**
|
||||
@@ -529,10 +593,10 @@ export class Web3Wrapper {
|
||||
*/
|
||||
public async setHeadAsync(blockNumber: number): Promise<void> {
|
||||
assert.isNumber('blockNumber', blockNumber);
|
||||
await this._sendRawPayloadAsync<void>({ method: 'debug_setHead', params: [this._web3.toHex(blockNumber)] });
|
||||
await this._sendRawPayloadAsync<void>({ method: 'debug_setHead', params: [web3Utils.toHex(blockNumber)] });
|
||||
}
|
||||
private async _sendRawPayloadAsync<A>(payload: Partial<JSONRPCRequestPayload>): Promise<A> {
|
||||
const sendAsync = this._web3.currentProvider.sendAsync.bind(this._web3.currentProvider);
|
||||
const sendAsync = this._provider.sendAsync.bind(this._provider);
|
||||
const payloadWithDefaults = {
|
||||
id: this._jsonRpcRequestId++,
|
||||
params: [],
|
||||
@@ -543,34 +607,4 @@ export class Web3Wrapper {
|
||||
const result = response.result;
|
||||
return result;
|
||||
}
|
||||
private _normalizeTxReceiptStatus(status: undefined | null | string | 0 | 1): null | 0 | 1 {
|
||||
// Transaction status might have four values
|
||||
// undefined - Testrpc and other old clients
|
||||
// null - New clients on old transactions
|
||||
// number - Parity
|
||||
// hex - Geth
|
||||
if (_.isString(status)) {
|
||||
return this._web3.toDecimal(status) as 0 | 1;
|
||||
} else if (_.isUndefined(status)) {
|
||||
return null;
|
||||
} else {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
private _formatLog(rawLog: RawLogEntry): LogEntry {
|
||||
const formattedLog = {
|
||||
...rawLog,
|
||||
logIndex: this._hexToDecimal(rawLog.logIndex),
|
||||
blockNumber: this._hexToDecimal(rawLog.blockNumber),
|
||||
transactionIndex: this._hexToDecimal(rawLog.transactionIndex),
|
||||
};
|
||||
return formattedLog;
|
||||
}
|
||||
private _hexToDecimal(hex: string | null): number | null {
|
||||
if (_.isNull(hex)) {
|
||||
return null;
|
||||
}
|
||||
const decimal = this._web3.toDecimal(hex);
|
||||
return decimal;
|
||||
}
|
||||
} // tslint:disable-line:max-file-line-count
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import * as chai from 'chai';
|
||||
import { BlockParamLiteral } from 'ethereum-types';
|
||||
import * as Ganache from 'ganache-core';
|
||||
import * as _ from 'lodash';
|
||||
import 'mocha';
|
||||
|
||||
import { Web3Wrapper } from '../src';
|
||||
import { utils } from '../src/utils';
|
||||
import { Web3Wrapper } from '../src/web3_wrapper';
|
||||
|
||||
import { chaiSetup } from './utils/chai_setup';
|
||||
chaiSetup.configure();
|
||||
@@ -16,6 +18,10 @@ describe('Web3Wrapper tests', () => {
|
||||
const NETWORK_ID = 50;
|
||||
const provider = Ganache.provider({ network_id: NETWORK_ID });
|
||||
const web3Wrapper = new Web3Wrapper(provider);
|
||||
let addresses: string[];
|
||||
before(async () => {
|
||||
addresses = await web3Wrapper.getAvailableAddressesAsync();
|
||||
});
|
||||
describe('#isAddress', () => {
|
||||
it('correctly checks if a string is a valid ethereum address', () => {
|
||||
expect(Web3Wrapper.isAddress('0x0')).to.be.false();
|
||||
@@ -47,13 +53,13 @@ describe('Web3Wrapper tests', () => {
|
||||
});
|
||||
describe('#getAvailableAddressesAsync', () => {
|
||||
it('gets the available addresses', async () => {
|
||||
const addresses = await web3Wrapper.getAvailableAddressesAsync();
|
||||
expect(addresses.length).to.be.equal(NUM_GANACHE_ADDRESSES);
|
||||
const availableAddresses = await web3Wrapper.getAvailableAddressesAsync();
|
||||
expect(availableAddresses.length).to.be.equal(NUM_GANACHE_ADDRESSES);
|
||||
expect(Web3Wrapper.isAddress(availableAddresses[0])).to.equal(true);
|
||||
});
|
||||
});
|
||||
describe('#getBalanceInWeiAsync', () => {
|
||||
it('gets the users balance in wei', async () => {
|
||||
const addresses = await web3Wrapper.getAvailableAddressesAsync();
|
||||
const secondAccount = addresses[1];
|
||||
const balanceInWei = await web3Wrapper.getBalanceInWeiAsync(secondAccount);
|
||||
const tenEthInWei = 100000000000000000000;
|
||||
@@ -64,11 +70,28 @@ describe('Web3Wrapper tests', () => {
|
||||
expect(web3Wrapper.getBalanceInWeiAsync(invalidEthAddress)).to.eventually.to.be.rejected();
|
||||
});
|
||||
});
|
||||
describe('#signMessageAsync', () => {
|
||||
it('should sign message', async () => {
|
||||
const message = '0xdeadbeef';
|
||||
const signer = addresses[1];
|
||||
const signature = await web3Wrapper.signMessageAsync(signer, message);
|
||||
const signatureLength = 132;
|
||||
expect(signature.length).to.be.equal(signatureLength);
|
||||
});
|
||||
});
|
||||
describe('#getBlockNumberAsync', () => {
|
||||
it('get block number', async () => {
|
||||
const blockNumber = await web3Wrapper.getBlockNumberAsync();
|
||||
expect(typeof blockNumber).to.be.equal('number');
|
||||
});
|
||||
});
|
||||
describe('#getBlockAsync', () => {
|
||||
it('gets block when supplied a valid BlockParamLiteral value', async () => {
|
||||
const blockParamLiteral = BlockParamLiteral.Earliest;
|
||||
const block = await web3Wrapper.getBlockAsync(blockParamLiteral);
|
||||
expect(block.number).to.be.equal(0);
|
||||
expect(utils.isBigNumber(block.difficulty)).to.equal(true);
|
||||
expect(_.isNumber(block.gasLimit)).to.equal(true);
|
||||
});
|
||||
it('gets block when supplied a block number', async () => {
|
||||
const blockParamLiteral = 0;
|
||||
@@ -86,4 +109,24 @@ describe('Web3Wrapper tests', () => {
|
||||
expect(web3Wrapper.getBlockAsync(invalidBlockParam)).to.eventually.to.be.rejected();
|
||||
});
|
||||
});
|
||||
describe('#getBlockWithTransactionDataAsync', () => {
|
||||
it('gets block when supplied a valid BlockParamLiteral value', async () => {
|
||||
const blockParamLiteral = BlockParamLiteral.Earliest;
|
||||
const block = await web3Wrapper.getBlockWithTransactionDataAsync(blockParamLiteral);
|
||||
expect(block.number).to.be.equal(0);
|
||||
expect(utils.isBigNumber(block.difficulty)).to.equal(true);
|
||||
expect(_.isNumber(block.gasLimit)).to.equal(true);
|
||||
});
|
||||
it('should throw if supplied invalid blockParam value', async () => {
|
||||
const invalidBlockParam = 'deadbeef';
|
||||
expect(web3Wrapper.getBlockWithTransactionDataAsync(invalidBlockParam)).to.eventually.to.be.rejected();
|
||||
});
|
||||
});
|
||||
describe('#getBlockTimestampAsync', () => {
|
||||
it('gets block timestamp', async () => {
|
||||
const blockParamLiteral = BlockParamLiteral.Earliest;
|
||||
const timestamp = await web3Wrapper.getBlockTimestampAsync(blockParamLiteral);
|
||||
expect(_.isNumber(timestamp)).to.be.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12649,7 +12649,7 @@ web3-typescript-typings@^0.10.2:
|
||||
dependencies:
|
||||
bignumber.js "~4.1.0"
|
||||
|
||||
web3-utils@1.0.0-beta.34:
|
||||
web3-utils@1.0.0-beta.34, web3-utils@^1.0.0-beta.34:
|
||||
version "1.0.0-beta.34"
|
||||
resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.0.0-beta.34.tgz#9411fc39aaef39ca4e06169f762297d9ff020970"
|
||||
dependencies:
|
||||
|
||||
Reference in New Issue
Block a user