You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
226 lines
5.1 KiB
226 lines
5.1 KiB
const {
|
|
addCurrencies,
|
|
conversionUtil,
|
|
conversionGTE,
|
|
multiplyCurrencies,
|
|
conversionGreaterThan,
|
|
} = require('../../conversion-util')
|
|
const {
|
|
calcTokenAmount,
|
|
} = require('../../token-util')
|
|
const {
|
|
INSUFFICIENT_FUNDS_ERROR,
|
|
INSUFFICIENT_TOKENS_ERROR,
|
|
NEGATIVE_ETH_ERROR,
|
|
ONE_GWEI_IN_WEI_HEX,
|
|
} = require('./send.constants')
|
|
const abi = require('ethereumjs-abi')
|
|
|
|
module.exports = {
|
|
calcGasTotal,
|
|
doesAmountErrorRequireUpdate,
|
|
estimateGas,
|
|
estimateGasPriceFromRecentBlocks,
|
|
generateTokenTransferData,
|
|
getAmountErrorObject,
|
|
getParamsForGasEstimate,
|
|
calcTokenBalance,
|
|
isBalanceSufficient,
|
|
isTokenBalanceSufficient,
|
|
}
|
|
|
|
function calcGasTotal (gasLimit, gasPrice) {
|
|
return multiplyCurrencies(gasLimit, gasPrice, {
|
|
toNumericBase: 'hex',
|
|
multiplicandBase: 16,
|
|
multiplierBase: 16,
|
|
})
|
|
}
|
|
|
|
function isBalanceSufficient ({
|
|
amount = '0x0',
|
|
amountConversionRate = 0,
|
|
balance,
|
|
conversionRate,
|
|
gasTotal = '0x0',
|
|
primaryCurrency,
|
|
}) {
|
|
const totalAmount = addCurrencies(amount, gasTotal, {
|
|
aBase: 16,
|
|
bBase: 16,
|
|
toNumericBase: 'hex',
|
|
})
|
|
|
|
const balanceIsSufficient = conversionGTE(
|
|
{
|
|
value: balance,
|
|
fromNumericBase: 'hex',
|
|
fromCurrency: primaryCurrency,
|
|
conversionRate,
|
|
},
|
|
{
|
|
value: totalAmount,
|
|
fromNumericBase: 'hex',
|
|
conversionRate: Number(amountConversionRate) || conversionRate,
|
|
fromCurrency: primaryCurrency,
|
|
},
|
|
)
|
|
|
|
return balanceIsSufficient
|
|
}
|
|
|
|
function isTokenBalanceSufficient ({
|
|
amount = '0x0',
|
|
tokenBalance,
|
|
decimals,
|
|
}) {
|
|
const amountInDec = conversionUtil(amount, {
|
|
fromNumericBase: 'hex',
|
|
})
|
|
|
|
const tokenBalanceIsSufficient = conversionGTE(
|
|
{
|
|
value: tokenBalance,
|
|
fromNumericBase: 'dec',
|
|
},
|
|
{
|
|
value: calcTokenAmount(amountInDec, decimals),
|
|
fromNumericBase: 'dec',
|
|
},
|
|
)
|
|
|
|
return tokenBalanceIsSufficient
|
|
}
|
|
|
|
function getAmountErrorObject ({
|
|
amount,
|
|
amountConversionRate,
|
|
balance,
|
|
conversionRate,
|
|
gasTotal,
|
|
primaryCurrency,
|
|
selectedToken,
|
|
tokenBalance,
|
|
}) {
|
|
let insufficientFunds = false
|
|
if (gasTotal && conversionRate) {
|
|
insufficientFunds = !isBalanceSufficient({
|
|
amount: selectedToken ? '0x0' : amount,
|
|
amountConversionRate,
|
|
balance,
|
|
conversionRate,
|
|
gasTotal,
|
|
primaryCurrency,
|
|
})
|
|
}
|
|
|
|
let inSufficientTokens = false
|
|
if (selectedToken && tokenBalance !== null) {
|
|
const { decimals } = selectedToken
|
|
inSufficientTokens = !isTokenBalanceSufficient({
|
|
tokenBalance,
|
|
amount,
|
|
decimals,
|
|
})
|
|
}
|
|
|
|
const amountLessThanZero = conversionGreaterThan(
|
|
{ value: 0, fromNumericBase: 'dec' },
|
|
{ value: amount, fromNumericBase: 'hex' },
|
|
)
|
|
|
|
let amountError = null
|
|
|
|
if (insufficientFunds) {
|
|
amountError = INSUFFICIENT_FUNDS_ERROR
|
|
} else if (inSufficientTokens) {
|
|
amountError = INSUFFICIENT_TOKENS_ERROR
|
|
} else if (amountLessThanZero) {
|
|
amountError = NEGATIVE_ETH_ERROR
|
|
}
|
|
|
|
return { amount: amountError }
|
|
}
|
|
|
|
function getParamsForGasEstimate (selectedAddress, selectedToken, data) {
|
|
const { symbol } = selectedToken || {}
|
|
const estimatedGasParams = {
|
|
from: selectedAddress,
|
|
gas: '746a528800',
|
|
}
|
|
|
|
if (symbol) {
|
|
Object.assign(estimatedGasParams, { value: '0x0' })
|
|
}
|
|
|
|
if (data) {
|
|
Object.assign(estimatedGasParams, { data })
|
|
}
|
|
|
|
return estimatedGasParams
|
|
}
|
|
|
|
function calcTokenBalance ({ selectedToken, usersToken }) {
|
|
const { decimals } = selectedToken || {}
|
|
return calcTokenAmount(usersToken.balance.toString(), decimals) + ''
|
|
}
|
|
|
|
function doesAmountErrorRequireUpdate ({
|
|
balance,
|
|
gasTotal,
|
|
prevBalance,
|
|
prevGasTotal,
|
|
prevTokenBalance,
|
|
selectedToken,
|
|
tokenBalance,
|
|
}) {
|
|
const balanceHasChanged = balance !== prevBalance
|
|
const gasTotalHasChange = gasTotal !== prevGasTotal
|
|
const tokenBalanceHasChanged = selectedToken && tokenBalance !== prevTokenBalance
|
|
const amountErrorRequiresUpdate = balanceHasChanged || gasTotalHasChange || tokenBalanceHasChanged
|
|
|
|
return amountErrorRequiresUpdate
|
|
}
|
|
|
|
function estimateGas (params = {}) {
|
|
return new Promise((resolve, reject) => {
|
|
global.ethQuery.estimateGas(params, (err, data) => {
|
|
if (err) {
|
|
return reject(err)
|
|
}
|
|
return resolve(data)
|
|
})
|
|
})
|
|
}
|
|
|
|
function generateTokenTransferData (selectedAddress, selectedToken) {
|
|
if (!selectedToken) return
|
|
console.log(`abi.rawEncode`, abi.rawEncode)
|
|
return Array.prototype.map.call(
|
|
abi.rawEncode(['address', 'uint256'], [selectedAddress, '0x0']),
|
|
x => ('00' + x.toString(16)).slice(-2)
|
|
).join('')
|
|
}
|
|
|
|
function hexComparator (a, b) {
|
|
return conversionGreaterThan(
|
|
{ value: a, fromNumericBase: 'hex' },
|
|
{ value: b, fromNumericBase: 'hex' },
|
|
) ? 1 : -1
|
|
}
|
|
|
|
function estimateGasPriceFromRecentBlocks (recentBlocks) {
|
|
// Return 1 gwei if no blocks have been observed:
|
|
if (!recentBlocks || recentBlocks.length === 0) {
|
|
return ONE_GWEI_IN_WEI_HEX
|
|
}
|
|
const lowestPrices = recentBlocks.map((block) => {
|
|
if (!block.gasPrices || block.gasPrices.length < 1) {
|
|
return ONE_GWEI_IN_WEI_HEX
|
|
}
|
|
return block.gasPrices
|
|
.sort(hexComparator)[0]
|
|
})
|
|
.sort(hexComparator)
|
|
return lowestPrices[Math.floor(lowestPrices.length / 2)]
|
|
}
|
|
|