import {h, Fragment} from 'preact' import {Base64} from 'js-base64' import {encodeAssetFingerprint} from '../../../wallet/shelley/helpers/addresses' import printAda from '../../../helpers/printAda' import toLocalDate from '../../../helpers/toLocalDate' import { RewardWithdrawal, StakingHistoryItemType, StakingHistoryObject, StakingReward, HexString, Lovelace, TxSummaryEntry, Token, AssetFamily, Address, } from '../../../types' import {AdaIcon} from '../../common/svg' import { FormattedAssetItem, FormattedAssetItemProps, FormattedHumanReadableLabelType, } from '../../common/asset' import styles from './transactionHistory.module.scss' import * as moment from 'moment' import {useActiveAccount} from '../../../selectors' import {useSelector} from '../../../helpers/connect' import {createTokenRegistrySubject} from '../../../tokenRegistry/tokenRegistry' import printTokenAmount from '../../../helpers/printTokenAmount' import {getCexplorerUrl} from '../../../helpers//common' import BigNumber from 'bignumber.js' import {useState} from 'preact/hooks' const FormattedAmount = ({amount}: {amount: Lovelace}): h.JSX.Element => { const value = printAda(amount) return (
{amount.gt(0) ? `+${value}` : value}
) } const FormattedFee = ({fee}: {fee: Lovelace}): h.JSX.Element => { const value = printAda(fee) return (
{`Fee: ${value}`}
) } const FormattedTransaction = ({txid}: {txid: HexString}): h.JSX.Element => (
View on Cexplorer
) const MultiAsset = (props: FormattedAssetItemProps) => { return ( {({ icon, formattedHumanReadableLabelVariants, formattedAmount, formattedAssetLink, formattedFingerprint, }) => { const {quantity} = props return (
{formattedHumanReadableLabelVariants.label} {formattedAssetLink}
{quantity.gt(0) ? `+${formattedAmount}` : formattedAmount}
{icon}
{formattedHumanReadableLabelVariants.type === FormattedHumanReadableLabelType.FINGERPRINT ? '' : formattedFingerprint}
) }}
) } interface Props { transactionHistory: Array stakingHistory: Array } const ExportCSV = ({transactionHistory, stakingHistory}: Props): h.JSX.Element => { const tokensMetadata = useSelector((state) => state.tokensMetadata) const withdrawalHistory: {[key: string]: Lovelace} = stakingHistory .filter((item) => item.type === StakingHistoryItemType.REWARD_WITHDRAWAL) .reduce((acc, withdrawal: RewardWithdrawal) => { acc[withdrawal.txHash] = withdrawal.amount return acc }, {}) const stakingRewards = stakingHistory.filter( (item) => item.type === StakingHistoryItemType.STAKING_REWARD ) as Array const delimiter = ',' const rowsDelimiter = '\n' const headers = [ 'Date', 'Transaction ID', 'Type', 'Received from (disclaimer: may not be accurate - first sender address only)', 'Received amount', 'Received currency', 'Sent amount', 'Sent currency', 'Fee amount', 'Fee currency', ] .map((header) => `${header}${delimiter}`) .join('') const enum TxSummaryType { RECEIVED = 'Received', SENT = 'Sent', REWARD_AWARDED = 'Reward awarded', } type TxEntry = { type: TxSummaryType txHash?: string dateTime: moment.Moment fee?: Lovelace currency: string receivedFrom?: Address } & ( | { assetFamily: AssetFamily.ADA sent?: Lovelace received?: Lovelace } | { assetFamily: AssetFamily.TOKEN sent?: BigNumber received?: BigNumber decimals: number } ) const transactionsEntries: Array = transactionHistory.flatMap( (transaction: TxSummaryEntry): Array => { const common = { txHash: transaction.ctbId, dateTime: moment.utc(new Date(transaction.ctbTimeIssued * 1000)), } const createTokenEntries = (): Array => transaction.tokenEffects.map((tokenEffect: Token) => { const tokenMetadata = tokensMetadata.get( createTokenRegistrySubject(tokenEffect.policyId, tokenEffect.assetName) ) const ticker = tokenMetadata?.ticker const fingerprint = encodeAssetFingerprint(tokenEffect.policyId, tokenEffect.assetName) return { ...common, ...(tokenEffect.quantity.gt(0) ? { type: TxSummaryType.RECEIVED, received: tokenEffect.quantity, receivedFrom: transaction.fromAddresses[0], } : { type: TxSummaryType.SENT, sent: tokenEffect.quantity.abs(), }), assetFamily: AssetFamily.TOKEN, currency: ticker ? `${ticker} (${fingerprint})` : fingerprint, decimals: tokenMetadata?.decimals || 0, } }) if (withdrawalHistory.hasOwnProperty(transaction.ctbId)) { return [ { ...common, type: TxSummaryType.SENT, assetFamily: AssetFamily.ADA, sent: transaction.effect .minus(withdrawalHistory[transaction.ctbId]) .abs() .minus(transaction.fee) as Lovelace, fee: transaction.fee, currency: 'ADA', }, ] } else if (transaction.effect.gt(0)) { return [ { ...common, type: TxSummaryType.RECEIVED, assetFamily: AssetFamily.ADA, received: transaction.effect, receivedFrom: transaction.fromAddresses[0], currency: 'ADA', }, ...createTokenEntries(), ] } else { return [ { ...common, type: TxSummaryType.SENT, assetFamily: AssetFamily.ADA, sent: transaction.effect.abs().minus(transaction.fee) as Lovelace, fee: transaction.fee, currency: 'ADA', }, ...createTokenEntries(), ] } } ) const rewardsEntries: Array = stakingRewards.map((stakingReward: StakingReward) => ({ type: TxSummaryType.REWARD_AWARDED, assetFamily: AssetFamily.ADA, dateTime: moment(stakingReward.dateTime), received: stakingReward.reward, currency: 'ADA', })) const entries: Array = [...transactionsEntries, ...rewardsEntries].sort( (a, b) => b.dateTime.unix() - a.dateTime.unix() ) const rows: Array = entries.map((entry) => { const printAmount = (amount: BigNumber | Lovelace) => entry.assetFamily === AssetFamily.ADA ? printAda(amount as Lovelace) : printTokenAmount(amount, entry.decimals as number) return [ entry.dateTime.format('MM/DD/YYYY hh:mm A [UTC]'), entry.txHash, entry.type, entry.received && entry.receivedFrom, entry.received && printAmount(entry.received), entry.received !== undefined ? entry.currency : undefined, entry.sent && printAmount(entry.sent), entry.sent !== undefined ? entry.currency : undefined, entry.fee && printAda(entry.fee as Lovelace), entry.fee !== undefined ? 'ADA' : undefined, ] .map((value) => (value === undefined ? delimiter : `${value}${delimiter}`)) .join('') }) const fileContents = `${headers}${rowsDelimiter}${rows.join(rowsDelimiter)}` const filename = 'transactions.csv' const filetype = 'text/plain' const dataURI = `data:${filetype};base64,${Base64.encode(fileContents)}` return ( Export to CSV ) } const DEFAULT_TRANSACTIONS_LIMIT = 50 const TransactionHistory = (): h.JSX.Element => { const {transactionHistory, stakingHistory} = useActiveAccount() const [showAll, setShowAll] = useState(false) return (

Transaction History

{transactionHistory.length === 0 ? (
No transactions found
) : (
    {(showAll ? transactionHistory : transactionHistory.slice(0, DEFAULT_TRANSACTIONS_LIMIT) ).map((transaction: TxSummaryEntry) => (
  • {toLocalDate(new Date(transaction.ctbTimeIssued * 1000))}
    {transaction.tokenEffects.map((tokenEffect: Token) => ( ))}
  • ))} {transactionHistory.length > DEFAULT_TRANSACTIONS_LIMIT && !showAll && (
  • setShowAll(true)}> show all
  • )}
)}
) } export default TransactionHistory