improvement(transactions): some clean up and unit tests
- fetch transaction state less often for old transactions - fix a bug calling non payable methods with value 0
This commit is contained in:
parent
365b429c0b
commit
e78b6d61f2
@ -6,7 +6,6 @@ import { PortisConnector } from '@web3-react/portis-connector'
|
|||||||
import { FortmaticConnector } from './Fortmatic'
|
import { FortmaticConnector } from './Fortmatic'
|
||||||
import { NetworkConnector } from './NetworkConnector'
|
import { NetworkConnector } from './NetworkConnector'
|
||||||
|
|
||||||
const POLLING_INTERVAL = 12500
|
|
||||||
const NETWORK_URL = process.env.REACT_APP_NETWORK_URL
|
const NETWORK_URL = process.env.REACT_APP_NETWORK_URL
|
||||||
const FORMATIC_KEY = process.env.REACT_APP_FORTMATIC_KEY
|
const FORMATIC_KEY = process.env.REACT_APP_FORTMATIC_KEY
|
||||||
const PORTIS_ID = process.env.REACT_APP_PORTIS_ID
|
const PORTIS_ID = process.env.REACT_APP_PORTIS_ID
|
||||||
@ -28,7 +27,7 @@ export const walletconnect = new WalletConnectConnector({
|
|||||||
rpc: { 1: NETWORK_URL },
|
rpc: { 1: NETWORK_URL },
|
||||||
bridge: 'https://bridge.walletconnect.org',
|
bridge: 'https://bridge.walletconnect.org',
|
||||||
qrcode: true,
|
qrcode: true,
|
||||||
pollingInterval: POLLING_INTERVAL
|
pollingInterval: 15000
|
||||||
})
|
})
|
||||||
|
|
||||||
// mainnet only
|
// mainnet only
|
||||||
|
@ -12,6 +12,10 @@ import { useV1ExchangeContract } from './useContract'
|
|||||||
import useENS from './useENS'
|
import useENS from './useENS'
|
||||||
import { Version } from './useToggledVersion'
|
import { Version } from './useToggledVersion'
|
||||||
|
|
||||||
|
function isZero(hexNumber: string) {
|
||||||
|
return /^0x0*$/.test(hexNumber)
|
||||||
|
}
|
||||||
|
|
||||||
// returns a function that will execute a swap, if the parameters are all valid
|
// returns a function that will execute a swap, if the parameters are all valid
|
||||||
// and the user has approved the slippage adjusted input amount for the trade
|
// and the user has approved the slippage adjusted input amount for the trade
|
||||||
export function useSwapCallback(
|
export function useSwapCallback(
|
||||||
@ -76,7 +80,7 @@ export function useSwapCallback(
|
|||||||
|
|
||||||
const safeGasEstimates: (BigNumber | undefined)[] = await Promise.all(
|
const safeGasEstimates: (BigNumber | undefined)[] = await Promise.all(
|
||||||
swapMethods.map(({ args, methodName, value }) =>
|
swapMethods.map(({ args, methodName, value }) =>
|
||||||
contract.estimateGas[methodName](...args, value ? { value } : {})
|
contract.estimateGas[methodName](...args, value && !isZero(value) ? { value } : {})
|
||||||
.then(calculateGasMargin)
|
.then(calculateGasMargin)
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error(`estimateGas failed for ${methodName}`, error)
|
console.error(`estimateGas failed for ${methodName}`, error)
|
||||||
@ -127,7 +131,7 @@ export function useSwapCallback(
|
|||||||
|
|
||||||
return contract[methodName](...args, {
|
return contract[methodName](...args, {
|
||||||
gasLimit: safeGasEstimate,
|
gasLimit: safeGasEstimate,
|
||||||
...(value ? { value } : {})
|
...(value && !isZero(value) ? { value } : {})
|
||||||
})
|
})
|
||||||
.then((response: any) => {
|
.then((response: any) => {
|
||||||
const inputSymbol = trade.inputAmount.currency.symbol
|
const inputSymbol = trade.inputAmount.currency.symbol
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { createAction } from '@reduxjs/toolkit'
|
import { createAction } from '@reduxjs/toolkit'
|
||||||
|
import { ChainId } from '@uniswap/sdk'
|
||||||
|
|
||||||
export interface SerializableTransactionReceipt {
|
export interface SerializableTransactionReceipt {
|
||||||
to: string
|
to: string
|
||||||
@ -12,15 +13,20 @@ export interface SerializableTransactionReceipt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const addTransaction = createAction<{
|
export const addTransaction = createAction<{
|
||||||
chainId: number
|
chainId: ChainId
|
||||||
hash: string
|
hash: string
|
||||||
from: string
|
from: string
|
||||||
approval?: { tokenAddress: string; spender: string }
|
approval?: { tokenAddress: string; spender: string }
|
||||||
summary?: string
|
summary?: string
|
||||||
}>('addTransaction')
|
}>('transactions/addTransaction')
|
||||||
export const clearAllTransactions = createAction<{ chainId: number }>('clearAllTransactions')
|
export const clearAllTransactions = createAction<{ chainId: ChainId }>('transactions/clearAllTransactions')
|
||||||
export const finalizeTransaction = createAction<{
|
export const finalizeTransaction = createAction<{
|
||||||
chainId: number
|
chainId: ChainId
|
||||||
hash: string
|
hash: string
|
||||||
receipt: SerializableTransactionReceipt
|
receipt: SerializableTransactionReceipt
|
||||||
}>('finalizeTransaction')
|
}>('transactions/finalizeTransaction')
|
||||||
|
export const checkedTransaction = createAction<{
|
||||||
|
chainId: ChainId
|
||||||
|
hash: string
|
||||||
|
blockNumber: number
|
||||||
|
}>('transactions/checkedTransaction')
|
||||||
|
@ -5,7 +5,7 @@ import { useDispatch, useSelector } from 'react-redux'
|
|||||||
import { useActiveWeb3React } from '../../hooks'
|
import { useActiveWeb3React } from '../../hooks'
|
||||||
import { AppDispatch, AppState } from '../index'
|
import { AppDispatch, AppState } from '../index'
|
||||||
import { addTransaction } from './actions'
|
import { addTransaction } from './actions'
|
||||||
import { TransactionDetails, TransactionState } from './reducer'
|
import { TransactionDetails } from './reducer'
|
||||||
|
|
||||||
// helper that can take a ethers library transaction response and add it to the list of transactions
|
// helper that can take a ethers library transaction response and add it to the list of transactions
|
||||||
export function useTransactionAdder(): (
|
export function useTransactionAdder(): (
|
||||||
@ -37,7 +37,7 @@ export function useTransactionAdder(): (
|
|||||||
export function useAllTransactions(): { [txHash: string]: TransactionDetails } {
|
export function useAllTransactions(): { [txHash: string]: TransactionDetails } {
|
||||||
const { chainId } = useActiveWeb3React()
|
const { chainId } = useActiveWeb3React()
|
||||||
|
|
||||||
const state = useSelector<AppState, TransactionState>(state => state.transactions)
|
const state = useSelector<AppState, AppState['transactions']>(state => state.transactions)
|
||||||
|
|
||||||
return chainId ? state[chainId] ?? {} : {}
|
return chainId ? state[chainId] ?? {} : {}
|
||||||
}
|
}
|
||||||
|
192
src/state/transactions/reducer.test.ts
Normal file
192
src/state/transactions/reducer.test.ts
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
import { ChainId } from '@uniswap/sdk'
|
||||||
|
import { createStore, Store } from 'redux'
|
||||||
|
import { addTransaction, checkedTransaction, clearAllTransactions, finalizeTransaction } from './actions'
|
||||||
|
import reducer, { initialState, TransactionState } from './reducer'
|
||||||
|
|
||||||
|
describe('transaction reducer', () => {
|
||||||
|
let store: Store<TransactionState>
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
store = createStore(reducer, initialState)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('addTransaction', () => {
|
||||||
|
it('adds the transaction', () => {
|
||||||
|
const beforeTime = new Date().getTime()
|
||||||
|
store.dispatch(
|
||||||
|
addTransaction({
|
||||||
|
chainId: ChainId.MAINNET,
|
||||||
|
summary: 'hello world',
|
||||||
|
hash: '0x0',
|
||||||
|
approval: { tokenAddress: 'abc', spender: 'def' },
|
||||||
|
from: 'abc'
|
||||||
|
})
|
||||||
|
)
|
||||||
|
const txs = store.getState()
|
||||||
|
expect(txs[ChainId.MAINNET]).toBeTruthy()
|
||||||
|
expect(txs[ChainId.MAINNET]?.['0x0']).toBeTruthy()
|
||||||
|
const tx = txs[ChainId.MAINNET]?.['0x0']
|
||||||
|
expect(tx).toBeTruthy()
|
||||||
|
expect(tx?.hash).toEqual('0x0')
|
||||||
|
expect(tx?.summary).toEqual('hello world')
|
||||||
|
expect(tx?.approval).toEqual({ tokenAddress: 'abc', spender: 'def' })
|
||||||
|
expect(tx?.from).toEqual('abc')
|
||||||
|
expect(tx?.addedTime).toBeGreaterThanOrEqual(beforeTime)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('finalizeTransaction', () => {
|
||||||
|
it('no op if not valid transaction', () => {
|
||||||
|
store.dispatch(
|
||||||
|
finalizeTransaction({
|
||||||
|
chainId: ChainId.RINKEBY,
|
||||||
|
hash: '0x0',
|
||||||
|
receipt: {
|
||||||
|
status: 1,
|
||||||
|
transactionIndex: 1,
|
||||||
|
transactionHash: '0x0',
|
||||||
|
to: '0x0',
|
||||||
|
from: '0x0',
|
||||||
|
contractAddress: '0x0',
|
||||||
|
blockHash: '0x0',
|
||||||
|
blockNumber: 1
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
expect(store.getState()).toEqual({})
|
||||||
|
})
|
||||||
|
it('sets receipt', () => {
|
||||||
|
store.dispatch(
|
||||||
|
addTransaction({
|
||||||
|
hash: '0x0',
|
||||||
|
chainId: ChainId.RINKEBY,
|
||||||
|
approval: { spender: '0x0', tokenAddress: '0x0' },
|
||||||
|
summary: 'hello world',
|
||||||
|
from: '0x0'
|
||||||
|
})
|
||||||
|
)
|
||||||
|
const beforeTime = new Date().getTime()
|
||||||
|
store.dispatch(
|
||||||
|
finalizeTransaction({
|
||||||
|
chainId: ChainId.RINKEBY,
|
||||||
|
hash: '0x0',
|
||||||
|
receipt: {
|
||||||
|
status: 1,
|
||||||
|
transactionIndex: 1,
|
||||||
|
transactionHash: '0x0',
|
||||||
|
to: '0x0',
|
||||||
|
from: '0x0',
|
||||||
|
contractAddress: '0x0',
|
||||||
|
blockHash: '0x0',
|
||||||
|
blockNumber: 1
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
const tx = store.getState()[ChainId.RINKEBY]?.['0x0']
|
||||||
|
expect(tx?.summary).toEqual('hello world')
|
||||||
|
expect(tx?.confirmedTime).toBeGreaterThanOrEqual(beforeTime)
|
||||||
|
expect(tx?.receipt).toEqual({
|
||||||
|
status: 1,
|
||||||
|
transactionIndex: 1,
|
||||||
|
transactionHash: '0x0',
|
||||||
|
to: '0x0',
|
||||||
|
from: '0x0',
|
||||||
|
contractAddress: '0x0',
|
||||||
|
blockHash: '0x0',
|
||||||
|
blockNumber: 1
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('checkedTransaction', () => {
|
||||||
|
it('no op if not valid transaction', () => {
|
||||||
|
store.dispatch(
|
||||||
|
checkedTransaction({
|
||||||
|
chainId: ChainId.RINKEBY,
|
||||||
|
hash: '0x0',
|
||||||
|
blockNumber: 1
|
||||||
|
})
|
||||||
|
)
|
||||||
|
expect(store.getState()).toEqual({})
|
||||||
|
})
|
||||||
|
it('sets lastCheckedBlockNumber', () => {
|
||||||
|
store.dispatch(
|
||||||
|
addTransaction({
|
||||||
|
hash: '0x0',
|
||||||
|
chainId: ChainId.RINKEBY,
|
||||||
|
approval: { spender: '0x0', tokenAddress: '0x0' },
|
||||||
|
summary: 'hello world',
|
||||||
|
from: '0x0'
|
||||||
|
})
|
||||||
|
)
|
||||||
|
store.dispatch(
|
||||||
|
checkedTransaction({
|
||||||
|
chainId: ChainId.RINKEBY,
|
||||||
|
hash: '0x0',
|
||||||
|
blockNumber: 1
|
||||||
|
})
|
||||||
|
)
|
||||||
|
const tx = store.getState()[ChainId.RINKEBY]?.['0x0']
|
||||||
|
expect(tx?.lastCheckedBlockNumber).toEqual(1)
|
||||||
|
})
|
||||||
|
it('never decreases', () => {
|
||||||
|
store.dispatch(
|
||||||
|
addTransaction({
|
||||||
|
hash: '0x0',
|
||||||
|
chainId: ChainId.RINKEBY,
|
||||||
|
approval: { spender: '0x0', tokenAddress: '0x0' },
|
||||||
|
summary: 'hello world',
|
||||||
|
from: '0x0'
|
||||||
|
})
|
||||||
|
)
|
||||||
|
store.dispatch(
|
||||||
|
checkedTransaction({
|
||||||
|
chainId: ChainId.RINKEBY,
|
||||||
|
hash: '0x0',
|
||||||
|
blockNumber: 3
|
||||||
|
})
|
||||||
|
)
|
||||||
|
store.dispatch(
|
||||||
|
checkedTransaction({
|
||||||
|
chainId: ChainId.RINKEBY,
|
||||||
|
hash: '0x0',
|
||||||
|
blockNumber: 1
|
||||||
|
})
|
||||||
|
)
|
||||||
|
const tx = store.getState()[ChainId.RINKEBY]?.['0x0']
|
||||||
|
expect(tx?.lastCheckedBlockNumber).toEqual(3)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('clearAllTransactions', () => {
|
||||||
|
it('removes all transactions for the chain', () => {
|
||||||
|
store.dispatch(
|
||||||
|
addTransaction({
|
||||||
|
chainId: ChainId.MAINNET,
|
||||||
|
summary: 'hello world',
|
||||||
|
hash: '0x0',
|
||||||
|
approval: { tokenAddress: 'abc', spender: 'def' },
|
||||||
|
from: 'abc'
|
||||||
|
})
|
||||||
|
)
|
||||||
|
store.dispatch(
|
||||||
|
addTransaction({
|
||||||
|
chainId: ChainId.RINKEBY,
|
||||||
|
summary: 'hello world',
|
||||||
|
hash: '0x1',
|
||||||
|
approval: { tokenAddress: 'abc', spender: 'def' },
|
||||||
|
from: 'abc'
|
||||||
|
})
|
||||||
|
)
|
||||||
|
expect(Object.keys(store.getState())).toHaveLength(2)
|
||||||
|
expect(Object.keys(store.getState())).toEqual([String(ChainId.MAINNET), String(ChainId.RINKEBY)])
|
||||||
|
expect(Object.keys(store.getState()[ChainId.MAINNET] ?? {})).toEqual(['0x0'])
|
||||||
|
expect(Object.keys(store.getState()[ChainId.RINKEBY] ?? {})).toEqual(['0x1'])
|
||||||
|
store.dispatch(clearAllTransactions({ chainId: ChainId.MAINNET }))
|
||||||
|
expect(Object.keys(store.getState())).toHaveLength(2)
|
||||||
|
expect(Object.keys(store.getState())).toEqual([String(ChainId.MAINNET), String(ChainId.RINKEBY)])
|
||||||
|
expect(Object.keys(store.getState()[ChainId.MAINNET] ?? {})).toEqual([])
|
||||||
|
expect(Object.keys(store.getState()[ChainId.RINKEBY] ?? {})).toEqual(['0x1'])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
@ -1,5 +1,11 @@
|
|||||||
import { createReducer } from '@reduxjs/toolkit'
|
import { createReducer } from '@reduxjs/toolkit'
|
||||||
import { addTransaction, clearAllTransactions, finalizeTransaction, SerializableTransactionReceipt } from './actions'
|
import {
|
||||||
|
addTransaction,
|
||||||
|
checkedTransaction,
|
||||||
|
clearAllTransactions,
|
||||||
|
finalizeTransaction,
|
||||||
|
SerializableTransactionReceipt
|
||||||
|
} from './actions'
|
||||||
|
|
||||||
const now = () => new Date().getTime()
|
const now = () => new Date().getTime()
|
||||||
|
|
||||||
@ -8,12 +14,10 @@ export interface TransactionDetails {
|
|||||||
approval?: { tokenAddress: string; spender: string }
|
approval?: { tokenAddress: string; spender: string }
|
||||||
summary?: string
|
summary?: string
|
||||||
receipt?: SerializableTransactionReceipt
|
receipt?: SerializableTransactionReceipt
|
||||||
|
lastCheckedBlockNumber?: number
|
||||||
addedTime: number
|
addedTime: number
|
||||||
confirmedTime?: number
|
confirmedTime?: number
|
||||||
from: string
|
from: string
|
||||||
|
|
||||||
// set to true when we receive a transaction count that exceeds the nonce of this transaction
|
|
||||||
unknownStatus?: boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TransactionState {
|
export interface TransactionState {
|
||||||
@ -22,28 +26,39 @@ export interface TransactionState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: TransactionState = {}
|
export const initialState: TransactionState = {}
|
||||||
|
|
||||||
export default createReducer(initialState, builder =>
|
export default createReducer(initialState, builder =>
|
||||||
builder
|
builder
|
||||||
.addCase(addTransaction, (state, { payload: { chainId, from, hash, approval, summary } }) => {
|
.addCase(addTransaction, (transactions, { payload: { chainId, from, hash, approval, summary } }) => {
|
||||||
if (state[chainId]?.[hash]) {
|
if (transactions[chainId]?.[hash]) {
|
||||||
throw Error('Attempted to add existing transaction.')
|
throw Error('Attempted to add existing transaction.')
|
||||||
}
|
}
|
||||||
state[chainId] = state[chainId] ?? {}
|
const txs = transactions[chainId] ?? {}
|
||||||
state[chainId][hash] = { hash, approval, summary, from, addedTime: now() }
|
txs[hash] = { hash, approval, summary, from, addedTime: now() }
|
||||||
|
transactions[chainId] = txs
|
||||||
})
|
})
|
||||||
.addCase(clearAllTransactions, (state, { payload: { chainId } }) => {
|
.addCase(clearAllTransactions, (transactions, { payload: { chainId } }) => {
|
||||||
if (!state[chainId]) return
|
if (!transactions[chainId]) return
|
||||||
state[chainId] = {}
|
transactions[chainId] = {}
|
||||||
})
|
})
|
||||||
.addCase(finalizeTransaction, (state, { payload: { hash, chainId, receipt } }) => {
|
.addCase(checkedTransaction, (transactions, { payload: { chainId, hash, blockNumber } }) => {
|
||||||
if (!state[chainId]?.[hash]) {
|
const tx = transactions[chainId]?.[hash]
|
||||||
throw Error('Attempted to finalize non-existent transaction.')
|
if (!tx) {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
state[chainId] = state[chainId] ?? {}
|
if (!tx.lastCheckedBlockNumber) {
|
||||||
state[chainId][hash].receipt = receipt
|
tx.lastCheckedBlockNumber = blockNumber
|
||||||
state[chainId][hash].unknownStatus = false
|
} else {
|
||||||
state[chainId][hash].confirmedTime = now()
|
tx.lastCheckedBlockNumber = Math.max(blockNumber, tx.lastCheckedBlockNumber)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.addCase(finalizeTransaction, (transactions, { payload: { hash, chainId, receipt } }) => {
|
||||||
|
const tx = transactions[chainId]?.[hash]
|
||||||
|
if (!tx) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tx.receipt = receipt
|
||||||
|
tx.confirmedTime = now()
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
35
src/state/transactions/updater.test.ts
Normal file
35
src/state/transactions/updater.test.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { shouldCheck } from './updater'
|
||||||
|
|
||||||
|
describe('transactions updater', () => {
|
||||||
|
describe('shouldCheck', () => {
|
||||||
|
it('returns true if no receipt and never checked', () => {
|
||||||
|
expect(shouldCheck(10, { addedTime: 100 })).toEqual(true)
|
||||||
|
})
|
||||||
|
it('returns false if has receipt and never checked', () => {
|
||||||
|
expect(shouldCheck(10, { addedTime: 100, receipt: {} })).toEqual(false)
|
||||||
|
})
|
||||||
|
it('returns true if has not been checked in 1 blocks', () => {
|
||||||
|
expect(shouldCheck(10, { addedTime: new Date().getTime(), lastCheckedBlockNumber: 9 })).toEqual(true)
|
||||||
|
})
|
||||||
|
it('returns false if checked in last 3 blocks and greater than 20 minutes old', () => {
|
||||||
|
expect(shouldCheck(10, { addedTime: new Date().getTime() - 21 * 60 * 1000, lastCheckedBlockNumber: 8 })).toEqual(
|
||||||
|
false
|
||||||
|
)
|
||||||
|
})
|
||||||
|
it('returns true if not checked in last 5 blocks and greater than 20 minutes old', () => {
|
||||||
|
expect(shouldCheck(10, { addedTime: new Date().getTime() - 21 * 60 * 1000, lastCheckedBlockNumber: 5 })).toEqual(
|
||||||
|
true
|
||||||
|
)
|
||||||
|
})
|
||||||
|
it('returns false if checked in last 10 blocks and greater than 60 minutes old', () => {
|
||||||
|
expect(shouldCheck(20, { addedTime: new Date().getTime() - 61 * 60 * 1000, lastCheckedBlockNumber: 11 })).toEqual(
|
||||||
|
false
|
||||||
|
)
|
||||||
|
})
|
||||||
|
it('returns true if checked in last 3 blocks and greater than 20 minutes old', () => {
|
||||||
|
expect(shouldCheck(20, { addedTime: new Date().getTime() - 61 * 60 * 1000, lastCheckedBlockNumber: 10 })).toEqual(
|
||||||
|
true
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
@ -3,7 +3,28 @@ import { useDispatch, useSelector } from 'react-redux'
|
|||||||
import { useActiveWeb3React } from '../../hooks'
|
import { useActiveWeb3React } from '../../hooks'
|
||||||
import { useAddPopup, useBlockNumber } from '../application/hooks'
|
import { useAddPopup, useBlockNumber } from '../application/hooks'
|
||||||
import { AppDispatch, AppState } from '../index'
|
import { AppDispatch, AppState } from '../index'
|
||||||
import { finalizeTransaction } from './actions'
|
import { checkedTransaction, finalizeTransaction } from './actions'
|
||||||
|
|
||||||
|
export function shouldCheck(
|
||||||
|
lastBlockNumber: number,
|
||||||
|
tx: { addedTime: number; receipt?: {}; lastCheckedBlockNumber?: number }
|
||||||
|
): boolean {
|
||||||
|
if (tx.receipt) return false
|
||||||
|
if (!tx.lastCheckedBlockNumber) return true
|
||||||
|
const blocksSinceCheck = lastBlockNumber - tx.lastCheckedBlockNumber
|
||||||
|
if (blocksSinceCheck < 1) return false
|
||||||
|
const minutesPending = (new Date().getTime() - tx.addedTime) / 1000 / 60
|
||||||
|
if (minutesPending > 60) {
|
||||||
|
// every 10 blocks if pending for longer than an hour
|
||||||
|
return blocksSinceCheck > 9
|
||||||
|
} else if (minutesPending > 5) {
|
||||||
|
// every 3 blocks if pending more than 5 minutes
|
||||||
|
return blocksSinceCheck > 2
|
||||||
|
} else {
|
||||||
|
// otherwise every block
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default function Updater() {
|
export default function Updater() {
|
||||||
const { chainId, library } = useActiveWeb3React()
|
const { chainId, library } = useActiveWeb3React()
|
||||||
@ -11,9 +32,9 @@ export default function Updater() {
|
|||||||
const lastBlockNumber = useBlockNumber()
|
const lastBlockNumber = useBlockNumber()
|
||||||
|
|
||||||
const dispatch = useDispatch<AppDispatch>()
|
const dispatch = useDispatch<AppDispatch>()
|
||||||
const transactions = useSelector<AppState, AppState['transactions']>(state => state.transactions)
|
const state = useSelector<AppState, AppState['transactions']>(state => state.transactions)
|
||||||
|
|
||||||
const allTransactions = chainId ? transactions[chainId] ?? {} : {}
|
const transactions = chainId ? state[chainId] ?? {} : {}
|
||||||
|
|
||||||
// show popup on confirm
|
// show popup on confirm
|
||||||
const addPopup = useAddPopup()
|
const addPopup = useAddPopup()
|
||||||
@ -21,8 +42,8 @@ export default function Updater() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!chainId || !library || !lastBlockNumber) return
|
if (!chainId || !library || !lastBlockNumber) return
|
||||||
|
|
||||||
Object.keys(allTransactions)
|
Object.keys(transactions)
|
||||||
.filter(hash => !allTransactions[hash].receipt)
|
.filter(hash => shouldCheck(lastBlockNumber, transactions[hash]))
|
||||||
.forEach(hash => {
|
.forEach(hash => {
|
||||||
library
|
library
|
||||||
.getTransactionReceipt(hash)
|
.getTransactionReceipt(hash)
|
||||||
@ -50,18 +71,20 @@ export default function Updater() {
|
|||||||
txn: {
|
txn: {
|
||||||
hash,
|
hash,
|
||||||
success: receipt.status === 1,
|
success: receipt.status === 1,
|
||||||
summary: allTransactions[hash]?.summary
|
summary: transactions[hash]?.summary
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
hash
|
hash
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
dispatch(checkedTransaction({ chainId, hash, blockNumber: lastBlockNumber }))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error(`failed to check transaction hash: ${hash}`, error)
|
console.error(`failed to check transaction hash: ${hash}`, error)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}, [chainId, library, allTransactions, lastBlockNumber, dispatch, addPopup])
|
}, [chainId, library, transactions, lastBlockNumber, dispatch, addPopup])
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
@ -175,6 +175,7 @@ html, input, textarea, button {
|
|||||||
@supports (font-variation-settings: normal) {
|
@supports (font-variation-settings: normal) {
|
||||||
html, input, textarea, button {
|
html, input, textarea, button {
|
||||||
font-family: 'Inter var', sans-serif;
|
font-family: 'Inter var', sans-serif;
|
||||||
|
font-display: fallback;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user