@algorandfoundation/algorand-typescript-testing 1.0.0-alpha.11 → 1.0.0-alpha.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/abi-metadata.d.ts CHANGED
@@ -2,15 +2,19 @@ import { BaseContract, Contract } from '@algorandfoundation/algorand-typescript'
2
2
  import { AbiMethodConfig, BareMethodConfig, CreateOptions, OnCompleteActionStr } from '@algorandfoundation/algorand-typescript/arc4';
3
3
  export interface AbiMetadata {
4
4
  methodName: string;
5
- methodSelector: string;
5
+ methodSignature: string | undefined;
6
6
  argTypes: string[];
7
7
  returnType: string;
8
8
  onCreate?: CreateOptions;
9
9
  allowActions?: OnCompleteActionStr[];
10
10
  }
11
+ export declare const isContractProxy: unique symbol;
11
12
  export declare const attachAbiMetadata: (contract: {
12
13
  new (): Contract;
13
14
  }, methodName: string, metadata: AbiMetadata) => void;
15
+ export declare const copyAbiMetadatas: <T extends BaseContract>(sourceContract: T, targetContract: T) => void;
14
16
  export declare const captureMethodConfig: <T extends Contract>(contract: T, methodName: string, config?: AbiMethodConfig<T> | BareMethodConfig) => void;
15
17
  export declare const hasAbiMetadata: <T extends Contract>(contract: T) => boolean;
16
- export declare const getAbiMetadata: <T extends BaseContract>(contract: T, methodName: string) => AbiMetadata;
18
+ export declare const getContractAbiMetadata: <T extends BaseContract>(contract: T) => Record<string, AbiMetadata>;
19
+ export declare const getContractMethodAbiMetadata: <T extends BaseContract>(contract: T, methodName: string) => AbiMetadata;
20
+ export declare const getArc4Signature: (metadata: AbiMetadata) => string;
@@ -0,0 +1,3 @@
1
+ import { uint64 } from '@algorandfoundation/algorand-typescript';
2
+ import { AbiMetadata } from '../abi-metadata';
3
+ export declare const checkRoutingConditions: (appId: uint64, metadata: AbiMetadata) => void;
@@ -1,4 +1,4 @@
1
- import { Account, internal } from '@algorandfoundation/algorand-typescript';
1
+ import { Account, BaseContract, internal } from '@algorandfoundation/algorand-typescript';
2
2
  import { AccountData } from '../impl/account';
3
3
  import { ApplicationData } from '../impl/application';
4
4
  import { AssetData } from '../impl/asset';
@@ -20,7 +20,7 @@ declare class InternalContext {
20
20
  get activeGroup(): TransactionGroup;
21
21
  getAccountData(account: Account): AccountData;
22
22
  getAssetData(id: internal.primitives.StubUint64Compat): AssetData;
23
- getApplicationData(id: internal.primitives.StubUint64Compat): ApplicationData;
23
+ getApplicationData(id: internal.primitives.StubUint64Compat | BaseContract): ApplicationData;
24
24
  }
25
25
  export declare const lazyContext: InternalContext;
26
26
  export {};
@@ -13,6 +13,7 @@ export declare class UintNImpl<N extends BitSize> extends UintN<N> {
13
13
  equals(other: this): boolean;
14
14
  static fromBytesImpl(value: internal.primitives.StubBytesCompat | Uint8Array, typeInfo: string | TypeInfo, prefix?: 'none' | 'log'): UintNImpl<BitSize>;
15
15
  static getMaxBitsLength(typeInfo: TypeInfo): BitSize;
16
+ static getArc4TypeName: (t: TypeInfo) => string;
16
17
  }
17
18
  export declare class UFixedNxMImpl<N extends BitSize, M extends number> extends UFixedNxM<N, M> {
18
19
  private value;
@@ -25,6 +26,7 @@ export declare class UFixedNxMImpl<N extends BitSize, M extends number> extends
25
26
  equals(other: this): boolean;
26
27
  static fromBytesImpl(value: internal.primitives.StubBytesCompat | Uint8Array, typeInfo: string | TypeInfo, prefix?: 'none' | 'log'): UFixedNxM<BitSize, number>;
27
28
  static getMaxBitsLength(typeInfo: TypeInfo): BitSize;
29
+ static getArc4TypeName: (t: TypeInfo) => string;
28
30
  }
29
31
  export declare class ByteImpl extends Byte {
30
32
  private value;
@@ -73,6 +75,7 @@ export declare class StaticArrayImpl<TItem extends ARC4Encoded, TLength extends
73
75
  copy(): StaticArrayImpl<TItem, TLength>;
74
76
  static fromBytesImpl(value: internal.primitives.StubBytesCompat | Uint8Array, typeInfo: string | TypeInfo, prefix?: 'none' | 'log'): StaticArrayImpl<ARC4Encoded, number>;
75
77
  static getMaxBytesLength(typeInfo: TypeInfo): number;
78
+ static getArc4TypeName: (t: TypeInfo) => string;
76
79
  }
77
80
  export declare class AddressImpl extends Address {
78
81
  private typeInfo;
@@ -105,6 +108,7 @@ export declare class DynamicArrayImpl<TItem extends ARC4Encoded> extends Dynamic
105
108
  push(...values: TItem[]): void;
106
109
  pop(): TItem;
107
110
  static fromBytesImpl(value: internal.primitives.StubBytesCompat | Uint8Array, typeInfo: string | TypeInfo, prefix?: 'none' | 'log'): DynamicArrayImpl<ARC4Encoded>;
111
+ static getArc4TypeName: (t: TypeInfo) => string;
108
112
  private encodeWithLength;
109
113
  }
110
114
  export declare class TupleImpl<TTuple extends [ARC4Encoded, ...ARC4Encoded[]]> extends Tuple<TTuple> {
@@ -121,10 +125,12 @@ export declare class TupleImpl<TTuple extends [ARC4Encoded, ...ARC4Encoded[]]> e
121
125
  private get items();
122
126
  static fromBytesImpl(value: internal.primitives.StubBytesCompat | Uint8Array, typeInfo: string | TypeInfo, prefix?: 'none' | 'log'): TupleImpl<[ARC4Encoded, ...ARC4Encoded[]]>;
123
127
  static getMaxBytesLength(typeInfo: TypeInfo): number;
128
+ static getArc4TypeName: (t: TypeInfo) => string;
124
129
  }
125
130
  type StructConstraint = Record<string, ARC4Encoded>;
126
- declare const StructImpl_base: any;
131
+ declare const StructImpl_base: DeliberateAny;
127
132
  export declare class StructImpl<T extends StructConstraint> extends StructImpl_base {
133
+ static [x: string]: any;
128
134
  private uint8ArrayValue?;
129
135
  private typeInfo;
130
136
  genericArgs: Record<string, TypeInfo>;
@@ -133,6 +139,7 @@ export declare class StructImpl<T extends StructConstraint> extends StructImpl_b
133
139
  get items(): T;
134
140
  private decodeAsProperties;
135
141
  static fromBytesImpl(value: internal.primitives.StubBytesCompat | Uint8Array, typeInfo: string | TypeInfo, prefix?: 'none' | 'log'): StructImpl<StructConstraint>;
142
+ static getArc4TypeName: (t: TypeInfo) => string;
136
143
  }
137
144
  export declare class DynamicBytesImpl extends DynamicBytes {
138
145
  private typeInfo;
@@ -145,6 +152,7 @@ export declare class DynamicBytesImpl extends DynamicBytes {
145
152
  get items(): ByteImpl[];
146
153
  setItem(_index: number, _value: ByteImpl): void;
147
154
  static fromBytesImpl(value: internal.primitives.StubBytesCompat | Uint8Array, typeInfo: string | TypeInfo, prefix?: 'none' | 'log'): DynamicBytesImpl;
155
+ static getArc4TypeName: (_t: TypeInfo) => string;
148
156
  }
149
157
  export declare class StaticBytesImpl extends StaticBytes {
150
158
  private value;
@@ -158,8 +166,10 @@ export declare class StaticBytesImpl extends StaticBytes {
158
166
  setItem(_index: number, _value: ByteImpl): void;
159
167
  static fromBytesImpl(value: internal.primitives.StubBytesCompat | Uint8Array, typeInfo: string | TypeInfo, prefix?: 'none' | 'log'): StaticBytesImpl;
160
168
  static getMaxBytesLength(typeInfo: TypeInfo): number;
169
+ static getArc4TypeName: (t: TypeInfo) => string;
161
170
  }
162
171
  export declare function interpretAsArc4Impl<T extends ARC4Encoded>(typeInfoString: string, bytes: internal.primitives.StubBytesCompat, prefix?: 'none' | 'log'): T;
163
172
  export declare const arc4Encoders: Record<string, fromBytes<DeliberateAny>>;
164
173
  export declare const getArc4Encoder: <T>(typeInfo: TypeInfo, encoders?: Record<string, fromBytes<DeliberateAny>>) => fromBytes<T>;
174
+ export declare const getArc4TypeName: (typeInfo: TypeInfo) => string | undefined;
165
175
  export {};
@@ -1,7 +1,7 @@
1
1
  import { arc4, itxn, TransactionType, uint64 } from '@algorandfoundation/algorand-typescript';
2
- import { ApplicationTransaction, AssetConfigTransaction, AssetFreezeTransaction, AssetTransferTransaction, KeyRegistrationTransaction, PaymentTransaction } from './transactions';
3
- import { InnerTxn, InnerTxnFields } from './itxn';
4
2
  import { Mutable } from '../typescript-helpers';
3
+ import { InnerTxn, InnerTxnFields } from './itxn';
4
+ import { ApplicationTransaction, AssetConfigTransaction, AssetFreezeTransaction, AssetTransferTransaction, KeyRegistrationTransaction, PaymentTransaction } from './transactions';
5
5
  export declare class PaymentInnerTxn extends PaymentTransaction implements itxn.PaymentInnerTxn {
6
6
  readonly isItxn?: true;
7
7
  static create(fields: itxn.PaymentFields): PaymentInnerTxn;
@@ -91,7 +91,7 @@ export declare class AssetFreezeTransaction extends TransactionBase implements g
91
91
  readonly typeBytes: bytes;
92
92
  }
93
93
  export type ApplicationTransactionFields = TxnFields<gtxn.ApplicationTxn> & Partial<{
94
- appArgs: Array<bytes>;
94
+ appArgs: Array<unknown>;
95
95
  accounts: Array<Account>;
96
96
  assets: Array<Asset>;
97
97
  apps: Array<Application>;
package/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { internal, Bytes, Uint64, Account, arc4, Ecdsa, op, Base64, TransactionType, Asset, Application, BigUint, BaseContract, Contract } from '@algorandfoundation/algorand-typescript';
2
- import algosdk from 'algosdk';
3
- import { a as asMaybeUint64Cls, b as asUint64, c as asBytes, d as asNumber, M as MAX_BOX_SIZE, t as toBytes, e as asBytesCls, f as asUint8Array, g as conactUint8Arrays, L as LOGIC_DATA_PREFIX, h as asBigInt, i as getObjectReference, j as MIN_TXN_FEE, D as DEFAULT_ACCOUNT_MIN_BALANCE, k as DEFAULT_MAX_TXN_LIFE, Z as ZERO_ADDRESS, l as DEFAULT_ASSET_CREATE_MIN_BALANCE, m as DEFAULT_ASSET_OPT_IN_MIN_BALANCE, n as DEFAULT_GLOBAL_GENESIS_HASH, o as asUint64Cls, p as asMaybeBytesCls, q as asBigUint, B as BITS_IN_BYTE, r as MAX_BYTES_SIZE, s as MAX_UINT8, u as binaryStringToBytes, U as UINT64_SIZE, v as MAX_UINT64, w as Uint64BackedCls, A as ALWAYS_APPROVE_TEAL_PROGRAM, x as BytesBackedCls, y as combineIntoMaxBytePages, z as MAX_ITEMS_IN_LOG, C as ABI_RETURN_VALUE_LOG_PREFIX, E as getRandomBytes, F as getArc4Encoder, G as arc4Encoders, H as getGenericTypeInfo, I as getAbiMetadata, J as iterBigInt, K as getRandomBigInt, N as captureMethodConfig } from './runtime-helpers-DlIX78iw.js';
1
+ import { internal, BaseContract, Bytes, Uint64, Account, arc4, Ecdsa, op, Base64, TransactionType, Asset, Application, BigUint, Contract } from '@algorandfoundation/algorand-typescript';
2
+ import algosdk, { ABIMethod } from 'algosdk';
3
+ import { a as asMaybeUint64Cls, b as asUint64, c as asBytes, t as toBytes, d as asNumber, M as MAX_BOX_SIZE, e as asBytesCls, f as asUint8Array, g as conactUint8Arrays, L as LOGIC_DATA_PREFIX, h as asBigInt, i as getObjectReference, j as MIN_TXN_FEE, D as DEFAULT_ACCOUNT_MIN_BALANCE, k as DEFAULT_MAX_TXN_LIFE, Z as ZERO_ADDRESS, l as DEFAULT_ASSET_CREATE_MIN_BALANCE, m as DEFAULT_ASSET_OPT_IN_MIN_BALANCE, n as DEFAULT_GLOBAL_GENESIS_HASH, o as asUint64Cls, p as asMaybeBytesCls, q as asBigUint, B as BITS_IN_BYTE, r as MAX_BYTES_SIZE, s as MAX_UINT8, u as binaryStringToBytes, U as UINT64_SIZE, v as MAX_UINT64, w as Uint64BackedCls, A as ALWAYS_APPROVE_TEAL_PROGRAM, x as BytesBackedCls, y as combineIntoMaxBytePages, z as MAX_ITEMS_IN_LOG, C as ABI_RETURN_VALUE_LOG_PREFIX, E as getRandomBytes, F as getArc4Encoder, G as arc4Encoders, H as getGenericTypeInfo, I as getArc4Signature, J as isContractProxy, K as getContractMethodAbiMetadata, N as copyAbiMetadatas, O as getContractAbiMetadata, P as iterBigInt, Q as getRandomBigInt, R as captureMethodConfig } from './runtime-helpers-D6TF197l.js';
4
4
  import { ec } from 'elliptic';
5
5
  import { sha256 as sha256$1 } from 'js-sha256';
6
6
  import { sha3_256 as sha3_256$1, keccak256 as keccak256$1 } from 'js-sha3';
@@ -57,7 +57,8 @@ class InternalContext {
57
57
  return data;
58
58
  }
59
59
  getApplicationData(id) {
60
- const data = this.ledger.applicationDataMap.get(id);
60
+ const uint64Id = id instanceof BaseContract ? this.ledger.getApplicationForContract(id).id : internal.primitives.Uint64Cls.fromCompat(id);
61
+ const data = this.ledger.applicationDataMap.get(uint64Id);
61
62
  if (!data) {
62
63
  throw internal.errors.internalError('Unknown application, check correct testing context is active');
63
64
  }
@@ -221,7 +222,7 @@ const AppGlobal = {
221
222
  if (!exists) {
222
223
  return [Bytes(), false];
223
224
  }
224
- return [state.value, exists];
225
+ return [toBytes(state.value), exists];
225
226
  },
226
227
  getExUint64(a, b) {
227
228
  const app = getApp(a);
@@ -263,7 +264,7 @@ const AppLocal = {
263
264
  if (!exists) {
264
265
  return [Bytes(), false];
265
266
  }
266
- return [state.value, exists];
267
+ return [toBytes(state.value), exists];
267
268
  },
268
269
  getExUint64: function (a, b, c) {
269
270
  const app = getApp(b);
@@ -3145,7 +3146,7 @@ class ApplicationTransaction extends TransactionBase {
3145
3146
  return this.#appLogs.at(-1) ?? lazyContext.getApplicationData(this.appId.id).appLogs.at(-1) ?? Bytes();
3146
3147
  }
3147
3148
  appArgs(index) {
3148
- return this.#appArgs[asNumber(index)];
3149
+ return toBytes(this.#appArgs[asNumber(index)]);
3149
3150
  }
3150
3151
  accounts(index) {
3151
3152
  return this.#accounts[asNumber(index)];
@@ -3295,7 +3296,7 @@ class ApplicationInnerTxn extends ApplicationTransaction {
3295
3296
  approvalProgramPages: Array.isArray(approvalProgram) ? approvalProgram : undefined,
3296
3297
  clearStateProgram: Array.isArray(clearStateProgram) ? undefined : clearStateProgram,
3297
3298
  clearStateProgramPages: Array.isArray(clearStateProgram) ? clearStateProgram : undefined,
3298
- appArgs: appArgs?.map((x) => toBytes(x)),
3299
+ appArgs: appArgs?.map((x) => x),
3299
3300
  accounts: accounts?.map((x) => x),
3300
3301
  assets: assets?.map((x) => x),
3301
3302
  apps: apps?.map((x) => x),
@@ -3755,12 +3756,27 @@ function BoxRef(options) {
3755
3756
  return new BoxRefCls(options?.key);
3756
3757
  }
3757
3758
 
3759
+ const checkRoutingConditions$1 = (appId, metadata) => {
3760
+ const appData = lazyContext.getApplicationData(appId);
3761
+ const isCreating = appData.isCreating;
3762
+ if (isCreating && metadata.onCreate === 'disallow') {
3763
+ throw new internal.errors.CodeError('method can not be called while creating');
3764
+ }
3765
+ if (!isCreating && metadata.onCreate === 'require') {
3766
+ throw new internal.errors.CodeError('method can only be called while creating');
3767
+ }
3768
+ const txn = lazyContext.activeGroup.activeTransaction;
3769
+ if (txn instanceof ApplicationTransaction && metadata.allowActions && !metadata.allowActions.includes(txn.onCompletion)) {
3770
+ throw new internal.errors.CodeError(`method can only be called with one of the following on_completion values: ${metadata.allowActions.join(', ')}`);
3771
+ }
3772
+ };
3773
+
3758
3774
  const isUint64GenericType = (typeInfo) => {
3759
3775
  if (!Array.isArray(typeInfo?.genericArgs) || !typeInfo?.genericArgs?.length)
3760
3776
  return false;
3761
3777
  return typeInfo.genericArgs.some((t) => t.name.toLocaleLowerCase() === 'uint64');
3762
3778
  };
3763
- const extractStates = (contract) => {
3779
+ const extractStates = (contract, contractOptions) => {
3764
3780
  const stateTotals = { globalNumBytes: 0, globalNumUint: 0, localNumBytes: 0, localNumUint: 0 };
3765
3781
  const states = {
3766
3782
  globalStates: new BytesMap(),
@@ -3796,34 +3812,36 @@ const extractStates = (contract) => {
3796
3812
  stateTotals.localNumBytes += isLocalState && !isUint64State ? 1 : 0;
3797
3813
  }
3798
3814
  });
3815
+ stateTotals.globalNumUint = contractOptions?.stateTotals?.globalUints ?? stateTotals.globalNumUint;
3816
+ stateTotals.globalNumBytes = contractOptions?.stateTotals?.globalBytes ?? stateTotals.globalNumBytes;
3817
+ stateTotals.localNumUint = contractOptions?.stateTotals?.localUints ?? stateTotals.localNumUint;
3818
+ stateTotals.localNumBytes = contractOptions?.stateTotals?.localBytes ?? stateTotals.localNumBytes;
3799
3819
  return states;
3800
3820
  };
3801
- const extractArraysFromArgs = (app, args) => {
3821
+ const extractArraysFromArgs = (app, methodSelector, args) => {
3802
3822
  const transactions = [];
3803
3823
  const accounts = [];
3804
3824
  const apps = [app];
3805
3825
  const assets = [];
3806
3826
  const appArgs = [];
3807
- // TODO: replace `asUint64Cls(accounts.length).toBytes().asAlgoTs()` with `arc4.Uint8(account.length).toBytes().asAlgoTs()`
3808
3827
  for (const arg of args) {
3809
3828
  if (isTransaction(arg)) {
3810
3829
  transactions.push(arg);
3811
3830
  }
3812
3831
  else if (arg instanceof AccountCls) {
3813
- appArgs.push(asUint64Cls(accounts.length).toBytes().asAlgoTs());
3832
+ appArgs.push(toBytes(accounts.length));
3814
3833
  accounts.push(arg);
3815
3834
  }
3816
3835
  else if (arg instanceof ApplicationCls) {
3817
- appArgs.push(asUint64Cls(apps.length).toBytes().asAlgoTs());
3836
+ appArgs.push(toBytes(apps.length));
3818
3837
  apps.push(arg);
3819
3838
  }
3820
3839
  else if (arg instanceof AssetCls) {
3821
- appArgs.push(asUint64Cls(assets.length).toBytes().asAlgoTs());
3840
+ appArgs.push(toBytes(assets.length));
3822
3841
  assets.push(arg);
3823
3842
  }
3824
3843
  }
3825
- // TODO: use actual method selector in appArgs
3826
- return { accounts, apps, assets, transactions, appArgs: [Bytes('method_selector'), ...appArgs] };
3844
+ return { accounts, apps, assets, transactions, appArgs: [Bytes(methodSelector), ...appArgs] };
3827
3845
  };
3828
3846
  function isTransaction(obj) {
3829
3847
  return (obj instanceof PaymentTransaction ||
@@ -3838,10 +3856,10 @@ class ContractContext {
3838
3856
  const proxy = new Proxy(type, this.getContractProxyHandler(this.isArc4(type)));
3839
3857
  return new proxy(...args);
3840
3858
  }
3841
- static createMethodCallTxns(contract, method, ...args) {
3842
- const abiMetadata = getAbiMetadata(contract, method.name);
3859
+ static createMethodCallTxns(contract, abiMetadata, ...args) {
3843
3860
  const app = lazyContext.ledger.getApplicationForContract(contract);
3844
- const { transactions, ...appCallArgs } = extractArraysFromArgs(app, args);
3861
+ const methodSelector = abiMetadata ? ABIMethod.fromSignature(getArc4Signature(abiMetadata)).getSelector() : new Uint8Array();
3862
+ const { transactions, ...appCallArgs } = extractArraysFromArgs(app, methodSelector, args);
3845
3863
  const appTxn = lazyContext.any.txn.applicationCall({
3846
3864
  appId: app,
3847
3865
  ...appCallArgs,
@@ -3865,8 +3883,8 @@ class ContractContext {
3865
3883
  return this.isArc4(proto);
3866
3884
  }
3867
3885
  getContractProxyHandler(isArc4) {
3868
- const onConstructed = (application, instance) => {
3869
- const states = extractStates(instance);
3886
+ const onConstructed = (application, instance, conrtactOptions) => {
3887
+ const states = extractStates(instance, conrtactOptions);
3870
3888
  const applicationData = lazyContext.ledger.applicationDataMap.getOrFail(application.id);
3871
3889
  applicationData.application = {
3872
3890
  ...applicationData.application,
@@ -3881,23 +3899,33 @@ class ContractContext {
3881
3899
  let t = undefined;
3882
3900
  const application = lazyContext.any.application();
3883
3901
  const txn = lazyContext.any.txn.applicationCall({ appId: application });
3902
+ const appData = lazyContext.ledger.applicationDataMap.getOrFail(application.id);
3903
+ appData.isCreating = true;
3884
3904
  lazyContext.txn.ensureScope([txn]).execute(() => {
3885
3905
  t = new target(...args);
3886
3906
  });
3907
+ appData.isCreating = hasCreateMethods(t);
3887
3908
  const instance = new Proxy(t, {
3888
3909
  get(target, prop, receiver) {
3910
+ if (prop === isContractProxy) {
3911
+ return true;
3912
+ }
3889
3913
  const orig = Reflect.get(target, prop, receiver);
3890
- const abiMetadata = getAbiMetadata(target, prop);
3914
+ const abiMetadata = getContractMethodAbiMetadata(target, prop);
3891
3915
  const isProgramMethod = prop === 'approvalProgram' || prop === 'clearStateProgram';
3892
3916
  const isAbiMethod = isArc4 && abiMetadata;
3893
3917
  if (isAbiMethod || isProgramMethod) {
3894
3918
  return (...args) => {
3895
- const txns = ContractContext.createMethodCallTxns(receiver, orig, ...args);
3919
+ const txns = ContractContext.createMethodCallTxns(receiver, abiMetadata, ...args);
3896
3920
  return lazyContext.txn.ensureScope(txns).execute(() => {
3921
+ if (isAbiMethod) {
3922
+ checkRoutingConditions$1(application.id, abiMetadata);
3923
+ }
3897
3924
  const returnValue = orig.apply(target, args);
3898
3925
  if (!isProgramMethod && isAbiMethod && returnValue !== undefined) {
3899
3926
  txns.at(-1).logArc4ReturnValue(returnValue);
3900
3927
  }
3928
+ appData.isCreating = false;
3901
3929
  return returnValue;
3902
3930
  });
3903
3931
  };
@@ -3905,12 +3933,21 @@ class ContractContext {
3905
3933
  return orig;
3906
3934
  },
3907
3935
  });
3908
- onConstructed(application, instance);
3936
+ onConstructed(application, instance, getContractOptions(t));
3937
+ copyAbiMetadatas(t, instance);
3909
3938
  return instance;
3910
3939
  },
3911
3940
  };
3912
3941
  }
3913
3942
  }
3943
+ const getContractOptions = (contract) => {
3944
+ const contractClass = contract.constructor;
3945
+ return contractClass[internal.ContractOptionsSymbol];
3946
+ };
3947
+ const hasCreateMethods = (contract) => {
3948
+ const metadatas = getContractAbiMetadata(contract);
3949
+ return Object.values(metadatas).some((metadata) => (metadata.onCreate ?? 'disallow') !== 'disallow');
3950
+ };
3914
3951
 
3915
3952
  class LedgerContext {
3916
3953
  appIdIter = iterBigInt(1001n, MAX_UINT64);
@@ -4068,19 +4105,35 @@ function ScopeGenerator(dispose) {
4068
4105
  },
4069
4106
  };
4070
4107
  }
4108
+ const checkRoutingConditions = (appId, metadata) => {
4109
+ const appData = lazyContext.getApplicationData(appId);
4110
+ const isCreating = appData.isCreating;
4111
+ if (isCreating && metadata.onCreate === 'disallow') {
4112
+ throw new internal.errors.CodeError('method can not be called while creating');
4113
+ }
4114
+ if (!isCreating && metadata.onCreate === 'require') {
4115
+ throw new internal.errors.CodeError('method can only be called while creating');
4116
+ }
4117
+ const txn = lazyContext.activeGroup.activeTransaction;
4118
+ if (txn instanceof ApplicationTransaction && metadata.allowActions && !metadata.allowActions.includes(txn.onCompletion)) {
4119
+ throw new internal.errors.CodeError(`method can only be called with one of the following on_completion values: ${metadata.allowActions.join(', ')}`);
4120
+ }
4121
+ };
4071
4122
  class DeferredAppCall {
4072
4123
  appId;
4073
4124
  txns;
4074
4125
  method;
4126
+ abiMetadata;
4075
4127
  args;
4076
- constructor(appId, txns, method, args) {
4128
+ constructor(appId, txns, method, abiMetadata, args) {
4077
4129
  this.appId = appId;
4078
4130
  this.txns = txns;
4079
4131
  this.method = method;
4132
+ this.abiMetadata = abiMetadata;
4080
4133
  this.args = args;
4081
4134
  }
4082
4135
  submit() {
4083
- // TODO: check_routing_conditions
4136
+ checkRoutingConditions(this.appId, this.abiMetadata);
4084
4137
  return this.method(...this.args);
4085
4138
  }
4086
4139
  }
@@ -4139,10 +4192,11 @@ class TransactionContext {
4139
4192
  }
4140
4193
  activeTransaction.appendLog(value);
4141
4194
  }
4142
- deferAppCall(contract, method, ...args) {
4195
+ deferAppCall(contract, method, methodName, ...args) {
4143
4196
  const appId = lazyContext.ledger.getApplicationForContract(contract);
4144
- const txns = ContractContext.createMethodCallTxns(contract, method, ...args);
4145
- return new DeferredAppCall(appId.id, txns, method, args);
4197
+ const abiMetadata = getContractMethodAbiMetadata(contract, methodName);
4198
+ const txns = ContractContext.createMethodCallTxns(contract, abiMetadata, ...args);
4199
+ return new DeferredAppCall(appId.id, txns, method, abiMetadata, args);
4146
4200
  }
4147
4201
  exportLogs(appId, ...decoding) {
4148
4202
  const transaction = this.lastGroup.transactions
@@ -4473,13 +4527,13 @@ class TestExecutionContext {
4473
4527
  #txnContext;
4474
4528
  #valueGenerator;
4475
4529
  #defaultSender;
4476
- constructor() {
4530
+ constructor(defaultSenderAddress) {
4477
4531
  internal.ctxMgr.instance = this;
4478
4532
  this.#contractContext = new ContractContext();
4479
4533
  this.#ledgerContext = new LedgerContext();
4480
4534
  this.#txnContext = new TransactionContext();
4481
4535
  this.#valueGenerator = new ValueGenerator();
4482
- this.#defaultSender = Account(Bytes.fromBase32(algosdk.generateAccount().addr));
4536
+ this.#defaultSender = Account(defaultSenderAddress ?? Bytes.fromBase32(algosdk.generateAccount().addr));
4483
4537
  }
4484
4538
  account(address) {
4485
4539
  return new AccountCls(address);