mirror of
https://github.com/status-im/safe-react.git
synced 2025-01-12 02:54:09 +00:00
* Generates a cache to avoid multiples getHumanFriendlyToken() for the same token address * Adds etags implementation for transactions * Caches outgoing and incoming safe transactions based on etag value * Removes cachedSafeTransactions, cachedSafeIncommingTransactions * Refactors getTokenInstance * Avoid recreating tokens on fetchTokens() once we have them in redux * Fixs error on catch * Batch request tokens balances * Fixs missing token names Changes the tokens limit from 300 to 3000 * fix: failed to instantiate non-standard ERC-20 tokens For the batchRequest of balances, we're just using the `balanceOf` method call. So having a simple ABI with that only method prevents errors with non-standard ERC-20 Tokens. * Removes unnecessary action updateSafeThreshold Removes unnecessary action fetchEtherBalance * Updated comments in code Replaces constant with directly dispatching action * BatchRequest done right * fix: invalid action name `savedToken` -> `saveToken` * Renames getTokenInstance to getTokenInfos Fixs first load of transactions are empty * Move fetchTokenBalances to `Balances` and `SendModal` components * fix: Incoming transaction type Backend now changed the type from 'incoming' to one of: `'ERC721_TRANSFER', 'ERC20_TRANSFER', 'ETHER_TRANSFER'` * fix: tokenInstance `symbol` and `decimal` extraction * Fix property name `decimals` instead of `tokenDecimals` * Standardize non-standard ERC20 tokens discovery * fix: isStandardERC20 * Revert "Move fetchTokenBalances to `Balances` and `SendModal` components" This reverts commit ed84bd92 * Fixs Typo INCOMING_TX_TYPES Renames tokenInstance with localToken * Renames getBatchBalances to getTokenBalances Returns saved tokens instead of tokenInstance in getTokenInfos * Remove promise returns Co-authored-by: fernandomg <fernando.greco@gmail.com>
This commit is contained in:
parent
58130760c4
commit
c73dafe3ce
@ -9,7 +9,7 @@ const fetchTokenBalanceList = (safeAddress: string) => {
|
||||
|
||||
return axios.get(url, {
|
||||
params: {
|
||||
limit: 300,
|
||||
limit: 3000,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ const fetchTokenList = () => {
|
||||
|
||||
return axios.get(url, {
|
||||
params: {
|
||||
limit: 300,
|
||||
limit: 3000,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@ -10,8 +10,10 @@ import saveTokens from './saveTokens'
|
||||
|
||||
import { fetchTokenList } from '~/logic/tokens/api'
|
||||
import { type TokenProps, makeToken } from '~/logic/tokens/store/model/token'
|
||||
import { tokensSelector } from '~/logic/tokens/store/selectors'
|
||||
import { getWeb3 } from '~/logic/wallets/getWeb3'
|
||||
import { type GlobalState } from '~/store'
|
||||
import { store } from '~/store/index'
|
||||
import { ensureOnce } from '~/utils/singleton'
|
||||
|
||||
const createStandardTokenContract = async () => {
|
||||
@ -37,12 +39,67 @@ const createERC721TokenContract = async () => {
|
||||
return erc721Token
|
||||
}
|
||||
|
||||
const OnlyBalanceToken = {
|
||||
contractName: 'OnlyBalanceToken',
|
||||
abi: [
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: 'owner',
|
||||
type: 'address',
|
||||
},
|
||||
],
|
||||
name: 'balanceOf',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: 'owner',
|
||||
type: 'address',
|
||||
},
|
||||
],
|
||||
name: 'balances',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
// For the `batchRequest` of balances, we're just using the `balanceOf` method call.
|
||||
// So having a simple ABI only with `balanceOf` prevents errors
|
||||
// when instantiating non-standard ERC-20 Tokens.
|
||||
const createOnlyBalanceToken = () => {
|
||||
const web3 = getWeb3()
|
||||
const contract = new web3.eth.Contract(OnlyBalanceToken.abi)
|
||||
return contract
|
||||
}
|
||||
|
||||
export const getHumanFriendlyToken = ensureOnce(createHumanFriendlyTokenContract)
|
||||
|
||||
export const getStandardTokenContract = ensureOnce(createStandardTokenContract)
|
||||
|
||||
export const getERC721TokenContract = ensureOnce(createERC721TokenContract)
|
||||
|
||||
export const getOnlyBalanceToken = ensureOnce(createOnlyBalanceToken)
|
||||
|
||||
export const containsMethodByHash = async (contractAddress: string, methodHash: string) => {
|
||||
const web3 = getWeb3()
|
||||
const byteCode = await web3.eth.getCode(contractAddress)
|
||||
@ -50,19 +107,54 @@ export const containsMethodByHash = async (contractAddress: string, methodHash:
|
||||
return byteCode.indexOf(methodHash.replace('0x', '')) !== -1
|
||||
}
|
||||
|
||||
export const fetchTokens = () => async (dispatch: ReduxDispatch<GlobalState>) => {
|
||||
export const getTokenInfos = async (tokenAddress: string) => {
|
||||
if (!tokenAddress) {
|
||||
return null
|
||||
}
|
||||
const { tokens } = store.getState()
|
||||
const localToken = tokens.get(tokenAddress)
|
||||
// If the token is inside the store we return the store token
|
||||
if (localToken) {
|
||||
return localToken
|
||||
}
|
||||
// Otherwise we fetch it, save it to the store and return it
|
||||
const tokenContract = await getHumanFriendlyToken()
|
||||
const tokenInstance = await tokenContract.at(tokenAddress)
|
||||
const [tokenSymbol, tokenDecimals, name] = await Promise.all([
|
||||
tokenInstance.symbol(),
|
||||
tokenInstance.decimals(),
|
||||
tokenInstance.name(),
|
||||
])
|
||||
const savedToken = makeToken({
|
||||
address: tokenAddress,
|
||||
name: name ? name : tokenSymbol,
|
||||
symbol: tokenSymbol,
|
||||
decimals: tokenDecimals,
|
||||
logoUri: '',
|
||||
})
|
||||
const newTokens = tokens.set(tokenAddress, savedToken)
|
||||
store.dispatch(saveTokens(newTokens))
|
||||
|
||||
return savedToken
|
||||
}
|
||||
|
||||
export const fetchTokens = () => async (dispatch: ReduxDispatch<GlobalState>, getState: Function) => {
|
||||
try {
|
||||
const currentSavedTokens = tokensSelector(getState())
|
||||
|
||||
const {
|
||||
data: { results: tokenList },
|
||||
} = await fetchTokenList()
|
||||
|
||||
if (currentSavedTokens && currentSavedTokens.size === tokenList.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const tokens = List(tokenList.map((token: TokenProps) => makeToken(token)))
|
||||
|
||||
dispatch(saveTokens(tokens))
|
||||
} catch (err) {
|
||||
console.error('Error fetching token list', err)
|
||||
|
||||
return Promise.resolve()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ import Paragraph from '~/components/layout/Paragraph'
|
||||
import Row from '~/components/layout/Row'
|
||||
import Span from '~/components/layout/Span'
|
||||
import IncomingTxDescription from '~/routes/safe/components/Transactions/TxsTable/ExpandedTx/IncomingTxDescription'
|
||||
import { INCOMING_TX_TYPE } from '~/routes/safe/store/models/incomingTransaction'
|
||||
import { INCOMING_TX_TYPES } from '~/routes/safe/store/models/incomingTransaction'
|
||||
import { type Owner } from '~/routes/safe/store/models/owner'
|
||||
import { type Transaction } from '~/routes/safe/store/models/transaction'
|
||||
|
||||
@ -58,8 +58,8 @@ const ExpandedTx = ({
|
||||
const [openModal, setOpenModal] = useState<OpenModal>(null)
|
||||
const openApproveModal = () => setOpenModal('approveTx')
|
||||
const closeModal = () => setOpenModal(null)
|
||||
const thresholdReached = tx.type !== INCOMING_TX_TYPE && threshold <= tx.confirmations.size
|
||||
const canExecute = tx.type !== INCOMING_TX_TYPE && nonce === tx.nonce
|
||||
const thresholdReached = !INCOMING_TX_TYPES.includes(tx.type) && threshold <= tx.confirmations.size
|
||||
const canExecute = !INCOMING_TX_TYPES.includes(tx.type) && nonce === tx.nonce
|
||||
const cancelThresholdReached = !!cancelTx && threshold <= cancelTx.confirmations.size
|
||||
const canExecuteCancel = nonce === tx.nonce
|
||||
|
||||
@ -76,7 +76,9 @@ const ExpandedTx = ({
|
||||
<Block className={classes.expandedTxBlock}>
|
||||
<Row>
|
||||
<Col layout="column" xs={6}>
|
||||
<Block className={cn(classes.txDataContainer, tx.type === INCOMING_TX_TYPE && classes.incomingTxBlock)}>
|
||||
<Block
|
||||
className={cn(classes.txDataContainer, INCOMING_TX_TYPES.includes(tx.type) && classes.incomingTxBlock)}
|
||||
>
|
||||
<Block align="left" className={classes.txData}>
|
||||
<Bold className={classes.txHash}>Hash:</Bold>
|
||||
{tx.executionTxHash ? <EtherScanLink cut={8} type="tx" value={tx.executionTxHash} /> : 'n/a'}
|
||||
@ -89,7 +91,7 @@ const ExpandedTx = ({
|
||||
<Bold>Fee: </Bold>
|
||||
{tx.fee ? tx.fee : 'n/a'}
|
||||
</Paragraph>
|
||||
{tx.type === INCOMING_TX_TYPE ? (
|
||||
{INCOMING_TX_TYPES.includes(tx.type) ? (
|
||||
<>
|
||||
<Paragraph noMargin>
|
||||
<Bold>Created: </Bold>
|
||||
@ -128,9 +130,9 @@ const ExpandedTx = ({
|
||||
)}
|
||||
</Block>
|
||||
<Hairline />
|
||||
{tx.type === INCOMING_TX_TYPE ? <IncomingTxDescription tx={tx} /> : <TxDescription tx={tx} />}
|
||||
{INCOMING_TX_TYPES.includes(tx.type) ? <IncomingTxDescription tx={tx} /> : <TxDescription tx={tx} />}
|
||||
</Col>
|
||||
{tx.type !== INCOMING_TX_TYPE && (
|
||||
{!INCOMING_TX_TYPES.includes(tx.type) && (
|
||||
<OwnersColumn
|
||||
cancelThresholdReached={cancelThresholdReached}
|
||||
cancelTx={cancelTx}
|
||||
|
@ -9,7 +9,7 @@ import TxType from './TxType'
|
||||
import { type Column } from '~/components/Table/TableHead'
|
||||
import { type SortRow, buildOrderFieldFrom } from '~/components/Table/sorting'
|
||||
import { getWeb3 } from '~/logic/wallets/getWeb3'
|
||||
import { INCOMING_TX_TYPE, type IncomingTransaction } from '~/routes/safe/store/models/incomingTransaction'
|
||||
import { INCOMING_TX_TYPES, type IncomingTransaction } from '~/routes/safe/store/models/incomingTransaction'
|
||||
import { type Transaction } from '~/routes/safe/store/models/transaction'
|
||||
|
||||
export const TX_TABLE_ID = 'id'
|
||||
@ -101,7 +101,7 @@ export const getTxTableData = (
|
||||
const cancelTxsByNonce = cancelTxs.reduce((acc, tx) => acc.set(tx.nonce, tx), Map())
|
||||
|
||||
return transactions.map(tx => {
|
||||
if (tx.type === INCOMING_TX_TYPE) {
|
||||
if (INCOMING_TX_TYPES.includes(tx.type)) {
|
||||
return getIncomingTxTableData(tx)
|
||||
}
|
||||
|
||||
|
@ -125,11 +125,12 @@ class SafeView extends React.Component<Props, State> {
|
||||
fetchEtherBalance,
|
||||
fetchTokenBalances,
|
||||
fetchTransactions,
|
||||
safe,
|
||||
safeUrl,
|
||||
} = this.props
|
||||
checkAndUpdateSafeOwners(safeUrl)
|
||||
fetchTokenBalances(safeUrl, activeTokens)
|
||||
fetchEtherBalance(safeUrl)
|
||||
fetchEtherBalance(safe)
|
||||
fetchTransactions(safeUrl)
|
||||
}
|
||||
|
||||
|
@ -3,13 +3,17 @@ import type { Dispatch as ReduxDispatch } from 'redux'
|
||||
|
||||
import { getBalanceInEtherOf } from '~/logic/wallets/getWeb3'
|
||||
import updateSafe from '~/routes/safe/store/actions/updateSafe'
|
||||
import type { Safe } from '~/routes/safe/store/models/safe'
|
||||
import { type GlobalState } from '~/store/index'
|
||||
|
||||
const fetchEtherBalance = (safeAddress: string) => async (dispatch: ReduxDispatch<GlobalState>) => {
|
||||
const fetchEtherBalance = (safe: Safe) => async (dispatch: ReduxDispatch<GlobalState>) => {
|
||||
try {
|
||||
const ethBalance = await getBalanceInEtherOf(safeAddress)
|
||||
const { address, ethBalance } = safe
|
||||
const newEthBalance = await getBalanceInEtherOf(address)
|
||||
|
||||
dispatch(updateSafe({ address: safeAddress, ethBalance }))
|
||||
if (newEthBalance !== ethBalance) {
|
||||
dispatch(updateSafe({ address, newEthBalance }))
|
||||
}
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line
|
||||
console.error('Error when fetching Ether balance:', err)
|
||||
|
@ -70,12 +70,16 @@ export const checkAndUpdateSafe = (safeAdd: string) => async (dispatch: ReduxDis
|
||||
const remoteOwners = await gnosisSafe.getOwners()
|
||||
// Converts from [ { address, ownerName} ] to address array
|
||||
const localOwners = localSafe.owners.map(localOwner => localOwner.address)
|
||||
const localThreshold = localSafe.threshold
|
||||
|
||||
// Updates threshold values
|
||||
const threshold = await gnosisSafe.getThreshold()
|
||||
localSafe.threshold = threshold.toNumber()
|
||||
const remoteThreshold = await gnosisSafe.getThreshold()
|
||||
localSafe.threshold = remoteThreshold.toNumber()
|
||||
|
||||
if (localThreshold !== remoteThreshold.toNumber()) {
|
||||
dispatch(updateSafeThreshold({ safeAddress, threshold: remoteThreshold.toNumber() }))
|
||||
}
|
||||
|
||||
dispatch(updateSafeThreshold({ safeAddress, threshold: threshold.toNumber() }))
|
||||
// If the remote owners does not contain a local address, we remove that local owner
|
||||
localOwners.forEach(localAddress => {
|
||||
const remoteOwnerIndex = remoteOwners.findIndex(remoteAddress => sameAddress(remoteAddress, localAddress))
|
||||
|
@ -5,10 +5,61 @@ import type { Dispatch as ReduxDispatch } from 'redux'
|
||||
|
||||
import updateSafe from './updateSafe'
|
||||
|
||||
import { getStandardTokenContract } from '~/logic/tokens/store/actions/fetchTokens'
|
||||
import { getOnlyBalanceToken, getStandardTokenContract } from '~/logic/tokens/store/actions/fetchTokens'
|
||||
import { type Token } from '~/logic/tokens/store/model/token'
|
||||
import { ETH_ADDRESS } from '~/logic/tokens/utils/tokenHelpers'
|
||||
import { sameAddress } from '~/logic/wallets/ethAddresses'
|
||||
import { ETHEREUM_NETWORK, getWeb3 } from '~/logic/wallets/getWeb3'
|
||||
import { type GlobalState } from '~/store/index'
|
||||
import { NETWORK } from '~/utils/constants'
|
||||
|
||||
// List of all the non-standard ERC20 tokens
|
||||
const nonStandardERC20 = [
|
||||
// DATAcoin
|
||||
{ network: ETHEREUM_NETWORK.RINKEBY, address: '0x0cf0ee63788a0849fe5297f3407f701e122cc023' },
|
||||
]
|
||||
|
||||
// This is done due to an issues with DATAcoin contract in Rinkeby
|
||||
// https://rinkeby.etherscan.io/address/0x0cf0ee63788a0849fe5297f3407f701e122cc023#readContract
|
||||
// It doesn't have a `balanceOf` method implemented.
|
||||
const isStandardERC20 = (address: string): boolean => {
|
||||
return !nonStandardERC20.find(token => sameAddress(address, token.address) && sameAddress(NETWORK, token.network))
|
||||
}
|
||||
|
||||
const getTokenBalances = (tokens: List<Token>, safeAddress: string) => {
|
||||
const web3 = getWeb3()
|
||||
const batch = new web3.BatchRequest()
|
||||
|
||||
const safeTokens = tokens.toJS().filter(({ address }) => address !== ETH_ADDRESS)
|
||||
const safeTokensBalances = safeTokens.map(({ address, decimals }: any) => {
|
||||
const onlyBalanceToken = getOnlyBalanceToken()
|
||||
onlyBalanceToken.options.address = address
|
||||
|
||||
// As a fallback, we're using `balances`
|
||||
const method = isStandardERC20(address) ? 'balanceOf' : 'balances'
|
||||
|
||||
return new Promise(resolve => {
|
||||
const request = onlyBalanceToken.methods[method](safeAddress).call.request((error, balance) => {
|
||||
if (error) {
|
||||
// if there's no balance, we log the error, but `resolve` with a default '0'
|
||||
console.error('No balance method found', error)
|
||||
resolve('0')
|
||||
} else {
|
||||
resolve({
|
||||
address,
|
||||
balance: new BigNumber(balance).div(`1e${decimals}`).toFixed(),
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
batch.add(request)
|
||||
})
|
||||
})
|
||||
|
||||
batch.execute()
|
||||
|
||||
return Promise.all(safeTokensBalances)
|
||||
}
|
||||
|
||||
export const calculateBalanceOf = async (tokenAddress: string, safeAddress: string, decimals: number = 18) => {
|
||||
if (tokenAddress === ETH_ADDRESS) {
|
||||
@ -34,15 +85,7 @@ const fetchTokenBalances = (safeAddress: string, tokens: List<Token>) => async (
|
||||
return
|
||||
}
|
||||
try {
|
||||
const withBalances = await Promise.all(
|
||||
tokens.map(async token => {
|
||||
const balance = await calculateBalanceOf(token.address, safeAddress, token.decimals)
|
||||
return {
|
||||
address: token.address,
|
||||
balance,
|
||||
}
|
||||
}),
|
||||
)
|
||||
const withBalances = await getTokenBalances(tokens, safeAddress)
|
||||
|
||||
const balances = Map().withMutations(map => {
|
||||
withBalances.forEach(({ address, balance }) => {
|
||||
|
@ -11,7 +11,7 @@ import { decodeParamsFromSafeMethod } from '~/logic/contracts/methodIds'
|
||||
import { buildIncomingTxServiceUrl } from '~/logic/safe/transactions/incomingTxHistory'
|
||||
import { type TxServiceType, buildTxServiceUrl } from '~/logic/safe/transactions/txHistory'
|
||||
import { getLocalSafe } from '~/logic/safe/utils'
|
||||
import { getHumanFriendlyToken } from '~/logic/tokens/store/actions/fetchTokens'
|
||||
import { getTokenInfos } from '~/logic/tokens/store/actions/fetchTokens'
|
||||
import { ALTERNATIVE_TOKEN_ABI } from '~/logic/tokens/utils/alternativeAbi'
|
||||
import {
|
||||
DECIMALS_METHOD_HASH,
|
||||
@ -111,9 +111,9 @@ export const buildTransactionFrom = async (safeAddress: string, tx: TxServiceMod
|
||||
let refundSymbol = 'ETH'
|
||||
let decimals = 18
|
||||
if (tx.gasToken !== ZERO_ADDRESS) {
|
||||
const gasToken = await (await getHumanFriendlyToken()).at(tx.gasToken)
|
||||
refundSymbol = await gasToken.symbol()
|
||||
decimals = await gasToken.decimals()
|
||||
const gasToken = await getTokenInfos(tx.gasToken)
|
||||
refundSymbol = gasToken.symbol
|
||||
decimals = gasToken.decimals
|
||||
}
|
||||
|
||||
const feeString = (tx.gasPrice * (tx.baseGas + tx.safeTxGas)).toString().padStart(decimals, 0)
|
||||
@ -131,10 +131,10 @@ export const buildTransactionFrom = async (safeAddress: string, tx: TxServiceMod
|
||||
let decimals = 18
|
||||
let decodedParams
|
||||
if (isSendTokenTx) {
|
||||
const tokenContract = await getHumanFriendlyToken()
|
||||
const tokenInstance = await tokenContract.at(tx.to)
|
||||
const tokenInstance = await getTokenInfos(tx.to)
|
||||
try {
|
||||
;[symbol, decimals] = await Promise.all([tokenInstance.symbol(), tokenInstance.decimals()])
|
||||
symbol = tokenInstance.symbol
|
||||
decimals = tokenInstance.decimals
|
||||
} catch (err) {
|
||||
const alternativeTokenInstance = new web3.eth.Contract(ALTERNATIVE_TOKEN_ABI, tx.to)
|
||||
const [tokenSymbol, tokenDecimals] = await Promise.all([
|
||||
@ -230,11 +230,9 @@ export const buildIncomingTransactionFrom = async (tx: IncomingTxServiceModel) =
|
||||
|
||||
if (tx.tokenAddress) {
|
||||
try {
|
||||
const tokenContract = await getHumanFriendlyToken()
|
||||
const tokenInstance = await tokenContract.at(tx.tokenAddress)
|
||||
const [tokenSymbol, tokenDecimals] = await Promise.all([tokenInstance.symbol(), tokenInstance.decimals()])
|
||||
symbol = tokenSymbol
|
||||
decimals = tokenDecimals
|
||||
const tokenInstance = await getTokenInfos(tx.tokenAddress)
|
||||
symbol = tokenInstance.symbol
|
||||
decimals = tokenInstance.decimals
|
||||
} catch (err) {
|
||||
try {
|
||||
const { methods } = new web3.eth.Contract(ALTERNATIVE_TOKEN_ABI, tx.tokenAddress)
|
||||
@ -269,19 +267,41 @@ export type SafeTransactionsType = {
|
||||
cancel: Map<string, List<TransactionProps>>,
|
||||
}
|
||||
|
||||
let etagSafeTransactions = null
|
||||
let etagCachedSafeIncommingTransactions = null
|
||||
export const loadSafeTransactions = async (safeAddress: string): Promise<SafeTransactionsType> => {
|
||||
let transactions: TxServiceModel[] = addMockSafeCreationTx(safeAddress)
|
||||
|
||||
try {
|
||||
const config = etagSafeTransactions
|
||||
? {
|
||||
headers: {
|
||||
'If-None-Match': etagSafeTransactions,
|
||||
},
|
||||
}
|
||||
: undefined
|
||||
|
||||
const url = buildTxServiceUrl(safeAddress)
|
||||
const response = await axios.get(url)
|
||||
const response = await axios.get(url, config)
|
||||
if (response.data.count > 0) {
|
||||
transactions = transactions.concat(response.data.results)
|
||||
if (etagSafeTransactions === response.headers.etag) {
|
||||
// The txs are the same, we can return the cached ones
|
||||
return
|
||||
}
|
||||
etagSafeTransactions = response.headers.etag
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`Requests for outgoing transactions for ${safeAddress} failed with 404`, err)
|
||||
if (err && err.response && err.response.status === 304) {
|
||||
// NOTE: this is the expected implementation, currently the backend is not returning 304.
|
||||
// So I check if the returned etag is the same instead (see above)
|
||||
return
|
||||
} else {
|
||||
console.error(`Requests for outgoing transactions for ${safeAddress} failed with 404`, err)
|
||||
}
|
||||
}
|
||||
|
||||
// In case that the etags don't match, we parse the new transactions and save them to the cache
|
||||
const txsRecord: Array<RecordInstance<TransactionProps>> = await Promise.all(
|
||||
transactions.map((tx: TxServiceModel) => buildTransactionFrom(safeAddress, tx)),
|
||||
)
|
||||
@ -297,26 +317,50 @@ export const loadSafeTransactions = async (safeAddress: string): Promise<SafeTra
|
||||
export const loadSafeIncomingTransactions = async (safeAddress: string) => {
|
||||
let incomingTransactions: IncomingTxServiceModel[] = []
|
||||
try {
|
||||
const config = etagCachedSafeIncommingTransactions
|
||||
? {
|
||||
headers: {
|
||||
'If-None-Match': etagCachedSafeIncommingTransactions,
|
||||
},
|
||||
}
|
||||
: undefined
|
||||
const url = buildIncomingTxServiceUrl(safeAddress)
|
||||
const response = await axios.get(url)
|
||||
const response = await axios.get(url, config)
|
||||
if (response.data.count > 0) {
|
||||
incomingTransactions = response.data.results
|
||||
if (etagCachedSafeIncommingTransactions === response.headers.etag) {
|
||||
// The txs are the same, we can return the cached ones
|
||||
return
|
||||
}
|
||||
etagCachedSafeIncommingTransactions = response.headers.etag
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`Requests for incoming transactions for ${safeAddress} failed with 404`, err)
|
||||
if (err && err.response && err.response.status === 304) {
|
||||
// We return cached transactions
|
||||
return
|
||||
} else {
|
||||
console.error(`Requests for incoming transactions for ${safeAddress} failed with 404`, err)
|
||||
}
|
||||
}
|
||||
|
||||
const incomingTxsRecord = await Promise.all(incomingTransactions.map(buildIncomingTransactionFrom))
|
||||
|
||||
return Map().set(safeAddress, List(incomingTxsRecord))
|
||||
}
|
||||
|
||||
export default (safeAddress: string) => async (dispatch: ReduxDispatch<GlobalState>) => {
|
||||
web3 = await getWeb3()
|
||||
|
||||
const { cancel, outgoing }: SafeTransactionsType = await loadSafeTransactions(safeAddress)
|
||||
const incomingTransactions: Map<string, List<IncomingTransaction>> = await loadSafeIncomingTransactions(safeAddress)
|
||||
dispatch(addCancellationTransactions(cancel))
|
||||
dispatch(addTransactions(outgoing))
|
||||
dispatch(addIncomingTransactions(incomingTransactions))
|
||||
const transactions: SafeTransactionsType | undefined = await loadSafeTransactions(safeAddress)
|
||||
if (transactions) {
|
||||
const { cancel, outgoing } = transactions
|
||||
dispatch(addCancellationTransactions(cancel))
|
||||
dispatch(addTransactions(outgoing))
|
||||
}
|
||||
const incomingTransactions: Map<string, List<IncomingTransaction>> | undefined = await loadSafeIncomingTransactions(
|
||||
safeAddress,
|
||||
)
|
||||
|
||||
if (incomingTransactions) {
|
||||
dispatch(addIncomingTransactions(incomingTransactions))
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
import { Record } from 'immutable'
|
||||
import type { RecordFactory, RecordOf } from 'immutable'
|
||||
|
||||
export const INCOMING_TX_TYPE = 'incoming'
|
||||
export const INCOMING_TX_TYPES = ['ERC721_TRANSFER', 'ERC20_TRANSFER', 'ETHER_TRANSFER']
|
||||
|
||||
export type IncomingTransactionProps = {
|
||||
blockNumber: number,
|
||||
@ -53,7 +53,7 @@ export const makeIncomingTransaction: RecordFactory<IncomingTransactionProps> =
|
||||
decimals: 18,
|
||||
fee: '',
|
||||
executionDate: '',
|
||||
type: INCOMING_TX_TYPE,
|
||||
type: INCOMING_TX_TYPES,
|
||||
status: 'success',
|
||||
nonce: null,
|
||||
confirmations: null,
|
||||
|
@ -1,5 +1,7 @@
|
||||
// @flow
|
||||
export const NETWORK = process.env.REACT_APP_NETWORK
|
||||
import { ETHEREUM_NETWORK } from '~/logic/wallets/getWeb3'
|
||||
|
||||
export const NETWORK = process.env.REACT_APP_NETWORK || ETHEREUM_NETWORK.RINKEBY
|
||||
export const GOOGLE_ANALYTICS_ID_RINKEBY = process.env.REACT_APP_GOOGLE_ANALYTICS_ID_RINKEBY
|
||||
export const GOOGLE_ANALYTICS_ID_MAINNET = process.env.REACT_APP_GOOGLE_ANALYTICS_ID_MAINNET
|
||||
export const INTERCOM_ID = process.env.REACT_APP_INTERCOM_ID
|
||||
|
Loading…
x
Reference in New Issue
Block a user