From f1b267cc9fe7f6e5566dc2535b064b92aef92df1 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Wed, 6 Dec 2017 20:55:09 +0300 Subject: Refactor web3Wrapper to a separate package --- packages/web3-wrapper/README.md | 10 ++ packages/web3-wrapper/package.json | 37 ++++++++ packages/web3-wrapper/src/index.ts | 181 ++++++++++++++++++++++++++++++++++++ packages/web3-wrapper/tsconfig.json | 16 ++++ packages/web3-wrapper/tslint.json | 5 + 5 files changed, 249 insertions(+) create mode 100644 packages/web3-wrapper/README.md create mode 100644 packages/web3-wrapper/package.json create mode 100644 packages/web3-wrapper/src/index.ts create mode 100644 packages/web3-wrapper/tsconfig.json create mode 100644 packages/web3-wrapper/tslint.json (limited to 'packages/web3-wrapper') diff --git a/packages/web3-wrapper/README.md b/packages/web3-wrapper/README.md new file mode 100644 index 000000000..0df8c6333 --- /dev/null +++ b/packages/web3-wrapper/README.md @@ -0,0 +1,10 @@ +Web3 wrapper +------ + +Wrapped version of web3 with nicer interface to be used across 0x projects and packages + +## Install + +```bash +yarn add @0xproject/web3-wrapper +``` diff --git a/packages/web3-wrapper/package.json b/packages/web3-wrapper/package.json new file mode 100644 index 000000000..dd1cd54fb --- /dev/null +++ b/packages/web3-wrapper/package.json @@ -0,0 +1,37 @@ +{ + "name": "@0xproject/web3-wrapper", + "version": "0.0.1", + "description": "Wraps around web3 and gives a nicer interface", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "scripts": { + "build": "tsc", + "clean": "shx rm -rf lib", + "lint": "tslint --project . 'src/**/*.ts'" + }, + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "https://github.com/0xProject/0x.js.git" + }, + "bugs": { + "url": "https://github.com/0xProject/0x.js/issues" + }, + "homepage": "https://github.com/0xProject/0x.js/packages/web3-wrapper/README.md", + "devDependencies": { + "@0xproject/tslint-config": "^0.2.0", + "@types/lodash": "^4.14.86", + "npm-run-all": "^4.1.2", + "shx": "^0.2.2", + "tslint": "5.8.0", + "typescript": "~2.6.1", + "web3-typescript-typings": "^0.7.2" + }, + "dependencies": { + "@0xproject/utils": "^0.0.1", + "@0xproject/types": "^0.0.1", + "bignumber.js": "~4.1.0", + "lodash": "^4.17.4", + "web3": "^0.20.0" + } +} diff --git a/packages/web3-wrapper/src/index.ts b/packages/web3-wrapper/src/index.ts new file mode 100644 index 000000000..9b37e5a0d --- /dev/null +++ b/packages/web3-wrapper/src/index.ts @@ -0,0 +1,181 @@ +import {TransactionReceipt, TxData} from '@0xproject/types'; +import {promisify} from '@0xproject/utils'; +import BigNumber from 'bignumber.js'; +import * as _ from 'lodash'; +import * as Web3 from 'web3'; + +interface RawLogEntry { + logIndex: string|null; + transactionIndex: string|null; + transactionHash: string; + blockHash: string|null; + blockNumber: string|null; + address: string; + data: string; + topics: string[]; +} + +export class Web3Wrapper { + private web3: Web3; + private networkId: number; + private defaults: Partial; + private jsonRpcRequestId: number; + constructor(provider: Web3.Provider, networkId: number, defaults?: Partial) { + 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 0x.js + (provider as any).sendAsync = (provider as any).send; + } + this.web3 = new Web3(); + this.networkId = networkId; + this.web3.setProvider(provider); + this.defaults = defaults || {}; + this.jsonRpcRequestId = 0; + } + public getContractDefaults(): Partial { + return this.defaults; + } + public setProvider(provider: Web3.Provider, networkId: number) { + this.networkId = networkId; + this.web3.setProvider(provider); + } + public isAddress(address: string): boolean { + return this.web3.isAddress(address); + } + public async isSenderAddressAvailableAsync(senderAddress: string): Promise { + const addresses = await this.getAvailableAddressesAsync(); + return _.includes(addresses, senderAddress); + } + public async getNodeVersionAsync(): Promise { + const nodeVersion = await promisify(this.web3.version.getNode)(); + return nodeVersion; + } + public async getTransactionReceiptAsync(txHash: string): Promise { + const transactionReceipt = await promisify(this.web3.eth.getTransactionReceipt)(txHash); + if (!_.isNull(transactionReceipt)) { + transactionReceipt.status = this.normalizeTxReceiptStatus(transactionReceipt.status); + } + return transactionReceipt; + } + public getCurrentProvider(): Web3.Provider { + return this.web3.currentProvider; + } + public getNetworkId(): number { + return this.networkId; + } + public toWei(ethAmount: BigNumber): BigNumber { + const balanceWei = this.web3.toWei(ethAmount, 'ether'); + return balanceWei; + } + public async getBalanceInWeiAsync(owner: string): Promise { + let balanceInWei = await promisify(this.web3.eth.getBalance)(owner); + // Rewrap in a new BigNumber + balanceInWei = new BigNumber(balanceInWei); + return balanceInWei; + } + public async getBalanceInEthAsync(owner: string): Promise { + const balanceInWei = await this.getBalanceInWeiAsync(owner); + const balanceEthOldBigNumber = this.web3.fromWei(balanceInWei, 'ether'); + const balanceEth = new BigNumber(balanceEthOldBigNumber); + return balanceEth; + } + public async doesContractExistAtAddressAsync(address: string): Promise { + const code = await promisify(this.web3.eth.getCode)(address); + // Regex matches 0x0, 0x00, 0x in order to accommodate poorly implemented clients + const codeIsEmpty = /^0x0{0,40}$/i.test(code); + return !codeIsEmpty; + } + public async signTransactionAsync(address: string, message: string): Promise { + const signData = await promisify(this.web3.eth.sign)(address, message); + return signData; + } + public async getBlockNumberAsync(): Promise { + const blockNumber = await promisify(this.web3.eth.getBlockNumber)(); + return blockNumber; + } + public async getBlockAsync(blockParam: string|Web3.BlockParam): Promise { + const block = await promisify(this.web3.eth.getBlock)(blockParam); + return block; + } + public async getBlockTimestampAsync(blockParam: string|Web3.BlockParam): Promise { + const {timestamp} = await this.getBlockAsync(blockParam); + return timestamp; + } + public async getAvailableAddressesAsync(): Promise { + const addresses = await promisify(this.web3.eth.getAccounts)(); + return addresses; + } + public async getLogsAsync(filter: Web3.FilterObject): Promise { + let fromBlock = filter.fromBlock; + if (_.isNumber(fromBlock)) { + fromBlock = this.web3.toHex(fromBlock); + } + let toBlock = filter.toBlock; + if (_.isNumber(toBlock)) { + toBlock = this.web3.toHex(toBlock); + } + const serializedFilter = { + ...filter, + fromBlock, + toBlock, + }; + const payload = { + jsonrpc: '2.0', + id: this.jsonRpcRequestId++, + method: 'eth_getLogs', + params: [serializedFilter], + }; + const rawLogs = await this.sendRawPayloadAsync(payload); + const formattedLogs = _.map(rawLogs, this.formatLog.bind(this)); + return formattedLogs; + } + public getContractFromAbi(abi: Web3.ContractAbi): Web3.Contract { + const web3Contract = this.web3.eth.contract(abi); + return web3Contract; + } + public getContractInstance(abi: Web3.ContractAbi, address: string): Web3.ContractInstance { + const web3ContractInstance = this.getContractFromAbi(abi).at(address); + return web3ContractInstance; + } + public async estimateGasAsync(data: string): Promise { + const gas = await promisify(this.web3.eth.estimateGas)({data}); + return gas; + } + private async sendRawPayloadAsync(payload: Web3.JSONRPCRequestPayload): Promise { + const sendAsync = this.web3.currentProvider.sendAsync.bind(this.web3.currentProvider); + const response = await promisify(sendAsync)(payload); + 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): Web3.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; + } +} diff --git a/packages/web3-wrapper/tsconfig.json b/packages/web3-wrapper/tsconfig.json new file mode 100644 index 000000000..de186cfc4 --- /dev/null +++ b/packages/web3-wrapper/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "lib": [ "es2017", "dom"], + "outDir": "lib", + "sourceMap": true, + "declaration": true, + "noImplicitAny": true, + "strictNullChecks": true + }, + "include": [ + "./src/**/*", + "../../node_modules/web3-typescript-typings/index.d.ts" + ] +} diff --git a/packages/web3-wrapper/tslint.json b/packages/web3-wrapper/tslint.json new file mode 100644 index 000000000..a07795151 --- /dev/null +++ b/packages/web3-wrapper/tslint.json @@ -0,0 +1,5 @@ +{ + "extends": [ + "@0xproject/tslint-config" + ] +} -- cgit