From 7a21c6854bec32f9a36e8ca3de14a815e9c9fa7d Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Thu, 23 Nov 2017 11:14:21 -0600 Subject: Add option config for gasPrice and gasLimit for every transaction sending method --- packages/0x.js/src/contract.ts | 22 +++- .../src/contract_wrappers/ether_token_wrapper.ts | 14 ++- .../src/contract_wrappers/exchange_wrapper.ts | 139 +++++---------------- .../0x.js/src/contract_wrappers/token_wrapper.ts | 41 +++--- packages/0x.js/src/types.ts | 17 ++- 5 files changed, 95 insertions(+), 138 deletions(-) (limited to 'packages') diff --git a/packages/0x.js/src/contract.ts b/packages/0x.js/src/contract.ts index e9c49c9f1..347f47aa0 100644 --- a/packages/0x.js/src/contract.ts +++ b/packages/0x.js/src/contract.ts @@ -34,9 +34,10 @@ export class Contract implements Web3.ContractInstance { } else { const cbStyleFunction = this.contract[functionAbi.name]; const cbStyleEstimateGasFunction = this.contract[functionAbi.name].estimateGas; + const estimateGasAsync = promisify(cbStyleEstimateGasFunction, this.contract); this[functionAbi.name] = { - estimateGasAsync: promisify(cbStyleEstimateGasFunction, this.contract), - sendTransactionAsync: this.promisifyWithDefaultParams(cbStyleFunction), + estimateGasAsync, + sendTransactionAsync: this.promisifyWithDefaultParams(cbStyleFunction, estimateGasAsync), }; } }); @@ -47,18 +48,29 @@ export class Contract implements Web3.ContractInstance { this[eventAbi.name] = this.contract[eventAbi.name]; }); } - private promisifyWithDefaultParams(fn: (...args: any[]) => void): (...args: any[]) => Promise { + private promisifyWithDefaultParams( + web3CbStyleFunction: (...args: any[]) => void, + estimateGasAsync: (...args: any[]) => Promise, + ): (...args: any[]) => Promise { const promisifiedWithDefaultParams = async (...args: any[]) => { - const promise = new Promise((resolve, reject) => { + const promise = new Promise(async (resolve, reject) => { const lastArg = args[args.length - 1]; let txData: Partial = {}; if (this.isTxData(lastArg)) { txData = args.pop(); } + // Gas amounts priorities: + // 1 - method level + // 2 - Library defaults + // 3 - estimate txData = { ...this.defaults, ...txData, }; + if (_.isUndefined(txData.gas)) { + const estimatedGas = await estimateGasAsync.apply(this.contract, [...args, txData]); + txData.gas = estimatedGas; + } const callback = (err: Error, data: any) => { if (_.isNull(err)) { resolve(data); @@ -68,7 +80,7 @@ export class Contract implements Web3.ContractInstance { }; args.push(txData); args.push(callback); - fn.apply(this.contract, args); + web3CbStyleFunction.apply(this.contract, args); }); return promise; }; diff --git a/packages/0x.js/src/contract_wrappers/ether_token_wrapper.ts b/packages/0x.js/src/contract_wrappers/ether_token_wrapper.ts index 7a3f2bc52..65f1cda24 100644 --- a/packages/0x.js/src/contract_wrappers/ether_token_wrapper.ts +++ b/packages/0x.js/src/contract_wrappers/ether_token_wrapper.ts @@ -2,7 +2,7 @@ import BigNumber from 'bignumber.js'; import * as _ from 'lodash'; import {artifacts} from '../artifacts'; -import {EtherTokenContract, ZeroExError} from '../types'; +import {EtherTokenContract, TransactionOpts, ZeroExError} from '../types'; import {assert} from '../utils/assert'; import {Web3Wrapper} from '../web3_wrapper'; @@ -27,10 +27,11 @@ export class EtherTokenWrapper extends ContractWrapper { * to the depositor address. These wrapped ETH tokens can be used in 0x trades and are redeemable for 1-to-1 * for ETH. * @param amountInWei Amount of ETH in Wei the caller wishes to deposit. - * @param depositor The hex encoded user Ethereum address that would like to make the deposit. + * @param depositor The hex encoded user Ethereum address that would like to make the deposit. + * @param txOpts Transaction parameters. * @return Transaction hash. */ - public async depositAsync(amountInWei: BigNumber, depositor: string): Promise { + public async depositAsync(amountInWei: BigNumber, depositor: string, txOpts: TransactionOpts): Promise { assert.isValidBaseUnitAmount('amountInWei', amountInWei); await assert.isSenderAddressAsync('depositor', depositor, this._web3Wrapper); @@ -41,6 +42,8 @@ export class EtherTokenWrapper extends ContractWrapper { const txHash = await wethContract.deposit.sendTransactionAsync({ from: depositor, value: amountInWei, + gas: txOpts.gasLimit, + gasPrice: txOpts.gasPrice, }); return txHash; } @@ -49,9 +52,10 @@ export class EtherTokenWrapper extends ContractWrapper { * equivalent number of wrapped ETH tokens. * @param amountInWei Amount of ETH in Wei the caller wishes to withdraw. * @param withdrawer The hex encoded user Ethereum address that would like to make the withdrawl. + * @param txOpts Transaction parameters. * @return Transaction hash. */ - public async withdrawAsync(amountInWei: BigNumber, withdrawer: string): Promise { + public async withdrawAsync(amountInWei: BigNumber, withdrawer: string, txOpts: TransactionOpts): Promise { assert.isValidBaseUnitAmount('amountInWei', amountInWei); await assert.isSenderAddressAsync('withdrawer', withdrawer, this._web3Wrapper); @@ -62,6 +66,8 @@ export class EtherTokenWrapper extends ContractWrapper { const wethContract = await this._getEtherTokenContractAsync(); const txHash = await wethContract.withdraw.sendTransactionAsync(amountInWei, { from: withdrawer, + gas: txOpts.gasLimit, + gasPrice: txOpts.gasPrice, }); return txHash; } diff --git a/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts b/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts index 91b41c4a4..76a537b45 100644 --- a/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts +++ b/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts @@ -169,16 +169,14 @@ export class ExchangeWrapper extends ContractWrapper { public async fillOrderAsync(signedOrder: SignedOrder, fillTakerTokenAmount: BigNumber, shouldThrowOnInsufficientBalanceOrAllowance: boolean, takerAddress: string, - orderTransactionOpts?: OrderTransactionOpts): Promise { + orderTransactionOpts: OrderTransactionOpts = {}): Promise { assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount); assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); const exchangeInstance = await this._getExchangeContractAsync(); - const shouldValidate = _.isUndefined(orderTransactionOpts) ? - SHOULD_VALIDATE_BY_DEFAULT : - orderTransactionOpts.shouldValidate; + const shouldValidate = orderTransactionOpts.shouldValidate || SHOULD_VALIDATE_BY_DEFAULT; if (shouldValidate) { const zrxTokenAddress = this.getZRXTokenAddress(); const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest); @@ -188,18 +186,6 @@ export class ExchangeWrapper extends ContractWrapper { const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(signedOrder); - const gas = await exchangeInstance.fillOrder.estimateGasAsync( - orderAddresses, - orderValues, - fillTakerTokenAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - signedOrder.ecSignature.v, - signedOrder.ecSignature.r, - signedOrder.ecSignature.s, - { - from: takerAddress, - }, - ); const txHash: string = await exchangeInstance.fillOrder.sendTransactionAsync( orderAddresses, orderValues, @@ -210,7 +196,8 @@ export class ExchangeWrapper extends ContractWrapper { signedOrder.ecSignature.s, { from: takerAddress, - gas, + gas: orderTransactionOpts.gasLimit, + gasPrice: orderTransactionOpts.gasPrice, }, ); return txHash; @@ -236,7 +223,7 @@ export class ExchangeWrapper extends ContractWrapper { public async fillOrdersUpToAsync(signedOrders: SignedOrder[], fillTakerTokenAmount: BigNumber, shouldThrowOnInsufficientBalanceOrAllowance: boolean, takerAddress: string, - orderTransactionOpts?: OrderTransactionOpts): Promise { + orderTransactionOpts: OrderTransactionOpts = {}): Promise { assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); const takerTokenAddresses = _.map(signedOrders, signedOrder => signedOrder.takerTokenAddress); assert.hasAtMostOneUniqueValue(takerTokenAddresses, @@ -248,9 +235,7 @@ export class ExchangeWrapper extends ContractWrapper { assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); - const shouldValidate = _.isUndefined(orderTransactionOpts) ? - SHOULD_VALIDATE_BY_DEFAULT : - orderTransactionOpts.shouldValidate; + const shouldValidate = orderTransactionOpts.shouldValidate || SHOULD_VALIDATE_BY_DEFAULT; if (shouldValidate) { const zrxTokenAddress = this.getZRXTokenAddress(); const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest); @@ -278,18 +263,6 @@ export class ExchangeWrapper extends ContractWrapper { ); const exchangeInstance = await this._getExchangeContractAsync(); - const gas = await exchangeInstance.fillOrdersUpTo.estimateGasAsync( - orderAddressesArray, - orderValuesArray, - fillTakerTokenAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - vArray, - rArray, - sArray, - { - from: takerAddress, - }, - ); const txHash = await exchangeInstance.fillOrdersUpTo.sendTransactionAsync( orderAddressesArray, orderValuesArray, @@ -300,7 +273,8 @@ export class ExchangeWrapper extends ContractWrapper { sArray, { from: takerAddress, - gas, + gas: orderTransactionOpts.gasLimit, + gasPrice: orderTransactionOpts.gasPrice, }, ); return txHash; @@ -328,7 +302,7 @@ export class ExchangeWrapper extends ContractWrapper { public async batchFillOrdersAsync(orderFillRequests: OrderFillRequest[], shouldThrowOnInsufficientBalanceOrAllowance: boolean, takerAddress: string, - orderTransactionOpts?: OrderTransactionOpts): Promise { + orderTransactionOpts: OrderTransactionOpts = {}): Promise { assert.doesConformToSchema('orderFillRequests', orderFillRequests, schemas.orderFillRequestsSchema); const exchangeContractAddresses = _.map( orderFillRequests, @@ -338,9 +312,7 @@ export class ExchangeWrapper extends ContractWrapper { ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress); assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); - const shouldValidate = _.isUndefined(orderTransactionOpts) ? - SHOULD_VALIDATE_BY_DEFAULT : - orderTransactionOpts.shouldValidate; + const shouldValidate = orderTransactionOpts.shouldValidate || SHOULD_VALIDATE_BY_DEFAULT; if (shouldValidate) { const zrxTokenAddress = this.getZRXTokenAddress(); const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest); @@ -370,18 +342,6 @@ export class ExchangeWrapper extends ContractWrapper { ); const exchangeInstance = await this._getExchangeContractAsync(); - const gas = await exchangeInstance.batchFillOrders.estimateGasAsync( - orderAddressesArray, - orderValuesArray, - fillTakerTokenAmounts, - shouldThrowOnInsufficientBalanceOrAllowance, - vArray, - rArray, - sArray, - { - from: takerAddress, - }, - ); const txHash = await exchangeInstance.batchFillOrders.sendTransactionAsync( orderAddressesArray, orderValuesArray, @@ -392,7 +352,8 @@ export class ExchangeWrapper extends ContractWrapper { sArray, { from: takerAddress, - gas, + gas: orderTransactionOpts.gasLimit, + gasPrice: orderTransactionOpts.gasPrice, }, ); return txHash; @@ -411,16 +372,14 @@ export class ExchangeWrapper extends ContractWrapper { @decorators.contractCallErrorHandler public async fillOrKillOrderAsync(signedOrder: SignedOrder, fillTakerTokenAmount: BigNumber, takerAddress: string, - orderTransactionOpts?: OrderTransactionOpts): Promise { + orderTransactionOpts: OrderTransactionOpts = {}): Promise { assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); const exchangeInstance = await this._getExchangeContractAsync(); - const shouldValidate = _.isUndefined(orderTransactionOpts) ? - SHOULD_VALIDATE_BY_DEFAULT : - orderTransactionOpts.shouldValidate; + const shouldValidate = orderTransactionOpts.shouldValidate || SHOULD_VALIDATE_BY_DEFAULT; if (shouldValidate) { const zrxTokenAddress = this.getZRXTokenAddress(); const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest); @@ -429,18 +388,6 @@ export class ExchangeWrapper extends ContractWrapper { } const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(signedOrder); - - const gas = await exchangeInstance.fillOrKillOrder.estimateGasAsync( - orderAddresses, - orderValues, - fillTakerTokenAmount, - signedOrder.ecSignature.v, - signedOrder.ecSignature.r, - signedOrder.ecSignature.s, - { - from: takerAddress, - }, - ); const txHash = await exchangeInstance.fillOrKillOrder.sendTransactionAsync( orderAddresses, orderValues, @@ -450,7 +397,8 @@ export class ExchangeWrapper extends ContractWrapper { signedOrder.ecSignature.s, { from: takerAddress, - gas, + gas: orderTransactionOpts.gasLimit, + gasPrice: orderTransactionOpts.gasPrice, }, ); return txHash; @@ -467,7 +415,7 @@ export class ExchangeWrapper extends ContractWrapper { @decorators.contractCallErrorHandler public async batchFillOrKillAsync(orderFillRequests: OrderFillRequest[], takerAddress: string, - orderTransactionOpts?: OrderTransactionOpts): Promise { + orderTransactionOpts: OrderTransactionOpts = {}): Promise { assert.doesConformToSchema('orderFillRequests', orderFillRequests, schemas.orderFillRequestsSchema); const exchangeContractAddresses = _.map( @@ -482,9 +430,7 @@ export class ExchangeWrapper extends ContractWrapper { } const exchangeInstance = await this._getExchangeContractAsync(); - const shouldValidate = _.isUndefined(orderTransactionOpts) ? - SHOULD_VALIDATE_BY_DEFAULT : - orderTransactionOpts.shouldValidate; + const shouldValidate = orderTransactionOpts.shouldValidate || SHOULD_VALIDATE_BY_DEFAULT; if (shouldValidate) { const zrxTokenAddress = this.getZRXTokenAddress(); const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest); @@ -509,18 +455,6 @@ export class ExchangeWrapper extends ContractWrapper { // We use _.unzip because _.unzip doesn't type check if values have different types :'( const [orderAddresses, orderValues, fillTakerTokenAmounts, vParams, rParams, sParams] = _.unzip(orderAddressesValuesAndTakerTokenFillAmounts); - - const gas = await exchangeInstance.batchFillOrKillOrders.estimateGasAsync( - orderAddresses, - orderValues, - fillTakerTokenAmounts, - vParams, - rParams, - sParams, - { - from: takerAddress, - }, - ); const txHash = await exchangeInstance.batchFillOrKillOrders.sendTransactionAsync( orderAddresses, orderValues, @@ -530,7 +464,8 @@ export class ExchangeWrapper extends ContractWrapper { sParams, { from: takerAddress, - gas, + gas: orderTransactionOpts.gasLimit, + gasPrice: orderTransactionOpts.gasPrice, }, ); return txHash; @@ -546,16 +481,14 @@ export class ExchangeWrapper extends ContractWrapper { @decorators.contractCallErrorHandler public async cancelOrderAsync(order: Order|SignedOrder, cancelTakerTokenAmount: BigNumber, - orderTransactionOpts?: OrderTransactionOpts): Promise { + orderTransactionOpts: OrderTransactionOpts = {}): Promise { assert.doesConformToSchema('order', order, schemas.orderSchema); assert.isValidBaseUnitAmount('takerTokenCancelAmount', cancelTakerTokenAmount); await assert.isSenderAddressAsync('order.maker', order.maker, this._web3Wrapper); const exchangeInstance = await this._getExchangeContractAsync(); - const shouldValidate = _.isUndefined(orderTransactionOpts) ? - SHOULD_VALIDATE_BY_DEFAULT : - orderTransactionOpts.shouldValidate; + const shouldValidate = orderTransactionOpts.shouldValidate || SHOULD_VALIDATE_BY_DEFAULT; if (shouldValidate) { const orderHash = utils.getOrderHashHex(order); const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash); @@ -564,21 +497,14 @@ export class ExchangeWrapper extends ContractWrapper { } const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(order); - const gas = await exchangeInstance.cancelOrder.estimateGasAsync( - orderAddresses, - orderValues, - cancelTakerTokenAmount, - { - from: order.maker, - }, - ); const txHash = await exchangeInstance.cancelOrder.sendTransactionAsync( orderAddresses, orderValues, cancelTakerTokenAmount, { from: order.maker, - gas, + gas: orderTransactionOpts.gasLimit, + gasPrice: orderTransactionOpts.gasPrice, }, ); return txHash; @@ -593,7 +519,7 @@ export class ExchangeWrapper extends ContractWrapper { */ @decorators.contractCallErrorHandler public async batchCancelOrdersAsync(orderCancellationRequests: OrderCancellationRequest[], - orderTransactionOpts?: OrderTransactionOpts): Promise { + orderTransactionOpts: OrderTransactionOpts = {}): Promise { assert.doesConformToSchema('orderCancellationRequests', orderCancellationRequests, schemas.orderCancellationRequestsSchema); const exchangeContractAddresses = _.map( @@ -606,9 +532,7 @@ export class ExchangeWrapper extends ContractWrapper { assert.hasAtMostOneUniqueValue(makers, ExchangeContractErrs.MultipleMakersInSingleCancelBatchDisallowed); const maker = makers[0]; await assert.isSenderAddressAsync('maker', maker, this._web3Wrapper); - const shouldValidate = _.isUndefined(orderTransactionOpts) ? - SHOULD_VALIDATE_BY_DEFAULT : - orderTransactionOpts.shouldValidate; + const shouldValidate = orderTransactionOpts.shouldValidate || SHOULD_VALIDATE_BY_DEFAULT; if (shouldValidate) { for (const orderCancellationRequest of orderCancellationRequests) { const orderHash = utils.getOrderHashHex(orderCancellationRequest.order); @@ -633,21 +557,14 @@ export class ExchangeWrapper extends ContractWrapper { // We use _.unzip because _.unzip doesn't type check if values have different types :'( const [orderAddresses, orderValues, cancelTakerTokenAmounts] = _.unzip(orderAddressesValuesAndTakerTokenCancelAmounts); - const gas = await exchangeInstance.batchCancelOrders.estimateGasAsync( - orderAddresses, - orderValues, - cancelTakerTokenAmounts, - { - from: maker, - }, - ); const txHash = await exchangeInstance.batchCancelOrders.sendTransactionAsync( orderAddresses, orderValues, cancelTakerTokenAmounts, { from: maker, - gas, + gas: orderTransactionOpts.gasLimit, + gasPrice: orderTransactionOpts.gasPrice, }, ); return txHash; diff --git a/packages/0x.js/src/contract_wrappers/token_wrapper.ts b/packages/0x.js/src/contract_wrappers/token_wrapper.ts index 5c6cfeaed..4a1dfcf8d 100644 --- a/packages/0x.js/src/contract_wrappers/token_wrapper.ts +++ b/packages/0x.js/src/contract_wrappers/token_wrapper.ts @@ -12,6 +12,7 @@ import { TokenContract, TokenContractEventArgs, TokenEvents, + TransactionOpts, ZeroExError, } from '../types'; import {AbiDecoder} from '../utils/abi_decoder'; @@ -66,24 +67,21 @@ export class TokenWrapper extends ContractWrapper { * for spenderAddress. * @param spenderAddress The hex encoded user Ethereum address who will be able to spend the set allowance. * @param amountInBaseUnits The allowance amount you would like to set. + * @param txOpts Transaction parameters. * @return Transaction hash. */ public async setAllowanceAsync(tokenAddress: string, ownerAddress: string, spenderAddress: string, - amountInBaseUnits: BigNumber): Promise { + amountInBaseUnits: BigNumber, txOpts: TransactionOpts = {}): Promise { await assert.isSenderAddressAsync('ownerAddress', ownerAddress, this._web3Wrapper); assert.isETHAddressHex('spenderAddress', spenderAddress); assert.isETHAddressHex('tokenAddress', tokenAddress); assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits); const tokenContract = await this._getTokenContractAsync(tokenAddress); - // Hack: for some reason default estimated gas amount causes `base fee exceeds gas limit` exception - // on testrpc. Probably related to https://github.com/ethereumjs/testrpc/issues/294 - // TODO: Debug issue in testrpc and submit a PR, then remove this hack - const networkId = this._web3Wrapper.getNetworkId(); - const gas = networkId === constants.TESTRPC_NETWORK_ID ? ALLOWANCE_TO_ZERO_GAS_AMOUNT : undefined; const txHash = await tokenContract.approve.sendTransactionAsync(spenderAddress, amountInBaseUnits, { from: ownerAddress, - gas, + gas: txOpts.gasLimit, + gasPrice: txOpts.gasPrice, }); return txHash; } @@ -96,12 +94,13 @@ export class TokenWrapper extends ContractWrapper { * @param ownerAddress The hex encoded user Ethereum address who would like to set an allowance * for spenderAddress. * @param spenderAddress The hex encoded user Ethereum address who will be able to spend the set allowance. + * @param txOpts Transaction parameters. * @return Transaction hash. */ public async setUnlimitedAllowanceAsync(tokenAddress: string, ownerAddress: string, - spenderAddress: string): Promise { + spenderAddress: string, txOpts: TransactionOpts = {}): Promise { const txHash = await this.setAllowanceAsync( - tokenAddress, ownerAddress, spenderAddress, this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS, + tokenAddress, ownerAddress, spenderAddress, this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS, txOpts, ); return txHash; } @@ -147,16 +146,19 @@ export class TokenWrapper extends ContractWrapper { * @param ownerAddress The hex encoded user Ethereum address who is setting an allowance * for the Proxy contract. * @param amountInBaseUnits The allowance amount specified in baseUnits. + * @param txOpts Transaction parameters. * @return Transaction hash. */ public async setProxyAllowanceAsync(tokenAddress: string, ownerAddress: string, - amountInBaseUnits: BigNumber): Promise { + amountInBaseUnits: BigNumber, txOpts: TransactionOpts = {}): Promise { assert.isETHAddressHex('ownerAddress', ownerAddress); assert.isETHAddressHex('tokenAddress', tokenAddress); assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits); const proxyAddress = this._tokenTransferProxyWrapper.getContractAddress(); - const txHash = await this.setAllowanceAsync(tokenAddress, ownerAddress, proxyAddress, amountInBaseUnits); + const txHash = await this.setAllowanceAsync( + tokenAddress, ownerAddress, proxyAddress, amountInBaseUnits, txOpts, + ); return txHash; } /** @@ -167,11 +169,14 @@ export class TokenWrapper extends ContractWrapper { * @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed. * @param ownerAddress The hex encoded user Ethereum address who is setting an allowance * for the Proxy contract. + * @param txOpts Transaction parameters. * @return Transaction hash. */ - public async setUnlimitedProxyAllowanceAsync(tokenAddress: string, ownerAddress: string): Promise { + public async setUnlimitedProxyAllowanceAsync( + tokenAddress: string, ownerAddress: string, txOpts: TransactionOpts = {}, + ): Promise { const txHash = await this.setProxyAllowanceAsync( - tokenAddress, ownerAddress, this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS, + tokenAddress, ownerAddress, this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS, txOpts, ); return txHash; } @@ -181,10 +186,11 @@ export class TokenWrapper extends ContractWrapper { * @param fromAddress The hex encoded user Ethereum address that will send the funds. * @param toAddress The hex encoded user Ethereum address that will receive the funds. * @param amountInBaseUnits The amount (specified in baseUnits) of the token to transfer. + * @param txOpts Transaction parameters. * @return Transaction hash. */ public async transferAsync(tokenAddress: string, fromAddress: string, toAddress: string, - amountInBaseUnits: BigNumber): Promise { + amountInBaseUnits: BigNumber, txOpts: TransactionOpts = {}): Promise { assert.isETHAddressHex('tokenAddress', tokenAddress); await assert.isSenderAddressAsync('fromAddress', fromAddress, this._web3Wrapper); assert.isETHAddressHex('toAddress', toAddress); @@ -199,6 +205,8 @@ export class TokenWrapper extends ContractWrapper { const txHash = await tokenContract.transfer.sendTransactionAsync(toAddress, amountInBaseUnits, { from: fromAddress, + gas: txOpts.gasLimit, + gasPrice: txOpts.gasPrice, }); return txHash; } @@ -213,10 +221,11 @@ export class TokenWrapper extends ContractWrapper { * `fromAddress` must have set an allowance to the `senderAddress` * before this call. * @param amountInBaseUnits The amount (specified in baseUnits) of the token to transfer. + * @param txOpts Transaction parameters. * @return Transaction hash. */ public async transferFromAsync(tokenAddress: string, fromAddress: string, toAddress: string, - senderAddress: string, amountInBaseUnits: BigNumber): + senderAddress: string, amountInBaseUnits: BigNumber, txOpts: TransactionOpts = {}): Promise { assert.isETHAddressHex('tokenAddress', tokenAddress); assert.isETHAddressHex('fromAddress', fromAddress); @@ -240,6 +249,8 @@ export class TokenWrapper extends ContractWrapper { fromAddress, toAddress, amountInBaseUnits, { from: senderAddress, + gas: txOpts.gasLimit, + gasPrice: txOpts.gasPrice, }, ); return txHash; diff --git a/packages/0x.js/src/types.ts b/packages/0x.js/src/types.ts index af4f054f0..d187af01a 100644 --- a/packages/0x.js/src/types.ts +++ b/packages/0x.js/src/types.ts @@ -332,6 +332,7 @@ export interface TxOpts { from: string; gas?: number; value?: BigNumber; + gasPrice?: BigNumber; } export interface TokenAddressBySymbol { @@ -476,17 +477,27 @@ export interface ValidateOrderFillableOpts { * let's the user query the blockchain's state at an arbitrary point in time. In order for this to work, the * backing Ethereum node must keep the entire historical state of the chain (e.g setting `--pruning=archive` * flag when running Parity). + * gasPrice: Gas price to use for a transaction in */ export interface MethodOpts { defaultBlock?: Web3.BlockParam; } +/* + * gasPrice: Gas price in Wei to use for a transaction + * gasLimit: The amount of gas to send with a transaction + */ +export interface TransactionOpts { + gasPrice?: BigNumber; + gasLimit?: number; +} + /* * shouldValidate: Flag indicating whether the library should make attempts to validate a transaction before - * broadcasting it. For example, order has a valid signature, maker has sufficient funds, etc. + * broadcasting it. For example, order has a valid signature, maker has sufficient funds, etc. Default: true */ -export interface OrderTransactionOpts { - shouldValidate: boolean; +export interface OrderTransactionOpts extends TransactionOpts { + shouldValidate?: boolean; } export type FilterObject = Web3.FilterObject; -- cgit v1.2.3 From 0e44a630f0479651522179df5743d80e6f596ace Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Thu, 23 Nov 2017 11:17:48 -0600 Subject: Add CHANGELOG entry --- packages/0x.js/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) (limited to 'packages') diff --git a/packages/0x.js/CHANGELOG.md b/packages/0x.js/CHANGELOG.md index 67b2b89b6..7fce9167a 100644 --- a/packages/0x.js/CHANGELOG.md +++ b/packages/0x.js/CHANGELOG.md @@ -8,6 +8,7 @@ vx.x.x * Remove `ZeroExError.ContractNotFound` and replace it with contract-specific errors (#233) * Make `DecodedLogEvent` contain `LogWithDecodedArgs` under log key instead of merging it in like web3 does (#234) * Rename `removed` to `isRemoved` in `DecodedLogEvent` (#234) + * Add config allowing to specify gasPrice and gasLimit for every transaction sending method (#235) * Modify order validation methods to validate against the `latest` block, not against the `pending` block (#236) v0.26.0 -- cgit v1.2.3 From d1065cd266b204f7b7f7468f5fbe0d663acf2a7b Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Thu, 23 Nov 2017 11:19:06 -0600 Subject: Add an initializer for txOpts in etherToken --- packages/0x.js/src/contract_wrappers/ether_token_wrapper.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'packages') diff --git a/packages/0x.js/src/contract_wrappers/ether_token_wrapper.ts b/packages/0x.js/src/contract_wrappers/ether_token_wrapper.ts index 65f1cda24..ede0460bd 100644 --- a/packages/0x.js/src/contract_wrappers/ether_token_wrapper.ts +++ b/packages/0x.js/src/contract_wrappers/ether_token_wrapper.ts @@ -31,7 +31,9 @@ export class EtherTokenWrapper extends ContractWrapper { * @param txOpts Transaction parameters. * @return Transaction hash. */ - public async depositAsync(amountInWei: BigNumber, depositor: string, txOpts: TransactionOpts): Promise { + public async depositAsync( + amountInWei: BigNumber, depositor: string, txOpts: TransactionOpts = {}, + ): Promise { assert.isValidBaseUnitAmount('amountInWei', amountInWei); await assert.isSenderAddressAsync('depositor', depositor, this._web3Wrapper); @@ -55,7 +57,9 @@ export class EtherTokenWrapper extends ContractWrapper { * @param txOpts Transaction parameters. * @return Transaction hash. */ - public async withdrawAsync(amountInWei: BigNumber, withdrawer: string, txOpts: TransactionOpts): Promise { + public async withdrawAsync( + amountInWei: BigNumber, withdrawer: string, txOpts: TransactionOpts = {}, + ): Promise { assert.isValidBaseUnitAmount('amountInWei', amountInWei); await assert.isSenderAddressAsync('withdrawer', withdrawer, this._web3Wrapper); -- cgit v1.2.3 From 33a8c7a9fb707f6a5f1bc345988ba1e420af67c5 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Mon, 27 Nov 2017 11:55:51 -0600 Subject: Update testrpc --- packages/0x.js/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages') diff --git a/packages/0x.js/package.json b/packages/0x.js/package.json index 024cb0fe9..c542fa2df 100644 --- a/packages/0x.js/package.json +++ b/packages/0x.js/package.json @@ -61,7 +61,7 @@ "copyfiles": "^1.2.0", "coveralls": "^3.0.0", "dirty-chai": "^2.0.1", - "ethereumjs-testrpc": "4.0.1", + "ethereumjs-testrpc": "^6.0.3", "json-loader": "^0.5.4", "mocha": "^4.0.0", "npm-run-all": "^4.0.2", -- cgit v1.2.3 From ee93f091ea5fc78d039f4f91cb234a94f428ec6a Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Mon, 27 Nov 2017 13:39:32 -0600 Subject: Add gas margin and remove undefined values --- packages/0x.js/src/contract.ts | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'packages') diff --git a/packages/0x.js/src/contract.ts b/packages/0x.js/src/contract.ts index 347f47aa0..6dc0dab66 100644 --- a/packages/0x.js/src/contract.ts +++ b/packages/0x.js/src/contract.ts @@ -5,6 +5,8 @@ import * as Web3 from 'web3'; import {AbiType} from './types'; +const GAS_MARGIN = 30000; + export class Contract implements Web3.ContractInstance { public address: string; public abi: Web3.ContractAbi; @@ -63,21 +65,23 @@ export class Contract implements Web3.ContractInstance { // 1 - method level // 2 - Library defaults // 3 - estimate + const removeUndefinedProperties = _.pickBy; txData = { - ...this.defaults, - ...txData, + ...removeUndefinedProperties(this.defaults), + ...removeUndefinedProperties(txData), }; if (_.isUndefined(txData.gas)) { - const estimatedGas = await estimateGasAsync.apply(this.contract, [...args, txData]); - txData.gas = estimatedGas; - } - const callback = (err: Error, data: any) => { - if (_.isNull(err)) { - resolve(data); - } else { + try { + const estimatedGas = await estimateGasAsync.apply(this.contract, [...args, txData]); + const gas = estimatedGas + GAS_MARGIN; + txData.gas = gas; + console.log('withGas', txData); + } catch (err) { reject(err); + return; } - }; + } + const callback = (err: Error, data: any) => _.isNull(err) ? resolve(data) : reject(err); args.push(txData); args.push(callback); web3CbStyleFunction.apply(this.contract, args); -- cgit v1.2.3 From 65697f589697b206cb576025e169e8d36ef2c740 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Mon, 27 Nov 2017 13:40:03 -0600 Subject: Update MAX_REASONABLE_GAS_COST_IN_WEI --- packages/0x.js/test/ether_token_wrapper_test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages') diff --git a/packages/0x.js/test/ether_token_wrapper_test.ts b/packages/0x.js/test/ether_token_wrapper_test.ts index 5b5e4c656..d3e4439ee 100644 --- a/packages/0x.js/test/ether_token_wrapper_test.ts +++ b/packages/0x.js/test/ether_token_wrapper_test.ts @@ -18,7 +18,7 @@ const blockchainLifecycle = new BlockchainLifecycle(); // a small amount of ETH will be used to pay this gas cost. We therefore check that the difference between // the expected balance and actual balance (given the amount of ETH deposited), only deviates by the amount // required to pay gas costs. -const MAX_REASONABLE_GAS_COST_IN_WEI = 62237; +const MAX_REASONABLE_GAS_COST_IN_WEI = 62517; describe('EtherTokenWrapper', () => { let web3: Web3; -- cgit v1.2.3 From 128fccb763bb780aa96063031116e45a98d163f9 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Mon, 27 Nov 2017 15:29:51 -0600 Subject: Fix defaults for shouldValidate --- .../src/contract_wrappers/exchange_wrapper.ts | 28 ++++++++++++++++------ 1 file changed, 21 insertions(+), 7 deletions(-) (limited to 'packages') diff --git a/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts b/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts index 76a537b45..273b348ff 100644 --- a/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts +++ b/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts @@ -176,7 +176,9 @@ export class ExchangeWrapper extends ContractWrapper { await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); const exchangeInstance = await this._getExchangeContractAsync(); - const shouldValidate = orderTransactionOpts.shouldValidate || SHOULD_VALIDATE_BY_DEFAULT; + const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate) ? + SHOULD_VALIDATE_BY_DEFAULT : + orderTransactionOpts.shouldValidate; if (shouldValidate) { const zrxTokenAddress = this.getZRXTokenAddress(); const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest); @@ -235,7 +237,9 @@ export class ExchangeWrapper extends ContractWrapper { assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); - const shouldValidate = orderTransactionOpts.shouldValidate || SHOULD_VALIDATE_BY_DEFAULT; + const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate) ? + SHOULD_VALIDATE_BY_DEFAULT : + orderTransactionOpts.shouldValidate; if (shouldValidate) { const zrxTokenAddress = this.getZRXTokenAddress(); const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest); @@ -312,7 +316,9 @@ export class ExchangeWrapper extends ContractWrapper { ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress); assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); - const shouldValidate = orderTransactionOpts.shouldValidate || SHOULD_VALIDATE_BY_DEFAULT; + const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate) ? + SHOULD_VALIDATE_BY_DEFAULT : + orderTransactionOpts.shouldValidate; if (shouldValidate) { const zrxTokenAddress = this.getZRXTokenAddress(); const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest); @@ -379,7 +385,9 @@ export class ExchangeWrapper extends ContractWrapper { const exchangeInstance = await this._getExchangeContractAsync(); - const shouldValidate = orderTransactionOpts.shouldValidate || SHOULD_VALIDATE_BY_DEFAULT; + const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate) ? + SHOULD_VALIDATE_BY_DEFAULT : + orderTransactionOpts.shouldValidate; if (shouldValidate) { const zrxTokenAddress = this.getZRXTokenAddress(); const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest); @@ -430,7 +438,9 @@ export class ExchangeWrapper extends ContractWrapper { } const exchangeInstance = await this._getExchangeContractAsync(); - const shouldValidate = orderTransactionOpts.shouldValidate || SHOULD_VALIDATE_BY_DEFAULT; + const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate) ? + SHOULD_VALIDATE_BY_DEFAULT : + orderTransactionOpts.shouldValidate; if (shouldValidate) { const zrxTokenAddress = this.getZRXTokenAddress(); const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest); @@ -488,7 +498,9 @@ export class ExchangeWrapper extends ContractWrapper { const exchangeInstance = await this._getExchangeContractAsync(); - const shouldValidate = orderTransactionOpts.shouldValidate || SHOULD_VALIDATE_BY_DEFAULT; + const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate) ? + SHOULD_VALIDATE_BY_DEFAULT : + orderTransactionOpts.shouldValidate; if (shouldValidate) { const orderHash = utils.getOrderHashHex(order); const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash); @@ -532,7 +544,9 @@ export class ExchangeWrapper extends ContractWrapper { assert.hasAtMostOneUniqueValue(makers, ExchangeContractErrs.MultipleMakersInSingleCancelBatchDisallowed); const maker = makers[0]; await assert.isSenderAddressAsync('maker', maker, this._web3Wrapper); - const shouldValidate = orderTransactionOpts.shouldValidate || SHOULD_VALIDATE_BY_DEFAULT; + const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate) ? + SHOULD_VALIDATE_BY_DEFAULT : + orderTransactionOpts.shouldValidate; if (shouldValidate) { for (const orderCancellationRequest of orderCancellationRequests) { const orderHash = utils.getOrderHashHex(orderCancellationRequest.order); -- cgit v1.2.3 From 5977fa881e6d68c6779d4708d55306cd05a61fb3 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Mon, 27 Nov 2017 15:30:23 -0600 Subject: Increase gas margin --- packages/0x.js/src/contract.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'packages') diff --git a/packages/0x.js/src/contract.ts b/packages/0x.js/src/contract.ts index 6dc0dab66..7e16c580d 100644 --- a/packages/0x.js/src/contract.ts +++ b/packages/0x.js/src/contract.ts @@ -5,7 +5,7 @@ import * as Web3 from 'web3'; import {AbiType} from './types'; -const GAS_MARGIN = 30000; +const GAS_MARGIN = 300000; export class Contract implements Web3.ContractInstance { public address: string; @@ -75,7 +75,6 @@ export class Contract implements Web3.ContractInstance { const estimatedGas = await estimateGasAsync.apply(this.contract, [...args, txData]); const gas = estimatedGas + GAS_MARGIN; txData.gas = gas; - console.log('withGas', txData); } catch (err) { reject(err); return; -- cgit v1.2.3 From 082c5c375e07fc8781ca74671326e5184422d298 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Mon, 27 Nov 2017 15:31:06 -0600 Subject: Fix tests --- packages/0x.js/test/exchange_wrapper_test.ts | 1 + 1 file changed, 1 insertion(+) (limited to 'packages') diff --git a/packages/0x.js/test/exchange_wrapper_test.ts b/packages/0x.js/test/exchange_wrapper_test.ts index f6c823cc4..3e2160330 100644 --- a/packages/0x.js/test/exchange_wrapper_test.ts +++ b/packages/0x.js/test/exchange_wrapper_test.ts @@ -124,6 +124,7 @@ describe('ExchangeWrapper', () => { it('should not validate when orderTransactionOptions specify not to validate', async () => { return expect(zeroEx.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress, { shouldValidate: false, + gasLimit: 200000, // If we don't pass this gas estimation will fail })).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); }); }); -- cgit v1.2.3 From e6887dc9d4b129eee062c9c2f80a970548ee7650 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Mon, 27 Nov 2017 15:33:20 -0600 Subject: Fix a comment --- packages/0x.js/src/contract.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'packages') diff --git a/packages/0x.js/src/contract.ts b/packages/0x.js/src/contract.ts index 7e16c580d..25a0609aa 100644 --- a/packages/0x.js/src/contract.ts +++ b/packages/0x.js/src/contract.ts @@ -61,10 +61,10 @@ export class Contract implements Web3.ContractInstance { if (this.isTxData(lastArg)) { txData = args.pop(); } - // Gas amounts priorities: - // 1 - method level - // 2 - Library defaults - // 3 - estimate + // Gas amount sourced with the following priorities: + // 1. Optional param passed it to public method call + // 2. Global config passed in at library instantiation + // 3. Gas estimate calculation + safety margin const removeUndefinedProperties = _.pickBy; txData = { ...removeUndefinedProperties(this.defaults), -- cgit v1.2.3 From 0500602ac3e8d4ea3c54d1cf2d4dcc2b0bcf80ba Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Mon, 27 Nov 2017 17:07:37 -0600 Subject: Fix tests --- packages/0x.js/test/exchange_wrapper_test.ts | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'packages') diff --git a/packages/0x.js/test/exchange_wrapper_test.ts b/packages/0x.js/test/exchange_wrapper_test.ts index 3e2160330..d862c347d 100644 --- a/packages/0x.js/test/exchange_wrapper_test.ts +++ b/packages/0x.js/test/exchange_wrapper_test.ts @@ -184,6 +184,7 @@ describe('ExchangeWrapper', () => { it('should not validate when orderTransactionOptions specify not to validate', async () => { return expect(zeroEx.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress, { shouldValidate: false, + gasLimit: 200000, // If we don't pass this gas estimation will fail })).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); }); }); @@ -363,6 +364,7 @@ describe('ExchangeWrapper', () => { return expect(zeroEx.exchange.batchFillOrdersAsync( orderFillBatch, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, { shouldValidate: false, + gasLimit: 200000, // If we don't pass this gas estimation will fail })).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); }); }); @@ -420,6 +422,7 @@ describe('ExchangeWrapper', () => { return expect(zeroEx.exchange.fillOrdersUpToAsync( signedOrders, emptyFillUpToAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, { shouldValidate: false, + gasLimit: 200000, // If we don't pass this gas estimation will fail })).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); }); }); @@ -468,6 +471,7 @@ describe('ExchangeWrapper', () => { it('should not validate when orderTransactionOptions specify not to validate', async () => { return expect(zeroEx.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount, { shouldValidate: false, + gasLimit: 200000, // If we don't pass this gas estimation will fail })).to.not.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); }); }); @@ -543,6 +547,7 @@ describe('ExchangeWrapper', () => { it('should not validate when orderTransactionOptions specify not to validate', async () => { return expect(zeroEx.exchange.batchCancelOrdersAsync(cancelBatch, { shouldValidate: false, + gasLimit: 200000, // If we don't pass this gas estimation will fail })).to.not.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); }); }); -- cgit v1.2.3 From 6e2a69976b9d537e345f793e89adc74c352ad7ac Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Mon, 27 Nov 2017 18:06:15 -0600 Subject: Fix website linter errors --- packages/json-schemas/package.json | 2 +- packages/website/package.json | 1 + packages/website/ts/blockchain.ts | 321 +++++++++++++------------- packages/website/ts/components/fill_order.tsx | 122 +++++----- packages/website/ts/globals.d.ts | 10 +- packages/website/ts/types.ts | 19 +- packages/website/ts/web3_wrapper.ts | 7 +- 7 files changed, 249 insertions(+), 233 deletions(-) (limited to 'packages') diff --git a/packages/json-schemas/package.json b/packages/json-schemas/package.json index 87194d362..9d207a923 100644 --- a/packages/json-schemas/package.json +++ b/packages/json-schemas/package.json @@ -5,7 +5,7 @@ "main": "lib/src/index.js", "types": "lib/src/index.d.ts", "scripts": { - "lint": "tslint --project . src/*.ts test/*.ts", + "lint": "tslint --project . 'src/*.ts' 'test/*.ts'", "test": "run-s clean build run_mocha", "test:circleci": "yarn test", "run_mocha": "mocha lib/test/**/*_test.js", diff --git a/packages/website/package.json b/packages/website/package.json index f91b579bd..f9951defb 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -3,6 +3,7 @@ "version": "0.0.0", "description": "Website and 0x portal dapp", "scripts": { + "lint": "tslint --project . 'ts/*.ts'", "build": "NODE_ENV=production webpack; exit 0;", "clean": "shx rm -f public/bundle*", "dev": "webpack-dev-server --content-base public --https", diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index b13c48a65..7851ea8ff 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -1,59 +1,60 @@ -import * as _ from 'lodash'; -import * as React from 'react'; import { - ZeroEx, - ZeroExError, + BlockParam, + DecodedLogEvent, ExchangeContractErrs, ExchangeContractEventArgs, ExchangeEvents, - SubscriptionOpts, IndexedFilterValues, - DecodedLogEvent, - BlockParam, - LogFillContractEventArgs, LogCancelContractEventArgs, - Token as ZeroExToken, + LogFillContractEventArgs, LogWithDecodedArgs, - TransactionReceiptWithDecodedLogs, - SignedOrder, Order, + SignedOrder, + SubscriptionOpts, + Token as ZeroExToken, + TransactionReceiptWithDecodedLogs, + ZeroEx, + ZeroExError, } from '0x.js'; import BigNumber from 'bignumber.js'; -import Web3 = require('web3'); +import compareVersions = require('compare-versions'); import promisify = require('es6-promisify'); +import ethUtil = require('ethereumjs-util'); import findVersions = require('find-versions'); -import compareVersions = require('compare-versions'); +import * as _ from 'lodash'; +import * as React from 'react'; import contract = require('truffle-contract'); -import ethUtil = require('ethereumjs-util'); -import ProviderEngine = require('web3-provider-engine'); -import FilterSubprovider = require('web3-provider-engine/subproviders/filters'); -import { TransactionSubmitted } from 'ts/components/flash_messages/transaction_submitted'; import {TokenSendCompleted} from 'ts/components/flash_messages/token_send_completed'; -import {RedundantRPCSubprovider} from 'ts/subproviders/redundant_rpc_subprovider'; +import { TransactionSubmitted } from 'ts/components/flash_messages/transaction_submitted'; +import {trackedTokenStorage} from 'ts/local_storage/tracked_token_storage'; +import {tradeHistoryStorage} from 'ts/local_storage/trade_history_storage'; +import {Dispatcher} from 'ts/redux/dispatcher'; import {InjectedWeb3SubProvider} from 'ts/subproviders/injected_web3_subprovider'; import {ledgerWalletSubproviderFactory} from 'ts/subproviders/ledger_wallet_subprovider_factory'; -import {Dispatcher} from 'ts/redux/dispatcher'; -import {utils} from 'ts/utils/utils'; -import {constants} from 'ts/utils/constants'; -import {configs} from 'ts/utils/configs'; +import {RedundantRPCSubprovider} from 'ts/subproviders/redundant_rpc_subprovider'; import { - BlockchainErrs, - Token, - SignatureData, - Side, - ContractResponse, BlockchainCallErrs, + BlockchainErrs, ContractInstance, - ProviderType, - LedgerWalletSubprovider, + ContractResponse, EtherscanLinkSuffixes, + LedgerWalletSubprovider, + ProviderType, + Side, + SignatureData, + Token, TokenByAddress, TokenStateByAddress, } from 'ts/types'; -import {Web3Wrapper} from 'ts/web3_wrapper'; +import {configs} from 'ts/utils/configs'; +import {constants} from 'ts/utils/constants'; import {errorReporter} from 'ts/utils/error_reporter'; -import {tradeHistoryStorage} from 'ts/local_storage/trade_history_storage'; -import {trackedTokenStorage} from 'ts/local_storage/tracked_token_storage'; +import {utils} from 'ts/utils/utils'; +import {Web3Wrapper} from 'ts/web3_wrapper'; +import Web3 = require('web3'); +import ProviderEngine = require('web3-provider-engine'); +import FilterSubprovider = require('web3-provider-engine/subproviders/filters'); + import * as MintableArtifacts from '../contracts/Mintable.json'; const ALLOWANCE_TO_ZERO_GAS_AMOUNT = 45730; @@ -72,9 +73,116 @@ export class Blockchain { private cachedProvider: Web3.Provider; private ledgerSubProvider: LedgerWalletSubprovider; private zrxPollIntervalId: number; + public static toHumanReadableErrorMsg(error: ZeroExError|ExchangeContractErrs, takerAddress: string): string { + const ZeroExErrorToHumanReadableError: {[error: string]: string} = { + [ZeroExError.TokenTransferProxyContractDoesNotExist]: 'Token transfer proxy contract does not exist', + [ZeroExError.TokenContractDoesNotExist]: 'Token contract does not exist', + [ZeroExError.ZRXContractDoesNotExist]: 'ZRX contract does not exist', + [ZeroExError.EtherTokenContractDoesNotExist]: 'Ether token contract does not exist', + [ZeroExError.ExchangeContractDoesNotExist]: 'Exchange contract does not exist', + [ZeroExError.UnhandledError]: ' Unhandled error occured', + [ZeroExError.UserHasNoAssociatedAddress]: 'User has no addresses available', + [ZeroExError.InvalidSignature]: 'Order signature is not valid', + [ZeroExError.ContractNotDeployedOnNetwork]: 'Contract is not deployed on the detected network', + [ZeroExError.InvalidJump]: 'Invalid jump occured while executing the transaction', + [ZeroExError.OutOfGas]: 'Transaction ran out of gas', + [ZeroExError.NoNetworkId]: 'No network id detected', + }; + const exchangeContractErrorToHumanReadableError: {[error: string]: string} = { + [ExchangeContractErrs.OrderFillExpired]: 'This order has expired', + [ExchangeContractErrs.OrderCancelExpired]: 'This order has expired', + [ExchangeContractErrs.OrderCancelAmountZero]: 'Order cancel amount can\'t be 0', + [ExchangeContractErrs.OrderAlreadyCancelledOrFilled]: + 'This order has already been completely filled or cancelled', + [ExchangeContractErrs.OrderFillAmountZero]: 'Order fill amount can\'t be 0', + [ExchangeContractErrs.OrderRemainingFillAmountZero]: + 'This order has already been completely filled or cancelled', + [ExchangeContractErrs.OrderFillRoundingError]: 'Rounding error will occur when filling this order', + [ExchangeContractErrs.InsufficientTakerBalance]: + 'Taker no longer has a sufficient balance to complete this order', + [ExchangeContractErrs.InsufficientTakerAllowance]: + 'Taker no longer has a sufficient allowance to complete this order', + [ExchangeContractErrs.InsufficientMakerBalance]: + 'Maker no longer has a sufficient balance to complete this order', + [ExchangeContractErrs.InsufficientMakerAllowance]: + 'Maker no longer has a sufficient allowance to complete this order', + [ExchangeContractErrs.InsufficientTakerFeeBalance]: 'Taker no longer has a sufficient balance to pay fees', + [ExchangeContractErrs.InsufficientTakerFeeAllowance]: + 'Taker no longer has a sufficient allowance to pay fees', + [ExchangeContractErrs.InsufficientMakerFeeBalance]: 'Maker no longer has a sufficient balance to pay fees', + [ExchangeContractErrs.InsufficientMakerFeeAllowance]: + 'Maker no longer has a sufficient allowance to pay fees', + [ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker]: + `This order can only be filled by ${takerAddress}`, + [ExchangeContractErrs.InsufficientRemainingFillAmount]: + 'Insufficient remaining fill amount', + }; + const humanReadableErrorMsg = exchangeContractErrorToHumanReadableError[error] || + ZeroExErrorToHumanReadableError[error]; + return humanReadableErrorMsg; + } + private static async getProviderAsync(injectedWeb3: Web3, networkIdIfExists: number): Promise { + const doesInjectedWeb3Exist = !_.isUndefined(injectedWeb3); + const publicNodeUrlsIfExistsForNetworkId = constants.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkIdIfExists]; + const isPublicNodeAvailableForNetworkId = !_.isUndefined(publicNodeUrlsIfExistsForNetworkId); + + let provider; + if (doesInjectedWeb3Exist && isPublicNodeAvailableForNetworkId) { + // We catch all requests involving a users account and send it to the injectedWeb3 + // instance. All other requests go to the public hosted node. + provider = new ProviderEngine(); + provider.addProvider(new InjectedWeb3SubProvider(injectedWeb3)); + provider.addProvider(new FilterSubprovider()); + provider.addProvider(new RedundantRPCSubprovider( + publicNodeUrlsIfExistsForNetworkId, + )); + provider.start(); + } else if (doesInjectedWeb3Exist) { + // Since no public node for this network, all requests go to injectedWeb3 instance + provider = injectedWeb3.currentProvider; + } else { + // If no injectedWeb3 instance, all requests fallback to our public hosted mainnet/testnet node + // We do this so that users can still browse the 0x Portal DApp even if they do not have web3 + // injected into their browser. + provider = new ProviderEngine(); + provider.addProvider(new FilterSubprovider()); + const networkId = configs.isMainnetEnabled ? + constants.MAINNET_NETWORK_ID : + constants.TESTNET_NETWORK_ID; + provider.addProvider(new RedundantRPCSubprovider( + constants.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkId], + )); + provider.start(); + } + + return provider; + } + private static getNameGivenProvider(provider: Web3.Provider): string { + if (!_.isUndefined((provider as any).isMetaMask)) { + return constants.METAMASK_PROVIDER_NAME; + } + + // HACK: We use the fact that Parity Signer's provider is an instance of their + // internal `Web3FrameProvider` class. + const isParitySigner = _.startsWith(provider.constructor.toString(), 'function Web3FrameProvider'); + if (isParitySigner) { + return constants.PARITY_SIGNER_PROVIDER_NAME; + } + + return constants.GENERIC_PROVIDER_NAME; + } + private static async onPageLoadAsync() { + if (document.readyState === 'complete') { + return; // Already loaded + } + return new Promise((resolve, reject) => { + window.onload = resolve; + }); + } constructor(dispatcher: Dispatcher, isSalePage: boolean = false) { this.dispatcher = dispatcher; this.userAddress = ''; + // tslint:disable-next-line:no-floating-promises this.onPageLoadInitFireAndForgetAsync(); } public async networkIdUpdatedFireAndForgetAsync(newNetworkId: number) { @@ -157,8 +265,8 @@ export class Blockchain { this.web3Wrapper.destroy(); const shouldPollUserAddress = false; this.web3Wrapper = new Web3Wrapper(this.dispatcher, provider, this.networkId, shouldPollUserAddress); - await this.zeroEx.setProviderAsync(provider); - await this.postInstantiationOrUpdatingProviderZeroExAsync(); + this.zeroEx.setProvider(provider, this.networkId); + this.postInstantiationOrUpdatingProviderZeroEx(); break; } @@ -169,8 +277,8 @@ export class Blockchain { provider = this.cachedProvider; const shouldPollUserAddress = true; this.web3Wrapper = new Web3Wrapper(this.dispatcher, provider, this.networkId, shouldPollUserAddress); - await this.zeroEx.setProviderAsync(provider); - await this.postInstantiationOrUpdatingProviderZeroExAsync(); + this.zeroEx.setProvider(provider, this.networkId); + this.postInstantiationOrUpdatingProviderZeroEx(); delete this.ledgerSubProvider; delete this.cachedProvider; break; @@ -208,7 +316,7 @@ export class Blockchain { amountInBaseUnits, })); } - public portalOrderToSignedOrder(maker: string, taker: string, makerTokenAddress: string, + public portalOrderToSignedOrder(maker: string, takerIfExists: string, makerTokenAddress: string, takerTokenAddress: string, makerTokenAmount: BigNumber, takerTokenAmount: BigNumber, makerFee: BigNumber, takerFee: BigNumber, expirationUnixTimestampSec: BigNumber, @@ -216,7 +324,7 @@ export class Blockchain { signatureData: SignatureData, salt: BigNumber): SignedOrder { const ecSignature = signatureData; const exchangeContractAddress = this.getExchangeContractAddressIfExists(); - taker = _.isEmpty(taker) ? constants.NULL_ADDRESS : taker; + const taker = _.isEmpty(takerIfExists) ? constants.NULL_ADDRESS : takerIfExists; const signedOrder = { ecSignature, exchangeContractAddress, @@ -273,51 +381,6 @@ export class Blockchain { public getExchangeContractAddressIfExists() { return this.exchangeAddress; } - public toHumanReadableErrorMsg(error: ZeroExError|ExchangeContractErrs, takerAddress: string): string { - const ZeroExErrorToHumanReadableError: {[error: string]: string} = { - [ZeroExError.ContractDoesNotExist]: 'Contract does not exist', - [ZeroExError.ExchangeContractDoesNotExist]: 'Exchange contract does not exist', - [ZeroExError.UnhandledError]: ' Unhandled error occured', - [ZeroExError.UserHasNoAssociatedAddress]: 'User has no addresses available', - [ZeroExError.InvalidSignature]: 'Order signature is not valid', - [ZeroExError.ContractNotDeployedOnNetwork]: 'Contract is not deployed on the detected network', - [ZeroExError.InvalidJump]: 'Invalid jump occured while executing the transaction', - [ZeroExError.OutOfGas]: 'Transaction ran out of gas', - [ZeroExError.NoNetworkId]: 'No network id detected', - }; - const exchangeContractErrorToHumanReadableError: {[error: string]: string} = { - [ExchangeContractErrs.OrderFillExpired]: 'This order has expired', - [ExchangeContractErrs.OrderCancelExpired]: 'This order has expired', - [ExchangeContractErrs.OrderCancelAmountZero]: 'Order cancel amount can\'t be 0', - [ExchangeContractErrs.OrderAlreadyCancelledOrFilled]: - 'This order has already been completely filled or cancelled', - [ExchangeContractErrs.OrderFillAmountZero]: 'Order fill amount can\'t be 0', - [ExchangeContractErrs.OrderRemainingFillAmountZero]: - 'This order has already been completely filled or cancelled', - [ExchangeContractErrs.OrderFillRoundingError]: 'Rounding error will occur when filling this order', - [ExchangeContractErrs.InsufficientTakerBalance]: - 'Taker no longer has a sufficient balance to complete this order', - [ExchangeContractErrs.InsufficientTakerAllowance]: - 'Taker no longer has a sufficient allowance to complete this order', - [ExchangeContractErrs.InsufficientMakerBalance]: - 'Maker no longer has a sufficient balance to complete this order', - [ExchangeContractErrs.InsufficientMakerAllowance]: - 'Maker no longer has a sufficient allowance to complete this order', - [ExchangeContractErrs.InsufficientTakerFeeBalance]: 'Taker no longer has a sufficient balance to pay fees', - [ExchangeContractErrs.InsufficientTakerFeeAllowance]: - 'Taker no longer has a sufficient allowance to pay fees', - [ExchangeContractErrs.InsufficientMakerFeeBalance]: 'Maker no longer has a sufficient balance to pay fees', - [ExchangeContractErrs.InsufficientMakerFeeAllowance]: - 'Maker no longer has a sufficient allowance to pay fees', - [ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker]: - `This order can only be filled by ${takerAddress}`, - [ExchangeContractErrs.InsufficientRemainingFillAmount]: - 'Insufficient remaining fill amount', - }; - const humanReadableErrorMsg = exchangeContractErrorToHumanReadableError[error] || - ZeroExErrorToHumanReadableError[error]; - return humanReadableErrorMsg; - } public async validateFillOrderThrowIfInvalidAsync(signedOrder: SignedOrder, fillTakerTokenAmount: BigNumber, takerAddress: string): Promise { @@ -446,6 +509,7 @@ export class Blockchain { public destroy() { clearInterval(this.zrxPollIntervalId); this.web3Wrapper.destroy(); + // tslint:disable-next-line:no-floating-promises this.stopWatchingExchangeLogFillEventsAsync(); // fire and forget } private async showEtherScanLinkAndAwaitTransactionMinedAsync( @@ -485,7 +549,7 @@ export class Blockchain { // Start a subscription for new logs const exchangeAddress = this.getExchangeContractAddressIfExists(); - const subscriptionId = await this.zeroEx.exchange.subscribeAsync( + const subscriptionId = this.zeroEx.exchange.subscribe( ExchangeEvents.LogFill, indexFilterValues, async (err: Error, decodedLogEvent: DecodedLogEvent) => { const decodedLog = decodedLogEvent.log; @@ -493,7 +557,9 @@ export class Blockchain { // Note: it's not entirely clear from the documentation which // errors will be thrown by `watch`. For now, let's log the error // to rollbar and stop watching when one occurs + // tslint:disable-next-line:no-floating-promises errorReporter.reportAsync(err); // fire and forget + // tslint:disable-next-line:no-floating-promises this.stopWatchingExchangeLogFillEventsAsync(); // fire and forget return; } else { @@ -529,7 +595,7 @@ export class Blockchain { } } private async convertDecodedLogToFillAsync(decodedLog: LogWithDecodedArgs) { - const args = decodedLog.args as LogFillContractEventArgs; + const args = decodedLog.args; const blockTimestamp = await this.web3Wrapper.getBlockTimestampAsync(decodedLog.blockHash); const fill = { filledTakerTokenAmount: args.filledTakerTokenAmount, @@ -548,7 +614,7 @@ export class Blockchain { return fill; } private doesLogEventInvolveUser(decodedLog: LogWithDecodedArgs) { - const args = decodedLog.args as LogFillContractEventArgs; + const args = decodedLog.args; const isUserMakerOrTaker = args.maker === this.userAddress || args.taker === this.userAddress; return isUserMakerOrTaker; @@ -594,7 +660,7 @@ export class Blockchain { return tokenByAddress; } private async onPageLoadInitFireAndForgetAsync() { - await this.onPageLoadAsync(); // wait for page to load + await Blockchain.onPageLoadAsync(); // wait for page to load // Hack: We need to know the networkId the injectedWeb3 is connected to (if it is defined) in // order to properly instantiate the web3Wrapper. Since we must use the async call, we cannot @@ -612,81 +678,34 @@ export class Blockchain { } } - const provider = await this.getProviderAsync(injectedWeb3, networkId); - this.zeroEx = new ZeroEx(provider); - await this.updateProviderName(injectedWeb3); + const provider = await Blockchain.getProviderAsync(injectedWeb3, networkId); + const zeroExConfig = { + networkId: this.networkId, + }; + this.zeroEx = new ZeroEx(provider, zeroExConfig); + this.updateProviderName(injectedWeb3); const shouldPollUserAddress = true; this.web3Wrapper = new Web3Wrapper(this.dispatcher, provider, networkId, shouldPollUserAddress); - await this.postInstantiationOrUpdatingProviderZeroExAsync(); + this.postInstantiationOrUpdatingProviderZeroEx(); } // This method should always be run after instantiating or updating the provider // of the ZeroEx instance. - private async postInstantiationOrUpdatingProviderZeroExAsync() { + private postInstantiationOrUpdatingProviderZeroEx(): void { utils.assert(!_.isUndefined(this.zeroEx), 'ZeroEx must be instantiated.'); - this.exchangeAddress = await this.zeroEx.exchange.getContractAddressAsync(); + this.exchangeAddress = this.zeroEx.exchange.getContractAddress(); } - private updateProviderName(injectedWeb3: Web3) { + private updateProviderName(injectedWeb3: Web3): void { const doesInjectedWeb3Exist = !_.isUndefined(injectedWeb3); const providerName = doesInjectedWeb3Exist ? - this.getNameGivenProvider(injectedWeb3.currentProvider) : + Blockchain.getNameGivenProvider(injectedWeb3.currentProvider) : constants.PUBLIC_PROVIDER_NAME; this.dispatcher.updateInjectedProviderName(providerName); } // This is only ever called by the LedgerWallet subprovider in order to retrieve // the current networkId without this value going stale. - private getBlockchainNetworkId() { + private getBlockchainNetworkId(): number { return this.networkId; } - private async getProviderAsync(injectedWeb3: Web3, networkIdIfExists: number) { - const doesInjectedWeb3Exist = !_.isUndefined(injectedWeb3); - const publicNodeUrlsIfExistsForNetworkId = constants.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkIdIfExists]; - const isPublicNodeAvailableForNetworkId = !_.isUndefined(publicNodeUrlsIfExistsForNetworkId); - - let provider; - if (doesInjectedWeb3Exist && isPublicNodeAvailableForNetworkId) { - // We catch all requests involving a users account and send it to the injectedWeb3 - // instance. All other requests go to the public hosted node. - provider = new ProviderEngine(); - provider.addProvider(new InjectedWeb3SubProvider(injectedWeb3)); - provider.addProvider(new FilterSubprovider()); - provider.addProvider(new RedundantRPCSubprovider( - publicNodeUrlsIfExistsForNetworkId, - )); - provider.start(); - } else if (doesInjectedWeb3Exist) { - // Since no public node for this network, all requests go to injectedWeb3 instance - provider = injectedWeb3.currentProvider; - } else { - // If no injectedWeb3 instance, all requests fallback to our public hosted mainnet/testnet node - // We do this so that users can still browse the 0x Portal DApp even if they do not have web3 - // injected into their browser. - provider = new ProviderEngine(); - provider.addProvider(new FilterSubprovider()); - const networkId = configs.isMainnetEnabled ? - constants.MAINNET_NETWORK_ID : - constants.TESTNET_NETWORK_ID; - provider.addProvider(new RedundantRPCSubprovider( - constants.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkId], - )); - provider.start(); - } - - return provider; - } - private getNameGivenProvider(provider: Web3.Provider): string { - if (!_.isUndefined((provider as any).isMetaMask)) { - return constants.METAMASK_PROVIDER_NAME; - } - - // HACK: We use the fact that Parity Signer's provider is an instance of their - // internal `Web3FrameProvider` class. - const isParitySigner = _.startsWith(provider.constructor.toString(), 'function Web3FrameProvider'); - if (isParitySigner) { - return constants.PARITY_SIGNER_PROVIDER_NAME; - } - - return constants.GENERIC_PROVIDER_NAME; - } private async fetchTokenInformationAsync() { utils.assert(!_.isUndefined(this.networkId), 'Cannot call fetchTokenInformationAsync if disconnected from Ethereum node'); @@ -776,12 +795,4 @@ export class Blockchain { } } } - private async onPageLoadAsync() { - if (document.readyState === 'complete') { - return; // Already loaded - } - return new Promise((resolve, reject) => { - window.onload = resolve; - }); - } -} +} // tslint:disable:max-file-line-count diff --git a/packages/website/ts/components/fill_order.tsx b/packages/website/ts/components/fill_order.tsx index dc965283e..4077e4f8b 100644 --- a/packages/website/ts/components/fill_order.tsx +++ b/packages/website/ts/components/fill_order.tsx @@ -1,44 +1,44 @@ -import * as _ from 'lodash'; -import * as React from 'react'; +import {Order as ZeroExOrder, ZeroEx} from '0x.js'; import * as accounting from 'accounting'; -import {Link} from 'react-router-dom'; -import {ZeroEx, Order as ZeroExOrder} from '0x.js'; -import * as moment from 'moment'; import BigNumber from 'bignumber.js'; -import Paper from 'material-ui/Paper'; -import {Card, CardText, CardHeader} from 'material-ui/Card'; +import * as _ from 'lodash'; +import {Card, CardHeader, CardText} from 'material-ui/Card'; import Divider from 'material-ui/Divider'; -import TextField from 'material-ui/TextField'; +import Paper from 'material-ui/Paper'; import RaisedButton from 'material-ui/RaisedButton'; -import {utils} from 'ts/utils/utils'; -import {constants} from 'ts/utils/constants'; +import TextField from 'material-ui/TextField'; +import * as moment from 'moment'; +import * as React from 'react'; +import {Link} from 'react-router-dom'; +import {Blockchain} from 'ts/blockchain'; +import {TrackTokenConfirmationDialog} from 'ts/components/dialogs/track_token_confirmation_dialog'; +import {FillOrderJSON} from 'ts/components/fill_order_json'; +import {FillWarningDialog} from 'ts/components/fill_warning_dialog'; +import {TokenAmountInput} from 'ts/components/inputs/token_amount_input'; +import {Alert} from 'ts/components/ui/alert'; +import {EthereumAddress} from 'ts/components/ui/ethereum_address'; +import {Identicon} from 'ts/components/ui/identicon'; +import {VisualOrder} from 'ts/components/visual_order'; +import {trackedTokenStorage} from 'ts/local_storage/tracked_token_storage'; +import {Dispatcher} from 'ts/redux/dispatcher'; +import {orderSchema} from 'ts/schemas/order_schema'; +import {SchemaValidator} from 'ts/schemas/validator'; import { - Side, - TokenByAddress, - TokenStateByAddress, - Order, + AlertTypes, BlockchainErrs, + ContractResponse, + ExchangeContractErrs, + Order, OrderToken, + Side, Token, - ExchangeContractErrs, - AlertTypes, - ContractResponse, + TokenByAddress, + TokenStateByAddress, WebsitePaths, } from 'ts/types'; -import {Alert} from 'ts/components/ui/alert'; -import {Identicon} from 'ts/components/ui/identicon'; -import {EthereumAddress} from 'ts/components/ui/ethereum_address'; -import {TokenAmountInput} from 'ts/components/inputs/token_amount_input'; -import {FillWarningDialog} from 'ts/components/fill_warning_dialog'; -import {FillOrderJSON} from 'ts/components/fill_order_json'; -import {VisualOrder} from 'ts/components/visual_order'; -import {SchemaValidator} from 'ts/schemas/validator'; -import {orderSchema} from 'ts/schemas/order_schema'; -import {Dispatcher} from 'ts/redux/dispatcher'; -import {Blockchain} from 'ts/blockchain'; +import {constants} from 'ts/utils/constants'; import {errorReporter} from 'ts/utils/error_reporter'; -import {trackedTokenStorage} from 'ts/local_storage/tracked_token_storage'; -import {TrackTokenConfirmationDialog} from 'ts/components/dialogs/track_token_confirmation_dialog'; +import {utils} from 'ts/utils/utils'; const CUSTOM_LIGHT_GRAY = '#BBBBBB'; @@ -76,6 +76,31 @@ interface FillOrderState { export class FillOrder extends React.Component { private validator: SchemaValidator; + private static formatCurrencyAmount(amount: BigNumber, decimals: number): number { + const unitAmount = ZeroEx.toUnitAmount(amount, decimals); + const roundedUnitAmount = Math.round(unitAmount.toNumber() * 100000) / 100000; + return roundedUnitAmount; + } + private static renderFillSuccessMsg() { + return ( +
+ Order successfully filled. See the trade details in your{' '} + + trade history + +
+ ); + } + private static renderCancelSuccessMsg() { + return ( +
+ Order successfully cancelled. +
+ ); + } constructor(props: FillOrderProps) { super(props); this.state = { @@ -219,7 +244,7 @@ export class FillOrder extends React.Component { let orderReceiveAmount = 0; if (!_.isUndefined(this.props.orderFillAmount)) { const orderReceiveAmountBigNumber = exchangeRate.mul(this.props.orderFillAmount); - orderReceiveAmount = this.formatCurrencyAmount(orderReceiveAmountBigNumber, makerToken.decimals); + orderReceiveAmount = FillOrder.formatCurrencyAmount(orderReceiveAmountBigNumber, makerToken.decimals); } const isUserMaker = !_.isUndefined(this.state.parsedOrder) && this.state.parsedOrder.maker.address === this.props.userAddress; @@ -299,7 +324,7 @@ export class FillOrder extends React.Component { {this.state.didCancelOrderSucceed && } : @@ -316,7 +341,7 @@ export class FillOrder extends React.Component { {this.state.didFillOrderSucceed && } @@ -325,26 +350,6 @@ export class FillOrder extends React.Component { ); } - private renderFillSuccessMsg() { - return ( -
- Order successfully filled. See the trade details in your{' '} - - trade history - -
- ); - } - private renderCancelSuccessMsg() { - return ( -
- Order successfully cancelled. -
- ); - } private onFillOrderClick() { if (!this.state.isMakerTokenAddressInRegistry || !this.state.isTakerTokenAddressInRegistry) { this.setState({ @@ -563,7 +568,7 @@ export class FillOrder extends React.Component { await this.props.blockchain.validateFillOrderThrowIfInvalidAsync( signedOrder, takerFillAmount, this.props.userAddress); } catch (err) { - globalErrMsg = this.props.blockchain.toHumanReadableErrorMsg(err.message, parsedOrder.taker.address); + globalErrMsg = Blockchain.toHumanReadableErrorMsg(err.message, parsedOrder.taker.address); } } if (!_.isEmpty(globalErrMsg)) { @@ -652,7 +657,7 @@ export class FillOrder extends React.Component { await this.props.blockchain.validateCancelOrderThrowIfInvalidAsync( signedOrder, availableTakerTokenAmount); } catch (err) { - globalErrMsg = this.props.blockchain.toHumanReadableErrorMsg(err.message, parsedOrder.taker.address); + globalErrMsg = Blockchain.toHumanReadableErrorMsg(err.message, parsedOrder.taker.address); } if (!_.isEmpty(globalErrMsg)) { this.setState({ @@ -689,11 +694,6 @@ export class FillOrder extends React.Component { return; } } - private formatCurrencyAmount(amount: BigNumber, decimals: number): number { - const unitAmount = ZeroEx.toUnitAmount(amount, decimals); - const roundedUnitAmount = Math.round(unitAmount.toNumber() * 100000) / 100000; - return roundedUnitAmount; - } private onToggleTrackConfirmDialog(didConfirmTokenTracking: boolean) { if (!didConfirmTokenTracking) { this.setState({ @@ -711,4 +711,4 @@ export class FillOrder extends React.Component { tokensToTrack: [], }); } -} +} // tslint:disable:max-file-line-count diff --git a/packages/website/ts/globals.d.ts b/packages/website/ts/globals.d.ts index ee449ecfd..a7f1c1f18 100644 --- a/packages/website/ts/globals.d.ts +++ b/packages/website/ts/globals.d.ts @@ -126,7 +126,7 @@ declare function isMobile(): boolean; declare module 'is-mobile' { export = isMobile; } - +// tslint:disable:max-classes-per-file // web3-provider-engine declarations declare class Subprovider {} declare module 'web3-provider-engine/subproviders/subprovider' { @@ -148,7 +148,9 @@ declare module 'web3-provider-engine/subproviders/hooked-wallet' { declare interface Artifact { abi: any; - networks: {[networkId: number]: { - address: string; - }}; + networks: { + [networkId: number]: { + address: string; + }; + }; } diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index 2d0103499..c1dcb35c5 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -1,5 +1,5 @@ -import * as _ from 'lodash'; import BigNumber from 'bignumber.js'; +import * as _ from 'lodash'; // Utility function to create a K:V from a list of strings // Adapted from: https://basarat.gitbooks.io/typescript/content/docs/types/literal-types.html @@ -16,7 +16,7 @@ export enum GenerateOrderSteps { RemainingConfigs, SignTransaction, CopyAndShare, -}; +} export const Side = strEnum([ 'receive', @@ -45,11 +45,11 @@ export interface Token { decimals: number; isTracked: boolean; isRegistered: boolean; -}; +} export interface TokenByAddress { [address: string]: Token; -}; +} export interface TokenState { allowance: BigNumber; @@ -58,7 +58,7 @@ export interface TokenState { export interface TokenStateByAddress { [address: string]: TokenState; -}; +} export interface AssetToken { address?: string; @@ -67,14 +67,14 @@ export interface AssetToken { export interface SideToAssetToken { [side: string]: AssetToken; -}; +} export interface SignatureData { hash: string; r: string; s: string; v: number; -}; +} export interface HashData { depositAmount: BigNumber; @@ -138,7 +138,7 @@ export enum BalanceErrs { wethConversionFailed, sendFailed, allowanceSettingFailed, -}; +} export const ActionTypes = strEnum([ // Portal @@ -566,7 +566,7 @@ export interface TxParams { export interface PublicNodeUrlsByNetworkId { [networkId: number]: string[]; -}; +} export interface JSONRPCPayload { params: any[]; @@ -690,3 +690,4 @@ export enum WebsitePaths { Whitepaper = '/pdfs/0x_white_paper.pdf', SmartContracts = '/docs/contracts', } +// tslint:disable:max-file-line-count diff --git a/packages/website/ts/web3_wrapper.ts b/packages/website/ts/web3_wrapper.ts index 24279f5d2..c43436c7e 100644 --- a/packages/website/ts/web3_wrapper.ts +++ b/packages/website/ts/web3_wrapper.ts @@ -1,8 +1,8 @@ -import * as _ from 'lodash'; -import Web3 = require('web3'); import BigNumber from 'bignumber.js'; import promisify = require('es6-promisify'); +import * as _ from 'lodash'; import {Dispatcher} from 'ts/redux/dispatcher'; +import Web3 = require('web3'); export class Web3Wrapper { private dispatcher: Dispatcher; @@ -21,6 +21,7 @@ export class Web3Wrapper { this.web3 = new Web3(); this.web3.setProvider(provider); + // tslint:disable-next-line:no-floating-promises this.startEmittingNetworkConnectionAndUserBalanceStateAsync(); } public isAddress(address: string) { @@ -35,7 +36,7 @@ export class Web3Wrapper { if (_.isEmpty(addresses)) { return ''; } - return (addresses as string[])[0]; + return (addresses)[0]; } public async getNodeVersionAsync() { const nodeVersion = await promisify(this.web3.version.getNode)(); -- cgit v1.2.3 From 977a6b279467c944223f3484bd0f07078706472c Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Tue, 28 Nov 2017 11:43:42 -0600 Subject: Revert "Fix website linter errors" This reverts commit 6e2a69976b9d537e345f793e89adc74c352ad7ac. --- packages/json-schemas/package.json | 2 +- packages/website/package.json | 1 - packages/website/ts/blockchain.ts | 321 +++++++++++++------------- packages/website/ts/components/fill_order.tsx | 122 +++++----- packages/website/ts/globals.d.ts | 10 +- packages/website/ts/types.ts | 19 +- packages/website/ts/web3_wrapper.ts | 7 +- 7 files changed, 233 insertions(+), 249 deletions(-) (limited to 'packages') diff --git a/packages/json-schemas/package.json b/packages/json-schemas/package.json index 9d207a923..87194d362 100644 --- a/packages/json-schemas/package.json +++ b/packages/json-schemas/package.json @@ -5,7 +5,7 @@ "main": "lib/src/index.js", "types": "lib/src/index.d.ts", "scripts": { - "lint": "tslint --project . 'src/*.ts' 'test/*.ts'", + "lint": "tslint --project . src/*.ts test/*.ts", "test": "run-s clean build run_mocha", "test:circleci": "yarn test", "run_mocha": "mocha lib/test/**/*_test.js", diff --git a/packages/website/package.json b/packages/website/package.json index f9951defb..f91b579bd 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -3,7 +3,6 @@ "version": "0.0.0", "description": "Website and 0x portal dapp", "scripts": { - "lint": "tslint --project . 'ts/*.ts'", "build": "NODE_ENV=production webpack; exit 0;", "clean": "shx rm -f public/bundle*", "dev": "webpack-dev-server --content-base public --https", diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index 7851ea8ff..b13c48a65 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -1,60 +1,59 @@ +import * as _ from 'lodash'; +import * as React from 'react'; import { - BlockParam, - DecodedLogEvent, + ZeroEx, + ZeroExError, ExchangeContractErrs, ExchangeContractEventArgs, ExchangeEvents, + SubscriptionOpts, IndexedFilterValues, - LogCancelContractEventArgs, + DecodedLogEvent, + BlockParam, LogFillContractEventArgs, - LogWithDecodedArgs, - Order, - SignedOrder, - SubscriptionOpts, + LogCancelContractEventArgs, Token as ZeroExToken, + LogWithDecodedArgs, TransactionReceiptWithDecodedLogs, - ZeroEx, - ZeroExError, + SignedOrder, + Order, } from '0x.js'; import BigNumber from 'bignumber.js'; -import compareVersions = require('compare-versions'); +import Web3 = require('web3'); import promisify = require('es6-promisify'); -import ethUtil = require('ethereumjs-util'); import findVersions = require('find-versions'); -import * as _ from 'lodash'; -import * as React from 'react'; +import compareVersions = require('compare-versions'); import contract = require('truffle-contract'); -import {TokenSendCompleted} from 'ts/components/flash_messages/token_send_completed'; +import ethUtil = require('ethereumjs-util'); +import ProviderEngine = require('web3-provider-engine'); +import FilterSubprovider = require('web3-provider-engine/subproviders/filters'); import { TransactionSubmitted } from 'ts/components/flash_messages/transaction_submitted'; -import {trackedTokenStorage} from 'ts/local_storage/tracked_token_storage'; -import {tradeHistoryStorage} from 'ts/local_storage/trade_history_storage'; -import {Dispatcher} from 'ts/redux/dispatcher'; +import {TokenSendCompleted} from 'ts/components/flash_messages/token_send_completed'; +import {RedundantRPCSubprovider} from 'ts/subproviders/redundant_rpc_subprovider'; import {InjectedWeb3SubProvider} from 'ts/subproviders/injected_web3_subprovider'; import {ledgerWalletSubproviderFactory} from 'ts/subproviders/ledger_wallet_subprovider_factory'; -import {RedundantRPCSubprovider} from 'ts/subproviders/redundant_rpc_subprovider'; +import {Dispatcher} from 'ts/redux/dispatcher'; +import {utils} from 'ts/utils/utils'; +import {constants} from 'ts/utils/constants'; +import {configs} from 'ts/utils/configs'; import { - BlockchainCallErrs, BlockchainErrs, - ContractInstance, + Token, + SignatureData, + Side, ContractResponse, - EtherscanLinkSuffixes, - LedgerWalletSubprovider, + BlockchainCallErrs, + ContractInstance, ProviderType, - Side, - SignatureData, - Token, + LedgerWalletSubprovider, + EtherscanLinkSuffixes, TokenByAddress, TokenStateByAddress, } from 'ts/types'; -import {configs} from 'ts/utils/configs'; -import {constants} from 'ts/utils/constants'; -import {errorReporter} from 'ts/utils/error_reporter'; -import {utils} from 'ts/utils/utils'; import {Web3Wrapper} from 'ts/web3_wrapper'; -import Web3 = require('web3'); -import ProviderEngine = require('web3-provider-engine'); -import FilterSubprovider = require('web3-provider-engine/subproviders/filters'); - +import {errorReporter} from 'ts/utils/error_reporter'; +import {tradeHistoryStorage} from 'ts/local_storage/trade_history_storage'; +import {trackedTokenStorage} from 'ts/local_storage/tracked_token_storage'; import * as MintableArtifacts from '../contracts/Mintable.json'; const ALLOWANCE_TO_ZERO_GAS_AMOUNT = 45730; @@ -73,116 +72,9 @@ export class Blockchain { private cachedProvider: Web3.Provider; private ledgerSubProvider: LedgerWalletSubprovider; private zrxPollIntervalId: number; - public static toHumanReadableErrorMsg(error: ZeroExError|ExchangeContractErrs, takerAddress: string): string { - const ZeroExErrorToHumanReadableError: {[error: string]: string} = { - [ZeroExError.TokenTransferProxyContractDoesNotExist]: 'Token transfer proxy contract does not exist', - [ZeroExError.TokenContractDoesNotExist]: 'Token contract does not exist', - [ZeroExError.ZRXContractDoesNotExist]: 'ZRX contract does not exist', - [ZeroExError.EtherTokenContractDoesNotExist]: 'Ether token contract does not exist', - [ZeroExError.ExchangeContractDoesNotExist]: 'Exchange contract does not exist', - [ZeroExError.UnhandledError]: ' Unhandled error occured', - [ZeroExError.UserHasNoAssociatedAddress]: 'User has no addresses available', - [ZeroExError.InvalidSignature]: 'Order signature is not valid', - [ZeroExError.ContractNotDeployedOnNetwork]: 'Contract is not deployed on the detected network', - [ZeroExError.InvalidJump]: 'Invalid jump occured while executing the transaction', - [ZeroExError.OutOfGas]: 'Transaction ran out of gas', - [ZeroExError.NoNetworkId]: 'No network id detected', - }; - const exchangeContractErrorToHumanReadableError: {[error: string]: string} = { - [ExchangeContractErrs.OrderFillExpired]: 'This order has expired', - [ExchangeContractErrs.OrderCancelExpired]: 'This order has expired', - [ExchangeContractErrs.OrderCancelAmountZero]: 'Order cancel amount can\'t be 0', - [ExchangeContractErrs.OrderAlreadyCancelledOrFilled]: - 'This order has already been completely filled or cancelled', - [ExchangeContractErrs.OrderFillAmountZero]: 'Order fill amount can\'t be 0', - [ExchangeContractErrs.OrderRemainingFillAmountZero]: - 'This order has already been completely filled or cancelled', - [ExchangeContractErrs.OrderFillRoundingError]: 'Rounding error will occur when filling this order', - [ExchangeContractErrs.InsufficientTakerBalance]: - 'Taker no longer has a sufficient balance to complete this order', - [ExchangeContractErrs.InsufficientTakerAllowance]: - 'Taker no longer has a sufficient allowance to complete this order', - [ExchangeContractErrs.InsufficientMakerBalance]: - 'Maker no longer has a sufficient balance to complete this order', - [ExchangeContractErrs.InsufficientMakerAllowance]: - 'Maker no longer has a sufficient allowance to complete this order', - [ExchangeContractErrs.InsufficientTakerFeeBalance]: 'Taker no longer has a sufficient balance to pay fees', - [ExchangeContractErrs.InsufficientTakerFeeAllowance]: - 'Taker no longer has a sufficient allowance to pay fees', - [ExchangeContractErrs.InsufficientMakerFeeBalance]: 'Maker no longer has a sufficient balance to pay fees', - [ExchangeContractErrs.InsufficientMakerFeeAllowance]: - 'Maker no longer has a sufficient allowance to pay fees', - [ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker]: - `This order can only be filled by ${takerAddress}`, - [ExchangeContractErrs.InsufficientRemainingFillAmount]: - 'Insufficient remaining fill amount', - }; - const humanReadableErrorMsg = exchangeContractErrorToHumanReadableError[error] || - ZeroExErrorToHumanReadableError[error]; - return humanReadableErrorMsg; - } - private static async getProviderAsync(injectedWeb3: Web3, networkIdIfExists: number): Promise { - const doesInjectedWeb3Exist = !_.isUndefined(injectedWeb3); - const publicNodeUrlsIfExistsForNetworkId = constants.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkIdIfExists]; - const isPublicNodeAvailableForNetworkId = !_.isUndefined(publicNodeUrlsIfExistsForNetworkId); - - let provider; - if (doesInjectedWeb3Exist && isPublicNodeAvailableForNetworkId) { - // We catch all requests involving a users account and send it to the injectedWeb3 - // instance. All other requests go to the public hosted node. - provider = new ProviderEngine(); - provider.addProvider(new InjectedWeb3SubProvider(injectedWeb3)); - provider.addProvider(new FilterSubprovider()); - provider.addProvider(new RedundantRPCSubprovider( - publicNodeUrlsIfExistsForNetworkId, - )); - provider.start(); - } else if (doesInjectedWeb3Exist) { - // Since no public node for this network, all requests go to injectedWeb3 instance - provider = injectedWeb3.currentProvider; - } else { - // If no injectedWeb3 instance, all requests fallback to our public hosted mainnet/testnet node - // We do this so that users can still browse the 0x Portal DApp even if they do not have web3 - // injected into their browser. - provider = new ProviderEngine(); - provider.addProvider(new FilterSubprovider()); - const networkId = configs.isMainnetEnabled ? - constants.MAINNET_NETWORK_ID : - constants.TESTNET_NETWORK_ID; - provider.addProvider(new RedundantRPCSubprovider( - constants.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkId], - )); - provider.start(); - } - - return provider; - } - private static getNameGivenProvider(provider: Web3.Provider): string { - if (!_.isUndefined((provider as any).isMetaMask)) { - return constants.METAMASK_PROVIDER_NAME; - } - - // HACK: We use the fact that Parity Signer's provider is an instance of their - // internal `Web3FrameProvider` class. - const isParitySigner = _.startsWith(provider.constructor.toString(), 'function Web3FrameProvider'); - if (isParitySigner) { - return constants.PARITY_SIGNER_PROVIDER_NAME; - } - - return constants.GENERIC_PROVIDER_NAME; - } - private static async onPageLoadAsync() { - if (document.readyState === 'complete') { - return; // Already loaded - } - return new Promise((resolve, reject) => { - window.onload = resolve; - }); - } constructor(dispatcher: Dispatcher, isSalePage: boolean = false) { this.dispatcher = dispatcher; this.userAddress = ''; - // tslint:disable-next-line:no-floating-promises this.onPageLoadInitFireAndForgetAsync(); } public async networkIdUpdatedFireAndForgetAsync(newNetworkId: number) { @@ -265,8 +157,8 @@ export class Blockchain { this.web3Wrapper.destroy(); const shouldPollUserAddress = false; this.web3Wrapper = new Web3Wrapper(this.dispatcher, provider, this.networkId, shouldPollUserAddress); - this.zeroEx.setProvider(provider, this.networkId); - this.postInstantiationOrUpdatingProviderZeroEx(); + await this.zeroEx.setProviderAsync(provider); + await this.postInstantiationOrUpdatingProviderZeroExAsync(); break; } @@ -277,8 +169,8 @@ export class Blockchain { provider = this.cachedProvider; const shouldPollUserAddress = true; this.web3Wrapper = new Web3Wrapper(this.dispatcher, provider, this.networkId, shouldPollUserAddress); - this.zeroEx.setProvider(provider, this.networkId); - this.postInstantiationOrUpdatingProviderZeroEx(); + await this.zeroEx.setProviderAsync(provider); + await this.postInstantiationOrUpdatingProviderZeroExAsync(); delete this.ledgerSubProvider; delete this.cachedProvider; break; @@ -316,7 +208,7 @@ export class Blockchain { amountInBaseUnits, })); } - public portalOrderToSignedOrder(maker: string, takerIfExists: string, makerTokenAddress: string, + public portalOrderToSignedOrder(maker: string, taker: string, makerTokenAddress: string, takerTokenAddress: string, makerTokenAmount: BigNumber, takerTokenAmount: BigNumber, makerFee: BigNumber, takerFee: BigNumber, expirationUnixTimestampSec: BigNumber, @@ -324,7 +216,7 @@ export class Blockchain { signatureData: SignatureData, salt: BigNumber): SignedOrder { const ecSignature = signatureData; const exchangeContractAddress = this.getExchangeContractAddressIfExists(); - const taker = _.isEmpty(takerIfExists) ? constants.NULL_ADDRESS : takerIfExists; + taker = _.isEmpty(taker) ? constants.NULL_ADDRESS : taker; const signedOrder = { ecSignature, exchangeContractAddress, @@ -381,6 +273,51 @@ export class Blockchain { public getExchangeContractAddressIfExists() { return this.exchangeAddress; } + public toHumanReadableErrorMsg(error: ZeroExError|ExchangeContractErrs, takerAddress: string): string { + const ZeroExErrorToHumanReadableError: {[error: string]: string} = { + [ZeroExError.ContractDoesNotExist]: 'Contract does not exist', + [ZeroExError.ExchangeContractDoesNotExist]: 'Exchange contract does not exist', + [ZeroExError.UnhandledError]: ' Unhandled error occured', + [ZeroExError.UserHasNoAssociatedAddress]: 'User has no addresses available', + [ZeroExError.InvalidSignature]: 'Order signature is not valid', + [ZeroExError.ContractNotDeployedOnNetwork]: 'Contract is not deployed on the detected network', + [ZeroExError.InvalidJump]: 'Invalid jump occured while executing the transaction', + [ZeroExError.OutOfGas]: 'Transaction ran out of gas', + [ZeroExError.NoNetworkId]: 'No network id detected', + }; + const exchangeContractErrorToHumanReadableError: {[error: string]: string} = { + [ExchangeContractErrs.OrderFillExpired]: 'This order has expired', + [ExchangeContractErrs.OrderCancelExpired]: 'This order has expired', + [ExchangeContractErrs.OrderCancelAmountZero]: 'Order cancel amount can\'t be 0', + [ExchangeContractErrs.OrderAlreadyCancelledOrFilled]: + 'This order has already been completely filled or cancelled', + [ExchangeContractErrs.OrderFillAmountZero]: 'Order fill amount can\'t be 0', + [ExchangeContractErrs.OrderRemainingFillAmountZero]: + 'This order has already been completely filled or cancelled', + [ExchangeContractErrs.OrderFillRoundingError]: 'Rounding error will occur when filling this order', + [ExchangeContractErrs.InsufficientTakerBalance]: + 'Taker no longer has a sufficient balance to complete this order', + [ExchangeContractErrs.InsufficientTakerAllowance]: + 'Taker no longer has a sufficient allowance to complete this order', + [ExchangeContractErrs.InsufficientMakerBalance]: + 'Maker no longer has a sufficient balance to complete this order', + [ExchangeContractErrs.InsufficientMakerAllowance]: + 'Maker no longer has a sufficient allowance to complete this order', + [ExchangeContractErrs.InsufficientTakerFeeBalance]: 'Taker no longer has a sufficient balance to pay fees', + [ExchangeContractErrs.InsufficientTakerFeeAllowance]: + 'Taker no longer has a sufficient allowance to pay fees', + [ExchangeContractErrs.InsufficientMakerFeeBalance]: 'Maker no longer has a sufficient balance to pay fees', + [ExchangeContractErrs.InsufficientMakerFeeAllowance]: + 'Maker no longer has a sufficient allowance to pay fees', + [ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker]: + `This order can only be filled by ${takerAddress}`, + [ExchangeContractErrs.InsufficientRemainingFillAmount]: + 'Insufficient remaining fill amount', + }; + const humanReadableErrorMsg = exchangeContractErrorToHumanReadableError[error] || + ZeroExErrorToHumanReadableError[error]; + return humanReadableErrorMsg; + } public async validateFillOrderThrowIfInvalidAsync(signedOrder: SignedOrder, fillTakerTokenAmount: BigNumber, takerAddress: string): Promise { @@ -509,7 +446,6 @@ export class Blockchain { public destroy() { clearInterval(this.zrxPollIntervalId); this.web3Wrapper.destroy(); - // tslint:disable-next-line:no-floating-promises this.stopWatchingExchangeLogFillEventsAsync(); // fire and forget } private async showEtherScanLinkAndAwaitTransactionMinedAsync( @@ -549,7 +485,7 @@ export class Blockchain { // Start a subscription for new logs const exchangeAddress = this.getExchangeContractAddressIfExists(); - const subscriptionId = this.zeroEx.exchange.subscribe( + const subscriptionId = await this.zeroEx.exchange.subscribeAsync( ExchangeEvents.LogFill, indexFilterValues, async (err: Error, decodedLogEvent: DecodedLogEvent) => { const decodedLog = decodedLogEvent.log; @@ -557,9 +493,7 @@ export class Blockchain { // Note: it's not entirely clear from the documentation which // errors will be thrown by `watch`. For now, let's log the error // to rollbar and stop watching when one occurs - // tslint:disable-next-line:no-floating-promises errorReporter.reportAsync(err); // fire and forget - // tslint:disable-next-line:no-floating-promises this.stopWatchingExchangeLogFillEventsAsync(); // fire and forget return; } else { @@ -595,7 +529,7 @@ export class Blockchain { } } private async convertDecodedLogToFillAsync(decodedLog: LogWithDecodedArgs) { - const args = decodedLog.args; + const args = decodedLog.args as LogFillContractEventArgs; const blockTimestamp = await this.web3Wrapper.getBlockTimestampAsync(decodedLog.blockHash); const fill = { filledTakerTokenAmount: args.filledTakerTokenAmount, @@ -614,7 +548,7 @@ export class Blockchain { return fill; } private doesLogEventInvolveUser(decodedLog: LogWithDecodedArgs) { - const args = decodedLog.args; + const args = decodedLog.args as LogFillContractEventArgs; const isUserMakerOrTaker = args.maker === this.userAddress || args.taker === this.userAddress; return isUserMakerOrTaker; @@ -660,7 +594,7 @@ export class Blockchain { return tokenByAddress; } private async onPageLoadInitFireAndForgetAsync() { - await Blockchain.onPageLoadAsync(); // wait for page to load + await this.onPageLoadAsync(); // wait for page to load // Hack: We need to know the networkId the injectedWeb3 is connected to (if it is defined) in // order to properly instantiate the web3Wrapper. Since we must use the async call, we cannot @@ -678,34 +612,81 @@ export class Blockchain { } } - const provider = await Blockchain.getProviderAsync(injectedWeb3, networkId); - const zeroExConfig = { - networkId: this.networkId, - }; - this.zeroEx = new ZeroEx(provider, zeroExConfig); - this.updateProviderName(injectedWeb3); + const provider = await this.getProviderAsync(injectedWeb3, networkId); + this.zeroEx = new ZeroEx(provider); + await this.updateProviderName(injectedWeb3); const shouldPollUserAddress = true; this.web3Wrapper = new Web3Wrapper(this.dispatcher, provider, networkId, shouldPollUserAddress); - this.postInstantiationOrUpdatingProviderZeroEx(); + await this.postInstantiationOrUpdatingProviderZeroExAsync(); } // This method should always be run after instantiating or updating the provider // of the ZeroEx instance. - private postInstantiationOrUpdatingProviderZeroEx(): void { + private async postInstantiationOrUpdatingProviderZeroExAsync() { utils.assert(!_.isUndefined(this.zeroEx), 'ZeroEx must be instantiated.'); - this.exchangeAddress = this.zeroEx.exchange.getContractAddress(); + this.exchangeAddress = await this.zeroEx.exchange.getContractAddressAsync(); } - private updateProviderName(injectedWeb3: Web3): void { + private updateProviderName(injectedWeb3: Web3) { const doesInjectedWeb3Exist = !_.isUndefined(injectedWeb3); const providerName = doesInjectedWeb3Exist ? - Blockchain.getNameGivenProvider(injectedWeb3.currentProvider) : + this.getNameGivenProvider(injectedWeb3.currentProvider) : constants.PUBLIC_PROVIDER_NAME; this.dispatcher.updateInjectedProviderName(providerName); } // This is only ever called by the LedgerWallet subprovider in order to retrieve // the current networkId without this value going stale. - private getBlockchainNetworkId(): number { + private getBlockchainNetworkId() { return this.networkId; } + private async getProviderAsync(injectedWeb3: Web3, networkIdIfExists: number) { + const doesInjectedWeb3Exist = !_.isUndefined(injectedWeb3); + const publicNodeUrlsIfExistsForNetworkId = constants.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkIdIfExists]; + const isPublicNodeAvailableForNetworkId = !_.isUndefined(publicNodeUrlsIfExistsForNetworkId); + + let provider; + if (doesInjectedWeb3Exist && isPublicNodeAvailableForNetworkId) { + // We catch all requests involving a users account and send it to the injectedWeb3 + // instance. All other requests go to the public hosted node. + provider = new ProviderEngine(); + provider.addProvider(new InjectedWeb3SubProvider(injectedWeb3)); + provider.addProvider(new FilterSubprovider()); + provider.addProvider(new RedundantRPCSubprovider( + publicNodeUrlsIfExistsForNetworkId, + )); + provider.start(); + } else if (doesInjectedWeb3Exist) { + // Since no public node for this network, all requests go to injectedWeb3 instance + provider = injectedWeb3.currentProvider; + } else { + // If no injectedWeb3 instance, all requests fallback to our public hosted mainnet/testnet node + // We do this so that users can still browse the 0x Portal DApp even if they do not have web3 + // injected into their browser. + provider = new ProviderEngine(); + provider.addProvider(new FilterSubprovider()); + const networkId = configs.isMainnetEnabled ? + constants.MAINNET_NETWORK_ID : + constants.TESTNET_NETWORK_ID; + provider.addProvider(new RedundantRPCSubprovider( + constants.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkId], + )); + provider.start(); + } + + return provider; + } + private getNameGivenProvider(provider: Web3.Provider): string { + if (!_.isUndefined((provider as any).isMetaMask)) { + return constants.METAMASK_PROVIDER_NAME; + } + + // HACK: We use the fact that Parity Signer's provider is an instance of their + // internal `Web3FrameProvider` class. + const isParitySigner = _.startsWith(provider.constructor.toString(), 'function Web3FrameProvider'); + if (isParitySigner) { + return constants.PARITY_SIGNER_PROVIDER_NAME; + } + + return constants.GENERIC_PROVIDER_NAME; + } private async fetchTokenInformationAsync() { utils.assert(!_.isUndefined(this.networkId), 'Cannot call fetchTokenInformationAsync if disconnected from Ethereum node'); @@ -795,4 +776,12 @@ export class Blockchain { } } } -} // tslint:disable:max-file-line-count + private async onPageLoadAsync() { + if (document.readyState === 'complete') { + return; // Already loaded + } + return new Promise((resolve, reject) => { + window.onload = resolve; + }); + } +} diff --git a/packages/website/ts/components/fill_order.tsx b/packages/website/ts/components/fill_order.tsx index 4077e4f8b..dc965283e 100644 --- a/packages/website/ts/components/fill_order.tsx +++ b/packages/website/ts/components/fill_order.tsx @@ -1,44 +1,44 @@ -import {Order as ZeroExOrder, ZeroEx} from '0x.js'; +import * as _ from 'lodash'; +import * as React from 'react'; import * as accounting from 'accounting'; +import {Link} from 'react-router-dom'; +import {ZeroEx, Order as ZeroExOrder} from '0x.js'; +import * as moment from 'moment'; import BigNumber from 'bignumber.js'; -import * as _ from 'lodash'; -import {Card, CardHeader, CardText} from 'material-ui/Card'; -import Divider from 'material-ui/Divider'; import Paper from 'material-ui/Paper'; -import RaisedButton from 'material-ui/RaisedButton'; +import {Card, CardText, CardHeader} from 'material-ui/Card'; +import Divider from 'material-ui/Divider'; import TextField from 'material-ui/TextField'; -import * as moment from 'moment'; -import * as React from 'react'; -import {Link} from 'react-router-dom'; -import {Blockchain} from 'ts/blockchain'; -import {TrackTokenConfirmationDialog} from 'ts/components/dialogs/track_token_confirmation_dialog'; -import {FillOrderJSON} from 'ts/components/fill_order_json'; -import {FillWarningDialog} from 'ts/components/fill_warning_dialog'; -import {TokenAmountInput} from 'ts/components/inputs/token_amount_input'; -import {Alert} from 'ts/components/ui/alert'; -import {EthereumAddress} from 'ts/components/ui/ethereum_address'; -import {Identicon} from 'ts/components/ui/identicon'; -import {VisualOrder} from 'ts/components/visual_order'; -import {trackedTokenStorage} from 'ts/local_storage/tracked_token_storage'; -import {Dispatcher} from 'ts/redux/dispatcher'; -import {orderSchema} from 'ts/schemas/order_schema'; -import {SchemaValidator} from 'ts/schemas/validator'; +import RaisedButton from 'material-ui/RaisedButton'; +import {utils} from 'ts/utils/utils'; +import {constants} from 'ts/utils/constants'; import { - AlertTypes, - BlockchainErrs, - ContractResponse, - ExchangeContractErrs, - Order, - OrderToken, Side, - Token, TokenByAddress, TokenStateByAddress, + Order, + BlockchainErrs, + OrderToken, + Token, + ExchangeContractErrs, + AlertTypes, + ContractResponse, WebsitePaths, } from 'ts/types'; -import {constants} from 'ts/utils/constants'; +import {Alert} from 'ts/components/ui/alert'; +import {Identicon} from 'ts/components/ui/identicon'; +import {EthereumAddress} from 'ts/components/ui/ethereum_address'; +import {TokenAmountInput} from 'ts/components/inputs/token_amount_input'; +import {FillWarningDialog} from 'ts/components/fill_warning_dialog'; +import {FillOrderJSON} from 'ts/components/fill_order_json'; +import {VisualOrder} from 'ts/components/visual_order'; +import {SchemaValidator} from 'ts/schemas/validator'; +import {orderSchema} from 'ts/schemas/order_schema'; +import {Dispatcher} from 'ts/redux/dispatcher'; +import {Blockchain} from 'ts/blockchain'; import {errorReporter} from 'ts/utils/error_reporter'; -import {utils} from 'ts/utils/utils'; +import {trackedTokenStorage} from 'ts/local_storage/tracked_token_storage'; +import {TrackTokenConfirmationDialog} from 'ts/components/dialogs/track_token_confirmation_dialog'; const CUSTOM_LIGHT_GRAY = '#BBBBBB'; @@ -76,31 +76,6 @@ interface FillOrderState { export class FillOrder extends React.Component { private validator: SchemaValidator; - private static formatCurrencyAmount(amount: BigNumber, decimals: number): number { - const unitAmount = ZeroEx.toUnitAmount(amount, decimals); - const roundedUnitAmount = Math.round(unitAmount.toNumber() * 100000) / 100000; - return roundedUnitAmount; - } - private static renderFillSuccessMsg() { - return ( -
- Order successfully filled. See the trade details in your{' '} - - trade history - -
- ); - } - private static renderCancelSuccessMsg() { - return ( -
- Order successfully cancelled. -
- ); - } constructor(props: FillOrderProps) { super(props); this.state = { @@ -244,7 +219,7 @@ export class FillOrder extends React.Component { let orderReceiveAmount = 0; if (!_.isUndefined(this.props.orderFillAmount)) { const orderReceiveAmountBigNumber = exchangeRate.mul(this.props.orderFillAmount); - orderReceiveAmount = FillOrder.formatCurrencyAmount(orderReceiveAmountBigNumber, makerToken.decimals); + orderReceiveAmount = this.formatCurrencyAmount(orderReceiveAmountBigNumber, makerToken.decimals); } const isUserMaker = !_.isUndefined(this.state.parsedOrder) && this.state.parsedOrder.maker.address === this.props.userAddress; @@ -324,7 +299,7 @@ export class FillOrder extends React.Component { {this.state.didCancelOrderSucceed && } : @@ -341,7 +316,7 @@ export class FillOrder extends React.Component { {this.state.didFillOrderSucceed && } @@ -350,6 +325,26 @@ export class FillOrder extends React.Component { ); } + private renderFillSuccessMsg() { + return ( +
+ Order successfully filled. See the trade details in your{' '} + + trade history + +
+ ); + } + private renderCancelSuccessMsg() { + return ( +
+ Order successfully cancelled. +
+ ); + } private onFillOrderClick() { if (!this.state.isMakerTokenAddressInRegistry || !this.state.isTakerTokenAddressInRegistry) { this.setState({ @@ -568,7 +563,7 @@ export class FillOrder extends React.Component { await this.props.blockchain.validateFillOrderThrowIfInvalidAsync( signedOrder, takerFillAmount, this.props.userAddress); } catch (err) { - globalErrMsg = Blockchain.toHumanReadableErrorMsg(err.message, parsedOrder.taker.address); + globalErrMsg = this.props.blockchain.toHumanReadableErrorMsg(err.message, parsedOrder.taker.address); } } if (!_.isEmpty(globalErrMsg)) { @@ -657,7 +652,7 @@ export class FillOrder extends React.Component { await this.props.blockchain.validateCancelOrderThrowIfInvalidAsync( signedOrder, availableTakerTokenAmount); } catch (err) { - globalErrMsg = Blockchain.toHumanReadableErrorMsg(err.message, parsedOrder.taker.address); + globalErrMsg = this.props.blockchain.toHumanReadableErrorMsg(err.message, parsedOrder.taker.address); } if (!_.isEmpty(globalErrMsg)) { this.setState({ @@ -694,6 +689,11 @@ export class FillOrder extends React.Component { return; } } + private formatCurrencyAmount(amount: BigNumber, decimals: number): number { + const unitAmount = ZeroEx.toUnitAmount(amount, decimals); + const roundedUnitAmount = Math.round(unitAmount.toNumber() * 100000) / 100000; + return roundedUnitAmount; + } private onToggleTrackConfirmDialog(didConfirmTokenTracking: boolean) { if (!didConfirmTokenTracking) { this.setState({ @@ -711,4 +711,4 @@ export class FillOrder extends React.Component { tokensToTrack: [], }); } -} // tslint:disable:max-file-line-count +} diff --git a/packages/website/ts/globals.d.ts b/packages/website/ts/globals.d.ts index a7f1c1f18..ee449ecfd 100644 --- a/packages/website/ts/globals.d.ts +++ b/packages/website/ts/globals.d.ts @@ -126,7 +126,7 @@ declare function isMobile(): boolean; declare module 'is-mobile' { export = isMobile; } -// tslint:disable:max-classes-per-file + // web3-provider-engine declarations declare class Subprovider {} declare module 'web3-provider-engine/subproviders/subprovider' { @@ -148,9 +148,7 @@ declare module 'web3-provider-engine/subproviders/hooked-wallet' { declare interface Artifact { abi: any; - networks: { - [networkId: number]: { - address: string; - }; - }; + networks: {[networkId: number]: { + address: string; + }}; } diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index c1dcb35c5..2d0103499 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -1,5 +1,5 @@ -import BigNumber from 'bignumber.js'; import * as _ from 'lodash'; +import BigNumber from 'bignumber.js'; // Utility function to create a K:V from a list of strings // Adapted from: https://basarat.gitbooks.io/typescript/content/docs/types/literal-types.html @@ -16,7 +16,7 @@ export enum GenerateOrderSteps { RemainingConfigs, SignTransaction, CopyAndShare, -} +}; export const Side = strEnum([ 'receive', @@ -45,11 +45,11 @@ export interface Token { decimals: number; isTracked: boolean; isRegistered: boolean; -} +}; export interface TokenByAddress { [address: string]: Token; -} +}; export interface TokenState { allowance: BigNumber; @@ -58,7 +58,7 @@ export interface TokenState { export interface TokenStateByAddress { [address: string]: TokenState; -} +}; export interface AssetToken { address?: string; @@ -67,14 +67,14 @@ export interface AssetToken { export interface SideToAssetToken { [side: string]: AssetToken; -} +}; export interface SignatureData { hash: string; r: string; s: string; v: number; -} +}; export interface HashData { depositAmount: BigNumber; @@ -138,7 +138,7 @@ export enum BalanceErrs { wethConversionFailed, sendFailed, allowanceSettingFailed, -} +}; export const ActionTypes = strEnum([ // Portal @@ -566,7 +566,7 @@ export interface TxParams { export interface PublicNodeUrlsByNetworkId { [networkId: number]: string[]; -} +}; export interface JSONRPCPayload { params: any[]; @@ -690,4 +690,3 @@ export enum WebsitePaths { Whitepaper = '/pdfs/0x_white_paper.pdf', SmartContracts = '/docs/contracts', } -// tslint:disable:max-file-line-count diff --git a/packages/website/ts/web3_wrapper.ts b/packages/website/ts/web3_wrapper.ts index c43436c7e..24279f5d2 100644 --- a/packages/website/ts/web3_wrapper.ts +++ b/packages/website/ts/web3_wrapper.ts @@ -1,8 +1,8 @@ +import * as _ from 'lodash'; +import Web3 = require('web3'); import BigNumber from 'bignumber.js'; import promisify = require('es6-promisify'); -import * as _ from 'lodash'; import {Dispatcher} from 'ts/redux/dispatcher'; -import Web3 = require('web3'); export class Web3Wrapper { private dispatcher: Dispatcher; @@ -21,7 +21,6 @@ export class Web3Wrapper { this.web3 = new Web3(); this.web3.setProvider(provider); - // tslint:disable-next-line:no-floating-promises this.startEmittingNetworkConnectionAndUserBalanceStateAsync(); } public isAddress(address: string) { @@ -36,7 +35,7 @@ export class Web3Wrapper { if (_.isEmpty(addresses)) { return ''; } - return (addresses)[0]; + return (addresses as string[])[0]; } public async getNodeVersionAsync() { const nodeVersion = await promisify(this.web3.version.getNode)(); -- cgit v1.2.3 From 36b21e6e7b8033117c9a1313c1294682184462f8 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Tue, 28 Nov 2017 11:44:10 -0600 Subject: Add fake gas estimate suprovider for tests --- packages/0x.js/src/contract.ts | 2 +- .../src/subproviders/empty_wallet_subprovider.ts | 27 ------------------- packages/0x.js/test/utils/constants.ts | 1 + .../utils/subproviders/empty_wallet_subprovider.ts | 27 +++++++++++++++++++ .../subproviders/fake_gas_estimate_subprovider.ts | 31 ++++++++++++++++++++++ packages/0x.js/test/utils/web3_factory.ts | 4 ++- 6 files changed, 63 insertions(+), 29 deletions(-) delete mode 100644 packages/0x.js/src/subproviders/empty_wallet_subprovider.ts create mode 100644 packages/0x.js/test/utils/subproviders/empty_wallet_subprovider.ts create mode 100644 packages/0x.js/test/utils/subproviders/fake_gas_estimate_subprovider.ts (limited to 'packages') diff --git a/packages/0x.js/src/contract.ts b/packages/0x.js/src/contract.ts index 25a0609aa..0cd3a315f 100644 --- a/packages/0x.js/src/contract.ts +++ b/packages/0x.js/src/contract.ts @@ -58,7 +58,7 @@ export class Contract implements Web3.ContractInstance { const promise = new Promise(async (resolve, reject) => { const lastArg = args[args.length - 1]; let txData: Partial = {}; - if (this.isTxData(lastArg)) { + if (!_.isUndefined(lastArg) && this.isTxData(lastArg)) { txData = args.pop(); } // Gas amount sourced with the following priorities: diff --git a/packages/0x.js/src/subproviders/empty_wallet_subprovider.ts b/packages/0x.js/src/subproviders/empty_wallet_subprovider.ts deleted file mode 100644 index 2993bc801..000000000 --- a/packages/0x.js/src/subproviders/empty_wallet_subprovider.ts +++ /dev/null @@ -1,27 +0,0 @@ -import {JSONRPCPayload} from '../types'; - -/* - * This class implements the web3-provider-engine subprovider interface and returns - * that the provider has no addresses when queried. - * Source: https://github.com/MetaMask/provider-engine/blob/master/subproviders/subprovider.js - */ -export class EmptyWalletSubProvider { - // This method needs to be here to satisfy the interface but linter wants it to be static. - // tslint:disable-next-line:prefer-function-over-method - public handleRequest(payload: JSONRPCPayload, next: () => void, end: (err: Error|null, result: any) => void) { - switch (payload.method) { - case 'eth_accounts': - end(null, []); - return; - - default: - next(); - return; - } - } - // Required to implement this method despite not needing it for this subprovider - // tslint:disable-next-line:prefer-function-over-method - public setEngine(engine: any) { - // noop - } -} diff --git a/packages/0x.js/test/utils/constants.ts b/packages/0x.js/test/utils/constants.ts index 212abf4d6..75fdf49c9 100644 --- a/packages/0x.js/test/utils/constants.ts +++ b/packages/0x.js/test/utils/constants.ts @@ -8,4 +8,5 @@ export const constants = { KOVAN_RPC_URL: 'https://kovan.infura.io', ROPSTEN_RPC_URL: 'https://ropsten.infura.io', ZRX_DECIMALS: 18, + GAS_ESTIMATE: 500000, }; diff --git a/packages/0x.js/test/utils/subproviders/empty_wallet_subprovider.ts b/packages/0x.js/test/utils/subproviders/empty_wallet_subprovider.ts new file mode 100644 index 000000000..bc11e56d8 --- /dev/null +++ b/packages/0x.js/test/utils/subproviders/empty_wallet_subprovider.ts @@ -0,0 +1,27 @@ +import {JSONRPCPayload} from '../../../src/types'; + +/* + * This class implements the web3-provider-engine subprovider interface and returns + * that the provider has no addresses when queried. + * Source: https://github.com/MetaMask/provider-engine/blob/master/subproviders/subprovider.js + */ +export class EmptyWalletSubProvider { + // This method needs to be here to satisfy the interface but linter wants it to be static. + // tslint:disable-next-line:prefer-function-over-method + public handleRequest(payload: JSONRPCPayload, next: () => void, end: (err: Error|null, result: any) => void) { + switch (payload.method) { + case 'eth_accounts': + end(null, []); + return; + + default: + next(); + return; + } + } + // Required to implement this method despite not needing it for this subprovider + // tslint:disable-next-line:prefer-function-over-method + public setEngine(engine: any) { + // noop + } +} diff --git a/packages/0x.js/test/utils/subproviders/fake_gas_estimate_subprovider.ts b/packages/0x.js/test/utils/subproviders/fake_gas_estimate_subprovider.ts new file mode 100644 index 000000000..5eccb3d24 --- /dev/null +++ b/packages/0x.js/test/utils/subproviders/fake_gas_estimate_subprovider.ts @@ -0,0 +1,31 @@ +import {JSONRPCPayload} from '../../../src/types'; + +/* + * This class implements the web3-provider-engine subprovider interface and returns + * the constant gas estimate when queried. + * Source: https://github.com/MetaMask/provider-engine/blob/master/subproviders/subprovider.js + */ +export class FakeGasEstimateProvider { + private constantGasAmount: number; + constructor(constantGasAmount: number) { + this.constantGasAmount = constantGasAmount; + } + // This method needs to be here to satisfy the interface but linter wants it to be static. + // tslint:disable-next-line:prefer-function-over-method + public handleRequest(payload: JSONRPCPayload, next: () => void, end: (err: Error|null, result: any) => void) { + switch (payload.method) { + case 'eth_estimateGas': + end(null, this.constantGasAmount); + return; + + default: + next(); + return; + } + } + // Required to implement this method despite not needing it for this subprovider + // tslint:disable-next-line:prefer-function-over-method + public setEngine(engine: any) { + // noop + } +} diff --git a/packages/0x.js/test/utils/web3_factory.ts b/packages/0x.js/test/utils/web3_factory.ts index b4bf1acd3..854c6091d 100644 --- a/packages/0x.js/test/utils/web3_factory.ts +++ b/packages/0x.js/test/utils/web3_factory.ts @@ -7,7 +7,8 @@ import * as Web3 from 'web3'; import ProviderEngine = require('web3-provider-engine'); import RpcSubprovider = require('web3-provider-engine/subproviders/rpc'); -import {EmptyWalletSubProvider} from '../../src/subproviders/empty_wallet_subprovider'; +import {EmptyWalletSubProvider} from './subproviders/empty_wallet_subprovider'; +import {FakeGasEstimateProvider} from './subproviders/fake_gas_estimate_subprovider'; import {constants} from './constants'; @@ -24,6 +25,7 @@ export const web3Factory = { if (!hasAddresses) { provider.addProvider(new EmptyWalletSubProvider()); } + provider.addProvider(new FakeGasEstimateProvider(constants.GAS_ESTIMATE)); provider.addProvider(new RpcSubprovider({ rpcUrl, })); -- cgit v1.2.3 From d6a9e7520da63b9163894bb5aa191a5dfc83ce40 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Tue, 28 Nov 2017 11:51:10 -0600 Subject: Remove gas params from tests --- packages/0x.js/test/exchange_wrapper_test.ts | 6 ------ 1 file changed, 6 deletions(-) (limited to 'packages') diff --git a/packages/0x.js/test/exchange_wrapper_test.ts b/packages/0x.js/test/exchange_wrapper_test.ts index d862c347d..f6c823cc4 100644 --- a/packages/0x.js/test/exchange_wrapper_test.ts +++ b/packages/0x.js/test/exchange_wrapper_test.ts @@ -124,7 +124,6 @@ describe('ExchangeWrapper', () => { it('should not validate when orderTransactionOptions specify not to validate', async () => { return expect(zeroEx.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress, { shouldValidate: false, - gasLimit: 200000, // If we don't pass this gas estimation will fail })).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); }); }); @@ -184,7 +183,6 @@ describe('ExchangeWrapper', () => { it('should not validate when orderTransactionOptions specify not to validate', async () => { return expect(zeroEx.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress, { shouldValidate: false, - gasLimit: 200000, // If we don't pass this gas estimation will fail })).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); }); }); @@ -364,7 +362,6 @@ describe('ExchangeWrapper', () => { return expect(zeroEx.exchange.batchFillOrdersAsync( orderFillBatch, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, { shouldValidate: false, - gasLimit: 200000, // If we don't pass this gas estimation will fail })).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); }); }); @@ -422,7 +419,6 @@ describe('ExchangeWrapper', () => { return expect(zeroEx.exchange.fillOrdersUpToAsync( signedOrders, emptyFillUpToAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, { shouldValidate: false, - gasLimit: 200000, // If we don't pass this gas estimation will fail })).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); }); }); @@ -471,7 +467,6 @@ describe('ExchangeWrapper', () => { it('should not validate when orderTransactionOptions specify not to validate', async () => { return expect(zeroEx.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount, { shouldValidate: false, - gasLimit: 200000, // If we don't pass this gas estimation will fail })).to.not.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); }); }); @@ -547,7 +542,6 @@ describe('ExchangeWrapper', () => { it('should not validate when orderTransactionOptions specify not to validate', async () => { return expect(zeroEx.exchange.batchCancelOrdersAsync(cancelBatch, { shouldValidate: false, - gasLimit: 200000, // If we don't pass this gas estimation will fail })).to.not.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); }); }); -- cgit v1.2.3 From d5e58ecdd88d4e85bd2e98b091e500b37ca66bfe Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Tue, 28 Nov 2017 12:05:32 -0600 Subject: Pin testrpc version --- packages/0x.js/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages') diff --git a/packages/0x.js/package.json b/packages/0x.js/package.json index c542fa2df..a8e99bacd 100644 --- a/packages/0x.js/package.json +++ b/packages/0x.js/package.json @@ -61,7 +61,7 @@ "copyfiles": "^1.2.0", "coveralls": "^3.0.0", "dirty-chai": "^2.0.1", - "ethereumjs-testrpc": "^6.0.3", + "ethereumjs-testrpc": "6.0.3", "json-loader": "^0.5.4", "mocha": "^4.0.0", "npm-run-all": "^4.0.2", -- cgit v1.2.3 From 0c5a9fbc2c615002cc0ce3d8f9c4b79a6fd3b5a2 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Tue, 28 Nov 2017 12:06:11 -0600 Subject: Fix a typo --- packages/0x.js/src/contract.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages') diff --git a/packages/0x.js/src/contract.ts b/packages/0x.js/src/contract.ts index 0cd3a315f..872f18b2a 100644 --- a/packages/0x.js/src/contract.ts +++ b/packages/0x.js/src/contract.ts @@ -62,7 +62,7 @@ export class Contract implements Web3.ContractInstance { txData = args.pop(); } // Gas amount sourced with the following priorities: - // 1. Optional param passed it to public method call + // 1. Optional param passed in to public method call // 2. Global config passed in at library instantiation // 3. Gas estimate calculation + safety margin const removeUndefinedProperties = _.pickBy; -- cgit v1.2.3 From 1d584b1329427e0cf756f9312c54a1ed7a7d4b34 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Tue, 28 Nov 2017 12:10:06 -0600 Subject: Remove a comment --- packages/0x.js/src/types.ts | 1 - 1 file changed, 1 deletion(-) (limited to 'packages') diff --git a/packages/0x.js/src/types.ts b/packages/0x.js/src/types.ts index d187af01a..3949c17c5 100644 --- a/packages/0x.js/src/types.ts +++ b/packages/0x.js/src/types.ts @@ -477,7 +477,6 @@ export interface ValidateOrderFillableOpts { * let's the user query the blockchain's state at an arbitrary point in time. In order for this to work, the * backing Ethereum node must keep the entire historical state of the chain (e.g setting `--pruning=archive` * flag when running Parity). - * gasPrice: Gas price to use for a transaction in */ export interface MethodOpts { defaultBlock?: Web3.BlockParam; -- cgit v1.2.3 From 50bf6a991d75fab76fd5d5e2078d805528a32b45 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Tue, 28 Nov 2017 12:12:25 -0600 Subject: Normalise subprovider names --- .../0x.js/test/utils/subproviders/empty_wallet_subprovider.ts | 2 +- .../test/utils/subproviders/fake_gas_estimate_subprovider.ts | 2 +- packages/0x.js/test/utils/web3_factory.ts | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'packages') diff --git a/packages/0x.js/test/utils/subproviders/empty_wallet_subprovider.ts b/packages/0x.js/test/utils/subproviders/empty_wallet_subprovider.ts index bc11e56d8..e5e279873 100644 --- a/packages/0x.js/test/utils/subproviders/empty_wallet_subprovider.ts +++ b/packages/0x.js/test/utils/subproviders/empty_wallet_subprovider.ts @@ -5,7 +5,7 @@ import {JSONRPCPayload} from '../../../src/types'; * that the provider has no addresses when queried. * Source: https://github.com/MetaMask/provider-engine/blob/master/subproviders/subprovider.js */ -export class EmptyWalletSubProvider { +export class EmptyWalletSubprovider { // This method needs to be here to satisfy the interface but linter wants it to be static. // tslint:disable-next-line:prefer-function-over-method public handleRequest(payload: JSONRPCPayload, next: () => void, end: (err: Error|null, result: any) => void) { diff --git a/packages/0x.js/test/utils/subproviders/fake_gas_estimate_subprovider.ts b/packages/0x.js/test/utils/subproviders/fake_gas_estimate_subprovider.ts index 5eccb3d24..14a6b33cf 100644 --- a/packages/0x.js/test/utils/subproviders/fake_gas_estimate_subprovider.ts +++ b/packages/0x.js/test/utils/subproviders/fake_gas_estimate_subprovider.ts @@ -5,7 +5,7 @@ import {JSONRPCPayload} from '../../../src/types'; * the constant gas estimate when queried. * Source: https://github.com/MetaMask/provider-engine/blob/master/subproviders/subprovider.js */ -export class FakeGasEstimateProvider { +export class FakeGasEstimateSubprovider { private constantGasAmount: number; constructor(constantGasAmount: number) { this.constantGasAmount = constantGasAmount; diff --git a/packages/0x.js/test/utils/web3_factory.ts b/packages/0x.js/test/utils/web3_factory.ts index 854c6091d..da4828943 100644 --- a/packages/0x.js/test/utils/web3_factory.ts +++ b/packages/0x.js/test/utils/web3_factory.ts @@ -7,8 +7,8 @@ import * as Web3 from 'web3'; import ProviderEngine = require('web3-provider-engine'); import RpcSubprovider = require('web3-provider-engine/subproviders/rpc'); -import {EmptyWalletSubProvider} from './subproviders/empty_wallet_subprovider'; -import {FakeGasEstimateProvider} from './subproviders/fake_gas_estimate_subprovider'; +import {EmptyWalletSubprovider} from './subproviders/empty_wallet_subprovider'; +import {FakeGasEstimateSubprovider} from './subproviders/fake_gas_estimate_subprovider'; import {constants} from './constants'; @@ -23,9 +23,9 @@ export const web3Factory = { const provider = new ProviderEngine(); const rpcUrl = `http://${constants.RPC_HOST}:${constants.RPC_PORT}`; if (!hasAddresses) { - provider.addProvider(new EmptyWalletSubProvider()); + provider.addProvider(new EmptyWalletSubprovider()); } - provider.addProvider(new FakeGasEstimateProvider(constants.GAS_ESTIMATE)); + provider.addProvider(new FakeGasEstimateSubprovider(constants.GAS_ESTIMATE)); provider.addProvider(new RpcSubprovider({ rpcUrl, })); -- cgit v1.2.3 From 9f5e724aec9c7fcc3ab2de4fff6d4ef8a1782080 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Tue, 28 Nov 2017 12:13:50 -0600 Subject: Add a HACK comment --- .../0x.js/test/utils/subproviders/fake_gas_estimate_subprovider.ts | 3 +++ 1 file changed, 3 insertions(+) (limited to 'packages') diff --git a/packages/0x.js/test/utils/subproviders/fake_gas_estimate_subprovider.ts b/packages/0x.js/test/utils/subproviders/fake_gas_estimate_subprovider.ts index 14a6b33cf..059163f2e 100644 --- a/packages/0x.js/test/utils/subproviders/fake_gas_estimate_subprovider.ts +++ b/packages/0x.js/test/utils/subproviders/fake_gas_estimate_subprovider.ts @@ -3,6 +3,9 @@ import {JSONRPCPayload} from '../../../src/types'; /* * This class implements the web3-provider-engine subprovider interface and returns * the constant gas estimate when queried. + * HACK: We need this so that our tests don't use testrpc gas estimation which sometimes kills the node. + * Source: https://github.com/trufflesuite/ganache-cli/issues/417 + * Source: https://github.com/trufflesuite/ganache-cli/issues/437 * Source: https://github.com/MetaMask/provider-engine/blob/master/subproviders/subprovider.js */ export class FakeGasEstimateSubprovider { -- cgit v1.2.3 From bead76a5b7eeb7d9bdaa0a677c366d2618feb425 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Tue, 28 Nov 2017 12:16:02 -0600 Subject: Add CHANGELOG comment --- packages/0x.js/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) (limited to 'packages') diff --git a/packages/0x.js/CHANGELOG.md b/packages/0x.js/CHANGELOG.md index 7fce9167a..8f9444da6 100644 --- a/packages/0x.js/CHANGELOG.md +++ b/packages/0x.js/CHANGELOG.md @@ -9,6 +9,7 @@ vx.x.x * Make `DecodedLogEvent
` contain `LogWithDecodedArgs` under log key instead of merging it in like web3 does (#234) * Rename `removed` to `isRemoved` in `DecodedLogEvent` (#234) * Add config allowing to specify gasPrice and gasLimit for every transaction sending method (#235) + * All transaction sending methods now call `estimateGas` if no gas amount was supplied (#235) * Modify order validation methods to validate against the `latest` block, not against the `pending` block (#236) v0.26.0 -- cgit v1.2.3 From 353abffba9afd73d70fce47fee2f2d6067c6c8ae Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Tue, 28 Nov 2017 12:23:41 -0600 Subject: Improve the comment --- packages/0x.js/src/contract.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'packages') diff --git a/packages/0x.js/src/contract.ts b/packages/0x.js/src/contract.ts index 872f18b2a..a4ee03910 100644 --- a/packages/0x.js/src/contract.ts +++ b/packages/0x.js/src/contract.ts @@ -5,7 +5,9 @@ import * as Web3 from 'web3'; import {AbiType} from './types'; -const GAS_MARGIN = 300000; +// HACK: Gas estimates on testrpc don't take into account gas refunds. +// Our calls can trigger max 8 gas refunds for SSTORE per transaction for 15k gas each which gives 120k. +const GAS_MARGIN = 120000; export class Contract implements Web3.ContractInstance { public address: string; -- cgit v1.2.3