Update calculateGasPrice to fetch from config (#1517)
* Update calculateGasPrice to fetch from config * Remove unused gas estimation function * Remove unused gasCost estimate function * Add gasParameter to gasPriceOracle
This commit is contained in:
parent
c9e9fe8ca6
commit
5f3a9ef1a9
|
@ -1,5 +1,5 @@
|
||||||
import networks from 'src/config/networks'
|
import networks from 'src/config/networks'
|
||||||
import { EnvironmentSettings, ETHEREUM_NETWORK, NetworkSettings, SafeFeatures, Wallets } from 'src/config/networks/network.d'
|
import { EnvironmentSettings, ETHEREUM_NETWORK, NetworkSettings, SafeFeatures, Wallets, GasPriceOracle } from 'src/config/networks/network.d'
|
||||||
import { APP_ENV, ETHERSCAN_API_KEY, GOOGLE_ANALYTICS_ID, INFURA_TOKEN, NETWORK, NODE_ENV } from 'src/utils/constants'
|
import { APP_ENV, ETHERSCAN_API_KEY, GOOGLE_ANALYTICS_ID, INFURA_TOKEN, NETWORK, NODE_ENV } from 'src/utils/constants'
|
||||||
import { ensureOnce } from 'src/utils/singleton'
|
import { ensureOnce } from 'src/utils/singleton'
|
||||||
import memoize from 'lodash.memoize'
|
import memoize from 'lodash.memoize'
|
||||||
|
@ -63,6 +63,10 @@ export const getRelayUrl = (): string | undefined => getConfig()?.relayApiUrl
|
||||||
|
|
||||||
export const getGnosisSafeAppsUrl = (): string => getConfig()?.safeAppsUrl
|
export const getGnosisSafeAppsUrl = (): string => getConfig()?.safeAppsUrl
|
||||||
|
|
||||||
|
export const getGasPrice = (): number | undefined => getConfig()?.gasPrice
|
||||||
|
|
||||||
|
export const getGasPriceOracle = (): GasPriceOracle | undefined => getConfig()?.gasPriceOracle
|
||||||
|
|
||||||
export const getRpcServiceUrl = (): string => {
|
export const getRpcServiceUrl = (): string => {
|
||||||
const usesInfuraRPC = [ETHEREUM_NETWORK.MAINNET, ETHEREUM_NETWORK.RINKEBY].includes(getNetworkId())
|
const usesInfuraRPC = [ETHEREUM_NETWORK.MAINNET, ETHEREUM_NETWORK.RINKEBY].includes(getNetworkId())
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,10 @@ const baseConfig: EnvironmentSettings = {
|
||||||
txServiceUrl: 'http://localhost:8000/api/v1',
|
txServiceUrl: 'http://localhost:8000/api/v1',
|
||||||
relayApiUrl: 'https://safe-relay.staging.gnosisdev.com/api/v1',
|
relayApiUrl: 'https://safe-relay.staging.gnosisdev.com/api/v1',
|
||||||
safeAppsUrl: 'http://localhost:3002',
|
safeAppsUrl: 'http://localhost:3002',
|
||||||
gasPriceOracleUrl: 'https://ethgasstation.info/json/ethgasAPI.json',
|
gasPriceOracle: {
|
||||||
|
url: 'https://ethgasstation.info/json/ethgasAPI.json',
|
||||||
|
gasParameter: 'average',
|
||||||
|
},
|
||||||
rpcServiceUrl: 'http://localhost:4447',
|
rpcServiceUrl: 'http://localhost:4447',
|
||||||
networkExplorerName: 'Etherscan',
|
networkExplorerName: 'Etherscan',
|
||||||
networkExplorerUrl: 'https://rinkeby.etherscan.io',
|
networkExplorerUrl: 'https://rinkeby.etherscan.io',
|
||||||
|
|
|
@ -4,7 +4,10 @@ import { EnvironmentSettings, ETHEREUM_NETWORK, NetworkConfig } from 'src/config
|
||||||
const baseConfig: EnvironmentSettings = {
|
const baseConfig: EnvironmentSettings = {
|
||||||
txServiceUrl: 'https://safe-transaction.mainnet.staging.gnosisdev.com/api/v1',
|
txServiceUrl: 'https://safe-transaction.mainnet.staging.gnosisdev.com/api/v1',
|
||||||
safeAppsUrl: 'https://safe-apps.dev.gnosisdev.com',
|
safeAppsUrl: 'https://safe-apps.dev.gnosisdev.com',
|
||||||
gasPriceOracleUrl: 'https://ethgasstation.info/json/ethgasAPI.json',
|
gasPriceOracle: {
|
||||||
|
url: 'https://ethgasstation.info/json/ethgasAPI.json',
|
||||||
|
gasParameter: 'average',
|
||||||
|
},
|
||||||
rpcServiceUrl: 'https://mainnet.infura.io:443/v3',
|
rpcServiceUrl: 'https://mainnet.infura.io:443/v3',
|
||||||
networkExplorerName: 'Etherscan',
|
networkExplorerName: 'Etherscan',
|
||||||
networkExplorerUrl: 'https://etherscan.io',
|
networkExplorerUrl: 'https://etherscan.io',
|
||||||
|
|
|
@ -64,14 +64,20 @@ export type SafeFeatures = FEATURES[]
|
||||||
|
|
||||||
export type Wallets = WALLETS[]
|
export type Wallets = WALLETS[]
|
||||||
|
|
||||||
|
export type GasPriceOracle = {
|
||||||
|
url: string
|
||||||
|
// Different gas api providers can use a different name to reflect different gas levels based on tx speed
|
||||||
|
// For example in ethGasStation for ETHEREUM_MAINNET = safeLow | average | fast
|
||||||
|
gasParameter: string
|
||||||
|
}
|
||||||
|
|
||||||
type GasPrice = {
|
type GasPrice = {
|
||||||
gasPrice: number
|
gasPrice: number
|
||||||
gasPriceOracleUrl?: string
|
gasPriceOracle?: GasPriceOracle
|
||||||
} | {
|
} | {
|
||||||
gasPrice?: number
|
gasPrice?: number
|
||||||
// for infura there's a REST API Token required stored in: `REACT_APP_INFURA_TOKEN`
|
// for infura there's a REST API Token required stored in: `REACT_APP_INFURA_TOKEN`
|
||||||
gasPriceOracleUrl: string
|
gasPriceOracle: GasPriceOracle
|
||||||
}
|
}
|
||||||
|
|
||||||
export type EnvironmentSettings = GasPrice & {
|
export type EnvironmentSettings = GasPrice & {
|
||||||
|
|
|
@ -4,7 +4,10 @@ import { EnvironmentSettings, ETHEREUM_NETWORK, NetworkConfig } from 'src/config
|
||||||
const baseConfig: EnvironmentSettings = {
|
const baseConfig: EnvironmentSettings = {
|
||||||
txServiceUrl: 'https://safe-transaction.staging.gnosisdev.com/api/v1',
|
txServiceUrl: 'https://safe-transaction.staging.gnosisdev.com/api/v1',
|
||||||
safeAppsUrl: 'https://safe-apps.dev.gnosisdev.com',
|
safeAppsUrl: 'https://safe-apps.dev.gnosisdev.com',
|
||||||
gasPriceOracleUrl: 'https://ethgasstation.info/json/ethgasAPI.json',
|
gasPriceOracle: {
|
||||||
|
url: 'https://ethgasstation.info/json/ethgasAPI.json',
|
||||||
|
gasParameter: 'average',
|
||||||
|
},
|
||||||
rpcServiceUrl: 'https://rinkeby.infura.io:443/v3',
|
rpcServiceUrl: 'https://rinkeby.infura.io:443/v3',
|
||||||
networkExplorerName: 'Etherscan',
|
networkExplorerName: 'Etherscan',
|
||||||
networkExplorerUrl: 'https://rinkeby.etherscan.io',
|
networkExplorerUrl: 'https://rinkeby.etherscan.io',
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
import axios from 'axios'
|
|
||||||
|
|
||||||
import { getRelayUrl } from 'src/config/index'
|
|
||||||
|
|
||||||
export const estimateTxGas = (safeAddress, to, value, data, operation = 0) => {
|
|
||||||
const apiUrl = getRelayUrl()
|
|
||||||
const url = `${apiUrl}/safes/${safeAddress}/transactions/estimate/`
|
|
||||||
// const estimationValue = isTokenTransfer(tx.data) ? '0' : value.toString(10)
|
|
||||||
|
|
||||||
return axios.post(url, {
|
|
||||||
safe: safeAddress,
|
|
||||||
to,
|
|
||||||
data: '0x',
|
|
||||||
value,
|
|
||||||
operation,
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
export * from './estimateTxGas'
|
|
|
@ -16,7 +16,7 @@ import {
|
||||||
saveTxToHistory,
|
saveTxToHistory,
|
||||||
tryOffchainSigning,
|
tryOffchainSigning,
|
||||||
} from 'src/logic/safe/transactions'
|
} from 'src/logic/safe/transactions'
|
||||||
import { estimateSafeTxGas } from 'src/logic/safe/transactions/gasNew'
|
import { estimateSafeTxGas } from 'src/logic/safe/transactions/gas'
|
||||||
import { getCurrentSafeVersion } from 'src/logic/safe/utils/safeVersion'
|
import { getCurrentSafeVersion } from 'src/logic/safe/utils/safeVersion'
|
||||||
import { ZERO_ADDRESS } from 'src/logic/wallets/ethAddresses'
|
import { ZERO_ADDRESS } from 'src/logic/wallets/ethAddresses'
|
||||||
import { EMPTY_DATA } from 'src/logic/wallets/ethTransactions'
|
import { EMPTY_DATA } from 'src/logic/wallets/ethTransactions'
|
||||||
|
|
|
@ -1,36 +1,168 @@
|
||||||
import { getGnosisSafeInstanceAt } from 'src/logic/contracts/safeContracts'
|
import GnosisSafeSol from '@gnosis.pm/safe-contracts/build/contracts/GnosisSafe.json'
|
||||||
|
import { BigNumber } from 'bignumber.js'
|
||||||
|
import { AbiItem } from 'web3-utils'
|
||||||
|
|
||||||
export const calculateTxFee = async (safe, safeAddress, from, data, to, valueInWei, operation) => {
|
import { CALL } from '.'
|
||||||
|
|
||||||
|
import { getGnosisSafeInstanceAt } from 'src/logic/contracts/safeContracts'
|
||||||
|
import { generateSignaturesFromTxConfirmations } from 'src/logic/safe/safeTxSigner'
|
||||||
|
import { Transaction } from 'src/logic/safe/store/models/types/transaction'
|
||||||
|
import { ZERO_ADDRESS } from 'src/logic/wallets/ethAddresses'
|
||||||
|
import { EMPTY_DATA, calculateGasOf, calculateGasPrice } from 'src/logic/wallets/ethTransactions'
|
||||||
|
import { getAccountFrom, getWeb3 } from 'src/logic/wallets/getWeb3'
|
||||||
|
import { GnosisSafe } from 'src/types/contracts/GnosisSafe.d'
|
||||||
|
|
||||||
|
const estimateDataGasCosts = (data: string): number => {
|
||||||
|
const reducer = (accumulator, currentValue) => {
|
||||||
|
if (currentValue === EMPTY_DATA) {
|
||||||
|
return accumulator + 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentValue === '00') {
|
||||||
|
return accumulator + 4
|
||||||
|
}
|
||||||
|
|
||||||
|
return accumulator + 16
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.match(/.{2}/g)?.reduce(reducer, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const estimateTxGasCosts = async (
|
||||||
|
safeAddress: string,
|
||||||
|
to: string,
|
||||||
|
data: string,
|
||||||
|
tx?: Transaction,
|
||||||
|
preApprovingOwner?: string,
|
||||||
|
): Promise<number> => {
|
||||||
|
try {
|
||||||
|
const web3 = getWeb3()
|
||||||
|
const from = await getAccountFrom(web3)
|
||||||
|
|
||||||
|
if (!from) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
const safeInstance = (new web3.eth.Contract(GnosisSafeSol.abi as AbiItem[], safeAddress) as unknown) as GnosisSafe
|
||||||
|
const nonce = await safeInstance.methods.nonce().call()
|
||||||
|
const threshold = await safeInstance.methods.getThreshold().call()
|
||||||
|
const isExecution = tx?.confirmations.size === Number(threshold) || !!preApprovingOwner || threshold === '1'
|
||||||
|
|
||||||
|
let txData
|
||||||
|
if (isExecution) {
|
||||||
|
// https://docs.gnosis.io/safe/docs/docs5/#pre-validated-signatures
|
||||||
|
const signatures = tx?.confirmations
|
||||||
|
? generateSignaturesFromTxConfirmations(tx.confirmations, preApprovingOwner)
|
||||||
|
: `0x000000000000000000000000${from.replace(
|
||||||
|
'0x',
|
||||||
|
'',
|
||||||
|
)}000000000000000000000000000000000000000000000000000000000000000001`
|
||||||
|
txData = await safeInstance.methods
|
||||||
|
.execTransaction(
|
||||||
|
to,
|
||||||
|
tx?.value || 0,
|
||||||
|
data,
|
||||||
|
CALL,
|
||||||
|
tx?.safeTxGas || 0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
ZERO_ADDRESS,
|
||||||
|
ZERO_ADDRESS,
|
||||||
|
signatures,
|
||||||
|
)
|
||||||
|
.encodeABI()
|
||||||
|
} else {
|
||||||
|
const txHash = await safeInstance.methods
|
||||||
|
.getTransactionHash(to, tx?.value || 0, data, CALL, 0, 0, 0, ZERO_ADDRESS, ZERO_ADDRESS, nonce)
|
||||||
|
.call({
|
||||||
|
from,
|
||||||
|
})
|
||||||
|
txData = await safeInstance.methods.approveHash(txHash).encodeABI()
|
||||||
|
}
|
||||||
|
|
||||||
|
const gas = await calculateGasOf(txData, from, safeAddress)
|
||||||
|
const gasPrice = await calculateGasPrice()
|
||||||
|
|
||||||
|
return gas * parseInt(gasPrice, 10)
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error while estimating transaction execution gas costs:')
|
||||||
|
console.error(err)
|
||||||
|
|
||||||
|
return 10000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const estimateSafeTxGas = async (
|
||||||
|
safe: GnosisSafe | undefined,
|
||||||
|
safeAddress: string,
|
||||||
|
data: string,
|
||||||
|
to: string,
|
||||||
|
valueInWei: string,
|
||||||
|
operation: number,
|
||||||
|
): Promise<number> => {
|
||||||
try {
|
try {
|
||||||
let safeInstance = safe
|
let safeInstance = safe
|
||||||
if (!safeInstance) {
|
if (!safeInstance) {
|
||||||
safeInstance = await getGnosisSafeInstanceAt(safeAddress)
|
safeInstance = await getGnosisSafeInstanceAt(safeAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://docs.gnosis.io/safe/docs/docs5/#pre-validated-signatures
|
const web3 = await getWeb3()
|
||||||
const sigs = `0x000000000000000000000000${from.replace(
|
const estimateData = safeInstance.methods.requiredTxGas(to, valueInWei, data, operation).encodeABI()
|
||||||
'0x',
|
const estimateResponse = await web3.eth.call({
|
||||||
'',
|
to: safeAddress,
|
||||||
)}000000000000000000000000000000000000000000000000000000000000000001`
|
from: safeAddress,
|
||||||
|
data: estimateData,
|
||||||
|
})
|
||||||
|
const txGasEstimation = new BigNumber(estimateResponse.substring(138), 16).toNumber() + 10000
|
||||||
|
|
||||||
// we get gas limit from this call, then it needs to be multiplied by the gas price
|
// 21000 - additional gas costs (e.g. base tx costs, transfer costs)
|
||||||
// https://safe-relay.gnosis.pm/api/v1/gas-station/
|
const dataGasEstimation = estimateDataGasCosts(estimateData) + 21000
|
||||||
// https://safe-relay.rinkeby.gnosis.pm/api/v1/about/
|
const additionalGasBatches = [10000, 20000, 40000, 80000, 160000, 320000, 640000, 1280000, 2560000, 5120000]
|
||||||
const estimate = await safeInstance.execTransaction.estimateGas(
|
|
||||||
to,
|
const batch = new web3.BatchRequest()
|
||||||
valueInWei,
|
const estimationRequests = additionalGasBatches.map(
|
||||||
data,
|
(additionalGas) =>
|
||||||
operation,
|
new Promise((resolve) => {
|
||||||
0,
|
// there are no type definitions for .request, so for now ts-ignore is there
|
||||||
0,
|
// Issue link: https://github.com/ethereum/web3.js/issues/3144
|
||||||
0,
|
// eslint-disable-next-line
|
||||||
'0x0000000000000000000000000000000000000000',
|
// @ts-ignore
|
||||||
'0x0000000000000000000000000000000000000000',
|
const request = web3.eth.call.request(
|
||||||
sigs,
|
{
|
||||||
{ from: '0xbc2BB26a6d821e69A38016f3858561a1D80d4182' },
|
to: safeAddress,
|
||||||
|
from: safeAddress,
|
||||||
|
data: estimateData,
|
||||||
|
gasPrice: 0,
|
||||||
|
gasLimit: txGasEstimation + dataGasEstimation + additionalGas,
|
||||||
|
},
|
||||||
|
(error, res) => {
|
||||||
|
// res.data check is for OpenEthereum/Parity revert messages format
|
||||||
|
const isOpenEthereumRevertMsg = res && typeof res.data === 'string'
|
||||||
|
|
||||||
|
const isEstimationSuccessful =
|
||||||
|
!error &&
|
||||||
|
((typeof res === 'string' && res !== '0x') || (isOpenEthereumRevertMsg && res.data.slice(9) !== '0x'))
|
||||||
|
|
||||||
|
resolve({
|
||||||
|
success: isEstimationSuccessful,
|
||||||
|
estimation: txGasEstimation + additionalGas,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
batch.add(request)
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
|
batch.execute()
|
||||||
|
|
||||||
return estimate
|
const estimationResponses = await Promise.all(estimationRequests)
|
||||||
|
const firstSuccessfulRequest: any = estimationResponses.find((res: any) => res.success)
|
||||||
|
|
||||||
|
if (firstSuccessfulRequest) {
|
||||||
|
return firstSuccessfulRequest.estimation
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error calculating tx gas estimation', error)
|
console.error('Error calculating tx gas estimation', error)
|
||||||
return 0
|
return 0
|
||||||
|
|
|
@ -1,170 +0,0 @@
|
||||||
import GnosisSafeSol from '@gnosis.pm/safe-contracts/build/contracts/GnosisSafe.json'
|
|
||||||
import { BigNumber } from 'bignumber.js'
|
|
||||||
import { AbiItem } from 'web3-utils'
|
|
||||||
|
|
||||||
import { CALL } from '.'
|
|
||||||
|
|
||||||
import { getGnosisSafeInstanceAt } from 'src/logic/contracts/safeContracts'
|
|
||||||
import { generateSignaturesFromTxConfirmations } from 'src/logic/safe/safeTxSigner'
|
|
||||||
import { Transaction } from 'src/logic/safe/store/models/types/transaction'
|
|
||||||
import { ZERO_ADDRESS } from 'src/logic/wallets/ethAddresses'
|
|
||||||
import { EMPTY_DATA, calculateGasOf, calculateGasPrice } from 'src/logic/wallets/ethTransactions'
|
|
||||||
import { getAccountFrom, getWeb3 } from 'src/logic/wallets/getWeb3'
|
|
||||||
import { GnosisSafe } from 'src/types/contracts/GnosisSafe.d'
|
|
||||||
|
|
||||||
const estimateDataGasCosts = (data: string): number => {
|
|
||||||
const reducer = (accumulator, currentValue) => {
|
|
||||||
if (currentValue === EMPTY_DATA) {
|
|
||||||
return accumulator + 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentValue === '00') {
|
|
||||||
return accumulator + 4
|
|
||||||
}
|
|
||||||
|
|
||||||
return accumulator + 16
|
|
||||||
}
|
|
||||||
|
|
||||||
return data.match(/.{2}/g)?.reduce(reducer, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const estimateTxGasCosts = async (
|
|
||||||
safeAddress: string,
|
|
||||||
to: string,
|
|
||||||
data: string,
|
|
||||||
tx?: Transaction,
|
|
||||||
preApprovingOwner?: string,
|
|
||||||
): Promise<number> => {
|
|
||||||
try {
|
|
||||||
const web3 = getWeb3()
|
|
||||||
const from = await getAccountFrom(web3)
|
|
||||||
|
|
||||||
if (!from) {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
const safeInstance = (new web3.eth.Contract(GnosisSafeSol.abi as AbiItem[], safeAddress) as unknown) as GnosisSafe
|
|
||||||
const nonce = await safeInstance.methods.nonce().call()
|
|
||||||
const threshold = await safeInstance.methods.getThreshold().call()
|
|
||||||
const isExecution = tx?.confirmations.size === Number(threshold) || !!preApprovingOwner || threshold === '1'
|
|
||||||
|
|
||||||
let txData
|
|
||||||
if (isExecution) {
|
|
||||||
// https://docs.gnosis.io/safe/docs/docs5/#pre-validated-signatures
|
|
||||||
const signatures = tx?.confirmations
|
|
||||||
? generateSignaturesFromTxConfirmations(tx.confirmations, preApprovingOwner)
|
|
||||||
: `0x000000000000000000000000${from.replace(
|
|
||||||
'0x',
|
|
||||||
'',
|
|
||||||
)}000000000000000000000000000000000000000000000000000000000000000001`
|
|
||||||
txData = await safeInstance.methods
|
|
||||||
.execTransaction(
|
|
||||||
to,
|
|
||||||
tx?.value || 0,
|
|
||||||
data,
|
|
||||||
CALL,
|
|
||||||
tx?.safeTxGas || 0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
ZERO_ADDRESS,
|
|
||||||
ZERO_ADDRESS,
|
|
||||||
signatures,
|
|
||||||
)
|
|
||||||
.encodeABI()
|
|
||||||
} else {
|
|
||||||
const txHash = await safeInstance.methods
|
|
||||||
.getTransactionHash(to, tx?.value || 0, data, CALL, 0, 0, 0, ZERO_ADDRESS, ZERO_ADDRESS, nonce)
|
|
||||||
.call({
|
|
||||||
from,
|
|
||||||
})
|
|
||||||
txData = await safeInstance.methods.approveHash(txHash).encodeABI()
|
|
||||||
}
|
|
||||||
|
|
||||||
const gas = await calculateGasOf(txData, from, safeAddress)
|
|
||||||
const gasPrice = await calculateGasPrice()
|
|
||||||
|
|
||||||
return gas * parseInt(gasPrice, 10)
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error while estimating transaction execution gas costs:')
|
|
||||||
console.error(err)
|
|
||||||
|
|
||||||
return 10000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const estimateSafeTxGas = async (
|
|
||||||
safe: GnosisSafe | undefined,
|
|
||||||
safeAddress: string,
|
|
||||||
data: string,
|
|
||||||
to: string,
|
|
||||||
valueInWei: string,
|
|
||||||
operation: number,
|
|
||||||
): Promise<number> => {
|
|
||||||
try {
|
|
||||||
let safeInstance = safe
|
|
||||||
if (!safeInstance) {
|
|
||||||
safeInstance = await getGnosisSafeInstanceAt(safeAddress)
|
|
||||||
}
|
|
||||||
|
|
||||||
const web3 = await getWeb3()
|
|
||||||
const estimateData = safeInstance.methods.requiredTxGas(to, valueInWei, data, operation).encodeABI()
|
|
||||||
const estimateResponse = await web3.eth.call({
|
|
||||||
to: safeAddress,
|
|
||||||
from: safeAddress,
|
|
||||||
data: estimateData,
|
|
||||||
})
|
|
||||||
const txGasEstimation = new BigNumber(estimateResponse.substring(138), 16).toNumber() + 10000
|
|
||||||
|
|
||||||
// 21000 - additional gas costs (e.g. base tx costs, transfer costs)
|
|
||||||
const dataGasEstimation = estimateDataGasCosts(estimateData) + 21000
|
|
||||||
const additionalGasBatches = [10000, 20000, 40000, 80000, 160000, 320000, 640000, 1280000, 2560000, 5120000]
|
|
||||||
|
|
||||||
const batch = new web3.BatchRequest()
|
|
||||||
const estimationRequests = additionalGasBatches.map(
|
|
||||||
(additionalGas) =>
|
|
||||||
new Promise((resolve) => {
|
|
||||||
// there are no type definitions for .request, so for now ts-ignore is there
|
|
||||||
// Issue link: https://github.com/ethereum/web3.js/issues/3144
|
|
||||||
// eslint-disable-next-line
|
|
||||||
// @ts-ignore
|
|
||||||
const request = web3.eth.call.request(
|
|
||||||
{
|
|
||||||
to: safeAddress,
|
|
||||||
from: safeAddress,
|
|
||||||
data: estimateData,
|
|
||||||
gasPrice: 0,
|
|
||||||
gasLimit: txGasEstimation + dataGasEstimation + additionalGas,
|
|
||||||
},
|
|
||||||
(error, res) => {
|
|
||||||
// res.data check is for OpenEthereum/Parity revert messages format
|
|
||||||
const isOpenEthereumRevertMsg = res && typeof res.data === 'string'
|
|
||||||
|
|
||||||
const isEstimationSuccessful =
|
|
||||||
!error &&
|
|
||||||
((typeof res === 'string' && res !== '0x') || (isOpenEthereumRevertMsg && res.data.slice(9) !== '0x'))
|
|
||||||
|
|
||||||
resolve({
|
|
||||||
success: isEstimationSuccessful,
|
|
||||||
estimation: txGasEstimation + additionalGas,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
batch.add(request)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
batch.execute()
|
|
||||||
|
|
||||||
const estimationResponses = await Promise.all(estimationRequests)
|
|
||||||
const firstSuccessfulRequest: any = estimationResponses.find((res: any) => res.success)
|
|
||||||
|
|
||||||
if (firstSuccessfulRequest) {
|
|
||||||
return firstSuccessfulRequest.estimation
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error calculating tx gas estimation', error)
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +1,3 @@
|
||||||
export * from './gas'
|
|
||||||
export * from './send'
|
export * from './send'
|
||||||
export * from './offchainSigner'
|
export * from './offchainSigner'
|
||||||
export * from './txHistory'
|
export * from './txHistory'
|
||||||
|
|
|
@ -2,6 +2,7 @@ import axios from 'axios'
|
||||||
import { BigNumber } from 'bignumber.js'
|
import { BigNumber } from 'bignumber.js'
|
||||||
|
|
||||||
import { getWeb3, web3ReadOnly } from 'src/logic/wallets/getWeb3'
|
import { getWeb3, web3ReadOnly } from 'src/logic/wallets/getWeb3'
|
||||||
|
import { getGasPrice, getGasPriceOracle } from 'src/config'
|
||||||
|
|
||||||
// const MAINNET_NETWORK = 1
|
// const MAINNET_NETWORK = 1
|
||||||
export const EMPTY_DATA = '0x'
|
export const EMPTY_DATA = '0x'
|
||||||
|
@ -26,7 +27,7 @@ export const checkReceiptStatus = async (hash) => {
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
export const calculateGasPrice = async () => {
|
export const calculateGasPrice = async (): Promise<string> => {
|
||||||
/*
|
/*
|
||||||
const web3 = getWeb3()
|
const web3 = getWeb3()
|
||||||
const { network } = web3.version
|
const { network } = web3.version
|
||||||
|
@ -41,11 +42,23 @@ export const calculateGasPrice = async () => {
|
||||||
return '20000000000'
|
return '20000000000'
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = 'https://ethgasstation.info/json/ethgasAPI.json'
|
const gasPrice = getGasPrice()
|
||||||
// const errMsg = 'Error querying gas station'
|
const gasPriceOracle = getGasPriceOracle()
|
||||||
const { data } = await axios.get(url)
|
|
||||||
|
|
||||||
return new BigNumber(data.average).multipliedBy(1e8).toString()
|
if (gasPrice) {
|
||||||
|
// Fixed gas price in configuration. xDai uses this approach
|
||||||
|
return new BigNumber(gasPrice).toString()
|
||||||
|
} else if (gasPriceOracle) {
|
||||||
|
const { url, gasParameter } = gasPriceOracle
|
||||||
|
|
||||||
|
// Fetch from gas price provider
|
||||||
|
const { data } = await axios.get(url)
|
||||||
|
|
||||||
|
return new BigNumber(data[gasParameter]).multipliedBy(1e8).toString()
|
||||||
|
} else {
|
||||||
|
const errorMsg = 'gasPrice or gasPriceOracle not set in config'
|
||||||
|
return Promise.reject(errorMsg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const calculateGasOf = async (data, from, to) => {
|
export const calculateGasOf = async (data, from, to) => {
|
||||||
|
|
|
@ -20,7 +20,7 @@ import createTransaction from 'src/logic/safe/store/actions/createTransaction'
|
||||||
import { MULTI_SEND_ADDRESS } from 'src/logic/contracts/safeContracts'
|
import { MULTI_SEND_ADDRESS } from 'src/logic/contracts/safeContracts'
|
||||||
import { DELEGATE_CALL, TX_NOTIFICATION_TYPES } from 'src/logic/safe/transactions'
|
import { DELEGATE_CALL, TX_NOTIFICATION_TYPES } from 'src/logic/safe/transactions'
|
||||||
import { encodeMultiSendCall } from 'src/logic/safe/transactions/multisend'
|
import { encodeMultiSendCall } from 'src/logic/safe/transactions/multisend'
|
||||||
import { estimateSafeTxGas } from 'src/logic/safe/transactions/gasNew'
|
import { estimateSafeTxGas } from 'src/logic/safe/transactions/gas'
|
||||||
|
|
||||||
import GasEstimationInfo from './GasEstimationInfo'
|
import GasEstimationInfo from './GasEstimationInfo'
|
||||||
import { getNetworkInfo } from 'src/config'
|
import { getNetworkInfo } from 'src/config'
|
||||||
|
|
|
@ -14,7 +14,7 @@ import Paragraph from 'src/components/layout/Paragraph'
|
||||||
import Row from 'src/components/layout/Row'
|
import Row from 'src/components/layout/Row'
|
||||||
import { AbiItemExtended } from 'src/logic/contractInteraction/sources/ABIService'
|
import { AbiItemExtended } from 'src/logic/contractInteraction/sources/ABIService'
|
||||||
import { TX_NOTIFICATION_TYPES } from 'src/logic/safe/transactions'
|
import { TX_NOTIFICATION_TYPES } from 'src/logic/safe/transactions'
|
||||||
import { estimateTxGasCosts } from 'src/logic/safe/transactions/gasNew'
|
import { estimateTxGasCosts } from 'src/logic/safe/transactions/gas'
|
||||||
import { formatAmount } from 'src/logic/tokens/utils/formatAmount'
|
import { formatAmount } from 'src/logic/tokens/utils/formatAmount'
|
||||||
import { getEthAsToken } from 'src/logic/tokens/utils/tokenHelpers'
|
import { getEthAsToken } from 'src/logic/tokens/utils/tokenHelpers'
|
||||||
import { styles } from 'src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/style'
|
import { styles } from 'src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/style'
|
||||||
|
|
|
@ -19,7 +19,7 @@ import Row from 'src/components/layout/Row'
|
||||||
import createTransaction from 'src/logic/safe/store/actions/createTransaction'
|
import createTransaction from 'src/logic/safe/store/actions/createTransaction'
|
||||||
import { safeSelector } from 'src/logic/safe/store/selectors'
|
import { safeSelector } from 'src/logic/safe/store/selectors'
|
||||||
import { TX_NOTIFICATION_TYPES } from 'src/logic/safe/transactions'
|
import { TX_NOTIFICATION_TYPES } from 'src/logic/safe/transactions'
|
||||||
import { estimateTxGasCosts } from 'src/logic/safe/transactions/gasNew'
|
import { estimateTxGasCosts } from 'src/logic/safe/transactions/gas'
|
||||||
import { formatAmount } from 'src/logic/tokens/utils/formatAmount'
|
import { formatAmount } from 'src/logic/tokens/utils/formatAmount'
|
||||||
import { getEthAsToken } from 'src/logic/tokens/utils/tokenHelpers'
|
import { getEthAsToken } from 'src/logic/tokens/utils/tokenHelpers'
|
||||||
import SafeInfo from 'src/routes/safe/components/Balances/SendModal/SafeInfo'
|
import SafeInfo from 'src/routes/safe/components/Balances/SendModal/SafeInfo'
|
||||||
|
|
|
@ -20,7 +20,7 @@ import { nftTokensSelector } from 'src/logic/collectibles/store/selectors'
|
||||||
import createTransaction from 'src/logic/safe/store/actions/createTransaction'
|
import createTransaction from 'src/logic/safe/store/actions/createTransaction'
|
||||||
import { safeSelector } from 'src/logic/safe/store/selectors'
|
import { safeSelector } from 'src/logic/safe/store/selectors'
|
||||||
import { TX_NOTIFICATION_TYPES } from 'src/logic/safe/transactions'
|
import { TX_NOTIFICATION_TYPES } from 'src/logic/safe/transactions'
|
||||||
import { estimateTxGasCosts } from 'src/logic/safe/transactions/gasNew'
|
import { estimateTxGasCosts } from 'src/logic/safe/transactions/gas'
|
||||||
import {
|
import {
|
||||||
containsMethodByHash,
|
containsMethodByHash,
|
||||||
getERC721TokenContract,
|
getERC721TokenContract,
|
||||||
|
|
|
@ -21,7 +21,7 @@ import Row from 'src/components/layout/Row'
|
||||||
import createTransaction from 'src/logic/safe/store/actions/createTransaction'
|
import createTransaction from 'src/logic/safe/store/actions/createTransaction'
|
||||||
import { safeSelector } from 'src/logic/safe/store/selectors'
|
import { safeSelector } from 'src/logic/safe/store/selectors'
|
||||||
import { TX_NOTIFICATION_TYPES } from 'src/logic/safe/transactions'
|
import { TX_NOTIFICATION_TYPES } from 'src/logic/safe/transactions'
|
||||||
import { estimateTxGasCosts } from 'src/logic/safe/transactions/gasNew'
|
import { estimateTxGasCosts } from 'src/logic/safe/transactions/gas'
|
||||||
import { getHumanFriendlyToken } from 'src/logic/tokens/store/actions/fetchTokens'
|
import { getHumanFriendlyToken } from 'src/logic/tokens/store/actions/fetchTokens'
|
||||||
import { formatAmount } from 'src/logic/tokens/utils/formatAmount'
|
import { formatAmount } from 'src/logic/tokens/utils/formatAmount'
|
||||||
import { EMPTY_DATA } from 'src/logic/wallets/ethTransactions'
|
import { EMPTY_DATA } from 'src/logic/wallets/ethTransactions'
|
||||||
|
|
|
@ -17,7 +17,7 @@ import Paragraph from 'src/components/layout/Paragraph'
|
||||||
import Row from 'src/components/layout/Row'
|
import Row from 'src/components/layout/Row'
|
||||||
import { getGnosisSafeInstanceAt } from 'src/logic/contracts/safeContracts'
|
import { getGnosisSafeInstanceAt } from 'src/logic/contracts/safeContracts'
|
||||||
import { safeNameSelector, safeOwnersSelector, safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors'
|
import { safeNameSelector, safeOwnersSelector, safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors'
|
||||||
import { estimateTxGasCosts } from 'src/logic/safe/transactions/gasNew'
|
import { estimateTxGasCosts } from 'src/logic/safe/transactions/gas'
|
||||||
import { formatAmount } from 'src/logic/tokens/utils/formatAmount'
|
import { formatAmount } from 'src/logic/tokens/utils/formatAmount'
|
||||||
|
|
||||||
import { styles } from './style'
|
import { styles } from './style'
|
||||||
|
|
|
@ -17,7 +17,7 @@ import Paragraph from 'src/components/layout/Paragraph'
|
||||||
import Row from 'src/components/layout/Row'
|
import Row from 'src/components/layout/Row'
|
||||||
import { getGnosisSafeInstanceAt, SENTINEL_ADDRESS } from 'src/logic/contracts/safeContracts'
|
import { getGnosisSafeInstanceAt, SENTINEL_ADDRESS } from 'src/logic/contracts/safeContracts'
|
||||||
import { safeNameSelector, safeOwnersSelector, safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors'
|
import { safeNameSelector, safeOwnersSelector, safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors'
|
||||||
import { estimateTxGasCosts } from 'src/logic/safe/transactions/gasNew'
|
import { estimateTxGasCosts } from 'src/logic/safe/transactions/gas'
|
||||||
import { formatAmount } from 'src/logic/tokens/utils/formatAmount'
|
import { formatAmount } from 'src/logic/tokens/utils/formatAmount'
|
||||||
|
|
||||||
import { styles } from './style'
|
import { styles } from './style'
|
||||||
|
|
|
@ -22,7 +22,7 @@ import {
|
||||||
safeParamAddressFromStateSelector,
|
safeParamAddressFromStateSelector,
|
||||||
safeThresholdSelector,
|
safeThresholdSelector,
|
||||||
} from 'src/logic/safe/store/selectors'
|
} from 'src/logic/safe/store/selectors'
|
||||||
import { estimateTxGasCosts } from 'src/logic/safe/transactions/gasNew'
|
import { estimateTxGasCosts } from 'src/logic/safe/transactions/gas'
|
||||||
import { formatAmount } from 'src/logic/tokens/utils/formatAmount'
|
import { formatAmount } from 'src/logic/tokens/utils/formatAmount'
|
||||||
|
|
||||||
import { styles } from './style'
|
import { styles } from './style'
|
||||||
|
|
|
@ -18,7 +18,7 @@ import Hairline from 'src/components/layout/Hairline'
|
||||||
import Paragraph from 'src/components/layout/Paragraph'
|
import Paragraph from 'src/components/layout/Paragraph'
|
||||||
import Row from 'src/components/layout/Row'
|
import Row from 'src/components/layout/Row'
|
||||||
import { getGnosisSafeInstanceAt } from 'src/logic/contracts/safeContracts'
|
import { getGnosisSafeInstanceAt } from 'src/logic/contracts/safeContracts'
|
||||||
import { estimateTxGasCosts } from 'src/logic/safe/transactions/gasNew'
|
import { estimateTxGasCosts } from 'src/logic/safe/transactions/gas'
|
||||||
import { formatAmount } from 'src/logic/tokens/utils/formatAmount'
|
import { formatAmount } from 'src/logic/tokens/utils/formatAmount'
|
||||||
|
|
||||||
const THRESHOLD_FIELD_NAME = 'threshold'
|
const THRESHOLD_FIELD_NAME = 'threshold'
|
||||||
|
|
|
@ -18,7 +18,7 @@ import Hairline from 'src/components/layout/Hairline'
|
||||||
import Paragraph from 'src/components/layout/Paragraph'
|
import Paragraph from 'src/components/layout/Paragraph'
|
||||||
import Row from 'src/components/layout/Row'
|
import Row from 'src/components/layout/Row'
|
||||||
import { TX_NOTIFICATION_TYPES } from 'src/logic/safe/transactions'
|
import { TX_NOTIFICATION_TYPES } from 'src/logic/safe/transactions'
|
||||||
import { estimateTxGasCosts } from 'src/logic/safe/transactions/gasNew'
|
import { estimateTxGasCosts } from 'src/logic/safe/transactions/gas'
|
||||||
import { formatAmount } from 'src/logic/tokens/utils/formatAmount'
|
import { formatAmount } from 'src/logic/tokens/utils/formatAmount'
|
||||||
import { userAccountSelector } from 'src/logic/wallets/store/selectors'
|
import { userAccountSelector } from 'src/logic/wallets/store/selectors'
|
||||||
import processTransaction from 'src/logic/safe/store/actions/processTransaction'
|
import processTransaction from 'src/logic/safe/store/actions/processTransaction'
|
||||||
|
|
|
@ -16,7 +16,7 @@ import Hairline from 'src/components/layout/Hairline'
|
||||||
import Paragraph from 'src/components/layout/Paragraph'
|
import Paragraph from 'src/components/layout/Paragraph'
|
||||||
import Row from 'src/components/layout/Row'
|
import Row from 'src/components/layout/Row'
|
||||||
import { TX_NOTIFICATION_TYPES } from 'src/logic/safe/transactions'
|
import { TX_NOTIFICATION_TYPES } from 'src/logic/safe/transactions'
|
||||||
import { estimateTxGasCosts } from 'src/logic/safe/transactions/gasNew'
|
import { estimateTxGasCosts } from 'src/logic/safe/transactions/gas'
|
||||||
import { formatAmount } from 'src/logic/tokens/utils/formatAmount'
|
import { formatAmount } from 'src/logic/tokens/utils/formatAmount'
|
||||||
import { EMPTY_DATA } from 'src/logic/wallets/ethTransactions'
|
import { EMPTY_DATA } from 'src/logic/wallets/ethTransactions'
|
||||||
import createTransaction from 'src/logic/safe/store/actions/createTransaction'
|
import createTransaction from 'src/logic/safe/store/actions/createTransaction'
|
||||||
|
|
Loading…
Reference in New Issue