From 33965c95541c590b2d5a9a3569bd8baac4491892 Mon Sep 17 00:00:00 2001 From: Hoang Dinh Date: Wed, 29 Jan 2025 12:42:02 +1000 Subject: [PATCH 1/4] fix: algod to indexer txn mapping - Add uintName field - Fix base64 encode - Add extraProgramPages field --- src/transform.ts | 11 +-- .../transform-appl-config-txn.spec.ts | 48 +++++++++++++ .../transform-asset-config-txn.spec.ts | 69 +++++++++++++++++++ 3 files changed, 123 insertions(+), 5 deletions(-) create mode 100644 tests/scenarios/transform-appl-config-txn.spec.ts create mode 100644 tests/scenarios/transform-asset-config-txn.spec.ts diff --git a/src/transform.ts b/src/transform.ts index f75c507..d7cf7e9 100644 --- a/src/transform.ts +++ b/src/transform.ts @@ -199,20 +199,20 @@ export function getIndexerTransactionFromAlgodTransaction(t: TransactionInBlock, metadataHash: transaction.assetConfig.assetMetadataHash, ...(transaction.assetConfig.unitName ? { - name: transaction.assetConfig.unitName, - nameB64: encoder.encode(Buffer.from(transaction.assetConfig.unitName).toString('base64')), + unitName: transaction.assetConfig.unitName, + unitNameB64: Buffer.from(transaction.assetConfig.unitName).toString('base64'), } : undefined), ...(transaction.assetConfig.assetName ? { name: transaction.assetConfig.assetName, - nameB64: encoder.encode(Buffer.from(transaction.assetConfig.assetName).toString('base64')), + nameB64: Buffer.from(transaction.assetConfig.assetName).toString('base64'), } : undefined), ...(transaction.assetConfig.assetURL ? { url: transaction.assetConfig.assetURL, - urlB64: encoder.encode(Buffer.from(transaction.assetConfig.assetURL).toString('base64')), + urlB64: Buffer.from(transaction.assetConfig.assetURL).toString('base64'), } : undefined), manager: transaction.assetConfig.manager?.toString(), @@ -248,7 +248,7 @@ export function getIndexerTransactionFromAlgodTransaction(t: TransactionInBlock, ? { assetTransferTransaction: new algosdk.indexerModels.TransactionAssetTransfer({ assetId: transaction.assetTransfer!.assetIndex, - amount: transaction.assetTransfer!.amount, // The amount can be undefined + amount: transaction.assetTransfer!.amount, receiver: transaction.assetTransfer!.receiver.toString(), sender: transaction.assetTransfer!.assetSender ? transaction.assetTransfer!.assetSender.toString() : undefined, closeAmount: assetCloseAmount, @@ -292,6 +292,7 @@ export function getIndexerTransactionFromAlgodTransaction(t: TransactionInBlock, } : undefined), accounts: transaction.applicationCall!.accounts.map((a) => a), + extraProgramPages: transaction.applicationCall!.extraPages, }), } : undefined), diff --git a/tests/scenarios/transform-appl-config-txn.spec.ts b/tests/scenarios/transform-appl-config-txn.spec.ts new file mode 100644 index 0000000..d48247b --- /dev/null +++ b/tests/scenarios/transform-appl-config-txn.spec.ts @@ -0,0 +1,48 @@ +import { AlgorandClient } from '@algorandfoundation/algokit-utils' +import { TransactionType } from 'algosdk' +import { describe, expect, it } from 'vitest' +import { GetSubscribedTransactions } from '../transactions' + +describe('Application config transaction', () => { + const txnId = 'ZCQ5OCGWV253DIN5XVJTFNWVVTOQ6PYOUI3KH7ORQISLN4PEXIGQ' + const roundNumber = 31171197n + const algorand = AlgorandClient.mainNet() + + it('Can have an app create transaction subscribed correctly from indexer', async () => { + const indexerTxns = await GetSubscribedTransactions( + { + filters: { + type: TransactionType.appl, + }, + roundsToSync: 1, + currentRound: roundNumber + 1n, + syncBehaviour: 'catchup-with-indexer', + watermark: roundNumber - 1n, + }, + algorand, + ) + + const txn = indexerTxns.subscribedTransactions.find((txn) => txn.id === txnId) + expect(txn).toBeDefined() + expect(txn!.createdApplicationIndex).toBe(1167143153n) + }) + + it('Can have an app create transaction subscribed correctly from algod', async () => { + const algodTxns = await GetSubscribedTransactions( + { + filters: { + type: TransactionType.appl, + }, + roundsToSync: 1, + currentRound: roundNumber + 1n, + syncBehaviour: 'sync-oldest', + watermark: roundNumber - 1n, + }, + algorand, + ) + + const txn = algodTxns.subscribedTransactions.find((txn) => txn.id === txnId) + expect(txn).toBeDefined() + expect(txn!.createdApplicationIndex).toBe(1167143153n) + }) +}) diff --git a/tests/scenarios/transform-asset-config-txn.spec.ts b/tests/scenarios/transform-asset-config-txn.spec.ts new file mode 100644 index 0000000..ee0f8b9 --- /dev/null +++ b/tests/scenarios/transform-asset-config-txn.spec.ts @@ -0,0 +1,69 @@ +import { AlgorandClient } from '@algorandfoundation/algokit-utils' +import { TransactionType } from 'algosdk' +import { describe, expect, it } from 'vitest' +import { getSubscribedTransactionForDiff } from '../subscribed-transactions' +import { GetSubscribedTransactions } from '../transactions' + +describe('Asset config transaction', () => { + const txnId = 'QHMYTRW27O7KYP6W3SMC3HP55DN5IL2H7Z27T2YDCNFCLXOVFOJQ' + const roundNumber = 44322184n + const algorand = AlgorandClient.mainNet() + + const expectedAssetCreateData = { + assetId: 0n, + params: { + creator: 'ATPVJYGEGP5H6GCZ4T6CG4PK7LH5OMWXHLXZHDPGO7RO6T3EHWTF6UUY6E', + decimals: 6, + total: 2000000000000000n, + clawback: 'ATPVJYGEGP5H6GCZ4T6CG4PK7LH5OMWXHLXZHDPGO7RO6T3EHWTF6UUY6E', + defaultFrozen: false, + freeze: 'ATPVJYGEGP5H6GCZ4T6CG4PK7LH5OMWXHLXZHDPGO7RO6T3EHWTF6UUY6E', + manager: 'ATPVJYGEGP5H6GCZ4T6CG4PK7LH5OMWXHLXZHDPGO7RO6T3EHWTF6UUY6E', + name: 'Fry Node', + nameB64: 'RnJ5IE5vZGU=', + reserve: 'ATPVJYGEGP5H6GCZ4T6CG4PK7LH5OMWXHLXZHDPGO7RO6T3EHWTF6UUY6E', + unitName: 'fNODE', + unitNameB64: 'Zk5PREU=', + url: 'https://frynetworks.com/', + urlB64: 'aHR0cHM6Ly9mcnluZXR3b3Jrcy5jb20v', + }, + } + + it('Can have an asset create with uint name transaction subscribed correctly from indexer', async () => { + const indexerTxns = await GetSubscribedTransactions( + { + filters: { + type: TransactionType.acfg, + }, + roundsToSync: 1, + currentRound: roundNumber + 1n, + syncBehaviour: 'catchup-with-indexer', + watermark: roundNumber - 1n, + }, + algorand, + ) + + const txn = indexerTxns.subscribedTransactions.find((txn) => txn.id === txnId) + expect(txn).toBeDefined() + expect(getSubscribedTransactionForDiff(txn!).assetConfigTransaction).toMatchObject(expectedAssetCreateData) + }) + + it('Can have an asset create with uint name transaction subscribed correctly from algod', async () => { + const algodTxns = await GetSubscribedTransactions( + { + filters: { + type: TransactionType.acfg, + }, + roundsToSync: 1, + currentRound: roundNumber + 1n, + syncBehaviour: 'sync-oldest', + watermark: roundNumber - 1n, + }, + algorand, + ) + + const txn = algodTxns.subscribedTransactions.find((txn) => txn.id === txnId) + expect(txn).toBeDefined() + expect(getSubscribedTransactionForDiff(txn!).assetConfigTransaction).toMatchObject(expectedAssetCreateData) + }) +}) From 916ec5aac009f02e7df90462eb0fb162c5dc046b Mon Sep 17 00:00:00 2001 From: Hoang Dinh Date: Wed, 29 Jan 2025 12:49:31 +1000 Subject: [PATCH 2/4] chore: lint --- src/transform.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/transform.ts b/src/transform.ts index d7cf7e9..4e14465 100644 --- a/src/transform.ts +++ b/src/transform.ts @@ -176,8 +176,6 @@ export function getIndexerTransactionFromAlgodTransaction(t: TransactionInBlock, let parentOffset = 1 const getParentOffset = () => parentOffset++ - const encoder = new TextEncoder() - try { // https://github.com/algorand/indexer/blob/main/api/converter_utils.go#L249 From b90c3546173738a4cf282547b176e1300157a3e8 Mon Sep 17 00:00:00 2001 From: Hoang Dinh Date: Wed, 29 Jan 2025 13:39:55 +1000 Subject: [PATCH 3/4] chore: fix tests --- tests/filterFixture.ts | 18 +++++++++++++++--- tests/scenarios/transform-complex-txn.spec.ts | 1 + 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/tests/filterFixture.ts b/tests/filterFixture.ts index c47cc0b..4c8907d 100644 --- a/tests/filterFixture.ts +++ b/tests/filterFixture.ts @@ -2,7 +2,7 @@ import { algorandFixture } from '@algorandfoundation/algokit-utils/testing' import { AlgorandFixture, AlgorandFixtureConfig } from '@algorandfoundation/algokit-utils/types/testing' import { SendAtomicTransactionComposerResults, SendTransactionResult } from '@algorandfoundation/algokit-utils/types/transaction' import type { Account, Transaction } from 'algosdk' -import algosdk from 'algosdk' +import algosdk, { TransactionType } from 'algosdk' import { expect, vitest } from 'vitest' import { Arc28EventGroup, TransactionFilter, TransactionSubscriptionResult } from '../src/types' import { GetSubscribedTransactions, SendXTransactions } from './transactions' @@ -106,8 +106,20 @@ export function filterFixture(fixtureConfig?: AlgorandFixtureConfig): { expect(algod.subscribedTransactions.length).toBe(results.length) expect(algod.subscribedTransactions.map((s) => s.id)).toEqual(results.map((r) => r.transaction.txID())) - expect(indexer.subscribedTransactions.length).toBe(results.length) - expect(indexer.subscribedTransactions.map((s) => s.id)).toEqual(results.map((r) => r.transaction.txID())) + + // Filter out the proposal payout transaction from the indexer + const subscribedTransactions = indexer.subscribedTransactions.filter( + (t) => + !( + t.fee === 0n && + t.confirmedRound === t.firstValid && + t.confirmedRound === t.lastValid && + t.txType === TransactionType.pay && + t.parentTransactionId === undefined + ), + ) + expect(subscribedTransactions.length).toBe(results.length) + expect(subscribedTransactions.map((s) => s.id)).toEqual(results.map((r) => r.transaction.txID())) return { algod, indexer } } diff --git a/tests/scenarios/transform-complex-txn.spec.ts b/tests/scenarios/transform-complex-txn.spec.ts index 6c52797..ce051de 100644 --- a/tests/scenarios/transform-complex-txn.spec.ts +++ b/tests/scenarios/transform-complex-txn.spec.ts @@ -191,6 +191,7 @@ describe('Complex transaction with many nested inner transactions', () => { "applicationId": 1390675395n, "approvalProgram": "", "clearStateProgram": "", + "extraProgramPages": 0, "foreignApps": [], "foreignAssets": [ 1390638935n, From bd62e9934698613f208b7311e9bc6d0111630378 Mon Sep 17 00:00:00 2001 From: Hoang Dinh Date: Wed, 29 Jan 2025 13:44:40 +1000 Subject: [PATCH 4/4] chore: PR feedback --- ...ppl-config-txn.spec.ts => transform-appl-create-txn.spec.ts} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename tests/scenarios/{transform-appl-config-txn.spec.ts => transform-appl-create-txn.spec.ts} (96%) diff --git a/tests/scenarios/transform-appl-config-txn.spec.ts b/tests/scenarios/transform-appl-create-txn.spec.ts similarity index 96% rename from tests/scenarios/transform-appl-config-txn.spec.ts rename to tests/scenarios/transform-appl-create-txn.spec.ts index d48247b..6dbfd89 100644 --- a/tests/scenarios/transform-appl-config-txn.spec.ts +++ b/tests/scenarios/transform-appl-create-txn.spec.ts @@ -3,7 +3,7 @@ import { TransactionType } from 'algosdk' import { describe, expect, it } from 'vitest' import { GetSubscribedTransactions } from '../transactions' -describe('Application config transaction', () => { +describe('Application create transaction', () => { const txnId = 'ZCQ5OCGWV253DIN5XVJTFNWVVTOQ6PYOUI3KH7ORQISLN4PEXIGQ' const roundNumber = 31171197n const algorand = AlgorandClient.mainNet()