// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // // SPDX-License-Identifier: BSD-3-Clause import abi from '@parity/shared/lib/contracts/abi/eip20'; import BigNumber from 'bignumber.js'; import { makeContract$ } from '@parity/light.js'; import memoize from 'lodash/memoize'; import { toWei } from '@parity/api/lib/util/wei'; import Debug from './debug'; const debug = Debug('estimateGas'); const GAS_MULT_FACTOR = 1.25; // Since estimateGas is not always accurate, we add a 33% factor for buffer. export const contractForToken = memoize(tokenAddress => makeContract$(tokenAddress, abi) ); /** * Estimate the amount of gas for our transaction. */ export const estimateGas = (tx, token, api) => { if (!tx || !Object.keys(tx).length) { return Promise.reject(new Error('Tx not set in sendStore.')); } if (token.address === 'ETH') { return estimateGasForEth(txForEth(tx), api).then(addBuffer); } else { return estimateGasForErc20(txForErc20(tx, token), token).then(addBuffer); } }; /** * Estimate gas to transfer in ERC20 contract. Expensive function, so we * memoize it. */ const estimateGasForErc20 = memoize((txForErc20, token) => { debug(`Estimating gas for tx on token contract.`, token, txForErc20); return contractForToken( token.address ).contractObject.instance.transfer.estimateGas( txForErc20.options, txForErc20.args ); }, JSON.stringify); /** * Estimate gas to transfer to an ETH address. Expensive function, so we * memoize it. */ const estimateGasForEth = memoize((txForEth, api) => { debug(`Estimating gas for tx.`, txForEth); return api.eth.estimateGas(txForEth); }, JSON.stringify); /** * Add some extra gas buffer just to be sure user has enough balance. * * @param {BigNumber} estimated - The estimated gas price returned by * estimateGas. */ const addBuffer = estimated => { const withBuffer = estimated.mul(GAS_MULT_FACTOR); debug(`Estimated gas ${+estimated}, with buffer ${+withBuffer}.`); return withBuffer; }; /** * This.tx is a user-friendly tx object. We convert it now as it can be * passed to makeContract$.transfer(...). */ export const txForErc20 = (tx, token) => { return { args: [ tx.to, new BigNumber(tx.amount).mul(new BigNumber(10).pow(token.decimals)) ], options: { gasPrice: toWei(tx.gasPrice, 'shannon') // shannon == gwei } }; }; /** * This.tx is a user-friendly tx object. We convert it now as it can be * passed to post$(tx). */ export const txForEth = tx => { return { gasPrice: toWei(tx.gasPrice, 'shannon'), // shannon == gwei to: tx.to, value: toWei(tx.amount.toString()) }; };