@@ -29,6 +29,9 @@ class TypeInfo:
2929 is_bytes : bool = False
3030 list_inner_is_bytes : bool = False
3131 is_signed_transaction : bool = False
32+ is_box_reference : bool = False
33+ is_locals_reference : bool = False
34+ is_holding_reference : bool = False
3235 needs_datetime : bool = False
3336 imports : set [str ] = field (default_factory = set )
3437
@@ -116,6 +119,12 @@ def _classify(self, schema: ctx.RawSchema) -> str:
116119 return "enum"
117120 if schema .get ("x-algokit-signed-txn" ):
118121 return "signed"
122+ if schema .get ("x-algokit-box-reference" ):
123+ return "box_reference"
124+ if schema .get ("x-algokit-locals-reference" ):
125+ return "locals_reference"
126+ if schema .get ("x-algokit-holding-reference" ):
127+ return "holding_reference"
119128 if schema .get ("type" ) == "object" or schema .get ("properties" ):
120129 return "model"
121130 return "alias"
@@ -142,6 +151,15 @@ def resolve(self, schema: ctx.RawSchema, *, hint: str = "Inline") -> TypeInfo:
142151 if schema .get ("x-algokit-signed-txn" ):
143152 info = TypeInfo (annotation = "SignedTransaction" , model = "SignedTransaction" , is_signed_transaction = True )
144153 return self ._maybe_optional (info , nullable = nullable )
154+ if schema .get ("x-algokit-box-reference" ):
155+ info = TypeInfo (annotation = "BoxReference" , model = "BoxReference" , is_box_reference = True )
156+ return self ._maybe_optional (info , nullable = nullable )
157+ if schema .get ("x-algokit-locals-reference" ):
158+ info = TypeInfo (annotation = "LocalsReference" , model = "LocalsReference" , is_locals_reference = True )
159+ return self ._maybe_optional (info , nullable = nullable )
160+ if schema .get ("x-algokit-holding-reference" ):
161+ info = TypeInfo (annotation = "HoldingReference" , model = "HoldingReference" , is_holding_reference = True )
162+ return self ._maybe_optional (info , nullable = nullable )
145163 if schema_type == "array" :
146164 info = self ._resolve_array (schema , hint = hint )
147165 return self ._maybe_optional (info , nullable = nullable )
@@ -183,6 +201,12 @@ def _type_from_entry(self, entry: SchemaEntry, *, hint: str) -> TypeInfo:
183201 return TypeInfo (annotation = entry .python_name , enum = entry .python_name )
184202 if entry .kind == "signed" :
185203 return TypeInfo (annotation = "SignedTransaction" , model = "SignedTransaction" , is_signed_transaction = True )
204+ if entry .kind == "box_reference" :
205+ return TypeInfo (annotation = "BoxReference" , model = "BoxReference" , is_box_reference = True )
206+ if entry .kind == "locals_reference" :
207+ return TypeInfo (annotation = "LocalsReference" , model = "LocalsReference" , is_locals_reference = True )
208+ if entry .kind == "holding_reference" :
209+ return TypeInfo (annotation = "HoldingReference" , model = "HoldingReference" , is_holding_reference = True )
186210 return self .resolve (entry .schema , hint = hint )
187211
188212 def _resolve_array (self , schema : ctx .RawSchema , * , hint : str ) -> TypeInfo :
@@ -196,6 +220,9 @@ def _resolve_array(self, schema: ctx.RawSchema, *, hint: str) -> TypeInfo:
196220 list_inner_enum = inner .enum ,
197221 list_inner_is_bytes = inner .is_bytes ,
198222 is_signed_transaction = inner .is_signed_transaction ,
223+ is_box_reference = inner .is_box_reference ,
224+ is_locals_reference = inner .is_locals_reference ,
225+ is_holding_reference = inner .is_holding_reference ,
199226 needs_datetime = inner .needs_datetime ,
200227 imports = set (inner .imports ),
201228 )
@@ -212,6 +239,9 @@ def __init__(self, registry: SchemaRegistry, resolver: TypeResolver, sanitizer:
212239 self .resolver = resolver
213240 self .sanitizer = sanitizer
214241 self .uses_signed_transaction = False
242+ self .uses_box_reference = False
243+ self .uses_locals_reference = False
244+ self .uses_holding_reference = False
215245
216246 def _compute_default_value (self , type_info : TypeInfo , prop_schema : ctx .RawSchema ) -> str | None :
217247 """Compute default value for a required field based on its type.
@@ -293,6 +323,9 @@ def build(self) -> tuple[list[ctx.ModelDescriptor], list[ctx.EnumDescriptor], li
293323 models .append (self ._build_model (entry ))
294324 elif entry .kind == "enum" :
295325 enums .append (self ._build_enum (entry ))
326+ elif entry .kind in ("signed" , "box_reference" , "locals_reference" , "holding_reference" ):
327+ # These are imported from algokit_transact, not generated
328+ pass
296329 elif entry .kind == "alias" :
297330 alias_type = self .resolver .resolve (entry .schema ).annotation
298331 alias_imports = self ._collect_alias_imports (alias_type , entry )
@@ -328,6 +361,15 @@ def _build_model(self, entry: SchemaEntry) -> ctx.ModelDescriptor: # noqa: C901
328361 if type_info .is_signed_transaction :
329362 self .uses_signed_transaction = True
330363 imports .add ("from algokit_transact.models.signed_transaction import SignedTransaction" )
364+ if type_info .is_box_reference :
365+ self .uses_box_reference = True
366+ imports .add ("from algokit_transact.models.app_call import BoxReference" )
367+ if type_info .is_locals_reference :
368+ self .uses_locals_reference = True
369+ imports .add ("from algokit_transact.models.app_call import LocalsReference" )
370+ if type_info .is_holding_reference :
371+ self .uses_holding_reference = True
372+ imports .add ("from algokit_transact.models.app_call import HoldingReference" )
331373 annotation = type_info .annotation
332374 if prop_name not in required and "| None" not in annotation :
333375 annotation = f"{ annotation } | None"
@@ -384,13 +426,22 @@ def _build_model(self, entry: SchemaEntry) -> ctx.ModelDescriptor: # noqa: C901
384426 if dep_module != entry .module_name :
385427 imports .add (f"from .{ dep_module } import { dep_entry .python_name } " )
386428 if type_info .list_inner_model :
387- dep_entry = self .registry .entries_by_python_name .get (type_info .list_inner_model )
388- if dep_entry :
389- dep_module = _get_import_module (dep_entry .python_name , dep_entry .module_name )
390- if dep_module != entry .module_name :
391- imports .add (f"from .{ dep_module } import { dep_entry .python_name } " )
429+ # Handle special external types first
392430 if type_info .list_inner_model == "SignedTransaction" :
393431 imports .add ("from algokit_transact.models.signed_transaction import SignedTransaction" )
432+ elif type_info .list_inner_model == "BoxReference" and type_info .is_box_reference :
433+ imports .add ("from algokit_transact.models.app_call import BoxReference" )
434+ elif type_info .list_inner_model == "LocalsReference" and type_info .is_locals_reference :
435+ imports .add ("from algokit_transact.models.app_call import LocalsReference" )
436+ elif type_info .list_inner_model == "HoldingReference" and type_info .is_holding_reference :
437+ imports .add ("from algokit_transact.models.app_call import HoldingReference" )
438+ else :
439+ # Only add local import if not a special external type
440+ dep_entry = self .registry .entries_by_python_name .get (type_info .list_inner_model )
441+ if dep_entry :
442+ dep_module = _get_import_module (dep_entry .python_name , dep_entry .module_name )
443+ if dep_module != entry .module_name :
444+ imports .add (f"from .{ dep_module } import { dep_entry .python_name } " )
394445 if type_info .enum :
395446 dep_entry = self .registry .entries_by_python_name .get (type_info .enum )
396447 if dep_entry :
@@ -483,7 +534,7 @@ def _forward_reference_tokens(self, entry: SchemaEntry, type_info: TypeInfo) ->
483534 def _requires_direct_forward_reference (self , entry : SchemaEntry , type_info : TypeInfo ) -> bool :
484535 if not type_info .model :
485536 return False
486- if type_info .model == "SignedTransaction" :
537+ if type_info .model in ( "SignedTransaction" , "BoxReference" , "LocalsReference" , "HoldingReference" ) :
487538 return False
488539 if type_info .model == entry .python_name :
489540 return True
@@ -551,6 +602,12 @@ def _build_metadata(self, wire_name: str, type_info: TypeInfo, *, required: bool
551602 )
552603 if type_info .is_signed_transaction :
553604 return f'nested("{ alias } ", lambda: SignedTransaction)'
605+ if type_info .is_box_reference :
606+ return f'nested("{ alias } ", lambda: BoxReference)'
607+ if type_info .is_locals_reference :
608+ return f'nested("{ alias } ", lambda: LocalsReference)'
609+ if type_info .is_holding_reference :
610+ return f'nested("{ alias } ", lambda: HoldingReference)'
554611 return f'wire("{ alias } ")'
555612
556613 def _collect_alias_imports (self , annotation : str , entry : SchemaEntry ) -> list [str ]:
@@ -580,6 +637,15 @@ def _collect_alias_imports(self, annotation: str, entry: SchemaEntry) -> list[st
580637 if token == "SignedTransaction" :
581638 imports .add ("from algokit_transact.models.signed_transaction import SignedTransaction" )
582639 continue
640+ if token == "BoxReference" :
641+ imports .add ("from algokit_transact.models.app_call import BoxReference" )
642+ continue
643+ if token == "LocalsReference" :
644+ imports .add ("from algokit_transact.models.app_call import LocalsReference" )
645+ continue
646+ if token == "HoldingReference" :
647+ imports .add ("from algokit_transact.models.app_call import HoldingReference" )
648+ continue
583649 dep_entry = self .registry .entries_by_python_name .get (token )
584650 if dep_entry :
585651 imports .add (f"from .{ dep_entry .module_name } import { dep_entry .python_name } " )
@@ -941,6 +1007,9 @@ def build_client_descriptor(
9411007 default_base_url = base_url ,
9421008 token_header = token_header ,
9431009 uses_signed_transaction = uses_signed_txn ,
1010+ uses_box_reference = model_builder .uses_box_reference ,
1011+ uses_locals_reference = model_builder .uses_locals_reference ,
1012+ uses_holding_reference = model_builder .uses_holding_reference ,
9441013 uses_msgpack = operation_builder .uses_msgpack ,
9451014 include_block_models = operation_builder .uses_block_models ,
9461015 include_ledger_state_delta = operation_builder .uses_ledger_state_delta ,
0 commit comments