Skip to content

Commit 969d96a

Browse files
committed
refactor: add tests for method selector
- fix: ApplicationTransaction.appId should return `0` when app `isCreating` - fix: set correct default active transaction index in group when using deferred app calls
1 parent 61ebed4 commit 969d96a

File tree

9 files changed

+478
-25
lines changed

9 files changed

+478
-25
lines changed

src/impl/transactions.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,10 +243,11 @@ export class ApplicationTransaction extends TransactionBase implements gtxn.Appl
243243
#approvalProgramPages: Array<bytes>
244244
#clearStateProgramPages: Array<bytes>
245245
#appLogs: Array<bytes>
246+
#appId: ApplicationType
246247

247248
protected constructor(fields: ApplicationTransactionFields) {
248249
super(fields)
249-
this.appId = fields.appId ?? Application()
250+
this.#appId = fields.appId ?? Application()
250251
this.onCompletion = fields.onCompletion ?? 'NoOp'
251252
this.globalNumUint = fields.globalNumUint ?? Uint64(0)
252253
this.globalNumBytes = fields.globalNumBytes ?? Uint64(0)
@@ -264,7 +265,20 @@ export class ApplicationTransaction extends TransactionBase implements gtxn.Appl
264265
Object.entries(fields.scratchSpace ?? {}).forEach(([k, v]) => this.setScratchSlot(Number(k), v))
265266
}
266267

267-
readonly appId: ApplicationType
268+
get backingAppId(): ApplicationType {
269+
return this.#appId
270+
}
271+
272+
get appId(): ApplicationType {
273+
if (asNumber(this.#appId.id) === 0) {
274+
return this.#appId
275+
}
276+
const appData = lazyContext.getApplicationData(this.#appId.id)
277+
if (appData && appData.isCreating) {
278+
return Application(0)
279+
}
280+
return this.#appId
281+
}
268282
readonly onCompletion: arc4.OnCompleteActionStr
269283
readonly globalNumUint: uint64
270284
readonly globalNumBytes: uint64

src/subcontexts/contract-context.ts

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1-
import type { Account, Application, Asset, bytes, contract, LocalState } from '@algorandfoundation/algorand-typescript'
1+
import type { Account, Application, Asset, contract, LocalState } from '@algorandfoundation/algorand-typescript'
2+
import type { ARC4Encoded } from '@algorandfoundation/algorand-typescript/arc4'
23
import type { AbiMetadata } from '../abi-metadata'
34
import { copyAbiMetadatas, getArc4Selector, getContractAbiMetadata, getContractMethodAbiMetadata, isContractProxy } from '../abi-metadata'
45
import { BytesMap } from '../collections/custom-key-map'
56
import { checkRoutingConditions } from '../context-helpers/context-util'
67
import { lazyContext } from '../context-helpers/internal-context'
7-
import { toBytes, type TypeInfo } from '../encoders'
8+
import { type TypeInfo } from '../encoders'
89
import { CodeError } from '../errors'
910
import { BaseContract, ContractOptionsSymbol } from '../impl/base-contract'
1011
import { Contract } from '../impl/contract'
12+
import { getArc4Encoded, UintNImpl } from '../impl/encoded-types'
1113
import { Bytes } from '../impl/primitives'
1214
import { AccountCls, ApplicationCls, AssetCls } from '../impl/reference'
1315
import { BoxCls, BoxMapCls, BoxRefCls, GlobalStateCls } from '../impl/state'
@@ -22,7 +24,6 @@ import {
2224
} from '../impl/transactions'
2325
import { getGenericTypeInfo } from '../runtime-helpers'
2426
import type { DeliberateAny, IConstructor } from '../typescript-helpers'
25-
2627
type ContractOptionsParameter = Parameters<typeof contract>[0]
2728

2829
type StateTotals = Pick<Application, 'globalNumBytes' | 'globalNumUint' | 'localNumBytes' | 'localNumUint'>
@@ -80,28 +81,43 @@ const extractStates = (contract: BaseContract, contractOptions: ContractOptionsP
8081
return states
8182
}
8283

84+
const getUintN8Impl = (value: number) => new UintNImpl({ name: 'UintN<8>', genericArgs: [{ name: '8' }] }, value)
85+
8386
const extractArraysFromArgs = (app: Application, methodSelector: Uint8Array, args: DeliberateAny[]) => {
8487
const transactions: Transaction[] = []
85-
const accounts: Account[] = []
88+
const accounts: Account[] = [lazyContext.defaultSender]
8689
const apps: Application[] = [app]
8790
const assets: Asset[] = []
88-
const appArgs: bytes[] = []
91+
let appArgs: ARC4Encoded[] = []
8992

9093
for (const arg of args) {
9194
if (isTransaction(arg)) {
9295
transactions.push(arg)
9396
} else if (arg instanceof AccountCls) {
94-
appArgs.push(toBytes(accounts.length))
97+
appArgs.push(getUintN8Impl(accounts.length))
9598
accounts.push(arg as Account)
9699
} else if (arg instanceof ApplicationCls) {
97-
appArgs.push(toBytes(apps.length))
100+
appArgs.push(getUintN8Impl(apps.length))
98101
apps.push(arg as Application)
99102
} else if (arg instanceof AssetCls) {
100-
appArgs.push(toBytes(assets.length))
103+
appArgs.push(getUintN8Impl(assets.length))
101104
assets.push(arg as Asset)
105+
} else {
106+
appArgs.push(arg)
102107
}
103108
}
104-
return { accounts, apps, assets, transactions, appArgs: [Bytes(methodSelector), ...appArgs] }
109+
110+
if (appArgs.length > 15) {
111+
const packed = getArc4Encoded(appArgs.slice(14))
112+
appArgs = [...appArgs.slice(0, 14), packed]
113+
}
114+
return {
115+
accounts,
116+
apps,
117+
assets,
118+
transactions,
119+
appArgs: [Bytes(methodSelector), ...appArgs.filter((a) => a !== undefined).map((a) => a.bytes)],
120+
}
105121
}
106122

107123
function isTransaction(obj: unknown): obj is Transaction {

src/subcontexts/ledger-context.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,9 @@ export class LedgerContext {
5555
* Retrieves an account by address.
5656
* @param address - The account address.
5757
* @returns The account.
58-
* @throws If the account is unknown.
5958
*/
60-
getAccount(address: AccountType): AccountType {
61-
if (this.accountDataMap.has(address)) {
62-
return new AccountCls(address.bytes)
63-
}
64-
throw new InternalError('Unknown account, check correct testing context is active')
59+
getAccount(address: AccountType | StubBytesCompat): AccountType {
60+
return new AccountCls(address instanceof AccountCls ? address.bytes : asBytes(address as StubBytesCompat))
6561
}
6662

6763
/**

src/subcontexts/transaction-context.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,18 @@ export class TransactionContext {
9393
group: Array<Transaction | DeferredAppCall<DeliberateAny[], DeliberateAny>>,
9494
activeTransactionIndex?: number,
9595
): ExecutionScope {
96+
let activeIndex = activeTransactionIndex
9697
const transactions = group.map((t) => (t instanceof DeferredAppCall ? t.txns : [t])).flat()
97-
const transactionGroup = new TransactionGroup(transactions, activeTransactionIndex)
98+
if (activeIndex === undefined) {
99+
const lastAppCall = group
100+
.filter((t) => t instanceof DeferredAppCall)
101+
.at(-1)
102+
?.txns.at(-1)
103+
if (lastAppCall) {
104+
activeIndex = transactions.indexOf(lastAppCall)
105+
}
106+
}
107+
const transactionGroup = new TransactionGroup(transactions, activeIndex)
98108

99109
this.#activeGroup = transactionGroup
100110

@@ -252,11 +262,12 @@ export class TransactionGroup {
252262
* @throws If there are no transactions in the group or the active transaction is not an application call.
253263
*/
254264
get activeApplicationId() {
265+
// this should return the true app_id and not 0 if the app is in the creation phase
255266
if (this.transactions.length === 0) {
256267
throw new InternalError('No transactions in the group')
257268
}
258269
testInvariant(this.activeTransaction.type === TransactionType.ApplicationCall, 'No app_id found in the active transaction')
259-
return this.activeTransaction.appId.id
270+
return (this.activeTransaction as ApplicationTransaction).backingAppId.id
260271
}
261272

262273
/* @internal */

0 commit comments

Comments
 (0)