Skip to content

Commit cc417e3

Browse files
feat: logicsig (#11)
* feat: add logicsig --------- Co-authored-by: Negar Abbasi <[email protected]>
1 parent f39f838 commit cc417e3

File tree

19 files changed

+395
-166
lines changed

19 files changed

+395
-166
lines changed

src/App.routes.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@ import { Outlet } from 'react-router-dom'
22
import { LayoutPage } from './features/layout/pages/layout-page'
33
import { Urls } from './routes/urls'
44
import { evalTemplates } from './routes/templated-route'
5-
import { TransactionPage } from './features/transactions/pages/transaction-page'
5+
import { TransactionPage, transactionPageTitle } from './features/transactions/pages/transaction-page'
66
import { ExplorePage } from './features/explore/pages/explore-page'
77
import { GroupPage } from './features/transactions/pages/group-page'
8-
import { transactionPageConstants } from './features/theme/constant'
98
import { ErrorPage } from './features/common/pages/error-page'
109

1110
export const routes = evalTemplates([
@@ -37,7 +36,7 @@ export const routes = evalTemplates([
3736
{
3837
template: Urls.Explore.Transaction.ById,
3938
element: <TransactionPage />,
40-
errorElement: <ErrorPage title={transactionPageConstants.title} />,
39+
errorElement: <ErrorPage title={transactionPageTitle} />,
4140
},
4241
{
4342
template: Urls.Explore.Group.ById,

src/features/blocks/data.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { atom, useAtom, useAtomValue } from 'jotai'
2-
import * as algokit from '@algorandfoundation/algokit-utils'
32
import { atomEffect } from 'jotai-effect'
43
import { transactionsAtom } from '@/features/transactions/data'
54
import { AlgorandSubscriber } from '@algorandfoundation/algokit-subscriber'
65
// import { BlockMetadata } from '@algorandfoundation/algokit-subscriber/types/subscription'
76
import { Buffer } from 'buffer'
7+
import { algod } from '../common/data'
88

99
type BlockMetadata = {
1010
round: number
@@ -14,12 +14,6 @@ type BlockMetadata = {
1414
// TODO: NC - Remove once https://github.com/algorandfoundation/algokit-subscriber-ts/pull/49 is merged
1515
window.Buffer = Buffer
1616

17-
// TODO: Move this elsewhere and make it configurable once we start using it more
18-
const algod = algokit.getAlgoClient({
19-
server: 'https://testnet-api.algonode.cloud/',
20-
port: 443,
21-
})
22-
2317
const syncedRoundAtom = atom<number | undefined>(undefined)
2418

2519
// TODO: Size should be capped at some limit, so memory usage doesn't grow indefinitely
@@ -30,9 +24,6 @@ const latestBlockAtom = atom((get) => {
3024
})
3125

3226
const subscribeToBlocksEffect = atomEffect((get, set) => {
33-
algokit.Config.configure({
34-
logger: algokit.Config.getLogger(true),
35-
})
3627
const subscriber = new AlgorandSubscriber(
3728
{
3829
filters: [

src/features/common/data.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { Config, getAlgoClient, getAlgoIndexerClient } from '@algorandfoundation/algokit-utils'
2+
3+
Config.configure({
4+
logger: Config.getLogger(true),
5+
})
6+
7+
export const indexer = getAlgoIndexerClient({
8+
server: 'https://mainnet-idx.algonode.cloud/',
9+
port: 443,
10+
})
11+
12+
export const algod = getAlgoClient({
13+
server: 'https://mainnet-api.algonode.cloud/',
14+
port: 443,
15+
})

src/features/theme/components/theme-toggle.test.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
import { describe, it, expect } from 'vitest'
22
import { render, waitFor } from '@/tests/testing-library'
3-
import { ThemeToggle } from '@/features/theme/components/theme-toggle'
3+
import { ThemeToggle, themeTogglelabel } from '@/features/theme/components/theme-toggle'
44
import { executeComponentTest } from '@/tests/test-component'
5-
import { themeConstants } from '../constant'
65

76
describe('when the theme is toggled to dark', () => {
87
it('the theme is set to dark', async () => {
98
return executeComponentTest(
109
() => render(<ThemeToggle />),
1110
async (component, user) => {
12-
user.click(await component.findByRole('button', { name: themeConstants.toggleButtonName }))
11+
user.click(await component.findByRole('button', { name: themeTogglelabel }))
1312
user.click(await component.findByText('Dark'))
1413

1514
await waitFor(() => expect(document.documentElement.classList.contains('dark')).toBe(true))

src/features/theme/components/theme-toggle.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,16 @@ import { Moon, Sun } from 'lucide-react'
33
import { Button } from '@/features/common/components/button'
44
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/features/common/components/dropdown-menu'
55
import { useTheme } from '../hooks/use-theme'
6-
import { themeConstants } from '../constant'
6+
7+
export const themeTogglelabel = 'Toggle theme'
78

89
export function ThemeToggle() {
910
const { setTheme } = useTheme()
1011

1112
return (
1213
<DropdownMenu>
1314
<DropdownMenuTrigger asChild>
14-
<Button variant="default" size="icon" aria-label={themeConstants.toggleButtonName}>
15+
<Button variant="default" size="icon" aria-label={themeTogglelabel}>
1516
<Sun className="size-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
1617
<Moon className="absolute size-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
1718
<span className="sr-only">Toggle theme</span>

src/features/theme/constant.ts

Lines changed: 0 additions & 29 deletions
This file was deleted.
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { LogicsigModel } from '../models'
2+
import { Card, CardContent } from '@/features/common/components/card'
3+
import { cn } from '@/features/common/utils'
4+
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@radix-ui/react-tabs'
5+
import { useLogicsigTeal } from '../data'
6+
import { RenderLoadable } from '@/features/common/components/render-loadable'
7+
8+
type LogicsigProps = {
9+
signature: LogicsigModel
10+
}
11+
12+
const base64LogicsigTabId = 'base64'
13+
const tealLogicsigTabId = 'teal'
14+
export const logicsigLabel = 'View Logic Signature Details'
15+
export const base64LogicsigTabLabel = 'Base64'
16+
export const tealLogicsigTabLabel = 'Teal'
17+
18+
export function Logicsig({ signature }: LogicsigProps) {
19+
const [tealLoadable, fetchTeal] = useLogicsigTeal(signature.logic)
20+
21+
return (
22+
<>
23+
<h1 className={cn('text-2xl text-primary font-bold')}>Logic Signature</h1>
24+
<Tabs
25+
defaultValue={base64LogicsigTabId}
26+
onValueChange={(activeTab) => {
27+
if (activeTab === tealLogicsigTabId) {
28+
fetchTeal()
29+
}
30+
}}
31+
>
32+
<TabsList aria-label={logicsigLabel}>
33+
<TabsTrigger className={cn('data-[state=active]:border-primary data-[state=active]:border-b-2 w-32')} value={base64LogicsigTabId}>
34+
{base64LogicsigTabLabel}
35+
</TabsTrigger>
36+
<TabsTrigger className={cn('data-[state=active]:border-primary data-[state=active]:border-b-2 w-32')} value={tealLogicsigTabId}>
37+
{tealLogicsigTabLabel}
38+
</TabsTrigger>
39+
</TabsList>
40+
<TabsContent value={base64LogicsigTabId} className={cn('border-solid border-2 border-border p-4')}>
41+
<Card className={cn('p-4')}>
42+
<CardContent className={cn('text-sm space-y-4')}>
43+
<div className={cn('space-y-2')}>
44+
<pre>{signature.logic}</pre>
45+
</div>
46+
</CardContent>
47+
</Card>
48+
</TabsContent>
49+
<TabsContent value={tealLogicsigTabId} className={cn('border-solid border-2 border-border p-4')}>
50+
<Card>
51+
<CardContent className={cn('text-sm space-y-4')}>
52+
<div className={cn('h-96 grid')}>
53+
<RenderLoadable loadable={tealLoadable}>{(teal) => <pre className={cn('overflow-scroll p-4')}>{teal}</pre>}</RenderLoadable>
54+
</div>
55+
</CardContent>
56+
</Card>
57+
</TabsContent>
58+
</Tabs>
59+
</>
60+
)
61+
}

src/features/transactions/components/multisig.tsx

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,32 @@ import { Card, CardContent } from '@/features/common/components/card'
33
import { cn } from '@/features/common/utils'
44
import { useMemo } from 'react'
55
import { DescriptionList } from '@/features/common/components/description-list'
6-
import { transactionPageConstants } from '@/features/theme/constant'
76

8-
export type MultisigProps = {
9-
multisig: MultisigModel
7+
type MultisigProps = {
8+
signature: MultisigModel
109
}
1110

12-
export function Multisig({ multisig: multisig }: MultisigProps) {
11+
export const multisigVersionLabel = 'Version'
12+
export const multisigThresholdLabel = 'Threshold'
13+
export const multisigSubsignersLabel = 'Subsigners'
14+
15+
export function Multisig({ signature }: MultisigProps) {
1316
const multisigItems = useMemo(
1417
() => [
1518
{
16-
dt: transactionPageConstants.labels.multisig.version,
17-
dd: multisig.version,
19+
dt: multisigVersionLabel,
20+
dd: signature.version,
1821
},
1922
{
20-
dt: transactionPageConstants.labels.multisig.threshold,
21-
dd: multisig.threshold,
23+
dt: multisigThresholdLabel,
24+
dd: signature.threshold,
2225
},
2326
{
24-
dt: transactionPageConstants.labels.multisig.subsigners,
25-
dd: multisig.subsigners.map((address, index) => <div key={index}>{address}</div>),
27+
dt: multisigSubsignersLabel,
28+
dd: signature.subsigners.map((address, index) => <div key={index}>{address}</div>),
2629
},
2730
],
28-
[multisig.subsigners, multisig.version, multisig.threshold]
31+
[signature.subsigners, signature.version, signature.threshold]
2932
)
3033

3134
return (

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

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,31 @@
11
import { Card, CardContent } from '@/features/common/components/card'
22
import { cn } from '@/features/common/utils'
33
import { DisplayAlgo } from '@/features/common/components/display-algo'
4-
import { Button } from '@/features/common/components/button'
54
import { TransactionInfo } from './transaction-info'
65
import { TransactionNote } from './transaction-note'
76
import { TransactionJson } from './transaction-json'
87
import { useMemo } from 'react'
9-
import { PaymentTransactionModel } from '../models'
8+
import { PaymentTransactionModel, SignatureType } from '../models'
109
import { TransactionResult } from '@algorandfoundation/algokit-utils/types/indexer'
1110
import { DescriptionList } from '@/features/common/components/description-list'
1211
import { TransactionViewVisual } from './transaction-view-visual'
1312
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/features/common/components/tabs'
1413
import { TransactionViewTable } from './transaction-view-table'
1514
import { Multisig } from './multisig'
15+
import { Logicsig } from './logicsig'
1616

17-
export type Props = {
17+
type PaymentTransactionProps = {
1818
transaction: PaymentTransactionModel
1919
rawTransaction: TransactionResult
2020
}
2121

22-
export function PaymentTransaction({ transaction, rawTransaction }: Props) {
22+
const visualTransactionDetailsTabId = 'visual'
23+
const tableTransactionDetailsTabId = 'table'
24+
export const transactionDetailsLabel = 'View Transaction Details'
25+
export const visualTransactionDetailsTabLabel = 'Visual'
26+
export const tableTransactionDetailsTabLabel = 'Table'
27+
28+
export function PaymentTransaction({ transaction, rawTransaction }: PaymentTransactionProps) {
2329
const paymentTransactionItems = useMemo(
2430
() => [
2531
{
@@ -54,29 +60,35 @@ export function PaymentTransaction({ transaction, rawTransaction }: Props) {
5460
<div className={cn('space-y-2')}>
5561
<div className={cn('flex items-center justify-between')}>
5662
<h1 className={cn('text-2xl text-primary font-bold')}>Payment</h1>
57-
<Button>Replay</Button>
5863
</div>
5964
<DescriptionList items={paymentTransactionItems} />
6065
</div>
61-
<Tabs defaultValue="visual">
62-
<TabsList aria-label="View Transaction">
63-
<TabsTrigger className={cn('data-[state=active]:border-primary data-[state=active]:border-b-2 w-32')} value="visual">
64-
Visual
66+
<Tabs defaultValue={visualTransactionDetailsTabId}>
67+
<TabsList aria-label={transactionDetailsLabel}>
68+
<TabsTrigger
69+
className={cn('data-[state=active]:border-primary data-[state=active]:border-b-2 w-32')}
70+
value={visualTransactionDetailsTabId}
71+
>
72+
{visualTransactionDetailsTabLabel}
6573
</TabsTrigger>
66-
<TabsTrigger className={cn('data-[state=active]:border-primary data-[state=active]:border-b-2 w-32')} value="table">
67-
Table
74+
<TabsTrigger
75+
className={cn('data-[state=active]:border-primary data-[state=active]:border-b-2 w-32')}
76+
value={tableTransactionDetailsTabId}
77+
>
78+
{tableTransactionDetailsTabLabel}
6879
</TabsTrigger>
6980
</TabsList>
70-
<TabsContent value="visual" className={cn('border-solid border-2 border-border p-4')}>
81+
<TabsContent value={visualTransactionDetailsTabId} className={cn('border-solid border-2 border-border p-4')}>
7182
<TransactionViewVisual transaction={transaction} />
7283
</TabsContent>
73-
<TabsContent value="table" className={cn('border-solid border-2 border-border p-4')}>
84+
<TabsContent value={tableTransactionDetailsTabId} className={cn('border-solid border-2 border-border p-4')}>
7485
<TransactionViewTable transaction={transaction} />
7586
</TabsContent>
7687
</Tabs>
7788
<TransactionNote transaction={transaction} />
7889
<TransactionJson transaction={rawTransaction} />
79-
{transaction.multisig && <Multisig multisig={transaction.multisig} />}
90+
{transaction.signature?.type === SignatureType.Multi && <Multisig signature={transaction.signature} />}
91+
{transaction.signature?.type === SignatureType.Logic && <Logicsig signature={transaction.signature} />}
8092
</CardContent>
8193
</Card>
8294
</div>

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

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,30 +6,36 @@ import { useMemo } from 'react'
66
import { PaymentTransactionModel } from '../models'
77
import { DescriptionList } from '@/features/common/components/description-list'
88
import { isDefined } from '@/utils/is-defined'
9-
import { transactionPageConstants } from '@/features/theme/constant'
109

11-
export type Props = {
10+
type Props = {
1211
transaction: PaymentTransactionModel
1312
}
1413

14+
export const transactionIdLabel = 'Transaction ID'
15+
export const transactionTypeLabel = 'Type'
16+
export const transactionTimestampLabel = 'Timestamp'
17+
export const transactionBlockLabel = 'Block'
18+
export const transactionGroupLabel = 'Group'
19+
export const transactionFeeLabel = 'Fee'
20+
1521
export function TransactionInfo({ transaction }: Props) {
1622
const transactionInfoItems = useMemo(
1723
() =>
1824
[
1925
{
20-
dt: transactionPageConstants.labels.transactionId,
26+
dt: transactionIdLabel,
2127
dd: transaction.id,
2228
},
2329
{
24-
dt: transactionPageConstants.labels.type,
30+
dt: transactionTypeLabel,
2531
dd: transaction.type,
2632
},
2733
{
28-
dt: transactionPageConstants.labels.timestamp,
34+
dt: transactionTimestampLabel,
2935
dd: dateFormatter.asLongDateTime(new Date(transaction.roundTime)),
3036
},
3137
{
32-
dt: transactionPageConstants.labels.block,
38+
dt: transactionBlockLabel,
3339
dd: (
3440
<a href="#" className={cn('text-primary underline')}>
3541
{transaction.confirmedRound}
@@ -38,7 +44,7 @@ export function TransactionInfo({ transaction }: Props) {
3844
},
3945
transaction.group
4046
? {
41-
dt: transactionPageConstants.labels.group,
47+
dt: transactionGroupLabel,
4248
dd: (
4349
<a href="#" className={cn('text-primary underline')}>
4450
{transaction.group}
@@ -47,7 +53,7 @@ export function TransactionInfo({ transaction }: Props) {
4753
}
4854
: undefined,
4955
{
50-
dt: transactionPageConstants.labels.fee,
56+
dt: transactionFeeLabel,
5157
dd: transaction.fee ? <DisplayAlgo amount={transaction.fee} /> : 'N/A',
5258
},
5359
].filter(isDefined),

0 commit comments

Comments
 (0)