Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(nami): pending tx poc #1608

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
45 changes: 29 additions & 16 deletions packages/nami/src/adapters/transactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -242,7 +243,7 @@ const getExtra = async ({
}: GetExtraProps): Promise<Extra[]> => {
const extra: Extra[] = [];

if (tx.witness.redeemers?.length) {
if (tx.witness?.redeemers?.length) {
extra.push('contract');
} else if (txType === 'multisig') {
extra.push('multisig');
Expand Down Expand Up @@ -305,6 +306,7 @@ export type TxInfo = Pick<TransactionDetail, 'metadata'> &
lovelace: bigint;
assets: NamiAsset[];
refund: string;
pending: boolean;
};

export interface EncodeToCborArgs {
Expand Down Expand Up @@ -369,31 +371,38 @@ export const getTxInfo = async ({
protocolParameters: Wallet.Cardano.ProtocolParameters;
assetInfo: Wallet.Assets;
rewardAccounts: Wallet.Cardano.RewardAccountInfo[];
}): Promise<TxInfo | undefined> => {
}): Promise<TxInfo> => {
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');
Expand All @@ -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,
Expand Down Expand Up @@ -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,
),
Expand Down Expand Up @@ -517,7 +530,7 @@ export const useWalletTxs = () => {
assetInfo,
createHistoricalOwnInputResolver,
} = useOutsideHandles();
const [txs, setTxs] = useState<(TxInfo | undefined)[]>();
const [txs, setTxs] = useState<TxInfo[]>();
const rewardAccounts = useObservable(
inMemoryWallet.delegation.rewardAccounts$,
);
Expand Down
4 changes: 1 addition & 3 deletions packages/nami/src/ui/app/components/historyViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<TxInfo[] | undefined>();
const [page, setPage] = React.useState(1);
const [isFinal, setIsFinal] = React.useState(false);

Expand Down
203 changes: 99 additions & 104 deletions packages/nami/src/ui/app/components/transaction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -81,7 +80,7 @@ const txTypeLabel = {
};

interface TransactionProps {
tx: TxInfo | undefined;
tx: TxInfo;
network: OutsideHandlesContextValue['environmentName'];
cardanoCoin: CommonOutsideHandlesContextValue['cardanoCoin'];
openExternalLink: OutsideHandlesContextValue['openExternalLink'];
Expand All @@ -95,8 +94,8 @@ const Transaction = ({
}: Readonly<TransactionProps>) => {
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'),
};

Expand All @@ -115,119 +114,115 @@ const Transaction = ({
return (
<AccordionItem borderTop="none" _last={{ borderBottom: 'none' }}>
<VStack spacing={2}>
{tx ? (
<Box align="center" fontSize={14} fontWeight={500} color="gray.500">
<Box align="center" fontSize={14} fontWeight={500} color="gray.500">
{tx.pending ? (
<Text>Pending...</Text>
) : (
<ReactTimeAgo
date={tx.date}
locale="en-US"
timeStyle="round-minute"
/>
)}
</Box>
<AccordionButton
data-testid={`transaction-button-${tx.txHash}`}
display="flex"
wordBreak="break-word"
justifyContent="space-between"
bg={colorMode.txBg}
borderRadius={10}
borderLeftRadius={30}
p={0}
_hover={{ backgroundColor: colorMode.txBgHover }}
_focus={{ border: 'none' }}
>
<Box
display="flex"
flexShrink={5}
p={5}
borderRadius={50}
bg={colorMode.iconBg}
position="relative"
left="-15px"
>
<TxIcon txType={tx.type} extra={tx.extra} />
</Box>
) : (
<Skeleton width="34%" height="22px" rounded="md" />
)}
{tx ? (
<AccordionButton
data-testid={`transaction-button-${tx.txHash}`}
<Box
display="flex"
wordBreak="break-word"
justifyContent="space-between"
bg={colorMode.txBg}
borderRadius={10}
borderLeftRadius={30}
p={0}
_hover={{ backgroundColor: colorMode.txBgHover }}
_focus={{ border: 'none' }}
flexDirection="column"
textAlign="center"
position="relative"
left="-15px"
>
<Box
display="flex"
flexShrink={5}
p={5}
borderRadius={50}
bg={colorMode.iconBg}
position="relative"
left="-15px"
>
<TxIcon txType={tx.type} extra={tx.extra} />
</Box>
<Box
display="flex"
flexDirection="column"
textAlign="center"
position="relative"
left="-15px"
>
{tx.lovelace ? (
{tx.lovelace ? (
<UnitDisplay
fontSize={18}
color={
tx.lovelace >= 0
? txTypeColor.externalIn
: txTypeColor.externalOut
}
quantity={tx.lovelace}
decimals={6}
symbol={cardanoCoin.symbol}
/>
) : (
extraInfo
)}
{['internalIn', 'externalIn'].includes(tx.type) ? (
''
) : (
<Box flexDirection="row" fontSize={12}>
Fee:{' '}
<UnitDisplay
fontSize={18}
color={
tx.lovelace >= 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) ? (
''
) : (
<Box flexDirection="row" fontSize={12}>
Fee:{' '}
<UnitDisplay
display="inline-block"
quantity={tx.fees}
decimals={6}
symbol={cardanoCoin.symbol}
/>
{!!Number.parseInt(tx.deposit) && (
<>
{' & Deposit: '}
<UnitDisplay
display="inline-block"
quantity={tx.deposit}
decimals={6}
symbol={cardanoCoin.symbol}
/>
</>
)}
{!!Number.parseInt(tx.refund) && (
<>
{' & Refund: '}
<UnitDisplay
display="inline-block"
quantity={tx.refund}
decimals={6}
symbol={cardanoCoin.symbol}
/>
</>
)}
</Box>
)}
{!!Number.parseInt(tx.deposit) && (
<>
{' & Deposit: '}
<UnitDisplay
display="inline-block"
quantity={tx.deposit}
decimals={6}
symbol={cardanoCoin.symbol}
/>
</>
)}
{!!Number.parseInt(tx.refund) && (
<>
{' & Refund: '}
<UnitDisplay
display="inline-block"
quantity={tx.refund}
decimals={6}
symbol={cardanoCoin.symbol}
/>
</>
)}
</Box>
)}

{tx.assets.length > 0 ? (
<Box flexDirection="row" fontSize={12}>
<Text
display="inline-block"
fontWeight="bold"
_hover={{ backgroundColor: colorMode.assetsBtnHover }}
borderRadius="md"
>
<AssetsPopover assets={tx.assets} isDifference />
</Text>
</Box>
) : (
''
)}
</Box>
<AccordionIcon color="teal.400" mr={5} fontSize={20} />
</AccordionButton>
) : (
<Skeleton width="100%" height="72px" rounded="md" />
)}
{tx.assets.length > 0 ? (
<Box flexDirection="row" fontSize={12}>
<Text
display="inline-block"
fontWeight="bold"
_hover={{ backgroundColor: colorMode.assetsBtnHover }}
borderRadius="md"
>
<AssetsPopover assets={tx.assets} isDifference />
</Text>
</Box>
) : (
''
)}
</Box>
<AccordionIcon color="teal.400" mr={5} fontSize={20} />
</AccordionButton>
<AccordionPanel wordBreak="break-word" pb={4}>
{tx && (
<TxDetail
Expand Down
Loading