Skip to content

Commit ef37b7c

Browse files
feat: support state proof transactions (#34)
* feat: support state proof transaction --------- Co-authored-by: Neil Campbell <[email protected]>
1 parent 15cc8a1 commit ef37b7c

File tree

11 files changed

+122
-5
lines changed

11 files changed

+122
-5
lines changed

src/features/common/components/description-list.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { cn } from '../utils'
22

33
type Props = {
4-
items: { dt: string; dd: string | number | JSX.Element[] | JSX.Element }[]
4+
items: { dt: string; dd: string | number | JSX.Element[] | JSX.Element | undefined }[]
55
}
66

77
export function DescriptionList({ items }: Props) {

src/features/transactions/components/latest-transactions.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ export function LatestTransactions() {
2828
<DescriptionList
2929
items={[
3030
{ dt: 'From:', dd: ellipseAddress(transaction.from) },
31-
{ dt: 'To:', dd: typeof transaction.to === 'string' ? ellipseAddress(transaction.to) : transaction.to },
31+
{
32+
dt: 'To:',
33+
dd: transaction.to && typeof transaction.to === 'string' ? ellipseAddress(transaction.to) : transaction.to,
34+
},
3235
]}
3336
/>
3437
</div>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { Card, CardContent } from '@/features/common/components/card'
2+
import { SignatureType, StateProofTransaction } from '../models'
3+
import { TransactionInfo } from './transaction-info'
4+
import { cn } from '@/features/common/utils'
5+
import { LogicsigDetails } from './logicsig-details'
6+
import { MultisigDetails } from './multisig-details'
7+
import { TransactionJson } from './transaction-json'
8+
import { TransactionNote } from './transaction-note'
9+
10+
type Props = {
11+
transaction: StateProofTransaction
12+
}
13+
14+
export function StateProofTransactionDetails({ transaction }: Props) {
15+
return (
16+
<div className={cn('space-y-6 pt-7')}>
17+
<TransactionInfo transaction={transaction} />
18+
<Card className={cn('p-4')}>
19+
<CardContent className={cn('text-sm space-y-4')}>
20+
<div className={cn('flex items-center justify-between')}>
21+
<h1 className={cn('text-2xl text-primary font-bold')}>State Proof</h1>
22+
</div>
23+
{transaction.note && <TransactionNote note={transaction.note} />}
24+
<TransactionJson json={transaction.json} />
25+
{transaction.signature?.type === SignatureType.Multi && <MultisigDetails signature={transaction.signature} />}
26+
{transaction.signature?.type === SignatureType.Logic && <LogicsigDetails signature={transaction.signature} />}
27+
</CardContent>
28+
</Card>
29+
</div>
30+
)
31+
}

src/features/transactions/components/transaction-details.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { InnerTransaction, Transaction, TransactionType } from '../models'
44
import { AppCallTransactionDetails } from './app-call-transaction-details'
55
import { AssetConfigTransactionDetails } from './asset-config-transaction-details'
66
import { AssetFreezeTransactionDetails } from './asset-freeze-transaction-details'
7+
import { StateProofTransactionDetails } from './state-proof-transaction-details'
78

89
type Props = {
910
transaction: Transaction | InnerTransaction
@@ -21,6 +22,8 @@ export function TransactionDetails({ transaction }: Props) {
2122
return <AssetConfigTransactionDetails transaction={transaction} />
2223
case TransactionType.AssetFreeze:
2324
return <AssetFreezeTransactionDetails transaction={transaction} />
25+
case TransactionType.StateProof:
26+
return <StateProofTransactionDetails transaction={transaction} />
2427
default:
2528
return <></>
2629
}

src/features/transactions/components/transaction-json.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export type Props = {
77
export function TransactionJson({ json }: Props) {
88
return (
99
<div className={cn('space-y-2')}>
10-
<h2 className={cn('text-xl font-bold')}>Transction JSON</h2>
10+
<h2 className={cn('text-xl font-bold')}>Transaction JSON</h2>
1111
<div className={cn('border-solid border-2 border-border h-96 grid')}>
1212
<pre className={cn('overflow-scroll p-4')}>{json}</pre>
1313
</div>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { TransactionResult } from '@algorandfoundation/algokit-utils/types/indexer'
2+
import { StateProofTransaction, TransactionType } from '../models'
3+
import { mapCommonTransactionProperties } from './transaction-common-properties-mappers'
4+
5+
export const asStateProofTransaction = (transactionResult: TransactionResult): StateProofTransaction => {
6+
return {
7+
id: transactionResult.id,
8+
type: TransactionType.StateProof,
9+
...mapCommonTransactionProperties(transactionResult),
10+
}
11+
}

src/features/transactions/mappers/transaction-mappers.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { Asset } from '@/features/assets/models'
1111
import { getAssetIdsForTransaction } from '../utils/get-asset-ids-for-transaction'
1212
import { asAssetConfigTransaction } from './asset-config-transaction-mappers'
1313
import { asAssetFreezeTransaction } from './asset-freeze-transaction-mappers'
14+
import { asStateProofTransaction } from './state-proof-transaction-mappers'
1415

1516
export const asTransaction = async (transactionResult: TransactionResult, assetResolver: (assetId: number) => Promise<Asset> | Asset) => {
1617
switch (transactionResult['tx-type']) {
@@ -38,6 +39,10 @@ export const asTransaction = async (transactionResult: TransactionResult, assetR
3839
const asset = await assetResolver(assetId)
3940
return asAssetFreezeTransaction(transactionResult, asset)
4041
}
42+
case algosdk.TransactionType.stpf: {
43+
invariant(transactionResult['state-proof-transaction'], 'state-proof-transaction is not set')
44+
return asStateProofTransaction(transactionResult)
45+
}
4146
default:
4247
// TODO: Once we support all transaction types, we should throw an error instead
4348
// throw new Error(`${transaction['tx-type']} is not supported`)
@@ -91,6 +96,13 @@ export const asTransactionSummary = (transactionResult: TransactionResult): Tran
9196
to: transactionResult['asset-freeze-transaction']['asset-id'],
9297
}
9398
}
99+
case algosdk.TransactionType.stpf: {
100+
invariant(transactionResult['state-proof-transaction'], 'state-proof-transaction is not set')
101+
return {
102+
...common,
103+
type: TransactionType.StateProof,
104+
}
105+
}
94106
default:
95107
// TODO: Once we support all transaction types, we should throw an error instead
96108
// throw new Error(`${transaction['tx-type']} is not supported`)

src/features/transactions/models/index.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export enum TransactionType {
2121
ApplicationCall = 'Application Call',
2222
AssetConfig = 'Asset Config',
2323
AssetFreeze = 'Asset Freeze',
24+
StateProof = 'State Proof',
2425
}
2526

2627
export enum AssetTransferTransactionSubType {
@@ -71,11 +72,12 @@ export type Transaction =
7172
| AppCallTransaction
7273
| AssetConfigTransaction
7374
| AssetFreezeTransaction
75+
| StateProofTransaction
7476

7577
export type TransactionSummary = Pick<CommonTransactionProperties, 'type'> & {
7678
id: string
7779
from: Address
78-
to: Address | number
80+
to?: Address | number
7981
}
8082

8183
export enum SignatureType {
@@ -207,3 +209,9 @@ export enum AssetFreezeStatus {
207209
Frozen = 'Frozen',
208210
Unfrozen = 'Unfrozen',
209211
}
212+
213+
export type StateProofTransaction = CommonTransactionProperties & {
214+
type: TransactionType.StateProof
215+
id: string
216+
subType?: undefined
217+
}

src/features/transactions/pages/transaction-page.test.tsx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,4 +1027,38 @@ describe('transaction-page', () => {
10271027
)
10281028
})
10291029
})
1030+
1031+
describe('when rendering a state proof transaction', () => {
1032+
const transaction = transactionResultMother
1033+
.stateProof()
1034+
['withRound-time'](1696316292)
1035+
['withConfirmed-round'](32563331)
1036+
.withFee(0)
1037+
.build()
1038+
1039+
it('should be rendered correctly', () => {
1040+
vi.mocked(useParams).mockImplementation(() => ({ transactionId: transaction.id }))
1041+
const myStore = createStore()
1042+
myStore.set(transactionResultsAtom, new Map([[transaction.id, transaction]]))
1043+
return executeComponentTest(
1044+
() => {
1045+
return render(<TransactionPage />, undefined, myStore)
1046+
},
1047+
async (component) => {
1048+
await waitFor(() => {
1049+
descriptionListAssertion({
1050+
container: component.container,
1051+
items: [
1052+
{ term: transactionIdLabel, description: transaction.id },
1053+
{ term: transactionTypeLabel, description: 'State Proof' },
1054+
{ term: transactionTimestampLabel, description: 'Tue, 03 October 2023 06:58:12' },
1055+
{ term: transactionBlockLabel, description: '32563331' },
1056+
{ term: transactionFeeLabel, description: '0' },
1057+
],
1058+
})
1059+
})
1060+
}
1061+
)
1062+
})
1063+
})
10301064
})

src/tests/builders/transaction-result-builder.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@ import {
77
randomDateBetween,
88
randomNumberBetween,
99
} from '@makerx/ts-dossier'
10-
import { ApplicationTransactionResult, AssetResult, TransactionResult } from '@algorandfoundation/algokit-utils/types/indexer'
10+
import {
11+
ApplicationTransactionResult,
12+
AssetResult,
13+
StateProofTransactionResult,
14+
TransactionResult,
15+
} from '@algorandfoundation/algokit-utils/types/indexer'
1116
import algosdk from 'algosdk'
1217

1318
export class TransactionResultBuilder extends DataBuilder<TransactionResult> {
@@ -59,6 +64,13 @@ export class TransactionResultBuilder extends DataBuilder<TransactionResult> {
5964
} as ApplicationTransactionResult
6065
return this
6166
}
67+
68+
public stateProofTransaction() {
69+
this.thing['tx-type'] = algosdk.TransactionType.stpf
70+
// HACK: do this because the type StateProofTransactionResult is very big
71+
this.thing['state-proof-transaction'] = {} as StateProofTransactionResult
72+
return this
73+
}
6274
}
6375

6476
export const transactionResultBuilder = dossierProxy<TransactionResultBuilder, TransactionResult>(TransactionResultBuilder)

0 commit comments

Comments
 (0)