- {application.id}
+ {application.id.toString()}
{application.isDeleted &&
Deleted}
diff --git a/src/features/applications/components/application-link.tsx b/src/features/applications/components/application-link.tsx
index fb71dac79..e9f5d6fb1 100644
--- a/src/features/applications/components/application-link.tsx
+++ b/src/features/applications/components/application-link.tsx
@@ -21,7 +21,7 @@ export function ApplicationLink({ applicationId, className, showCopyButton, chil
urlTemplate={Urls.Network.Explore.Application.ById}
urlParams={{ applicationId: applicationId.toString(), networkId: selectedNetwork }}
>
- {children ? children : applicationId}
+ {children ? children : applicationId.toString()}
)
diff --git a/src/features/applications/components/application-live-transactions.tsx b/src/features/applications/components/application-live-transactions.tsx
index c41b2725f..0ca196e1c 100644
--- a/src/features/applications/components/application-live-transactions.tsx
+++ b/src/features/applications/components/application-live-transactions.tsx
@@ -1,12 +1,12 @@
import { ApplicationId } from '../data/types'
import { useCallback } from 'react'
import { LiveTransactionsTable } from '@/features/transactions/components/live-transactions-table'
-import { TransactionResult } from '@algorandfoundation/algokit-utils/types/indexer'
import { flattenTransactionResult } from '@/features/transactions/utils/flatten-transaction-result'
import { TransactionType as AlgoSdkTransactionType } from 'algosdk'
import { Transaction, InnerTransaction } from '@/features/transactions/models'
import { getApplicationTransactionsTableSubRows } from '../utils/get-application-transactions-table-sub-rows'
import { transactionsTableColumns } from '@/features/transactions/components/transactions-table-columns'
+import { TransactionResult } from '@/features/transactions/data/types'
type Props = {
applicationId: ApplicationId
@@ -17,7 +17,7 @@ export function ApplicationLiveTransactions({ applicationId }: Props) {
(transactionResult: TransactionResult) => {
const flattenedTransactionResults = flattenTransactionResult(transactionResult)
return flattenedTransactionResults.some(
- (txn) => txn['tx-type'] === AlgoSdkTransactionType.appl && txn['application-transaction']?.['application-id'] === applicationId
+ (txn) => txn.txType === AlgoSdkTransactionType.appl && txn.applicationTransaction?.applicationId === applicationId
)
},
[applicationId]
diff --git a/src/features/applications/components/application-method-definitions.test.tsx b/src/features/applications/components/application-method-definitions.test.tsx
index b217d4963..32e848ef6 100644
--- a/src/features/applications/components/application-method-definitions.test.tsx
+++ b/src/features/applications/components/application-method-definitions.test.tsx
@@ -27,6 +27,8 @@ import { randomGuid } from '@/utils/random-guid'
import { asAddressOrNfd } from '@/features/transaction-wizard/mappers/as-address-or-nfd'
import { BuildableTransactionType } from '@/features/transaction-wizard/models'
import { asMethodDefinitions } from '@/features/applications/mappers'
+import { uint8ArrayToBase64 } from '@/utils/uint8-array-to-base64'
+import { act } from 'react'
describe('application-method-definitions', () => {
const localnet = algorandFixture()
@@ -41,8 +43,16 @@ describe('application-method-definitions', () => {
beforeEach(async () => {
const myStore = getTestStore()
await setWalletAddressAndSigner(localnet)
- const { app } = await deploySmartContract(localnet, Arc32TestContractAppSpec as AppSpec)
- appId = Number(app.appId)
+ const { app } = await deploySmartContract(localnet.context.testAccount, localnet.algorand, Arc32TestContractAppSpec as AppSpec)
+ appId = app.appId
+
+ const dispenser = await localnet.context.algorand.account.dispenserFromEnvironment()
+ await localnet.context.algorand.send.payment({
+ sender: dispenser,
+ receiver: app.appAddress,
+ amount: algo(10),
+ note: 'Fund app account',
+ })
const dbConnection = await myStore.get(dbConnectionAtom)
await upsertAppInterface(dbConnection, {
@@ -160,12 +170,8 @@ describe('application-method-definitions', () => {
)
const result = await localnet.context.waitForIndexerTransaction(transactionId)
- expect(result.transaction.sender).toBe(testAccount.addr)
- expect(result.transaction['logs']!).toMatchInlineSnapshot(`
- [
- "FR98dQAAAAAAAAAD",
- ]
- `)
+ expect(result.transaction.sender).toBe(testAccount.addr.toString())
+ expect(uint8ArrayToBase64(result.transaction.logs![0])).toBe('FR98dQAAAAAAAAAD')
}
)
})
@@ -244,12 +250,8 @@ describe('application-method-definitions', () => {
)
const result = await localnet.context.waitForIndexerTransaction(transactionId)
- expect(result.transaction.sender).toBe(testAccount.addr)
- expect(result.transaction['logs']!).toMatchInlineSnapshot(`
- [
- "FR98dQACAgI=",
- ]
- `)
+ expect(result.transaction.sender).toBe(testAccount.addr.toString())
+ expect(uint8ArrayToBase64(result.transaction.logs![0])).toBe('FR98dQACAgI=')
}
)
})
@@ -393,13 +395,9 @@ describe('application-method-definitions', () => {
)
const result = await localnet.context.waitForIndexerTransaction(transactionId)
- expect(result.transaction.sender).toBe(localnet.context.testAccount.addr)
- expect(result.transaction.fee).toBe(0)
- expect(result.transaction['logs']!).toMatchInlineSnapshot(`
- [
- "FR98dQAAAAAAAAAD",
- ]
- `)
+ expect(result.transaction.sender).toBe(localnet.context.testAccount.addr.toString())
+ expect(result.transaction.fee).toBe(0n)
+ expect(uint8ArrayToBase64(result.transaction.logs![0])).toBe('FR98dQAAAAAAAAAD')
}
)
})
@@ -479,12 +477,13 @@ describe('application-method-definitions', () => {
{ timeout: 10_000 }
)
const paymentTransaction = await localnet.context.waitForIndexerTransaction(paymentTransactionId)
- expect(paymentTransaction.transaction.sender).toBe(testAccount.addr)
- expect(paymentTransaction.transaction['payment-transaction']!).toMatchInlineSnapshot(`
- {
- "amount": 500000,
- "close-amount": 0,
- "receiver": "${testAccount2.addr}",
+ expect(paymentTransaction.transaction.sender).toBe(testAccount.addr.toString())
+ expect(paymentTransaction.transaction.paymentTransaction!).toMatchInlineSnapshot(`
+ TransactionPayment {
+ "amount": 500000n,
+ "closeAmount": 0n,
+ "closeRemainderTo": undefined,
+ "receiver": "${testAccount2.addr.toString()}",
}
`)
@@ -499,13 +498,18 @@ describe('application-method-definitions', () => {
{ timeout: 10_000 }
)
+ // This is a bit hacky, we traverse the DOM to find the inner app call method name
+ // This is to verify that the abiMethod is resolved correctly for the app call
+ const innerAppCallTxnLink = within(resultsDiv)
+ .getAllByRole('link')
+ .find((a) => a.getAttribute('href')?.startsWith(`/localnet/transaction/${appCallTransactionId}`))
+ expect(innerAppCallTxnLink?.parentElement?.parentElement?.parentElement?.parentElement?.nextSibling?.textContent).toBe(
+ '1App Callget_pay_txn_amount'
+ )
+
const appCallTransaction = await localnet.context.waitForIndexerTransaction(appCallTransactionId)
- expect(appCallTransaction.transaction.sender).toBe(testAccount.addr)
- expect(appCallTransaction.transaction['logs']!).toMatchInlineSnapshot(`
- [
- "FR98dQAAAAAAB6Eg",
- ]
- `)
+ expect(appCallTransaction.transaction.sender).toBe(testAccount.addr.toString())
+ expect(uint8ArrayToBase64(appCallTransaction.transaction.logs![0])).toBe('FR98dQAAAAAAB6Eg')
}
)
})
@@ -601,12 +605,13 @@ describe('application-method-definitions', () => {
{ timeout: 10_000 }
)
const paymentTransaction = await localnet.context.waitForIndexerTransaction(paymentTransactionId)
- expect(paymentTransaction.transaction.sender).toBe(testAccount.addr)
- expect(paymentTransaction.transaction['payment-transaction']!).toMatchInlineSnapshot(`
- {
- "amount": 600000,
- "close-amount": 0,
- "receiver": "${testAccount2.addr}",
+ expect(paymentTransaction.transaction.sender).toBe(testAccount.addr.toString())
+ expect(paymentTransaction.transaction.paymentTransaction!).toMatchInlineSnapshot(`
+ TransactionPayment {
+ "amount": 600000n,
+ "closeAmount": 0n,
+ "closeRemainderTo": undefined,
+ "receiver": "${testAccount2.addr.toString()}",
}
`)
@@ -622,12 +627,8 @@ describe('application-method-definitions', () => {
)
const appCallTransaction = await localnet.context.waitForIndexerTransaction(appCallTransactionId)
- expect(appCallTransaction.transaction.sender).toBe(testAccount.addr)
- expect(appCallTransaction.transaction['logs']!).toMatchInlineSnapshot(`
- [
- "FR98dQAAAAAACSfA",
- ]
- `)
+ expect(appCallTransaction.transaction.sender).toBe(testAccount.addr.toString())
+ expect(uint8ArrayToBase64(appCallTransaction.transaction.logs![0])).toBe('FR98dQAAAAAACSfA')
}
)
})
@@ -727,12 +728,13 @@ describe('application-method-definitions', () => {
{ timeout: 10_000 }
)
const paymentTransaction = await localnet.context.waitForIndexerTransaction(paymentTransactionId)
- expect(paymentTransaction.transaction.sender).toBe(testAccount.addr)
- expect(paymentTransaction.transaction['payment-transaction']!).toMatchInlineSnapshot(`
- {
- "amount": 500000,
- "close-amount": 0,
- "receiver": "${testAccount2.addr}",
+ expect(paymentTransaction.transaction.sender).toBe(testAccount.addr.toString())
+ expect(paymentTransaction.transaction.paymentTransaction!).toMatchInlineSnapshot(`
+ TransactionPayment {
+ "amount": 500000n,
+ "closeAmount": 0n,
+ "closeRemainderTo": undefined,
+ "receiver": "${testAccount2.addr.toString()}",
}
`)
@@ -748,8 +750,8 @@ describe('application-method-definitions', () => {
)
const appCallTransaction = await localnet.context.waitForIndexerTransaction(appCallTransactionId)
- expect(appCallTransaction.transaction.sender).toBe(testAccount.addr)
- expect(appCallTransaction.transaction.note).toBe('aGVsbG8gd29ybGQh')
+ expect(appCallTransaction.transaction.sender).toBe(testAccount.addr.toString())
+ expect(uint8ArrayToBase64(appCallTransaction.transaction.note!)).toBe('aGVsbG8gd29ybGQh')
}
)
})
@@ -893,12 +895,8 @@ describe('application-method-definitions', () => {
)
const result = await localnet.context.waitForIndexerTransaction(transactionId)
- expect(result.transaction.sender).toBe(testAccount.addr)
- expect(result.transaction['logs']!).toMatchInlineSnapshot(`
- [
- "FR98dQABAQ==",
- ]
- `)
+ expect(result.transaction.sender).toBe(testAccount.addr.toString())
+ expect(uint8ArrayToBase64(result.transaction.logs![0])).toBe('FR98dQABAQ==')
}
)
})
@@ -983,12 +981,8 @@ describe('application-method-definitions', () => {
)
const result = await localnet.context.waitForIndexerTransaction(transactionId)
- expect(result.transaction.sender).toBe(testAccount.addr)
- expect(result.transaction['logs']!).toMatchInlineSnapshot(`
- [
- "FR98dQACAgI=",
- ]
- `)
+ expect(result.transaction.sender).toBe(testAccount.addr.toString())
+ expect(uint8ArrayToBase64(result.transaction.logs![0])).toBe('FR98dQACAgI=')
}
)
})
@@ -1096,12 +1090,8 @@ describe('application-method-definitions', () => {
)
const result = await localnet.context.waitForIndexerTransaction(transactionId)
- expect(result.transaction.sender).toBe(testAccount.addr)
- expect(result.transaction['logs']!).toMatchInlineSnapshot(`
- [
- "FR98dQAAAAAAAAABAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAAE",
- ]
- `)
+ expect(result.transaction.sender).toBe(testAccount.addr.toString())
+ expect(uint8ArrayToBase64(result.transaction.logs![0])).toBe('FR98dQAAAAAAAAABAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAAE')
}
)
})
@@ -1203,12 +1193,8 @@ describe('application-method-definitions', () => {
)
const result = await localnet.context.waitForIndexerTransaction(transactionId)
- expect(result.transaction.sender).toBe(testAccount.addr)
- expect(result.transaction['logs']!).toMatchInlineSnapshot(`
- [
- "FR98dQAAAAAAAAABAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAAs",
- ]
- `)
+ expect(result.transaction.sender).toBe(testAccount.addr.toString())
+ expect(uint8ArrayToBase64(result.transaction.logs![0])).toBe('FR98dQAAAAAAAAABAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAAs')
}
)
})
@@ -1284,12 +1270,8 @@ describe('application-method-definitions', () => {
)
const result = await localnet.context.waitForIndexerTransaction(transactionId)
- expect(result.transaction.sender).toBe(testAccount.addr)
- expect(result.transaction['logs']!).toMatchInlineSnapshot(`
- [
- "FR98dQADAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAD",
- ]
- `)
+ expect(result.transaction.sender).toBe(testAccount.addr.toString())
+ expect(uint8ArrayToBase64(result.transaction.logs![0])).toBe('FR98dQADAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAD')
}
)
})
@@ -1391,12 +1373,8 @@ describe('application-method-definitions', () => {
)
const result = await localnet.context.waitForIndexerTransaction(transactionId)
- expect(result.transaction.sender).toBe(testAccount.addr)
- expect(result.transaction['logs']!).toMatchInlineSnapshot(`
- [
- "FR98dQAEAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAADAAAAAAAAAAQ=",
- ]
- `)
+ expect(result.transaction.sender).toBe(testAccount.addr.toString())
+ expect(uint8ArrayToBase64(result.transaction.logs![0])).toBe('FR98dQAEAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAADAAAAAAAAAAQ=')
}
)
})
@@ -1489,12 +1467,10 @@ describe('application-method-definitions', () => {
)
const result = await localnet.context.waitForIndexerTransaction(transactionId)
- expect(result.transaction.sender).toBe(testAccount.addr)
- expect(result.transaction['logs']!).toMatchInlineSnapshot(`
- [
- "FR98dQAEAC4AAgAEABYAAgAAAAAAAAABAAAAAAAAAAIAAgAAAAAAAAADAAAAAAAAAAQABAAWAAIAAAAAAAAABQAAAAAAAAAGAAVIZWxsbw==",
- ]
- `)
+ expect(result.transaction.sender).toBe(testAccount.addr.toString())
+ expect(uint8ArrayToBase64(result.transaction.logs![0])).toBe(
+ 'FR98dQAEAC4AAgAEABYAAgAAAAAAAAABAAAAAAAAAAIAAgAAAAAAAAADAAAAAAAAAAQABAAWAAIAAAAAAAAABQAAAAAAAAAGAAVIZWxsbw=='
+ )
}
)
})
@@ -1625,12 +1601,10 @@ describe('application-method-definitions', () => {
)
const result = await localnet.context.waitForIndexerTransaction(transactionId)
- expect(result.transaction.sender).toBe(testAccount.addr)
- expect(result.transaction['logs']!).toMatchInlineSnapshot(`
- [
- "FR98dQAEAD4AAgAEAB4AAwAAAAAAAAABAAAAAAAAAAIAAAAAAAAACwADAAAAAAAAAAMAAAAAAAAABAAAAAAAAAAWAAQAHgADAAAAAAAAAAUAAAAAAAAABgAAAAAAAAAhAAVIZWxsbw==",
- ]
- `)
+ expect(result.transaction.sender).toBe(testAccount.addr.toString())
+ expect(uint8ArrayToBase64(result.transaction.logs![0])).toBe(
+ 'FR98dQAEAD4AAgAEAB4AAwAAAAAAAAABAAAAAAAAAAIAAAAAAAAACwADAAAAAAAAAAMAAAAAAAAABAAAAAAAAAAWAAQAHgADAAAAAAAAAAUAAAAAAAAABgAAAAAAAAAhAAVIZWxsbw=='
+ )
}
)
})
@@ -1695,12 +1669,8 @@ describe('application-method-definitions', () => {
)
const result = await localnet.context.waitForIndexerTransaction(transactionId)
- expect(result.transaction.sender).toBe(testAccount.addr)
- expect(result.transaction['logs']!).toMatchInlineSnapshot(`
- [
- "FR98dYA=",
- ]
- `)
+ expect(result.transaction.sender).toBe(testAccount.addr.toString())
+ expect(uint8ArrayToBase64(result.transaction.logs![0])).toBe('FR98dYA=')
}
)
})
@@ -1787,12 +1757,122 @@ describe('application-method-definitions', () => {
)
const result = await localnet.context.waitForIndexerTransaction(transactionId)
- expect(result.transaction.sender).toBe(testAccount.addr)
- expect(result.transaction['logs']!).toMatchInlineSnapshot(`
- [
- "FR98dQA=",
- ]
- `)
+ expect(result.transaction.sender).toBe(testAccount.addr.toString())
+ expect(uint8ArrayToBase64(result.transaction.logs![0])).toBe('FR98dQA=')
+ }
+ )
+ })
+ })
+
+ describe('when calling inner_pay_appl method', () => {
+ let innerAppId: ApplicationId
+
+ beforeEach(async () => {
+ const myStore = getTestStore()
+ const testAccount2 = await localnet.context.generateAccount({ initialFunds: algo(10) })
+
+ const { app: innerApp } = await deploySmartContract(testAccount2, localnet.algorand, Arc32TestContractAppSpec as AppSpec, {
+ onUpdate: 'append',
+ })
+ innerAppId = innerApp.appId
+
+ const dbConnection = await myStore.get(dbConnectionAtom)
+ await upsertAppInterface(dbConnection, {
+ applicationId: innerAppId,
+ name: 'test_inner',
+ appSpecVersions: [
+ {
+ standard: AppSpecStandard.ARC32,
+ appSpec: Arc32TestContractAppSpec as unknown as Arc32AppSpec,
+ },
+ ],
+ lastModified: createTimestamp(),
+ } satisfies AppInterfaceEntity)
+ })
+
+ it('succeeds when all fields have been correctly supplied', async () => {
+ const myStore = getTestStore()
+ const { testAccount } = localnet.context
+ vi.mocked(useParams).mockImplementation(() => ({ applicationId: appId.toString() }))
+
+ return executeComponentTest(
+ () => {
+ return render(
, undefined, myStore)
+ },
+ async (component, user) => {
+ const methodPanel = await expandMethodAccordion(component, user, 'inner_pay_appl')
+
+ // Call the method
+ const callButton = await waitFor(() => {
+ const callButton = within(methodPanel).getByRole('button', { name: 'Call' })
+ expect(callButton).not.toBeDisabled()
+ return callButton!
+ })
+ await user.click(callButton)
+ const formDialog = component.getByRole('dialog')
+
+ // Enter the inner app id
+ const arg1Input = await getArgInput(formDialog, 'Argument 1')
+ fireEvent.input(arg1Input, {
+ target: { value: innerAppId.toString() },
+ })
+
+ await setCheckbox(formDialog, user, 'Set fee automatically', false)
+ const feeInput = await within(formDialog).findByLabelText(/Fee/)
+ fireEvent.input(feeInput, {
+ target: { value: '0.003' },
+ })
+
+ // Save the transaction
+ await user.click(within(formDialog).getByRole('button', { name: 'Add' }))
+
+ // Populate resources
+ const populateResourcesButton = await waitFor(() => {
+ const populateResourcesButton = component.getByRole('button', { name: 'Populate Resources' })
+ expect(populateResourcesButton).not.toBeDisabled()
+ return populateResourcesButton!
+ })
+ await user.click(populateResourcesButton)
+
+ // Send the transactions
+ const sendButton = await waitFor(() => {
+ const sendButton = component.getByRole('button', { name: sendButtonLabel })
+ expect(sendButton).not.toBeDisabled()
+ return sendButton!
+ })
+ await user.click(sendButton)
+
+ const resultsDiv = await waitFor(
+ () => {
+ expect(component.queryByText('Required')).not.toBeInTheDocument()
+ return component.getByText(groupSendResultsLabel).parentElement!
+ },
+ { timeout: 10_000 }
+ )
+
+ // Check the app call transaction
+ const appCallTransactionId = await waitFor(
+ () => {
+ const transactionLink = within(resultsDiv)
+ .getAllByRole('link')
+ .find((a) => a.getAttribute('href')?.startsWith('/localnet/transaction'))!
+ return transactionLink.getAttribute('href')!.split('/').pop()!
+ },
+ { timeout: 10_000 }
+ )
+
+ const appCallTransaction = await act(() => localnet.context.waitForIndexerTransaction(appCallTransactionId))
+ expect(appCallTransaction.transaction.sender).toBe(testAccount.addr.toString())
+ expect(uint8ArrayToBase64(appCallTransaction.transaction.logs![0])).toBe('FR98dQAAAAAAAYag')
+
+ // This is a bit hacky, we traverse the DOM to find the inner app call method name
+ // This is to verify that the abiMethod is resolved correctly for the inner app call
+ const innerAppCallTxnLink = within(resultsDiv)
+ .getAllByRole('link')
+ .find((a) => a.getAttribute('href')?.startsWith(`/localnet/transaction/${appCallTransactionId}/inner/2`))
+ expect(innerAppCallTxnLink?.parentElement?.parentElement?.parentElement?.nextSibling?.nextSibling?.textContent).toBe(
+ '2App Callget_pay_txn_amount'
+ )
}
)
})
@@ -1807,13 +1887,13 @@ describe('application-method-definitions', () => {
const { appId: _, ...params } = await asMethodCallParams({
id: randomGuid(),
- applicationId: 1988,
+ applicationId: 1988n,
type: BuildableTransactionType.MethodCall,
appSpec: Arc56TestContractAppSpec as Arc56Contract,
methodDefinition: asMethodDefinitions(Arc56TestContractAppSpec).find((m) => m.name === 'createApplication')!,
onComplete: 0,
methodArgs: [],
- sender: asAddressOrNfd(localnet.context.testAccount.addr),
+ sender: asAddressOrNfd(localnet.context.testAccount.addr.toString()),
fee: {
setAutomatically: true,
},
@@ -1822,26 +1902,31 @@ describe('application-method-definitions', () => {
},
})
- const { app } = await deploySmartContract(localnet, Arc56TestContractAppSpec as Arc56Contract, {
- createParams: {
- ...params,
- method: params.method.name,
- onComplete: params.onComplete,
- schema: {
- localInts: Arc56TestContractAppSpec.state.schema.local.ints ?? 0,
- localByteSlices: Arc56TestContractAppSpec.state.schema.local.bytes ?? 0,
- globalInts: Arc56TestContractAppSpec.state.schema.global.ints ?? 0,
- globalByteSlices: Arc56TestContractAppSpec.state.schema.global.bytes ?? 0,
+ const { app } = await deploySmartContract(
+ localnet.context.testAccount,
+ localnet.algorand,
+ Arc56TestContractAppSpec as Arc56Contract,
+ {
+ createParams: {
+ ...params,
+ method: params.method.name,
+ onComplete: params.onComplete,
+ schema: {
+ localInts: Arc56TestContractAppSpec.state.schema.local.ints ?? 0,
+ localByteSlices: Arc56TestContractAppSpec.state.schema.local.bytes ?? 0,
+ globalInts: Arc56TestContractAppSpec.state.schema.global.ints ?? 0,
+ globalByteSlices: Arc56TestContractAppSpec.state.schema.global.bytes ?? 0,
+ },
},
- },
- onUpdate: 'append',
- onSchemaBreak: 'append',
- deployTimeParams: {
- someNumber: 1000,
- },
- populateAppCallResources: true,
- })
- appId = Number(app.appId)
+ onUpdate: 'append',
+ onSchemaBreak: 'append',
+ deployTimeParams: {
+ someNumber: 1000,
+ },
+ populateAppCallResources: true,
+ }
+ )
+ appId = app.appId
const dbConnection = await myStore.get(dbConnectionAtom)
await upsertAppInterface(dbConnection, {
@@ -1932,12 +2017,8 @@ describe('application-method-definitions', () => {
)
const result = await localnet.context.waitForIndexerTransaction(transactionId)
- expect(result.transaction.sender).toBe(testAccount.addr)
- expect(result.transaction['logs']!).toMatchInlineSnapshot(`
- [
- "FR98dQAAAAAAAAADAAAAAAAAAAI=",
- ]
- `)
+ expect(result.transaction.sender).toBe(testAccount.addr.toString())
+ expect(uint8ArrayToBase64(result.transaction.logs![0])).toBe('FR98dQAAAAAAAAADAAAAAAAAAAI=')
}
)
})
@@ -2048,12 +2129,8 @@ describe('application-method-definitions', () => {
)
const result = await localnet.context.waitForIndexerTransaction(transactionId)
- expect(result.transaction.sender).toBe(testAccount.addr)
- expect(result.transaction['logs']!).toMatchInlineSnapshot(`
- [
- "FR98dQAAAAAAAAAGAAAAAAAAAAI=",
- ]
- `)
+ expect(result.transaction.sender).toBe(testAccount.addr.toString())
+ expect(uint8ArrayToBase64(result.transaction.logs![0])).toBe('FR98dQAAAAAAAAAGAAAAAAAAAAI=')
}
)
})
@@ -2104,12 +2181,9 @@ const addItemIntoDynamicArray = async (parentComponent: HTMLElement, user: UserE
}
const expandMethodAccordion = async (component: RenderResult, user: UserEvent, methodName: string) => {
- return waitFor(async () => {
- const accordionTrigger = component.getByRole('button', { name: methodName })
- await user.click(accordionTrigger)
-
- return component.getByRole('region', { name: methodName })
- })
+ const accordionTrigger = await component.findByRole('button', { name: methodName })
+ await user.click(accordionTrigger)
+ return component.getByRole('region', { name: methodName })
}
const getStructArgInput = async (parentComponent: HTMLElement, argName: string, path: string[]) => {
diff --git a/src/features/applications/components/application-program.test.tsx b/src/features/applications/components/application-program.test.tsx
index 21a6ae800..9e23933f3 100644
--- a/src/features/applications/components/application-program.test.tsx
+++ b/src/features/applications/components/application-program.test.tsx
@@ -3,6 +3,7 @@ import { getByRole, render, waitFor } from '../../../tests/testing-library'
import { ApplicationProgram, base64ProgramTabLabel, tealProgramTabLabel } from './application-program'
import { executeComponentTest } from '@/tests/test-component'
import { algod } from '@/features/common/data/algo-client'
+import algosdk from 'algosdk'
vi.mock('@/features/common/data/algo-client', async () => {
const original = await vi.importActual('@/features/common/data/algo-client')
@@ -23,7 +24,9 @@ describe('application-program', () => {
const teal = '\n#pragma version 8\nint 1\nreturn\n'
it('should be rendered with the correct data', () => {
- vi.mocked(algod.disassemble('').do).mockImplementation(() => Promise.resolve({ result: teal }))
+ vi.mocked(algod.disassemble('').do).mockImplementation(() =>
+ Promise.resolve(new algosdk.modelsv2.DisassembleResponse({ result: teal }))
+ )
return executeComponentTest(
() => {
diff --git a/src/features/applications/components/confirm-transactions-resources-form.tsx b/src/features/applications/components/confirm-transactions-resources-form.tsx
index 728375c29..77e18c83c 100644
--- a/src/features/applications/components/confirm-transactions-resources-form.tsx
+++ b/src/features/applications/components/confirm-transactions-resources-form.tsx
@@ -3,18 +3,19 @@ import { CancelButton } from '@/features/forms/components/cancel-button'
import { Form } from '@/features/forms/components/form'
import { FormActions } from '@/features/forms/components/form-actions'
import { SubmitButton } from '@/features/forms/components/submit-button'
-import { numberSchema } from '@/features/forms/data/common'
+import { bigIntSchema } from '@/features/forms/data/common'
import { useCallback, useMemo } from 'react'
import { z } from 'zod'
import { zfd } from 'zod-form-data'
import { ApplicationId } from '../data/types'
import { randomGuid } from '@/utils/random-guid'
import { isAddress } from '@/utils/is-address'
+import { AssetId } from '@/features/assets/data/types'
export type TransactionResources = {
accounts: Address[]
- assets: number[]
- applications: number[]
+ assets: AssetId[]
+ applications: ApplicationId[]
boxes: (readonly [ApplicationId, string])[]
}
@@ -37,10 +38,10 @@ const formSchema = zfd.formData({
)
.max(4)
),
- assets: zfd.repeatable(z.array(z.object({ id: z.string(), assetId: numberSchema(z.number().min(0)) })).max(8)),
- applications: zfd.repeatable(z.array(z.object({ id: z.string(), applicationId: numberSchema(z.number().min(0)) })).max(8)),
+ assets: zfd.repeatable(z.array(z.object({ id: z.string(), assetId: bigIntSchema(z.bigint().min(0n)) })).max(8)),
+ applications: zfd.repeatable(z.array(z.object({ id: z.string(), applicationId: bigIntSchema(z.bigint().min(0n)) })).max(8)),
boxes: zfd.repeatable(
- z.array(z.object({ id: z.string(), applicationId: numberSchema(z.number().min(0)), boxName: zfd.text(z.string().optional()) })).max(8)
+ z.array(z.object({ id: z.string(), applicationId: bigIntSchema(z.bigint().min(0n)), boxName: zfd.text(z.string().optional()) })).max(8)
),
})
diff --git a/src/features/applications/data/application-boxes.ts b/src/features/applications/data/application-boxes.ts
index c54797576..8a22e8eb2 100644
--- a/src/features/applications/data/application-boxes.ts
+++ b/src/features/applications/data/application-boxes.ts
@@ -12,6 +12,7 @@ import { asBoxDescriptor } from '../mappers'
import { base64ToUtf8IfValid } from '@/utils/base64-to-utf8'
import { base64ToBytes } from '@/utils/base64-to-bytes'
import { asDecodedAbiStorageValue } from '@/features/abi-methods/mappers'
+import { uint8ArrayToUtf8 } from '@/utils/uint8-array-to-utf8'
const getApplicationBoxNames = async (applicationId: ApplicationId, appSpec?: Arc56Contract, nextPageToken?: string) => {
const results = await indexer
@@ -44,7 +45,10 @@ export const useApplicationBox = (application: Application, boxDescriptor: BoxDe
return useMemo(() => {
return atom(async () => {
const result = await getApplicationBox(application.id, boxDescriptor.base64Name)
- const box = result.get_obj_for_encoding(false) as ApplicationBox
+ const box = {
+ name: uint8ArrayToUtf8(result.name),
+ value: uint8ArrayToUtf8(result.value),
+ } as ApplicationBox
if (application.appSpec && 'valueType' in boxDescriptor) {
return asDecodedAbiStorageValue(application.appSpec, boxDescriptor.valueType, base64ToBytes(box.value))
diff --git a/src/features/applications/data/application-metadata.ts b/src/features/applications/data/application-metadata.ts
index f24520fcf..99d7eab98 100644
--- a/src/features/applications/data/application-metadata.ts
+++ b/src/features/applications/data/application-metadata.ts
@@ -1,13 +1,13 @@
import { readOnlyAtomCache } from '@/features/common/data'
import { ApplicationMetadataResult, ApplicationResult } from './types'
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'
import { parseJson } from '@/utils/parse-json'
import { indexer } from '@/features/common/data/algo-client'
import { Getter, Setter } from 'jotai/index'
+import { uint8ArrayToUtf8 } from '@/utils/uint8-array-to-utf8'
+import { indexerTransactionToTransactionResult } from '@/features/transactions/mappers/indexer-transaction-mappers'
const getApplicationMetadataResult = async (
_: Getter,
@@ -20,14 +20,14 @@ const getApplicationMetadataResult = async (
.applicationID(applicationResult.id)
.limit(3)
.do()
- .then((res) => res.transactions as TransactionResult[])
+ .then((res) => res.transactions.map((txn) => indexerTransactionToTransactionResult(txn)))
const creationTransaction = transactionResults
.flatMap((txn) => flattenTransactionResult(txn))
- .find((txn) => txn['tx-type'] === TransactionType.appl && txn['created-application-index'] === applicationResult.id)
+ .find((txn) => txn.txType === TransactionType.appl && txn.createdApplicationIndex === applicationResult.id)
if (!creationTransaction) return null
- const text = base64ToUtf8(creationTransaction.note ?? '')
+ const text = uint8ArrayToUtf8(creationTransaction.note ?? new Uint8Array())
const maybeArc2 = parseArc2(text)
if (maybeArc2 && maybeArc2.format === 'j') {
diff --git a/src/features/applications/data/application-result.ts b/src/features/applications/data/application-result.ts
index 9837b1dee..3b35f90db 100644
--- a/src/features/applications/data/application-result.ts
+++ b/src/features/applications/data/application-result.ts
@@ -2,6 +2,7 @@ import { ApplicationId, ApplicationResult } from './types'
import { readOnlyAtomCache } from '@/features/common/data'
import { algod, indexer } from '@/features/common/data/algo-client'
import { asError, is404 } from '@/utils/error'
+import { removeEncodableMethods } from '@/utils/remove-encodable-methods'
import { Getter, Setter } from 'jotai/index'
const getApplicationResult = async (_: Getter, __: Setter, applicationId: ApplicationId) => {
@@ -10,7 +11,7 @@ const getApplicationResult = async (_: Getter, __: Setter, applicationId: Applic
return await algod
.getApplicationByID(applicationId)
.do()
- .then((result) => result as ApplicationResult)
+ .then((result) => removeEncodableMethods(result) as ApplicationResult)
} catch (e: unknown) {
if (is404(asError(e))) {
// Handle deleted applications or applications that may not be available in algod potentially due to the node type
@@ -18,7 +19,12 @@ const getApplicationResult = async (_: Getter, __: Setter, applicationId: Applic
.lookupApplications(applicationId)
.includeAll(true)
.do()
- .then((result) => result.application as ApplicationResult)
+ .then((result) => {
+ if (!result.application) {
+ throw new Error(`Application ${applicationId} not found`)
+ }
+ return removeEncodableMethods(result.application) as ApplicationResult
+ })
}
throw e
}
diff --git a/src/features/applications/data/application-transaction-history.ts b/src/features/applications/data/application-transaction-history.ts
index b2a98c946..da89c9416 100644
--- a/src/features/applications/data/application-transaction-history.ts
+++ b/src/features/applications/data/application-transaction-history.ts
@@ -1,23 +1,24 @@
import { ApplicationId } from './types'
import { createReadOnlyAtomAndTimestamp } from '@/features/common/data'
-import { TransactionResult, TransactionSearchResults } from '@algorandfoundation/algokit-utils/types/indexer'
import { createTransactionsAtom, transactionResultsAtom } from '@/features/transactions/data'
import { atomEffect } from 'jotai-effect'
import { atom } from 'jotai'
import { createLoadableViewModelPageAtom } from '@/features/common/data/lazy-load-pagination'
import { DEFAULT_FETCH_SIZE } from '@/features/common/constants'
import { indexer } from '@/features/common/data/algo-client'
+import { TransactionResult } from '@/features/transactions/data/types'
+import { indexerTransactionToTransactionResult } from '@/features/transactions/mappers/indexer-transaction-mappers'
const getApplicationTransactionResults = async (applicationID: ApplicationId, nextPageToken?: string) => {
- const results = (await indexer
+ const results = await indexer
.searchForTransactions()
.applicationID(applicationID)
.nextToken(nextPageToken ?? '')
.limit(DEFAULT_FETCH_SIZE)
- .do()) as TransactionSearchResults
+ .do()
return {
- transactionResults: results.transactions,
- nextPageToken: results['next-token'],
+ transactionResults: results.transactions.map((txn) => indexerTransactionToTransactionResult(txn)),
+ nextPageToken: results.nextToken,
} as const
}
diff --git a/src/features/applications/data/types.ts b/src/features/applications/data/types.ts
index 5aaaa4369..4f50d63d6 100644
--- a/src/features/applications/data/types.ts
+++ b/src/features/applications/data/types.ts
@@ -1,16 +1,26 @@
-import { ApplicationResult as IndexerApplicationResult } from '@algorandfoundation/algokit-utils/types/indexer'
+import algosdk from 'algosdk'
import { AppCallMethodCall } from '@algorandfoundation/algokit-utils/types/composer'
-export type ApplicationId = number
+export type ApplicationId = bigint
export type ApplicationMetadataResult = {
name: string
} | null
-export type ApplicationResult = Omit
& {
- params: Omit & {
- 'global-state'?: IndexerApplicationResult['params']['global-state']
+export type ApplicationResult = Omit<
+ algosdk.indexerModels.Application,
+ 'getEncodingSchema' | 'toEncodingData' | 'createdAtRound' | 'deletedAtRound' | 'params'
+> & {
+ params: Omit<
+ algosdk.indexerModels.ApplicationParams,
+ 'getEncodingSchema' | 'toEncodingData' | 'globalState' | 'globalStateSchema' | 'localStateSchema'
+ > & {
+ globalState?: algosdk.indexerModels.ApplicationParams['globalState']
+ globalStateSchema?: ApplicationStateSchema
+ localStateSchema?: ApplicationStateSchema
}
}
export type AppClientMethodCallParamsArgs = NonNullable[number]
+
+export type ApplicationStateSchema = Omit
diff --git a/src/features/applications/mappers/index.ts b/src/features/applications/mappers/index.ts
index a3597f849..5348f2386 100644
--- a/src/features/applications/mappers/index.ts
+++ b/src/features/applications/mappers/index.ts
@@ -15,9 +15,8 @@ import {
} from '../models'
import algosdk, { encodeAddress, getApplicationAddress, modelsv2 } from 'algosdk'
import isUtf8 from 'isutf8'
-import { Buffer } from 'buffer'
import { ApplicationMetadataResult, ApplicationResult } from '../data/types'
-import { asJson } from '@/utils/as-json'
+import { asJson, normaliseAlgoSdkData } from '@/utils/as-json'
import { AppSpec, Arc32AppSpec } from '@/features/app-interfaces/data/types'
import { isArc32AppSpec, isArc4AppSpec, isArc56AppSpec } from '@/features/common/utils'
import { AppSpec as UtiltsAppSpec, arc32ToArc56 } from '@algorandfoundation/algokit-utils/types/app-spec'
@@ -29,6 +28,8 @@ import { base64ToBytes } from '@/utils/base64-to-bytes'
import { DecodedAbiStorageKeyType, DecodedAbiStorageValue, DecodedAbiType } from '@/features/abi-methods/models'
import { asDecodedAbiStorageValue } from '@/features/abi-methods/mappers'
import { uint8ArrayStartsWith } from '@/utils/uint8-array-starts-with'
+import { ZERO_ADDRESS } from '@/features/common/constants'
+import { uint8ArrayToBase64 } from '@/utils/uint8-array-to-base64'
export const asApplicationSummary = (application: ApplicationResult): ApplicationSummary => {
return {
@@ -44,30 +45,30 @@ export const asApplication = (
return {
id: application.id,
name: metadata?.name,
- creator: application.params.creator,
- account: getApplicationAddress(application.id),
- globalStateSchema: application.params['global-state-schema']
+ creator: application.params.creator?.toString() ?? ZERO_ADDRESS,
+ account: getApplicationAddress(application.id).toString(),
+ globalStateSchema: application.params.globalStateSchema
? {
- numByteSlice: application.params['global-state-schema']['num-byte-slice'],
- numUint: application.params['global-state-schema']['num-uint'],
+ numByteSlice: application.params.globalStateSchema.numByteSlice,
+ numUint: application.params.globalStateSchema.numUint,
}
: undefined,
- localStateSchema: application.params['local-state-schema']
+ localStateSchema: application.params.localStateSchema
? {
- numByteSlice: application.params['local-state-schema']['num-byte-slice'],
- numUint: application.params['local-state-schema']['num-uint'],
+ numByteSlice: application.params.localStateSchema.numByteSlice,
+ numUint: application.params.localStateSchema.numUint,
}
: undefined,
- approvalProgram: application.params['approval-program'],
- clearStateProgram: application.params['clear-state-program'],
- globalState: asGlobalStateValue(application.params['global-state'], appSpec),
+ approvalProgram: uint8ArrayToBase64(application.params.approvalProgram),
+ clearStateProgram: uint8ArrayToBase64(application.params.clearStateProgram),
+ globalState: asGlobalStateValue(application.params.globalState, appSpec),
isDeleted: application.deleted ?? false,
- json: asJson(application),
+ json: asJson(normaliseAlgoSdkData(application)),
appSpec,
}
}
-export const asGlobalStateValue = (globalState: ApplicationResult['params']['global-state'], appSpec?: Arc56Contract): GlobalState[] => {
+export const asGlobalStateValue = (globalState: ApplicationResult['params']['globalState'], appSpec?: Arc56Contract): GlobalState[] => {
if (!globalState) {
return []
}
@@ -81,22 +82,21 @@ export const asGlobalStateValue = (globalState: ApplicationResult['params']['glo
})
}
-const asRawGlobalKey = (key: string): string => {
- const buffer = Buffer.from(key, 'base64')
+const asRawGlobalKey = (key: Uint8Array): string => {
+ const buffer = Buffer.from(key)
if (isUtf8(buffer)) {
return buffer.toString()
} else {
- return key
+ return uint8ArrayToBase64(key)
}
}
-const asRawGlobalValue = (bytes: string) => {
- const buf = Buffer.from(bytes, 'base64')
- if (buf.length === 32) {
- return encodeAddress(new Uint8Array(buf))
+const asRawGlobalValue = (bytes: Uint8Array) => {
+ if (bytes.length === 32) {
+ return encodeAddress(bytes)
}
- return base64ToUtf8IfValid(bytes)
+ return base64ToUtf8IfValid(uint8ArrayToBase64(bytes))
}
const getRawGlobalState = (state: modelsv2.TealKeyValue): RawGlobalState => {
@@ -118,7 +118,8 @@ const getRawGlobalState = (state: modelsv2.TealKeyValue): RawGlobalState => {
}
const asGlobalState = (state: modelsv2.TealKeyValue, appSpec?: Arc56Contract): GlobalState => {
- const { key, value } = state
+ const { key: keyBytes, value } = state
+ const key = uint8ArrayToBase64(keyBytes)
if (!appSpec) {
return getRawGlobalState(state)
@@ -202,7 +203,7 @@ const tealValueToAbiStorageValue = (appSpec: Arc56Contract, type: string, value:
} satisfies DecodedAbiStorageValue
}
- return asDecodedAbiStorageValue(appSpec, type, base64ToBytes(value.bytes))
+ return asDecodedAbiStorageValue(appSpec, type, value.bytes)
}
export const asArc56AppSpec = (appSpec: AppSpec): Arc56Contract => {
diff --git a/src/features/applications/pages/application-page-localnet.test.tsx b/src/features/applications/pages/application-page-localnet.test.tsx
index f1c5f59f6..bbb66b4ef 100644
--- a/src/features/applications/pages/application-page-localnet.test.tsx
+++ b/src/features/applications/pages/application-page-localnet.test.tsx
@@ -26,8 +26,8 @@ describe('application-page on localnet', () => {
})
beforeEach(async () => {
- const { app } = await deploySmartContract(localnet, Arc32TestContractAppSpec as AppSpec)
- appId = Number(app.appId)
+ const { app } = await deploySmartContract(localnet.context.testAccount, localnet.algorand, Arc32TestContractAppSpec as AppSpec)
+ appId = app.appId
})
it('should be rendered with the correct data', async () => {
diff --git a/src/features/applications/pages/application-page.test.tsx b/src/features/applications/pages/application-page.test.tsx
index 7c066ff8b..9739030af 100644
--- a/src/features/applications/pages/application-page.test.tsx
+++ b/src/features/applications/pages/application-page.test.tsx
@@ -30,7 +30,7 @@ import {
} from '../components/labels'
import { descriptionListAssertion } from '@/tests/assertions/description-list-assertion'
import { tableAssertion } from '@/tests/assertions/table-assertion'
-import { modelsv2, indexerModels } from 'algosdk'
+import algosdk, { modelsv2, indexerModels } from 'algosdk'
import { transactionResultMother } from '@/tests/object-mother/transaction-result'
import { refreshButtonLabel } from '@/features/common/components/refresh-button'
import { algod, indexer } from '@/features/common/data/algo-client'
@@ -165,7 +165,7 @@ describe('application-page', () => {
)
)
vi.mocked(indexer.searchForTransactions().applicationID(applicationResult.id).limit(3).do).mockImplementation(() =>
- Promise.resolve({ currentRound: 123, transactions: [], nextToken: '' })
+ Promise.resolve(new algosdk.indexerModels.TransactionsResponse({ currentRound: 123n, transactions: [], nextToken: '' }))
)
return executeComponentTest(
@@ -173,60 +173,58 @@ describe('application-page', () => {
return render(, undefined, myStore)
},
async (component, user) => {
- await waitFor(async () => {
- const detailsCard = component.getByLabelText(applicationDetailsLabel)
- descriptionListAssertion({
- container: detailsCard,
- items: [
- { term: applicationIdLabel, description: '80441968' },
- { term: applicationCreatorAccountLabel, description: '24YD4UNKUGVNGZ6QGXWIUPQ5L456FBH7LB5L6KFGQJ65YLQHXX4CQNPCZA' },
- { term: applicationAccountLabel, description: 'S3TLYVDRMR5VRKPACAYFXFLPNTYWQG37A6LPKERQ2DNABLTTGCXDUE2T3E' },
- { term: applicationGlobalStateByteLabel, description: '3' },
- { term: applicationLocalStateByteLabel, description: '0' },
- { term: applicationGlobalStateUintLabel, description: '12' },
- { term: applicationLocalStateUintLabel, description: '2' },
- ],
- })
+ const detailsCard = await component.findByLabelText(applicationDetailsLabel)
+ descriptionListAssertion({
+ container: detailsCard,
+ items: [
+ { term: applicationIdLabel, description: '80441968' },
+ { term: applicationCreatorAccountLabel, description: '24YD4UNKUGVNGZ6QGXWIUPQ5L456FBH7LB5L6KFGQJ65YLQHXX4CQNPCZA' },
+ { term: applicationAccountLabel, description: 'S3TLYVDRMR5VRKPACAYFXFLPNTYWQG37A6LPKERQ2DNABLTTGCXDUE2T3E' },
+ { term: applicationGlobalStateByteLabel, description: '3' },
+ { term: applicationLocalStateByteLabel, description: '0' },
+ { term: applicationGlobalStateUintLabel, description: '12' },
+ { term: applicationLocalStateUintLabel, description: '2' },
+ ],
+ })
- const applicationStateTabList = component.getByRole('tablist', { name: applicationStateLabel })
- expect(applicationStateTabList).toBeTruthy()
- // Only test the first 10 rows, should be enough
- const globalStateTab = await component.findByRole('tabpanel', {
- name: applicationGlobalStateLabel,
- })
- await tableAssertion({
- container: globalStateTab,
- rows: [
- { cells: ['Bids', 'Uint', '0'] },
- { cells: ['Creator', 'Bytes', '24YD4UNKUGVNGZ6QGXWIUPQ5L456FBH7LB5L6KFGQJ65YLQHXX4CQNPCZA'] },
- { cells: ['Dividend', 'Uint', '5'] },
- { cells: ['Escrow', 'Bytes', '24YD4UNKUGVNGZ6QGXWIUPQ5L456FBH7LB5L6KFGQJ65YLQHXX4CQNPCZA'] },
- { cells: ['FeesFirst', 'Uint', '250000'] },
- { cells: ['FeesSecond', 'Uint', '500000'] },
- { cells: ['Multiplier', 'Uint', '5'] },
- { cells: ['Pot', 'Uint', '0'] },
- { cells: ['Price', 'Uint', '1000000'] },
- { cells: ['RoundBegin', 'Uint', '1606905675'] },
- ],
- })
+ const applicationStateTabList = component.getByRole('tablist', { name: applicationStateLabel })
+ expect(applicationStateTabList).toBeTruthy()
+ // Only test the first 10 rows, should be enough
+ const globalStateTab = await component.findByRole('tabpanel', {
+ name: applicationGlobalStateLabel,
+ })
+ await tableAssertion({
+ container: globalStateTab,
+ rows: [
+ { cells: ['Bids', 'Uint', '0'] },
+ { cells: ['Creator', 'Bytes', '24YD4UNKUGVNGZ6QGXWIUPQ5L456FBH7LB5L6KFGQJ65YLQHXX4CQNPCZA'] },
+ { cells: ['Dividend', 'Uint', '5'] },
+ { cells: ['Escrow', 'Bytes', '24YD4UNKUGVNGZ6QGXWIUPQ5L456FBH7LB5L6KFGQJ65YLQHXX4CQNPCZA'] },
+ { cells: ['FeesFirst', 'Uint', '250000'] },
+ { cells: ['FeesSecond', 'Uint', '500000'] },
+ { cells: ['Multiplier', 'Uint', '5'] },
+ { cells: ['Pot', 'Uint', '0'] },
+ { cells: ['Price', 'Uint', '1000000'] },
+ { cells: ['RoundBegin', 'Uint', '1606905675'] },
+ ],
+ })
- await user.click(getByRole(applicationStateTabList, 'tab', { name: applicationBoxesLabel }))
- const boxesTab = await component.findByRole('tabpanel', { name: applicationBoxesLabel })
- await tableAssertion({
- container: boxesTab,
- rows: [
- { cells: ['AAAAAAAAAAAAAAAAABhjNpJEU5krRanhldfCDWa2Rs8='] },
- { cells: ['AAAAAAAAAAAAAAAAAB3fFPhSWjPaBhjzsx3NbXvlBK4='] },
- { cells: ['AAAAAAAAAAAAAAAAACctz98iaZ1MeSEbj+XCnD5CCwQ='] },
- { cells: ['AAAAAAAAAAAAAAAAACh7tCy49kQrUL7ykRWDmayeLKk='] },
- { cells: ['AAAAAAAAAAAAAAAAAECfyDmi7C5tEjBUI9N80BEnnAk='] },
- { cells: ['AAAAAAAAAAAAAAAAAEKTl0iZ2Q9UxPJphTgwplTfk6U='] },
- { cells: ['AAAAAAAAAAAAAAAAAEO4cIhnhmQ0qdQDLoXi7q0+G7o='] },
- { cells: ['AAAAAAAAAAAAAAAAAEVLZkp/l5eUQJZ/QEYYy9yNtuc='] },
- { cells: ['AAAAAAAAAAAAAAAAAEkbM2/K1+8IrJ/jdkgEoF/O5k0='] },
- { cells: ['AAAAAAAAAAAAAAAAAFwILIUnvVR4R/Xe9jTEV2SzTck='] },
- ],
- })
+ await user.click(getByRole(applicationStateTabList, 'tab', { name: applicationBoxesLabel }))
+ const boxesTab = await component.findByRole('tabpanel', { name: applicationBoxesLabel })
+ await tableAssertion({
+ container: boxesTab,
+ rows: [
+ { cells: ['AAAAAAAAAAAAAAAAABhjNpJEU5krRanhldfCDWa2Rs8='] },
+ { cells: ['AAAAAAAAAAAAAAAAAB3fFPhSWjPaBhjzsx3NbXvlBK4='] },
+ { cells: ['AAAAAAAAAAAAAAAAACctz98iaZ1MeSEbj+XCnD5CCwQ='] },
+ { cells: ['AAAAAAAAAAAAAAAAACh7tCy49kQrUL7ykRWDmayeLKk='] },
+ { cells: ['AAAAAAAAAAAAAAAAAECfyDmi7C5tEjBUI9N80BEnnAk='] },
+ { cells: ['AAAAAAAAAAAAAAAAAEKTl0iZ2Q9UxPJphTgwplTfk6U='] },
+ { cells: ['AAAAAAAAAAAAAAAAAEO4cIhnhmQ0qdQDLoXi7q0+G7o='] },
+ { cells: ['AAAAAAAAAAAAAAAAAEVLZkp/l5eUQJZ/QEYYy9yNtuc='] },
+ { cells: ['AAAAAAAAAAAAAAAAAEkbM2/K1+8IrJ/jdkgEoF/O5k0='] },
+ { cells: ['AAAAAAAAAAAAAAAAAFwILIUnvVR4R/Xe9jTEV2SzTck='] },
+ ],
})
}
)
@@ -245,7 +243,13 @@ describe('application-page', () => {
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: '' })
+ Promise.resolve(
+ new algosdk.indexerModels.TransactionsResponse({
+ currentRound: 123n,
+ transactions: [transactionResult] as algosdk.indexerModels.Transaction[],
+ nextToken: '',
+ })
+ )
)
return executeComponentTest(
@@ -319,7 +323,13 @@ describe('application-page', () => {
)
)
vi.mocked(indexer.searchForTransactions().applicationID(applicationResult.id).limit(3).do).mockImplementation(() =>
- Promise.resolve({ currentRound: 123, transactions: [], nextToken: '' })
+ Promise.resolve(
+ new algosdk.indexerModels.TransactionsResponse({
+ currentRound: 123n,
+ transactions: [],
+ nextToken: '',
+ })
+ )
)
return executeComponentTest(
@@ -361,7 +371,13 @@ describe('application-page', () => {
const applicationResult = applicationResultMother['testnet-718348254']().build()
vi.mocked(useParams).mockImplementation(() => ({ applicationId: applicationResult.id.toString() }))
vi.mocked(indexer.searchForTransactions().applicationID(applicationResult.id).limit(3).do).mockImplementation(() =>
- Promise.resolve({ currentRound: 123, transactions: [], nextToken: '' })
+ Promise.resolve(
+ new algosdk.indexerModels.TransactionsResponse({
+ currentRound: 123n,
+ transactions: [],
+ nextToken: '',
+ })
+ )
)
const myStore = createStore()
@@ -386,38 +402,36 @@ describe('application-page', () => {
return render(, undefined, myStore)
},
async (component, user) => {
- await waitFor(async () => {
- const abiMethodsCard = component.getByLabelText(applicationAbiMethodDefinitionsLabel)
- expect(abiMethodsCard).toBeTruthy()
+ const abiMethodsCard = await component.findByLabelText(applicationAbiMethodDefinitionsLabel)
+ expect(abiMethodsCard).toBeTruthy()
- const echoStructAccordionTrigger = component.getByRole('button', { name: 'echo_struct' })
- await user.click(echoStructAccordionTrigger)
+ const echoStructAccordionTrigger = component.getByRole('button', { name: 'echo_struct' })
+ await user.click(echoStructAccordionTrigger)
- const echoStructAccordionPanel = component.getByRole('region', { name: 'echo_struct' })
- expect(echoStructAccordionPanel).toBeTruthy()
+ const echoStructAccordionPanel = component.getByRole('region', { name: 'echo_struct' })
+ expect(echoStructAccordionPanel).toBeTruthy()
- const argumentsDiv = within(echoStructAccordionPanel).getByText('Arguments').parentElement
- expect(argumentsDiv).toBeTruthy()
+ const argumentsDiv = within(echoStructAccordionPanel).getByText('Arguments').parentElement
+ expect(argumentsDiv).toBeTruthy()
- const argument1Div = within(argumentsDiv!).getByText('Argument 1').parentElement
- descriptionListAssertion({
- container: argument1Div!,
- items: [
- { term: 'Name', description: 'inputUser' },
- { term: 'Type', description: 'UserStruct:name: stringid: uint64' },
- ],
- })
+ const argument1Div = within(argumentsDiv!).getByText('Argument 1').parentElement
+ descriptionListAssertion({
+ container: argument1Div!,
+ items: [
+ { term: 'Name', description: 'inputUser' },
+ { term: 'Type', description: 'UserStruct:name: stringid: uint64' },
+ ],
+ })
- const returnDiv = within(echoStructAccordionPanel).getByText('Return').parentElement
- expect(returnDiv).toBeTruthy()
+ const returnDiv = within(echoStructAccordionPanel).getByText('Return').parentElement
+ expect(returnDiv).toBeTruthy()
- const returnTypeDiv = within(returnDiv!).getByText('Type').parentElement
- expect(returnTypeDiv).toBeTruthy()
+ const returnTypeDiv = within(returnDiv!).getByText('Type').parentElement
+ expect(returnTypeDiv).toBeTruthy()
- descriptionListAssertion({
- container: returnTypeDiv!,
- items: [{ term: 'Type', description: 'UserStruct:name: stringid: uint64' }],
- })
+ descriptionListAssertion({
+ container: returnTypeDiv!,
+ items: [{ term: 'Type', description: 'UserStruct:name: stringid: uint64' }],
})
}
)
@@ -444,7 +458,13 @@ describe('application-page', () => {
)
)
vi.mocked(indexer.searchForTransactions().applicationID(applicationResult.id).limit(3).do).mockImplementation(() =>
- Promise.resolve({ currentRound: 123, transactions: [], nextToken: '' })
+ Promise.resolve(
+ new algosdk.indexerModels.TransactionsResponse({
+ currentRound: 123n,
+ transactions: [],
+ nextToken: '',
+ })
+ )
)
const myStore = createStore()
@@ -515,7 +535,13 @@ describe('application-page', () => {
)
)
vi.mocked(indexer.searchForTransactions().applicationID(applicationResult.id).limit(3).do).mockImplementation(() =>
- Promise.resolve({ currentRound: 123, transactions: [], nextToken: '' })
+ Promise.resolve(
+ new algosdk.indexerModels.TransactionsResponse({
+ currentRound: 123n,
+ transactions: [],
+ nextToken: '',
+ })
+ )
)
const myStore = createStore()
diff --git a/src/features/applications/pages/application-page.tsx b/src/features/applications/pages/application-page.tsx
index 846b7f8fe..991dfb854 100644
--- a/src/features/applications/pages/application-page.tsx
+++ b/src/features/applications/pages/application-page.tsx
@@ -31,7 +31,7 @@ export function ApplicationPage() {
const { applicationId: _applicationId } = useRequiredParam(UrlParams.ApplicationId)
invariant(isInteger(_applicationId), applicationInvalidIdMessage)
- const applicationId = parseInt(_applicationId, 10)
+ const applicationId = BigInt(_applicationId)
const [loadableApplication, refreshApplication, isStale] = useLoadableApplication(applicationId)
useTitle()
diff --git a/src/features/applications/utils/get-application-transactions-table-sub-rows.ts b/src/features/applications/utils/get-application-transactions-table-sub-rows.ts
index 4c2b7869a..e10d7e92e 100644
--- a/src/features/applications/utils/get-application-transactions-table-sub-rows.ts
+++ b/src/features/applications/utils/get-application-transactions-table-sub-rows.ts
@@ -1,7 +1,8 @@
import { Transaction, InnerTransaction, TransactionType } from '@/features/transactions/models'
import { flattenInnerTransactions } from '@/utils/flatten-inner-transactions'
+import { ApplicationId } from '../data/types'
-export const getApplicationTransactionsTableSubRows = (applicationId: number, transaction: Transaction | InnerTransaction) => {
+export const getApplicationTransactionsTableSubRows = (applicationId: ApplicationId, transaction: Transaction | InnerTransaction) => {
if (transaction.type !== TransactionType.AppCall || transaction.innerTransactions.length === 0) {
return []
}
diff --git a/src/features/assets/components/asset-details.tsx b/src/features/assets/components/asset-details.tsx
index 72a4456f0..18c0d46b0 100644
--- a/src/features/assets/components/asset-details.tsx
+++ b/src/features/assets/components/asset-details.tsx
@@ -57,7 +57,7 @@ export function AssetDetails({ asset }: Props) {
dd: (
- {asset.id}
+ {asset.id.toString()}
{asset.standardsUsed.map((s, i) => (
@@ -162,7 +162,7 @@ export function AssetDetails({ asset }: Props) {
- {asset.id !== 0 && (
+ {asset.id !== 0n && (
<>
diff --git a/src/features/assets/components/asset-link.tsx b/src/features/assets/components/asset-link.tsx
index a4afc3a6a..cce7a1fbb 100644
--- a/src/features/assets/components/asset-link.tsx
+++ b/src/features/assets/components/asset-link.tsx
@@ -7,6 +7,7 @@ import { AsyncMaybeAtom } from '@/features/common/data/types'
import { RenderInlineAsyncAtom } from '@/features/common/components/render-inline-async-atom'
import { CopyButton } from '@/features/common/components/copy-button'
import { useSelectedNetwork } from '@/features/network/data'
+import { AssetId } from '../data/types'
type CommonProps = {
className?: string
@@ -15,13 +16,13 @@ type CommonProps = {
type AssetIdLinkProps = PropsWithChildren<
{
- assetId: number
+ assetId: AssetId
} & CommonProps
>
type AssetIdAndNameLinkProps = PropsWithChildren<
{
- assetId: number
+ assetId: AssetId
assetName?: string
} & CommonProps
>
@@ -40,7 +41,7 @@ function Link(props: AssetIdLinkProps | AssetIdAndNameLinkProps) {
urlTemplate={Urls.Network.Explore.Asset.ById}
urlParams={{ assetId: props.assetId.toString(), networkId: selectedNetwork }}
>
- {props.children ? props.children : props.assetId}
+ {props.children ? props.children : props.assetId.toString()}
)
diff --git a/src/features/assets/components/asset-live-transactions.tsx b/src/features/assets/components/asset-live-transactions.tsx
index 2c493e78c..d57ee39ba 100644
--- a/src/features/assets/components/asset-live-transactions.tsx
+++ b/src/features/assets/components/asset-live-transactions.tsx
@@ -1,7 +1,7 @@
import { AssetId } from '../data/types'
import { useCallback } from 'react'
import { LiveTransactionsTable } from '@/features/transactions/components/live-transactions-table'
-import { TransactionResult } from '@algorandfoundation/algokit-utils/types/indexer'
+import { TransactionResult } from '@/features/transactions/data/types'
import { getAssetIdsForTransaction } from '@/features/transactions/utils/get-asset-ids-for-transaction'
import { InnerTransaction, Transaction } from '@/features/transactions/models'
import { getAssetTransactionsTableSubRows } from '../utils/get-asset-transactions-table-sub-rows'
diff --git a/src/features/assets/components/asset-transaction-history.test.tsx b/src/features/assets/components/asset-transaction-history.test.tsx
index 3108fe0ef..f237a4eab 100644
--- a/src/features/assets/components/asset-transaction-history.test.tsx
+++ b/src/features/assets/components/asset-transaction-history.test.tsx
@@ -11,6 +11,7 @@ import { transactionResultMother } from '@/tests/object-mother/transaction-resul
import { getAllByRole } from '@testing-library/dom'
import { ANY_NUMBER, ANY_STRING, searchTransactionsMock } from '@/tests/setup/mocks'
import { RenderResult } from '@testing-library/react'
+import algosdk from 'algosdk'
vi.mock('@/features/common/data/algo-client', async () => {
const original = await vi.importActual('@/features/common/data/algo-client')
@@ -33,14 +34,23 @@ describe('asset-transaction-history', () => {
vi.mocked(indexer.searchForTransactions().assetID(ANY_NUMBER).nextToken(ANY_STRING).limit(ANY_NUMBER).do).mockImplementation(() => {
const args = searchTransactionsMock.args
if (args.nextToken === '') {
- return Promise.resolve({
- transactions: Array.from({ length: 18 }).map(() => transactionResultMother.transfer(asset).build()),
- ['next-token']: '4652AgAAAAAFAAAA',
- })
+ return Promise.resolve(
+ new algosdk.indexerModels.TransactionsResponse({
+ transactions: Array.from({ length: 18 }).map(
+ () => transactionResultMother.transfer(asset).build() as algosdk.indexerModels.Transaction
+ ),
+ nextToken: '4652AgAAAAAFAAAA',
+ currentRound: 1,
+ })
+ )
}
- return Promise.resolve({
- transactions: [],
- })
+ return Promise.resolve(
+ new algosdk.indexerModels.TransactionsResponse({
+ transactions: [],
+ nextToken: undefined,
+ currentRound: 1,
+ })
+ )
})
// First page should have 10 items
diff --git a/src/features/assets/data/asset-metadata.ts b/src/features/assets/data/asset-metadata.ts
index 363177846..269ba46df 100644
--- a/src/features/assets/data/asset-metadata.ts
+++ b/src/features/assets/data/asset-metadata.ts
@@ -1,4 +1,3 @@
-import { TransactionResult, TransactionSearchResults } from '@algorandfoundation/algokit-utils/types/indexer'
import { flattenTransactionResult } from '@/features/transactions/utils/flatten-transaction-result'
import { TransactionType } from 'algosdk'
import { Arc3MetadataResult, Arc69MetadataResult, AssetMetadataResult, AssetResult } from './types'
@@ -11,6 +10,10 @@ import { readOnlyAtomCache } from '@/features/common/data'
import { indexer } from '@/features/common/data/algo-client'
import { replaceIpfsWithGatewayIfNeeded } from '../utils/replace-ipfs-with-gateway-if-needed'
import { Getter, Setter } from 'jotai/index'
+import { TransactionResult } from '@/features/transactions/data/types'
+import algosdk from 'algosdk'
+import { uint8ArrayToBase64 } from '@/utils/uint8-array-to-base64'
+import { indexerTransactionToTransactionResult } from '@/features/transactions/mappers/indexer-transaction-mappers'
// Currently, we support ARC-3, 19 and 69. Their specs can be found here https://github.com/algorandfoundation/ARCs/tree/main/ARCs
// ARCs are community standard, therefore, there are edge cases
@@ -28,7 +31,7 @@ const createAssetMetadataResult = async (
// Get ARC-69 metadata if applicable
if (latestAssetCreateOrReconfigureTransaction && latestAssetCreateOrReconfigureTransaction.note) {
- const metadata = noteToArc69Metadata(latestAssetCreateOrReconfigureTransaction.note)
+ const metadata = noteToArc69Metadata(uint8ArrayToBase64(latestAssetCreateOrReconfigureTransaction.note))
if (metadata) {
arc69MetadataResult = {
metadata,
@@ -108,7 +111,7 @@ const noteToArc69Metadata = (note: string | undefined) => {
}
const getAssetMetadataResult = async (_: Getter, __: Setter, assetResult: AssetResult) => {
- if (assetResult.index === 0) {
+ if (assetResult.index === 0n) {
return null
}
@@ -122,13 +125,13 @@ const getAssetMetadataResult = async (_: Getter, __: Setter, assetResult: AssetR
.addressRole('sender')
.limit(2) // Return 2 to cater for a destroy transaction and any potential eventual consistency delays between transactions and assets.
.do()
- .then((res) => res.transactions as TransactionResult[]) // Implicitly newest to oldest when filtering with an address.
+ .then((res) => res.transactions.map((txn) => indexerTransactionToTransactionResult(txn))) // Implicitly newest to oldest when filtering with an address.
: []
if (results.length === 0) {
// The asset has been destroyed, is an immutable asset, or the asset is mutable however has never been mutated.
// Fetch the entire acfg transaction history and reverse the order, so it's newest to oldest.
results = await executePaginatedRequest(
- (res: TransactionSearchResults) => res.transactions,
+ (res: algosdk.indexerModels.TransactionsResponse) => res.transactions.map((txn) => indexerTransactionToTransactionResult(txn)),
(nextToken) => {
let s = indexer.searchForTransactions().assetID(assetResult.index).txType('acfg')
if (nextToken) {
@@ -140,8 +143,8 @@ const getAssetMetadataResult = async (_: Getter, __: Setter, assetResult: AssetR
}
const assetConfigTransactionResults = results.flatMap(flattenTransactionResult).filter((t) => {
- const isAssetConfigTransaction = t['tx-type'] === TransactionType.acfg
- const isDestroyTransaction = t['asset-config-transaction']?.['params'] === undefined
+ const isAssetConfigTransaction = t.txType === TransactionType.acfg
+ const isDestroyTransaction = t.assetConfigTransaction?.params === undefined
return isAssetConfigTransaction && !isDestroyTransaction
})
diff --git a/src/features/assets/data/asset-opt-in-out.ts b/src/features/assets/data/asset-opt-in-out.ts
index afbedb153..1ac6503ba 100644
--- a/src/features/assets/data/asset-opt-in-out.ts
+++ b/src/features/assets/data/asset-opt-in-out.ts
@@ -12,7 +12,7 @@ export const useAssetOptInOut = (asset: Asset) => {
return atom(async (get) => {
const activeAccount = await get(activeWalletAccountAtom)
- if (asset.id === 0 || !activeAccount) {
+ if (asset.id === 0n || !activeAccount) {
return {
hasActiveAccount: !!activeAccount,
canOptIn: false,
@@ -23,7 +23,7 @@ export const useAssetOptInOut = (asset: Asset) => {
return {
hasActiveAccount: !!activeAccount,
canOptIn: !activeAccount.assetHolding.has(asset.id),
- canOptOut: activeAccount.assetHolding.has(asset.id) && activeAccount.assetHolding.get(asset.id)!.amount === 0,
+ canOptOut: activeAccount.assetHolding.has(asset.id) && activeAccount.assetHolding.get(asset.id)!.amount === 0n,
}
})
}, [asset])
diff --git a/src/features/assets/data/asset-result.ts b/src/features/assets/data/asset-result.ts
index b56871833..4035def05 100644
--- a/src/features/assets/data/asset-result.ts
+++ b/src/features/assets/data/asset-result.ts
@@ -5,19 +5,20 @@ import { readOnlyAtomCache } from '@/features/common/data'
import { ZERO_ADDRESS } from '@/features/common/constants'
import { algod, indexer } from '@/features/common/data/algo-client'
import { Getter, Setter } from 'jotai/index'
+import { removeEncodableMethods } from '@/utils/remove-encodable-methods'
-export const algoAssetResult = {
- index: 0,
- 'created-at-round': 0,
+export const algoAssetResult: AssetResult = {
+ index: 0n,
+ createdAtRound: 0n,
params: {
creator: ZERO_ADDRESS,
decimals: 6,
total: 10_000_000_000_000_000n,
name: 'ALGO',
- 'unit-name': 'ALGO',
+ unitName: 'ALGO',
url: 'https://www.algorand.foundation',
},
-} as AssetResult
+}
const getAssetResult = async (_: Getter, __: Setter, assetId: AssetId) => {
try {
@@ -25,7 +26,7 @@ const getAssetResult = async (_: Getter, __: Setter, assetId: AssetId) => {
return await algod
.getAssetByID(assetId)
.do()
- .then((result) => result as AssetResult)
+ .then((result) => removeEncodableMethods(result) as AssetResult)
} catch (e: unknown) {
if (is404(asError(e))) {
// Handle destroyed assets or assets that may not be available in algod potentially due to the node type
@@ -33,7 +34,12 @@ const getAssetResult = async (_: Getter, __: Setter, assetId: AssetId) => {
.lookupAssetByID(assetId)
.includeAll(true) // Returns destroyed assets
.do()
- .then((result) => result.asset as AssetResult)
+ .then((result) => {
+ if (!result.asset) {
+ throw new Error(`Asset ${assetId} not found`)
+ }
+ return removeEncodableMethods(result.asset) as AssetResult
+ })
}
throw e
}
diff --git a/src/features/assets/data/asset-transaction-history.ts b/src/features/assets/data/asset-transaction-history.ts
index fb9f09e2d..fe7243eb2 100644
--- a/src/features/assets/data/asset-transaction-history.ts
+++ b/src/features/assets/data/asset-transaction-history.ts
@@ -1,23 +1,24 @@
import { AssetId } from '../data/types'
import { createReadOnlyAtomAndTimestamp } from '@/features/common/data'
-import { TransactionResult, TransactionSearchResults } from '@algorandfoundation/algokit-utils/types/indexer'
import { createTransactionsAtom, transactionResultsAtom } from '@/features/transactions/data'
import { atomEffect } from 'jotai-effect'
import { atom } from 'jotai'
import { createLoadableViewModelPageAtom } from '@/features/common/data/lazy-load-pagination'
import { DEFAULT_FETCH_SIZE } from '@/features/common/constants'
import { indexer } from '@/features/common/data/algo-client'
+import { TransactionResult } from '@/features/transactions/data/types'
+import { indexerTransactionToTransactionResult } from '@/features/transactions/mappers/indexer-transaction-mappers'
const getAssetTransactionResults = async (assetId: AssetId, nextPageToken?: string) => {
- const results = (await indexer
+ const results = await indexer
.searchForTransactions()
.assetID(assetId)
.nextToken(nextPageToken ?? '')
.limit(DEFAULT_FETCH_SIZE)
- .do()) as TransactionSearchResults
+ .do()
return {
- transactionResults: results.transactions,
- nextPageToken: results['next-token'],
+ transactionResults: results.transactions.map((txn) => indexerTransactionToTransactionResult(txn)),
+ nextPageToken: results.nextToken,
} as const
}
diff --git a/src/features/assets/data/types.ts b/src/features/assets/data/types.ts
index cf50a747d..adc1baf92 100644
--- a/src/features/assets/data/types.ts
+++ b/src/features/assets/data/types.ts
@@ -1,8 +1,10 @@
-import { AssetResult as IndexerAssetResult } from '@algorandfoundation/algokit-utils/types/indexer'
+import algosdk from 'algosdk'
-export type AssetId = number
+export type AssetId = bigint
-export type AssetResult = Omit
+export type AssetResult = Omit & {
+ params: Omit
+}
type Arc16MetadataProperties = {
traits?: Record
diff --git a/src/features/assets/mappers/asset-summary.ts b/src/features/assets/mappers/asset-summary.ts
index 75e22e49d..ec971190b 100644
--- a/src/features/assets/mappers/asset-summary.ts
+++ b/src/features/assets/mappers/asset-summary.ts
@@ -4,9 +4,9 @@ import { AssetSummary } from '../models'
export const asAssetSummary = (assetResult: AssetResult): AssetSummary => {
return {
id: assetResult.index,
- name: assetResult.params.name ?? assetResult.params['name-b64']?.toString(),
+ name: assetResult.params.name ?? assetResult.params.nameB64?.toString(),
decimals: Number(assetResult.params.decimals),
- unitName: assetResult.params['unit-name'],
+ unitName: assetResult.params.unitName,
creator: assetResult.params.creator,
manager: assetResult.params.manager,
reserve: assetResult.params.reserve,
diff --git a/src/features/assets/mappers/asset.ts b/src/features/assets/mappers/asset.ts
index 62990da2c..167d9998d 100644
--- a/src/features/assets/mappers/asset.ts
+++ b/src/features/assets/mappers/asset.ts
@@ -6,20 +6,20 @@ import { replaceIpfsWithGatewayIfNeeded } from '../utils/replace-ipfs-with-gatew
import Decimal from 'decimal.js'
import { getArc19Url, isArc19Url } from '../utils/arc19'
import { isArc16Properties } from '../utils/arc16'
-import { asJson } from '@/utils/as-json'
+import { asJson, normaliseAlgoSdkData } from '@/utils/as-json'
export const asAsset = (assetResult: AssetResult, metadataResult: AssetMetadataResult): Asset => {
return {
...asAssetSummary(assetResult),
total: assetResult.params.total,
- defaultFrozen: assetResult.params['default-frozen'] ?? false,
+ defaultFrozen: assetResult.params.defaultFrozen ?? false,
url: assetResult.params.url,
type: asType(assetResult),
standardsUsed: asStandardsUsed(assetResult, metadataResult),
traits: asTraits(metadataResult),
media: asMedia(assetResult, metadataResult),
metadata: asMetadata(metadataResult),
- json: asJson(assetResult),
+ json: asJson(normaliseAlgoSdkData(assetResult)),
}
}
@@ -133,7 +133,7 @@ const asType = (assetResult: AssetResult): AssetType => {
return AssetType.Deleted
}
- if (assetResult.params.total === 1 && assetResult.params.decimals === 0) {
+ if (assetResult.params.total === 1n && assetResult.params.decimals === 0) {
return AssetType.PureNonFungible
}
// Check for fractional non-fungible
diff --git a/src/features/assets/models/index.ts b/src/features/assets/models/index.ts
index 8184e3f46..0b99c277e 100644
--- a/src/features/assets/models/index.ts
+++ b/src/features/assets/models/index.ts
@@ -1,5 +1,7 @@
+import { AssetId } from '../data/types'
+
export type AssetSummary = {
- id: number
+ id: AssetId
name?: string
decimals: number
unitName?: string
diff --git a/src/features/assets/pages/asset-page.test.tsx b/src/features/assets/pages/asset-page.test.tsx
index 4ce656c67..a70eca4c5 100644
--- a/src/features/assets/pages/asset-page.test.tsx
+++ b/src/features/assets/pages/asset-page.test.tsx
@@ -35,6 +35,7 @@ import { algod, indexer } from '@/features/common/data/algo-client'
import { setupServer } from 'msw/node'
import { http, HttpResponse } from 'msw'
import { searchTransactionsMock } from '@/tests/setup/mocks'
+import algosdk from 'algosdk'
const server = setupServer()
@@ -117,7 +118,15 @@ describe('asset-page', () => {
vi.mocked(useParams).mockImplementation(() => ({ assetId: assetResult.index.toString() }))
vi.mocked(
indexer.searchForTransactions().assetID(assetResult.index).txType('acfg').address('').addressRole('sender').limit(2).do
- ).mockReturnValue(Promise.resolve({ transactions: [transactionResult] }))
+ ).mockReturnValue(
+ Promise.resolve(
+ new algosdk.indexerModels.TransactionsResponse({
+ transactions: [transactionResult as algosdk.indexerModels.Transaction],
+ nextToken: undefined,
+ currentRound: 1,
+ })
+ )
+ )
server.use(
http.get('https://ipfs.algonode.xyz/ipfs/QmUitxJuPJJrcuAdAiVdEEpuzGmsELGgAvhLd5FiXRShEu', () => {
return HttpResponse.json({
@@ -202,7 +211,15 @@ describe('asset-page', () => {
vi.mocked(useParams).mockImplementation(() => ({ assetId: assetResult.index.toString() }))
vi.mocked(
indexer.searchForTransactions().assetID(assetResult.index).txType('acfg').address('').addressRole('sender').limit(2).do
- ).mockReturnValue(Promise.resolve({ transactions: [transactionResult] }))
+ ).mockReturnValue(
+ Promise.resolve(
+ new algosdk.indexerModels.TransactionsResponse({
+ transactions: [transactionResult as algosdk.indexerModels.Transaction],
+ nextToken: undefined,
+ currentRound: 1,
+ })
+ )
+ )
server.use(
http.get('https://ipfs.algonode.xyz/ipfs/bafkreidt263gwlss4t5kdg6tekxhlxsedb42l5ntvt5mzbv5jywzrzk2ku', () => {
return HttpResponse.json({
@@ -303,7 +320,15 @@ describe('asset-page', () => {
vi.mocked(useParams).mockImplementation(() => ({ assetId: assetResult.index.toString() }))
vi.mocked(
indexer.searchForTransactions().assetID(assetResult.index).txType('acfg').address('').addressRole('sender').limit(2).do
- ).mockReturnValue(Promise.resolve({ transactions: [transactionResult] }))
+ ).mockReturnValue(
+ Promise.resolve(
+ new algosdk.indexerModels.TransactionsResponse({
+ transactions: [transactionResult as algosdk.indexerModels.Transaction],
+ nextToken: undefined,
+ currentRound: 1,
+ })
+ )
+ )
return executeComponentTest(
() => {
@@ -389,7 +414,15 @@ describe('asset-page', () => {
vi.mocked(useParams).mockImplementation(() => ({ assetId: assetResult.index.toString() }))
vi.mocked(
indexer.searchForTransactions().assetID(assetResult.index).txType('acfg').address('').addressRole('sender').limit(2).do
- ).mockReturnValue(Promise.resolve({ transactions: [transactionResult] }))
+ ).mockReturnValue(
+ Promise.resolve(
+ new algosdk.indexerModels.TransactionsResponse({
+ transactions: [transactionResult as algosdk.indexerModels.Transaction],
+ nextToken: undefined,
+ currentRound: 1,
+ })
+ )
+ )
server.use(
http.get('https://ipfs.algonode.xyz/ipfs/bafkreifpfaqwwfyj2zcy76hr6eswkhbqak5bxjzhryeeg7tqnzjgmx5xfi', () => {
return HttpResponse.json({
@@ -474,7 +507,15 @@ describe('asset-page', () => {
vi.mocked(useParams).mockImplementation(() => ({ assetId: assetResult.index.toString() }))
vi.mocked(
indexer.searchForTransactions().assetID(assetResult.index).txType('acfg').address('').addressRole('sender').limit(2).do
- ).mockReturnValue(Promise.resolve({ transactions: [transactionResult] }))
+ ).mockReturnValue(
+ Promise.resolve(
+ new algosdk.indexerModels.TransactionsResponse({
+ transactions: [transactionResult as algosdk.indexerModels.Transaction],
+ nextToken: undefined,
+ currentRound: 1,
+ })
+ )
+ )
server.use(
http.get('https://ipfs.algonode.xyz/ipfs/bafkreihwm3mg4t4bgdvsf6j4epr4v7qwmuhbk6dv3qt3kmtmmm7uagrji4', () => {
return HttpResponse.json({
@@ -591,7 +632,15 @@ describe('asset-page', () => {
vi.mocked(useParams).mockImplementation(() => ({ assetId: assetResult.index.toString() }))
vi.mocked(
indexer.searchForTransactions().assetID(assetResult.index).txType('acfg').address('').addressRole('sender').limit(2).do
- ).mockReturnValue(Promise.resolve({ transactions: [transactionResult] }))
+ ).mockReturnValue(
+ Promise.resolve(
+ new algosdk.indexerModels.TransactionsResponse({
+ transactions: [transactionResult as algosdk.indexerModels.Transaction],
+ nextToken: undefined,
+ currentRound: 1,
+ })
+ )
+ )
server.use(
http.get('https://ipfs.algonode.xyz/ipfs/QmfYFvNon3vfxbwtcetjYc1uZZ1Faw7AsQtSzz45sxXnaj', () => {
return HttpResponse.json({
@@ -724,7 +773,17 @@ describe('asset-page', () => {
vi.mocked(useParams).mockImplementation(() => ({ assetId: assetResult.index.toString() }))
vi.mocked(indexer.searchForTransactions().assetID(assetResult.index).txType('acfg').do).mockImplementation(() =>
- Promise.resolve([createAssetTransactionResult, reconfigureAssetTransactionResult, destroyAssetTransactionResult])
+ Promise.resolve(
+ new algosdk.indexerModels.TransactionsResponse({
+ transactions: [
+ createAssetTransactionResult as algosdk.indexerModels.Transaction,
+ reconfigureAssetTransactionResult as algosdk.indexerModels.Transaction,
+ destroyAssetTransactionResult as algosdk.indexerModels.Transaction,
+ ],
+ nextToken: undefined,
+ currentRound: 1,
+ })
+ )
)
return executeComponentTest(
@@ -810,7 +869,15 @@ describe('asset-page', () => {
vi.mocked(useParams).mockImplementation(() => ({ assetId: assetResult.index.toString() }))
vi.mocked(
indexer.searchForTransactions().assetID(assetResult.index).txType('acfg').address('').addressRole('sender').limit(2).do
- ).mockReturnValue(Promise.resolve({ transactions: [transactionResult] }))
+ ).mockReturnValue(
+ Promise.resolve(
+ new algosdk.indexerModels.TransactionsResponse({
+ transactions: [transactionResult as algosdk.indexerModels.Transaction],
+ nextToken: undefined,
+ currentRound: 1,
+ })
+ )
+ )
server.use(
http.get('https://ipfs.algonode.xyz/ipfs/QmUitxJuPJJrcuAdAiVdEEpuzGmsELGgAvhLd5FiXRShEu', () => {
return HttpResponse.json({
@@ -869,7 +936,15 @@ describe('asset-page', () => {
vi.mocked(useParams).mockImplementation(() => ({ assetId: assetResult.index.toString() }))
vi.mocked(
indexer.searchForTransactions().assetID(assetResult.index).txType('acfg').address('').addressRole('sender').limit(2).do
- ).mockReturnValue(Promise.resolve({ transactions: [transactionResult] }))
+ ).mockReturnValue(
+ Promise.resolve(
+ new algosdk.indexerModels.TransactionsResponse({
+ transactions: [transactionResult as algosdk.indexerModels.Transaction],
+ nextToken: undefined,
+ currentRound: 1,
+ })
+ )
+ )
server.use(
http.head('https://ipfs.algonode.xyz/ipfs/QmbYMPpNdec5Nj8g11JCcaArCSreLWYUcAhPqAK6LjPAtd', () => {
return new Response(null, { status: 200, headers: { 'Content-Type': 'image/png' } })
diff --git a/src/features/assets/pages/asset-page.tsx b/src/features/assets/pages/asset-page.tsx
index 49cf5a93c..503d22c5a 100644
--- a/src/features/assets/pages/asset-page.tsx
+++ b/src/features/assets/pages/asset-page.tsx
@@ -31,7 +31,7 @@ export function AssetPage() {
const { assetId: _assetId } = useRequiredParam(UrlParams.AssetId)
invariant(isInteger(_assetId), assetInvalidIdMessage)
- const assetId = parseInt(_assetId, 10)
+ const assetId = BigInt(_assetId)
const [loadableAsset, refreshAsset, isStale] = useLoadableAsset(assetId)
useTitle()
diff --git a/src/features/blocks/components/block-details.tsx b/src/features/blocks/components/block-details.tsx
index 7afcd5593..53544a31b 100644
--- a/src/features/blocks/components/block-details.tsx
+++ b/src/features/blocks/components/block-details.tsx
@@ -31,7 +31,7 @@ export function BlockDetails({ block }: Props) {
dt: roundLabel,
dd: (
- {block.round}
+ {block.round.toString()}
),
diff --git a/src/features/blocks/components/block-link.tsx b/src/features/blocks/components/block-link.tsx
index dee6e5b16..725912fdd 100644
--- a/src/features/blocks/components/block-link.tsx
+++ b/src/features/blocks/components/block-link.tsx
@@ -3,9 +3,10 @@ import { TemplatedNavLink } from '@/features/routing/components/templated-nav-li
import { Urls } from '@/routes/urls'
import { PropsWithChildren } from 'react'
import { useSelectedNetwork } from '@/features/network/data'
+import { Round } from '../data/types'
type Props = PropsWithChildren<{
- round: number
+ round: Round
className?: string
}>
@@ -18,7 +19,7 @@ export function BlockLink({ round, className, children }: Props) {
urlTemplate={Urls.Network.Explore.Block.ByRound}
urlParams={{ round: round.toString(), networkId: selectedNetwork }}
>
- {children ? children : round}
+ {children ? children : round.toString()}
)
}
diff --git a/src/features/blocks/components/latest-blocks.tsx b/src/features/blocks/components/latest-blocks.tsx
index cb843b946..88d61316a 100644
--- a/src/features/blocks/components/latest-blocks.tsx
+++ b/src/features/blocks/components/latest-blocks.tsx
@@ -24,7 +24,7 @@ export function LatestBlocks({ latestBlocks }: Props) {
-
{block.round}
+ {block.round.toString()}
diff --git a/src/features/blocks/data/block-result.ts b/src/features/blocks/data/block-result.ts
index 6ebbabead..eab7e5d02 100644
--- a/src/features/blocks/data/block-result.ts
+++ b/src/features/blocks/data/block-result.ts
@@ -1,12 +1,14 @@
import { atom, Getter, Setter } from 'jotai'
import { createReadOnlyAtomAndTimestamp, readOnlyAtomCache } from '@/features/common/data'
-import { TransactionResult } from '@algorandfoundation/algokit-utils/types/indexer'
import { transactionResultsAtom } from '@/features/transactions/data'
import { BlockResult, Round } from './types'
import { groupResultsAtom } from '@/features/groups/data'
import { GroupId, GroupResult } from '@/features/groups/data/types'
import { flattenTransactionResult } from '@/features/transactions/utils/flatten-transaction-result'
import { indexer } from '@/features/common/data/algo-client'
+import { TransactionResult } from '@/features/transactions/data/types'
+import { uint8ArrayToBase64 } from '@/utils/uint8-array-to-base64'
+import { indexerTransactionToTransactionResult } from '@/features/transactions/mappers/indexer-transaction-mappers'
export const getBlockAndExtractData = async (round: Round) => {
// We use indexer instead of algod, as algod might not have the full history of blocks
@@ -15,25 +17,29 @@ export const getBlockAndExtractData = async (round: Round) => {
.do()
.then((result) => {
const { transactions, ...block } = result
- const [transactionIds, groupResults] = ((transactions ?? []) as TransactionResult[]).reduce(
- (acc, t) => {
- // Accumulate transactions
- acc[0].push(t.id)
+ const [transactionIds, groupResults] = (transactions ?? [])
+ .map((txn) => indexerTransactionToTransactionResult(txn))
+ .reduce(
+ (acc, t) => {
+ // Accumulate transactions
+ acc[0].push(t.id)
- // Accumulate group results
- accumulateGroupsFromTransaction(acc[1], t, block.round, block.timestamp)
+ // Accumulate group results
+ accumulateGroupsFromTransaction(acc[1], t, block.round, block.timestamp)
- return acc
- },
- [[], new Map()] as [string[], Map]
- )
+ return acc
+ },
+ [[], new Map()] as [string[], Map]
+ )
return [
{
...block,
+ timestamp: block.timestamp,
transactionIds,
+ txnCounter: block.txnCounter !== undefined ? BigInt(block.txnCounter) : undefined,
} as BlockResult,
- (transactions ?? []) as TransactionResult[],
+ (transactions ?? []).map((txn) => indexerTransactionToTransactionResult(txn)),
Array.from(groupResults.values()),
] as const
})
@@ -42,23 +48,23 @@ export const getBlockAndExtractData = async (round: Round) => {
export const accumulateGroupsFromTransaction = (
acc: Map,
transaction: TransactionResult,
- round: number,
+ round: bigint,
roundTime: number
) => {
// Inner transactions can be part of a group, just like regular transactions.
- // In this scenario we add the root transaction id to the group, as inner transactions don't have ids on the network.
flattenTransactionResult(transaction).forEach((txn) => {
- if (txn.group) {
- const group: GroupResult = acc.get(txn.group) ?? {
- id: txn.group,
+ const groupId = txn.group ? uint8ArrayToBase64(txn.group) : undefined
+ if (groupId) {
+ const group: GroupResult = acc.get(groupId) ?? {
+ id: groupId,
round,
timestamp: new Date(roundTime * 1000).toISOString(),
transactionIds: [],
}
- if (!group.transactionIds.find((id) => id === transaction.id)) {
- group.transactionIds.push(transaction.id)
+ if (!group.transactionIds.find((id) => id === txn.id)) {
+ group.transactionIds.push(txn.id)
}
- acc.set(txn.group, group)
+ acc.set(groupId, group)
}
})
}
@@ -72,7 +78,7 @@ export const addStateExtractedFromBlocksAtom = atom(
set(transactionResultsAtom, (prev) => {
const next = new Map(prev)
transactionResultsToAdd.forEach((transactionResult) => {
- if (!next.has(transactionResult.id)) {
+ if (transactionResult.id && !next.has(transactionResult.id)) {
next.set(transactionResult.id, createReadOnlyAtomAndTimestamp(transactionResult))
}
})
diff --git a/src/features/blocks/data/block.ts b/src/features/blocks/data/block.ts
index 7c319619a..05b944939 100644
--- a/src/features/blocks/data/block.ts
+++ b/src/features/blocks/data/block.ts
@@ -9,13 +9,13 @@ import { syncedRoundAtom } from './synced-round'
import { atomEffect } from 'jotai-effect'
const createBlockAtom = (round: Round) => {
- const nextRound = round + 1
+ const nextRound = round + 1n
// This atom packages up the next round number, which may not be available yet.
// We start by initialising as a promise that never resolves (async forever atom).
// We then activate an atomEffect, which sets the next round number based on the round that we've synced up to.
// If we've synced the round, we know that block is available to query.
- const nextRoundWhenAvailableAtom = atom | number>(new Promise(() => {}))
+ const nextRoundWhenAvailableAtom = atom | Round>(new Promise(() => {}))
const setNextRoundWhenAvailableEffect = atomEffect((get, set) => {
// Conditionally subscribe to updates on the syncedRoundAtom
diff --git a/src/features/blocks/data/genesis-hash.ts b/src/features/blocks/data/genesis-hash.ts
index 2fc1349bd..95a77aa68 100644
--- a/src/features/blocks/data/genesis-hash.ts
+++ b/src/features/blocks/data/genesis-hash.ts
@@ -1,4 +1,5 @@
import { algod } from '@/features/common/data/algo-client'
+import { uint8ArrayToBase64 } from '@/utils/uint8-array-to-base64'
import { atomWithDefault, useAtomCallback } from 'jotai/utils'
import { useCallback } from 'react'
@@ -7,7 +8,7 @@ const getGenesisHash = () =>
.versionsCheck()
.do()
.then((result) => {
- return result.genesis_hash_b64 as string
+ return uint8ArrayToBase64(result.genesisHashB64)
})
export const genesisHashAtom = atomWithDefault>(async () => {
diff --git a/src/features/blocks/data/latest-blocks.ts b/src/features/blocks/data/latest-blocks.ts
index 79ead773c..187b76d7c 100644
--- a/src/features/blocks/data/latest-blocks.ts
+++ b/src/features/blocks/data/latest-blocks.ts
@@ -3,7 +3,7 @@ import { isDefined } from '@/utils/is-defined'
import { latestTransactionIdsAtom } from '@/features/transactions/data'
import { atomEffect } from 'jotai-effect'
import { AlgorandSubscriber } from '@algorandfoundation/algokit-subscriber'
-import { ApplicationOnComplete, TransactionResult } from '@algorandfoundation/algokit-utils/types/indexer'
+import { ApplicationOnComplete } from '@algorandfoundation/algokit-utils/types/indexer'
import { BlockResult, Round, SubscriberState, SubscriberStatus, SubscriberStoppedDetails, SubscriberStoppedReason } from './types'
import { assetMetadataResultsAtom } from '@/features/assets/data'
import algosdk from 'algosdk'
@@ -24,6 +24,9 @@ import { createTimestamp, maxBlocksToDisplay } from '@/features/common/data'
import { genesisHashAtom } from './genesis-hash'
import { asError } from '@/utils/error'
import { activeWalletAccountAtom } from '@/features/wallet/data/active-wallet'
+import { TransactionResult } from '@/features/transactions/data/types'
+import { base64ToBytes } from '@/utils/base64-to-bytes'
+import { subscribedTransactionToTransactionResult } from '@/features/transactions/mappers/subscriber-transaction-mappers'
const notStartedSubscriberStatus = { state: SubscriberState.NotStarted } satisfies SubscriberStatus
const startedSubscriberStatus = { state: SubscriberState.Started } satisfies SubscriberStatus
@@ -68,7 +71,7 @@ const subscriberAtom = atom(null, (get, set) => {
waitForBlockWhenAtTip: true,
syncBehaviour: 'sync-oldest-start-now',
watermarkPersistence: {
- get: async () => get(syncedRoundAtom) ?? 0,
+ get: async () => get(syncedRoundAtom) ?? 0n,
set: async (watermark) => {
set(syncedRoundAtom, watermark)
},
@@ -77,18 +80,6 @@ const subscriberAtom = atom(null, (get, set) => {
algod
)
- const reduceAllTransactions = (acc: BalanceChange[], transaction: SubscribedTransaction): BalanceChange[] => {
- if (transaction.balanceChanges && transaction.balanceChanges.length > 0) {
- acc.push(...transaction.balanceChanges)
- }
- transaction.id = ''
- transaction.balanceChanges = undefined
- transaction.filtersMatched = undefined
- transaction.arc28Events = undefined
-
- return (transaction.innerTransactions ?? []).reduce(reduceAllTransactions, acc)
- }
-
subscriber.onPoll(async (result) => {
if (!result.blockMetadata || result.blockMetadata.length < 1) {
return
@@ -107,11 +98,11 @@ const subscriberAtom = atom(null, (get, set) => {
const [blockTransactionIds, transactionResults, groupResults, staleAssetIds, staleAddresses, staleApplicationIds] =
result.subscribedTransactions.reduce(
(acc, t) => {
- if (!t.parentTransactionId && t['confirmed-round'] != null) {
- const round = t['confirmed-round']
- // Remove filtersMatched, balanceChanges and arc28Events, as we don't need to store them in the transaction
- const { filtersMatched: _filtersMatched, balanceChanges: _balanceChanges, arc28Events: _arc28Events, ...transaction } = t
- const balanceChanges = (transaction['inner-txns'] ?? []).reduce(reduceAllTransactions, _balanceChanges ?? [])
+ if (!t.parentTransactionId && t.confirmedRound != null) {
+ const round = t.confirmedRound
+
+ const balanceChanges = extractBalanceChanges(t)
+ const transaction = subscribedTransactionToTransactionResult(t)
// Accumulate transaction ids by round
acc[0].set(round, (acc[0].get(round) ?? []).concat(transaction.id))
@@ -120,15 +111,16 @@ const subscriberAtom = atom(null, (get, set) => {
acc[1].push(transaction)
// Accumulate group results
- accumulateGroupsFromTransaction(acc[2], transaction, round, transaction['round-time'] ?? Math.floor(Date.now() / 1000))
+ accumulateGroupsFromTransaction(acc[2], transaction, round, transaction.roundTime ?? Math.floor(Date.now() / 1000))
// Accumulate stale asset ids
const staleAssetIds = flattenTransactionResult(t)
- .filter((t) => t['tx-type'] === algosdk.TransactionType.acfg)
- .map((t) => t['asset-config-transaction']!['asset-id'])
+ .filter((t) => t.txType === algosdk.TransactionType.acfg)
+ .map((t) => t.assetConfigTransaction!.assetId)
.filter(distinct((x) => x))
.filter(isDefined) // We ignore asset create transactions because they aren't in the atom
- acc[3].push(...staleAssetIds)
+ .filter((x) => x !== 0n) // ALGO is never stale
+ acc[3] = acc[3].concat(staleAssetIds)
// Accumulate stale addresses
const addressesStaleDueToBalanceChanges =
@@ -136,7 +128,7 @@ const subscriberAtom = atom(null, (get, set) => {
?.filter((bc) => {
const isAssetOptIn =
bc.amount === 0n &&
- bc.assetId !== 0 &&
+ bc.assetId !== 0n &&
bc.roles.includes(BalanceChangeRole.Sender) &&
bc.roles.includes(BalanceChangeRole.Receiver)
const isNonZeroAmount = bc.amount !== 0n // Can either be negative (decreased balance) or positive (increased balance)
@@ -152,21 +144,21 @@ const subscriberAtom = atom(null, (get, set) => {
const addressesStaleDueToTransactions = flattenTransactionResult(t)
.filter((t) => {
- const accountIsStaleDueToRekey = t['rekey-to']
+ const accountIsStaleDueToRekey = t.rekeyTo
return accountIsStaleDueToAppChanges(t) || accountIsStaleDueToRekey
})
.map((t) => t.sender)
.filter(distinct((x) => x))
const staleAddresses = Array.from(new Set(addressesStaleDueToBalanceChanges.concat(addressesStaleDueToTransactions)))
- acc[4].push(...staleAddresses)
+ acc[4] = acc[4].concat(staleAddresses)
// Accumulate stale application ids
const staleApplicationIds = flattenTransactionResult(t)
- .filter((t) => t['tx-type'] === algosdk.TransactionType.appl)
- .map((t) => t['application-transaction']?.['application-id'])
+ .filter((t) => t.txType === algosdk.TransactionType.appl)
+ .map((t) => t.applicationTransaction?.applicationId)
.filter(distinct((x) => x))
.filter(isDefined) // We ignore application create transactions because they aren't in the atom
- acc[5].push(...staleApplicationIds)
+ acc[5] = acc[5].concat(staleApplicationIds)
}
return acc
},
@@ -181,63 +173,63 @@ const subscriberAtom = atom(null, (get, set) => {
)
const blockResults = result.blockMetadata.map((b) => {
return {
- ['genesis-hash']: b.genesisHash,
- ['genesis-id']: b.genesisId,
- ['previous-block-hash']: b.previousBlockHash ? b.previousBlockHash : '',
+ genesisHash: base64ToBytes(b.genesisHash),
+ genesisId: b.genesisId,
+ previousBlockHash: base64ToBytes(b.previousBlockHash ?? ''),
...(b.rewards
? {
rewards: {
- ['fee-sink']: b.rewards.feeSink,
- ['rewards-calculation-round']: b.rewards.rewardsCalculationRound,
- ['rewards-level']: b.rewards.rewardsLevel,
- ['rewards-pool']: b.rewards.rewardsPool,
- ['rewards-rate']: b.rewards.rewardsRate,
- ['rewards-residue']: Number(b.rewards.rewardsResidue),
+ feeSink: b.rewards.feeSink,
+ rewardsCalculationRound: b.rewards.rewardsCalculationRound,
+ rewardsLevel: b.rewards.rewardsLevel,
+ rewardsPool: b.rewards.rewardsPool,
+ rewardsRate: b.rewards.rewardsRate,
+ rewardsResidue: b.rewards.rewardsResidue,
},
}
: undefined),
round: b.round,
- seed: b.seed ?? '',
+ seed: base64ToBytes(b.seed ?? ''),
...(b.stateProofTracking && b.stateProofTracking.length > 0
? {
- 'state-proof-tracking': b.stateProofTracking.map((tracking) => ({
- 'next-round': tracking.nextRound,
- 'online-total-weight': tracking.onlineTotalWeight,
+ stateProofTracking: b.stateProofTracking.map((tracking) => ({
+ nextRound: tracking.nextRound,
+ onlineTotalWeight: tracking.onlineTotalWeight,
type: tracking.type,
- 'voters-commitment': tracking.votersCommitment,
+ votersCommitment: tracking.votersCommitment,
})),
}
: undefined),
timestamp: b.timestamp,
- ['transactions-root']: b.transactionsRoot,
- ['transactions-root-sha256']: b.transactionsRootSha256,
- ['txn-counter']: b.txnCounter,
+ transactionsRoot: base64ToBytes(b.transactionsRoot),
+ transactionsRootSha256: base64ToBytes(b.transactionsRootSha256),
+ txnCounter: b.txnCounter,
proposer: b.proposer,
...(b.upgradeState
? {
- ['upgrade-state']: {
- ['current-protocol']: b.upgradeState.currentProtocol,
- ['next-protocol']: b.upgradeState.nextProtocol,
- ['next-protocol-approvals']: b.upgradeState.nextProtocolApprovals ?? 0,
- ['next-protocol-switch-on']: b.upgradeState.nextProtocolSwitchOn ?? 0,
- ['next-protocol-vote-before']: b.upgradeState.nextProtocolVoteBefore ?? 0,
+ upgradeState: {
+ currentProtocol: b.upgradeState.currentProtocol,
+ nextProtocol: b.upgradeState.nextProtocol,
+ nextProtocolApprovals: b.upgradeState.nextProtocolApprovals ?? 0n,
+ nextProtocolSwitchOn: b.upgradeState.nextProtocolSwitchOn ?? 0n,
+ nextProtocolVoteBefore: b.upgradeState.nextProtocolVoteBefore ?? 0n,
},
}
: undefined),
...(b.upgradeVote
? {
- 'upgrade-vote': {
- 'upgrade-approve': b.upgradeVote.upgradeApprove ?? false,
- 'upgrade-delay': b.upgradeVote.upgradeDelay ?? 0,
- 'upgrade-propose': b.upgradeVote.upgradePropose,
+ upgradeVote: {
+ upgradeApprove: b.upgradeVote.upgradeApprove ?? false,
+ upgradeDelay: b.upgradeVote.upgradeDelay ?? 0n,
+ upgradePropose: b.upgradeVote.upgradePropose,
},
}
: undefined),
...(b.participationUpdates
? {
- 'participation-updates': {
- 'absent-participation-accounts': b.participationUpdates.absentParticipationAccounts,
- 'expired-participation-accounts': b.participationUpdates.expiredParticipationAccounts,
+ participationUpdates: {
+ absentParticipationAccounts: b.participationUpdates.absentParticipationAccounts,
+ expiredParticipationAccounts: b.participationUpdates.expiredParticipationAccounts,
},
}
: undefined),
@@ -345,11 +337,16 @@ export const useSubscribeToBlocksEffect = () => {
}
const accountIsStaleDueToAppChanges = (txn: TransactionResult) => {
- if (txn['tx-type'] !== algosdk.TransactionType.appl) {
+ if (txn.txType !== algosdk.TransactionType.appl) {
return false
}
- const appCallTransaction = txn['application-transaction']!
- const isAppCreate = appCallTransaction['on-completion'] === ApplicationOnComplete.noop && !appCallTransaction['application-id']
- const isAppOptIn = appCallTransaction['on-completion'] === ApplicationOnComplete.optin && appCallTransaction['application-id']
+ const appCallTransaction = txn.applicationTransaction!
+ const isAppCreate = appCallTransaction.onCompletion === ApplicationOnComplete.noop && !appCallTransaction.applicationId
+ const isAppOptIn = appCallTransaction.onCompletion === ApplicationOnComplete.optin && appCallTransaction.applicationId
return isAppCreate || isAppOptIn
}
+
+const extractBalanceChanges = (txn: SubscribedTransaction): BalanceChange[] => {
+ const innerTxnsBalanceChanges = (txn.innerTxns ?? []).flatMap((innerTxn) => extractBalanceChanges(innerTxn)) ?? []
+ return (txn.balanceChanges ?? []).concat(innerTxnsBalanceChanges)
+}
diff --git a/src/features/blocks/data/types.ts b/src/features/blocks/data/types.ts
index f64d3379d..07e8434fe 100644
--- a/src/features/blocks/data/types.ts
+++ b/src/features/blocks/data/types.ts
@@ -1,60 +1,60 @@
import { Address } from '@/features/accounts/data/types'
import { GroupResult } from '@/features/groups/data/types'
-import { TransactionResult } from '@algorandfoundation/algokit-utils/types/indexer'
+import { TransactionResult } from '@/features/transactions/data/types'
// Matches the data returned from indexer
export type BlockResult = {
- round: number
+ round: bigint
timestamp: number
- ['genesis-id']: string
- ['genesis-hash']: string
- ['previous-block-hash']?: string
- seed: string
+ genesisId: string
+ genesisHash: Uint8Array
+ previousBlockHash: Uint8Array
+ seed: Uint8Array
rewards?: BlockRewards
- ['txn-counter']: number
- ['transactions-root']: string
- ['transactions-root-sha256']: string
- ['upgrade-state']?: BlockUpgradeState
+ txnCounter: bigint
+ transactionsRoot: Uint8Array
+ transactionsRootSha256: Uint8Array
+ upgradeState?: BlockUpgradeState
transactionIds: string[]
- ['state-proof-tracking']?: BlockStateProofTracking[]
- ['upgrade-vote']?: BlockUpgradeVote
- ['participation-updates']?: ParticipationUpdates
+ stateProofTracking?: StateProofTracking[]
+ upgradeVote?: BlockUpgradeVote
+ participationUpdates?: ParticipationUpdates
proposer?: Address
}
export type BlockRewards = {
- ['fee-sink']: string
- ['rewards-level']: number
- ['rewards-calculation-round']: number
- ['rewards-pool']: string
- ['rewards-residue']: number
- ['rewards-rate']: number
+ feeSink: string
+ rewardsCalculationRound: bigint
+ rewardsLevel: bigint
+ rewardsPool: string
+ rewardsRate: bigint
+ rewardsResidue: bigint
}
export type BlockUpgradeState = {
- ['current-protocol']: string
- ['next-protocol']?: string
- ['next-protocol-approvals']?: number
- ['next-protocol-vote-before']?: number
- ['next-protocol-switch-on']?: number
+ currentProtocol: string
+ nextProtocol?: string
+ nextProtocolApprovals?: bigint
+ nextProtocolSwitchOn?: bigint
+ nextProtocolVoteBefore?: bigint
}
-export type BlockStateProofTracking = {
- ['next-round']?: number
- ['online-total-weight']?: number
+export type StateProofTracking = {
+ nextRound?: bigint
+ onlineTotalWeight?: bigint
type?: number
- ['voters-commitment']?: string
+ votersCommitment?: string | Uint8Array
}
export interface BlockUpgradeVote {
- ['upgrade-approve']?: boolean
- ['upgrade-delay']?: number | bigint
- ['upgrade-propose']?: string
+ upgradeApprove?: boolean
+ upgradeDelay?: bigint
+ upgradePropose?: string
}
export interface ParticipationUpdates {
- ['absent-participation-accounts']?: string[]
- ['expired-participation-accounts']?: string[]
+ absentParticipationAccounts?: string[]
+ expiredParticipationAccounts?: string[]
}
export type BlocksExtract = {
@@ -63,7 +63,7 @@ export type BlocksExtract = {
groupResults: GroupResult[]
}
-export type Round = number
+export type Round = bigint
export enum SubscriberState {
NotStarted = 'NotStarted',
diff --git a/src/features/blocks/mappers/index.ts b/src/features/blocks/mappers/index.ts
index f2844c101..059092217 100644
--- a/src/features/blocks/mappers/index.ts
+++ b/src/features/blocks/mappers/index.ts
@@ -1,10 +1,10 @@
import { Transaction, TransactionSummary } from '@/features/transactions/models'
import { Block, BlockSummary, CommonBlockProperties } from '../models'
-import { BlockResult } from '../data/types'
+import { BlockResult, Round } from '../data/types'
import { asTransactionsSummary } from '@/features/transactions/mappers'
import { AsyncMaybeAtom } from '@/features/common/data/types'
-import { TransactionResult } from '@algorandfoundation/algokit-utils/types/indexer'
-import { asJson } from '@/utils/as-json'
+import { asJson, normaliseAlgoSdkData } from '@/utils/as-json'
+import { TransactionResult } from '@/features/transactions/data/types'
const asCommonBlock = (block: BlockResult, transactions: (Transaction | TransactionSummary)[]): CommonBlockProperties => {
return {
@@ -22,20 +22,22 @@ export const asBlock = (
block: BlockResult,
transactions: Transaction[],
transactionResults: TransactionResult[],
- nextRound: AsyncMaybeAtom
+ nextRound: AsyncMaybeAtom
): Block => {
const { transactionIds: _, ...rest } = block
return {
...asCommonBlock(block, transactions),
- previousRound: block.round > 0 ? block.round - 1 : undefined,
+ previousRound: block.round > 0 ? block.round - 1n : undefined,
nextRound,
transactions,
- json: asJson({
- ...rest,
- ...(!rest['upgrade-vote'] ? { ['upgrade-vote']: { ['upgrade-approve']: false, ['upgrade-delay']: 0 } } : undefined), // Match how indexer handles an undefined upgrade-vote
- transactions: transactionResults,
- }),
+ json: asJson(
+ normaliseAlgoSdkData({
+ ...rest,
+ ...(!rest.upgradeVote ? { upgradeVote: { upgradeApprove: false, upgradeDelay: 0 } } : undefined), // Match how indexer handles an undefined upgrade-vote
+ transactions: transactionResults,
+ })
+ ),
proposer: block.proposer,
}
}
diff --git a/src/features/blocks/models/index.ts b/src/features/blocks/models/index.ts
index 45b7edb70..ca6396a15 100644
--- a/src/features/blocks/models/index.ts
+++ b/src/features/blocks/models/index.ts
@@ -2,16 +2,17 @@ import { Address } from '@/features/accounts/data/types'
import { AsyncMaybeAtom } from '@/features/common/data/types'
import { TransactionsSummary } from '@/features/common/models'
import { Transaction, TransactionSummary } from '@/features/transactions/models'
+import { Round } from '../data/types'
export type CommonBlockProperties = {
- round: number
+ round: Round
timestamp: string
transactionsSummary: TransactionsSummary
}
export type Block = CommonBlockProperties & {
- previousRound?: number
- nextRound: AsyncMaybeAtom
+ previousRound?: Round
+ nextRound: AsyncMaybeAtom
transactions: Transaction[]
json: string
proposer?: Address
diff --git a/src/features/blocks/pages/block-page.test.tsx b/src/features/blocks/pages/block-page.test.tsx
index ab22a9bd5..7d038d975 100644
--- a/src/features/blocks/pages/block-page.test.tsx
+++ b/src/features/blocks/pages/block-page.test.tsx
@@ -25,6 +25,8 @@ import { tableAssertion } from '@/tests/assertions/table-assertion'
import { descriptionListAssertion } from '@/tests/assertions/description-list-assertion'
import { assetResultsAtom } from '@/features/assets/data'
import { indexer } from '@/features/common/data/algo-client'
+import { base64ToBytes } from '@/utils/base64-to-bytes'
+import { uint8ArrayToBase64 } from '@/utils/uint8-array-to-base64'
vi.mock('@/features/common/data/algo-client', async () => {
const original = await vi.importActual('@/features/common/data/algo-client')
@@ -82,13 +84,13 @@ describe('block-page', () => {
describe('when rendering a block that exists', () => {
describe('and has no transactions', () => {
- const block = blockResultMother.blockWithoutTransactions().withRound(12345).withTimestamp(1719284618).build()
+ const block = blockResultMother.blockWithoutTransactions().withRound(12345n).withTimestamp(1719284618).build()
it('should be rendered with the correct data', () => {
vi.mocked(useParams).mockImplementation(() => ({ round: block.round.toString() }))
const myStore = createStore()
myStore.set(blockResultsAtom, new Map([[block.round, createReadOnlyAtomAndTimestamp(block)]]))
- myStore.set(syncedRoundAtom, block.round + 1)
+ myStore.set(syncedRoundAtom, block.round + 1n)
return executeComponentTest(
() => render(, undefined, myStore),
@@ -100,8 +102,8 @@ describe('block-page', () => {
{ term: roundLabel, description: block.round.toString() },
{ term: timestampLabel, description: 'Tue, 25 June 2024 03:03:38' },
{ term: transactionsLabel, description: '0' },
- { term: previousRoundLabel, description: (block.round - 1).toString() },
- { term: nextRoundLabel, description: (block.round + 1).toString() },
+ { term: previousRoundLabel, description: (block.round - 1n).toString() },
+ { term: nextRoundLabel, description: (block.round + 1n).toString() },
],
})
)
@@ -113,13 +115,13 @@ describe('block-page', () => {
})
describe('and has a proposer', () => {
- const block = blockResultMother.blockWithoutTransactions().withRound(1644).withTimestamp(1724943091).build()
+ const block = blockResultMother.blockWithoutTransactions().withRound(1644n).withTimestamp(1724943091).build()
it('should be rendered with the correct data', () => {
vi.mocked(useParams).mockImplementation(() => ({ round: block.round.toString() }))
const myStore = createStore()
myStore.set(blockResultsAtom, new Map([[block.round, createReadOnlyAtomAndTimestamp(block)]]))
- myStore.set(syncedRoundAtom, block.round + 1)
+ myStore.set(syncedRoundAtom, block.round + 1n)
return executeComponentTest(
() => render(, undefined, myStore),
@@ -131,8 +133,8 @@ describe('block-page', () => {
{ term: roundLabel, description: block.round.toString() },
{ term: timestampLabel, description: 'Thu, 29 August 2024 14:51:31' },
{ term: transactionsLabel, description: '0' },
- { term: previousRoundLabel, description: (block.round - 1).toString() },
- { term: nextRoundLabel, description: (block.round + 1).toString() },
+ { term: previousRoundLabel, description: (block.round - 1n).toString() },
+ { term: nextRoundLabel, description: (block.round + 1n).toString() },
{ term: proposerLabel, description: block.proposer ?? '' },
],
})
@@ -146,7 +148,10 @@ describe('block-page', () => {
describe('and has transactions', () => {
const asset = assetResultMother['mainnet-312769']().build()
- const transactionResult1 = transactionResultMother.payment().withGroup('W3pIVuWVJlzmMDGvX8St0W/DPxslnpt6vKV8zoFb6rg=').build()
+ const transactionResult1 = transactionResultMother
+ .payment()
+ .withGroup(base64ToBytes('W3pIVuWVJlzmMDGvX8St0W/DPxslnpt6vKV8zoFb6rg='))
+ .build()
const transactionResult2 = transactionResultMother.transfer(asset).build()
const transactionResults = [transactionResult1, transactionResult2]
const block = blockResultMother.blockWithTransactions(transactionResults).withTimestamp(1719284618).build()
@@ -158,7 +163,7 @@ describe('block-page', () => {
myStore.set(blockResultsAtom, new Map([[block.round, createReadOnlyAtomAndTimestamp(block)]]))
myStore.set(transactionResultsAtom, new Map(transactionResults.map((t) => [t.id, createReadOnlyAtomAndTimestamp(t)])))
myStore.set(assetResultsAtom, new Map([[asset.index, createReadOnlyAtomAndTimestamp(asset)]]))
- myStore.set(syncedRoundAtom, block.round + 1)
+ myStore.set(syncedRoundAtom, block.round + 1n)
return executeComponentTest(
() => render(, undefined, myStore),
@@ -170,8 +175,8 @@ describe('block-page', () => {
{ term: roundLabel, description: block.round.toString() },
{ term: timestampLabel, description: 'Tue, 25 June 2024 03:03:38' },
{ term: transactionsLabel, description: '2Payment=1Asset Transfer=1' },
- { term: previousRoundLabel, description: (block.round - 1).toString() },
- { term: nextRoundLabel, description: (block.round + 1).toString() },
+ { term: previousRoundLabel, description: (block.round - 1n).toString() },
+ { term: nextRoundLabel, description: (block.round + 1n).toString() },
],
})
)
@@ -186,22 +191,22 @@ describe('block-page', () => {
cells: [
'',
ellipseId(transactionResult1.id),
- ellipseId(transactionResult1.group),
+ ellipseId(transactionResult1.group ? uint8ArrayToBase64(transactionResult1.group) : undefined),
ellipseAddress(transactionResult1.sender),
- ellipseAddress(transactionResult1['payment-transaction']!.receiver),
+ ellipseAddress(transactionResult1.paymentTransaction?.receiver),
'Payment',
- (transactionResult1['payment-transaction']!.amount / 1e6).toString(),
+ (Number(transactionResult1.paymentTransaction!.amount) / 1e6).toString(),
],
},
{
cells: [
'',
ellipseId(transactionResult2.id),
- ellipseId(transactionResult2.group),
+ ellipseId(transactionResult2.group ? uint8ArrayToBase64(transactionResult2.group) : undefined),
ellipseAddress(transactionResult2.sender),
- ellipseAddress(transactionResult2['asset-transfer-transaction']!.receiver),
+ ellipseAddress(transactionResult2.assetTransferTransaction?.receiver),
'Asset Transfer',
- `${(transactionResult2['asset-transfer-transaction']!.amount as number) / 1e6}USDt`,
+ `${Number(transactionResult2.assetTransferTransaction!.amount) / 1e6}USDt`,
],
},
],
diff --git a/src/features/blocks/pages/block-page.tsx b/src/features/blocks/pages/block-page.tsx
index d18570873..b970c9c49 100644
--- a/src/features/blocks/pages/block-page.tsx
+++ b/src/features/blocks/pages/block-page.tsx
@@ -30,7 +30,7 @@ export function BlockPage() {
useTitle()
const { round: _round } = useRequiredParam(UrlParams.Round)
invariant(isInteger(_round), blockInvalidRoundMessage)
- const round = parseInt(_round, 10)
+ const round = BigInt(_round)
const loadableBlock = useLoadableBlock(round)
return (
diff --git a/src/features/common/components/platform-provider.tsx b/src/features/common/components/platform-provider.tsx
index 99927e577..6a05090b4 100644
--- a/src/features/common/components/platform-provider.tsx
+++ b/src/features/common/components/platform-provider.tsx
@@ -30,7 +30,7 @@ export function PlatformProvider({ children }: PropsWithChildren) {
// The DataProvider key prop is super important it governs if the provider is reinitialized
const dataProvider = shouldPromptForTokens ? (
- {children}
+ {children}
) : (
diff --git a/src/features/common/components/wallet-provider-inner.tsx b/src/features/common/components/wallet-provider-inner.tsx
index a6b3ddaca..36e8f299a 100644
--- a/src/features/common/components/wallet-provider-inner.tsx
+++ b/src/features/common/components/wallet-provider-inner.tsx
@@ -7,8 +7,8 @@ type Props = PropsWithChildren<{
}>
function SetActiveWalletState({ children }: PropsWithChildren) {
- const { activeAddress, transactionSigner } = useWallet()
- useSetActiveWalletState(activeAddress ?? undefined, transactionSigner)
+ const { isReady, activeAddress, transactionSigner } = useWallet()
+ useSetActiveWalletState(isReady, activeAddress ?? undefined, transactionSigner)
return <>{children}>
}
diff --git a/src/features/common/components/wallet-provider.tsx b/src/features/common/components/wallet-provider.tsx
index e290e0267..a5370914f 100644
--- a/src/features/common/components/wallet-provider.tsx
+++ b/src/features/common/components/wallet-provider.tsx
@@ -1,8 +1,8 @@
import { PropsWithChildren, useMemo } from 'react'
import { WalletProviderInner } from './wallet-provider-inner'
import { defaultKmdWallet, useSelectedKmdWallet } from '@/features/wallet/data/selected-kmd-wallet'
-import { NetworkConfigWithId } from '@/features/network/data/types'
-import { NetworkId, SupportedWallet, WalletId, WalletIdConfig, WalletManager } from '@txnlab/use-wallet-react'
+import { mainnetId, NetworkConfigWithId } from '@/features/network/data/types'
+import { SupportedWallet, WalletId, WalletIdConfig, WalletManager } from '@txnlab/use-wallet-react'
import { DialogBodyProps, useDialogForm } from '../hooks/use-dialog-form'
import { PromptForm } from './prompt-form'
import { loraKmdDevWalletName } from '@/features/fund/utils/kmd'
@@ -89,15 +89,22 @@ export function WalletProvider({ networkConfig, children }: Props) {
const walletManager = useMemo(() => {
return new WalletManager({
wallets: wallets,
- // use-wallet doesn't support custom network, we set it to localnet always to get around this.
- network: NetworkId.LOCALNET,
- algod: {
- baseServer: networkConfig.algod.server,
- port: networkConfig.algod.port,
- token: networkConfig.algod.token,
+ defaultNetwork: networkConfig.id,
+ networks: {
+ [networkConfig.id]: {
+ algod: {
+ baseServer: networkConfig.algod.server,
+ port: networkConfig.algod.port,
+ token: networkConfig.algod.token ?? '',
+ },
+ isTestnet: networkConfig.id !== mainnetId,
+ },
+ },
+ options: {
+ resetNetwork: true,
},
})
- }, [networkConfig.algod.port, networkConfig.algod.server, networkConfig.algod.token, wallets])
+ }, [networkConfig.algod.port, networkConfig.algod.server, networkConfig.algod.token, networkConfig.id, wallets])
return (
// The key prop is super important it governs if the provider is reinitialized
diff --git a/src/features/common/data/algo-client.ts b/src/features/common/data/algo-client.ts
index 29754465d..0b7efa845 100644
--- a/src/features/common/data/algo-client.ts
+++ b/src/features/common/data/algo-client.ts
@@ -18,25 +18,15 @@ const shouldCreateKmdClient = (config: NetworkConfig) => {
const networkConfig = settingsStore.get(networkConfigAtom)
export let indexer = ClientManager.getIndexerClient(networkConfig.indexer)
-indexer.setIntEncoding(algosdk.IntDecoding.MIXED)
-
export let algod = ClientManager.getAlgodClient(networkConfig.algod)
-algod.setIntEncoding(algosdk.IntDecoding.MIXED)
-
export let kmd: algosdk.Kmd | undefined = shouldCreateKmdClient(networkConfig) ? ClientManager.getKmdClient(networkConfig.kmd!) : undefined
-kmd?.setIntEncoding(algosdk.IntDecoding.MIXED)
export let algorandClient = AlgorandClient.fromClients({ algod, indexer, kmd })
export const updateClientConfig = (networkConfig: NetworkConfigWithId) => {
indexer = ClientManager.getIndexerClient(networkConfig.indexer)
- indexer.setIntEncoding(algosdk.IntDecoding.MIXED)
-
algod = ClientManager.getAlgodClient(networkConfig.algod)
- algod.setIntEncoding(algosdk.IntDecoding.MIXED)
-
kmd = shouldCreateKmdClient(networkConfig) ? ClientManager.getKmdClient(networkConfig.kmd!) : undefined
- kmd?.setIntEncoding(algosdk.IntDecoding.MIXED)
algorandClient = AlgorandClient.fromClients({ algod, indexer, kmd })
if (networkConfig.id !== localnetId) {
diff --git a/src/features/common/data/atom-cache.ts b/src/features/common/data/atom-cache.ts
index 00c6b7d23..03b39cda5 100644
--- a/src/features/common/data/atom-cache.ts
+++ b/src/features/common/data/atom-cache.ts
@@ -7,7 +7,7 @@ export const createReadOnlyAtomAndTimestamp = (value: T) => {
return [atom(() => value), createTimestamp()] as const
}
-function getOrCreateValueInCacheAtom(
+function getOrCreateValueInCacheAtom(
keySelector: (...args: Args) => Key,
cacheAtom: PrimitiveAtom