diff --git a/components/Transaction/TransactionAmm.js b/components/Transaction/TransactionAmm.js index 523feabf8..91ef7484e 100644 --- a/components/Transaction/TransactionAmm.js +++ b/components/Transaction/TransactionAmm.js @@ -1,8 +1,15 @@ import { TData } from '../Table' import { TransactionCard } from './TransactionCard' -import { AddressWithIconFilled } from '../../utils/format' +import { + AddressWithIconFilled, + addressUsernameOrServiceLink, + amountFormat, + nativeCurrencyToFiat, + niceCurrency +} from '../../utils/format' import { divide } from '../../utils/calc' +import { addressBalanceChanges } from '../../utils/transaction' // AMM Flag definitions based on XRPL documentation const AMMWithdrawFlags = { @@ -24,6 +31,41 @@ const AMMDepositFlags = { twoAssetIfEmpty: 'Perform a special double-asset deposit to an AMM with an empty pool' } +const AMMClawbackFlags = { + tfClawTwoAssets: + "Claw back the specified amount of Asset, and a corresponding amount of Asset2 based on the AMM pool's asset proportion" +} + +// Helper function to create amount object with issuer details +const createAmountWithIssuer = (specification, outcome, sourceAddress, amountKey) => { + const amountData = specification?.[amountKey] + if (!amountData) return null + + return { + currency: amountData.currency, + issuer: amountData.issuer, + counterparty: amountData.counterparty, + issuerDetails: outcome?.balanceChanges + ?.find((change) => change.address === sourceAddress) + ?.balanceChanges?.find((change) => change.currency === amountData.currency)?.issuerDetails, + value: amountData.value + } +} + +// Helper function to render asset with issuer +const renderAssetWithIssuer = (assetData) => ( + <> + {niceCurrency(assetData.currency)} + {assetData.issuer && ( + <> + {' ('} + {addressUsernameOrServiceLink(assetData, 'issuer', { short: true })} + {')'} + > + )} + > +) + // Component to display AMM flags with tooltips const AMMFlags = ({ flags, txType }) => { if (!flags || typeof flags !== 'object') return '' @@ -33,6 +75,8 @@ const AMMFlags = ({ flags, txType }) => { Object.assign(flagDefinitions, AMMWithdrawFlags) } else if (txType === 'AMMDeposit') { Object.assign(flagDefinitions, AMMDepositFlags) + } else if (txType === 'AMMClawback') { + Object.assign(flagDefinitions, AMMClawbackFlags) } const activeFlags = [] @@ -89,9 +133,54 @@ const AMMFlags = ({ flags, txType }) => { export const TransactionAMM = ({ data, pageFiatRate, selectedCurrency }) => { if (!data) return null - const { specification, tx } = data + const { specification, tx, outcome } = data + const txType = tx.TransactionType const tradingFee = tx?.TradingFee + const amount = createAmountWithIssuer(specification, outcome, specification.source.address, 'amount') + const amount2 = createAmountWithIssuer(specification, outcome, specification.source.address, 'amount2') + const asset = specification?.asset + const asset2 = specification?.asset2 + const ePrice = specification?.EPrice + const lpTokenOut = specification?.LPTokenOut + const lpTokenIn = specification?.LPTokenIn + const bidMax = createAmountWithIssuer(specification, outcome, specification.source.address, 'bidMax') + const bidMin = createAmountWithIssuer(specification, outcome, specification.source.address, 'bidMin') + const holder = { + address: specification?.holder, + addressDetails: outcome?.balanceChanges?.find((change) => change.address === specification.holder)?.addressDetails + } + // Executor balance changes adjusted for fee + const sourceBalanceChangesList = addressBalanceChanges(data, specification.source.address) || [] + const depositedList = sourceBalanceChangesList.filter((c) => Number(c?.value) < 0) + const receivedList = sourceBalanceChangesList.filter((c) => Number(c?.value) > 0) + + // Helper function to render amount with issuer + const renderAmountWithIssuer = (amountData, options) => ( + <> + {amountFormat(amountData)} + {amountData.issuer && ( + <> + {'('} + {addressUsernameOrServiceLink(amountData, 'issuer', { short: true })} + {')'} + > + )} + {options?.includeFiat && + selectedCurrency && + amountData?.value && + amountData?.currency && + amountData?.value !== '0' && ( + + {nativeCurrencyToFiat({ + amount: amountData, + selectedCurrency, + fiatRate: pageFiatRate + })}{' '} + + )} + > + ) return ( { + {(txType === 'AMMVote' || txType === 'AMMBid' || txType === 'AMMDelete') && asset && asset2 && ( + + Liquidity pool + + {renderAssetWithIssuer(asset)} / {renderAssetWithIssuer(asset2)} + + + )} + {(txType === 'AMMCreate' || txType === 'AMMDeposit') && ( + <> + {depositedList.length > 0 && ( + + Deposited + + {depositedList.map((change, idx) => ( + + {renderAmountWithIssuer( + { ...change, value: Math.abs(Number(change.value)).toString() }, + { + includeFiat: true + } + )} + + ))} + + + )} + {receivedList.length > 0 && ( + + Received + + {receivedList.map((change, idx) => ( + {renderAmountWithIssuer(change, { includeFiat: true })} + ))} + + + )} + > + )} + {txType === 'AMMWithdraw' && ( + <> + {depositedList.length > 0 && ( + + Deposited + + {depositedList.map((change, idx) => ( + + {amountFormat({ ...change, value: Math.abs(Number(change.value)).toString() })} + {change?.issuer && <>({addressUsernameOrServiceLink(change, 'issuer', { short: true })})>} + + ))} + + + )} + {(() => { + const targetReceivedList = ( + holder?.address ? addressBalanceChanges(data, holder.address) || [] : sourceBalanceChangesList + ).filter((c) => Number(c?.value) > 0) + return targetReceivedList.length > 0 ? ( + + Withdrawn + + {targetReceivedList.map((change, idx) => ( + + {amountFormat(change)} + {change?.issuer && <>({addressUsernameOrServiceLink(change, 'issuer', { short: true })})>} + + ))} + + + ) : ( + '' + ) + })()} + > + )} + {ePrice && ( + + EPrice + {ePrice} + + )} + {lpTokenIn && ( + + LP Token In + {lpTokenIn} + + )} + {lpTokenOut && ( + + LP Token Out + {lpTokenOut} + + )} + {bidMax?.currency && bidMax?.value && bidMin?.currency && bidMin?.value && bidMax.value === bidMin.value ? ( + + Bid + {renderAmountWithIssuer(bidMax)} + + ) : ( + <> + {bidMax?.currency && bidMax?.value && ( + + Bid Max + {renderAmountWithIssuer(bidMax)} + + )} + {bidMin?.currency && bidMin?.value && ( + + Bid Min + {renderAmountWithIssuer(bidMin)} + + )} + > + )} + {holder?.address && ( + + Holder + + + + + )} {tradingFee ? ( Trading fee @@ -122,6 +334,37 @@ export const TransactionAMM = ({ data, pageFiatRate, selectedCurrency }) => { )} + + {txType === 'AMMDeposit' || txType === 'AMMCreate' || txType === 'AMMWithdraw' ? ( + + Specification + + It was instructed to {txType === 'AMMDeposit' || txType === 'AMMCreate' ? 'deposit' : 'withdraw'} maximum{' '} + {amount?.currency && amount?.value && ( + <> + {renderAmountWithIssuer(amount)} + {amount2?.currency && amount2?.value && ' and '} + > + )} + {amount2?.currency && amount2?.value && renderAmountWithIssuer(amount2)} + + + ) : ( + <> + {amount?.currency && amount?.value && ( + + Asset 1 + {renderAmountWithIssuer(amount)} + + )} + {amount2?.currency && amount2?.value && ( + + Asset 2 + {renderAmountWithIssuer(amount2)} + + )} + > + )} ) }