From cd766ea2a1fb56282fc45a1a19d991bbcae8db99 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Wed, 4 Jul 2018 08:54:43 +0200 Subject: Add more assertions to Web3Wrapper public methods --- packages/web3-wrapper/src/web3_wrapper.ts | 57 +++++++++++++++++++++++-- packages/web3-wrapper/test/web3_wrapper_test.ts | 50 ++++++++++++++++++++++ 2 files changed, 104 insertions(+), 3 deletions(-) diff --git a/packages/web3-wrapper/src/web3_wrapper.ts b/packages/web3-wrapper/src/web3_wrapper.ts index 6ea69883c..e66c7073f 100644 --- a/packages/web3-wrapper/src/web3_wrapper.ts +++ b/packages/web3-wrapper/src/web3_wrapper.ts @@ -1,6 +1,8 @@ +import { assert } from '@0xproject/assert'; import { AbiDecoder, addressUtils, BigNumber, intervalUtils, promisify } from '@0xproject/utils'; import { BlockParam, + BlockParamLiteral, BlockWithoutTransactionData, BlockWithTransactionData, CallData, @@ -60,6 +62,8 @@ export class Web3Wrapper { * @return The amount in units. */ 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 unit = amount.div(aUnit); return unit; @@ -73,6 +77,8 @@ export class Web3Wrapper { * @return The amount in baseUnits. */ 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 baseUnitAmount = amount.times(unit); const hasDecimals = baseUnitAmount.decimalPlaces() !== 0; @@ -87,10 +93,30 @@ export class Web3Wrapper { * @returns Amount in wei */ public static toWei(ethAmount: BigNumber): BigNumber { + assert.isBigNumber('ethAmount', ethAmount); const ETH_DECIMALS = 18; const balanceWei = Web3Wrapper.toBaseUnitAmount(ethAmount, ETH_DECIMALS); 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. * @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. */ constructor(provider: Provider, txDefaults?: Partial) { + assert.isWeb3Provider('provider', provider); if (_.isUndefined((provider as any).sendAsync)) { // 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` @@ -130,6 +157,7 @@ export class Web3Wrapper { * @param provider The new Web3 provider to be set */ public setProvider(provider: Provider): void { + assert.isWeb3Provider('provider', provider); this._web3.setProvider(provider); } /** @@ -140,6 +168,7 @@ export class Web3Wrapper { * @returns Whether the address is available through the provider. */ public async isSenderAddressAvailableAsync(senderAddress: string): Promise { + assert.isETHAddressHex('senderAddress', senderAddress); const addresses = await this.getAvailableAddressesAsync(); const normalizedAddress = senderAddress.toLowerCase(); return _.includes(addresses, normalizedAddress); @@ -179,10 +208,13 @@ export class Web3Wrapper { * @returns Balance in wei */ public async getBalanceInWeiAsync(owner: string): Promise { - let balanceInWei = await promisify(this._web3.eth.getBalance)(owner); + assert.isETHAddressHex('owner', owner); + const balanceInWei = await this._sendRawPayloadAsync({ + method: 'eth_getBalance', + params: [owner], + }); // Rewrap in a new BigNumber - balanceInWei = new BigNumber(balanceInWei); - return balanceInWei; + return new BigNumber(balanceInWei); } /** * 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 */ public async doesContractExistAtAddressAsync(address: string): Promise { + assert.isETHAddressHex('address', address); const code = await this.getContractCodeAsync(address); // Regex matches 0x0, 0x00, 0x in order to accommodate poorly implemented clients const isCodeEmpty = /^0x0{0,40}$/i.test(code); @@ -201,6 +234,7 @@ export class Web3Wrapper { * @return Code of the contract */ public async getContractCodeAsync(address: string): Promise { + assert.isETHAddressHex('address', address); const code = await promisify(this._web3.eth.getCode)(address); return code; } @@ -211,6 +245,7 @@ export class Web3Wrapper { * @return Transaction trace */ public async getTransactionTraceAsync(txHash: string, traceParams: TraceParams): Promise { + assert.isHexString('txHash', txHash); const trace = await this._sendRawPayloadAsync({ method: 'debug_traceTransaction', params: [txHash, traceParams], @@ -224,6 +259,8 @@ export class Web3Wrapper { * @returns Signature string (might be VRS or RSV depending on the Signer) */ public async signMessageAsync(address: string, message: string): Promise { + assert.isETHAddressHex('address', address); + assert.isString('message', message); // TODO: Should this be stricter? Hex string? const signData = await promisify(this._web3.eth.sign)(address, message); return signData; } @@ -241,6 +278,7 @@ export class Web3Wrapper { * @returns The requested block without transaction data */ public async getBlockAsync(blockParam: string | BlockParam): Promise { + Web3Wrapper._assertBlockParamOrString(blockParam); const shouldIncludeTransactionData = false; const blockWithoutTransactionData = await promisify(this._web3.eth.getBlock)( blockParam, @@ -254,6 +292,7 @@ export class Web3Wrapper { * @returns The requested block with transaction data */ public async getBlockWithTransactionDataAsync(blockParam: string | BlockParam): Promise { + Web3Wrapper._assertBlockParamOrString(blockParam); const shouldIncludeTransactionData = true; const blockWithTransactionData = await promisify(this._web3.eth.getBlock)( blockParam, @@ -267,6 +306,7 @@ export class Web3Wrapper { * @returns The block's timestamp */ public async getBlockTimestampAsync(blockParam: string | BlockParam): Promise { + Web3Wrapper._assertBlockParamOrString(blockParam); const { timestamp } = await this.getBlockAsync(blockParam); return timestamp; } @@ -293,6 +333,7 @@ export class Web3Wrapper { * @returns Whether the revert was successful */ public async revertSnapshotAsync(snapshotId: number): Promise { + assert.isNumber('snapshotId', snapshotId); const didRevert = await this._sendRawPayloadAsync({ method: 'evm_revert', params: [snapshotId] }); return didRevert; } @@ -308,6 +349,7 @@ export class Web3Wrapper { * @param timeDelta Amount of time to add in seconds */ public async increaseTimeAsync(timeDelta: number): Promise { + assert.isNumber('timeDelta', timeDelta); // Detect Geth vs. Ganache and use appropriate endpoint. const version = await this.getNodeVersionAsync(); if (_.includes(version, uniqueVersionIds.geth)) { @@ -371,6 +413,9 @@ export class Web3Wrapper { * @returns The raw call result */ public async callAsync(callData: CallData, defaultBlock?: BlockParam): Promise { + if (!_.isUndefined(defaultBlock)) { + Web3Wrapper._assertBlockParam(defaultBlock); + } const rawCallResult = await promisify(this._web3.eth.call)(callData, defaultBlock); if (rawCallResult === '0x') { throw new Error('Contract call failed (returned null)'); @@ -402,6 +447,11 @@ export class Web3Wrapper { pollingIntervalMs: number = 1000, timeoutMs?: number, ): Promise { + assert.isHexString('txHash', txHash); + assert.isNumber('pollingIntervalMs', pollingIntervalMs); + if (!_.isUndefined(timeoutMs)) { + assert.isNumber('timeoutMs', timeoutMs); + } // Immediately check if the transaction has already been mined. let transactionReceipt = await this.getTransactionReceiptAsync(txHash); if (!_.isNull(transactionReceipt)) { @@ -487,6 +537,7 @@ export class Web3Wrapper { * @param blockNumber The block number to reset to. */ public async setHeadAsync(blockNumber: number): Promise { + assert.isNumber('blockNumber', blockNumber); await this._sendRawPayloadAsync({ method: 'debug_setHead', params: [this._web3.toHex(blockNumber)] }); } private async _sendRawPayloadAsync(payload: Partial): Promise { diff --git a/packages/web3-wrapper/test/web3_wrapper_test.ts b/packages/web3-wrapper/test/web3_wrapper_test.ts index 35eab3aa2..8919775c2 100644 --- a/packages/web3-wrapper/test/web3_wrapper_test.ts +++ b/packages/web3-wrapper/test/web3_wrapper_test.ts @@ -1,4 +1,5 @@ import * as chai from 'chai'; +import { BlockParamLiteral } from 'ethereum-types'; import * as Ganache from 'ganache-core'; import 'mocha'; @@ -9,6 +10,8 @@ chaiSetup.configure(); const { expect } = chai; +const NUM_GANACHE_ADDRESSES = 10; + describe('Web3Wrapper tests', () => { const NETWORK_ID = 50; const provider = Ganache.provider({ network_id: NETWORK_ID }); @@ -36,4 +39,51 @@ describe('Web3Wrapper tests', () => { 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(); + }); + }); }); -- cgit v1.2.3 From 2bec5cb7fd2383a415c04ed67e9cf4cd2ce1d87e Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Wed, 4 Jul 2018 08:55:16 +0200 Subject: Fix bug in string enum assertion so that we check enum values not keys --- packages/assert/src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/assert/src/index.ts b/packages/assert/src/index.ts index 95c7b658b..8e18416c5 100644 --- a/packages/assert/src/index.ts +++ b/packages/assert/src/index.ts @@ -41,8 +41,8 @@ export const assert = { value: string, stringEnum: any /* There is no base type for every string enum */, ): void { - const doesBelongToStringEnum = !_.isUndefined(stringEnum[value]); - const enumValues = _.keys(stringEnum); + const enumValues = _.values(stringEnum); + const doesBelongToStringEnum = _.includes(enumValues, value); const enumValuesAsStrings = _.map(enumValues, enumValue => `'${enumValue}'`); const enumValuesAsString = enumValuesAsStrings.join(', '); assert.assert( -- cgit v1.2.3 From ce86ae01919017a158cc2f0d5ada50c555df90e3 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Wed, 4 Jul 2018 08:56:12 +0200 Subject: Add assert dep --- packages/web3-wrapper/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/web3-wrapper/package.json b/packages/web3-wrapper/package.json index 5f7f2cb00..d6d86b699 100644 --- a/packages/web3-wrapper/package.json +++ b/packages/web3-wrapper/package.json @@ -64,6 +64,7 @@ "typescript": "2.7.1" }, "dependencies": { + "@0xproject/assert": "^0.2.12", "@0xproject/typescript-typings": "^0.4.1", "@0xproject/utils": "^0.7.1", "ethereum-types": "^0.0.1", -- cgit v1.2.3 From d712dc47d97d3051e2938bf11b9088aba657b869 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Wed, 4 Jul 2018 18:11:50 +0200 Subject: Removee getContractFromAbi method since we no longer use it and do not recommend others use it --- packages/web3-wrapper/src/web3_wrapper.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/packages/web3-wrapper/src/web3_wrapper.ts b/packages/web3-wrapper/src/web3_wrapper.ts index e66c7073f..640d3eac9 100644 --- a/packages/web3-wrapper/src/web3_wrapper.ts +++ b/packages/web3-wrapper/src/web3_wrapper.ts @@ -388,15 +388,6 @@ export class Web3Wrapper { const formattedLogs = _.map(rawLogs, this._formatLog.bind(this)); return formattedLogs; } - /** - * Get a Web3 contract factory instance for a given ABI - * @param abi Smart contract ABI - * @returns Web3 contract factory which can create Web3 Contract instances from the supplied ABI - */ - public getContractFromAbi(abi: ContractAbi): Web3.Contract { - const web3Contract = this._web3.eth.contract(abi); - return web3Contract; - } /** * Calculate the estimated gas cost for a given transaction * @param txData Transaction data -- cgit v1.2.3 From 1050ecdf3c9e0dbc881342b8ff377180ed1b0dad Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 5 Jul 2018 00:36:01 +0200 Subject: Refactor Web3Wrapper to no longer use Web3.js & add more test coverage --- packages/web3-wrapper/package.json | 5 +- packages/web3-wrapper/src/globals.d.ts | 8 ++ packages/web3-wrapper/src/marshaller.ts | 177 ++++++++++++++++++++++++ packages/web3-wrapper/src/types.ts | 56 ++++++++ packages/web3-wrapper/src/utils.ts | 8 ++ packages/web3-wrapper/src/web3_wrapper.ts | 158 ++++++++++++--------- packages/web3-wrapper/test/web3_wrapper_test.ts | 51 ++++++- yarn.lock | 2 +- 8 files changed, 397 insertions(+), 68 deletions(-) create mode 100644 packages/web3-wrapper/src/marshaller.ts create mode 100644 packages/web3-wrapper/src/utils.ts diff --git a/packages/web3-wrapper/package.json b/packages/web3-wrapper/package.json index d6d86b699..f12142d3c 100644 --- a/packages/web3-wrapper/package.json +++ b/packages/web3-wrapper/package.json @@ -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" diff --git a/packages/web3-wrapper/src/globals.d.ts b/packages/web3-wrapper/src/globals.d.ts index 94e63a32d..09c9fd819 100644 --- a/packages/web3-wrapper/src/globals.d.ts +++ b/packages/web3-wrapper/src/globals.d.ts @@ -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 */ diff --git a/packages/web3-wrapper/src/marshaller.ts b/packages/web3-wrapper/src/marshaller.ts new file mode 100644 index 000000000..06556ce90 --- /dev/null +++ b/packages/web3-wrapper/src/marshaller.ts @@ -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): Partial { + 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): Partial { + 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): Partial { + 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; + }, +}; diff --git a/packages/web3-wrapper/src/types.ts b/packages/web3-wrapper/src/types.ts index 79542da10..54a5f3f0e 100644 --- a/packages/web3-wrapper/src/types.ts +++ b/packages/web3-wrapper/src/types.ts @@ -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; +} diff --git a/packages/web3-wrapper/src/utils.ts b/packages/web3-wrapper/src/utils.ts new file mode 100644 index 000000000..376f7e89b --- /dev/null +++ b/packages/web3-wrapper/src/utils.ts @@ -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; + }, +}; diff --git a/packages/web3-wrapper/src/web3_wrapper.ts b/packages/web3-wrapper/src/web3_wrapper.ts index 640d3eac9..29adb18ff 100644 --- a/packages/web3-wrapper/src/web3_wrapper.ts +++ b/packages/web3-wrapper/src/web3_wrapper.ts @@ -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; 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 { - const transactionReceipt = await promisify(this._web3.eth.getTransactionReceipt)(txHash); + assert.isHexString('txHash', txHash); + const transactionReceipt = await this._sendRawPayloadAsync({ + 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 { + public async getContractCodeAsync(address: string, defaultBlock?: BlockParam): Promise { assert.isETHAddressHex('address', address); - const code = await promisify(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({ + 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 { + assert.isETHAddressHex('address', address); assert.isETHAddressHex('address', address); assert.isString('message', message); // TODO: Should this be stricter? Hex string? - const signData = await promisify(this._web3.eth.sign)(address, message); + const signData = await this._sendRawPayloadAsync({ + method: 'eth_sign', + params: [address, message], + }); return signData; } /** @@ -269,8 +304,12 @@ export class Web3Wrapper { * @returns Block number */ public async getBlockNumberAsync(): Promise { - const blockNumber = await promisify(this._web3.eth.getBlockNumber)(); - return blockNumber; + const blockNumberHex = await this._sendRawPayloadAsync({ + 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 { Web3Wrapper._assertBlockParamOrString(blockParam); + const encodedBlockParam = marshaller.marshalBlockParam(blockParam); + const method = web3Utils.isHexStrict(blockParam) ? 'eth_getBlockByHash' : 'eth_getBlockByNumber'; const shouldIncludeTransactionData = false; - const blockWithoutTransactionData = await promisify(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 { 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(this._web3.eth.getBlock)( - blockParam, - shouldIncludeTransactionData, + const blockWithTransactionDataWithHexValues = await this._sendRawPayloadAsync({ + 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 { - const addresses = await promisify(this._web3.eth.getAccounts)(); + const addresses = await this._sendRawPayloadAsync({ + 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 { 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(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): Promise { - const gas = await promisify(this._web3.eth.estimateGas)(txData); + const txDataHex = marshaller.marshalTxData(txData); + const gasHex = await this._sendRawPayloadAsync({ 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(this._web3.eth.call)(callData, defaultBlock); + const marshalledDefaultBlock = marshaller.marshalBlockParam(defaultBlock); + const callDataHex = marshaller.marshalCallData(callData); + const rawCallResult = await this._sendRawPayloadAsync({ + 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 { - const txHash = await promisify(this._web3.eth.sendTransaction)(txData); + const txDataHex = marshaller.marshalTxData(txData); + const txHash = await this._sendRawPayloadAsync({ method: 'eth_sendTransaction', params: [txDataHex] }); return txHash; } /** @@ -529,10 +593,10 @@ export class Web3Wrapper { */ public async setHeadAsync(blockNumber: number): Promise { assert.isNumber('blockNumber', blockNumber); - await this._sendRawPayloadAsync({ method: 'debug_setHead', params: [this._web3.toHex(blockNumber)] }); + await this._sendRawPayloadAsync({ method: 'debug_setHead', params: [web3Utils.toHex(blockNumber)] }); } private async _sendRawPayloadAsync(payload: Partial): Promise { - 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 diff --git a/packages/web3-wrapper/test/web3_wrapper_test.ts b/packages/web3-wrapper/test/web3_wrapper_test.ts index 8919775c2..b4fd8bb44 100644 --- a/packages/web3-wrapper/test/web3_wrapper_test.ts +++ b/packages/web3-wrapper/test/web3_wrapper_test.ts @@ -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); + }); + }); }); diff --git a/yarn.lock b/yarn.lock index fae380f11..b44f45c0f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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: -- cgit v1.2.3 From 5204e4c08da7d32012047ee6ca4d898b0c45a47d Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 5 Jul 2018 00:36:30 +0200 Subject: Pass 'from' param to estimateGas since it is required --- packages/base-contract/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/base-contract/src/index.ts b/packages/base-contract/src/index.ts index cb9042415..6b970ec27 100644 --- a/packages/base-contract/src/index.ts +++ b/packages/base-contract/src/index.ts @@ -80,7 +80,7 @@ export class BaseContract { // Awaiting https://github.com/Microsoft/TypeScript/pull/13288 to be merged } as any; if (_.isUndefined(txDataWithDefaults.gas) && !_.isUndefined(estimateGasAsync)) { - txDataWithDefaults.gas = await estimateGasAsync(txData); + txDataWithDefaults.gas = await estimateGasAsync(txDataWithDefaults as any); } return txDataWithDefaults; } -- cgit v1.2.3 From 7efa17ef7d855e45cb874adedeee8dfc935fe40e Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 5 Jul 2018 00:40:43 +0200 Subject: Add new param javadoc comment --- packages/web3-wrapper/src/web3_wrapper.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/web3-wrapper/src/web3_wrapper.ts b/packages/web3-wrapper/src/web3_wrapper.ts index e56b99366..b1d5a59d9 100644 --- a/packages/web3-wrapper/src/web3_wrapper.ts +++ b/packages/web3-wrapper/src/web3_wrapper.ts @@ -260,6 +260,7 @@ export class Web3Wrapper { /** * Gets the contract code by address * @param address Address of the contract + * @param defaultBlock Block height at which to make the call. Defaults to `latest` * @return Code of the contract */ public async getContractCodeAsync(address: string, defaultBlock?: BlockParam): Promise { -- cgit v1.2.3 From fd242a9cba7516f08579f0e97923a15740ef4d7a Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 5 Jul 2018 01:38:14 +0200 Subject: Remove web3Utils dep and write necessary utility methods --- packages/web3-wrapper/package.json | 4 +- packages/web3-wrapper/src/globals.d.ts | 8 --- packages/web3-wrapper/src/marshaller.ts | 87 +++++++++++-------------------- packages/web3-wrapper/src/types.ts | 2 +- packages/web3-wrapper/src/utils.ts | 51 ++++++++++++++++++ packages/web3-wrapper/src/web3_wrapper.ts | 20 +++---- 6 files changed, 93 insertions(+), 79 deletions(-) diff --git a/packages/web3-wrapper/package.json b/packages/web3-wrapper/package.json index 2084cce73..2b7c4555e 100644 --- a/packages/web3-wrapper/package.json +++ b/packages/web3-wrapper/package.json @@ -71,9 +71,7 @@ "ethereum-types": "^0.0.2", "ethereumjs-util": "^5.1.1", "ethers": "3.0.22", - "lodash": "^4.17.4", - "web3": "^0.20.0", - "web3-utils": "^1.0.0-beta.34" + "lodash": "^4.17.4" }, "publishConfig": { "access": "public" diff --git a/packages/web3-wrapper/src/globals.d.ts b/packages/web3-wrapper/src/globals.d.ts index 09c9fd819..94e63a32d 100644 --- a/packages/web3-wrapper/src/globals.d.ts +++ b/packages/web3-wrapper/src/globals.d.ts @@ -1,11 +1,3 @@ -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 */ diff --git a/packages/web3-wrapper/src/marshaller.ts b/packages/web3-wrapper/src/marshaller.ts index 06556ce90..d0cf6ff2d 100644 --- a/packages/web3-wrapper/src/marshaller.ts +++ b/packages/web3-wrapper/src/marshaller.ts @@ -13,7 +13,6 @@ import { } from 'ethereum-types'; import ethUtil = require('ethereumjs-util'); import * as _ from 'lodash'; -import web3Utils = require('web3-utils'); import { utils } from './utils'; @@ -33,26 +32,26 @@ export const marshaller = { ): 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), + gasLimit: utils.convertHexToNumber(blockWithHexValues.gasLimit), + gasUsed: utils.convertHexToNumber(blockWithHexValues.gasUsed), + size: utils.convertHexToNumber(blockWithHexValues.size), + timestamp: utils.convertHexToNumber(blockWithHexValues.timestamp), + number: _.isNull(blockWithHexValues.number) ? null : utils.convertHexToNumber(blockWithHexValues.number), + difficulty: utils.convertAmountToBigNumber(blockWithHexValues.difficulty), + totalDifficulty: utils.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), + gasLimit: utils.convertHexToNumber(blockWithHexValues.gasLimit), + gasUsed: utils.convertHexToNumber(blockWithHexValues.gasUsed), + size: utils.convertHexToNumber(blockWithHexValues.size), + timestamp: utils.convertHexToNumber(blockWithHexValues.timestamp), + number: _.isNull(blockWithHexValues.number) ? null : utils.convertHexToNumber(blockWithHexValues.number), + difficulty: utils.convertAmountToBigNumber(blockWithHexValues.difficulty), + totalDifficulty: utils.convertAmountToBigNumber(blockWithHexValues.totalDifficulty), transactions: [] as Transaction[], }; block.transactions = _.map(blockWithHexValues.transactions, (tx: TransactionRPC) => { @@ -64,12 +63,14 @@ export const marshaller = { 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), + blockNumber: !_.isNull(txRpc.blockNumber) ? utils.convertHexToNumber(txRpc.blockNumber) : null, + transactionIndex: !_.isNull(txRpc.transactionIndex) + ? utils.convertHexToNumber(txRpc.transactionIndex) + : null, + nonce: utils.convertHexToNumber(txRpc.nonce), + gas: utils.convertHexToNumber(txRpc.gas), + gasPrice: utils.convertAmountToBigNumber(txRpc.gasPrice), + value: utils.convertAmountToBigNumber(txRpc.value), }; return tx; }, @@ -116,15 +117,15 @@ export const marshaller = { if (_.isUndefined(blockParam)) { return BlockParamLiteral.Latest; } - const encodedBlockParam = _.isNumber(blockParam) ? web3Utils.toHex(blockParam) : blockParam; + const encodedBlockParam = _.isNumber(blockParam) ? utils.numberToHex(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), + logIndex: utils.convertHexToNumberOrNull(rawLog.logIndex), + blockNumber: utils.convertHexToNumberOrNull(rawLog.blockNumber), + transactionIndex: utils.convertHexToNumberOrNull(rawLog.transactionIndex), }; return formattedLog; }, @@ -134,44 +135,16 @@ export const marshaller = { 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), + : utils.encodeAmountAsHexString(callTxDataBase.gasPrice), + gas: _.isUndefined(callTxDataBase.gas) ? undefined : utils.encodeAmountAsHexString(callTxDataBase.gas), value: _.isUndefined(callTxDataBase.value) ? undefined - : this._encodeAmountAsHexString(callTxDataBase.value), + : utils.encodeAmountAsHexString(callTxDataBase.value), nonce: _.isUndefined(callTxDataBase.nonce) ? undefined - : this._encodeAmountAsHexString(callTxDataBase.nonce), + : utils.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; - }, }; diff --git a/packages/web3-wrapper/src/types.ts b/packages/web3-wrapper/src/types.ts index 54a5f3f0e..b7b6bd68a 100644 --- a/packages/web3-wrapper/src/types.ts +++ b/packages/web3-wrapper/src/types.ts @@ -29,7 +29,7 @@ export interface BlockWithTransactionDataRPC extends AbstractBlockRPC { } export interface TransactionRPC { hash: string; - nonce: number; + nonce: string; blockHash: string | null; blockNumber: string | null; transactionIndex: string | null; diff --git a/packages/web3-wrapper/src/utils.ts b/packages/web3-wrapper/src/utils.ts index 376f7e89b..43ab73238 100644 --- a/packages/web3-wrapper/src/utils.ts +++ b/packages/web3-wrapper/src/utils.ts @@ -1,3 +1,4 @@ +import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; export const utils = { @@ -5,4 +6,54 @@ export const utils = { const isBigNumber = _.isObject(value) && (value as any).isBigNumber; return isBigNumber; }, + convertHexToNumber(value: string): number { + console.log('value', value); + const valueBigNumber = new BigNumber(value); + const valueNumber = valueBigNumber.toNumber(); + return valueNumber; + }, + convertHexToNumberOrNull(hex: string | null): number | null { + if (_.isNull(hex)) { + return null; + } + const decimal = this.convertHexToNumber(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 = utils.convertAmountToBigNumber(value); + const hexBase = 16; + const valueHex = valueBigNumber.toString(hexBase); + + return valueBigNumber.lessThan(0) ? '-0x' + valueHex.substr(1) : '0x' + valueHex; + }, + numberToHex(value: number): string { + if (!isFinite(value) && !this.isHexStrict(value)) { + throw new Error('Given input "' + value + '" is not a number.'); + } + + const valueBigNumber = new BigNumber(value); + const hexBase = 16; + const result = valueBigNumber.toString(hexBase); + + return valueBigNumber.lt(0) ? '-0x' + result.substr(1) : '0x' + result; + }, + isHexStrict(hex: string | number): boolean { + return ( + (_.isString(hex) || _.isNumber(hex)) && /^(-)?0x[0-9a-f]*$/i.test(_.isNumber(hex) ? hex.toString() : hex) + ); + }, }; diff --git a/packages/web3-wrapper/src/web3_wrapper.ts b/packages/web3-wrapper/src/web3_wrapper.ts index b1d5a59d9..35ecc5244 100644 --- a/packages/web3-wrapper/src/web3_wrapper.ts +++ b/packages/web3-wrapper/src/web3_wrapper.ts @@ -20,7 +20,6 @@ import { TxData, } from 'ethereum-types'; import * as _ from 'lodash'; -import * as web3Utils from 'web3-utils'; import { marshaller } from './marshaller'; import { @@ -29,6 +28,7 @@ import { TransactionRPC, Web3WrapperErrors, } from './types'; +import { utils } from './utils'; const BASE_TEN = 10; @@ -136,7 +136,7 @@ export class Web3Wrapper { // number - Parity // hex - Geth if (_.isString(status)) { - return web3Utils.toDecimal(status) as 0 | 1; + return utils.convertHexToNumber(status) as 0 | 1; } else if (_.isUndefined(status)) { return null; } else { @@ -315,7 +315,7 @@ export class Web3Wrapper { method: 'eth_blockNumber', params: [], }); - const blockNumber = marshaller.convertHexToNumberOrNull(blockNumberHex); + const blockNumber = utils.convertHexToNumberOrNull(blockNumberHex); return blockNumber as number; } /** @@ -326,7 +326,7 @@ export class Web3Wrapper { public async getBlockAsync(blockParam: string | BlockParam): Promise { Web3Wrapper._assertBlockParamOrString(blockParam); const encodedBlockParam = marshaller.marshalBlockParam(blockParam); - const method = web3Utils.isHexStrict(blockParam) ? 'eth_getBlockByHash' : 'eth_getBlockByNumber'; + const method = utils.isHexStrict(blockParam) ? 'eth_getBlockByHash' : 'eth_getBlockByNumber'; const shouldIncludeTransactionData = false; const blockWithoutTransactionDataWithHexValues = await this._sendRawPayloadAsync< BlockWithoutTransactionDataRPC @@ -348,9 +348,9 @@ export class Web3Wrapper { Web3Wrapper._assertBlockParamOrString(blockParam); let encodedBlockParam = blockParam; if (_.isNumber(blockParam)) { - encodedBlockParam = web3Utils.toHex(blockParam); + encodedBlockParam = utils.numberToHex(blockParam); } - const method = web3Utils.isHexStrict(blockParam) ? 'eth_getBlockByHash' : 'eth_getBlockByNumber'; + const method = utils.isHexStrict(blockParam) ? 'eth_getBlockByHash' : 'eth_getBlockByNumber'; const shouldIncludeTransactionData = true; const blockWithTransactionDataWithHexValues = await this._sendRawPayloadAsync({ method, @@ -432,11 +432,11 @@ export class Web3Wrapper { public async getLogsAsync(filter: FilterObject): Promise { let fromBlock = filter.fromBlock; if (_.isNumber(fromBlock)) { - fromBlock = web3Utils.toHex(fromBlock); + fromBlock = utils.numberToHex(fromBlock); } let toBlock = filter.toBlock; if (_.isNumber(toBlock)) { - toBlock = web3Utils.toHex(toBlock); + toBlock = utils.numberToHex(toBlock); } const serializedFilter = { ...filter, @@ -459,7 +459,7 @@ export class Web3Wrapper { public async estimateGasAsync(txData: Partial): Promise { const txDataHex = marshaller.marshalTxData(txData); const gasHex = await this._sendRawPayloadAsync({ method: 'eth_estimateGas', params: [txDataHex] }); - const gas = web3Utils.toDecimal(gasHex); + const gas = utils.convertHexToNumber(gasHex); return gas; } /** @@ -600,7 +600,7 @@ export class Web3Wrapper { */ public async setHeadAsync(blockNumber: number): Promise { assert.isNumber('blockNumber', blockNumber); - await this._sendRawPayloadAsync({ method: 'debug_setHead', params: [web3Utils.toHex(blockNumber)] }); + await this._sendRawPayloadAsync({ method: 'debug_setHead', params: [utils.numberToHex(blockNumber)] }); } /** * Returns either NodeType.Geth or NodeType.Ganache depending on the type of -- cgit v1.2.3 From 139d289b017e9d3781d84ff998104e1a0df660ad Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 5 Jul 2018 01:39:46 +0200 Subject: remove stray console.log --- packages/web3-wrapper/src/utils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/web3-wrapper/src/utils.ts b/packages/web3-wrapper/src/utils.ts index 43ab73238..ddb177d91 100644 --- a/packages/web3-wrapper/src/utils.ts +++ b/packages/web3-wrapper/src/utils.ts @@ -7,7 +7,6 @@ export const utils = { return isBigNumber; }, convertHexToNumber(value: string): number { - console.log('value', value); const valueBigNumber = new BigNumber(value); const valueNumber = valueBigNumber.toNumber(); return valueNumber; -- cgit v1.2.3 From f5b1fe0e6b47e18e10ca42f734639482c5a03239 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 5 Jul 2018 01:45:56 +0200 Subject: Fix linter issues --- packages/web3-wrapper/src/marshaller.ts | 3 +-- packages/web3-wrapper/src/utils.ts | 4 ++-- packages/web3-wrapper/src/web3_wrapper.ts | 8 +------- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/packages/web3-wrapper/src/marshaller.ts b/packages/web3-wrapper/src/marshaller.ts index d0cf6ff2d..84c351387 100644 --- a/packages/web3-wrapper/src/marshaller.ts +++ b/packages/web3-wrapper/src/marshaller.ts @@ -1,4 +1,4 @@ -import { addressUtils, BigNumber } from '@0xproject/utils'; +import { addressUtils } from '@0xproject/utils'; import { BlockParam, BlockParamLiteral, @@ -17,7 +17,6 @@ import * as _ from 'lodash'; import { utils } from './utils'; import { - AbstractBlockRPC, BlockWithoutTransactionDataRPC, BlockWithTransactionDataRPC, CallDataRPC, diff --git a/packages/web3-wrapper/src/utils.ts b/packages/web3-wrapper/src/utils.ts index ddb177d91..d13eb9404 100644 --- a/packages/web3-wrapper/src/utils.ts +++ b/packages/web3-wrapper/src/utils.ts @@ -3,7 +3,7 @@ import * as _ from 'lodash'; export const utils = { isBigNumber(value: any): boolean { - const isBigNumber = _.isObject(value) && (value as any).isBigNumber; + const isBigNumber = _.isObject(value) && value.isBigNumber; return isBigNumber; }, convertHexToNumber(value: string): number { @@ -41,7 +41,7 @@ export const utils = { }, numberToHex(value: number): string { if (!isFinite(value) && !this.isHexStrict(value)) { - throw new Error('Given input "' + value + '" is not a number.'); + throw new Error(`Given input ${value} is not a number.`); } const valueBigNumber = new BigNumber(value); diff --git a/packages/web3-wrapper/src/web3_wrapper.ts b/packages/web3-wrapper/src/web3_wrapper.ts index 35ecc5244..d1e4c1edf 100644 --- a/packages/web3-wrapper/src/web3_wrapper.ts +++ b/packages/web3-wrapper/src/web3_wrapper.ts @@ -6,7 +6,6 @@ import { BlockWithoutTransactionData, BlockWithTransactionData, CallData, - ContractAbi, FilterObject, JSONRPCRequestPayload, JSONRPCResponsePayload, @@ -22,12 +21,7 @@ import { import * as _ from 'lodash'; import { marshaller } from './marshaller'; -import { - BlockWithoutTransactionDataRPC, - BlockWithTransactionDataRPC, - TransactionRPC, - Web3WrapperErrors, -} from './types'; +import { BlockWithoutTransactionDataRPC, BlockWithTransactionDataRPC, Web3WrapperErrors } from './types'; import { utils } from './utils'; const BASE_TEN = 10; -- cgit v1.2.3 From b0daec838453eddbee50604b9f8ffffccd5a2be1 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 5 Jul 2018 01:56:40 +0200 Subject: Remove bn.js unused dep --- packages/web3-wrapper/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/web3-wrapper/package.json b/packages/web3-wrapper/package.json index 2b7c4555e..1e9d2530d 100644 --- a/packages/web3-wrapper/package.json +++ b/packages/web3-wrapper/package.json @@ -67,7 +67,6 @@ "@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.2", "ethereumjs-util": "^5.1.1", "ethers": "3.0.22", -- cgit v1.2.3 From 3d67f122a501e49a005c4ebdb0a68be0f67fcea8 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 5 Jul 2018 12:03:34 +0200 Subject: Move 'from' check to sendTransaction --- packages/web3-wrapper/src/marshaller.ts | 3 --- packages/web3-wrapper/src/web3_wrapper.ts | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/web3-wrapper/src/marshaller.ts b/packages/web3-wrapper/src/marshaller.ts index 84c351387..7af61752d 100644 --- a/packages/web3-wrapper/src/marshaller.ts +++ b/packages/web3-wrapper/src/marshaller.ts @@ -74,9 +74,6 @@ export const marshaller = { return tx; }, marshalTxData(txData: Partial): Partial { - if (_.isUndefined(txData.from)) { - throw new Error(`txData is missing required "from" address.`); - } const callTxDataBase = { ...txData, }; diff --git a/packages/web3-wrapper/src/web3_wrapper.ts b/packages/web3-wrapper/src/web3_wrapper.ts index d1e4c1edf..2d7b590b5 100644 --- a/packages/web3-wrapper/src/web3_wrapper.ts +++ b/packages/web3-wrapper/src/web3_wrapper.ts @@ -483,6 +483,9 @@ export class Web3Wrapper { * @returns Transaction hash */ public async sendTransactionAsync(txData: TxData): Promise { + if (_.isUndefined(txData.from)) { + throw new Error(`txData is missing required "from" address.`); + } const txDataHex = marshaller.marshalTxData(txData); const txHash = await this._sendRawPayloadAsync({ method: 'eth_sendTransaction', params: [txDataHex] }); return txHash; -- cgit v1.2.3 From 0a32ae6cb553076667513fa61af14d4e8eef5b5b Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 5 Jul 2018 12:04:08 +0200 Subject: Add defaultBlock param to getBalanceInWeiAsync --- packages/web3-wrapper/src/web3_wrapper.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/web3-wrapper/src/web3_wrapper.ts b/packages/web3-wrapper/src/web3_wrapper.ts index 2d7b590b5..5dd1150d7 100644 --- a/packages/web3-wrapper/src/web3_wrapper.ts +++ b/packages/web3-wrapper/src/web3_wrapper.ts @@ -230,11 +230,16 @@ export class Web3Wrapper { * @param owner Account whose balance you wish to check * @returns Balance in wei */ - public async getBalanceInWeiAsync(owner: string): Promise { + public async getBalanceInWeiAsync(owner: string, defaultBlock?: BlockParam): Promise { assert.isETHAddressHex('owner', owner); + if (!_.isUndefined(defaultBlock)) { + Web3Wrapper._assertBlockParam(defaultBlock); + } + const marshalledDefaultBlock = marshaller.marshalBlockParam(defaultBlock); + const encodedOwner = marshaller.marshalAddress(owner); const balanceInWei = await this._sendRawPayloadAsync({ method: 'eth_getBalance', - params: [owner], + params: [encodedOwner, marshalledDefaultBlock], }); // Rewrap in a new BigNumber return new BigNumber(balanceInWei); -- cgit v1.2.3 From 75babed69317ab1da02a10706871ab0bb2293883 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 5 Jul 2018 12:07:17 +0200 Subject: Update assert changelog --- packages/assert/CHANGELOG.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/assert/CHANGELOG.json b/packages/assert/CHANGELOG.json index 883d55f5b..26f3d0d66 100644 --- a/packages/assert/CHANGELOG.json +++ b/packages/assert/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "0.2.13", + "changes": [ + { + "note": "Fix bug in string enum assertion. We erroneously were checking against the enum keys, not values", + "pr": 821 + } + ] + }, { "timestamp": 1529397769, "version": "0.2.12", -- cgit v1.2.3 From 2a7b3aecc3886bb4b699df17a0efef984e633d1f Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 5 Jul 2018 12:33:48 +0200 Subject: Add CallData schema to json-schemas --- packages/json-schemas/CHANGELOG.json | 4 ++++ packages/json-schemas/schemas/call_data_schema.ts | 27 +++++++++++++++++++++++ packages/json-schemas/src/schemas.ts | 2 ++ 3 files changed, 33 insertions(+) create mode 100644 packages/json-schemas/schemas/call_data_schema.ts diff --git a/packages/json-schemas/CHANGELOG.json b/packages/json-schemas/CHANGELOG.json index 2b37e1836..dfefdfc77 100644 --- a/packages/json-schemas/CHANGELOG.json +++ b/packages/json-schemas/CHANGELOG.json @@ -5,6 +5,10 @@ { "note": "Update schemas for V2", "pr": 615 + }, + { + "note": "Added CallData schema", + "pr": 821 } ] }, diff --git a/packages/json-schemas/schemas/call_data_schema.ts b/packages/json-schemas/schemas/call_data_schema.ts new file mode 100644 index 000000000..e8fcc7512 --- /dev/null +++ b/packages/json-schemas/schemas/call_data_schema.ts @@ -0,0 +1,27 @@ +export const callDataSchema = { + id: '/TxData', + properties: { + from: { $ref: '/Address' }, + to: { $ref: '/Address' }, + value: { + oneOf: [{ $ref: '/Number' }, { $ref: '/JsNumber' }], + }, + gas: { + oneOf: [{ $ref: '/Number' }, { $ref: '/JsNumber' }], + }, + gasPrice: { + oneOf: [{ $ref: '/Number' }, { $ref: '/JsNumber' }], + }, + data: { + type: 'string', + pattern: '^0x[0-9a-f]*$', + }, + nonce: { + type: 'number', + minimum: 0, + }, + }, + required: [], + type: 'object', + additionalProperties: false, +}; diff --git a/packages/json-schemas/src/schemas.ts b/packages/json-schemas/src/schemas.ts index 77ea88f5c..4e13aeafb 100644 --- a/packages/json-schemas/src/schemas.ts +++ b/packages/json-schemas/src/schemas.ts @@ -1,5 +1,6 @@ import { addressSchema, hexSchema, numberSchema } from '../schemas/basic_type_schemas'; import { blockParamSchema, blockRangeSchema } from '../schemas/block_range_schema'; +import { callDataSchema } from '../schemas/call_data_schema'; import { ecSignatureSchema } from '../schemas/ec_signature_schema'; import { indexFilterValuesSchema } from '../schemas/index_filter_values_schema'; import { orderCancellationRequestsSchema } from '../schemas/order_cancel_schema'; @@ -31,6 +32,7 @@ import { jsNumber, txDataSchema } from '../schemas/tx_data_schema'; export const schemas = { numberSchema, addressSchema, + callDataSchema, hexSchema, ecSignatureSchema, indexFilterValuesSchema, -- cgit v1.2.3 From 11747c6cf47a2f75d32953c84ccdf66f6f2f70a3 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 5 Jul 2018 12:34:15 +0200 Subject: Make sure from is included in txData --- packages/web3-wrapper/src/marshaller.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/web3-wrapper/src/marshaller.ts b/packages/web3-wrapper/src/marshaller.ts index 7af61752d..e9fd35a11 100644 --- a/packages/web3-wrapper/src/marshaller.ts +++ b/packages/web3-wrapper/src/marshaller.ts @@ -74,6 +74,9 @@ export const marshaller = { return tx; }, marshalTxData(txData: Partial): Partial { + if (_.isUndefined(txData.from)) { + throw new Error(`txData must include valid 'from' value.`); + } const callTxDataBase = { ...txData, }; -- cgit v1.2.3 From 1c9a657693f324fe9de15fc832398aca715e0727 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 5 Jul 2018 12:35:16 +0200 Subject: Add schema assertion checks for callData and txData --- packages/web3-wrapper/src/web3_wrapper.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/web3-wrapper/src/web3_wrapper.ts b/packages/web3-wrapper/src/web3_wrapper.ts index 5dd1150d7..c21d66d0b 100644 --- a/packages/web3-wrapper/src/web3_wrapper.ts +++ b/packages/web3-wrapper/src/web3_wrapper.ts @@ -1,4 +1,5 @@ import { assert } from '@0xproject/assert'; +import { schemas } from '@0xproject/json-schemas'; import { AbiDecoder, addressUtils, BigNumber, intervalUtils, promisify } from '@0xproject/utils'; import { BlockParam, @@ -456,6 +457,7 @@ export class Web3Wrapper { * @returns Estimated gas cost */ public async estimateGasAsync(txData: Partial): Promise { + assert.doesConformToSchema('txData', txData, schemas.txDataSchema, [schemas.addressSchema, schemas.numberSchema, schemas.jsNumber]); const txDataHex = marshaller.marshalTxData(txData); const gasHex = await this._sendRawPayloadAsync({ method: 'eth_estimateGas', params: [txDataHex] }); const gas = utils.convertHexToNumber(gasHex); @@ -468,6 +470,7 @@ export class Web3Wrapper { * @returns The raw call result */ public async callAsync(callData: CallData, defaultBlock?: BlockParam): Promise { + assert.doesConformToSchema('callData', callData, schemas.callDataSchema, [schemas.addressSchema, schemas.numberSchema, schemas.jsNumber]); if (!_.isUndefined(defaultBlock)) { Web3Wrapper._assertBlockParam(defaultBlock); } @@ -488,9 +491,7 @@ export class Web3Wrapper { * @returns Transaction hash */ public async sendTransactionAsync(txData: TxData): Promise { - if (_.isUndefined(txData.from)) { - throw new Error(`txData is missing required "from" address.`); - } + assert.doesConformToSchema('txData', txData, schemas.txDataSchema, [schemas.addressSchema, schemas.numberSchema, schemas.jsNumber]); const txDataHex = marshaller.marshalTxData(txData); const txHash = await this._sendRawPayloadAsync({ method: 'eth_sendTransaction', params: [txDataHex] }); return txHash; -- cgit v1.2.3 From 40d1b0a23cc8d58ce4c693e39038b1b61e3945b9 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 5 Jul 2018 12:35:38 +0200 Subject: Update json-schema version used by web3-wrapper --- packages/web3-wrapper/package.json | 1 + yarn.lock | 12 +----------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/packages/web3-wrapper/package.json b/packages/web3-wrapper/package.json index 1e9d2530d..ccd41fa1d 100644 --- a/packages/web3-wrapper/package.json +++ b/packages/web3-wrapper/package.json @@ -65,6 +65,7 @@ }, "dependencies": { "@0xproject/assert": "^0.2.12", + "@0xproject/json-schemas": "^1.0.0", "@0xproject/typescript-typings": "^0.4.1", "@0xproject/utils": "^0.7.1", "ethereum-types": "^0.0.2", diff --git a/yarn.lock b/yarn.lock index 548859767..079223ca4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12689,7 +12689,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: @@ -12721,16 +12721,6 @@ web3@^0.18.0: xhr2 "*" xmlhttprequest "*" -web3@^0.20.0: - version "0.20.6" - resolved "https://registry.yarnpkg.com/web3/-/web3-0.20.6.tgz#3e97306ae024fb24e10a3d75c884302562215120" - dependencies: - bignumber.js "git+https://github.com/frozeman/bignumber.js-nolookahead.git" - crypto-js "^3.1.4" - utf8 "^2.1.1" - xhr2 "*" - xmlhttprequest "*" - web3@^1.0.0-beta.34: version "1.0.0-beta.34" resolved "https://registry.yarnpkg.com/web3/-/web3-1.0.0-beta.34.tgz#347e561b784098cb5563315f490479a1d91f2ab1" -- cgit v1.2.3 From a1fb438a8c8722d14c7a1e52e844898ff5451e66 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 5 Jul 2018 13:03:33 +0200 Subject: Prettier fixes --- packages/assert/CHANGELOG.json | 5 +++-- packages/web3-wrapper/src/web3_wrapper.ts | 18 +++++++++++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/packages/assert/CHANGELOG.json b/packages/assert/CHANGELOG.json index 26f3d0d66..b44cac4a1 100644 --- a/packages/assert/CHANGELOG.json +++ b/packages/assert/CHANGELOG.json @@ -3,8 +3,9 @@ "version": "0.2.13", "changes": [ { - "note": "Fix bug in string enum assertion. We erroneously were checking against the enum keys, not values", - "pr": 821 + "note": + "Fix bug in string enum assertion. We erroneously were checking against the enum keys, not values", + "pr": 821 } ] }, diff --git a/packages/web3-wrapper/src/web3_wrapper.ts b/packages/web3-wrapper/src/web3_wrapper.ts index c21d66d0b..495523e44 100644 --- a/packages/web3-wrapper/src/web3_wrapper.ts +++ b/packages/web3-wrapper/src/web3_wrapper.ts @@ -457,7 +457,11 @@ export class Web3Wrapper { * @returns Estimated gas cost */ public async estimateGasAsync(txData: Partial): Promise { - assert.doesConformToSchema('txData', txData, schemas.txDataSchema, [schemas.addressSchema, schemas.numberSchema, schemas.jsNumber]); + assert.doesConformToSchema('txData', txData, schemas.txDataSchema, [ + schemas.addressSchema, + schemas.numberSchema, + schemas.jsNumber, + ]); const txDataHex = marshaller.marshalTxData(txData); const gasHex = await this._sendRawPayloadAsync({ method: 'eth_estimateGas', params: [txDataHex] }); const gas = utils.convertHexToNumber(gasHex); @@ -470,7 +474,11 @@ export class Web3Wrapper { * @returns The raw call result */ public async callAsync(callData: CallData, defaultBlock?: BlockParam): Promise { - assert.doesConformToSchema('callData', callData, schemas.callDataSchema, [schemas.addressSchema, schemas.numberSchema, schemas.jsNumber]); + assert.doesConformToSchema('callData', callData, schemas.callDataSchema, [ + schemas.addressSchema, + schemas.numberSchema, + schemas.jsNumber, + ]); if (!_.isUndefined(defaultBlock)) { Web3Wrapper._assertBlockParam(defaultBlock); } @@ -491,7 +499,11 @@ export class Web3Wrapper { * @returns Transaction hash */ public async sendTransactionAsync(txData: TxData): Promise { - assert.doesConformToSchema('txData', txData, schemas.txDataSchema, [schemas.addressSchema, schemas.numberSchema, schemas.jsNumber]); + assert.doesConformToSchema('txData', txData, schemas.txDataSchema, [ + schemas.addressSchema, + schemas.numberSchema, + schemas.jsNumber, + ]); const txDataHex = marshaller.marshalTxData(txData); const txHash = await this._sendRawPayloadAsync({ method: 'eth_sendTransaction', params: [txDataHex] }); return txHash; -- cgit v1.2.3