Add more assertions to Web3Wrapper public methods
This commit is contained in:
parent
590033bcb2
commit
cd766ea2a1
@ -1,6 +1,8 @@
|
|||||||
|
import { assert } from '@0xproject/assert';
|
||||||
import { AbiDecoder, addressUtils, BigNumber, intervalUtils, promisify } from '@0xproject/utils';
|
import { AbiDecoder, addressUtils, BigNumber, intervalUtils, promisify } from '@0xproject/utils';
|
||||||
import {
|
import {
|
||||||
BlockParam,
|
BlockParam,
|
||||||
|
BlockParamLiteral,
|
||||||
BlockWithoutTransactionData,
|
BlockWithoutTransactionData,
|
||||||
BlockWithTransactionData,
|
BlockWithTransactionData,
|
||||||
CallData,
|
CallData,
|
||||||
@ -60,6 +62,8 @@ export class Web3Wrapper {
|
|||||||
* @return The amount in units.
|
* @return The amount in units.
|
||||||
*/
|
*/
|
||||||
public static toUnitAmount(amount: BigNumber, decimals: number): BigNumber {
|
public static toUnitAmount(amount: BigNumber, decimals: number): BigNumber {
|
||||||
|
assert.isValidBaseUnitAmount('amount', amount);
|
||||||
|
assert.isNumber('decimals', decimals);
|
||||||
const aUnit = new BigNumber(BASE_TEN).pow(decimals);
|
const aUnit = new BigNumber(BASE_TEN).pow(decimals);
|
||||||
const unit = amount.div(aUnit);
|
const unit = amount.div(aUnit);
|
||||||
return unit;
|
return unit;
|
||||||
@ -73,6 +77,8 @@ export class Web3Wrapper {
|
|||||||
* @return The amount in baseUnits.
|
* @return The amount in baseUnits.
|
||||||
*/
|
*/
|
||||||
public static toBaseUnitAmount(amount: BigNumber, decimals: number): BigNumber {
|
public static toBaseUnitAmount(amount: BigNumber, decimals: number): BigNumber {
|
||||||
|
assert.isBigNumber('amount', amount);
|
||||||
|
assert.isNumber('decimals', decimals);
|
||||||
const unit = new BigNumber(BASE_TEN).pow(decimals);
|
const unit = new BigNumber(BASE_TEN).pow(decimals);
|
||||||
const baseUnitAmount = amount.times(unit);
|
const baseUnitAmount = amount.times(unit);
|
||||||
const hasDecimals = baseUnitAmount.decimalPlaces() !== 0;
|
const hasDecimals = baseUnitAmount.decimalPlaces() !== 0;
|
||||||
@ -87,10 +93,30 @@ export class Web3Wrapper {
|
|||||||
* @returns Amount in wei
|
* @returns Amount in wei
|
||||||
*/
|
*/
|
||||||
public static toWei(ethAmount: BigNumber): BigNumber {
|
public static toWei(ethAmount: BigNumber): BigNumber {
|
||||||
|
assert.isBigNumber('ethAmount', ethAmount);
|
||||||
const ETH_DECIMALS = 18;
|
const ETH_DECIMALS = 18;
|
||||||
const balanceWei = Web3Wrapper.toBaseUnitAmount(ethAmount, ETH_DECIMALS);
|
const balanceWei = Web3Wrapper.toBaseUnitAmount(ethAmount, ETH_DECIMALS);
|
||||||
return balanceWei;
|
return balanceWei;
|
||||||
}
|
}
|
||||||
|
private static _assertBlockParam(blockParam: string | BlockParam): void {
|
||||||
|
if (_.isNumber(blockParam)) {
|
||||||
|
return;
|
||||||
|
} else if (_.isString(blockParam)) {
|
||||||
|
assert.doesBelongToStringEnum('blockParam', blockParam, BlockParamLiteral);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static _assertBlockParamOrString(blockParam: string | BlockParam): void {
|
||||||
|
try {
|
||||||
|
Web3Wrapper._assertBlockParam(blockParam);
|
||||||
|
} catch (err) {
|
||||||
|
try {
|
||||||
|
assert.isHexString('blockParam', blockParam as string);
|
||||||
|
return;
|
||||||
|
} catch (err) {
|
||||||
|
throw new Error(`Expected blockParam to be of type "string | BlockParam", encountered ${blockParam}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Instantiates a new Web3Wrapper.
|
* Instantiates a new Web3Wrapper.
|
||||||
* @param provider The Web3 provider instance you would like the Web3Wrapper to use for interacting with
|
* @param provider The Web3 provider instance you would like the Web3Wrapper to use for interacting with
|
||||||
@ -99,6 +125,7 @@ export class Web3Wrapper {
|
|||||||
* @return An instance of the Web3Wrapper class.
|
* @return An instance of the Web3Wrapper class.
|
||||||
*/
|
*/
|
||||||
constructor(provider: Provider, txDefaults?: Partial<TxData>) {
|
constructor(provider: Provider, txDefaults?: Partial<TxData>) {
|
||||||
|
assert.isWeb3Provider('provider', provider);
|
||||||
if (_.isUndefined((provider as any).sendAsync)) {
|
if (_.isUndefined((provider as any).sendAsync)) {
|
||||||
// Web3@1.0 provider doesn't support synchronous http requests,
|
// Web3@1.0 provider doesn't support synchronous http requests,
|
||||||
// so it only has an async `send` method, instead of a `send` and `sendAsync` in web3@0.x.x`
|
// so it only has an async `send` method, instead of a `send` and `sendAsync` in web3@0.x.x`
|
||||||
@ -130,6 +157,7 @@ export class Web3Wrapper {
|
|||||||
* @param provider The new Web3 provider to be set
|
* @param provider The new Web3 provider to be set
|
||||||
*/
|
*/
|
||||||
public setProvider(provider: Provider): void {
|
public setProvider(provider: Provider): void {
|
||||||
|
assert.isWeb3Provider('provider', provider);
|
||||||
this._web3.setProvider(provider);
|
this._web3.setProvider(provider);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@ -140,6 +168,7 @@ export class Web3Wrapper {
|
|||||||
* @returns Whether the address is available through the provider.
|
* @returns Whether the address is available through the provider.
|
||||||
*/
|
*/
|
||||||
public async isSenderAddressAvailableAsync(senderAddress: string): Promise<boolean> {
|
public async isSenderAddressAvailableAsync(senderAddress: string): Promise<boolean> {
|
||||||
|
assert.isETHAddressHex('senderAddress', senderAddress);
|
||||||
const addresses = await this.getAvailableAddressesAsync();
|
const addresses = await this.getAvailableAddressesAsync();
|
||||||
const normalizedAddress = senderAddress.toLowerCase();
|
const normalizedAddress = senderAddress.toLowerCase();
|
||||||
return _.includes(addresses, normalizedAddress);
|
return _.includes(addresses, normalizedAddress);
|
||||||
@ -179,10 +208,13 @@ export class Web3Wrapper {
|
|||||||
* @returns Balance in wei
|
* @returns Balance in wei
|
||||||
*/
|
*/
|
||||||
public async getBalanceInWeiAsync(owner: string): Promise<BigNumber> {
|
public async getBalanceInWeiAsync(owner: string): Promise<BigNumber> {
|
||||||
let balanceInWei = await promisify<BigNumber>(this._web3.eth.getBalance)(owner);
|
assert.isETHAddressHex('owner', owner);
|
||||||
|
const balanceInWei = await this._sendRawPayloadAsync<string>({
|
||||||
|
method: 'eth_getBalance',
|
||||||
|
params: [owner],
|
||||||
|
});
|
||||||
// Rewrap in a new BigNumber
|
// Rewrap in a new BigNumber
|
||||||
balanceInWei = new BigNumber(balanceInWei);
|
return new BigNumber(balanceInWei);
|
||||||
return balanceInWei;
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Check if a contract exists at a given address
|
* Check if a contract exists at a given address
|
||||||
@ -190,6 +222,7 @@ export class Web3Wrapper {
|
|||||||
* @returns Whether or not contract code was found at the supplied address
|
* @returns Whether or not contract code was found at the supplied address
|
||||||
*/
|
*/
|
||||||
public async doesContractExistAtAddressAsync(address: string): Promise<boolean> {
|
public async doesContractExistAtAddressAsync(address: string): Promise<boolean> {
|
||||||
|
assert.isETHAddressHex('address', address);
|
||||||
const code = await this.getContractCodeAsync(address);
|
const code = await this.getContractCodeAsync(address);
|
||||||
// Regex matches 0x0, 0x00, 0x in order to accommodate poorly implemented clients
|
// Regex matches 0x0, 0x00, 0x in order to accommodate poorly implemented clients
|
||||||
const isCodeEmpty = /^0x0{0,40}$/i.test(code);
|
const isCodeEmpty = /^0x0{0,40}$/i.test(code);
|
||||||
@ -201,6 +234,7 @@ export class Web3Wrapper {
|
|||||||
* @return Code of the contract
|
* @return Code of the contract
|
||||||
*/
|
*/
|
||||||
public async getContractCodeAsync(address: string): Promise<string> {
|
public async getContractCodeAsync(address: string): Promise<string> {
|
||||||
|
assert.isETHAddressHex('address', address);
|
||||||
const code = await promisify<string>(this._web3.eth.getCode)(address);
|
const code = await promisify<string>(this._web3.eth.getCode)(address);
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
@ -211,6 +245,7 @@ export class Web3Wrapper {
|
|||||||
* @return Transaction trace
|
* @return Transaction trace
|
||||||
*/
|
*/
|
||||||
public async getTransactionTraceAsync(txHash: string, traceParams: TraceParams): Promise<TransactionTrace> {
|
public async getTransactionTraceAsync(txHash: string, traceParams: TraceParams): Promise<TransactionTrace> {
|
||||||
|
assert.isHexString('txHash', txHash);
|
||||||
const trace = await this._sendRawPayloadAsync<TransactionTrace>({
|
const trace = await this._sendRawPayloadAsync<TransactionTrace>({
|
||||||
method: 'debug_traceTransaction',
|
method: 'debug_traceTransaction',
|
||||||
params: [txHash, traceParams],
|
params: [txHash, traceParams],
|
||||||
@ -224,6 +259,8 @@ export class Web3Wrapper {
|
|||||||
* @returns Signature string (might be VRS or RSV depending on the Signer)
|
* @returns Signature string (might be VRS or RSV depending on the Signer)
|
||||||
*/
|
*/
|
||||||
public async signMessageAsync(address: string, message: string): Promise<string> {
|
public async signMessageAsync(address: string, message: string): Promise<string> {
|
||||||
|
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 promisify<string>(this._web3.eth.sign)(address, message);
|
||||||
return signData;
|
return signData;
|
||||||
}
|
}
|
||||||
@ -241,6 +278,7 @@ export class Web3Wrapper {
|
|||||||
* @returns The requested block without transaction data
|
* @returns The requested block without transaction data
|
||||||
*/
|
*/
|
||||||
public async getBlockAsync(blockParam: string | BlockParam): Promise<BlockWithoutTransactionData> {
|
public async getBlockAsync(blockParam: string | BlockParam): Promise<BlockWithoutTransactionData> {
|
||||||
|
Web3Wrapper._assertBlockParamOrString(blockParam);
|
||||||
const shouldIncludeTransactionData = false;
|
const shouldIncludeTransactionData = false;
|
||||||
const blockWithoutTransactionData = await promisify<BlockWithoutTransactionData>(this._web3.eth.getBlock)(
|
const blockWithoutTransactionData = await promisify<BlockWithoutTransactionData>(this._web3.eth.getBlock)(
|
||||||
blockParam,
|
blockParam,
|
||||||
@ -254,6 +292,7 @@ export class Web3Wrapper {
|
|||||||
* @returns The requested block with transaction data
|
* @returns The requested block with transaction data
|
||||||
*/
|
*/
|
||||||
public async getBlockWithTransactionDataAsync(blockParam: string | BlockParam): Promise<BlockWithTransactionData> {
|
public async getBlockWithTransactionDataAsync(blockParam: string | BlockParam): Promise<BlockWithTransactionData> {
|
||||||
|
Web3Wrapper._assertBlockParamOrString(blockParam);
|
||||||
const shouldIncludeTransactionData = true;
|
const shouldIncludeTransactionData = true;
|
||||||
const blockWithTransactionData = await promisify<BlockWithTransactionData>(this._web3.eth.getBlock)(
|
const blockWithTransactionData = await promisify<BlockWithTransactionData>(this._web3.eth.getBlock)(
|
||||||
blockParam,
|
blockParam,
|
||||||
@ -267,6 +306,7 @@ export class Web3Wrapper {
|
|||||||
* @returns The block's timestamp
|
* @returns The block's timestamp
|
||||||
*/
|
*/
|
||||||
public async getBlockTimestampAsync(blockParam: string | BlockParam): Promise<number> {
|
public async getBlockTimestampAsync(blockParam: string | BlockParam): Promise<number> {
|
||||||
|
Web3Wrapper._assertBlockParamOrString(blockParam);
|
||||||
const { timestamp } = await this.getBlockAsync(blockParam);
|
const { timestamp } = await this.getBlockAsync(blockParam);
|
||||||
return timestamp;
|
return timestamp;
|
||||||
}
|
}
|
||||||
@ -293,6 +333,7 @@ export class Web3Wrapper {
|
|||||||
* @returns Whether the revert was successful
|
* @returns Whether the revert was successful
|
||||||
*/
|
*/
|
||||||
public async revertSnapshotAsync(snapshotId: number): Promise<boolean> {
|
public async revertSnapshotAsync(snapshotId: number): Promise<boolean> {
|
||||||
|
assert.isNumber('snapshotId', snapshotId);
|
||||||
const didRevert = await this._sendRawPayloadAsync<boolean>({ method: 'evm_revert', params: [snapshotId] });
|
const didRevert = await this._sendRawPayloadAsync<boolean>({ method: 'evm_revert', params: [snapshotId] });
|
||||||
return didRevert;
|
return didRevert;
|
||||||
}
|
}
|
||||||
@ -308,6 +349,7 @@ export class Web3Wrapper {
|
|||||||
* @param timeDelta Amount of time to add in seconds
|
* @param timeDelta Amount of time to add in seconds
|
||||||
*/
|
*/
|
||||||
public async increaseTimeAsync(timeDelta: number): Promise<number> {
|
public async increaseTimeAsync(timeDelta: number): Promise<number> {
|
||||||
|
assert.isNumber('timeDelta', timeDelta);
|
||||||
// Detect Geth vs. Ganache and use appropriate endpoint.
|
// Detect Geth vs. Ganache and use appropriate endpoint.
|
||||||
const version = await this.getNodeVersionAsync();
|
const version = await this.getNodeVersionAsync();
|
||||||
if (_.includes(version, uniqueVersionIds.geth)) {
|
if (_.includes(version, uniqueVersionIds.geth)) {
|
||||||
@ -371,6 +413,9 @@ export class Web3Wrapper {
|
|||||||
* @returns The raw call result
|
* @returns The raw call result
|
||||||
*/
|
*/
|
||||||
public async callAsync(callData: CallData, defaultBlock?: BlockParam): Promise<string> {
|
public async callAsync(callData: CallData, defaultBlock?: BlockParam): Promise<string> {
|
||||||
|
if (!_.isUndefined(defaultBlock)) {
|
||||||
|
Web3Wrapper._assertBlockParam(defaultBlock);
|
||||||
|
}
|
||||||
const rawCallResult = await promisify<string>(this._web3.eth.call)(callData, defaultBlock);
|
const rawCallResult = await promisify<string>(this._web3.eth.call)(callData, defaultBlock);
|
||||||
if (rawCallResult === '0x') {
|
if (rawCallResult === '0x') {
|
||||||
throw new Error('Contract call failed (returned null)');
|
throw new Error('Contract call failed (returned null)');
|
||||||
@ -402,6 +447,11 @@ export class Web3Wrapper {
|
|||||||
pollingIntervalMs: number = 1000,
|
pollingIntervalMs: number = 1000,
|
||||||
timeoutMs?: number,
|
timeoutMs?: number,
|
||||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||||
|
assert.isHexString('txHash', txHash);
|
||||||
|
assert.isNumber('pollingIntervalMs', pollingIntervalMs);
|
||||||
|
if (!_.isUndefined(timeoutMs)) {
|
||||||
|
assert.isNumber('timeoutMs', timeoutMs);
|
||||||
|
}
|
||||||
// Immediately check if the transaction has already been mined.
|
// Immediately check if the transaction has already been mined.
|
||||||
let transactionReceipt = await this.getTransactionReceiptAsync(txHash);
|
let transactionReceipt = await this.getTransactionReceiptAsync(txHash);
|
||||||
if (!_.isNull(transactionReceipt)) {
|
if (!_.isNull(transactionReceipt)) {
|
||||||
@ -487,6 +537,7 @@ export class Web3Wrapper {
|
|||||||
* @param blockNumber The block number to reset to.
|
* @param blockNumber The block number to reset to.
|
||||||
*/
|
*/
|
||||||
public async setHeadAsync(blockNumber: number): Promise<void> {
|
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: [this._web3.toHex(blockNumber)] });
|
||||||
}
|
}
|
||||||
private async _sendRawPayloadAsync<A>(payload: Partial<JSONRPCRequestPayload>): Promise<A> {
|
private async _sendRawPayloadAsync<A>(payload: Partial<JSONRPCRequestPayload>): Promise<A> {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import * as chai from 'chai';
|
import * as chai from 'chai';
|
||||||
|
import { BlockParamLiteral } from 'ethereum-types';
|
||||||
import * as Ganache from 'ganache-core';
|
import * as Ganache from 'ganache-core';
|
||||||
import 'mocha';
|
import 'mocha';
|
||||||
|
|
||||||
@ -9,6 +10,8 @@ chaiSetup.configure();
|
|||||||
|
|
||||||
const { expect } = chai;
|
const { expect } = chai;
|
||||||
|
|
||||||
|
const NUM_GANACHE_ADDRESSES = 10;
|
||||||
|
|
||||||
describe('Web3Wrapper tests', () => {
|
describe('Web3Wrapper tests', () => {
|
||||||
const NETWORK_ID = 50;
|
const NETWORK_ID = 50;
|
||||||
const provider = Ganache.provider({ network_id: NETWORK_ID });
|
const provider = Ganache.provider({ network_id: NETWORK_ID });
|
||||||
@ -36,4 +39,51 @@ describe('Web3Wrapper tests', () => {
|
|||||||
expect(networkId).to.be.equal(NETWORK_ID);
|
expect(networkId).to.be.equal(NETWORK_ID);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe('#getNetworkIdAsync', () => {
|
||||||
|
it('gets the network id', async () => {
|
||||||
|
const networkId = await web3Wrapper.getNetworkIdAsync();
|
||||||
|
expect(networkId).to.be.equal(NETWORK_ID);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('#getAvailableAddressesAsync', () => {
|
||||||
|
it('gets the available addresses', async () => {
|
||||||
|
const addresses = await web3Wrapper.getAvailableAddressesAsync();
|
||||||
|
expect(addresses.length).to.be.equal(NUM_GANACHE_ADDRESSES);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
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;
|
||||||
|
expect(balanceInWei).to.be.bignumber.equal(tenEthInWei);
|
||||||
|
});
|
||||||
|
it('should throw if supplied owner not an Ethereum address hex string', async () => {
|
||||||
|
const invalidEthAddress = 'deadbeef';
|
||||||
|
expect(web3Wrapper.getBalanceInWeiAsync(invalidEthAddress)).to.eventually.to.be.rejected();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
it('gets block when supplied a block number', async () => {
|
||||||
|
const blockParamLiteral = 0;
|
||||||
|
const block = await web3Wrapper.getBlockAsync(blockParamLiteral);
|
||||||
|
expect(block.number).to.be.equal(0);
|
||||||
|
});
|
||||||
|
it('gets block when supplied a block hash', async () => {
|
||||||
|
const blockParamLiteral = 0;
|
||||||
|
const block = await web3Wrapper.getBlockAsync(blockParamLiteral);
|
||||||
|
const sameBlock = await web3Wrapper.getBlockAsync(block.hash as string);
|
||||||
|
expect(sameBlock.number).to.be.equal(0);
|
||||||
|
});
|
||||||
|
it('should throw if supplied invalid blockParam value', async () => {
|
||||||
|
const invalidBlockParam = 'deadbeef';
|
||||||
|
expect(web3Wrapper.getBlockAsync(invalidBlockParam)).to.eventually.to.be.rejected();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user