algokit-utils 2.4.0b1__py3-none-any.whl → 3.0.0b2__py3-none-any.whl

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.

Potentially problematic release.


This version of algokit-utils might be problematic. Click here for more details.

Files changed (70) hide show
  1. algokit_utils/__init__.py +23 -181
  2. algokit_utils/_debugging.py +89 -45
  3. algokit_utils/_legacy_v2/__init__.py +177 -0
  4. algokit_utils/{_ensure_funded.py → _legacy_v2/_ensure_funded.py} +19 -18
  5. algokit_utils/{_transfer.py → _legacy_v2/_transfer.py} +24 -23
  6. algokit_utils/_legacy_v2/account.py +203 -0
  7. algokit_utils/_legacy_v2/application_client.py +1471 -0
  8. algokit_utils/_legacy_v2/application_specification.py +21 -0
  9. algokit_utils/_legacy_v2/asset.py +168 -0
  10. algokit_utils/_legacy_v2/common.py +28 -0
  11. algokit_utils/_legacy_v2/deploy.py +822 -0
  12. algokit_utils/_legacy_v2/logic_error.py +14 -0
  13. algokit_utils/{models.py → _legacy_v2/models.py} +16 -45
  14. algokit_utils/_legacy_v2/network_clients.py +140 -0
  15. algokit_utils/account.py +12 -183
  16. algokit_utils/accounts/__init__.py +2 -0
  17. algokit_utils/accounts/account_manager.py +909 -0
  18. algokit_utils/accounts/kmd_account_manager.py +159 -0
  19. algokit_utils/algorand.py +265 -0
  20. algokit_utils/application_client.py +9 -1447
  21. algokit_utils/application_specification.py +39 -197
  22. algokit_utils/applications/__init__.py +7 -0
  23. algokit_utils/applications/abi.py +276 -0
  24. algokit_utils/applications/app_client.py +2056 -0
  25. algokit_utils/applications/app_deployer.py +600 -0
  26. algokit_utils/applications/app_factory.py +826 -0
  27. algokit_utils/applications/app_manager.py +470 -0
  28. algokit_utils/applications/app_spec/__init__.py +2 -0
  29. algokit_utils/applications/app_spec/arc32.py +207 -0
  30. algokit_utils/applications/app_spec/arc56.py +1023 -0
  31. algokit_utils/applications/enums.py +40 -0
  32. algokit_utils/asset.py +32 -168
  33. algokit_utils/assets/__init__.py +1 -0
  34. algokit_utils/assets/asset_manager.py +320 -0
  35. algokit_utils/beta/_utils.py +36 -0
  36. algokit_utils/beta/account_manager.py +4 -195
  37. algokit_utils/beta/algorand_client.py +4 -314
  38. algokit_utils/beta/client_manager.py +5 -74
  39. algokit_utils/beta/composer.py +5 -712
  40. algokit_utils/clients/__init__.py +2 -0
  41. algokit_utils/clients/client_manager.py +656 -0
  42. algokit_utils/clients/dispenser_api_client.py +192 -0
  43. algokit_utils/common.py +8 -26
  44. algokit_utils/config.py +71 -18
  45. algokit_utils/deploy.py +7 -894
  46. algokit_utils/dispenser_api.py +8 -176
  47. algokit_utils/errors/__init__.py +1 -0
  48. algokit_utils/errors/logic_error.py +121 -0
  49. algokit_utils/logic_error.py +7 -82
  50. algokit_utils/models/__init__.py +8 -0
  51. algokit_utils/models/account.py +193 -0
  52. algokit_utils/models/amount.py +198 -0
  53. algokit_utils/models/application.py +61 -0
  54. algokit_utils/models/network.py +25 -0
  55. algokit_utils/models/simulate.py +11 -0
  56. algokit_utils/models/state.py +59 -0
  57. algokit_utils/models/transaction.py +100 -0
  58. algokit_utils/network_clients.py +7 -128
  59. algokit_utils/protocols/__init__.py +2 -0
  60. algokit_utils/protocols/account.py +22 -0
  61. algokit_utils/protocols/typed_clients.py +108 -0
  62. algokit_utils/transactions/__init__.py +3 -0
  63. algokit_utils/transactions/transaction_composer.py +2293 -0
  64. algokit_utils/transactions/transaction_creator.py +156 -0
  65. algokit_utils/transactions/transaction_sender.py +574 -0
  66. {algokit_utils-2.4.0b1.dist-info → algokit_utils-3.0.0b2.dist-info}/METADATA +11 -7
  67. algokit_utils-3.0.0b2.dist-info/RECORD +70 -0
  68. {algokit_utils-2.4.0b1.dist-info → algokit_utils-3.0.0b2.dist-info}/WHEEL +1 -1
  69. algokit_utils-2.4.0b1.dist-info/RECORD +0 -24
  70. {algokit_utils-2.4.0b1.dist-info → algokit_utils-3.0.0b2.dist-info}/LICENSE +0 -0
@@ -0,0 +1,826 @@
1
+ import base64
2
+ import dataclasses
3
+ from collections.abc import Callable, Sequence
4
+ from dataclasses import asdict, dataclass
5
+ from typing import Any, Generic, TypeVar
6
+
7
+ from algosdk.atomic_transaction_composer import TransactionSigner
8
+ from algosdk.source_map import SourceMap
9
+ from algosdk.transaction import OnComplete, Transaction
10
+ from typing_extensions import Self
11
+
12
+ from algokit_utils._legacy_v2.application_specification import ApplicationSpecification
13
+ from algokit_utils.algorand import AlgorandClient
14
+ from algokit_utils.applications.abi import (
15
+ ABIReturn,
16
+ Arc56ReturnValueType,
17
+ get_abi_decoded_value,
18
+ get_abi_tuple_from_abi_struct,
19
+ )
20
+ from algokit_utils.applications.app_client import (
21
+ AppClient,
22
+ AppClientBareCallCreateParams,
23
+ AppClientBareCallParams,
24
+ AppClientCompilationParams,
25
+ AppClientCompilationResult,
26
+ AppClientCreateSchema,
27
+ AppClientMethodCallCreateParams,
28
+ AppClientMethodCallParams,
29
+ AppClientParams,
30
+ CreateOnComplete,
31
+ )
32
+ from algokit_utils.applications.app_deployer import (
33
+ AppDeploymentMetaData,
34
+ AppDeployParams,
35
+ AppDeployResult,
36
+ ApplicationLookup,
37
+ ApplicationMetaData,
38
+ OnSchemaBreak,
39
+ OnUpdate,
40
+ OperationPerformed,
41
+ )
42
+ from algokit_utils.applications.app_manager import DELETABLE_TEMPLATE_NAME, UPDATABLE_TEMPLATE_NAME
43
+ from algokit_utils.applications.app_spec.arc56 import Arc56Contract, Method
44
+ from algokit_utils.models.application import (
45
+ AppSourceMaps,
46
+ )
47
+ from algokit_utils.models.transaction import SendParams
48
+ from algokit_utils.transactions.transaction_composer import (
49
+ AppCreateMethodCallParams,
50
+ AppCreateParams,
51
+ AppDeleteMethodCallParams,
52
+ AppDeleteParams,
53
+ AppUpdateMethodCallParams,
54
+ AppUpdateParams,
55
+ BuiltTransactions,
56
+ )
57
+ from algokit_utils.transactions.transaction_sender import (
58
+ SendAppCreateTransactionResult,
59
+ SendAppTransactionResult,
60
+ SendAppUpdateTransactionResult,
61
+ SendSingleTransactionResult,
62
+ )
63
+
64
+ T = TypeVar("T")
65
+
66
+ __all__ = [
67
+ "AppFactory",
68
+ "AppFactoryCreateMethodCallParams",
69
+ "AppFactoryCreateMethodCallResult",
70
+ "AppFactoryCreateParams",
71
+ "AppFactoryDeployResult",
72
+ "AppFactoryParams",
73
+ "SendAppCreateFactoryTransactionResult",
74
+ "SendAppFactoryTransactionResult",
75
+ "SendAppUpdateFactoryTransactionResult",
76
+ ]
77
+
78
+
79
+ @dataclass(kw_only=True, frozen=True)
80
+ class AppFactoryParams:
81
+ algorand: AlgorandClient
82
+ app_spec: Arc56Contract | ApplicationSpecification | str
83
+ app_name: str | None = None
84
+ default_sender: str | None = None
85
+ default_signer: TransactionSigner | None = None
86
+ version: str | None = None
87
+ compilation_params: AppClientCompilationParams | None = None
88
+
89
+
90
+ @dataclass(kw_only=True, frozen=True)
91
+ class _AppFactoryCreateBaseParams(AppClientCreateSchema):
92
+ on_complete: CreateOnComplete | None = None
93
+
94
+
95
+ @dataclass(kw_only=True, frozen=True)
96
+ class AppFactoryCreateParams(_AppFactoryCreateBaseParams, AppClientBareCallParams):
97
+ pass
98
+
99
+
100
+ @dataclass(kw_only=True, frozen=True)
101
+ class AppFactoryCreateMethodCallParams(_AppFactoryCreateBaseParams, AppClientMethodCallParams):
102
+ pass
103
+
104
+
105
+ ABIReturnT = TypeVar(
106
+ "ABIReturnT",
107
+ bound=Arc56ReturnValueType,
108
+ )
109
+
110
+
111
+ @dataclass(frozen=True, kw_only=True)
112
+ class AppFactoryCreateMethodCallResult(SendSingleTransactionResult, Generic[ABIReturnT]):
113
+ app_id: int
114
+ app_address: str
115
+ compiled_approval: Any | None = None
116
+ compiled_clear: Any | None = None
117
+ abi_return: ABIReturnT | None = None
118
+
119
+
120
+ @dataclass(frozen=True)
121
+ class SendAppFactoryTransactionResult(SendAppTransactionResult[Arc56ReturnValueType]):
122
+ pass
123
+
124
+
125
+ @dataclass(frozen=True)
126
+ class SendAppUpdateFactoryTransactionResult(SendAppUpdateTransactionResult[Arc56ReturnValueType]):
127
+ pass
128
+
129
+
130
+ @dataclass(frozen=True, kw_only=True)
131
+ class SendAppCreateFactoryTransactionResult(SendAppCreateTransactionResult[Arc56ReturnValueType]):
132
+ pass
133
+
134
+
135
+ @dataclass(frozen=True)
136
+ class AppFactoryDeployResult:
137
+ """Result from deploying an application via AppFactory"""
138
+
139
+ app: ApplicationMetaData
140
+ operation_performed: OperationPerformed
141
+ create_result: SendAppCreateFactoryTransactionResult | None = None
142
+ update_result: SendAppUpdateFactoryTransactionResult | None = None
143
+ delete_result: SendAppFactoryTransactionResult | None = None
144
+
145
+ @classmethod
146
+ def from_deploy_result(
147
+ cls,
148
+ response: AppDeployResult,
149
+ deploy_params: AppDeployParams,
150
+ app_spec: Arc56Contract,
151
+ app_compilation_data: AppClientCompilationResult | None = None,
152
+ ) -> Self:
153
+ def to_factory_result(
154
+ response_data: SendAppTransactionResult[ABIReturn]
155
+ | SendAppCreateTransactionResult
156
+ | SendAppUpdateTransactionResult
157
+ | None,
158
+ params: Any, # noqa: ANN401
159
+ ) -> Any | None: # noqa: ANN401
160
+ if not response_data:
161
+ return None
162
+
163
+ response_data_dict = asdict(response_data)
164
+ abi_return = response_data.abi_return
165
+ if abi_return and abi_return.method:
166
+ response_data_dict["abi_return"] = abi_return.get_arc56_value(params.method, app_spec.structs)
167
+
168
+ match response_data:
169
+ case SendAppCreateTransactionResult():
170
+ return SendAppCreateFactoryTransactionResult(**response_data_dict)
171
+ case SendAppUpdateTransactionResult():
172
+ response_data_dict["compiled_approval"] = (
173
+ app_compilation_data.compiled_approval if app_compilation_data else None
174
+ )
175
+ response_data_dict["compiled_clear"] = (
176
+ app_compilation_data.compiled_clear if app_compilation_data else None
177
+ )
178
+ return SendAppUpdateFactoryTransactionResult(**response_data_dict)
179
+ case SendAppTransactionResult():
180
+ return SendAppFactoryTransactionResult(**response_data_dict)
181
+
182
+ return cls(
183
+ app=response.app,
184
+ operation_performed=response.operation_performed,
185
+ create_result=to_factory_result(
186
+ response.create_result,
187
+ deploy_params.create_params,
188
+ ),
189
+ update_result=to_factory_result(
190
+ response.update_result,
191
+ deploy_params.update_params,
192
+ ),
193
+ delete_result=to_factory_result(
194
+ response.delete_result,
195
+ deploy_params.delete_params,
196
+ ),
197
+ )
198
+
199
+
200
+ class _BareParamsBuilder:
201
+ def __init__(self, factory: "AppFactory") -> None:
202
+ self._factory = factory
203
+ self._algorand = factory._algorand
204
+
205
+ def create(
206
+ self, params: AppFactoryCreateParams | None = None, compilation_params: AppClientCompilationParams | None = None
207
+ ) -> AppCreateParams:
208
+ base_params = params or AppFactoryCreateParams()
209
+ compiled = self._factory.compile(compilation_params)
210
+
211
+ return AppCreateParams(
212
+ **{
213
+ **{
214
+ param: value
215
+ for param, value in asdict(base_params).items()
216
+ if param in {f.name for f in dataclasses.fields(AppCreateParams)}
217
+ },
218
+ "approval_program": compiled.approval_program,
219
+ "clear_state_program": compiled.clear_state_program,
220
+ "schema": base_params.schema
221
+ or {
222
+ "global_byte_slices": self._factory._app_spec.state.schema.global_state.bytes,
223
+ "global_ints": self._factory._app_spec.state.schema.global_state.ints,
224
+ "local_byte_slices": self._factory._app_spec.state.schema.local_state.bytes,
225
+ "local_ints": self._factory._app_spec.state.schema.local_state.ints,
226
+ },
227
+ "sender": self._factory._get_sender(base_params.sender),
228
+ "signer": self._factory._get_signer(base_params.sender, base_params.signer),
229
+ "on_complete": base_params.on_complete or OnComplete.NoOpOC,
230
+ }
231
+ )
232
+
233
+ def deploy_update(self, params: AppClientBareCallParams | None = None) -> AppUpdateParams:
234
+ return AppUpdateParams(
235
+ **{
236
+ **{
237
+ param: value
238
+ for param, value in asdict(params or AppClientBareCallParams()).items()
239
+ if param in {f.name for f in dataclasses.fields(AppUpdateParams)}
240
+ },
241
+ "app_id": 0,
242
+ "approval_program": "",
243
+ "clear_state_program": "",
244
+ "sender": self._factory._get_sender(params.sender if params else None),
245
+ "on_complete": OnComplete.UpdateApplicationOC,
246
+ "signer": self._factory._get_signer(
247
+ params.sender if params else None, params.signer if params else None
248
+ ),
249
+ }
250
+ )
251
+
252
+ def deploy_delete(self, params: AppClientBareCallParams | None = None) -> AppDeleteParams:
253
+ return AppDeleteParams(
254
+ **{
255
+ **{
256
+ param: value
257
+ for param, value in asdict(params or AppClientBareCallParams()).items()
258
+ if param in {f.name for f in dataclasses.fields(AppDeleteParams)}
259
+ },
260
+ "app_id": 0,
261
+ "sender": self._factory._get_sender(params.sender if params else None),
262
+ "signer": self._factory._get_signer(
263
+ params.sender if params else None, params.signer if params else None
264
+ ),
265
+ "on_complete": OnComplete.DeleteApplicationOC,
266
+ }
267
+ )
268
+
269
+
270
+ class _MethodParamsBuilder:
271
+ def __init__(self, factory: "AppFactory") -> None:
272
+ self._factory = factory
273
+ self._bare = _BareParamsBuilder(factory)
274
+
275
+ @property
276
+ def bare(self) -> _BareParamsBuilder:
277
+ return self._bare
278
+
279
+ def create(
280
+ self, params: AppFactoryCreateMethodCallParams, compilation_params: AppClientCompilationParams | None = None
281
+ ) -> AppCreateMethodCallParams:
282
+ compiled = self._factory.compile(compilation_params)
283
+
284
+ return AppCreateMethodCallParams(
285
+ **{
286
+ **{
287
+ param: value
288
+ for param, value in asdict(params).items()
289
+ if param in {f.name for f in dataclasses.fields(AppCreateMethodCallParams)}
290
+ },
291
+ "app_id": 0,
292
+ "approval_program": compiled.approval_program,
293
+ "clear_state_program": compiled.clear_state_program,
294
+ "schema": params.schema
295
+ or {
296
+ "global_byte_slices": self._factory._app_spec.state.schema.global_state.bytes,
297
+ "global_ints": self._factory._app_spec.state.schema.global_state.ints,
298
+ "local_byte_slices": self._factory._app_spec.state.schema.local_state.bytes,
299
+ "local_ints": self._factory._app_spec.state.schema.local_state.ints,
300
+ },
301
+ "sender": self._factory._get_sender(params.sender),
302
+ "signer": self._factory._get_signer(
303
+ params.sender if params else None, params.signer if params else None
304
+ ),
305
+ "method": self._factory._app_spec.get_arc56_method(params.method).to_abi_method(),
306
+ "args": self._factory._get_create_abi_args_with_default_values(params.method, params.args),
307
+ "on_complete": params.on_complete or OnComplete.NoOpOC,
308
+ }
309
+ )
310
+
311
+ def deploy_update(self, params: AppClientMethodCallParams) -> AppUpdateMethodCallParams:
312
+ return AppUpdateMethodCallParams(
313
+ **{
314
+ **{
315
+ param: value
316
+ for param, value in asdict(params).items()
317
+ if param in {f.name for f in dataclasses.fields(AppUpdateMethodCallParams)}
318
+ },
319
+ "app_id": 0,
320
+ "approval_program": "",
321
+ "clear_state_program": "",
322
+ "sender": self._factory._get_sender(params.sender),
323
+ "signer": self._factory._get_signer(
324
+ params.sender if params else None, params.signer if params else None
325
+ ),
326
+ "method": self._factory._app_spec.get_arc56_method(params.method).to_abi_method(),
327
+ "args": self._factory._get_create_abi_args_with_default_values(params.method, params.args),
328
+ "on_complete": OnComplete.UpdateApplicationOC,
329
+ }
330
+ )
331
+
332
+ def deploy_delete(self, params: AppClientMethodCallParams) -> AppDeleteMethodCallParams:
333
+ return AppDeleteMethodCallParams(
334
+ **{
335
+ **{
336
+ param: value
337
+ for param, value in asdict(params).items()
338
+ if param in {f.name for f in dataclasses.fields(AppDeleteMethodCallParams)}
339
+ },
340
+ "app_id": 0,
341
+ "sender": self._factory._get_sender(params.sender),
342
+ "signer": self._factory._get_signer(
343
+ params.sender if params else None, params.signer if params else None
344
+ ),
345
+ "method": self._factory.app_spec.get_arc56_method(params.method).to_abi_method(),
346
+ "args": self._factory._get_create_abi_args_with_default_values(params.method, params.args),
347
+ "on_complete": OnComplete.DeleteApplicationOC,
348
+ }
349
+ )
350
+
351
+
352
+ class _AppFactoryBareCreateTransactionAccessor:
353
+ def __init__(self, factory: "AppFactory") -> None:
354
+ self._factory = factory
355
+
356
+ def create(self, params: AppFactoryCreateParams | None = None) -> Transaction:
357
+ return self._factory._algorand.create_transaction.app_create(self._factory.params.bare.create(params))
358
+
359
+
360
+ class _TransactionCreator:
361
+ def __init__(self, factory: "AppFactory") -> None:
362
+ self._factory = factory
363
+ self._bare = _AppFactoryBareCreateTransactionAccessor(factory)
364
+
365
+ @property
366
+ def bare(self) -> _AppFactoryBareCreateTransactionAccessor:
367
+ return self._bare
368
+
369
+ def create(self, params: AppFactoryCreateMethodCallParams) -> BuiltTransactions:
370
+ return self._factory._algorand.create_transaction.app_create_method_call(self._factory.params.create(params))
371
+
372
+
373
+ class _AppFactoryBareSendAccessor:
374
+ def __init__(self, factory: "AppFactory") -> None:
375
+ self._factory = factory
376
+ self._algorand = factory._algorand
377
+
378
+ def create(
379
+ self,
380
+ params: AppFactoryCreateParams | None = None,
381
+ send_params: SendParams | None = None,
382
+ compilation_params: AppClientCompilationParams | None = None,
383
+ ) -> tuple[AppClient, SendAppCreateTransactionResult]:
384
+ compilation_params = compilation_params or AppClientCompilationParams()
385
+ compilation_params["updatable"] = (
386
+ compilation_params.get("updatable")
387
+ if compilation_params.get("updatable") is not None
388
+ else self._factory._updatable
389
+ )
390
+ compilation_params["deletable"] = (
391
+ compilation_params.get("deletable")
392
+ if compilation_params.get("deletable") is not None
393
+ else self._factory._deletable
394
+ )
395
+ compilation_params["deploy_time_params"] = (
396
+ compilation_params.get("deploy_time_params")
397
+ if compilation_params.get("deploy_time_params") is not None
398
+ else self._factory._deploy_time_params
399
+ )
400
+
401
+ compiled = self._factory.compile(compilation_params)
402
+
403
+ result = self._factory._handle_call_errors(
404
+ lambda: self._algorand.send.app_create(
405
+ self._factory.params.bare.create(params, compilation_params), send_params
406
+ )
407
+ )
408
+
409
+ return (
410
+ self._factory.get_app_client_by_id(
411
+ app_id=result.app_id,
412
+ ),
413
+ SendAppCreateTransactionResult[ABIReturn](
414
+ transaction=result.transaction,
415
+ confirmation=result.confirmation,
416
+ app_id=result.app_id,
417
+ app_address=result.app_address,
418
+ compiled_approval=compiled.compiled_approval if compiled else None,
419
+ compiled_clear=compiled.compiled_clear if compiled else None,
420
+ group_id=result.group_id,
421
+ tx_ids=result.tx_ids,
422
+ transactions=result.transactions,
423
+ confirmations=result.confirmations,
424
+ ),
425
+ )
426
+
427
+
428
+ class _TransactionSender:
429
+ def __init__(self, factory: "AppFactory") -> None:
430
+ self._factory = factory
431
+ self._algorand = factory._algorand
432
+ self._bare = _AppFactoryBareSendAccessor(factory)
433
+
434
+ @property
435
+ def bare(self) -> _AppFactoryBareSendAccessor:
436
+ return self._bare
437
+
438
+ def create(
439
+ self,
440
+ params: AppFactoryCreateMethodCallParams,
441
+ send_params: SendParams | None = None,
442
+ compilation_params: AppClientCompilationParams | None = None,
443
+ ) -> tuple[AppClient, AppFactoryCreateMethodCallResult[Arc56ReturnValueType]]:
444
+ compilation_params = compilation_params or AppClientCompilationParams()
445
+ compilation_params["updatable"] = (
446
+ compilation_params.get("updatable")
447
+ if compilation_params.get("updatable") is not None
448
+ else self._factory._updatable
449
+ )
450
+ compilation_params["deletable"] = (
451
+ compilation_params.get("deletable")
452
+ if compilation_params.get("deletable") is not None
453
+ else self._factory._deletable
454
+ )
455
+ compilation_params["deploy_time_params"] = (
456
+ compilation_params.get("deploy_time_params")
457
+ if compilation_params.get("deploy_time_params") is not None
458
+ else self._factory._deploy_time_params
459
+ )
460
+
461
+ compiled = self._factory.compile(compilation_params)
462
+ result = self._factory._handle_call_errors(
463
+ lambda: self._factory._parse_method_call_return(
464
+ lambda: self._algorand.send.app_create_method_call(
465
+ self._factory.params.create(params, compilation_params), send_params
466
+ ),
467
+ self._factory._app_spec.get_arc56_method(params.method),
468
+ )
469
+ )
470
+
471
+ return (
472
+ self._factory.get_app_client_by_id(
473
+ app_id=result.app_id,
474
+ ),
475
+ AppFactoryCreateMethodCallResult[Arc56ReturnValueType](
476
+ transaction=result.transaction,
477
+ confirmation=result.confirmation,
478
+ tx_id=result.tx_id,
479
+ app_id=result.app_id,
480
+ app_address=result.app_address,
481
+ abi_return=result.abi_return,
482
+ compiled_approval=compiled.compiled_approval if compiled else None,
483
+ compiled_clear=compiled.compiled_clear if compiled else None,
484
+ group_id=result.group_id,
485
+ tx_ids=result.tx_ids,
486
+ transactions=result.transactions,
487
+ confirmations=result.confirmations,
488
+ returns=result.returns,
489
+ ),
490
+ )
491
+
492
+
493
+ class AppFactory:
494
+ def __init__(self, params: AppFactoryParams) -> None:
495
+ self._app_spec = AppClient.normalise_app_spec(params.app_spec)
496
+ self._app_name = params.app_name or self._app_spec.name
497
+ self._algorand = params.algorand
498
+ self._version = params.version or "1.0"
499
+ self._default_sender = params.default_sender
500
+ self._default_signer = params.default_signer
501
+ self._approval_source_map: SourceMap | None = None
502
+ self._clear_source_map: SourceMap | None = None
503
+ self._params_accessor = _MethodParamsBuilder(self)
504
+ self._send_accessor = _TransactionSender(self)
505
+ self._create_transaction_accessor = _TransactionCreator(self)
506
+
507
+ compilation_params = params.compilation_params or AppClientCompilationParams()
508
+ self._deploy_time_params = compilation_params.get("deploy_time_params")
509
+ self._updatable = compilation_params.get("updatable")
510
+ self._deletable = compilation_params.get("deletable")
511
+
512
+ @property
513
+ def app_name(self) -> str:
514
+ return self._app_name
515
+
516
+ @property
517
+ def app_spec(self) -> Arc56Contract:
518
+ return self._app_spec
519
+
520
+ @property
521
+ def algorand(self) -> AlgorandClient:
522
+ return self._algorand
523
+
524
+ @property
525
+ def params(self) -> _MethodParamsBuilder:
526
+ return self._params_accessor
527
+
528
+ @property
529
+ def send(self) -> _TransactionSender:
530
+ return self._send_accessor
531
+
532
+ @property
533
+ def create_transaction(self) -> _TransactionCreator:
534
+ return self._create_transaction_accessor
535
+
536
+ def deploy(
537
+ self,
538
+ *,
539
+ on_update: OnUpdate | None = None,
540
+ on_schema_break: OnSchemaBreak | None = None,
541
+ create_params: AppClientMethodCallCreateParams | AppClientBareCallCreateParams | None = None,
542
+ update_params: AppClientMethodCallParams | AppClientBareCallParams | None = None,
543
+ delete_params: AppClientMethodCallParams | AppClientBareCallParams | None = None,
544
+ existing_deployments: ApplicationLookup | None = None,
545
+ ignore_cache: bool = False,
546
+ app_name: str | None = None,
547
+ send_params: SendParams | None = None,
548
+ compilation_params: AppClientCompilationParams | None = None,
549
+ ) -> tuple[AppClient, AppFactoryDeployResult]:
550
+ """Deploy the application with the specified parameters."""
551
+ # Resolve control parameters with factory defaults
552
+ send_params = send_params or SendParams()
553
+ compilation_params = compilation_params or AppClientCompilationParams()
554
+ resolved_updatable = (
555
+ upd
556
+ if (upd := compilation_params.get("updatable")) is not None
557
+ else self._updatable or self._get_deploy_time_control("updatable")
558
+ )
559
+ resolved_deletable = (
560
+ dlb
561
+ if (dlb := compilation_params.get("deletable")) is not None
562
+ else self._deletable or self._get_deploy_time_control("deletable")
563
+ )
564
+ resolved_deploy_time_params = compilation_params.get("deploy_time_params") or self._deploy_time_params
565
+
566
+ def prepare_create_args() -> AppCreateMethodCallParams | AppCreateParams:
567
+ """Prepare create arguments based on parameter type."""
568
+ if create_params and isinstance(create_params, AppClientMethodCallCreateParams):
569
+ return self.params.create(
570
+ AppFactoryCreateMethodCallParams(
571
+ **asdict(create_params),
572
+ ),
573
+ compilation_params={
574
+ "updatable": resolved_updatable,
575
+ "deletable": resolved_deletable,
576
+ "deploy_time_params": resolved_deploy_time_params,
577
+ },
578
+ )
579
+
580
+ base_params = create_params or AppClientBareCallCreateParams()
581
+ return self.params.bare.create(
582
+ AppFactoryCreateParams(
583
+ **asdict(base_params) if base_params else {},
584
+ ),
585
+ compilation_params={
586
+ "updatable": resolved_updatable,
587
+ "deletable": resolved_deletable,
588
+ "deploy_time_params": resolved_deploy_time_params,
589
+ },
590
+ )
591
+
592
+ def prepare_update_args() -> AppUpdateMethodCallParams | AppUpdateParams:
593
+ """Prepare update arguments based on parameter type."""
594
+ return (
595
+ self.params.deploy_update(update_params)
596
+ if isinstance(update_params, AppClientMethodCallParams)
597
+ else self.params.bare.deploy_update(update_params)
598
+ )
599
+
600
+ def prepare_delete_args() -> AppDeleteMethodCallParams | AppDeleteParams:
601
+ """Prepare delete arguments based on parameter type."""
602
+ return (
603
+ self.params.deploy_delete(delete_params)
604
+ if isinstance(delete_params, AppClientMethodCallParams)
605
+ else self.params.bare.deploy_delete(delete_params)
606
+ )
607
+
608
+ # Execute deployment
609
+ deploy_params = AppDeployParams(
610
+ deploy_time_params=resolved_deploy_time_params,
611
+ on_schema_break=on_schema_break,
612
+ on_update=on_update,
613
+ existing_deployments=existing_deployments,
614
+ ignore_cache=ignore_cache,
615
+ create_params=prepare_create_args(),
616
+ update_params=prepare_update_args(),
617
+ delete_params=prepare_delete_args(),
618
+ metadata=AppDeploymentMetaData(
619
+ name=app_name or self._app_name,
620
+ version=self._version,
621
+ updatable=resolved_updatable,
622
+ deletable=resolved_deletable,
623
+ ),
624
+ send_params=send_params,
625
+ )
626
+ deploy_result = self._algorand.app_deployer.deploy(deploy_params)
627
+
628
+ # Prepare app client and factory deploy response
629
+ app_client = self.get_app_client_by_id(
630
+ app_id=deploy_result.app.app_id,
631
+ app_name=app_name,
632
+ default_sender=self._default_sender,
633
+ default_signer=self._default_signer,
634
+ )
635
+ factory_deploy_result = AppFactoryDeployResult.from_deploy_result(
636
+ response=deploy_result,
637
+ deploy_params=deploy_params,
638
+ app_spec=app_client.app_spec,
639
+ app_compilation_data=self.compile(
640
+ AppClientCompilationParams(
641
+ deploy_time_params=resolved_deploy_time_params,
642
+ updatable=resolved_updatable,
643
+ deletable=resolved_deletable,
644
+ )
645
+ ),
646
+ )
647
+
648
+ return app_client, factory_deploy_result
649
+
650
+ def get_app_client_by_id(
651
+ self,
652
+ app_id: int,
653
+ app_name: str | None = None,
654
+ default_sender: str | None = None, # Address can be string or bytes
655
+ default_signer: TransactionSigner | None = None,
656
+ approval_source_map: SourceMap | None = None,
657
+ clear_source_map: SourceMap | None = None,
658
+ ) -> AppClient:
659
+ return AppClient(
660
+ AppClientParams(
661
+ app_id=app_id,
662
+ algorand=self._algorand,
663
+ app_spec=self._app_spec,
664
+ app_name=app_name or self._app_name,
665
+ default_sender=default_sender or self._default_sender,
666
+ default_signer=default_signer or self._default_signer,
667
+ approval_source_map=approval_source_map or self._approval_source_map,
668
+ clear_source_map=clear_source_map or self._clear_source_map,
669
+ )
670
+ )
671
+
672
+ def get_app_client_by_creator_and_name(
673
+ self,
674
+ creator_address: str,
675
+ app_name: str,
676
+ default_sender: str | None = None,
677
+ default_signer: TransactionSigner | None = None,
678
+ ignore_cache: bool | None = None,
679
+ app_lookup_cache: ApplicationLookup | None = None,
680
+ approval_source_map: SourceMap | None = None,
681
+ clear_source_map: SourceMap | None = None,
682
+ ) -> AppClient:
683
+ return AppClient.from_creator_and_name(
684
+ creator_address=creator_address,
685
+ app_name=app_name or self._app_name,
686
+ default_sender=default_sender or self._default_sender,
687
+ default_signer=default_signer or self._default_signer,
688
+ approval_source_map=approval_source_map or self._approval_source_map,
689
+ clear_source_map=clear_source_map or self._clear_source_map,
690
+ ignore_cache=ignore_cache,
691
+ app_lookup_cache=app_lookup_cache,
692
+ app_spec=self._app_spec,
693
+ algorand=self._algorand,
694
+ )
695
+
696
+ def export_source_maps(self) -> AppSourceMaps:
697
+ if not self._approval_source_map or not self._clear_source_map:
698
+ raise ValueError(
699
+ "Unable to export source maps; they haven't been loaded into this client - "
700
+ "you need to call create, update, or deploy first"
701
+ )
702
+ return AppSourceMaps(
703
+ approval_source_map=self._approval_source_map,
704
+ clear_source_map=self._clear_source_map,
705
+ )
706
+
707
+ def import_source_maps(self, source_maps: AppSourceMaps) -> None:
708
+ self._approval_source_map = source_maps.approval_source_map
709
+ self._clear_source_map = source_maps.clear_source_map
710
+
711
+ def compile(self, compilation_params: AppClientCompilationParams | None = None) -> AppClientCompilationResult:
712
+ compilation = compilation_params or AppClientCompilationParams()
713
+ result = AppClient.compile(
714
+ app_spec=self._app_spec,
715
+ app_manager=self._algorand.app,
716
+ compilation_params=compilation,
717
+ )
718
+
719
+ if result.compiled_approval:
720
+ self._approval_source_map = result.compiled_approval.source_map
721
+ if result.compiled_clear:
722
+ self._clear_source_map = result.compiled_clear.source_map
723
+
724
+ return result
725
+
726
+ def _expose_logic_error(self, e: Exception, is_clear_state_program: bool = False) -> Exception: # noqa: FBT002 FBT001
727
+ return AppClient._expose_logic_error_static(
728
+ e=e,
729
+ app_spec=self._app_spec,
730
+ is_clear_state_program=is_clear_state_program,
731
+ approval_source_map=self._approval_source_map,
732
+ clear_source_map=self._clear_source_map,
733
+ program=None,
734
+ approval_source_info=(self._app_spec.source_info.approval if self._app_spec.source_info else None),
735
+ clear_source_info=(self._app_spec.source_info.clear if self._app_spec.source_info else None),
736
+ )
737
+
738
+ def _get_deploy_time_control(self, control: str) -> bool | None:
739
+ approval = self._app_spec.source.get_decoded_approval() if self._app_spec.source else None
740
+
741
+ template_name = UPDATABLE_TEMPLATE_NAME if control == "updatable" else DELETABLE_TEMPLATE_NAME
742
+ if not approval or template_name not in approval:
743
+ return None
744
+
745
+ on_complete = "UpdateApplication" if control == "updatable" else "DeleteApplication"
746
+ return on_complete in self._app_spec.bare_actions.call or any(
747
+ on_complete in m.actions.call for m in self._app_spec.methods if m.actions and m.actions.call
748
+ )
749
+
750
+ def _get_sender(self, sender: str | None) -> str:
751
+ if not sender and not self._default_sender:
752
+ raise Exception(
753
+ f"No sender provided and no default sender present in app client for call to app {self._app_name}"
754
+ )
755
+ return str(sender or self._default_sender)
756
+
757
+ def _get_signer(self, sender: str | None, signer: TransactionSigner | None) -> TransactionSigner | None:
758
+ return signer or (self._default_signer if not sender or sender == self._default_sender else None)
759
+
760
+ def _handle_call_errors(self, call: Callable[[], T]) -> T:
761
+ try:
762
+ return call()
763
+ except Exception as e:
764
+ raise self._expose_logic_error(e) from None
765
+
766
+ def _parse_method_call_return(
767
+ self,
768
+ result: Callable[
769
+ [], SendAppTransactionResult | SendAppCreateTransactionResult | SendAppUpdateTransactionResult
770
+ ],
771
+ method: Method,
772
+ ) -> AppFactoryCreateMethodCallResult[Arc56ReturnValueType]:
773
+ result_value = result()
774
+ return AppFactoryCreateMethodCallResult[Arc56ReturnValueType](
775
+ **{
776
+ **result_value.__dict__,
777
+ "abi_return": result_value.abi_return.get_arc56_value(method, self._app_spec.structs)
778
+ if isinstance(result_value.abi_return, ABIReturn)
779
+ else None,
780
+ }
781
+ )
782
+
783
+ def _get_create_abi_args_with_default_values(
784
+ self,
785
+ method_name_or_signature: str,
786
+ user_args: Sequence[Any] | None,
787
+ ) -> list[Any]:
788
+ """
789
+ Builds a list of ABI argument values for creation calls, applying default
790
+ argument values when not provided.
791
+ """
792
+ method = self._app_spec.get_arc56_method(method_name_or_signature)
793
+
794
+ results: list[Any] = []
795
+
796
+ for i, param in enumerate(method.args):
797
+ if user_args and i < len(user_args):
798
+ arg_value = user_args[i]
799
+ if param.struct and isinstance(arg_value, dict):
800
+ arg_value = get_abi_tuple_from_abi_struct(
801
+ arg_value,
802
+ self._app_spec.structs[param.struct],
803
+ self._app_spec.structs,
804
+ )
805
+ results.append(arg_value)
806
+ continue
807
+
808
+ default_value = getattr(param, "default_value", None)
809
+ if default_value:
810
+ if default_value.source == "literal":
811
+ raw_value = base64.b64decode(default_value.data)
812
+ value_type = default_value.type or str(param.type)
813
+ decoded_value = get_abi_decoded_value(raw_value, value_type, self._app_spec.structs)
814
+ results.append(decoded_value)
815
+ else:
816
+ raise ValueError(
817
+ f"Cannot provide default value from source={default_value.source} "
818
+ "for a contract creation call."
819
+ )
820
+ else:
821
+ param_name = param.name or f"arg{i + 1}"
822
+ raise ValueError(
823
+ f"No value provided for required argument {param_name} " f"in call to method {method.name}"
824
+ )
825
+
826
+ return results