diff --git a/packages/nami/src/adapters/transactions.ts b/packages/nami/src/adapters/transactions.ts index 4225fc9024..38ee6f35cb 100644 --- a/packages/nami/src/adapters/transactions.ts +++ b/packages/nami/src/adapters/transactions.ts @@ -78,9 +78,10 @@ const getTxType = ({ }; const dateFromUnix = ( - slot: Wallet.Cardano.Slot, eraSummaries: OutsideHandlesContextValue['eraSummaries'], + slot?: Wallet.Cardano.Slot, ) => { + if (!slot) return new Date(); const slotTimeCalc = Wallet.createSlotTimeCalc(eraSummaries); const date = slotTimeCalc(slot); @@ -242,7 +243,7 @@ const getExtra = async ({ }: GetExtraProps): Promise => { const extra: Extra[] = []; - if (tx.witness.redeemers?.length) { + if (tx.witness?.redeemers?.length) { extra.push('contract'); } else if (txType === 'multisig') { extra.push('multisig'); @@ -305,6 +306,7 @@ export type TxInfo = Pick & lovelace: bigint; assets: NamiAsset[]; refund: string; + pending: boolean; }; export interface EncodeToCborArgs { @@ -369,31 +371,38 @@ export const getTxInfo = async ({ protocolParameters: Wallet.Cardano.ProtocolParameters; assetInfo: Wallet.Assets; rewardAccounts: Wallet.Cardano.RewardAccountInfo[]; -}): Promise => { +}): Promise => { const rewardAccountsAddresses = new Set(rewardAccounts?.map(a => a.address)); const currentAddress = addresses[0]; - if (!protocolParameters || !('blockHeader' in tx)) return undefined; const implicitCoin = Wallet.Cardano.util.computeImplicitCoin( protocolParameters, tx.body, ); const uTxOList = { - inputs: tx.body.inputs, + inputs: tx.body.inputs as unknown as Wallet.Cardano.HydratedTxIn[], outputs: tx.body.outputs, - collaterals: tx.body.collaterals, + collaterals: tx.body + .collaterals as unknown as Wallet.Cardano.HydratedTxIn[], }; const type = getTxType({ currentAddress, addresses: addresses, uTxOList, }); - const date = dateFromUnix(tx.blockHeader.slot, eraSummaries); + let date = new Date(); + if ('blockHeader' in tx) { + date = dateFromUnix(eraSummaries, tx.blockHeader.slot); + } else if ('submittedAt' in tx) { + date = dateFromUnix(eraSummaries, tx.submittedAt); + } const txInputs = await getTxInputsValueAndAddress(tx.body.inputs); const amounts = calculateAmount({ currentAddress, uTxOList: { ...uTxOList, inputs: txInputs }, - validContract: tx.inputSource === Wallet.Cardano.InputSource.inputs, + validContract: + 'inputSource' in tx && + tx.inputSource === Wallet.Cardano.InputSource.inputs, }); const assets = amounts.filter(amount => amount.unit !== 'lovelace'); const lovelaceAsset = amounts.find(amount => amount.unit === 'lovelace'); @@ -408,21 +417,23 @@ export const getTxInfo = async ({ }); const info: TxInfo = { + pending: !('blockHeader' in tx), txHash: tx.id.toString(), fees: tx.body.fee.toString(), deposit: implicitCoin.deposit?.toString() ?? '', refund: implicitCoin.reclaimDeposit?.toString() ?? '', - metadata: [...(tx.auxiliaryData?.blob?.entries() ?? [])].map( - ([key, value]) => ({ - label: key.toString(), - json_metadata: Wallet.cardanoMetadatumToObj(value), - }), - ), + metadata: [ + ...(('auxiliaryData' in tx ? tx.auxiliaryData?.blob?.entries() : []) ?? + []), + ].map(([key, value]) => ({ + label: key.toString(), + json_metadata: Wallet.cardanoMetadatumToObj(value), + })), date, timestamp: getTimestamp(date), type, extra: await getExtra({ - tx, + tx: tx as unknown as Wallet.Cardano.HydratedTx, txType: type, certificateInspectorFactory, rewardAccountsAddresses, @@ -483,6 +494,8 @@ export const mapWalletActivities = memoize( return await Promise.all( [ ...transactions.outgoing.inFlight, + // TODO: track signed tx + // ...transactions.outgoing.signed, ...transactions.history.sort( (tx1, tx2) => tx2.blockHeader.slot - tx1.blockHeader.slot, ), @@ -517,7 +530,7 @@ export const useWalletTxs = () => { assetInfo, createHistoricalOwnInputResolver, } = useOutsideHandles(); - const [txs, setTxs] = useState<(TxInfo | undefined)[]>(); + const [txs, setTxs] = useState(); const rewardAccounts = useObservable( inMemoryWallet.delegation.rewardAccounts$, ); diff --git a/packages/nami/src/ui/app/components/historyViewer.tsx b/packages/nami/src/ui/app/components/historyViewer.tsx index 4340e5fea8..17e0b76e38 100644 --- a/packages/nami/src/ui/app/components/historyViewer.tsx +++ b/packages/nami/src/ui/app/components/historyViewer.tsx @@ -24,9 +24,7 @@ const HistoryViewer = () => { const transactions = useWalletTxs(); const { cardanoCoin } = useCommonOutsideHandles(); - const [historySlice, setHistorySlice] = React.useState< - (TxInfo | undefined)[] | undefined - >(); + const [historySlice, setHistorySlice] = React.useState(); const [page, setPage] = React.useState(1); const [isFinal, setIsFinal] = React.useState(false); diff --git a/packages/nami/src/ui/app/components/transaction.tsx b/packages/nami/src/ui/app/components/transaction.tsx index eb9cba6cb1..de251382d9 100644 --- a/packages/nami/src/ui/app/components/transaction.tsx +++ b/packages/nami/src/ui/app/components/transaction.tsx @@ -15,7 +15,6 @@ import { VStack, Icon, useColorModeValue, - Skeleton, } from '@chakra-ui/react'; import TimeAgo from 'javascript-time-ago'; import en from 'javascript-time-ago/locale/en'; @@ -81,7 +80,7 @@ const txTypeLabel = { }; interface TransactionProps { - tx: TxInfo | undefined; + tx: TxInfo; network: OutsideHandlesContextValue['environmentName']; cardanoCoin: CommonOutsideHandlesContextValue['cardanoCoin']; openExternalLink: OutsideHandlesContextValue['openExternalLink']; @@ -95,8 +94,8 @@ const Transaction = ({ }: Readonly) => { const colorMode = { iconBg: useColorModeValue('white', 'gray.800'), - txBg: useColorModeValue('teal.50', 'gray.700'), - txBgHover: useColorModeValue('teal.100', 'gray.600'), + txBg: useColorModeValue(tx.pending ? 'gray.100' : 'teal.50', tx.pending ? 'gray.500' : 'gray.700'), + txBgHover: useColorModeValue(tx.pending ? 'gray.200' : 'teal.100', tx.pending ? 'gray.400' : 'gray.600'), assetsBtnHover: useColorModeValue('teal.200', 'gray.700'), }; @@ -115,119 +114,115 @@ const Transaction = ({ return ( - {tx ? ( - + + {tx.pending ? ( + Pending... + ) : ( + )} + + + + - ) : ( - - )} - {tx ? ( - - - - - - {tx.lovelace ? ( + {tx.lovelace ? ( + = 0 + ? txTypeColor.externalIn + : txTypeColor.externalOut + } + quantity={tx.lovelace} + decimals={6} + symbol={cardanoCoin.symbol} + /> + ) : ( + extraInfo + )} + {['internalIn', 'externalIn'].includes(tx.type) ? ( + '' + ) : ( + + Fee:{' '} = 0 - ? txTypeColor.externalIn - : txTypeColor.externalOut - } - quantity={tx.lovelace} + display="inline-block" + quantity={tx.fees} decimals={6} symbol={cardanoCoin.symbol} /> - ) : ( - extraInfo - )} - {['internalIn', 'externalIn'].includes(tx.type) ? ( - '' - ) : ( - - Fee:{' '} - - {!!Number.parseInt(tx.deposit) && ( - <> - {' & Deposit: '} - - - )} - {!!Number.parseInt(tx.refund) && ( - <> - {' & Refund: '} - - - )} - - )} + {!!Number.parseInt(tx.deposit) && ( + <> + {' & Deposit: '} + + + )} + {!!Number.parseInt(tx.refund) && ( + <> + {' & Refund: '} + + + )} + + )} - {tx.assets.length > 0 ? ( - - - - - - ) : ( - '' - )} - - - - ) : ( - - )} + {tx.assets.length > 0 ? ( + + + + + + ) : ( + '' + )} + + + {tx && (