diff options
Diffstat (limited to 'packages/web3-wrapper')
-rw-r--r-- | packages/web3-wrapper/CHANGELOG.json | 116 | ||||
-rw-r--r-- | packages/web3-wrapper/CHANGELOG.md | 44 | ||||
-rw-r--r-- | packages/web3-wrapper/README.md | 10 | ||||
-rw-r--r-- | packages/web3-wrapper/package.json | 45 | ||||
-rw-r--r-- | packages/web3-wrapper/src/index.ts | 48 | ||||
-rw-r--r-- | packages/web3-wrapper/src/marshaller.ts | 2 | ||||
-rw-r--r-- | packages/web3-wrapper/src/monorepo_scripts/postpublish.ts | 8 | ||||
-rw-r--r-- | packages/web3-wrapper/src/monorepo_scripts/stage_docs.ts | 8 | ||||
-rw-r--r-- | packages/web3-wrapper/src/utils.ts | 6 | ||||
-rw-r--r-- | packages/web3-wrapper/src/web3_wrapper.ts | 142 | ||||
-rw-r--r-- | packages/web3-wrapper/test/web3_wrapper_test.ts | 49 | ||||
-rw-r--r-- | packages/web3-wrapper/tsconfig.json | 3 | ||||
-rw-r--r-- | packages/web3-wrapper/tslint.json | 2 | ||||
-rw-r--r-- | packages/web3-wrapper/typedoc-tsconfig.json | 7 |
14 files changed, 371 insertions, 119 deletions
diff --git a/packages/web3-wrapper/CHANGELOG.json b/packages/web3-wrapper/CHANGELOG.json index 422059cc0..6b554110f 100644 --- a/packages/web3-wrapper/CHANGELOG.json +++ b/packages/web3-wrapper/CHANGELOG.json @@ -1,5 +1,121 @@ [ { + "version": "3.1.1", + "changes": [ + { + "note": + "Fix bug in `getTransactionByHashAsync` which was causing the return value to have the wrong type (raw fields instead of unmarshalled fields).", + "pr": 1177 + } + ] + }, + { + "version": "3.1.0", + "changes": [ + { + "note": "Add `signTypedData` to perform EIP712 `eth_signTypedData`.", + "pr": 1102 + }, + { + "note": + "Web3Wrapper now throws when an RPC request contains an error field in the response. Previously errors could be swallowed and undefined returned.", + "pr": 1102 + } + ], + "timestamp": 1539871071 + }, + { + "version": "3.0.3", + "changes": [ + { + "note": "Dependencies updated" + } + ], + "timestamp": 1538693146 + }, + { + "timestamp": 1538157789, + "version": "3.0.2", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { + "timestamp": 1537907159, + "version": "3.0.1", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { + "version": "3.0.0", + "changes": [ + { + "note": + "Rename `getBlockAsync` to `getBlockIfExistsAsync` and rather then throw if the requested block wasn't found, return undefined.", + "pr": 1082 + }, + { + "note": + "Expose `sendRawPayloadAsync` so one can easily extend `Web3Wrapper` with their own custom JSON RPC calls", + "pr": 1080 + } + ], + "timestamp": 1537875740 + }, + { + "version": "2.0.3", + "changes": [ + { + "note": + "Fixes issue #1076 where Parity now returns a placeholder transactionReceipt before the transaction is mined.", + "pr": 1079 + } + ], + "timestamp": 1537541580 + }, + { + "timestamp": 1536142250, + "version": "2.0.2", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { + "timestamp": 1535377027, + "version": "2.0.1", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { + "version": "2.0.0", + "changes": [ + { + "note": + "Export types: `BlockParam`, `TxData`, `Provider`, `TransactionReceipt`, `Transaction`, `TraceParams`, `TransactionTrace``, BlockWithoutTransactionDat`a, `LogEntry`, `FilterObject`, `CallData`, `TransactionReceiptWithDecodedLogs`, `BlockWithTransactionData``, LogTopi`c, `JSONRPCRequestPayload`, `TransactionReceiptStatus`, `DecodedLogArgs`, `StructLog`, `JSONRPCErrorCallback``, BlockParamLitera`l, `ContractEventArg`, `DecodedLogEntry`, `LogEntryEvent`, `OpCode`, `TxDataPayable`, `JSONRPCResponsePayload``, RawLogEntr`y, `DecodedLogEntryEvent`, `LogWithDecodedArgs`, `AbiDefinition`, `RawLog`, `FunctionAbi`, `EventAbi`, `EventParameter``, MethodAb`i, `ConstructorAbi`, `FallbackAbi`, `DataItem`, `ConstructorStateMutability` and `StateMutability`", + "pr": 924 + }, + { + "note": "Stop exporting types: `CallTxDataBaseRPC` and `AbstractBlockRPC`", + "pr": 924 + }, + { + "note": "Export `AbiDecoder` class", + "pr": 924 + } + ], + "timestamp": 1535133899 + }, + { "version": "1.2.0", "changes": [ { diff --git a/packages/web3-wrapper/CHANGELOG.md b/packages/web3-wrapper/CHANGELOG.md index 0955993f4..90d62953a 100644 --- a/packages/web3-wrapper/CHANGELOG.md +++ b/packages/web3-wrapper/CHANGELOG.md @@ -5,7 +5,47 @@ Edit the package's CHANGELOG.json file only. CHANGELOG -## v1.2.0 - _August 13, 2018_ +## v3.1.0 - _October 18, 2018_ + + * Add `signTypedData` to perform EIP712 `eth_signTypedData`. (#1102) + * Web3Wrapper now throws when an RPC request contains an error field in the response. Previously errors could be swallowed and undefined returned. (#1102) + +## v3.0.3 - _October 4, 2018_ + + * Dependencies updated + +## v3.0.2 - _September 28, 2018_ + + * Dependencies updated + +## v3.0.1 - _September 25, 2018_ + + * Dependencies updated + +## v3.0.0 - _September 25, 2018_ + + * Rename `getBlockAsync` to `getBlockIfExistsAsync` and rather then throw if the requested block wasn't found, return undefined. (#1082) + * Expose `sendRawPayloadAsync` so one can easily extend `Web3Wrapper` with their own custom JSON RPC calls (#1080) + +## v2.0.3 - _September 21, 2018_ + + * Fixes issue #1076 where Parity now returns a placeholder transactionReceipt before the transaction is mined. (#1079) + +## v2.0.2 - _September 5, 2018_ + + * Dependencies updated + +## v2.0.1 - _August 27, 2018_ + + * Dependencies updated + +## v2.0.0 - _August 24, 2018_ + + * Export types: `BlockParam`, `TxData`, `Provider`, `TransactionReceipt`, `Transaction`, `TraceParams`, `TransactionTrace``, BlockWithoutTransactionDat`a, `LogEntry`, `FilterObject`, `CallData`, `TransactionReceiptWithDecodedLogs`, `BlockWithTransactionData``, LogTopi`c, `JSONRPCRequestPayload`, `TransactionReceiptStatus`, `DecodedLogArgs`, `StructLog`, `JSONRPCErrorCallback``, BlockParamLitera`l, `ContractEventArg`, `DecodedLogEntry`, `LogEntryEvent`, `OpCode`, `TxDataPayable`, `JSONRPCResponsePayload``, RawLogEntr`y, `DecodedLogEntryEvent`, `LogWithDecodedArgs`, `AbiDefinition`, `RawLog`, `FunctionAbi`, `EventAbi`, `EventParameter``, MethodAb`i, `ConstructorAbi`, `FallbackAbi`, `DataItem`, `ConstructorStateMutability` and `StateMutability` (#924) + * Stop exporting types: `CallTxDataBaseRPC` and `AbstractBlockRPC` (#924) + * Export `AbiDecoder` class (#924) + +## v1.2.0 - _August 14, 2018_ * Export marshaller to convert between RPC and user-space data formats (#938) * Export RPC types (#938) @@ -45,7 +85,7 @@ CHANGELOG * Dependencies updated -## v0.7.0 - _June 3, 2018_ +## v0.7.0 - _June 4, 2018_ * Add `web3Wrapper.getContractCodeAsync` (#675) * Add `web3Wrapper.getTransactionTraceAsync` (#675) diff --git a/packages/web3-wrapper/README.md b/packages/web3-wrapper/README.md index 06c788101..4402de19c 100644 --- a/packages/web3-wrapper/README.md +++ b/packages/web3-wrapper/README.md @@ -1,4 +1,4 @@ -## @0xproject/web3-wrapper +## @0x/web3-wrapper Web3-wrapper is a JSON-RPC client for Ethereum nodes. It is a type-safe alternative to [Web3.js](https://github.com/ethereum/web3.js/) written in TypeScript. @@ -7,14 +7,14 @@ Web3-wrapper is a JSON-RPC client for Ethereum nodes. It is a type-safe alternat ## Installation ```bash -yarn add @0xproject/web3-wrapper +yarn add @0x/web3-wrapper ``` If your project is in [TypeScript](https://www.typescriptlang.org/), add the following to your `tsconfig.json`: ```json "compilerOptions": { - "typeRoots": ["node_modules/@0xproject/typescript-typings/types", "node_modules/@types"], + "typeRoots": ["node_modules/@0x/typescript-typings/types", "node_modules/@types"], } ``` @@ -43,13 +43,13 @@ yarn install To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory: ```bash -PKG=@0xproject/web3-wrapper yarn build +PKG=@0x/web3-wrapper yarn build ``` Or continuously rebuild on change: ```bash -PKG=@0xproject/web3-wrapper yarn watch +PKG=@0x/web3-wrapper yarn watch ``` ### Clean diff --git a/packages/web3-wrapper/package.json b/packages/web3-wrapper/package.json index d672db7e4..e95245df8 100644 --- a/packages/web3-wrapper/package.json +++ b/packages/web3-wrapper/package.json @@ -1,6 +1,6 @@ { - "name": "@0xproject/web3-wrapper", - "version": "1.2.0", + "name": "@0x/web3-wrapper", + "version": "3.1.0", "engines": { "node": ">=6.12" }, @@ -8,31 +8,21 @@ "main": "lib/src/index.js", "types": "lib/src/index.d.ts", "scripts": { - "watch_without_deps": "tsc -w", - "build": "tsc && copyfiles -u 3 './lib/src/monorepo_scripts/**/*' ./scripts", - "clean": "shx rm -rf lib scripts", - "lint": "tslint --project .", + "build": "tsc -b", + "build:ci": "yarn build", + "clean": "shx rm -rf lib generated_docs", + "lint": "tslint --format stylish --project .", "test": "yarn run_mocha", "rebuild_and_test": "run-s clean build test", "test:circleci": "yarn test:coverage", "run_mocha": "mocha --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js --bail --exit", "test:coverage": "nyc npm run test --all && yarn coverage:report:lcov", "coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info", - "manual:postpublish": "yarn build; node ./scripts/postpublish.js", - "docs:stage": "node scripts/stage_docs.js", - "docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --json $JSON_FILE_PATH $PROJECT_FILES", - "upload_docs_json": "aws s3 cp generated_docs/index.json $S3_URL --profile 0xproject --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers --content-type application/json" + "docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES" }, "config": { "postpublish": { - "docPublishConfigs": { - "extraFileIncludes": [ - "../types/src/index.ts", - "../ethereum-types/src/index.ts" - ], - "s3BucketPath": "s3://doc-jsons/web3-wrapper/", - "s3StagingBucketPath": "s3://staging-doc-jsons/web3-wrapper/" - } + "assets": [] } }, "license": "Apache-2.0", @@ -45,13 +35,12 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/web3-wrapper/README.md", "devDependencies": { - "@0xproject/monorepo-scripts": "^1.0.5", - "@0xproject/tslint-config": "^1.0.5", + "@0x/tslint-config": "^1.0.9", + "@types/ganache-core": "^2.1.0", "@types/lodash": "4.14.104", "chai": "^4.0.1", "chai-as-promised": "^7.1.0", "chai-bignumber": "^2.0.1", - "copyfiles": "^2.0.0", "dirty-chai": "^2.0.1", "ganache-core": "0xProject/ganache-core#monorepo-dep", "make-promises-safe": "^1.1.0", @@ -60,17 +49,17 @@ "nyc": "^11.0.1", "shx": "^0.2.2", "tslint": "5.11.0", - "typedoc": "0xProject/typedoc", + "typedoc": "0.13.0", "typescript": "3.0.1" }, "dependencies": { - "@0xproject/assert": "^1.0.5", - "@0xproject/json-schemas": "^1.0.1-rc.4", - "@0xproject/typescript-typings": "^1.0.4", - "@0xproject/utils": "^1.0.5", - "ethereum-types": "^1.0.4", + "@0x/assert": "^1.0.14", + "@0x/json-schemas": "^2.0.0", + "@0x/typescript-typings": "^3.0.3", + "@0x/utils": "^2.0.3", + "ethereum-types": "^1.1.1", "ethereumjs-util": "^5.1.1", - "ethers": "3.0.22", + "ethers": "~4.0.4", "lodash": "^4.17.5" }, "publishConfig": { diff --git a/packages/web3-wrapper/src/index.ts b/packages/web3-wrapper/src/index.ts index 5d3d135e4..679563a2b 100644 --- a/packages/web3-wrapper/src/index.ts +++ b/packages/web3-wrapper/src/index.ts @@ -1,11 +1,55 @@ export { Web3Wrapper } from './web3_wrapper'; export { marshaller } from './marshaller'; + +export { AbiDecoder } from '@0x/utils'; + +export { + BlockParam, + TxData, + Provider, + TransactionReceipt, + Transaction, + TraceParams, + TransactionTrace, + BlockWithoutTransactionData, + LogEntry, + FilterObject, + CallData, + TransactionReceiptWithDecodedLogs, + BlockWithTransactionData, + LogTopic, + JSONRPCRequestPayload, + TransactionReceiptStatus, + DecodedLogArgs, + StructLog, + JSONRPCErrorCallback, + BlockParamLiteral, + ContractEventArg, + DecodedLogEntry, + LogEntryEvent, + OpCode, + TxDataPayable, + JSONRPCResponsePayload, + JSONRPCResponseError, + RawLogEntry, + DecodedLogEntryEvent, + LogWithDecodedArgs, + AbiDefinition, + RawLog, + FunctionAbi, + EventAbi, + EventParameter, + MethodAbi, + ConstructorAbi, + FallbackAbi, + DataItem, + ConstructorStateMutability, + StateMutability, +} from 'ethereum-types'; export { Web3WrapperErrors, NodeType, CallDataRPC, - CallTxDataBaseRPC, - AbstractBlockRPC, BlockWithoutTransactionDataRPC, BlockWithTransactionDataRPC, TransactionRPC, diff --git a/packages/web3-wrapper/src/marshaller.ts b/packages/web3-wrapper/src/marshaller.ts index 572a322d6..299c6a64c 100644 --- a/packages/web3-wrapper/src/marshaller.ts +++ b/packages/web3-wrapper/src/marshaller.ts @@ -1,4 +1,4 @@ -import { addressUtils } from '@0xproject/utils'; +import { addressUtils } from '@0x/utils'; import { BlockParam, BlockParamLiteral, diff --git a/packages/web3-wrapper/src/monorepo_scripts/postpublish.ts b/packages/web3-wrapper/src/monorepo_scripts/postpublish.ts deleted file mode 100644 index dcb99d0f7..000000000 --- a/packages/web3-wrapper/src/monorepo_scripts/postpublish.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { postpublishUtils } from '@0xproject/monorepo-scripts'; - -import * as packageJSON from '../package.json'; -import * as tsConfigJSON from '../tsconfig.json'; - -const cwd = `${__dirname}/..`; -// tslint:disable-next-line:no-floating-promises -postpublishUtils.runAsync(packageJSON, tsConfigJSON, cwd); diff --git a/packages/web3-wrapper/src/monorepo_scripts/stage_docs.ts b/packages/web3-wrapper/src/monorepo_scripts/stage_docs.ts deleted file mode 100644 index e732ac8eb..000000000 --- a/packages/web3-wrapper/src/monorepo_scripts/stage_docs.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { postpublishUtils } from '@0xproject/monorepo-scripts'; - -import * as packageJSON from '../package.json'; -import * as tsConfigJSON from '../tsconfig.json'; - -const cwd = `${__dirname}/..`; -// tslint:disable-next-line:no-floating-promises -postpublishUtils.publishDocsToStagingAsync(packageJSON, tsConfigJSON, cwd); diff --git a/packages/web3-wrapper/src/utils.ts b/packages/web3-wrapper/src/utils.ts index 01605dc9a..c68587632 100644 --- a/packages/web3-wrapper/src/utils.ts +++ b/packages/web3-wrapper/src/utils.ts @@ -1,4 +1,4 @@ -import { BigNumber } from '@0xproject/utils'; +import { BigNumber } from '@0x/utils'; import * as _ from 'lodash'; export const utils = { @@ -37,7 +37,7 @@ export const utils = { const hexBase = 16; const valueHex = valueBigNumber.toString(hexBase); - return valueBigNumber.lessThan(0) ? '-0x' + valueHex.substr(1) : '0x' + valueHex; + return valueBigNumber.lessThan(0) ? `-0x${valueHex.substr(1)}` : `0x${valueHex}`; }, numberToHex(value: number): string { if (!isFinite(value) && !utils.isHexStrict(value)) { @@ -48,7 +48,7 @@ export const utils = { const hexBase = 16; const result = valueBigNumber.toString(hexBase); - return valueBigNumber.lt(0) ? '-0x' + result.substr(1) : '0x' + result; + return valueBigNumber.lt(0) ? `-0x${result.substr(1)}` : `0x${result}`; }, isHexStrict(hex: string | number): boolean { return ( diff --git a/packages/web3-wrapper/src/web3_wrapper.ts b/packages/web3-wrapper/src/web3_wrapper.ts index dd35e2094..56877fef3 100644 --- a/packages/web3-wrapper/src/web3_wrapper.ts +++ b/packages/web3-wrapper/src/web3_wrapper.ts @@ -1,6 +1,6 @@ -import { assert } from '@0xproject/assert'; -import { schemas } from '@0xproject/json-schemas'; -import { AbiDecoder, addressUtils, BigNumber, intervalUtils, promisify } from '@0xproject/utils'; +import { assert } from '@0x/assert'; +import { schemas } from '@0x/json-schemas'; +import { AbiDecoder, addressUtils, BigNumber, intervalUtils, promisify } from '@0x/utils'; import { BlockParam, BlockParamLiteral, @@ -23,7 +23,13 @@ import { import * as _ from 'lodash'; import { marshaller } from './marshaller'; -import { BlockWithoutTransactionDataRPC, BlockWithTransactionDataRPC, NodeType, Web3WrapperErrors } from './types'; +import { + BlockWithoutTransactionDataRPC, + BlockWithTransactionDataRPC, + NodeType, + TransactionRPC, + Web3WrapperErrors, +} from './types'; import { utils } from './utils'; const BASE_TEN = 10; @@ -145,7 +151,7 @@ export class Web3Wrapper { 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` - // We re-assign the send method so that Web3@1.0 providers work with @0xproject/web3-wrapper + // We re-assign the send method so that Web3@1.0 providers work with @0x/web3-wrapper (provider as any).sendAsync = (provider as any).send; } this.abiDecoder = new AbiDecoder([]); @@ -193,7 +199,7 @@ export class Web3Wrapper { * @returns Ethereum node's version string */ public async getNodeVersionAsync(): Promise<string> { - const nodeVersion = await this._sendRawPayloadAsync<string>({ method: 'web3_clientVersion' }); + const nodeVersion = await this.sendRawPayloadAsync<string>({ method: 'web3_clientVersion' }); return nodeVersion; } /** @@ -201,7 +207,7 @@ export class Web3Wrapper { * @returns The network id */ public async getNetworkIdAsync(): Promise<number> { - const networkIdStr = await this._sendRawPayloadAsync<string>({ method: 'net_version' }); + const networkIdStr = await this.sendRawPayloadAsync<string>({ method: 'net_version' }); const networkId = _.parseInt(networkIdStr); return networkId; } @@ -212,7 +218,7 @@ export class Web3Wrapper { */ public async getTransactionReceiptAsync(txHash: string): Promise<TransactionReceipt> { assert.isHexString('txHash', txHash); - const transactionReceipt = await this._sendRawPayloadAsync<TransactionReceipt>({ + const transactionReceipt = await this.sendRawPayloadAsync<TransactionReceipt>({ method: 'eth_getTransactionReceipt', params: [txHash], }); @@ -228,15 +234,17 @@ export class Web3Wrapper { */ public async getTransactionByHashAsync(txHash: string): Promise<Transaction> { assert.isHexString('txHash', txHash); - const transaction = await this._sendRawPayloadAsync<Transaction>({ + const transactionRpc = await this.sendRawPayloadAsync<TransactionRPC>({ method: 'eth_getTransactionByHash', params: [txHash], }); + const transaction = marshaller.unmarshalTransaction(transactionRpc); return transaction; } /** * Retrieves an accounts Ether balance in wei * @param owner Account whose balance you wish to check + * @param defaultBlock The block depth at which to fetch the balance (default=latest) * @returns Balance in wei */ public async getBalanceInWeiAsync(owner: string, defaultBlock?: BlockParam): Promise<BigNumber> { @@ -246,7 +254,7 @@ export class Web3Wrapper { } const marshalledDefaultBlock = marshaller.marshalBlockParam(defaultBlock); const encodedOwner = marshaller.marshalAddress(owner); - const balanceInWei = await this._sendRawPayloadAsync<string>({ + const balanceInWei = await this.sendRawPayloadAsync<string>({ method: 'eth_getBalance', params: [encodedOwner, marshalledDefaultBlock], }); @@ -278,7 +286,7 @@ export class Web3Wrapper { } const marshalledDefaultBlock = marshaller.marshalBlockParam(defaultBlock); const encodedAddress = marshaller.marshalAddress(address); - const code = await this._sendRawPayloadAsync<string>({ + const code = await this.sendRawPayloadAsync<string>({ method: 'eth_getCode', params: [encodedAddress, marshalledDefaultBlock], }); @@ -292,7 +300,7 @@ export class Web3Wrapper { */ 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', params: [txHash, traceParams], }); @@ -307,18 +315,33 @@ export class Web3Wrapper { 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 this._sendRawPayloadAsync<string>({ + const signData = await this.sendRawPayloadAsync<string>({ method: 'eth_sign', params: [address, message], }); return signData; } /** + * Sign an EIP712 typed data message with a specific address's private key (`eth_signTypedData`) + * @param address Address of signer + * @param typedData Typed data message to sign + * @returns Signature string (as RSV) + */ + public async signTypedDataAsync(address: string, typedData: any): Promise<string> { + assert.isETHAddressHex('address', address); + assert.doesConformToSchema('typedData', typedData, schemas.eip712TypedDataSchema); + const signData = await this.sendRawPayloadAsync<string>({ + method: 'eth_signTypedData', + params: [address, typedData], + }); + return signData; + } + /** * Fetches the latest block number * @returns Block number */ public async getBlockNumberAsync(): Promise<number> { - const blockNumberHex = await this._sendRawPayloadAsync<string>({ + const blockNumberHex = await this.sendRawPayloadAsync<string>({ method: 'eth_blockNumber', params: [], }); @@ -328,23 +351,29 @@ export class Web3Wrapper { /** * Fetch a specific Ethereum block without transaction data * @param blockParam The block you wish to fetch (blockHash, blockNumber or blockLiteral) - * @returns The requested block without transaction data + * @returns The requested block without transaction data, or undefined if block was not found + * (e.g the node isn't fully synced, there was a block re-org and the requested block was uncles, etc...) */ - public async getBlockAsync(blockParam: string | BlockParam): Promise<BlockWithoutTransactionData> { + public async getBlockIfExistsAsync( + blockParam: string | BlockParam, + ): Promise<BlockWithoutTransactionData | undefined> { Web3Wrapper._assertBlockParamOrString(blockParam); const encodedBlockParam = marshaller.marshalBlockParam(blockParam); const method = utils.isHexStrict(blockParam) ? 'eth_getBlockByHash' : 'eth_getBlockByNumber'; const shouldIncludeTransactionData = false; - const blockWithoutTransactionDataWithHexValues = await this._sendRawPayloadAsync< + const blockWithoutTransactionDataWithHexValuesOrNull = await this.sendRawPayloadAsync< BlockWithoutTransactionDataRPC >({ method, params: [encodedBlockParam, shouldIncludeTransactionData], }); - const blockWithoutTransactionData = marshaller.unmarshalIntoBlockWithoutTransactionData( - blockWithoutTransactionDataWithHexValues, - ); - return blockWithoutTransactionData; + let blockWithoutTransactionDataIfExists; + if (!_.isNull(blockWithoutTransactionDataWithHexValuesOrNull)) { + blockWithoutTransactionDataIfExists = marshaller.unmarshalIntoBlockWithoutTransactionData( + blockWithoutTransactionDataWithHexValuesOrNull, + ); + } + return blockWithoutTransactionDataIfExists; } /** * Fetch a specific Ethereum block with transaction data @@ -359,7 +388,7 @@ export class Web3Wrapper { } const method = utils.isHexStrict(blockParam) ? 'eth_getBlockByHash' : 'eth_getBlockByNumber'; const shouldIncludeTransactionData = true; - const blockWithTransactionDataWithHexValues = await this._sendRawPayloadAsync<BlockWithTransactionDataRPC>({ + const blockWithTransactionDataWithHexValues = await this.sendRawPayloadAsync<BlockWithTransactionDataRPC>({ method, params: [encodedBlockParam, shouldIncludeTransactionData], }); @@ -375,15 +404,18 @@ export class Web3Wrapper { */ public async getBlockTimestampAsync(blockParam: string | BlockParam): Promise<number> { Web3Wrapper._assertBlockParamOrString(blockParam); - const { timestamp } = await this.getBlockAsync(blockParam); - return timestamp; + const blockIfExists = await this.getBlockIfExistsAsync(blockParam); + if (_.isUndefined(blockIfExists)) { + throw new Error(`Failed to fetch block with blockParam: ${JSON.stringify(blockParam)}`); + } + return blockIfExists.timestamp; } /** * Retrieve the user addresses available through the backing provider * @returns Available user addresses */ public async getAvailableAddressesAsync(): Promise<string[]> { - const addresses = await this._sendRawPayloadAsync<string>({ + const addresses = await this.sendRawPayloadAsync<string>({ method: 'eth_accounts', params: [], }); @@ -395,7 +427,7 @@ export class Web3Wrapper { * @returns The snapshot id. This can be used to revert to this snapshot */ public async takeSnapshotAsync(): Promise<number> { - const snapshotId = Number(await this._sendRawPayloadAsync<string>({ method: 'evm_snapshot', params: [] })); + const snapshotId = Number(await this.sendRawPayloadAsync<string>({ method: 'evm_snapshot', params: [] })); return snapshotId; } /** @@ -405,14 +437,14 @@ export class Web3Wrapper { */ 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; } /** * Mine a block on a TestRPC/Ganache local node */ public async mineBlockAsync(): Promise<void> { - await this._sendRawPayloadAsync<string>({ method: 'evm_mine', params: [] }); + await this.sendRawPayloadAsync<string>({ method: 'evm_mine', params: [] }); } /** * Increase the next blocks timestamp on TestRPC/Ganache or Geth local node. @@ -424,9 +456,9 @@ export class Web3Wrapper { // Detect Geth vs. Ganache and use appropriate endpoint. const version = await this.getNodeVersionAsync(); if (_.includes(version, uniqueVersionIds.geth)) { - return this._sendRawPayloadAsync<number>({ method: 'debug_increaseTime', params: [timeDelta] }); + return this.sendRawPayloadAsync<number>({ method: 'debug_increaseTime', params: [timeDelta] }); } else if (_.includes(version, uniqueVersionIds.ganache)) { - return this._sendRawPayloadAsync<number>({ method: 'evm_increaseTime', params: [timeDelta] }); + return this.sendRawPayloadAsync<number>({ method: 'evm_increaseTime', params: [timeDelta] }); } else { throw new Error(`Unknown client version: ${version}`); } @@ -437,6 +469,12 @@ export class Web3Wrapper { * @returns The corresponding log entries */ public async getLogsAsync(filter: FilterObject): Promise<LogEntry[]> { + if (!_.isUndefined(filter.blockHash) && (!_.isUndefined(filter.fromBlock) || !_.isUndefined(filter.toBlock))) { + throw new Error( + `Cannot specify 'blockHash' as well as 'fromBlock'/'toBlock' in the filter supplied to 'getLogsAsync'`, + ); + } + let fromBlock = filter.fromBlock; if (_.isNumber(fromBlock)) { fromBlock = utils.numberToHex(fromBlock); @@ -454,7 +492,7 @@ export class Web3Wrapper { method: 'eth_getLogs', params: [serializedFilter], }; - const rawLogs = await this._sendRawPayloadAsync<RawLogEntry[]>(payload); + const rawLogs = await this.sendRawPayloadAsync<RawLogEntry[]>(payload); const formattedLogs = _.map(rawLogs, marshaller.unmarshalLog.bind(marshaller)); return formattedLogs; } @@ -470,7 +508,7 @@ export class Web3Wrapper { schemas.jsNumber, ]); const txDataHex = marshaller.marshalTxData(txData); - const gasHex = await this._sendRawPayloadAsync<string>({ method: 'eth_estimateGas', params: [txDataHex] }); + const gasHex = await this.sendRawPayloadAsync<string>({ method: 'eth_estimateGas', params: [txDataHex] }); const gas = utils.convertHexToNumber(gasHex); return gas; } @@ -491,7 +529,7 @@ export class Web3Wrapper { } const marshalledDefaultBlock = marshaller.marshalBlockParam(defaultBlock); const callDataHex = marshaller.marshalCallData(callData); - const rawCallResult = await this._sendRawPayloadAsync<string>({ + const rawCallResult = await this.sendRawPayloadAsync<string>({ method: 'eth_call', params: [callDataHex, marshalledDefaultBlock], }); @@ -512,7 +550,7 @@ export class Web3Wrapper { schemas.jsNumber, ]); const txDataHex = marshaller.marshalTxData(txData); - const txHash = await this._sendRawPayloadAsync<string>({ method: 'eth_sendTransaction', params: [txDataHex] }); + const txHash = await this.sendRawPayloadAsync<string>({ method: 'eth_sendTransaction', params: [txDataHex] }); return txHash; } /** @@ -538,7 +576,7 @@ export class Web3Wrapper { } // Immediately check if the transaction has already been mined. let transactionReceipt = await this.getTransactionReceiptAsync(txHash); - if (!_.isNull(transactionReceipt)) { + if (!_.isNull(transactionReceipt) && !_.isNull(transactionReceipt.blockNumber)) { const logsWithDecodedArgs = _.map( transactionReceipt.logs, this.abiDecoder.tryToDecodeLogOrNoop.bind(this.abiDecoder), @@ -622,7 +660,27 @@ export class Web3Wrapper { */ public async setHeadAsync(blockNumber: number): Promise<void> { assert.isNumber('blockNumber', blockNumber); - await this._sendRawPayloadAsync<void>({ method: 'debug_setHead', params: [utils.numberToHex(blockNumber)] }); + await this.sendRawPayloadAsync<void>({ method: 'debug_setHead', params: [utils.numberToHex(blockNumber)] }); + } + /** + * Sends a raw Ethereum JSON RPC payload and returns the response's `result` key + * @param payload A partial JSON RPC payload. No need to include version, id, params (if none needed) + * @return The contents nested under the result key of the response body + */ + public async sendRawPayloadAsync<A>(payload: Partial<JSONRPCRequestPayload>): Promise<A> { + const sendAsync = this._provider.sendAsync.bind(this._provider); + const payloadWithDefaults = { + id: this._jsonRpcRequestId++, + params: [], + jsonrpc: '2.0', + ...payload, + }; + const response = await promisify<JSONRPCResponsePayload>(sendAsync)(payloadWithDefaults); + if (response.error) { + throw new Error(response.error.message); + } + const result = response.result; + return result; } /** * Returns either NodeType.Geth or NodeType.Ganache depending on the type of @@ -638,16 +696,4 @@ export class Web3Wrapper { throw new Error(`Unknown client version: ${version}`); } } - private async _sendRawPayloadAsync<A>(payload: Partial<JSONRPCRequestPayload>): Promise<A> { - const sendAsync = this._provider.sendAsync.bind(this._provider); - const payloadWithDefaults = { - id: this._jsonRpcRequestId++, - params: [], - jsonrpc: '2.0', - ...payload, - }; - const response = await promisify<JSONRPCResponsePayload>(sendAsync)(payloadWithDefaults); - const result = response.result; - return result; - } } // 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 b4fd8bb44..164253777 100644 --- a/packages/web3-wrapper/test/web3_wrapper_test.ts +++ b/packages/web3-wrapper/test/web3_wrapper_test.ts @@ -1,5 +1,5 @@ import * as chai from 'chai'; -import { BlockParamLiteral } from 'ethereum-types'; +import { BlockParamLiteral, JSONRPCErrorCallback, JSONRPCRequestPayload } from 'ethereum-types'; import * as Ganache from 'ganache-core'; import * as _ from 'lodash'; import 'mocha'; @@ -78,6 +78,19 @@ describe('Web3Wrapper tests', () => { const signatureLength = 132; expect(signature.length).to.be.equal(signatureLength); }); + it('should throw if the provider returns an error', async () => { + const message = '0xdeadbeef'; + const signer = addresses[1]; + const fakeProvider = { + async sendAsync(payload: JSONRPCRequestPayload, callback: JSONRPCErrorCallback): Promise<void> { + callback(new Error('User denied message signature')); + }, + }; + const errorWeb3Wrapper = new Web3Wrapper(fakeProvider); + expect(errorWeb3Wrapper.signMessageAsync(signer, message)).to.be.rejectedWith( + 'User denied message signature', + ); + }); }); describe('#getBlockNumberAsync', () => { it('get block number', async () => { @@ -85,28 +98,40 @@ describe('Web3Wrapper tests', () => { expect(typeof blockNumber).to.be.equal('number'); }); }); - describe('#getBlockAsync', () => { + describe('#getBlockIfExistsAsync', () => { 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); + const blockIfExists = await web3Wrapper.getBlockIfExistsAsync(blockParamLiteral); + if (_.isUndefined(blockIfExists)) { + throw new Error('Expected block to exist'); + } + expect(blockIfExists.number).to.be.equal(0); + expect(utils.isBigNumber(blockIfExists.difficulty)).to.equal(true); + expect(_.isNumber(blockIfExists.gasLimit)).to.equal(true); }); 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); + const blockIfExists = await web3Wrapper.getBlockIfExistsAsync(blockParamLiteral); + if (_.isUndefined(blockIfExists)) { + throw new Error('Expected block to exist'); + } + expect(blockIfExists.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); + const blockIfExists = await web3Wrapper.getBlockIfExistsAsync(blockParamLiteral); + if (_.isUndefined(blockIfExists)) { + throw new Error('Expected block to exist'); + } + const sameBlockIfExists = await web3Wrapper.getBlockIfExistsAsync(blockIfExists.hash as string); + if (_.isUndefined(sameBlockIfExists)) { + throw new Error('Expected block to exist'); + } + expect(sameBlockIfExists.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(); + expect(web3Wrapper.getBlockIfExistsAsync(invalidBlockParam)).to.eventually.to.be.rejected(); }); }); describe('#getBlockWithTransactionDataAsync', () => { diff --git a/packages/web3-wrapper/tsconfig.json b/packages/web3-wrapper/tsconfig.json index 8b4cd47a2..718e623c7 100644 --- a/packages/web3-wrapper/tsconfig.json +++ b/packages/web3-wrapper/tsconfig.json @@ -1,7 +1,8 @@ { "extends": "../../tsconfig", "compilerOptions": { - "outDir": "lib" + "outDir": "lib", + "rootDir": "." }, "include": ["src/**/*", "test/**/*"] } diff --git a/packages/web3-wrapper/tslint.json b/packages/web3-wrapper/tslint.json index ffaefe83a..dd9053357 100644 --- a/packages/web3-wrapper/tslint.json +++ b/packages/web3-wrapper/tslint.json @@ -1,3 +1,3 @@ { - "extends": ["@0xproject/tslint-config"] + "extends": ["@0x/tslint-config"] } diff --git a/packages/web3-wrapper/typedoc-tsconfig.json b/packages/web3-wrapper/typedoc-tsconfig.json new file mode 100644 index 000000000..b9c6b36f3 --- /dev/null +++ b/packages/web3-wrapper/typedoc-tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../typedoc-tsconfig", + "compilerOptions": { + "outDir": "lib" + }, + "include": ["src/**/*", "test/**/*"] +} |