Skip to content

Commit 5eb06c2

Browse files
authored
chore: view asset transactions
support opt-in, opt-out, clawback
1 parent 2c4d520 commit 5eb06c2

13 files changed

+367
-11
lines changed
Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
import { AssetModel } from '@/features/assets/models'
2+
import { cn } from '../utils'
23
import Decimal from 'decimal.js'
34

45
type Props = {
56
amount: number | bigint
67
asset: AssetModel
8+
className?: string
79
}
810

9-
export const DisplayAssetAmount = ({ amount, asset }: Props) => {
11+
export const DisplayAssetAmount = ({ amount, asset, className }: Props) => {
1012
// asset decimals value must be from 0 to 19 so it is safe to use .toString() here
11-
const decimals = asset.decimals.toString()
1213
// the amount is uint64, should be safe to be .toString()
13-
const amountAsString = amount.toString()
14+
const amountToDisplay = new Decimal(amount.toString()).div(new Decimal(10).pow(asset.decimals.toString())).toString()
1415

1516
return (
16-
<div>
17-
{new Decimal(amountAsString).div(new Decimal(10).pow(decimals)).toString()} {asset.unitName ?? ''}
17+
<div className={cn(className)}>
18+
{amountToDisplay} {asset.unitName ?? ''}
1819
</div>
1920
)
2021
}

src/features/common/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const ZERO_ADDRESS = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<div>
2+
<div
3+
class="relative grid"
4+
style="grid-template-columns: minmax(128px, 128px) repeat(2, 128px); grid-template-rows: repeat(2, 40px);"
5+
>
6+
<div />
7+
<div
8+
class="p-2 flex justify-center"
9+
>
10+
<h1
11+
class="text-l font-semibold"
12+
>
13+
SMO6...ALKU
14+
</h1>
15+
</div>
16+
<div />
17+
<div
18+
class="absolute right-0 -z-10"
19+
style="top: 40px;"
20+
>
21+
<div>
22+
<div
23+
class="p-0"
24+
/>
25+
<div
26+
class="p-0"
27+
style="height: 40px; width: 128px;"
28+
>
29+
<div
30+
class="grid h-full"
31+
style="grid-template-columns: repeat(1, minmax(0, 1fr)); height: 40px;"
32+
>
33+
<div
34+
class="flex justify-center"
35+
>
36+
<div
37+
class="border-muted h-full border-dashed"
38+
style="border-left-width: 2px;"
39+
/>
40+
</div>
41+
</div>
42+
</div>
43+
</div>
44+
</div>
45+
<div
46+
class="p-0 relative pr-8"
47+
>
48+
<div
49+
class="relative h-full p-0 flex items-center px-0"
50+
style="margin-left: 0px;"
51+
>
52+
<div
53+
class="inline"
54+
style="margin-left: 16px;"
55+
>
56+
563MNGE...
57+
</div>
58+
</div>
59+
</div>
60+
<div
61+
class="flex items-center justify-center relative"
62+
data-state="closed"
63+
style="grid-column-start: 2; grid-column-end: 4; color: rgb(126 200 191);"
64+
>
65+
<svg
66+
height="20"
67+
viewBox="0 0 21 21"
68+
width="20"
69+
xmlns="http://www.w3.org/2000/svg"
70+
xmlns:xlink="http://www.w3.org/1999/xlink"
71+
>
72+
<g
73+
transform="matrix(1 0 0 1 -153 -143 )"
74+
>
75+
<path
76+
d="M 163.5 143 C 169.38 143 174 147.62 174 153.5 C 174 159.38 169.38 164 163.5 164 C 157.62 164 153 159.38 153 153.5 C 153 147.62 157.62 143 163.5 143 Z "
77+
fill="currentColor"
78+
fill-rule="nonzero"
79+
stroke="none"
80+
/>
81+
</g>
82+
</svg>
83+
<div
84+
style="width: calc(50% - 10px); height: 20px;"
85+
>
86+
<svg
87+
class="relative"
88+
height="19px"
89+
preserveAspectRatio="xMinYMid meet"
90+
style=""
91+
viewBox="159 229 7 10"
92+
width="11px"
93+
xmlns="http://www.w3.org/2000/svg"
94+
xmlns:xlink="http://www.w3.org/1999/xlink"
95+
>
96+
<path
97+
d="M 165.8 228.1 L 159.1 234.1 L 165.8 240.1 L 163.5 234.1 L 165.8 228.1 Z"
98+
fill="currentColor"
99+
fill-rule="nonzero"
100+
stroke="none"
101+
/>
102+
</svg>
103+
</div>
104+
<div
105+
class="absolute size-1/2"
106+
style="border-width: 2px; border-radius: 4px; bottom: 1px; right: calc(25% - 4px);"
107+
/>
108+
<div
109+
class="absolute text-foreground right-1/4 w-[40%] flex justify-center"
110+
>
111+
<div
112+
class="w-min pl-1 pr-1 bg-card"
113+
>
114+
0
115+
116+
USDt
117+
</div>
118+
</div>
119+
</div>
120+
</div>
121+
</div>

src/features/transactions/components/__snapshots__/asset-transfer-view-visual.JBDSQEI37W5KWPQICT2IGCG2FWMUGJEUYYK3KFKNSYRNAXU2ARUA.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,9 @@
126126
class="absolute z-20 bg-card p-2 text-foreground w-20 text-xs"
127127
>
128128
Transfer
129-
<div>
129+
<div
130+
class=""
131+
>
130132
0.3
131133

132134
AKTA

src/features/transactions/components/asset-transfer-transaction-info.tsx

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { cn } from '@/features/common/utils'
22
import { useMemo } from 'react'
3-
import { AssetTransferTransactionModel } from '../models'
3+
import { AssetTransferTransactionModel, AssetTransferTransactionSubType } from '../models'
44
import { DescriptionList } from '@/features/common/components/description-list'
55
import { transactionSenderLabel, transactionReceiverLabel, transactionAmountLabel } from './transaction-view-table'
66
import { DisplayAssetAmount } from '@/features/common/components/display-asset-amount'
@@ -12,6 +12,7 @@ type Props = {
1212
export const assetLabel = 'Asset'
1313
export const transactionCloseRemainderToLabel = 'Close Remainder To'
1414
export const transactionCloseRemainderAmountLabel = 'Close Remainder Amount'
15+
export const transactionClawbackAddressLabel = 'Clawback From'
1516

1617
export function AssetTransferTransactionInfo({ transaction }: Props) {
1718
const items = useMemo(
@@ -32,6 +33,18 @@ export function AssetTransferTransactionInfo({ transaction }: Props) {
3233
</a>
3334
),
3435
},
36+
...(transaction.subType === AssetTransferTransactionSubType.Clawback
37+
? [
38+
{
39+
dt: transactionClawbackAddressLabel,
40+
dd: (
41+
<a href="#" className={cn('text-primary underline')}>
42+
{transaction.clawbackFrom}
43+
</a>
44+
),
45+
},
46+
]
47+
: []),
3548
{
3649
dt: assetLabel,
3750
dd: (
@@ -61,7 +74,15 @@ export function AssetTransferTransactionInfo({ transaction }: Props) {
6174
]
6275
: []),
6376
],
64-
[transaction.sender, transaction.receiver, transaction.asset, transaction.amount, transaction.closeRemainder]
77+
[
78+
transaction.amount,
79+
transaction.asset,
80+
transaction.clawbackFrom,
81+
transaction.closeRemainder,
82+
transaction.receiver,
83+
transaction.sender,
84+
transaction.subType,
85+
]
6586
)
6687

6788
return (

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { cn } from '@/features/common/utils'
33
import { dateFormatter } from '@/utils/format'
44
import { DisplayAlgo } from '@/features/common/components/display-algo'
55
import { useMemo } from 'react'
6-
import { TransactionModel, SignatureType } from '../models'
6+
import { TransactionModel, SignatureType, TransactionType } from '../models'
77
import { DescriptionList } from '@/features/common/components/description-list'
88
import { Badge } from '@/features/common/components/badge'
99
import { BlockLink } from '@/features/blocks/components/block-link'
@@ -31,6 +31,9 @@ export function TransactionInfo({ transaction }: Props) {
3131
dd: (
3232
<>
3333
{transaction.type}
34+
{transaction.type === TransactionType.AssetTransfer && transaction.subType && (
35+
<Badge variant="outline">{transaction.subType}</Badge>
36+
)}
3437
{transaction.signature?.type === SignatureType.Multi && <Badge variant="outline">Multisig</Badge>}
3538
{transaction.signature?.type === SignatureType.Logic && <Badge variant="outline">LogicSig</Badge>}
3639
</>
@@ -68,6 +71,7 @@ export function TransactionInfo({ transaction }: Props) {
6871
transaction.id,
6972
transaction.roundTime,
7073
transaction.signature?.type,
74+
transaction.subType,
7175
transaction.type,
7276
]
7377
)

src/features/transactions/components/transaction-view-visual.test.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ describe('asset-transfer-transaction-view-visual', () => {
4343
transactionResult: transactionResultMother['mainnet-JBDSQEI37W5KWPQICT2IGCG2FWMUGJEUYYK3KFKNSYRNAXU2ARUA']().build(),
4444
assetResult: assetResultMother['mainnet-523683256']().build(),
4545
},
46+
{
47+
transactionResult: transactionResultMother['mainnet-563MNGEL2OF4IBA7CFLIJNMBETT5QNKZURSLIONJBTJFALGYOAUA']().build(),
48+
assetResult: assetResultMother['mainnet-312769']().build(),
49+
},
4650
])(
4751
'when rendering transaction $id',
4852
({ transactionResult, assetResult }: { transactionResult: TransactionResult; assetResult: AssetResult }) => {

src/features/transactions/components/transaction-view-visual.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,9 @@ const DisplaySelfTransaction = fixedForwardRef(
190190
{transaction.type === TransactionType.Payment && (
191191
<DisplayAlgo className={cn('w-min pl-1 pr-1 bg-card')} amount={transaction.amount} />
192192
)}
193+
{transaction.type === TransactionType.AssetTransfer && (
194+
<DisplayAssetAmount className={cn('w-min pl-1 pr-1 bg-card')} amount={transaction.amount} asset={transaction.asset} />
195+
)}
193196
</div>
194197
</div>
195198
)

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { AssetResult, TransactionResult, TransactionSignature } from '@algorandfoundation/algokit-utils/types/indexer'
22
import {
33
AssetTransferTransactionModel,
4+
AssetTransferTransactionSubType,
45
LogicsigModel,
56
MultisigModel,
67
PaymentTransactionModel,
@@ -12,6 +13,7 @@ import { invariant } from '@/utils/invariant'
1213
import { publicKeyToAddress } from '@/utils/publickey-to-addess'
1314
import * as algokit from '@algorandfoundation/algokit-utils'
1415
import { asAsset } from '@/features/assets/mappers/asset-mappers'
16+
import { ZERO_ADDRESS } from '@/features/common/constants'
1517

1618
export const asPaymentTransaction = (transaction: TransactionResult): PaymentTransactionModel => {
1719
invariant(transaction['confirmed-round'], 'confirmed-round is not set')
@@ -70,9 +72,33 @@ export const asAssetTransferTransaction = (transaction: TransactionResult, asset
7072
invariant(transaction['round-time'], 'round-time is not set')
7173
invariant(transaction['asset-transfer-transaction'], 'asset-transfer-transaction is not set')
7274

75+
const subType = () => {
76+
invariant(transaction['asset-transfer-transaction'], 'asset-transfer-transaction is not set')
77+
78+
if (transaction['asset-transfer-transaction']['close-to']) {
79+
return AssetTransferTransactionSubType.OptOut
80+
}
81+
if (
82+
transaction.sender === transaction['asset-transfer-transaction'].receiver &&
83+
transaction['asset-transfer-transaction'].amount === 0
84+
) {
85+
return AssetTransferTransactionSubType.OptIn
86+
}
87+
if (
88+
transaction.sender === asset.params.clawback &&
89+
transaction['asset-transfer-transaction'].sender &&
90+
transaction['asset-transfer-transaction'].sender !== ZERO_ADDRESS
91+
) {
92+
return AssetTransferTransactionSubType.Clawback
93+
}
94+
95+
undefined
96+
}
97+
7398
return {
7499
id: transaction.id,
75100
type: TransactionType.AssetTransfer,
101+
subType: subType(),
76102
asset: asAsset(asset),
77103
confirmedRound: transaction['confirmed-round'],
78104
roundTime: transaction['round-time'] * 1000,
@@ -88,5 +114,6 @@ export const asAssetTransferTransaction = (transaction: TransactionResult, asset
88114
}
89115
: undefined,
90116
signature: transformSignature(transaction.signature),
117+
clawbackFrom: transaction['asset-transfer-transaction'].sender,
91118
}
92119
}

src/features/transactions/models/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ export enum TransactionType {
2121
AssetTransfer = 'Asset Transfer',
2222
}
2323

24+
export enum AssetTransferTransactionSubType {
25+
OptIn = 'Opt-In',
26+
Clawback = 'Clawback',
27+
OptOut = 'Opt-Out',
28+
}
29+
2430
export type CloseAlgoRemainder = {
2531
to: Address
2632
amount: AlgoAmount
@@ -36,14 +42,17 @@ export type PaymentTransactionModel = CommonTransactionProperties & {
3642
receiver: Address
3743
amount: AlgoAmount
3844
closeRemainder?: CloseAlgoRemainder
45+
subType?: undefined
3946
}
4047

4148
export type AssetTransferTransactionModel = CommonTransactionProperties & {
4249
type: TransactionType.AssetTransfer
50+
subType?: AssetTransferTransactionSubType
4351
receiver: Address
4452
amount: number | bigint
4553
closeRemainder?: CloseAssetRemainder
4654
asset: AssetModel
55+
clawbackFrom?: Address
4756
}
4857

4958
export type TransactionModel = PaymentTransactionModel | AssetTransferTransactionModel

0 commit comments

Comments
 (0)