Skip to content

Commit

Permalink
feat: optimize transaction details rendering (#1528)
Browse files Browse the repository at this point in the history
  • Loading branch information
AngelCastilloB authored Nov 12, 2024
1 parent 82561b3 commit acd5cef
Show file tree
Hide file tree
Showing 13 changed files with 103 additions and 95 deletions.
4 changes: 2 additions & 2 deletions apps/browser-extension-wallet/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@
"@cardano-sdk/tx-construction": "0.21.10",
"@cardano-sdk/util": "0.15.5",
"@cardano-sdk/util-rxjs": "0.7.38",
"@cardano-sdk/wallet": "0.44.16",
"@cardano-sdk/web-extension": "0.34.15",
"@cardano-sdk/wallet": "0.44.17",
"@cardano-sdk/web-extension": "0.34.16",
"@emurgo/cip14-js": "~3.0.1",
"@input-output-hk/lace-ui-toolkit": "1.21.0",
"@lace/cardano": "0.1.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ const inMemoryWallet = {
available$
},
transactions: {
history$: new BehaviorSubject([]),
outgoing: {
signed$
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
UISlice
} from '../types';
import { CardanoTxOut, Transaction, ActivityDetail, TransactionActivityDetail } from '../../types';
import { blockTransformer, inputOutputTransformer } from '../../api/transformers';
import { inputOutputTransformer } from '../../api/transformers';
import { Wallet } from '@lace/cardano';
import { getTransactionTotalOutput } from '../../utils/get-transaction-total-output';
import { inspectTxValues } from '@src/utils/tx-inspection';
Expand All @@ -27,6 +27,7 @@ import { formatDate, formatTime } from '@src/utils/format-date';
import { createHistoricalOwnInputResolver, HistoricalOwnInputResolverArgs } from '@src/utils/own-input-resolver';
import { getCollateral } from '@cardano-sdk/core';
import { hasPhase2ValidationFailed } from '@src/utils/phase2-validation';
import { eraSlotDateTime } from '@utils/era-slot-datetime';

/**
* validates if the transaction is confirmed
Expand Down Expand Up @@ -82,25 +83,43 @@ const shouldIncludeFee = (
);
};

const poolsVolatileCache: Map<Wallet.Cardano.PoolId, Wallet.Cardano.StakePool> = new Map();

export const getPoolInfos = async (
poolIds: Wallet.Cardano.PoolId[],
stakePoolProvider: Wallet.StakePoolProvider
): Promise<Wallet.Cardano.StakePool[]> => {
const filters: Wallet.QueryStakePoolsArgs = {
filters: {
identifier: {
_condition: 'or',
values: poolIds.map((poolId) => ({ id: poolId }))
}
},
pagination: {
startAt: 0,
limit: MAX_POOLS_COUNT
const poolsToFetch = [...poolIds];
const result: Wallet.Cardano.StakePool[] = [];

for (const poolId of poolIds) {
const pool = poolsVolatileCache.get(poolId);
if (pool) {
result.push(pool);
poolsToFetch.splice(poolsToFetch.indexOf(poolId), 1);
}
};
const { pageResults: pools } = await stakePoolProvider.queryStakePools(filters);
}

if (poolsToFetch.length > 0) {
const filters: Wallet.QueryStakePoolsArgs = {
filters: {
identifier: {
_condition: 'or',
values: poolsToFetch.map((poolId) => ({ id: poolId }))
}
},
pagination: {
startAt: 0,
limit: MAX_POOLS_COUNT
}
};
const { pageResults: fetchedPools } = await stakePoolProvider.queryStakePools(filters);

return pools;
fetchedPools.forEach((pool) => poolsVolatileCache.set(pool.id, pool));
result.push(...fetchedPools);
}

return result;
};

const computeCollateral = async (
Expand Down Expand Up @@ -207,12 +226,6 @@ const buildGetActivityDetail =
const totalOutput = getTransactionTotalOutput(tx.body.outputs).minus(tx.body.fee.toString());
const totalOutputInAda = Wallet.util.lovelacesToAdaString(totalOutput.toString());

// Block Info
const txBlock = isConfirmedTransaction(tx)
? await Wallet.getBlockInfoByHash(tx.blockHeader.hash, chainHistoryProvider, stakePoolProvider)
: undefined;
const blocks = txBlock ? blockTransformer(txBlock) : undefined;

// Metadata
const txMetadata = !isEmpty(tx.auxiliaryData?.blob)
? transactionMetadataTransformer(tx.auxiliaryData.blob)
Expand Down Expand Up @@ -245,6 +258,16 @@ const buildGetActivityDetail =
(certificate) => certificate.__typename === Wallet.Cardano.CertificateType.StakeDelegation
) as Wallet.Cardano.StakeDelegationCertificate[];

let utcDate;
let utcTime;

if (isConfirmedTransaction(tx)) {
const summaries = await firstValueFrom(wallet.eraSummaries$);
const times = eraSlotDateTime(summaries, tx.blockHeader?.slot);
utcDate = times.utcDate;
utcTime = times.utcTime;
}

let transaction: ActivityDetail['activity'] = {
hash: tx.id.toString(),
totalOutput: totalOutputInAda,
Expand All @@ -254,8 +277,8 @@ const buildGetActivityDetail =
addrInputs: inputs,
addrOutputs: outputs,
metadata: txMetadata,
includedUtcDate: blocks?.utcDate,
includedUtcTime: blocks?.utcTime,
includedUtcDate: utcDate,
includedUtcTime: utcTime,
collateral: collateralInAda,
votingProcedures: tx.body.votingProcedures,
proposalProcedures: tx.body.proposalProcedures,
Expand Down Expand Up @@ -283,7 +306,7 @@ const buildGetActivityDetail =
}

set({ fetchingActivityInfo: false });
return { activity: transaction, blocks, status, assetAmount, type };
return { activity: transaction, status, assetAmount, type };
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ interface UtxoAndBackendChainHistoryResolverArgs {
utxo: Wallet.ObservableWallet['utxo'];
chainHistoryProvider: Wallet.ChainHistoryProvider;
transactions: {
history$: Observable<Cardano.HydratedTx[]>;
outgoing: {
signed$: Observable<WitnessedTx[]>;
};
Expand Down
4 changes: 2 additions & 2 deletions packages/cardano/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@
"@cardano-sdk/key-management": "0.24.8",
"@cardano-sdk/tx-construction": "0.21.10",
"@cardano-sdk/util": "0.15.5",
"@cardano-sdk/wallet": "0.44.16",
"@cardano-sdk/web-extension": "0.34.15",
"@cardano-sdk/wallet": "0.44.17",
"@cardano-sdk/web-extension": "0.34.16",
"@lace/common": "0.1.0",
"@ledgerhq/devices": "^8.2.1",
"@stablelib/chacha20poly1305": "1.0.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ describe('Testing getTxInputsValueAndAddress function', () => {
unspendable$: of([])
},
transactions: {
history$: of([]),
outgoing: {
signed$: of([])
}
Expand Down Expand Up @@ -264,7 +265,7 @@ describe('Testing getTxInputsValueAndAddress function', () => {
});
});

test('should return inputs with value and address for more inputs than pagination limit', async () => {
test('should return inputs with value and address for every input', async () => {
const transactionsByHashes = jest.fn().mockImplementation(({ ids }) =>
Promise.resolve(
ids.map((id: Cardano.TransactionId) => ({
Expand Down Expand Up @@ -293,7 +294,7 @@ describe('Testing getTxInputsValueAndAddress function', () => {

await act(async () => {
const inputs = await result;
expect(transactionsByHashes).toBeCalledTimes(2);
expect(transactionsByHashes).toBeCalledTimes(30);
expect(inputs.every(({ value, address }) => value && address)).toBe(true);
expect(inputs).toHaveLength(30);
});
Expand Down
51 changes: 12 additions & 39 deletions packages/cardano/src/wallet/lib/get-inputs-value.ts
Original file line number Diff line number Diff line change
@@ -1,61 +1,34 @@
import { Cardano, ChainHistoryProvider } from '@cardano-sdk/core';
import { createWalletUtil, ObservableWallet } from '@cardano-sdk/wallet';
import flattenDeep from 'lodash/flattenDeep';

const TX_PAGINATION_LIMIT = 25;

export type TxInput = { value?: Cardano.Value; address?: Cardano.HydratedTxIn['address'] } & Pick<
Cardano.HydratedTxIn,
'index' | 'txId'
>;

const fetchTransactionByHashes = async (chainProviderInstance: ChainHistoryProvider, ids: Cardano.TransactionId[]) => {
if (ids.length <= TX_PAGINATION_LIMIT) {
return chainProviderInstance.transactionsByHashes({ ids });
}

const paginatedPromises = [];
for (let i = 0; i < ids.length; i += TX_PAGINATION_LIMIT) {
paginatedPromises.push(chainProviderInstance.transactionsByHashes({ ids: ids.slice(i, i + TX_PAGINATION_LIMIT) }));
}
return flattenDeep(await Promise.all(paginatedPromises));
};

export const getTxInputsValueAndAddress = async (
inputs: Cardano.HydratedTxIn[] | Cardano.TxIn[],
chainProviderInstance: ChainHistoryProvider,
wallet: ObservableWallet
): Promise<TxInput[]> => {
const inputsOutputsMapping = new Map<Cardano.TransactionId, Cardano.TxOut>();
const txIdsToResolveInputs = new Set<Cardano.TransactionId>();
const util = createWalletUtil(wallet);
const resolvedInputs = new Array<Cardano.Utxo>();
const util = createWalletUtil({
utxo: wallet.utxo,
transactions: wallet.transactions,
protocolParameters$: wallet.protocolParameters$,
chainHistoryProvider: chainProviderInstance
});

for (const input of inputs) {
const resolvedInput = await util.resolveInput(input);
if (resolvedInput) {
inputsOutputsMapping.set(input.txId, resolvedInput);
} else {
txIdsToResolveInputs.add(input.txId);
}
const output = await util.resolveInput(input);
resolvedInputs.push([{ address: output.address, ...input }, output]);
}

const txsWithResolvedInputs: Cardano.HydratedTx[] = await fetchTransactionByHashes(chainProviderInstance, [
...txIdsToResolveInputs
]);

return inputs.map((input) => {
let txOut = inputsOutputsMapping.get(input.txId);

for (const transaction of txsWithResolvedInputs) {
if (transaction.id === input.txId) {
txOut = transaction.body.outputs[input.index];
}
}

const { address, value } = txOut;
return resolvedInputs.map((utxo) => {
const { address, value } = utxo[1];

return {
...input,
...utxo[0],
value,
address
};
Expand Down
4 changes: 2 additions & 2 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@
},
"dependencies": {
"@ant-design/icons": "^4.7.0",
"@cardano-sdk/wallet": "0.44.16",
"@cardano-sdk/web-extension": "0.34.15",
"@cardano-sdk/wallet": "0.44.17",
"@cardano-sdk/web-extension": "0.34.16",
"@input-output-hk/lace-ui-toolkit": "1.19.0",
"@lace/cardano": "0.1.0",
"@lace/common": "0.1.0",
Expand Down
8 changes: 8 additions & 0 deletions packages/e2e-tests/src/elements/transactionDetails.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable no-undef */
import { ChainablePromiseElement } from 'webdriverio';
import CommonDrawerElements from './CommonDrawerElements';
import { browser } from '@wdio/globals';

class ActivityDetailsPage extends CommonDrawerElements {
protected CONTAINER = '[data-testid="custom-drawer"]';
Expand Down Expand Up @@ -383,6 +384,13 @@ class ActivityDetailsPage extends CommonDrawerElements {
async getTransactionDetailsStakepoolIds(): Promise<string[]> {
return await this.getTextValues(await this.transactionDetailsStakePoolIds);
}

async waitUntilTxHashNotEmpty() {
await browser.waitUntil(async () => (await this.transactionDetailsHash.getText()) !== '', {
timeout: 6000,
timeoutMsg: 'failed while waiting for tx hash value'
});
}
}

export default new ActivityDetailsPage();
1 change: 1 addition & 0 deletions packages/e2e-tests/src/steps/transactionsSteps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ When(
await TransactionsPage.clickOnTransactionRow(i);
await TransactionDetailsPage.transactionDetailsSkeleton.waitForDisplayed({ timeout: 30_000, reverse: true });
if (valueForCheck === 'hash') {
await TransactionDetailsPage.waitUntilTxHashNotEmpty();
actualValue = await TransactionDetailsPage.transactionDetailsHash.getText();
expectedValue = String(testContext.load('txHashValue'));
} else {
Expand Down
2 changes: 1 addition & 1 deletion packages/nami/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
"@cardano-sdk/crypto": "0.1.30",
"@cardano-sdk/tx-construction": "0.21.10",
"@cardano-sdk/util": "0.15.5",
"@cardano-sdk/web-extension": "0.34.15",
"@cardano-sdk/web-extension": "0.34.16",
"@chakra-ui/css-reset": "1.0.0",
"@chakra-ui/icons": "1.0.13",
"@chakra-ui/react": "1.6.4",
Expand Down
8 changes: 4 additions & 4 deletions packages/staking/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@
"@cardano-sdk/input-selection": "0.13.25",
"@cardano-sdk/tx-construction": "0.21.10",
"@cardano-sdk/util": "0.15.5",
"@cardano-sdk/wallet": "0.44.16",
"@cardano-sdk/web-extension": "0.34.15",
"@cardano-sdk/wallet": "0.44.17",
"@cardano-sdk/web-extension": "0.34.16",
"@storybook/addon-actions": "^7.6.7",
"@storybook/addon-essentials": "^7.6.7",
"@storybook/addon-interactions": "^7.6.7",
Expand Down Expand Up @@ -129,8 +129,8 @@
"@cardano-sdk/input-selection": "0.13.25",
"@cardano-sdk/tx-construction": "0.21.10",
"@cardano-sdk/util": "0.15.5",
"@cardano-sdk/wallet": "0.44.16",
"@cardano-sdk/web-extension": "0.34.15",
"@cardano-sdk/wallet": "0.44.17",
"@cardano-sdk/web-extension": "0.34.16",
"@lace/cardano": "^0.1.0",
"@lace/common": "^0.1.0",
"@lace/core": "0.1.0",
Expand Down
Loading

0 comments on commit acd5cef

Please sign in to comment.