Skip to content

Commit d06fd8a

Browse files
feat: account live transactions (#53)
* feat: add account live transactions * chore: refactoring based on changes in main --------- Co-authored-by: Neil Campbell <[email protected]>
1 parent 97be076 commit d06fd8a

19 files changed

+154
-183
lines changed

src/features/accounts/components/account-activity-tabs.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { useMemo } from 'react'
44
import { AccountAssetsHeld } from './account-assets-held'
55
import { Account } from '../models'
66
import { AccountTransactionHistory } from './account-transaction-history'
7+
import { AccountLiveTransactions } from './account-live-transactions'
78
import {
89
accountActivityLabel,
910
accountLiveTransactionsTabId,
@@ -34,7 +35,7 @@ export function AccountActivityTabs({ account }: Props) {
3435
{
3536
id: accountLiveTransactionsTabId,
3637
label: accountLiveTransactionsTabLabel,
37-
children: '',
38+
children: <AccountLiveTransactions address={account.address} />,
3839
},
3940
{
4041
id: accountHistoricalTransactionsTabId,
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { LiveTransactionsTable } from '@/features/transactions/components/live-transactions-table'
2+
import { TransactionResult } from '@algorandfoundation/algokit-utils/types/indexer'
3+
import { useCallback } from 'react'
4+
import { Address } from '../data/types'
5+
import { getAddressesForTransaction } from '../utils/get-account-address-for-transactions'
6+
import { InnerTransaction, Transaction } from '@/features/transactions/models'
7+
import { getAccountTransactionsTableSubRows } from '../utils/get-account-transactions-table-sub-rows'
8+
import { transactionsTableColumns } from '@/features/transactions/components/transactions-table-columns'
9+
10+
type Props = {
11+
address: Address
12+
}
13+
14+
export function AccountLiveTransactions({ address }: Props) {
15+
const filter = useCallback(
16+
(transactionResult: TransactionResult) => {
17+
const addressesForTransaction = getAddressesForTransaction(transactionResult)
18+
return addressesForTransaction.includes(address)
19+
},
20+
[address]
21+
)
22+
23+
const getSubRows = useCallback((row: Transaction | InnerTransaction) => getAccountTransactionsTableSubRows(address, row), [address])
24+
return <LiveTransactionsTable filter={filter} getSubRows={getSubRows} columns={transactionsTableColumns} />
25+
}
Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
import { LazyLoadDataTable } from '@/features/common/components/lazy-load-data-table'
22
import { Address } from '../data/types'
33
import { useFetchNextAccountTransactionPage } from '../data/account-transaction-history'
4-
import { accountTransactionsTableColumns } from './account-transaction-table-columns'
4+
import { useCallback } from 'react'
5+
import { getAccountTransactionsTableSubRows } from '../utils/get-account-transactions-table-sub-rows'
6+
import { InnerTransaction, Transaction } from '@/features/transactions/models'
7+
import { transactionsTableColumns } from '@/features/transactions/components/transactions-table-columns'
58

69
type Props = {
710
address: Address
811
}
912

1013
export function AccountTransactionHistory({ address }: Props) {
1114
const fetchNextPage = useFetchNextAccountTransactionPage(address)
15+
const getSubRows = useCallback((row: Transaction | InnerTransaction) => getAccountTransactionsTableSubRows(address, row), [address])
1216

13-
return <LazyLoadDataTable columns={accountTransactionsTableColumns} fetchNextPage={fetchNextPage} />
17+
return <LazyLoadDataTable columns={transactionsTableColumns} getSubRows={getSubRows} fetchNextPage={fetchNextPage} />
1418
}

src/features/accounts/components/account-transaction-table-columns.tsx

Lines changed: 0 additions & 57 deletions
This file was deleted.

src/features/accounts/data/account-transaction-history.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,8 @@ import { JotaiStore } from '@/features/common/data/types'
66
import { createTransactionsAtom, transactionResultsAtom } from '@/features/transactions/data'
77
import { atomEffect } from 'jotai-effect'
88
import { atom, useStore } from 'jotai'
9-
import { extractTransactionsForAccount } from '../utils/extract-transaction-for-account'
109

11-
const fetchAccountTransactionResults = async (address: Address, pageSize: number, nextPageToken?: string) => {
10+
const getAccountTransactionResults = async (address: Address, pageSize: number, nextPageToken?: string) => {
1211
const results = (await indexer
1312
.searchForTransactions()
1413
.address(address)
@@ -41,16 +40,16 @@ const createSyncEffect = (transactionResults: TransactionResult[]) => {
4140
})
4241
}
4342

44-
const creatAccountTransactionAtom = (store: JotaiStore, address: Address, pageSize: number, nextPageToken?: string) => {
43+
const createAccountTransactionAtom = (store: JotaiStore, address: Address, pageSize: number, nextPageToken?: string) => {
4544
return atom(async (get) => {
46-
const { transactionResults, nextPageToken: newNextPageToken } = await fetchAccountTransactionResults(address, pageSize, nextPageToken)
45+
const { transactionResults, nextPageToken: newNextPageToken } = await getAccountTransactionResults(address, pageSize, nextPageToken)
4746

4847
get(createSyncEffect(transactionResults))
4948

5049
const transactions = await get(createTransactionsAtom(store, transactionResults))
51-
const transactionsForAccount = transactions.flatMap((transaction) => extractTransactionsForAccount(transaction, address))
50+
5251
return {
53-
rows: transactionsForAccount,
52+
rows: transactions,
5453
nextPageToken: newNextPageToken,
5554
}
5655
})
@@ -60,6 +59,6 @@ export const useFetchNextAccountTransactionPage = (address: Address) => {
6059
const store = useStore()
6160

6261
return useMemo(() => {
63-
return (pageSize: number, nextPageToken?: string) => creatAccountTransactionAtom(store, address, pageSize, nextPageToken)
62+
return (pageSize: number, nextPageToken?: string) => createAccountTransactionAtom(store, address, pageSize, nextPageToken)
6463
}, [store, address])
6564
}

src/features/accounts/utils/extract-transaction-for-account.ts

Lines changed: 0 additions & 36 deletions
This file was deleted.
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import algosdk from 'algosdk'
2+
import { TransactionResult } from '@algorandfoundation/algokit-utils/types/indexer'
3+
import { invariant } from '@/utils/invariant'
4+
import { Address } from '../data/types'
5+
6+
export const getAddressesForTransaction = (transaction: TransactionResult): Address[] => {
7+
const addresses = new Set<Address>()
8+
addresses.add(transaction.sender)
9+
if (transaction['tx-type'] === algosdk.TransactionType.pay) {
10+
invariant(transaction['payment-transaction'], 'payment-transaction is not set')
11+
12+
addresses.add(transaction['payment-transaction']['receiver'])
13+
if (transaction['payment-transaction']['close-remainder-to']) {
14+
addresses.add(transaction['payment-transaction']['close-remainder-to'])
15+
}
16+
} else if (transaction['tx-type'] === algosdk.TransactionType.axfer) {
17+
invariant(transaction['asset-transfer-transaction'], 'asset-transfer-transaction is not set')
18+
19+
addresses.add(transaction['asset-transfer-transaction']['receiver'])
20+
if (transaction['asset-transfer-transaction']['close-to']) {
21+
addresses.add(transaction['asset-transfer-transaction']['close-to'])
22+
}
23+
} else if (transaction['tx-type'] === algosdk.TransactionType.afrz) {
24+
invariant(transaction['asset-freeze-transaction'], 'asset-freeze-transaction is not set')
25+
26+
addresses.add(transaction['asset-freeze-transaction']['address'])
27+
} else if (transaction['tx-type'] === algosdk.TransactionType.acfg) {
28+
invariant(transaction['asset-config-transaction'], 'asset-config-transaction is not set')
29+
if (transaction['asset-config-transaction'].params?.manager) {
30+
addresses.add(transaction['asset-config-transaction'].params?.manager)
31+
}
32+
if (transaction['asset-config-transaction'].params?.reserve) {
33+
addresses.add(transaction['asset-config-transaction'].params?.reserve)
34+
}
35+
if (transaction['asset-config-transaction'].params?.freeze) {
36+
addresses.add(transaction['asset-config-transaction'].params?.freeze)
37+
}
38+
if (transaction['asset-config-transaction'].params?.clawback) {
39+
addresses.add(transaction['asset-config-transaction'].params?.clawback)
40+
}
41+
} else if (transaction['tx-type'] === algosdk.TransactionType.appl) {
42+
invariant(transaction['application-transaction'], 'application-transaction is not set')
43+
44+
const innerTransactions = transaction['inner-txns'] ?? []
45+
for (const innerTxn of innerTransactions) {
46+
const innerAddresses = getAddressesForTransaction(innerTxn)
47+
for (const address of innerAddresses) {
48+
addresses.add(address)
49+
}
50+
}
51+
}
52+
return Array.from(addresses)
53+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Transaction, InnerTransaction, TransactionType } from '@/features/transactions/models'
2+
import { flattenInnerTransactions } from '@/utils/flatten-inner-transactions'
3+
import { Address } from '../data/types'
4+
5+
export const getAccountTransactionsTableSubRows = (address: Address, transaction: Transaction | InnerTransaction) => {
6+
if (transaction.type !== TransactionType.ApplicationCall || transaction.innerTransactions.length === 0) {
7+
return []
8+
}
9+
10+
return transaction.innerTransactions.filter((innerTransaction) => {
11+
const txns = flattenInnerTransactions(innerTransaction)
12+
return txns.some(({ transaction }) => {
13+
return (
14+
transaction.sender === address ||
15+
(transaction.type === TransactionType.Payment &&
16+
(transaction.receiver === address || transaction.closeRemainder?.to === address)) ||
17+
(transaction.type === TransactionType.AssetTransfer &&
18+
(transaction.receiver === address || transaction.closeRemainder?.to === address || transaction.clawbackFrom === address)) ||
19+
(transaction.type === TransactionType.AssetConfig &&
20+
(transaction.manager === address ||
21+
transaction.clawback === address ||
22+
transaction.reserve === address ||
23+
transaction.freeze === address)) ||
24+
(transaction.type === TransactionType.AssetFreeze && transaction.address === address)
25+
)
26+
})
27+
})
28+
}

src/features/applications/components/application-live-transactions.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import { LiveTransactionsTable } from '@/features/transactions/components/live-t
44
import { TransactionResult } from '@algorandfoundation/algokit-utils/types/indexer'
55
import { flattenTransactionResult } from '@/features/transactions/utils/flatten-transaction-result'
66
import { TransactionType as AlgoSdkTransactionType } from 'algosdk'
7-
import { applicationTransactionsTableColumns } from '../utils/application-transactions-table-columns'
87
import { Transaction, InnerTransaction } from '@/features/transactions/models'
98
import { getApplicationTransactionsTableSubRows } from '../utils/get-application-transactions-table-sub-rows'
9+
import { transactionsTableColumns } from '@/features/transactions/components/transactions-table-columns'
1010

1111
type Props = {
1212
applicationId: ApplicationId
@@ -28,5 +28,5 @@ export function ApplicationLiveTransactions({ applicationId }: Props) {
2828
[applicationId]
2929
)
3030

31-
return <LiveTransactionsTable filter={filter} getSubRows={getSubRows} columns={applicationTransactionsTableColumns} />
31+
return <LiveTransactionsTable filter={filter} getSubRows={getSubRows} columns={transactionsTableColumns} />
3232
}

src/features/applications/components/application-transaction-history.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { LazyLoadDataTable } from '@/features/common/components/lazy-load-data-table'
22
import { ApplicationId } from '../data/types'
33
import { useFetchNextApplicationTransactionsPage } from '../data/application-transaction-history'
4-
import { applicationTransactionsTableColumns } from '../utils/application-transactions-table-columns'
54
import { InnerTransaction, Transaction } from '@/features/transactions/models'
65
import { useCallback } from 'react'
76
import { getApplicationTransactionsTableSubRows } from '../utils/get-application-transactions-table-sub-rows'
7+
import { transactionsTableColumns } from '@/features/transactions/components/transactions-table-columns'
88

99
type Props = {
1010
applicationId: ApplicationId
@@ -22,5 +22,5 @@ export function ApplicationTransactionHistory({ applicationId }: Props) {
2222
[applicationId]
2323
)
2424

25-
return <LazyLoadDataTable columns={applicationTransactionsTableColumns} getSubRows={getSubRows} fetchNextPage={fetchNextPage} />
25+
return <LazyLoadDataTable columns={transactionsTableColumns} getSubRows={getSubRows} fetchNextPage={fetchNextPage} />
2626
}

0 commit comments

Comments
 (0)