Skip to content
This repository has been archived by the owner on Nov 10, 2023. It is now read-only.

(Feature) Tx Table amount notations #812

Merged
merged 14 commits into from
Apr 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/logic/tokens/store/reducer/tokens.js
Original file line number Diff line number Diff line change
Expand Up @@ -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, *>(
{
Expand Down
2 changes: 1 addition & 1 deletion src/logic/tokens/utils/formatAmount.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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>
Expand Down Expand Up @@ -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}
Expand Down
52 changes: 31 additions & 21 deletions src/routes/safe/components/Transactions/TxsTable/columns.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand All @@ -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

let txAmount = 'n/a'

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}`
const NOT_AVAILABLE = '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
mmv08 marked this conversation as resolved.
Show resolved Hide resolved
const txAmount = finalValue === 'NaN' ? NOT_AVAILABLE : finalValue

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 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 txAmount
return getAmountWithSymbol({ decimals, symbol, value }, formatted)
}

export type TransactionRow = SortRow<TxData>
Expand Down Expand Up @@ -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)
}

Expand Down
28 changes: 17 additions & 11 deletions src/routes/safe/store/actions/fetchTransactions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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))
Expand All @@ -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)
Expand Down Expand Up @@ -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,
Expand All @@ -243,7 +243,7 @@ const batchRequestTxsData = (txs: any[]) => {

web3Batch.execute()

return Promise.all(whenTxsValues)
return Promise.all(txsTokenInfo)
}

const batchRequestIncomingTxsData = (txs: IncomingTxServiceModel[]) => {
Expand Down Expand Up @@ -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'))
Expand Down
7 changes: 6 additions & 1 deletion src/routes/safe/store/models/incomingTransaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down