diff --git a/src/features/applications/components/application-details.tsx b/src/features/applications/components/application-details.tsx
index a938d4ec3..27bcccfd2 100644
--- a/src/features/applications/components/application-details.tsx
+++ b/src/features/applications/components/application-details.tsx
@@ -23,6 +23,8 @@ import {
applicationLocalStateByteLabel,
applicationLocalStateUintLabel,
applicationTransactionsLabel,
+ applicationJsonLabel,
+ applicationNameLabel,
} from './labels'
import { isDefined } from '@/utils/is-defined'
import { ApplicationProgram } from './application-program'
@@ -43,6 +45,12 @@ export function ApplicationDetails({ application }: Props) {
dt: applicationIdLabel,
dd: application.id,
},
+ application.name
+ ? {
+ dt: applicationNameLabel,
+ dd: application.name,
+ }
+ : undefined,
{
dt: applicationCreatorAccountLabel,
dd: application.creator,
@@ -76,7 +84,14 @@ export function ApplicationDetails({ application }: Props) {
}
: undefined,
],
- [application.id, application.creator, application.account, application.globalStateSchema, application.localStateSchema]
+ [
+ application.id,
+ application.name,
+ application.creator,
+ application.account,
+ application.globalStateSchema,
+ application.localStateSchema,
+ ]
).filter(isDefined)
return (
@@ -141,6 +156,14 @@ export function ApplicationDetails({ application }: Props) {
+
+
+ {applicationJsonLabel}
+
+
+
)
}
diff --git a/src/features/applications/components/labels.ts b/src/features/applications/components/labels.ts
index 51f38a284..b6f20f5ca 100644
--- a/src/features/applications/components/labels.ts
+++ b/src/features/applications/components/labels.ts
@@ -1,5 +1,6 @@
export const applicationDetailsLabel = 'Application Details'
export const applicationIdLabel = 'Application ID'
+export const applicationNameLabel = 'Application Name'
export const applicationCreatorAccountLabel = 'Creator'
export const applicationAccountLabel = 'Account'
export const applicationGlobalStateByteLabel = 'Global State Byte'
@@ -25,3 +26,5 @@ export const applicationLiveTransactionsTabId = 'live-transactions'
export const applicationLiveTransactionsTabLabel = 'Live Transactions'
export const applicationHistoricalTransactionsTabId = 'historical-transactions'
export const applicationHistoricalTransactionsTabLabel = 'Historical Transactions'
+
+export const applicationJsonLabel = 'Application JSON'
diff --git a/src/features/applications/data/application-metadata.ts b/src/features/applications/data/application-metadata.ts
new file mode 100644
index 000000000..e896e0013
--- /dev/null
+++ b/src/features/applications/data/application-metadata.ts
@@ -0,0 +1,45 @@
+import { ApplicationResult } from '@/features/accounts/data/types'
+import { atomsInAtom } from '@/features/common/data/atoms-in-atom'
+import { atom } from 'jotai'
+import { ApplicationMetadataResult } from './types'
+import { indexer } from '@/features/common/data'
+import { flattenTransactionResult } from '@/features/transactions/utils/flatten-transaction-result'
+import { TransactionResult } from '@algorandfoundation/algokit-utils/types/indexer'
+import { TransactionType } from 'algosdk'
+import { base64ToUtf8 } from '@/utils/base64-to-utf8'
+import { parseArc2 } from '@/features/transactions/mappers/arc-2'
+import { parseJson } from '@/utils/parse-json'
+
+const createApplicationMetadataResultAtom = (applicationResult: ApplicationResult) => {
+ return atom | ApplicationMetadataResult>(async (_get) => {
+ // We only need to fetch the first page to find the application creation transaction
+ const transactionResults = await indexer
+ .searchForTransactions()
+ .applicationID(applicationResult.id)
+ .limit(3)
+ .do()
+ .then((res) => res.transactions as TransactionResult[])
+
+ const creationTransaction = transactionResults
+ .flatMap((txn) => flattenTransactionResult(txn))
+ .find((txn) => txn['tx-type'] === TransactionType.appl && txn['created-application-index'] === applicationResult.id)
+ if (!creationTransaction) return null
+
+ const text = base64ToUtf8(creationTransaction.note ?? '')
+
+ const maybeArc2 = parseArc2(text)
+ if (maybeArc2 && maybeArc2.format === 'j') {
+ const arc2Data = parseJson(maybeArc2.data)
+ if (arc2Data && 'name' in arc2Data) {
+ return { name: arc2Data.name }
+ }
+ }
+
+ return null
+ })
+}
+
+export const [applicationMetadataResultsAtom, getApplicationMetadataResultAtom] = atomsInAtom(
+ createApplicationMetadataResultAtom,
+ (applicationResult) => applicationResult.id
+)
diff --git a/src/features/applications/data/application-summary.ts b/src/features/applications/data/application-summary.ts
new file mode 100644
index 000000000..8ad1d997f
--- /dev/null
+++ b/src/features/applications/data/application-summary.ts
@@ -0,0 +1,12 @@
+import { JotaiStore } from '@/features/common/data/types'
+import { ApplicationId } from './types'
+import { atom } from 'jotai'
+import { getApplicationResultAtom } from './application-result'
+import { asApplicationSummary } from '../mappers'
+
+export const createApplicationSummaryAtom = (store: JotaiStore, applicationId: ApplicationId) => {
+ return atom(async (get) => {
+ const applicationResult = await get(getApplicationResultAtom(store, applicationId))
+ return asApplicationSummary(applicationResult)
+ })
+}
diff --git a/src/features/applications/data/application.ts b/src/features/applications/data/application.ts
index c711d7e13..c8fc9bc1a 100644
--- a/src/features/applications/data/application.ts
+++ b/src/features/applications/data/application.ts
@@ -5,11 +5,13 @@ import { useMemo } from 'react'
import { loadable } from 'jotai/utils'
import { ApplicationId } from './types'
import { getApplicationResultAtom } from './application-result'
+import { getApplicationMetadataResultAtom } from './application-metadata'
export const createApplicationAtom = (store: JotaiStore, applicationId: ApplicationId) => {
return atom(async (get) => {
const applicationResult = await get(getApplicationResultAtom(store, applicationId))
- return asApplication(applicationResult)
+ const applicationMetadata = await get(getApplicationMetadataResultAtom(store, applicationResult))
+ return asApplication(applicationResult, applicationMetadata)
})
}
diff --git a/src/features/applications/data/types.ts b/src/features/applications/data/types.ts
index 4f325a3ca..36488bcdc 100644
--- a/src/features/applications/data/types.ts
+++ b/src/features/applications/data/types.ts
@@ -1 +1,5 @@
export type ApplicationId = number
+
+export type ApplicationMetadataResult = {
+ name: string
+}
diff --git a/src/features/applications/mappers/index.ts b/src/features/applications/mappers/index.ts
index 86f05c7e6..0a28bcc81 100644
--- a/src/features/applications/mappers/index.ts
+++ b/src/features/applications/mappers/index.ts
@@ -1,12 +1,21 @@
-import { Application, ApplicationGlobalStateType, ApplicationGlobalStateValue } from '../models'
+import { Application, ApplicationGlobalStateType, ApplicationGlobalStateValue, ApplicationSummary } from '../models'
import { ApplicationResult } from '@algorandfoundation/algokit-utils/types/indexer'
import { getApplicationAddress, modelsv2, encodeAddress } from 'algosdk'
import isUtf8 from 'isutf8'
import { Buffer } from 'buffer'
+import { ApplicationMetadataResult } from '../data/types'
+import { asJson } from '@/utils/as-json'
-export const asApplication = (application: ApplicationResult): Application => {
+export const asApplicationSummary = (application: ApplicationResult): ApplicationSummary => {
return {
id: application.id,
+ }
+}
+
+export const asApplication = (application: ApplicationResult, metadata?: ApplicationMetadataResult): Application => {
+ return {
+ id: application.id,
+ name: metadata?.name,
creator: application.params.creator,
account: getApplicationAddress(application.id),
globalStateSchema: application.params['global-state-schema']
@@ -24,6 +33,7 @@ export const asApplication = (application: ApplicationResult): Application => {
approvalProgram: application.params['approval-program'],
clearStateProgram: application.params['clear-state-program'],
globalState: asGlobalStateValue(application),
+ json: asJson(application),
}
}
diff --git a/src/features/applications/models/index.ts b/src/features/applications/models/index.ts
index 906252ffd..0777772db 100644
--- a/src/features/applications/models/index.ts
+++ b/src/features/applications/models/index.ts
@@ -1,7 +1,12 @@
import { ApplicationId } from '../data/types'
+export type ApplicationSummary = {
+ id: ApplicationId
+}
+
export type Application = {
id: ApplicationId
+ name?: string
account: string
creator: string
globalStateSchema?: ApplicationStateSchema
@@ -9,7 +14,7 @@ export type Application = {
approvalProgram: string
clearStateProgram: string
globalState: Map
- // TODO: PD - ARC2 app stuff
+ json: string
}
export type ApplicationStateSchema = {
diff --git a/src/features/applications/pages/application-page.test.tsx b/src/features/applications/pages/application-page.test.tsx
index e868613f0..2a5501dec 100644
--- a/src/features/applications/pages/application-page.test.tsx
+++ b/src/features/applications/pages/application-page.test.tsx
@@ -24,10 +24,12 @@ import {
applicationIdLabel,
applicationLocalStateByteLabel,
applicationLocalStateUintLabel,
+ applicationNameLabel,
} from '../components/labels'
import { descriptionListAssertion } from '@/tests/assertions/description-list-assertion'
import { tableAssertion } from '@/tests/assertions/table-assertion'
import { modelsv2, indexerModels } from 'algosdk'
+import { transactionResultMother } from '@/tests/object-mother/transaction-result'
describe('application-page', () => {
describe('when rendering an application using an invalid application Id', () => {
@@ -72,7 +74,7 @@ describe('application-page', () => {
})
describe('when rendering an application', () => {
- const applicationResult = applicationResultMother['mainner-80441968']().build()
+ const applicationResult = applicationResultMother['mainnet-80441968']().build()
it('should be rendered with the correct data', () => {
const myStore = createStore()
@@ -119,6 +121,9 @@ describe('application-page', () => {
})
)
)
+ vi.mocked(indexer.searchForTransactions().applicationID(applicationResult.id).limit(3).do).mockImplementation(() =>
+ Promise.resolve({ currentRound: 123, transactions: [], nextToken: '' })
+ )
return executeComponentTest(
() => {
@@ -179,4 +184,37 @@ describe('application-page', () => {
)
})
})
+
+ describe('when rendering an application that has app name following algokit standard', () => {
+ const applicationResult = applicationResultMother['mainnet-1196727051']().build()
+ const transactionResult = transactionResultMother['mainnet-XCXQW7J5G5QSPVU5JFYEELVIAAABPLZH2I36BMNVZLVHOA75MPAQ']().build()
+
+ it('should be rendered with the correct app name', () => {
+ const myStore = createStore()
+ myStore.set(applicationResultsAtom, new Map([[applicationResult.id, atom(applicationResult)]]))
+
+ vi.mocked(useParams).mockImplementation(() => ({ applicationId: applicationResult.id.toString() }))
+ vi.mocked(indexer.searchForTransactions().applicationID(applicationResult.id).limit(3).do).mockImplementation(() =>
+ Promise.resolve({ currentRound: 123, transactions: [transactionResult], nextToken: '' })
+ )
+
+ return executeComponentTest(
+ () => {
+ return render(, undefined, myStore)
+ },
+ async (component) => {
+ await waitFor(async () => {
+ const detailsCard = component.getByLabelText(applicationDetailsLabel)
+ descriptionListAssertion({
+ container: detailsCard,
+ items: [
+ { term: applicationIdLabel, description: '1196727051' },
+ { term: applicationNameLabel, description: 'cryptoless-JIUK4YAO2GU7UX36JHH35KWI4AJ3PDEYSRQ75PCJJKR5UBX6RQ6Y5UZSJQ' },
+ ],
+ })
+ })
+ }
+ )
+ })
+ })
})
diff --git a/src/features/search/data/search.ts b/src/features/search/data/search.ts
index fb12c4bab..6402d16c3 100644
--- a/src/features/search/data/search.ts
+++ b/src/features/search/data/search.ts
@@ -13,8 +13,8 @@ import { atomWithDebounce } from '@/features/common/data'
import { isAddress } from '@/utils/is-address'
import { isTransactionId } from '@/utils/is-transaction-id'
import { isInteger } from '@/utils/is-integer'
-import { createApplicationAtom } from '@/features/applications/data'
import { syncedRoundAtom } from '@/features/blocks/data'
+import { createApplicationSummaryAtom } from '@/features/applications/data/application-summary'
const handle404 = (e: Error) => {
if (is404(e)) {
@@ -66,7 +66,7 @@ const createSearchAtoms = (store: JotaiStore) => {
}
const assetAtom = createAssetSummaryAtom(store, id)
- const applicationAtom = createApplicationAtom(store, id)
+ const applicationAtom = createApplicationSummaryAtom(store, id)
try {
const [asset, application] = await Promise.all([
diff --git a/src/features/transactions/components/transaction-note.tsx b/src/features/transactions/components/transaction-note.tsx
index 199056d2b..9a1683de6 100644
--- a/src/features/transactions/components/transaction-note.tsx
+++ b/src/features/transactions/components/transaction-note.tsx
@@ -1,38 +1,15 @@
import { cn } from '@/features/common/utils'
-import { Arc2TransactionNote } from '@algorandfoundation/algokit-utils/types/transaction'
import { useMemo } from 'react'
import { DescriptionList } from '@/features/common/components/description-list'
import { base64ToUtf8 } from '@/utils/base64-to-utf8'
import { OverflowAutoTabsContent, Tabs, TabsList, TabsTrigger } from '@/features/common/components/tabs'
+import { parseArc2 } from '../mappers/arc-2'
+import { parseJson } from '@/utils/parse-json'
type TransactionNoteProps = {
note: string
}
-function parseJson(maybeJson: string) {
- try {
- const json = JSON.parse(maybeJson)
- if (json && typeof json === 'object') {
- return json
- }
- } catch (e) {
- // ignore
- }
-}
-
-// Based on the ARC-2 spec https://github.com/algorandfoundation/ARCs/blob/main/ARCs/arc-0002.md#specification
-const arc2Regex = /^([a-zA-Z0-9][a-zA-Z0-9_/@.-]{4,31}):([mjbu]{1})(.*)$/
-function parseArc2(maybeArc2: string) {
- const result = maybeArc2.match(arc2Regex)
- if (result && result.length === 4) {
- return {
- dAppName: result[1],
- format: result[2] as 'm' | 'b' | 'u' | 'j',
- data: result[3],
- } satisfies Arc2TransactionNote
- }
-}
-
const base64NoteTabId = 'base64'
const textNoteTabId = 'text'
const jsonNoteTabId = 'json'
diff --git a/src/features/transactions/mappers/arc-2.ts b/src/features/transactions/mappers/arc-2.ts
new file mode 100644
index 000000000..595505433
--- /dev/null
+++ b/src/features/transactions/mappers/arc-2.ts
@@ -0,0 +1,15 @@
+import { Arc2TransactionNote } from '@algorandfoundation/algokit-utils/types/transaction'
+
+// Based on the ARC-2 spec https://github.com/algorandfoundation/ARCs/blob/main/ARCs/arc-0002.md#specification
+const arc2Regex = /^([a-zA-Z0-9][a-zA-Z0-9_/@.-]{4,31}):([mjbu]{1})(.*)$/
+
+export function parseArc2(maybeArc2: string) {
+ const result = maybeArc2.match(arc2Regex)
+ if (result && result.length === 4) {
+ return {
+ dAppName: result[1],
+ format: result[2] as 'm' | 'b' | 'u' | 'j',
+ data: result[3],
+ } satisfies Arc2TransactionNote
+ }
+}
diff --git a/src/tests/object-mother/application-result.ts b/src/tests/object-mother/application-result.ts
index aa29ce629..4ab0eafbd 100644
--- a/src/tests/object-mother/application-result.ts
+++ b/src/tests/object-mother/application-result.ts
@@ -5,7 +5,7 @@ export const applicationResultMother = {
basic: () => {
return applicationResultBuilder()
},
- 'mainner-80441968': () => {
+ 'mainnet-80441968': () => {
return new ApplicationResultBuilder({
id: 80441968,
params: {
@@ -35,6 +35,53 @@ export const applicationResultMother = {
},
})
},
+ 'mainnet-1196727051': () => {
+ return new ApplicationResultBuilder({
+ 'created-at-round': 32218016,
+ deleted: false,
+ id: 1196727051,
+ params: {
+ 'approval-program':
+ 'CSAEAAEGAiYJDG5mdGlja2V0X2FwcAABAA9tYW5hZ2VyX2FkZHJlc3MBAQQuU0kZBGg0o6oSbWV0aG9kX3Blcm1pc3Npb25zB2FpcmxpbmUxGyISQAEtNhoAgAThNZDwEkABETYaAIAEnNbuoRJAAPU2GgCABF5iYz4SQADZNhoAgAS2olF1EkAAvTYaAIAE+Qj2KRJAAKE2GgCABB0UDY4SQACFNhoAgATG43mkEkAAaTYaACcFEkAAUTYaAIAEnvUJRhJAADU2GgCABE+i3akSQAAZNhoAJwYSQAABADEZIhIxGCITEESIBBYjQzEZIhIxGCITEESIA+wjQzEZIhIxGCITEESIA7IjQzEZIhIxGCITEESIA34jQzEZIhIxGCITEESIA0wjQzEZIhIxGCITEESIAxAjQzEZIhIxGCISEESIAuAjQzEZIhIxGCITEESIArcjQzEZIhIxGCITEESIApcjQzEZIhIxGCITEESIAncjQzEZIhIxGCITEESIAlcjQzEZIxJAADYxGSUSQAAlMRmBBBJAABMxGYEFEkAAAQAxGCITRIgAViNDMRgiE0SIAEEjQzEYIhNEiABSI0MxGCITRIgAQyNDigIBi/4yCGFAAAQiQgAbi/4iJwdjNQE1ADQBQAAHIov/U0IABTQAQv/1iYoAADEAMgkSRCNDigAAMQAyCRJEI0OKAAAjQ4oAACNDigEAMQAyCRJEK4v/wBxniYoBADEAK2QSRLEkshAjshmL/8AyshgisgGziYoBADEAK2QSRLEkshAlshmL/8AyshgisgGziYoCADEAK2QSRIv+wBwnB4v/ZomKAgGL/icIZBJAAAqL/ov/iP9JQgABI4mKAwAoi/7AMmcri/1nJwiL/2cjQ4oFADEAIoj/y0SxJLIQKGSyGIAEWOGHaLIai/uyGov8shqL/bIai/6yGov/shoisgGziYoDADEAJIj/mESxJLIQKGSyGIv/wByyHIv+wDCyMIAErnlpMLIai/2yGiqyGicEshoisgGziYoDADEAgQOI/2FEsSSyEChkshiL/sAcshyL/8AcshyL/cAwsjAnBbIaKrIaJwSyGoABArIaIrIBs4mKBAAxAIEFiP8nRLEkshAoZLIYi/zAMLIwgAQ7tliRshoqshqL/bIai/4WVwQAshqL/7IaIrIBs4mKAgAxAIEEiP7uRLEkshAoZLIYi/7AMLIwgARMfzwNshoqshoqIov/VrIaIrIBs4mKAwAxACWI/r9EsSSyEChkshiL/8AcshyL/sAwsjAnBrIai/2yGiqyGicEshoisgGziYoAACI2GgEiVYwAiwCI/i2JigAAIjYaASJVjACLAIj+LImKAAAiNhoBIlWMAIsAiP42iYoAACJJNhoBIlWMADYaAheMAYsAiwGI/jeJigAAKSIpNhoBjAA2GgIiVYwBNhoDjAKLAIsBiwKI/kGJigAAKUcENhoBjAA2GgKMATYaA4wCNhoEjAM2GgWMBIsAiwGLAosDiwSI/iiJigAAKSJJNhoBjAA2GgIiVYwBNhoDIlWMAosAiwGLAoj+OImKAAAiRwI2GgEiVYwANhoCIlWMATYaAyJVjAKLAIsBiwKI/kmJigAAIikiKTYaASJVjAA2GgKMATYaAyJajAI2GgSMA4sAiwGLAosDiP5YiYoAACJJNhoBIlWMADYaAiJVjAGLAIsBiP52iYoAACkiSTYaAYwANhoCIlWMATYaAyJVjAKLAIsBiwKI/oOJ',
+ 'clear-state-program': 'CYEAQw==',
+ creator: '52MVNW6FNW7L6W7IAKSROD5FYZGZNLVKT6WUWNUFEE3DT737RYIIL2YQ3Y',
+ 'global-state': [
+ toTealKeyValue({
+ key: 'bWFuYWdlcl9hZGRyZXNz',
+ value: {
+ bytes: '0QGHvZ1GkfgBxdPm8vbFxBpukSHn/8UGJmPuEFl9eDk=',
+ type: 1,
+ uint: 0,
+ },
+ }),
+ toTealKeyValue({
+ key: 'YWlybGluZQ==',
+ value: {
+ bytes: 'SiiuYA7RqfpffknPvqrI4BO3jJiUYf68SUqj2gb+jD0=',
+ type: 1,
+ uint: 0,
+ },
+ }),
+ toTealKeyValue({
+ key: 'bmZ0aWNrZXRfYXBw',
+ value: {
+ bytes: '',
+ type: 2,
+ uint: 1196710954,
+ },
+ }),
+ ],
+ 'global-state-schema': {
+ 'num-byte-slice': 2,
+ 'num-uint': 1,
+ },
+ 'local-state-schema': {
+ 'num-byte-slice': 0,
+ 'num-uint': 1,
+ },
+ },
+ })
+ },
}
const toTealKeyValue = ({ key, value }: { key: string; value: { type: number; uint: number; bytes: string } }) =>
diff --git a/src/tests/object-mother/transaction-result.ts b/src/tests/object-mother/transaction-result.ts
index fb593d275..37a98a172 100644
--- a/src/tests/object-mother/transaction-result.ts
+++ b/src/tests/object-mother/transaction-result.ts
@@ -1281,4 +1281,77 @@ export const transactionResultMother = {
'tx-type': TransactionType.acfg,
})
},
+ ['mainnet-XCXQW7J5G5QSPVU5JFYEELVIAAABPLZH2I36BMNVZLVHOA75MPAQ']: () => {
+ return new TransactionResultBuilder({
+ 'application-transaction': {
+ accounts: [],
+ 'application-args': [
+ '+Qj2KQ==',
+ '0QGHvZ1GkfgBxdPm8vbFxBpukSHn/8UGJmPuEFl9eDk=',
+ 'AQ==',
+ 'SiiuYA7RqfpffknPvqrI4BO3jJiUYf68SUqj2gb+jD0=',
+ ],
+ 'application-id': 0,
+ 'approval-program':
+ 'CSAEAAEGAiYJDG5mdGlja2V0X2FwcAABAA9tYW5hZ2VyX2FkZHJlc3MBAQQuU0kZBGg0o6oSbWV0aG9kX3Blcm1pc3Npb25zB2FpcmxpbmUxGyISQAEtNhoAgAThNZDwEkABETYaAIAEnNbuoRJAAPU2GgCABF5iYz4SQADZNhoAgAS2olF1EkAAvTYaAIAE+Qj2KRJAAKE2GgCABB0UDY4SQACFNhoAgATG43mkEkAAaTYaACcFEkAAUTYaAIAEnvUJRhJAADU2GgCABE+i3akSQAAZNhoAJwYSQAABADEZIhIxGCITEESIBBYjQzEZIhIxGCITEESIA+wjQzEZIhIxGCITEESIA7IjQzEZIhIxGCITEESIA34jQzEZIhIxGCITEESIA0wjQzEZIhIxGCITEESIAxAjQzEZIhIxGCISEESIAuAjQzEZIhIxGCITEESIArcjQzEZIhIxGCITEESIApcjQzEZIhIxGCITEESIAncjQzEZIhIxGCITEESIAlcjQzEZIxJAADYxGSUSQAAlMRmBBBJAABMxGYEFEkAAAQAxGCITRIgAViNDMRgiE0SIAEEjQzEYIhNEiABSI0MxGCITRIgAQyNDigIBi/4yCGFAAAQiQgAbi/4iJwdjNQE1ADQBQAAHIov/U0IABTQAQv/1iYoAADEAMgkSRCNDigAAMQAyCRJEI0OKAAAjQ4oAACNDigEAMQAyCRJEK4v/wBxniYoBADEAK2QSRLEkshAjshmL/8AyshgisgGziYoBADEAK2QSRLEkshAlshmL/8AyshgisgGziYoCADEAK2QSRIv+wBwnB4v/ZomKAgGL/icIZBJAAAqL/ov/iP9JQgABI4mKAwAoi/7AMmcri/1nJwiL/2cjQ4oFADEAIoj/y0SxJLIQKGSyGIAEWOGHaLIai/uyGov8shqL/bIai/6yGov/shoisgGziYoDADEAJIj/mESxJLIQKGSyGIv/wByyHIv+wDCyMIAErnlpMLIai/2yGiqyGicEshoisgGziYoDADEAgQOI/2FEsSSyEChkshiL/sAcshyL/8AcshyL/cAwsjAnBbIaKrIaJwSyGoABArIaIrIBs4mKBAAxAIEFiP8nRLEkshAoZLIYi/zAMLIwgAQ7tliRshoqshqL/bIai/4WVwQAshqL/7IaIrIBs4mKAgAxAIEEiP7uRLEkshAoZLIYi/7AMLIwgARMfzwNshoqshoqIov/VrIaIrIBs4mKAwAxACWI/r9EsSSyEChkshiL/8AcshyL/sAwsjAnBrIai/2yGiqyGicEshoisgGziYoAACI2GgEiVYwAiwCI/i2JigAAIjYaASJVjACLAIj+LImKAAAiNhoBIlWMAIsAiP42iYoAACJJNhoBIlWMADYaAheMAYsAiwGI/jeJigAAKSIpNhoBjAA2GgIiVYwBNhoDjAKLAIsBiwKI/kGJigAAKUcENhoBjAA2GgKMATYaA4wCNhoEjAM2GgWMBIsAiwGLAosDiwSI/iiJigAAKSJJNhoBjAA2GgIiVYwBNhoDIlWMAosAiwGLAoj+OImKAAAiRwI2GgEiVYwANhoCIlWMATYaAyJVjAKLAIsBiwKI/kmJigAAIikiKTYaASJVjAA2GgKMATYaAyJajAI2GgSMA4sAiwGLAosDiP5YiYoAACJJNhoBIlWMADYaAiJVjAGLAIsBiP52iYoAACkiSTYaAYwANhoCIlWMATYaAyJVjAKLAIsBiwKI/oOJ',
+ 'clear-state-program': 'CYEAQw==',
+ 'foreign-apps': [1196710954],
+ 'foreign-assets': [],
+ 'global-state-schema': {
+ 'num-byte-slice': 2,
+ 'num-uint': 1,
+ },
+ 'local-state-schema': {
+ 'num-byte-slice': 0,
+ 'num-uint': 1,
+ },
+ 'on-completion': 'noop',
+ },
+ 'close-rewards': 0,
+ 'closing-amount': 0,
+ 'confirmed-round': 32218016,
+ 'created-application-index': 1196727051,
+ fee: 1000,
+ 'first-valid': 32218000,
+ 'genesis-hash': 'wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=',
+ 'genesis-id': 'mainnet-v1.0',
+ 'global-state-delta': [
+ {
+ key: 'YWlybGluZQ==',
+ value: {
+ action: 1,
+ bytes: 'SiiuYA7RqfpffknPvqrI4BO3jJiUYf68SUqj2gb+jD0=',
+ uint: 0,
+ },
+ },
+ {
+ key: 'bWFuYWdlcl9hZGRyZXNz',
+ value: {
+ action: 1,
+ bytes: '0QGHvZ1GkfgBxdPm8vbFxBpukSHn/8UGJmPuEFl9eDk=',
+ uint: 0,
+ },
+ },
+ {
+ key: 'bmZ0aWNrZXRfYXBw',
+ value: {
+ action: 2,
+ uint: 1196710954,
+ },
+ },
+ ],
+ id: 'XCXQW7J5G5QSPVU5JFYEELVIAAABPLZH2I36BMNVZLVHOA75MPAQ',
+ 'intra-round-offset': 18,
+ 'last-valid': 32219000,
+ note: 'QUxHT0tJVF9ERVBMT1lFUjpqeyJuYW1lIjogImNyeXB0b2xlc3MtSklVSzRZQU8yR1U3VVgzNkpISDM1S1dJNEFKM1BERVlTUlE3NVBDSkpLUjVVQlg2UlE2WTVVWlNKUSIsICJ2ZXJzaW9uIjogInYxLjAiLCAiZGVsZXRhYmxlIjogbnVsbCwgInVwZGF0YWJsZSI6IG51bGx9',
+ 'receiver-rewards': 0,
+ 'round-time': 1695154915,
+ sender: '52MVNW6FNW7L6W7IAKSROD5FYZGZNLVKT6WUWNUFEE3DT737RYIIL2YQ3Y',
+ 'sender-rewards': 0,
+ signature: {
+ sig: 'hogSpsFw9RMBA7wlp1bf66qInAlIQ9Q762bWwd/Wah2o5jeZ0dNp29QhXsposgCalhThD5PLVrr6N77vdWZICg==',
+ },
+ 'tx-type': 'appl',
+ } as unknown as TransactionResult)
+ },
}
diff --git a/src/tests/setup/mocks.ts b/src/tests/setup/mocks.ts
index e688c4036..619c3dba2 100644
--- a/src/tests/setup/mocks.ts
+++ b/src/tests/setup/mocks.ts
@@ -53,6 +53,11 @@ vi.mock('@/features/common/data', async () => {
}),
}),
}),
+ applicationID: vi.fn().mockReturnValue({
+ limit: vi.fn().mockReturnValue({
+ do: vi.fn().mockReturnValue({ then: vi.fn() }),
+ }),
+ }),
}),
lookupApplications: vi.fn().mockReturnValue({
includeAll: vi.fn().mockReturnValue({
diff --git a/src/utils/as-json.ts b/src/utils/as-json.ts
index 20e91d622..35103f04c 100644
--- a/src/utils/as-json.ts
+++ b/src/utils/as-json.ts
@@ -1,2 +1 @@
-export const asJson = (transactionResult: unknown) =>
- JSON.stringify(transactionResult, (_, v) => (typeof v === 'bigint' ? v.toString() : v), 2)
+export const asJson = (indexerResult: unknown) => JSON.stringify(indexerResult, (_, v) => (typeof v === 'bigint' ? v.toString() : v), 2)
diff --git a/src/utils/parse-json.ts b/src/utils/parse-json.ts
new file mode 100644
index 000000000..d5d49b1eb
--- /dev/null
+++ b/src/utils/parse-json.ts
@@ -0,0 +1,10 @@
+export function parseJson(maybeJson: string) {
+ try {
+ const json = JSON.parse(maybeJson)
+ if (json && typeof json === 'object') {
+ return json
+ }
+ } catch (e) {
+ // ignore
+ }
+}