(Feature) Tx Table amount notations (#812)

This commit is contained in:
Fernando 2020-04-27 12:15:33 -03:00 committed by GitHub
parent 5014e86c3a
commit 653f68b09c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 69 additions and 47 deletions

View File

@ -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, *>(
{

View File

@ -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) {

View File

@ -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>
)
}

View File

@ -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 && (

View File

@ -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}

View File

@ -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)
}

View File

@ -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'))

View File

@ -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,