1- import { Account , Application , Asset , BaseContract , Bytes , bytes , Contract , LocalState } from '@algorandfoundation/algorand-typescript'
1+ import {
2+ Account ,
3+ Application ,
4+ Asset ,
5+ BaseContract ,
6+ Bytes ,
7+ bytes ,
8+ Contract ,
9+ contract ,
10+ LocalState ,
11+ } from '@algorandfoundation/algorand-typescript'
212import { ABIMethod } from 'algosdk'
3- import { AbiMetadata , getAbiMetadata , getArc4Signature } from '../abi-metadata'
13+ import {
14+ AbiMetadata ,
15+ copyAbiMetadatas ,
16+ getArc4Signature ,
17+ getContractAbiMetadata ,
18+ getContractMethodAbiMetadata ,
19+ isContractProxy ,
20+ } from '../abi-metadata'
421import { BytesMap } from '../collections/custom-key-map'
22+ import { checkRoutingConditions } from '../context-helpers/context-util'
523import { lazyContext } from '../context-helpers/internal-context'
624import type { TypeInfo } from '../encoders'
725import { AccountCls } from '../impl/account'
@@ -21,6 +39,8 @@ import { getGenericTypeInfo } from '../runtime-helpers'
2139import { DeliberateAny , IConstructor } from '../typescript-helpers'
2240import { toBytes } from '../util'
2341
42+ type ContractOptionsParameter = Parameters < typeof contract > [ 0 ]
43+
2444type StateTotals = Pick < Application , 'globalNumBytes' | 'globalNumUint' | 'localNumBytes' | 'localNumUint' >
2545
2646interface States {
@@ -34,7 +54,7 @@ const isUint64GenericType = (typeInfo: TypeInfo | undefined) => {
3454 return typeInfo . genericArgs . some ( ( t ) => t . name . toLocaleLowerCase ( ) === 'uint64' )
3555}
3656
37- const extractStates = ( contract : BaseContract ) : States => {
57+ const extractStates = ( contract : BaseContract , contractOptions : ContractOptionsParameter | undefined ) : States => {
3858 const stateTotals = { globalNumBytes : 0 , globalNumUint : 0 , localNumBytes : 0 , localNumUint : 0 }
3959 const states = {
4060 globalStates : new BytesMap < GlobalStateCls < unknown > > ( ) ,
@@ -67,6 +87,12 @@ const extractStates = (contract: BaseContract): States => {
6787 stateTotals . localNumBytes += isLocalState && ! isUint64State ? 1 : 0
6888 }
6989 } )
90+
91+ stateTotals . globalNumUint = contractOptions ?. stateTotals ?. globalUints ?? stateTotals . globalNumUint
92+ stateTotals . globalNumBytes = contractOptions ?. stateTotals ?. globalBytes ?? stateTotals . globalNumBytes
93+ stateTotals . localNumUint = contractOptions ?. stateTotals ?. localUints ?? stateTotals . localNumUint
94+ stateTotals . localNumBytes = contractOptions ?. stateTotals ?. localBytes ?? stateTotals . localNumBytes
95+
7096 return states
7197}
7298
@@ -142,8 +168,8 @@ export class ContractContext {
142168 }
143169
144170 private getContractProxyHandler < T extends BaseContract > ( isArc4 : boolean ) : ProxyHandler < IConstructor < T > > {
145- const onConstructed = ( application : Application , instance : T ) => {
146- const states = extractStates ( instance )
171+ const onConstructed = ( application : Application , instance : T , conrtactOptions : ContractOptionsParameter | undefined ) => {
172+ const states = extractStates ( instance , conrtactOptions )
147173
148174 const applicationData = lazyContext . ledger . applicationDataMap . getOrFail ( application . id )
149175 applicationData . application = {
@@ -159,23 +185,33 @@ export class ContractContext {
159185 let t : T | undefined = undefined
160186 const application = lazyContext . any . application ( )
161187 const txn = lazyContext . any . txn . applicationCall ( { appId : application } )
188+ const appData = lazyContext . ledger . applicationDataMap . getOrFail ( application . id )
189+ appData . isCreating = true
162190 lazyContext . txn . ensureScope ( [ txn ] ) . execute ( ( ) => {
163191 t = new target ( ...args )
164192 } )
193+ appData . isCreating = hasCreateMethods ( t ! )
165194 const instance = new Proxy ( t ! , {
166195 get ( target , prop , receiver ) {
196+ if ( prop === isContractProxy ) {
197+ return true
198+ }
167199 const orig = Reflect . get ( target , prop , receiver )
168- const abiMetadata = getAbiMetadata ( target , prop as string )
200+ const abiMetadata = getContractMethodAbiMetadata ( target , prop as string )
169201 const isProgramMethod = prop === 'approvalProgram' || prop === 'clearStateProgram'
170202 const isAbiMethod = isArc4 && abiMetadata
171203 if ( isAbiMethod || isProgramMethod ) {
172204 return ( ...args : DeliberateAny [ ] ) : DeliberateAny => {
173205 const txns = ContractContext . createMethodCallTxns ( receiver , abiMetadata , ...args )
174206 return lazyContext . txn . ensureScope ( txns ) . execute ( ( ) => {
207+ if ( isAbiMethod ) {
208+ checkRoutingConditions ( application . id , abiMetadata )
209+ }
175210 const returnValue = ( orig as DeliberateAny ) . apply ( target , args )
176211 if ( ! isProgramMethod && isAbiMethod && returnValue !== undefined ) {
177212 ; ( txns . at ( - 1 ) as ApplicationTransaction ) . logArc4ReturnValue ( returnValue )
178213 }
214+ appData . isCreating = false
179215 return returnValue
180216 } )
181217 }
@@ -184,10 +220,18 @@ export class ContractContext {
184220 } ,
185221 } )
186222
187- onConstructed ( application , instance )
223+ onConstructed ( application , instance , ( t ! . constructor as DeliberateAny ) . contractOptions )
224+
225+ copyAbiMetadatas ( t ! , instance )
188226
189227 return instance
190228 } ,
191229 }
192230 }
193231}
232+
233+ // TODO: add test_contract.py
234+ const hasCreateMethods = ( contract : BaseContract ) => {
235+ const metadatas = getContractAbiMetadata ( contract )
236+ return Object . values ( metadatas ) . some ( ( metadata ) => ( metadata . onCreate ?? 'disallow' ) !== 'disallow' )
237+ }
0 commit comments