(Feature) Tx Table amount notations (#812)
This commit is contained in:
parent
5014e86c3a
commit
653f68b09c
|
@ -9,7 +9,7 @@ import { type Token, makeToken } from '~/logic/tokens/store/model/token'
|
|||
|
||||
export const TOKEN_REDUCER_ID = 'tokens'
|
||||
|
||||
export type State = Map<string, Map<string, Token>>
|
||||
export type State = Map<string, Token>
|
||||
|
||||
export default handleActions<State, *>(
|
||||
{
|
||||
|
|
|
@ -18,7 +18,7 @@ export const formatAmount = (number: string | number) => {
|
|||
let numberFloat = parseFloat(number)
|
||||
|
||||
if (numberFloat === 0) {
|
||||
numberFloat = '0.000'
|
||||
numberFloat = '0'
|
||||
} else if (numberFloat < 0.001) {
|
||||
numberFloat = '< 0.001'
|
||||
} else if (numberFloat < 1000) {
|
||||
|
|
|
@ -49,7 +49,7 @@ const IncomingTxDescription = ({ tx }: Props) => {
|
|||
const txFromName = getNameFromAddressBook(tx.from)
|
||||
return (
|
||||
<Block className={classes.txDataContainer}>
|
||||
<TransferDescription from={tx.from} txFromName={txFromName} value={getIncomingTxAmount(tx)} />
|
||||
<TransferDescription from={tx.from} txFromName={txFromName} value={getIncomingTxAmount(tx, false)} />
|
||||
</Block>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -222,7 +222,7 @@ const TxDescription = ({ classes, tx }: Props) => {
|
|||
removedOwner,
|
||||
upgradeTx,
|
||||
} = getTxData(tx)
|
||||
const amount = getTxAmount(tx)
|
||||
const amount = getTxAmount(tx, false)
|
||||
return (
|
||||
<Block className={classes.txDataContainer}>
|
||||
{modifySettingsTx && action && (
|
||||
|
|
|
@ -41,8 +41,9 @@ const ExpandedTx = ({ cancelTx, tx }: Props) => {
|
|||
const [openModal, setOpenModal] = useState<OpenModal>(null)
|
||||
const openApproveModal = () => setOpenModal('approveTx')
|
||||
const closeModal = () => setOpenModal(null)
|
||||
const thresholdReached = !INCOMING_TX_TYPES.includes(tx.type) && threshold <= tx.confirmations.size
|
||||
const canExecute = !INCOMING_TX_TYPES.includes(tx.type) && nonce === tx.nonce
|
||||
const isIncomingTx = !!INCOMING_TX_TYPES[tx.type]
|
||||
const thresholdReached = !isIncomingTx && threshold <= tx.confirmations.size
|
||||
const canExecute = !isIncomingTx && nonce === tx.nonce
|
||||
const cancelThresholdReached = !!cancelTx && threshold <= cancelTx.confirmations.size
|
||||
const canExecuteCancel = nonce === tx.nonce
|
||||
|
||||
|
@ -59,22 +60,22 @@ const ExpandedTx = ({ cancelTx, tx }: Props) => {
|
|||
<Block className={classes.expandedTxBlock}>
|
||||
<Row>
|
||||
<Col layout="column" xs={6}>
|
||||
<Block
|
||||
className={cn(classes.txDataContainer, INCOMING_TX_TYPES.includes(tx.type) && classes.incomingTxBlock)}
|
||||
>
|
||||
<Block className={cn(classes.txDataContainer, isIncomingTx && 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'}
|
||||
</Block>
|
||||
<Paragraph noMargin>
|
||||
<Bold>Nonce: </Bold>
|
||||
<Span>{tx.nonce}</Span>
|
||||
</Paragraph>
|
||||
{!isIncomingTx && (
|
||||
<Paragraph noMargin>
|
||||
<Bold>Nonce: </Bold>
|
||||
<Span>{tx.nonce}</Span>
|
||||
</Paragraph>
|
||||
)}
|
||||
<Paragraph noMargin>
|
||||
<Bold>Fee: </Bold>
|
||||
{tx.fee ? tx.fee : 'n/a'}
|
||||
</Paragraph>
|
||||
{INCOMING_TX_TYPES.includes(tx.type) ? (
|
||||
{isIncomingTx ? (
|
||||
<>
|
||||
<Paragraph noMargin>
|
||||
<Bold>Created: </Bold>
|
||||
|
@ -113,9 +114,9 @@ const ExpandedTx = ({ cancelTx, tx }: Props) => {
|
|||
)}
|
||||
</Block>
|
||||
<Hairline />
|
||||
{INCOMING_TX_TYPES.includes(tx.type) ? <IncomingTxDescription tx={tx} /> : <TxDescription tx={tx} />}
|
||||
{isIncomingTx ? <IncomingTxDescription tx={tx} /> : <TxDescription tx={tx} />}
|
||||
</Col>
|
||||
{!INCOMING_TX_TYPES.includes(tx.type) && (
|
||||
{!isIncomingTx && (
|
||||
<OwnersColumn
|
||||
cancelThresholdReached={cancelThresholdReached}
|
||||
cancelTx={cancelTx}
|
||||
|
|
|
@ -9,7 +9,6 @@ import TxType from './TxType'
|
|||
import { type Column } from '~/components/Table/TableHead'
|
||||
import { type SortRow, buildOrderFieldFrom } from '~/components/Table/sorting'
|
||||
import { formatAmount } from '~/logic/tokens/utils/formatAmount'
|
||||
import { getWeb3 } from '~/logic/wallets/getWeb3'
|
||||
import { INCOMING_TX_TYPES, type IncomingTransaction } from '~/routes/safe/store/models/incomingTransaction'
|
||||
import { type Transaction } from '~/routes/safe/store/models/transaction'
|
||||
|
||||
|
@ -34,29 +33,40 @@ type TxData = {
|
|||
|
||||
export const formatDate = (date: string): string => format(parseISO(date), 'MMM d, yyyy - HH:mm:ss')
|
||||
|
||||
export const getIncomingTxAmount = (tx: IncomingTransaction) => {
|
||||
const txAmount = tx.value ? `${new BigNumber(tx.value).div(`1e${tx.decimals}`).toFixed()}` : 'n/a'
|
||||
return `${txAmount} ${tx.symbol || 'n/a'}`
|
||||
type TxValues = {
|
||||
value?: string | number,
|
||||
decimals?: string | number,
|
||||
symbol?: string,
|
||||
}
|
||||
|
||||
export const getTxAmount = (tx: Transaction) => {
|
||||
const web3 = getWeb3()
|
||||
const { fromWei, toBN } = web3.utils
|
||||
const NOT_AVAILABLE = 'n/a'
|
||||
|
||||
let txAmount = 'n/a'
|
||||
const getAmountWithSymbol = ({ decimals = 0, symbol = NOT_AVAILABLE, value }: TxValues, formatted = false) => {
|
||||
const nonFormattedValue = BigNumber(value).times(`1e-${decimals}`).toFixed()
|
||||
const finalValue = formatted ? formatAmount(nonFormattedValue).toString() : nonFormattedValue
|
||||
const txAmount = finalValue === 'NaN' ? NOT_AVAILABLE : finalValue
|
||||
|
||||
if (tx.isTokenTransfer && tx.decodedParams) {
|
||||
const tokenDecimals = tx.decimals.toNumber ? tx.decimals.toNumber() : tx.decimals
|
||||
txAmount = `${formatAmount(
|
||||
BigNumber(tx.decodedParams.value)
|
||||
.div(10 ** tokenDecimals)
|
||||
.toString(),
|
||||
)} ${tx.symbol}`
|
||||
} else if (Number(tx.value) > 0) {
|
||||
txAmount = `${fromWei(toBN(tx.value), 'ether')} ${tx.symbol}`
|
||||
return `${txAmount} ${symbol}`
|
||||
}
|
||||
|
||||
export const getIncomingTxAmount = (tx: IncomingTransaction, formatted: boolean = true) => {
|
||||
// simple workaround to avoid displaying unexpected values for incoming NFT transfer
|
||||
if (INCOMING_TX_TYPES[tx.type] === INCOMING_TX_TYPES.ERC721_TRANSFER) {
|
||||
return `1 ${tx.symbol}`
|
||||
}
|
||||
|
||||
return txAmount
|
||||
return getAmountWithSymbol(tx, formatted)
|
||||
}
|
||||
|
||||
export const getTxAmount = (tx: Transaction, formatted: boolean = true) => {
|
||||
const { decimals = 18, decodedParams, isTokenTransfer, symbol } = tx
|
||||
const { value } = isTokenTransfer && decodedParams && decodedParams.value ? decodedParams : tx
|
||||
|
||||
if (!isTokenTransfer && !(Number(value) > 0)) {
|
||||
return NOT_AVAILABLE
|
||||
}
|
||||
|
||||
return getAmountWithSymbol({ decimals, symbol, value }, formatted)
|
||||
}
|
||||
|
||||
export type TransactionRow = SortRow<TxData>
|
||||
|
@ -106,7 +116,7 @@ export const getTxTableData = (
|
|||
const cancelTxsByNonce = cancelTxs.reduce((acc, tx) => acc.set(tx.nonce, tx), Map())
|
||||
|
||||
return transactions.map((tx) => {
|
||||
if (INCOMING_TX_TYPES.includes(tx.type)) {
|
||||
if (INCOMING_TX_TYPES[tx.type]) {
|
||||
return getIncomingTxTableData(tx)
|
||||
}
|
||||
|
||||
|
|
|
@ -76,12 +76,12 @@ type IncomingTxServiceModel = {
|
|||
|
||||
export const buildTransactionFrom = async (
|
||||
safeAddress: string,
|
||||
tx: TxServiceModel,
|
||||
knownTokens,
|
||||
tx: TxServiceModel,
|
||||
txTokenCode,
|
||||
txTokenDecimals,
|
||||
txTokenSymbol,
|
||||
txTokenName,
|
||||
code,
|
||||
txTokenSymbol,
|
||||
): Promise<Transaction> => {
|
||||
const localSafe = await getLocalSafe(safeAddress)
|
||||
|
||||
|
@ -108,7 +108,7 @@ export const buildTransactionFrom = async (
|
|||
const modifySettingsTx = sameAddress(tx.to, safeAddress) && Number(tx.value) === 0 && !!tx.data
|
||||
const cancellationTx = sameAddress(tx.to, safeAddress) && Number(tx.value) === 0 && !tx.data
|
||||
const isERC721Token =
|
||||
(code && code.includes(SAFE_TRANSFER_FROM_WITHOUT_DATA_HASH)) ||
|
||||
(txTokenCode && txTokenCode.includes(SAFE_TRANSFER_FROM_WITHOUT_DATA_HASH)) ||
|
||||
(isTokenTransfer(tx.data, Number(tx.value)) && !knownTokens.get(tx.to) && txTokenDecimals !== null)
|
||||
let isSendTokenTx = !isERC721Token && isTokenTransfer(tx.data, Number(tx.value))
|
||||
const isMultiSendTx = isMultisendTransaction(tx.data, Number(tx.value))
|
||||
|
@ -118,7 +118,7 @@ export const buildTransactionFrom = async (
|
|||
let refundParams = null
|
||||
if (tx.gasPrice > 0) {
|
||||
const refundSymbol = txTokenSymbol || 'ETH'
|
||||
const decimals = txTokenName || 18
|
||||
const decimals = txTokenDecimals || 18
|
||||
const feeString = (tx.gasPrice * (tx.baseGas + tx.safeTxGas)).toString().padStart(decimals, 0)
|
||||
const whole = feeString.slice(0, feeString.length - decimals) || '0'
|
||||
const fraction = feeString.slice(feeString.length - decimals)
|
||||
|
@ -230,8 +230,8 @@ const addMockSafeCreationTx = (safeAddress): Array<TxServiceModel> => [
|
|||
const batchRequestTxsData = (txs: any[]) => {
|
||||
const web3Batch = new web3.BatchRequest()
|
||||
|
||||
const whenTxsValues = txs.map((tx) => {
|
||||
const methods = ['decimals', { method: 'getCode', type: 'eth', args: [tx.to] }, 'symbol', 'name']
|
||||
const txsTokenInfo = txs.map((tx) => {
|
||||
const methods = [{ method: 'getCode', type: 'eth', args: [tx.to] }, 'decimals', 'name', 'symbol']
|
||||
return generateBatchRequests({
|
||||
abi: ERC20Detailed.abi,
|
||||
address: tx.to,
|
||||
|
@ -243,7 +243,7 @@ const batchRequestTxsData = (txs: any[]) => {
|
|||
|
||||
web3Batch.execute()
|
||||
|
||||
return Promise.all(whenTxsValues)
|
||||
return Promise.all(txsTokenInfo)
|
||||
}
|
||||
|
||||
const batchRequestIncomingTxsData = (txs: IncomingTxServiceModel[]) => {
|
||||
|
@ -341,9 +341,15 @@ export const loadSafeTransactions = async (safeAddress: string, getState: GetSta
|
|||
const txsWithData = await batchRequestTxsData(transactions)
|
||||
// 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(
|
||||
txsWithData.map(([tx: TxServiceModel, decimals, code, symbol, name]) =>
|
||||
buildTransactionFrom(safeAddress, tx, knownTokens, decimals, symbol, name, code),
|
||||
),
|
||||
txsWithData.map(([tx: TxServiceModel, code, decimals, name, symbol]) => {
|
||||
const knownToken = knownTokens.get(tx.to)
|
||||
|
||||
if (knownToken) {
|
||||
;({ decimals, name, symbol } = knownToken)
|
||||
}
|
||||
|
||||
return buildTransactionFrom(safeAddress, knownTokens, tx, code, decimals, name, symbol)
|
||||
}),
|
||||
)
|
||||
|
||||
const groupedTxs = List(txsRecord).groupBy((tx) => (tx.get('cancellationTx') ? 'cancel' : 'outgoing'))
|
||||
|
|
|
@ -2,7 +2,12 @@
|
|||
import { Record } from 'immutable'
|
||||
import type { RecordFactory, RecordOf } from 'immutable'
|
||||
|
||||
export const INCOMING_TX_TYPES = ['INCOMING', 'ERC721_TRANSFER', 'ERC20_TRANSFER', 'ETHER_TRANSFER']
|
||||
export const INCOMING_TX_TYPES = {
|
||||
INCOMING: 'INCOMING',
|
||||
ERC721_TRANSFER: 'ERC721_TRANSFER',
|
||||
ERC20_TRANSFER: 'ERC20_TRANSFER',
|
||||
ETHER_TRANSFER: 'ETHER_TRANSFER',
|
||||
}
|
||||
|
||||
export type IncomingTransactionProps = {
|
||||
blockNumber: number,
|
||||
|
|
Loading…
Reference in New Issue