diff --git a/examples/hello-world-abi/contract.spec.ts b/examples/hello-world-abi/contract.spec.ts index ff66d2e6..ff99b409 100644 --- a/examples/hello-world-abi/contract.spec.ts +++ b/examples/hello-world-abi/contract.spec.ts @@ -1,7 +1,8 @@ -import type { gtxn } from '@algorandfoundation/algo-ts' +import { TransactionType } from '@algorandfoundation/algo-ts' +import { TestExecutionContext } from '@algorandfoundation/algo-ts-testing' import { afterEach, describe, expect, it } from 'vitest' +import { invariant } from '../../src/util' import HelloWorldContract from './contract.algo' -import { TestExecutionContext } from '@algorandfoundation/algo-ts-testing' describe('HelloWorldContract', () => { const ctx = new TestExecutionContext() @@ -11,19 +12,17 @@ describe('HelloWorldContract', () => { it('logs the returned value when sayBananas is called', async () => { const contract = ctx.contract.create(HelloWorldContract) const result = contract.sayBananas() - - const application = (ctx.txn.lastActive as gtxn.ApplicationTxn).appId + invariant(ctx.txn.lastActive.type === TransactionType.ApplicationCall, 'Last txn must be app') expect(result).toBe('Bananas') - expect(ctx.exportLogs(application.id, 's')).toStrictEqual([result]) + expect(ctx.exportLogs(ctx.txn.lastActive.appId.id, 's')).toStrictEqual([result]) }) it('logs the returned value when sayHello is called', async () => { const contract = ctx.contract.create(HelloWorldContract) const result = contract.sayHello('John', 'Doe') - - const application = (ctx.txn.lastActive as gtxn.ApplicationTxn).appId + invariant(ctx.txn.lastActive.type === TransactionType.ApplicationCall, 'Last txn must be app') expect(result).toBe('Hello John Doe') - expect(ctx.exportLogs(application.id, 's')).toStrictEqual([result]) + expect(ctx.exportLogs(ctx.txn.lastActive.appId.id, 's')).toStrictEqual([result]) }) }) diff --git a/packages/algo-ts-testing/src/impl/transactions.ts b/packages/algo-ts-testing/src/impl/transactions.ts index 6f7cce02..efacbf22 100644 --- a/packages/algo-ts-testing/src/impl/transactions.ts +++ b/packages/algo-ts-testing/src/impl/transactions.ts @@ -1,7 +1,8 @@ -import { Account, Application, Asset, bytes, Bytes, gtxn, internal, TransactionType, Uint64 } from '@algorandfoundation/algo-ts' +import { Account, Application, Asset, bytes, Bytes, gtxn, internal, TransactionType, uint64, Uint64 } from '@algorandfoundation/algo-ts' +import { OnCompleteActionStr } from '@algorandfoundation/algo-ts/arc4' import { MAX_ITEMS_IN_LOG } from '../constants' import { lazyContext } from '../context-helpers/internal-context' -import { FunctionKeys, Mutable, ObjectKeys } from '../typescript-helpers' +import { Mutable, ObjectKeys } from '../typescript-helpers' import { asBytes, asNumber, getRandomBytes } from '../util' const baseDefaultFields = () => ({ @@ -19,122 +20,165 @@ const baseDefaultFields = () => ({ }) export type TxnFields = Partial>>> -export type TxnFuncs = Pick> - -const getTxnProxy = (fields: TxnFields, funcs: TxnFuncs) => { - return new Proxy( - {} as TTxn, - { - get(_target: TTxn, property: TProperty): TTxn[TProperty] { - return ((Reflect.has(funcs, property) ? funcs : fields) as TTxn)[property] - }, - set(_target: TTxn, property: TProperty, value: TTxn[TProperty]): boolean { - if (Reflect.has(fields, property)) { - ;(fields as TTxn)[property] = value - return true - } - return false - }, - } as ProxyHandler, - ) + +abstract class TransactionBase { + protected constructor(fields: Partial>) { + const baseDefaults = baseDefaultFields() + this.sender = fields.sender ?? baseDefaults.sender + this.fee = fields.fee ?? baseDefaults.fee + this.firstValid = fields.firstValid ?? baseDefaults.firstValid + this.firstValidTime = fields.firstValidTime ?? baseDefaults.firstValidTime + this.lastValid = fields.lastValid ?? baseDefaults.lastValid + this.note = fields.note ?? baseDefaults.note + this.lease = fields.lease ?? baseDefaults.lease + this.typeBytes = fields.typeBytes ?? baseDefaults.typeBytes + this.groupIndex = fields.groupIndex ?? baseDefaults.groupIndex + this.txnId = fields.txnId ?? baseDefaults.txnId + this.rekeyTo = fields.rekeyTo ?? baseDefaults.rekeyTo + } + + readonly sender: Account + readonly fee: uint64 + readonly firstValid: uint64 + readonly firstValidTime: uint64 + readonly lastValid: uint64 + readonly note: bytes + readonly lease: bytes + readonly typeBytes: bytes + readonly groupIndex: uint64 + readonly txnId: bytes + readonly rekeyTo: Account } -export const PaymentTransaction = (txnFields: TxnFields) => { - const defaultFields: gtxn.PaymentTxn = { - ...baseDefaultFields(), - type: TransactionType.Payment, - receiver: Account(), - amount: Uint64(0), - closeRemainderTo: Account(), +export class PaymentTransaction extends TransactionBase implements gtxn.PaymentTxn { + /* @internal */ + static create(fields: TxnFields) { + return new PaymentTransaction(fields) } - const fields = { - ...defaultFields, - ...txnFields, + + private constructor(fields: TxnFields) { + super(fields) + this.receiver = fields.receiver ?? Account() + this.amount = fields.amount ?? Uint64(0) + this.closeRemainderTo = fields.closeRemainderTo ?? Account() } - return getTxnProxy(fields, {} as TxnFuncs) + readonly receiver: Account + readonly amount: uint64 + readonly closeRemainderTo: Account + readonly type: TransactionType.Payment = TransactionType.Payment } -export const KeyRegistrationTransaction = (txnFields: TxnFields) => { - const defaultFields: gtxn.KeyRegistrationTxn = { - ...baseDefaultFields(), - type: TransactionType.KeyRegistration, - voteKey: Bytes(), - selectionKey: Bytes(), - voteFirst: Uint64(0), - voteLast: Uint64(0), - voteKeyDilution: Uint64(0), - nonparticipation: false, - stateProofKey: Bytes(), - } - const fields = { - ...defaultFields, - ...txnFields, - } - return getTxnProxy(fields, {} as TxnFuncs) -} +export class KeyRegistrationTransaction extends TransactionBase implements gtxn.KeyRegistrationTxn { + /* @internal */ + static create(fields: TxnFields) { + return new KeyRegistrationTransaction(fields) + } -export const AssetConfigTransaction = (txnFields: TxnFields) => { - const defaultFields: gtxn.AssetConfigTxn = { - ...baseDefaultFields(), - type: TransactionType.AssetConfig, - configAsset: Asset(), - total: Uint64(0), - decimals: Uint64(0), - defaultFrozen: false, - unitName: Bytes(), - assetName: Bytes(), - url: Bytes(), - metadataHash: Bytes(), - manager: Account(), - reserve: Account(), - freeze: Account(), - clawback: Account(), - createdAsset: Asset(), - } - const fields = { - ...defaultFields, - ...txnFields, - } - return getTxnProxy(fields, {} as TxnFuncs) + private constructor(fields: TxnFields) { + super(fields) + this.voteKey = fields.voteKey ?? Bytes() + this.selectionKey = fields.selectionKey ?? Bytes() + this.voteFirst = fields.voteFirst ?? Uint64(0) + this.voteLast = fields.voteLast ?? Uint64(0) + this.voteKeyDilution = fields.voteKeyDilution ?? Uint64(0) + this.nonparticipation = fields.nonparticipation ?? false + this.stateProofKey = fields.stateProofKey ?? Bytes() + } + + readonly voteKey: bytes + readonly selectionKey: bytes + readonly voteFirst: uint64 + readonly voteLast: uint64 + readonly voteKeyDilution: uint64 + readonly nonparticipation: boolean + readonly stateProofKey: bytes + readonly type: TransactionType.KeyRegistration = TransactionType.KeyRegistration } -export const AssetTransferTransaction = (txnFields: TxnFields) => { - const defaultFields: gtxn.AssetTransferTxn = { - ...baseDefaultFields(), - type: TransactionType.AssetTransfer, - xferAsset: Asset(), - assetAmount: Uint64(0), - assetSender: Account(), - assetReceiver: Account(), - assetCloseTo: Account(), - } - const fields = { - ...defaultFields, - ...txnFields, - } - return getTxnProxy(fields, {} as TxnFuncs) +export class AssetConfigTransaction extends TransactionBase implements gtxn.AssetConfigTxn { + /* @internal */ + static create(fields: TxnFields) { + return new AssetConfigTransaction(fields) + } + + private constructor(fields: TxnFields) { + super(fields) + this.configAsset = fields.configAsset ?? Asset() + this.total = fields.total ?? Uint64(0) + this.decimals = fields.decimals ?? Uint64(0) + this.defaultFrozen = fields.defaultFrozen ?? false + this.unitName = fields.unitName ?? Bytes() + this.assetName = fields.assetName ?? Bytes() + this.url = fields.url ?? Bytes() + this.metadataHash = fields.metadataHash ?? Bytes() + this.manager = fields.manager ?? Account() + this.reserve = fields.reserve ?? Account() + this.freeze = fields.freeze ?? Account() + this.clawback = fields.clawback ?? Account() + this.createdAsset = fields.createdAsset ?? Asset() + } + + readonly configAsset: Asset + readonly total: uint64 + readonly decimals: uint64 + readonly defaultFrozen: boolean + readonly unitName: bytes + readonly assetName: bytes + readonly url: bytes + readonly metadataHash: bytes + readonly manager: Account + readonly reserve: Account + readonly freeze: Account + readonly clawback: Account + readonly createdAsset: Asset + readonly type: TransactionType.AssetConfig = TransactionType.AssetConfig } -export const AssetFreezeTransaction = (txnFields: TxnFields) => { - const defaultFields: gtxn.AssetFreezeTxn = { - ...baseDefaultFields(), - type: TransactionType.AssetFreeze, - freezeAsset: Asset(), - freezeAccount: Account(), - frozen: false, +export class AssetTransferTransaction extends TransactionBase implements gtxn.AssetTransferTxn { + /* @internal */ + static create(fields: TxnFields) { + return new AssetTransferTransaction(fields) } - const fields = { - ...defaultFields, - ...txnFields, + + private constructor(fields: TxnFields) { + super(fields) + this.xferAsset = fields.xferAsset ?? Asset() + this.assetAmount = fields.assetAmount ?? Uint64(0) + this.assetSender = fields.assetSender ?? Account() + this.assetReceiver = fields.assetReceiver ?? Account() + this.assetCloseTo = fields.assetCloseTo ?? Account() } - return getTxnProxy(fields, {} as TxnFuncs) + + readonly xferAsset: Asset + readonly assetAmount: uint64 + readonly assetSender: Account + readonly assetReceiver: Account + readonly assetCloseTo: Account + + readonly type: TransactionType.AssetTransfer = TransactionType.AssetTransfer } -export type TransactionWithLogFunc = { - appLogs: Array - appendLog: (value: internal.primitives.StubBytesCompat) => void +export class AssetFreezeTransaction extends TransactionBase implements gtxn.AssetFreezeTxn { + /* @internal */ + static create(fields: TxnFields) { + return new AssetFreezeTransaction(fields) + } + + private constructor(fields: TxnFields) { + super(fields) + this.freezeAsset = fields.freezeAsset ?? Asset() + this.freezeAccount = fields.freezeAccount ?? Account() + this.frozen = fields.frozen ?? false + } + + readonly freezeAsset: Asset + readonly freezeAccount: Account + readonly frozen: boolean + + readonly type: TransactionType.AssetFreeze = TransactionType.AssetFreeze } + export type ApplicationTransactionFields = TxnFields & Partial<{ appArgs: Array @@ -145,93 +189,122 @@ export type ApplicationTransactionFields = TxnFields & clearStateProgramPages: Array appLogs: Array }> -export const ApplicationTransaction = (txnFields: ApplicationTransactionFields): gtxn.ApplicationTxn & TransactionWithLogFunc => { - const arrayData = { - appArgs: txnFields.appArgs ?? [], - appLogs: txnFields.appLogs ?? [], - accounts: txnFields.accounts ?? [], - assets: txnFields.assets ?? [], - apps: txnFields.apps ?? [], - approvalProgramPages: txnFields.approvalProgramPages ?? (txnFields.approvalProgram ? [txnFields.approvalProgram] : []), - clearStateProgramPages: txnFields.clearStateProgramPages ?? (txnFields.clearStateProgram ? [txnFields.clearStateProgram] : []), - } - const txn: gtxn.ApplicationTxn & TransactionWithLogFunc = { - ...baseDefaultFields(), - type: TransactionType.ApplicationCall, - appId: Application(), - onCompletion: 'NoOp', - globalNumUint: Uint64(0), - globalNumBytes: Uint64(0), - localNumUint: Uint64(0), - localNumBytes: Uint64(0), - extraProgramPages: Uint64(0), - createdApp: Application(), - ...txnFields, - get appLogs() { - return arrayData.appLogs - }, - get approvalProgram() { - return this.approvalProgramPages(0) - }, - get clearStateProgram() { - return this.clearStateProgramPages(0) - }, - get numAppArgs() { - return Uint64(arrayData.appArgs.length) - }, - get numAccounts() { - return Uint64(arrayData.accounts.length) - }, - get numAssets() { - return Uint64(arrayData.assets.length) - }, - get numApps() { - return Uint64(arrayData.apps.length) - }, - get numApprovalProgramPages() { - return Uint64(arrayData.approvalProgramPages.length) - }, - get numClearStateProgramPages() { - return Uint64(arrayData.clearStateProgramPages.length) - }, - get numLogs() { - return Uint64(arrayData.appLogs.length || lazyContext.getApplicationData(this.appId.id).appLogs.length) - }, - get lastLog() { - return arrayData.appLogs.at(-1) ?? lazyContext.getApplicationData(this.appId.id).appLogs.at(-1) ?? Bytes() - }, - appArgs(index: internal.primitives.StubUint64Compat): bytes { - return arrayData.appArgs[asNumber(index)] - }, - accounts(index: internal.primitives.StubUint64Compat): Account { - return arrayData.accounts[asNumber(index)] - }, - assets(index: internal.primitives.StubUint64Compat): Asset { - return arrayData.assets[asNumber(index)] - }, - apps(index: internal.primitives.StubUint64Compat): Application { - return arrayData.apps[asNumber(index)] - }, - approvalProgramPages(index: internal.primitives.StubUint64Compat): bytes { - return arrayData.approvalProgramPages[asNumber(index)] - }, - clearStateProgramPages(index: internal.primitives.StubUint64Compat): bytes { - return arrayData.clearStateProgramPages[asNumber(index)] - }, - logs(index: internal.primitives.StubUint64Compat): bytes { - const i = asNumber(index) - return arrayData.appLogs[i] ?? lazyContext.getApplicationData(this.appId.id).appLogs ?? Bytes() - }, - appendLog(value: internal.primitives.StubBytesCompat): void { - if (arrayData.appLogs.length + 1 > MAX_ITEMS_IN_LOG) { - throw internal.errors.internalError(`Too many log calls in program, up to ${MAX_ITEMS_IN_LOG} is allowed`) - } - arrayData.appLogs.push(asBytes(value)) - }, - } - return txn + +export class ApplicationTransaction extends TransactionBase implements gtxn.ApplicationTxn { + /* @internal */ + static create(fields: ApplicationTransactionFields) { + return new ApplicationTransaction(fields) + } + #appArgs: Array + #accounts: Array + #assets: Array + #apps: Array + #approvalProgramPages: Array + #clearStateProgramPages: Array + #appLogs: Array + + private constructor(fields: ApplicationTransactionFields) { + super(fields) + this.appId = fields.appId ?? Application() + this.onCompletion = fields.onCompletion ?? 'NoOp' + this.globalNumUint = fields.globalNumUint ?? Uint64(0) + this.globalNumBytes = fields.globalNumBytes ?? Uint64(0) + this.localNumUint = fields.localNumUint ?? Uint64(0) + this.localNumBytes = fields.localNumBytes ?? Uint64(0) + this.extraProgramPages = fields.extraProgramPages ?? Uint64(0) + this.createdApp = fields.createdApp ?? Application() + this.#appArgs = fields.appArgs ?? [] + this.#appLogs = fields.appLogs ?? [] + this.#accounts = fields.accounts ?? [] + this.#assets = fields.assets ?? [] + this.#apps = fields.apps ?? [] + this.#approvalProgramPages = fields.approvalProgramPages ?? (fields.approvalProgram ? [fields.approvalProgram] : []) + this.#clearStateProgramPages = fields.clearStateProgramPages ?? (fields.clearStateProgram ? [fields.clearStateProgram] : []) + } + + readonly appId: Application + readonly onCompletion: OnCompleteActionStr + readonly globalNumUint: uint64 + readonly globalNumBytes: uint64 + readonly localNumUint: uint64 + readonly localNumBytes: uint64 + readonly extraProgramPages: uint64 + readonly createdApp: Application + get approvalProgram() { + return this.approvalProgramPages(0) + } + get clearStateProgram() { + return this.clearStateProgramPages(0) + } + get numAppArgs() { + return Uint64(this.#appArgs.length) + } + get numAccounts() { + return Uint64(this.#accounts.length) + } + get numAssets() { + return Uint64(this.#assets.length) + } + get numApps() { + return Uint64(this.#apps.length) + } + get numApprovalProgramPages() { + return Uint64(this.#approvalProgramPages.length) + } + get numClearStateProgramPages() { + return Uint64(this.#clearStateProgramPages.length) + } + get numLogs() { + return Uint64(this.#appLogs.length || lazyContext.getApplicationData(this.appId.id).appLogs.length) + } + get lastLog() { + return this.#appLogs.at(-1) ?? lazyContext.getApplicationData(this.appId.id).appLogs.at(-1) ?? Bytes() + } + appArgs(index: internal.primitives.StubUint64Compat): bytes { + return this.#appArgs[asNumber(index)] + } + accounts(index: internal.primitives.StubUint64Compat): Account { + return this.#accounts[asNumber(index)] + } + assets(index: internal.primitives.StubUint64Compat): Asset { + return this.#assets[asNumber(index)] + } + apps(index: internal.primitives.StubUint64Compat): Application { + return this.#apps[asNumber(index)] + } + approvalProgramPages(index: internal.primitives.StubUint64Compat): bytes { + return this.#approvalProgramPages[asNumber(index)] + } + clearStateProgramPages(index: internal.primitives.StubUint64Compat): bytes { + return this.#clearStateProgramPages[asNumber(index)] + } + logs(index: internal.primitives.StubUint64Compat): bytes { + const i = asNumber(index) + return this.#appLogs[i] ?? lazyContext.getApplicationData(this.appId.id).appLogs ?? Bytes() + } + readonly type: TransactionType.ApplicationCall = TransactionType.ApplicationCall + + /* @internal */ + get appLogs() { + return this.#appLogs + } + /* @internal */ + appendLog(value: internal.primitives.StubBytesCompat): void { + if (this.#appLogs.length + 1 > MAX_ITEMS_IN_LOG) { + throw internal.errors.internalError(`Too many log calls in program, up to ${MAX_ITEMS_IN_LOG} is allowed`) + } + this.#appLogs.push(asBytes(value)) + } } +export type Transaction = + | PaymentTransaction + | KeyRegistrationTransaction + | AssetConfigTransaction + | AssetTransferTransaction + | AssetFreezeTransaction + | ApplicationTransaction + export type AllTransactionFields = TxnFields & TxnFields & TxnFields & diff --git a/packages/algo-ts-testing/src/subcontexts/contract-context.ts b/packages/algo-ts-testing/src/subcontexts/contract-context.ts index d078ea53..a50145b7 100644 --- a/packages/algo-ts-testing/src/subcontexts/contract-context.ts +++ b/packages/algo-ts-testing/src/subcontexts/contract-context.ts @@ -1,20 +1,18 @@ -import { - Account, - Application, - Asset, - BaseContract, - Bytes, - bytes, - Contract, - gtxn, - internal, - TransactionType, -} from '@algorandfoundation/algo-ts' +import { Account, Application, Asset, BaseContract, Bytes, bytes, Contract, internal } from '@algorandfoundation/algo-ts' import { getAbiMetadata, hasAbiMetadata } from '../abi-metadata' import { lazyContext } from '../context-helpers/internal-context' import { AccountCls } from '../impl/account' import { ApplicationCls } from '../impl/application' import { AssetCls } from '../impl/asset' +import { + ApplicationTransaction, + AssetConfigTransaction, + AssetFreezeTransaction, + AssetTransferTransaction, + KeyRegistrationTransaction, + PaymentTransaction, + Transaction, +} from '../impl/transactions' import { getGenericTypeInfo } from '../runtime-helpers' import { DeliberateAny } from '../typescript-helpers' import { extractGenericTypeArgs } from '../util' @@ -67,13 +65,13 @@ const extractStates = (contract: BaseContract): States => { } const extractArraysFromArgs = (args: DeliberateAny[]) => { - const transactions: gtxn.Transaction[] = [] + const transactions: Transaction[] = [] const accounts: Account[] = [] const apps: Application[] = [] const assets: Asset[] = [] for (const arg of args) { - if ((arg as gtxn.Transaction).type in TransactionType) { - transactions.push(arg as gtxn.Transaction) + if (isTransaction(arg)) { + transactions.push(arg) } else if (arg instanceof AccountCls) { accounts.push(arg as Account) } else if (arg instanceof ApplicationCls) { @@ -85,6 +83,17 @@ const extractArraysFromArgs = (args: DeliberateAny[]) => { return { accounts, apps, assets, transactions } } +function isTransaction(obj: unknown): obj is Transaction { + return ( + obj instanceof PaymentTransaction || + obj instanceof KeyRegistrationTransaction || + obj instanceof AssetConfigTransaction || + obj instanceof AssetTransferTransaction || + obj instanceof AssetFreezeTransaction || + obj instanceof ApplicationTransaction + ) +} + export class ContractContext { create(type: IConstructor, ...args: DeliberateAny[]): T { const proxy = new Proxy(type, this.getContractProxyHandler()) diff --git a/packages/algo-ts-testing/src/subcontexts/transaction-context.ts b/packages/algo-ts-testing/src/subcontexts/transaction-context.ts index a61574c2..adcee33c 100644 --- a/packages/algo-ts-testing/src/subcontexts/transaction-context.ts +++ b/packages/algo-ts-testing/src/subcontexts/transaction-context.ts @@ -1,8 +1,9 @@ -import { gtxn, internal, TransactionType, uint64 } from '@algorandfoundation/algo-ts' +import { internal, TransactionType, uint64 } from '@algorandfoundation/algo-ts' import algosdk from 'algosdk' +import { invariant } from '../../../../src/util' import { lazyContext } from '../context-helpers/internal-context' import { DecodedLogs, decodeLogs, LogDecoding } from '../decode-logs' -import { AllTransactionFields, ApplicationTransactionFields, TransactionWithLogFunc } from '../impl/transactions' +import { AllTransactionFields, Transaction } from '../impl/transactions' import { asBigInt, asUint64 } from '../util' function ScopeGenerator(dispose: () => void) { @@ -30,7 +31,7 @@ export class TransactionContext { readonly groups: TransactionGroup[] = [] #activeGroup: TransactionGroup | undefined - createScope(group: gtxn.Transaction[], activeTransactionIndex?: number): ExecutionScope { + createScope(group: Transaction[], activeTransactionIndex?: number): ExecutionScope { const transactionGroup = new TransactionGroup(group, activeTransactionIndex) const previousGroup = this.#activeGroup this.#activeGroup = transactionGroup @@ -50,7 +51,7 @@ export class TransactionContext { } } - ensureScope(group: gtxn.Transaction[], activeTransactionIndex?: number): ExecutionScope { + ensureScope(group: Transaction[], activeTransactionIndex?: number): ExecutionScope { if (!this.#activeGroup || !this.#activeGroup.transactions.length) { return this.createScope(group, activeTransactionIndex) } @@ -75,25 +76,27 @@ export class TransactionContext { return this.groups.at(-1)! } - get lastActive(): gtxn.Transaction { + get lastActive(): Transaction { return this.lastGroup.activeTransaction } + /* internal */ appendLog(value: internal.primitives.StubBytesCompat): void { const activeTransaction = this.activeGroup.activeTransaction if (activeTransaction.type !== TransactionType.ApplicationCall) { throw internal.errors.internalError('Can only add logs to ApplicationCallTransaction!') } - ;(activeTransaction as unknown as TransactionWithLogFunc).appendLog(value) + activeTransaction.appendLog(value) } exportLogs(appId: uint64, ...decoding: T): DecodedLogs { const transaction = this.groups .flatMap((g) => g.transactions) - .find((t) => t.type === TransactionType.ApplicationCall && asBigInt(t.appId.id) === asBigInt(appId)) + .filter((t) => t.type === TransactionType.ApplicationCall) + .find((t) => asBigInt(t.appId.id) === asBigInt(appId)) let logs = [] if (transaction) { - logs = (transaction as unknown as ApplicationTransactionFields).appLogs ?? [] + logs = transaction.appLogs } else { logs = lazyContext.getApplicationData(appId).appLogs } @@ -105,9 +108,9 @@ export class TransactionContext { export class TransactionGroup { activeTransactionIndex: number latestTimestamp: number - transactions: gtxn.Transaction[] + transactions: Transaction[] - constructor(transactions: gtxn.Transaction[], activeTransactionIndex?: number) { + constructor(transactions: Transaction[], activeTransactionIndex?: number) { this.latestTimestamp = Date.now() if (transactions.length > algosdk.AtomicTransactionComposer.MAX_GROUP_SIZE) { internal.errors.internalError( @@ -127,12 +130,8 @@ export class TransactionGroup { if (this.transactions.length === 0) { internal.errors.internalError('No transactions in the group') } - - const appId = (this.activeTransaction as gtxn.ApplicationTxn)?.appId - if (appId) { - return appId.id - } - internal.errors.internalError('No app_id found in the active transaction') + invariant(this.activeTransaction.type === TransactionType.ApplicationCall, 'No app_id found in the active transaction') + return this.activeTransaction.appId } patchActiveTransactionFields(fields: AllTransactionFields) { diff --git a/packages/algo-ts-testing/src/value-generators/txn.ts b/packages/algo-ts-testing/src/value-generators/txn.ts index 4c503c63..82a3aab6 100644 --- a/packages/algo-ts-testing/src/value-generators/txn.ts +++ b/packages/algo-ts-testing/src/value-generators/txn.ts @@ -13,7 +13,7 @@ import { import { asBigInt } from '../util' export class TxnValueGenerator { - applicationCall(fields?: ApplicationTransactionFields): gtxn.ApplicationTxn { + applicationCall(fields?: ApplicationTransactionFields): ApplicationTransaction { const params = fields ?? {} if (params.appId && !lazyContext.ledger.applicationDataMap.has(asBigInt(params.appId.id))) { internal.errors.internalError(`Application ID ${params.appId.id} not found in test context`) @@ -22,26 +22,26 @@ export class TxnValueGenerator { params.appId = lazyContext.any.application() } - return ApplicationTransaction(params) + return ApplicationTransaction.create(params) } - payment(fields?: TxnFields): gtxn.PaymentTxn { - return PaymentTransaction(fields ?? {}) + payment(fields?: TxnFields): PaymentTransaction { + return PaymentTransaction.create(fields ?? {}) } - keyRegistration(fields?: TxnFields): gtxn.KeyRegistrationTxn { - return KeyRegistrationTransaction(fields ?? {}) + keyRegistration(fields?: TxnFields): KeyRegistrationTransaction { + return KeyRegistrationTransaction.create(fields ?? {}) } - assetConfig(fields?: TxnFields): gtxn.AssetConfigTxn { - return AssetConfigTransaction(fields ?? {}) + assetConfig(fields?: TxnFields): AssetConfigTransaction { + return AssetConfigTransaction.create(fields ?? {}) } - assetTransfer(fields?: TxnFields): gtxn.AssetTransferTxn { - return AssetTransferTransaction(fields ?? {}) + assetTransfer(fields?: TxnFields): AssetTransferTransaction { + return AssetTransferTransaction.create(fields ?? {}) } - assetFreeze(fields?: TxnFields): gtxn.AssetFreezeTxn { - return AssetFreezeTransaction(fields ?? {}) + assetFreeze(fields?: TxnFields): AssetFreezeTransaction { + return AssetFreezeTransaction.create(fields ?? {}) } } diff --git a/src/awst_build/type-resolver.ts b/src/awst_build/type-resolver.ts index 301904c8..261b2448 100644 --- a/src/awst_build/type-resolver.ts +++ b/src/awst_build/type-resolver.ts @@ -63,7 +63,7 @@ export class TypeResolver { /* The method getTypeArgumentsForResolvedSignature has not made it into typescript yet, but it has been proposed here: https://github.com/microsoft/TypeScript/issues/59637 and added to the backlog. For now - the method has been patched into the TypeScript 5.5.2 using patch-package + the method has been patched into the TypeScript 5.6.2 using patch-package */ const tps = this.checker.getTypeArgumentsForResolvedSignature(sig) return tps?.map((t) => this.resolveType(t, sourceLocation)) ?? [] diff --git a/tests/primitives/biguint.spec.ts b/tests/primitives/biguint.spec.ts index 0aba3e83..4bea609e 100644 --- a/tests/primitives/biguint.spec.ts +++ b/tests/primitives/biguint.spec.ts @@ -1,7 +1,7 @@ -import { biguint, BigUint, Bytes, internal, Uint64 } from '@algorandfoundation/algo-ts' -import { AppSpec } from '@algorandfoundation/algokit-utils/types/app-spec' +import type { biguint } from '@algorandfoundation/algo-ts' +import { BigUint, Bytes, internal, Uint64 } from '@algorandfoundation/algo-ts' +import type { AppSpec } from '@algorandfoundation/algokit-utils/types/app-spec' import { describe, expect, it } from 'vitest' - import appSpecJson from './artifacts/data/PrimitiveOpsContract.arc32.json' import { getAlgorandAppClient, getAvmResult, getAvmResultRaw } from './avm-invoker'