algokit-utils 2.4.0__py3-none-any.whl → 3.0.0__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} +21 -24
  5. algokit_utils/{_transfer.py → _legacy_v2/_transfer.py} +26 -23
  6. algokit_utils/_legacy_v2/account.py +203 -0
  7. algokit_utils/_legacy_v2/application_client.py +1472 -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 +144 -0
  15. algokit_utils/account.py +12 -183
  16. algokit_utils/accounts/__init__.py +2 -0
  17. algokit_utils/accounts/account_manager.py +912 -0
  18. algokit_utils/accounts/kmd_account_manager.py +161 -0
  19. algokit_utils/algorand.py +359 -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 +275 -0
  24. algokit_utils/applications/app_client.py +2108 -0
  25. algokit_utils/applications/app_deployer.py +725 -0
  26. algokit_utils/applications/app_factory.py +1134 -0
  27. algokit_utils/applications/app_manager.py +578 -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 +989 -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 +336 -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 +738 -0
  42. algokit_utils/clients/dispenser_api_client.py +224 -0
  43. algokit_utils/common.py +8 -26
  44. algokit_utils/config.py +76 -29
  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 +217 -0
  52. algokit_utils/models/amount.py +200 -0
  53. algokit_utils/models/application.py +91 -0
  54. algokit_utils/models/network.py +29 -0
  55. algokit_utils/models/simulate.py +11 -0
  56. algokit_utils/models/state.py +68 -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 +2499 -0
  64. algokit_utils/transactions/transaction_creator.py +688 -0
  65. algokit_utils/transactions/transaction_sender.py +1219 -0
  66. {algokit_utils-2.4.0.dist-info → algokit_utils-3.0.0.dist-info}/METADATA +11 -7
  67. algokit_utils-3.0.0.dist-info/RECORD +70 -0
  68. {algokit_utils-2.4.0.dist-info → algokit_utils-3.0.0.dist-info}/WHEEL +1 -1
  69. algokit_utils-2.4.0.dist-info/RECORD +0 -24
  70. {algokit_utils-2.4.0.dist-info → algokit_utils-3.0.0.dist-info}/LICENSE +0 -0
@@ -0,0 +1,1134 @@
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
+ AppClientMethodCallCreateParams,
27
+ AppClientMethodCallParams,
28
+ AppClientParams,
29
+ CreateOnComplete,
30
+ )
31
+ from algokit_utils.applications.app_deployer import (
32
+ AppDeploymentMetaData,
33
+ AppDeployParams,
34
+ AppDeployResult,
35
+ ApplicationLookup,
36
+ ApplicationMetaData,
37
+ OnSchemaBreak,
38
+ OnUpdate,
39
+ OperationPerformed,
40
+ )
41
+ from algokit_utils.applications.app_manager import DELETABLE_TEMPLATE_NAME, UPDATABLE_TEMPLATE_NAME
42
+ from algokit_utils.applications.app_spec.arc56 import Arc56Contract, Method
43
+ from algokit_utils.models.application import (
44
+ AppSourceMaps,
45
+ )
46
+ from algokit_utils.models.transaction import SendParams
47
+ from algokit_utils.transactions.transaction_composer import (
48
+ AppCreateMethodCallParams,
49
+ AppCreateParams,
50
+ AppDeleteMethodCallParams,
51
+ AppDeleteParams,
52
+ AppUpdateMethodCallParams,
53
+ AppUpdateParams,
54
+ BuiltTransactions,
55
+ )
56
+ from algokit_utils.transactions.transaction_sender import (
57
+ SendAppCreateTransactionResult,
58
+ SendAppTransactionResult,
59
+ SendAppUpdateTransactionResult,
60
+ SendSingleTransactionResult,
61
+ )
62
+
63
+ T = TypeVar("T")
64
+
65
+ __all__ = [
66
+ "AppFactory",
67
+ "AppFactoryCreateMethodCallParams",
68
+ "AppFactoryCreateMethodCallResult",
69
+ "AppFactoryCreateParams",
70
+ "AppFactoryDeployResult",
71
+ "AppFactoryParams",
72
+ "SendAppCreateFactoryTransactionResult",
73
+ "SendAppFactoryTransactionResult",
74
+ "SendAppUpdateFactoryTransactionResult",
75
+ ]
76
+
77
+
78
+ @dataclass(kw_only=True, frozen=True)
79
+ class AppFactoryParams:
80
+ algorand: AlgorandClient
81
+ app_spec: Arc56Contract | ApplicationSpecification | str
82
+ app_name: str | None = None
83
+ default_sender: str | None = None
84
+ default_signer: TransactionSigner | None = None
85
+ version: str | None = None
86
+ compilation_params: AppClientCompilationParams | None = None
87
+
88
+
89
+ @dataclass(kw_only=True, frozen=True)
90
+ class AppFactoryCreateParams(AppClientBareCallCreateParams):
91
+ on_complete: CreateOnComplete | None = None
92
+
93
+
94
+ @dataclass(kw_only=True, frozen=True)
95
+ class AppFactoryCreateMethodCallParams(AppClientMethodCallCreateParams):
96
+ pass
97
+
98
+
99
+ ABIReturnT = TypeVar(
100
+ "ABIReturnT",
101
+ bound=Arc56ReturnValueType,
102
+ )
103
+
104
+
105
+ @dataclass(frozen=True, kw_only=True)
106
+ class AppFactoryCreateMethodCallResult(SendSingleTransactionResult, Generic[ABIReturnT]):
107
+ app_id: int
108
+ app_address: str
109
+ compiled_approval: Any | None = None
110
+ compiled_clear: Any | None = None
111
+ abi_return: ABIReturnT | None = None
112
+
113
+
114
+ @dataclass(frozen=True)
115
+ class SendAppFactoryTransactionResult(SendAppTransactionResult[Arc56ReturnValueType]):
116
+ pass
117
+
118
+
119
+ @dataclass(frozen=True)
120
+ class SendAppUpdateFactoryTransactionResult(SendAppUpdateTransactionResult[Arc56ReturnValueType]):
121
+ pass
122
+
123
+
124
+ @dataclass(frozen=True, kw_only=True)
125
+ class SendAppCreateFactoryTransactionResult(SendAppCreateTransactionResult[Arc56ReturnValueType]):
126
+ pass
127
+
128
+
129
+ @dataclass(frozen=True)
130
+ class AppFactoryDeployResult:
131
+ """Result from deploying an application via AppFactory"""
132
+
133
+ app: ApplicationMetaData
134
+ """The application metadata"""
135
+ operation_performed: OperationPerformed
136
+ """The operation performed"""
137
+ create_result: SendAppCreateFactoryTransactionResult | None = None
138
+ """The create result"""
139
+ update_result: SendAppUpdateFactoryTransactionResult | None = None
140
+ """The update result"""
141
+ delete_result: SendAppFactoryTransactionResult | None = None
142
+ """The delete result"""
143
+
144
+ @classmethod
145
+ def from_deploy_result(
146
+ cls,
147
+ response: AppDeployResult,
148
+ deploy_params: AppDeployParams,
149
+ app_spec: Arc56Contract,
150
+ app_compilation_data: AppClientCompilationResult | None = None,
151
+ ) -> Self:
152
+ """
153
+ Construct an AppFactoryDeployResult from a deployment result.
154
+
155
+ :param response: The deployment response.
156
+ :param deploy_params: The deployment parameters.
157
+ :param app_spec: The application specification.
158
+ :param app_compilation_data: Optional app compilation data.
159
+ :return: An instance of AppFactoryDeployResult.
160
+ """
161
+
162
+ def to_factory_result(
163
+ response_data: SendAppTransactionResult[ABIReturn]
164
+ | SendAppCreateTransactionResult
165
+ | SendAppUpdateTransactionResult
166
+ | None,
167
+ params: Any, # noqa: ANN401
168
+ ) -> Any | None: # noqa: ANN401
169
+ if not response_data:
170
+ return None
171
+
172
+ response_data_dict = asdict(response_data)
173
+ abi_return = response_data.abi_return
174
+ if abi_return and abi_return.method:
175
+ response_data_dict["abi_return"] = abi_return.get_arc56_value(params.method, app_spec.structs)
176
+
177
+ match response_data:
178
+ case SendAppCreateTransactionResult():
179
+ return SendAppCreateFactoryTransactionResult(**response_data_dict)
180
+ case SendAppUpdateTransactionResult():
181
+ response_data_dict["compiled_approval"] = (
182
+ app_compilation_data.compiled_approval if app_compilation_data else None
183
+ )
184
+ response_data_dict["compiled_clear"] = (
185
+ app_compilation_data.compiled_clear if app_compilation_data else None
186
+ )
187
+ return SendAppUpdateFactoryTransactionResult(**response_data_dict)
188
+ case SendAppTransactionResult():
189
+ return SendAppFactoryTransactionResult(**response_data_dict)
190
+
191
+ return cls(
192
+ app=response.app,
193
+ operation_performed=response.operation_performed,
194
+ create_result=to_factory_result(
195
+ response.create_result,
196
+ deploy_params.create_params,
197
+ ),
198
+ update_result=to_factory_result(
199
+ response.update_result,
200
+ deploy_params.update_params,
201
+ ),
202
+ delete_result=to_factory_result(
203
+ response.delete_result,
204
+ deploy_params.delete_params,
205
+ ),
206
+ )
207
+
208
+
209
+ class _BareParamsBuilder:
210
+ """The bare params builder.
211
+
212
+ :param factory: The AppFactory instance.
213
+ """
214
+
215
+ def __init__(self, factory: "AppFactory") -> None:
216
+ self._factory = factory
217
+ self._algorand = factory._algorand
218
+
219
+ def create(
220
+ self, params: AppFactoryCreateParams | None = None, compilation_params: AppClientCompilationParams | None = None
221
+ ) -> AppCreateParams:
222
+ """
223
+ Create AppCreateParams using the provided parameters and compilation settings.
224
+
225
+ :param params: Optional AppFactoryCreateParams instance.
226
+ :param compilation_params: Optional AppClientCompilationParams instance.
227
+ :return: An instance of AppCreateParams.
228
+ """
229
+ base_params = params or AppFactoryCreateParams()
230
+ compiled = self._factory.compile(compilation_params)
231
+
232
+ return AppCreateParams(
233
+ **{
234
+ **{
235
+ param: value
236
+ for param, value in asdict(base_params).items()
237
+ if param in {f.name for f in dataclasses.fields(AppCreateParams)}
238
+ },
239
+ "approval_program": compiled.approval_program,
240
+ "clear_state_program": compiled.clear_state_program,
241
+ "schema": base_params.schema
242
+ or {
243
+ "global_byte_slices": self._factory._app_spec.state.schema.global_state.bytes,
244
+ "global_ints": self._factory._app_spec.state.schema.global_state.ints,
245
+ "local_byte_slices": self._factory._app_spec.state.schema.local_state.bytes,
246
+ "local_ints": self._factory._app_spec.state.schema.local_state.ints,
247
+ },
248
+ "sender": self._factory._get_sender(base_params.sender),
249
+ "signer": self._factory._get_signer(base_params.sender, base_params.signer),
250
+ "on_complete": base_params.on_complete or OnComplete.NoOpOC,
251
+ }
252
+ )
253
+
254
+ def deploy_update(self, params: AppClientBareCallParams | None = None) -> AppUpdateParams:
255
+ """
256
+ Create AppUpdateParams for an update operation.
257
+
258
+ :param params: Optional AppClientBareCallParams instance.
259
+ :return: An instance of AppUpdateParams.
260
+ """
261
+ return AppUpdateParams(
262
+ **{
263
+ **{
264
+ param: value
265
+ for param, value in asdict(params or AppClientBareCallParams()).items()
266
+ if param in {f.name for f in dataclasses.fields(AppUpdateParams)}
267
+ },
268
+ "app_id": 0,
269
+ "approval_program": "",
270
+ "clear_state_program": "",
271
+ "sender": self._factory._get_sender(params.sender if params else None),
272
+ "on_complete": OnComplete.UpdateApplicationOC,
273
+ "signer": self._factory._get_signer(
274
+ params.sender if params else None, params.signer if params else None
275
+ ),
276
+ }
277
+ )
278
+
279
+ def deploy_delete(self, params: AppClientBareCallParams | None = None) -> AppDeleteParams:
280
+ """
281
+ Create AppDeleteParams for a delete operation.
282
+
283
+ :param params: Optional AppClientBareCallParams instance.
284
+ :return: An instance of AppDeleteParams.
285
+ """
286
+ return AppDeleteParams(
287
+ **{
288
+ **{
289
+ param: value
290
+ for param, value in asdict(params or AppClientBareCallParams()).items()
291
+ if param in {f.name for f in dataclasses.fields(AppDeleteParams)}
292
+ },
293
+ "app_id": 0,
294
+ "sender": self._factory._get_sender(params.sender if params else None),
295
+ "signer": self._factory._get_signer(
296
+ params.sender if params else None, params.signer if params else None
297
+ ),
298
+ "on_complete": OnComplete.DeleteApplicationOC,
299
+ }
300
+ )
301
+
302
+
303
+ class _MethodParamsBuilder:
304
+ """The method params builder.
305
+
306
+ :param factory: The AppFactory instance.
307
+ """
308
+
309
+ def __init__(self, factory: "AppFactory") -> None:
310
+ self._factory = factory
311
+ self._bare = _BareParamsBuilder(factory)
312
+
313
+ @property
314
+ def bare(self) -> _BareParamsBuilder:
315
+ """
316
+ Get the bare parameters builder.
317
+
318
+ :return: The _BareParamsBuilder instance.
319
+ """
320
+ return self._bare
321
+
322
+ def create(
323
+ self, params: AppFactoryCreateMethodCallParams, compilation_params: AppClientCompilationParams | None = None
324
+ ) -> AppCreateMethodCallParams:
325
+ """
326
+ Create AppCreateMethodCallParams using the provided parameters and compilation settings.
327
+
328
+ :param params: AppFactoryCreateMethodCallParams instance.
329
+ :param compilation_params: Optional AppClientCompilationParams instance.
330
+ :return: An instance of AppCreateMethodCallParams.
331
+ """
332
+ compiled = self._factory.compile(compilation_params)
333
+
334
+ return AppCreateMethodCallParams(
335
+ **{
336
+ **{
337
+ param: value
338
+ for param, value in asdict(params).items()
339
+ if param in {f.name for f in dataclasses.fields(AppCreateMethodCallParams)}
340
+ },
341
+ "app_id": 0,
342
+ "approval_program": compiled.approval_program,
343
+ "clear_state_program": compiled.clear_state_program,
344
+ "schema": params.schema
345
+ or {
346
+ "global_byte_slices": self._factory._app_spec.state.schema.global_state.bytes,
347
+ "global_ints": self._factory._app_spec.state.schema.global_state.ints,
348
+ "local_byte_slices": self._factory._app_spec.state.schema.local_state.bytes,
349
+ "local_ints": self._factory._app_spec.state.schema.local_state.ints,
350
+ },
351
+ "sender": self._factory._get_sender(params.sender),
352
+ "signer": self._factory._get_signer(
353
+ params.sender if params else None, params.signer if params else None
354
+ ),
355
+ "method": self._factory._app_spec.get_arc56_method(params.method).to_abi_method(),
356
+ "args": self._factory._get_create_abi_args_with_default_values(params.method, params.args),
357
+ "on_complete": params.on_complete or OnComplete.NoOpOC,
358
+ }
359
+ )
360
+
361
+ def deploy_update(self, params: AppClientMethodCallParams) -> AppUpdateMethodCallParams:
362
+ """
363
+ Create AppUpdateMethodCallParams for an update operation.
364
+
365
+ :param params: AppClientMethodCallParams instance.
366
+ :return: An instance of AppUpdateMethodCallParams.
367
+ """
368
+ return AppUpdateMethodCallParams(
369
+ **{
370
+ **{
371
+ param: value
372
+ for param, value in asdict(params).items()
373
+ if param in {f.name for f in dataclasses.fields(AppUpdateMethodCallParams)}
374
+ },
375
+ "app_id": 0,
376
+ "approval_program": "",
377
+ "clear_state_program": "",
378
+ "sender": self._factory._get_sender(params.sender),
379
+ "signer": self._factory._get_signer(
380
+ params.sender if params else None, params.signer if params else None
381
+ ),
382
+ "method": self._factory._app_spec.get_arc56_method(params.method).to_abi_method(),
383
+ "args": self._factory._get_create_abi_args_with_default_values(params.method, params.args),
384
+ "on_complete": OnComplete.UpdateApplicationOC,
385
+ }
386
+ )
387
+
388
+ def deploy_delete(self, params: AppClientMethodCallParams) -> AppDeleteMethodCallParams:
389
+ """
390
+ Create AppDeleteMethodCallParams for a delete operation.
391
+
392
+ :param params: AppClientMethodCallParams instance.
393
+ :return: An instance of AppDeleteMethodCallParams.
394
+ """
395
+ return AppDeleteMethodCallParams(
396
+ **{
397
+ **{
398
+ param: value
399
+ for param, value in asdict(params).items()
400
+ if param in {f.name for f in dataclasses.fields(AppDeleteMethodCallParams)}
401
+ },
402
+ "app_id": 0,
403
+ "sender": self._factory._get_sender(params.sender),
404
+ "signer": self._factory._get_signer(
405
+ params.sender if params else None, params.signer if params else None
406
+ ),
407
+ "method": self._factory.app_spec.get_arc56_method(params.method).to_abi_method(),
408
+ "args": self._factory._get_create_abi_args_with_default_values(params.method, params.args),
409
+ "on_complete": OnComplete.DeleteApplicationOC,
410
+ }
411
+ )
412
+
413
+
414
+ class _AppFactoryBareCreateTransactionAccessor:
415
+ """Initialize the bare create transaction accessor.
416
+
417
+ :param factory: The AppFactory instance.
418
+ """
419
+
420
+ def __init__(self, factory: "AppFactory") -> None:
421
+ self._factory = factory
422
+
423
+ def create(self, params: AppFactoryCreateParams | None = None) -> Transaction:
424
+ """
425
+ Create a transaction for app creation.
426
+
427
+ :param params: Optional AppFactoryCreateParams instance.
428
+ :return: A Transaction instance.
429
+ """
430
+ return self._factory._algorand.create_transaction.app_create(self._factory.params.bare.create(params))
431
+
432
+
433
+ class _TransactionCreator:
434
+ """
435
+ The transaction creator.
436
+
437
+ :param factory: The AppFactory instance.
438
+ """
439
+
440
+ def __init__(self, factory: "AppFactory") -> None:
441
+ self._factory = factory
442
+ self._bare = _AppFactoryBareCreateTransactionAccessor(factory)
443
+
444
+ @property
445
+ def bare(self) -> _AppFactoryBareCreateTransactionAccessor:
446
+ """
447
+ Get the bare create transaction accessor.
448
+
449
+ :return: The _AppFactoryBareCreateTransactionAccessor instance.
450
+ """
451
+ return self._bare
452
+
453
+ def create(self, params: AppFactoryCreateMethodCallParams) -> BuiltTransactions:
454
+ """
455
+ Create built transactions for an app method call.
456
+
457
+ :param params: AppFactoryCreateMethodCallParams instance.
458
+ :return: A BuiltTransactions instance.
459
+ """
460
+ return self._factory._algorand.create_transaction.app_create_method_call(self._factory.params.create(params))
461
+
462
+
463
+ class _AppFactoryBareSendAccessor:
464
+ """
465
+ The bare send accessor.
466
+
467
+ :param factory: The AppFactory instance.
468
+ """
469
+
470
+ def __init__(self, factory: "AppFactory") -> None:
471
+ self._factory = factory
472
+ self._algorand = factory._algorand
473
+
474
+ def create(
475
+ self,
476
+ params: AppFactoryCreateParams | None = None,
477
+ send_params: SendParams | None = None,
478
+ compilation_params: AppClientCompilationParams | None = None,
479
+ ) -> tuple[AppClient, SendAppCreateTransactionResult]:
480
+ """
481
+ Send an app creation transaction and return the app client along with the transaction result.
482
+
483
+ :param params: Optional AppFactoryCreateParams instance.
484
+ :param send_params: Optional SendParams instance.
485
+ :param compilation_params: Optional AppClientCompilationParams instance.
486
+ :return: A tuple containing the AppClient and SendAppCreateTransactionResult.
487
+ """
488
+ compilation_params = compilation_params or AppClientCompilationParams()
489
+ compilation_params["updatable"] = (
490
+ compilation_params.get("updatable")
491
+ if compilation_params.get("updatable") is not None
492
+ else self._factory._updatable
493
+ )
494
+ compilation_params["deletable"] = (
495
+ compilation_params.get("deletable")
496
+ if compilation_params.get("deletable") is not None
497
+ else self._factory._deletable
498
+ )
499
+ compilation_params["deploy_time_params"] = (
500
+ compilation_params.get("deploy_time_params")
501
+ if compilation_params.get("deploy_time_params") is not None
502
+ else self._factory._deploy_time_params
503
+ )
504
+
505
+ compiled = self._factory.compile(compilation_params)
506
+
507
+ result = self._factory._handle_call_errors(
508
+ lambda: self._algorand.send.app_create(
509
+ self._factory.params.bare.create(params, compilation_params), send_params
510
+ )
511
+ )
512
+
513
+ return (
514
+ self._factory.get_app_client_by_id(
515
+ app_id=result.app_id,
516
+ ),
517
+ SendAppCreateTransactionResult[ABIReturn](
518
+ transaction=result.transaction,
519
+ confirmation=result.confirmation,
520
+ app_id=result.app_id,
521
+ app_address=result.app_address,
522
+ compiled_approval=compiled.compiled_approval if compiled else None,
523
+ compiled_clear=compiled.compiled_clear if compiled else None,
524
+ group_id=result.group_id,
525
+ tx_ids=result.tx_ids,
526
+ transactions=result.transactions,
527
+ confirmations=result.confirmations,
528
+ ),
529
+ )
530
+
531
+
532
+ class _TransactionSender:
533
+ """
534
+ The transaction sender.
535
+
536
+ :param factory: The AppFactory instance.
537
+ """
538
+
539
+ def __init__(self, factory: "AppFactory") -> None:
540
+ self._factory = factory
541
+ self._algorand = factory._algorand
542
+ self._bare = _AppFactoryBareSendAccessor(factory)
543
+
544
+ @property
545
+ def bare(self) -> _AppFactoryBareSendAccessor:
546
+ """
547
+ Get the bare send accessor.
548
+
549
+ :return: The _AppFactoryBareSendAccessor instance.
550
+ """
551
+ return self._bare
552
+
553
+ def create(
554
+ self,
555
+ params: AppFactoryCreateMethodCallParams,
556
+ send_params: SendParams | None = None,
557
+ compilation_params: AppClientCompilationParams | None = None,
558
+ ) -> tuple[AppClient, AppFactoryCreateMethodCallResult[Arc56ReturnValueType]]:
559
+ """
560
+ Send an app creation method call and return the app client along with the method call result.
561
+
562
+ :param params: AppFactoryCreateMethodCallParams instance.
563
+ :param send_params: Optional SendParams instance.
564
+ :param compilation_params: Optional AppClientCompilationParams instance.
565
+ :return: A tuple containing the AppClient and AppFactoryCreateMethodCallResult.
566
+ """
567
+ compilation_params = compilation_params or AppClientCompilationParams()
568
+ compilation_params["updatable"] = (
569
+ compilation_params.get("updatable")
570
+ if compilation_params.get("updatable") is not None
571
+ else self._factory._updatable
572
+ )
573
+ compilation_params["deletable"] = (
574
+ compilation_params.get("deletable")
575
+ if compilation_params.get("deletable") is not None
576
+ else self._factory._deletable
577
+ )
578
+ compilation_params["deploy_time_params"] = (
579
+ compilation_params.get("deploy_time_params")
580
+ if compilation_params.get("deploy_time_params") is not None
581
+ else self._factory._deploy_time_params
582
+ )
583
+
584
+ compiled = self._factory.compile(compilation_params)
585
+ result = self._factory._handle_call_errors(
586
+ lambda: self._factory._parse_method_call_return(
587
+ lambda: self._algorand.send.app_create_method_call(
588
+ self._factory.params.create(params, compilation_params), send_params
589
+ ),
590
+ self._factory._app_spec.get_arc56_method(params.method),
591
+ )
592
+ )
593
+
594
+ return (
595
+ self._factory.get_app_client_by_id(
596
+ app_id=result.app_id,
597
+ ),
598
+ AppFactoryCreateMethodCallResult[Arc56ReturnValueType](
599
+ transaction=result.transaction,
600
+ confirmation=result.confirmation,
601
+ tx_id=result.tx_id,
602
+ app_id=result.app_id,
603
+ app_address=result.app_address,
604
+ abi_return=result.abi_return,
605
+ compiled_approval=compiled.compiled_approval if compiled else None,
606
+ compiled_clear=compiled.compiled_clear if compiled else None,
607
+ group_id=result.group_id,
608
+ tx_ids=result.tx_ids,
609
+ transactions=result.transactions,
610
+ confirmations=result.confirmations,
611
+ returns=result.returns,
612
+ ),
613
+ )
614
+
615
+
616
+ class AppFactory:
617
+ """ARC-56/ARC-32 app factory that, for a given app spec, allows you to create
618
+ and deploy one or more app instances and to create one or more app clients
619
+ to interact with those (or other) app instances.
620
+
621
+ :param params: The parameters for the factory
622
+
623
+ :example:
624
+ >>> factory = AppFactory(AppFactoryParams(
625
+ >>> algorand=AlgorandClient.mainnet(),
626
+ >>> app_spec=app_spec,
627
+ >>> )
628
+ >>> )
629
+ """
630
+
631
+ def __init__(self, params: AppFactoryParams) -> None:
632
+ self._app_spec = AppClient.normalise_app_spec(params.app_spec)
633
+ self._app_name = params.app_name or self._app_spec.name
634
+ self._algorand = params.algorand
635
+ self._version = params.version or "1.0"
636
+ self._default_sender = params.default_sender
637
+ self._default_signer = params.default_signer
638
+ self._approval_source_map: SourceMap | None = None
639
+ self._clear_source_map: SourceMap | None = None
640
+ self._params_accessor = _MethodParamsBuilder(self)
641
+ self._send_accessor = _TransactionSender(self)
642
+ self._create_transaction_accessor = _TransactionCreator(self)
643
+
644
+ compilation_params = params.compilation_params or AppClientCompilationParams()
645
+ self._deploy_time_params = compilation_params.get("deploy_time_params")
646
+ self._updatable = compilation_params.get("updatable")
647
+ self._deletable = compilation_params.get("deletable")
648
+
649
+ @property
650
+ def app_name(self) -> str:
651
+ """The name of the app"""
652
+ return self._app_name
653
+
654
+ @property
655
+ def app_spec(self) -> Arc56Contract:
656
+ """The app spec"""
657
+ return self._app_spec
658
+
659
+ @property
660
+ def algorand(self) -> AlgorandClient:
661
+ """The algorand client"""
662
+ return self._algorand
663
+
664
+ @property
665
+ def params(self) -> _MethodParamsBuilder:
666
+ """Get parameters to create transactions (create and deploy related calls) for the current app.
667
+
668
+ A good mental model for this is that these parameters represent a deferred transaction creation.
669
+
670
+ :example: Create a transaction in the future using Algorand Client
671
+ >>> create_app_params = app_factory.params.create(
672
+ ... AppFactoryCreateMethodCallParams(
673
+ ... method='create_method',
674
+ ... args=[123, 'hello']
675
+ ... )
676
+ ... )
677
+ >>> # ...
678
+ >>> algorand.send.app_create_method_call(create_app_params)
679
+
680
+ :example: Define a nested transaction as an ABI argument
681
+ >>> create_app_params = appFactory.params.create(
682
+ ... AppFactoryCreateMethodCallParams(
683
+ ... method='create_method',
684
+ ... args=[123, 'hello']
685
+ ... )
686
+ ... )
687
+ >>> app_client.send.call(
688
+ ... AppClientMethodCallParams(
689
+ ... method='my_method',
690
+ ... args=[create_app_params]
691
+ ... )
692
+ ... )
693
+ """
694
+ return self._params_accessor
695
+
696
+ @property
697
+ def send(self) -> _TransactionSender:
698
+ """
699
+ Get the transaction sender.
700
+
701
+ :return: The _TransactionSender instance.
702
+ """
703
+ return self._send_accessor
704
+
705
+ @property
706
+ def create_transaction(self) -> _TransactionCreator:
707
+ """
708
+ Get the transaction creator.
709
+
710
+ :return: The _TransactionCreator instance.
711
+ """
712
+ return self._create_transaction_accessor
713
+
714
+ def deploy(
715
+ self,
716
+ *,
717
+ on_update: OnUpdate | None = None,
718
+ on_schema_break: OnSchemaBreak | None = None,
719
+ create_params: AppClientMethodCallCreateParams | AppClientBareCallCreateParams | None = None,
720
+ update_params: AppClientMethodCallParams | AppClientBareCallParams | None = None,
721
+ delete_params: AppClientMethodCallParams | AppClientBareCallParams | None = None,
722
+ existing_deployments: ApplicationLookup | None = None,
723
+ ignore_cache: bool = False,
724
+ app_name: str | None = None,
725
+ send_params: SendParams | None = None,
726
+ compilation_params: AppClientCompilationParams | None = None,
727
+ ) -> tuple[AppClient, AppFactoryDeployResult]:
728
+ """Idempotently deploy (create if not exists, update if changed) an app against the given name for the given
729
+ creator account, including deploy-time TEAL template placeholder substitutions (if specified).
730
+
731
+ **Note:** When using the return from this function be sure to check `operationPerformed` to get access to
732
+ various return properties like `transaction`, `confirmation` and `deleteResult`.
733
+
734
+ **Note:** if there is a breaking state schema change to an existing app (and `onSchemaBreak` is set to
735
+ `'replace'`) the existing app will be deleted and re-created.
736
+
737
+ **Note:** if there is an update (different TEAL code) to an existing app (and `onUpdate` is set to
738
+ `'replace'`) the existing app will be deleted and re-created.
739
+
740
+ :param on_update: The action to take if there is an update to the app
741
+ :param on_schema_break: The action to take if there is a breaking state schema change to the app
742
+ :param create_params: The arguments to create the app
743
+ :param update_params: The arguments to update the app
744
+ :param delete_params: The arguments to delete the app
745
+ :param existing_deployments: The existing deployments to use
746
+ :param ignore_cache: Whether to ignore the cache
747
+ :param app_name: The name of the app
748
+ :param send_params: The parameters for the send call
749
+ :param compilation_params: The parameters for the compilation
750
+ :returns: The app client and the result of the deployment
751
+
752
+ :example:
753
+ >>> app_client, result = factory.deploy({
754
+ >>> create_params=AppClientMethodCallCreateParams(
755
+ >>> sender='SENDER_ADDRESS',
756
+ >>> approval_program='APPROVAL PROGRAM',
757
+ >>> clear_state_program='CLEAR PROGRAM',
758
+ >>> schema={
759
+ >>> "global_byte_slices": 0,
760
+ >>> "global_ints": 0,
761
+ >>> "local_byte_slices": 0,
762
+ >>> "local_ints": 0
763
+ >>> }
764
+ >>> ),
765
+ >>> update_params=AppClientMethodCallParams(
766
+ >>> sender='SENDER_ADDRESS'
767
+ >>> ),
768
+ >>> delete_params=AppClientMethodCallParams(
769
+ >>> sender='SENDER_ADDRESS'
770
+ >>> ),
771
+ >>> compilation_params=AppClientCompilationParams(
772
+ >>> updatable=False,
773
+ >>> deletable=False
774
+ >>> ),
775
+ >>> app_name='my_app',
776
+ >>> on_schema_break=OnSchemaBreak.AppendApp,
777
+ >>> on_update=OnUpdate.AppendApp
778
+ >>> })
779
+ """
780
+ # Resolve control parameters with factory defaults
781
+ send_params = send_params or SendParams()
782
+ compilation_params = compilation_params or AppClientCompilationParams()
783
+ resolved_updatable = (
784
+ upd
785
+ if (upd := compilation_params.get("updatable")) is not None
786
+ else self._updatable or self._get_deploy_time_control("updatable")
787
+ )
788
+ resolved_deletable = (
789
+ dlb
790
+ if (dlb := compilation_params.get("deletable")) is not None
791
+ else self._deletable or self._get_deploy_time_control("deletable")
792
+ )
793
+ resolved_deploy_time_params = compilation_params.get("deploy_time_params") or self._deploy_time_params
794
+
795
+ def prepare_create_args() -> AppCreateMethodCallParams | AppCreateParams:
796
+ """Prepare create arguments based on parameter type."""
797
+ if create_params and isinstance(create_params, AppClientMethodCallCreateParams):
798
+ return self.params.create(
799
+ AppFactoryCreateMethodCallParams(
800
+ **asdict(create_params),
801
+ ),
802
+ compilation_params={
803
+ "updatable": resolved_updatable,
804
+ "deletable": resolved_deletable,
805
+ "deploy_time_params": resolved_deploy_time_params,
806
+ },
807
+ )
808
+
809
+ base_params = create_params or AppClientBareCallCreateParams()
810
+ return self.params.bare.create(
811
+ AppFactoryCreateParams(
812
+ **asdict(base_params) if base_params else {},
813
+ ),
814
+ compilation_params={
815
+ "updatable": resolved_updatable,
816
+ "deletable": resolved_deletable,
817
+ "deploy_time_params": resolved_deploy_time_params,
818
+ },
819
+ )
820
+
821
+ def prepare_update_args() -> AppUpdateMethodCallParams | AppUpdateParams:
822
+ """Prepare update arguments based on parameter type."""
823
+ return (
824
+ self.params.deploy_update(update_params)
825
+ if isinstance(update_params, AppClientMethodCallParams)
826
+ else self.params.bare.deploy_update(update_params)
827
+ )
828
+
829
+ def prepare_delete_args() -> AppDeleteMethodCallParams | AppDeleteParams:
830
+ """Prepare delete arguments based on parameter type."""
831
+ return (
832
+ self.params.deploy_delete(delete_params)
833
+ if isinstance(delete_params, AppClientMethodCallParams)
834
+ else self.params.bare.deploy_delete(delete_params)
835
+ )
836
+
837
+ # Execute deployment
838
+ deploy_params = AppDeployParams(
839
+ deploy_time_params=resolved_deploy_time_params,
840
+ on_schema_break=on_schema_break,
841
+ on_update=on_update,
842
+ existing_deployments=existing_deployments,
843
+ ignore_cache=ignore_cache,
844
+ create_params=prepare_create_args(),
845
+ update_params=prepare_update_args(),
846
+ delete_params=prepare_delete_args(),
847
+ metadata=AppDeploymentMetaData(
848
+ name=app_name or self._app_name,
849
+ version=self._version,
850
+ updatable=resolved_updatable,
851
+ deletable=resolved_deletable,
852
+ ),
853
+ send_params=send_params,
854
+ )
855
+ deploy_result = self._algorand.app_deployer.deploy(deploy_params)
856
+
857
+ # Prepare app client and factory deploy response
858
+ app_client = self.get_app_client_by_id(
859
+ app_id=deploy_result.app.app_id,
860
+ app_name=app_name,
861
+ default_sender=self._default_sender,
862
+ default_signer=self._default_signer,
863
+ )
864
+ factory_deploy_result = AppFactoryDeployResult.from_deploy_result(
865
+ response=deploy_result,
866
+ deploy_params=deploy_params,
867
+ app_spec=app_client.app_spec,
868
+ app_compilation_data=self.compile(
869
+ AppClientCompilationParams(
870
+ deploy_time_params=resolved_deploy_time_params,
871
+ updatable=resolved_updatable,
872
+ deletable=resolved_deletable,
873
+ )
874
+ ),
875
+ )
876
+
877
+ return app_client, factory_deploy_result
878
+
879
+ def get_app_client_by_id(
880
+ self,
881
+ app_id: int,
882
+ app_name: str | None = None,
883
+ default_sender: str | None = None, # Address can be string or bytes
884
+ default_signer: TransactionSigner | None = None,
885
+ approval_source_map: SourceMap | None = None,
886
+ clear_source_map: SourceMap | None = None,
887
+ ) -> AppClient:
888
+ """Returns a new `AppClient` client for an app instance of the given ID.
889
+
890
+ :param app_id: The id of the app
891
+ :param app_name: The name of the app
892
+ :param default_sender: The default sender address
893
+ :param default_signer: The default signer
894
+ :param approval_source_map: The approval source map
895
+ :param clear_source_map: The clear source map
896
+ :return AppClient: The app client
897
+
898
+ :example:
899
+ >>> app_client = factory.get_app_client_by_id(app_id=123)
900
+ """
901
+ return AppClient(
902
+ AppClientParams(
903
+ app_id=app_id,
904
+ algorand=self._algorand,
905
+ app_spec=self._app_spec,
906
+ app_name=app_name or self._app_name,
907
+ default_sender=default_sender or self._default_sender,
908
+ default_signer=default_signer or self._default_signer,
909
+ approval_source_map=approval_source_map or self._approval_source_map,
910
+ clear_source_map=clear_source_map or self._clear_source_map,
911
+ )
912
+ )
913
+
914
+ def get_app_client_by_creator_and_name(
915
+ self,
916
+ creator_address: str,
917
+ app_name: str,
918
+ default_sender: str | None = None,
919
+ default_signer: TransactionSigner | None = None,
920
+ ignore_cache: bool | None = None,
921
+ app_lookup_cache: ApplicationLookup | None = None,
922
+ approval_source_map: SourceMap | None = None,
923
+ clear_source_map: SourceMap | None = None,
924
+ ) -> AppClient:
925
+ """Returns a new `AppClient` client, resolving the app by creator address and name
926
+ using AlgoKit app deployment semantics (i.e. looking for the app creation transaction note).
927
+
928
+ :param creator_address: The creator address
929
+ :param app_name: The name of the app
930
+ :param default_sender: The default sender address
931
+ :param default_signer: The default signer
932
+ :param ignore_cache: Whether to ignore the cache and force a lookup
933
+ :param app_lookup_cache: Optional cache of existing app deployments to use instead of querying the indexer
934
+ :param approval_source_map: Optional source map for the approval program
935
+ :param clear_source_map: Optional source map for the clear state program
936
+ :return: An AppClient instance configured for the resolved application
937
+
938
+ :example:
939
+ >>> app_client = factory.get_app_client_by_creator_and_name(
940
+ ... creator_address='SENDER_ADDRESS',
941
+ ... app_name='my_app'
942
+ ... )
943
+ """
944
+ return AppClient.from_creator_and_name(
945
+ creator_address=creator_address,
946
+ app_name=app_name or self._app_name,
947
+ default_sender=default_sender or self._default_sender,
948
+ default_signer=default_signer or self._default_signer,
949
+ approval_source_map=approval_source_map or self._approval_source_map,
950
+ clear_source_map=clear_source_map or self._clear_source_map,
951
+ ignore_cache=ignore_cache,
952
+ app_lookup_cache=app_lookup_cache,
953
+ app_spec=self._app_spec,
954
+ algorand=self._algorand,
955
+ )
956
+
957
+ def export_source_maps(self) -> AppSourceMaps:
958
+ if not self._approval_source_map or not self._clear_source_map:
959
+ raise ValueError(
960
+ "Unable to export source maps; they haven't been loaded into this client - "
961
+ "you need to call create, update, or deploy first"
962
+ )
963
+ return AppSourceMaps(
964
+ approval_source_map=self._approval_source_map,
965
+ clear_source_map=self._clear_source_map,
966
+ )
967
+
968
+ def import_source_maps(self, source_maps: AppSourceMaps) -> None:
969
+ """
970
+ Import the provided source maps into the factory.
971
+
972
+ :param source_maps: An AppSourceMaps instance containing the approval and clear source maps.
973
+ """
974
+ self._approval_source_map = source_maps.approval_source_map
975
+ self._clear_source_map = source_maps.clear_source_map
976
+
977
+ def compile(self, compilation_params: AppClientCompilationParams | None = None) -> AppClientCompilationResult:
978
+ """Compile the app's TEAL code.
979
+
980
+ :param compilation_params: The compilation parameters
981
+ :return AppClientCompilationResult: The compilation result
982
+
983
+ :example:
984
+ >>> compilation_result = factory.compile()
985
+ """
986
+ compilation = compilation_params or AppClientCompilationParams()
987
+ result = AppClient.compile(
988
+ app_spec=self._app_spec,
989
+ app_manager=self._algorand.app,
990
+ compilation_params=compilation,
991
+ )
992
+
993
+ if result.compiled_approval:
994
+ self._approval_source_map = result.compiled_approval.source_map
995
+ if result.compiled_clear:
996
+ self._clear_source_map = result.compiled_clear.source_map
997
+
998
+ return result
999
+
1000
+ def _expose_logic_error(self, e: Exception, is_clear_state_program: bool = False) -> Exception: # noqa: FBT002 FBT001
1001
+ """
1002
+ Convert a low-level exception into a descriptive logic error.
1003
+
1004
+ :param e: The original exception.
1005
+ :param is_clear_state_program: Flag indicating if the error is related to the clear state program.
1006
+ :return: The transformed exception.
1007
+ """
1008
+ return AppClient._expose_logic_error_static(
1009
+ e=e,
1010
+ app_spec=self._app_spec,
1011
+ is_clear_state_program=is_clear_state_program,
1012
+ approval_source_map=self._approval_source_map,
1013
+ clear_source_map=self._clear_source_map,
1014
+ program=None,
1015
+ approval_source_info=(self._app_spec.source_info.approval if self._app_spec.source_info else None),
1016
+ clear_source_info=(self._app_spec.source_info.clear if self._app_spec.source_info else None),
1017
+ )
1018
+
1019
+ def _get_deploy_time_control(self, control: str) -> bool | None:
1020
+ """
1021
+ Determine the deploy time control flag for the specified control type.
1022
+
1023
+ :param control: The control type ('updatable' or 'deletable').
1024
+ :return: A boolean flag or None if not determinable.
1025
+ """
1026
+ approval = self._app_spec.source.get_decoded_approval() if self._app_spec.source else None
1027
+
1028
+ template_name = UPDATABLE_TEMPLATE_NAME if control == "updatable" else DELETABLE_TEMPLATE_NAME
1029
+ if not approval or template_name not in approval:
1030
+ return None
1031
+
1032
+ on_complete = "UpdateApplication" if control == "updatable" else "DeleteApplication"
1033
+ return on_complete in self._app_spec.bare_actions.call or any(
1034
+ on_complete in m.actions.call for m in self._app_spec.methods if m.actions and m.actions.call
1035
+ )
1036
+
1037
+ def _get_sender(self, sender: str | None) -> str:
1038
+ """
1039
+ Retrieve the sender address.
1040
+
1041
+ :param sender: The specified sender address.
1042
+ :return: The sender address.
1043
+ :raises Exception: If no sender is provided and no default sender is set.
1044
+ """
1045
+ if not sender and not self._default_sender:
1046
+ raise Exception(
1047
+ f"No sender provided and no default sender present in app client for call to app {self._app_name}"
1048
+ )
1049
+ return str(sender or self._default_sender)
1050
+
1051
+ def _get_signer(self, sender: str | None, signer: TransactionSigner | None) -> TransactionSigner | None:
1052
+ """
1053
+ Retrieve the transaction signer.
1054
+
1055
+ :param sender: The sender address.
1056
+ :param signer: The provided signer.
1057
+ :return: The transaction signer if available.
1058
+ """
1059
+ return signer or (self._default_signer if not sender or sender == self._default_sender else None)
1060
+
1061
+ def _handle_call_errors(self, call: Callable[[], T]) -> T:
1062
+ try:
1063
+ return call()
1064
+ except Exception as e:
1065
+ raise self._expose_logic_error(e) from None
1066
+
1067
+ def _parse_method_call_return(
1068
+ self,
1069
+ result: Callable[
1070
+ [], SendAppTransactionResult | SendAppCreateTransactionResult | SendAppUpdateTransactionResult
1071
+ ],
1072
+ method: Method,
1073
+ ) -> AppFactoryCreateMethodCallResult[Arc56ReturnValueType]:
1074
+ """
1075
+ Parse the method call return value and convert the ABI return.
1076
+
1077
+ :param result: A callable that returns the transaction result.
1078
+ :param method: The ABI method associated with the call.
1079
+ :return: An AppFactoryCreateMethodCallResult with the parsed ABI return.
1080
+ """
1081
+ result_value = result()
1082
+ return AppFactoryCreateMethodCallResult[Arc56ReturnValueType](
1083
+ **{
1084
+ **result_value.__dict__,
1085
+ "abi_return": result_value.abi_return.get_arc56_value(method, self._app_spec.structs)
1086
+ if isinstance(result_value.abi_return, ABIReturn)
1087
+ else None,
1088
+ }
1089
+ )
1090
+
1091
+ def _get_create_abi_args_with_default_values(
1092
+ self,
1093
+ method_name_or_signature: str,
1094
+ user_args: Sequence[Any] | None,
1095
+ ) -> list[Any]:
1096
+ """
1097
+ Builds a list of ABI argument values for creation calls, applying default
1098
+ argument values when not provided.
1099
+ """
1100
+ method = self._app_spec.get_arc56_method(method_name_or_signature)
1101
+
1102
+ results: list[Any] = []
1103
+
1104
+ for i, param in enumerate(method.args):
1105
+ if user_args and i < len(user_args):
1106
+ arg_value = user_args[i]
1107
+ if param.struct and isinstance(arg_value, dict):
1108
+ arg_value = get_abi_tuple_from_abi_struct(
1109
+ arg_value,
1110
+ self._app_spec.structs[param.struct],
1111
+ self._app_spec.structs,
1112
+ )
1113
+ results.append(arg_value)
1114
+ continue
1115
+
1116
+ default_value = getattr(param, "default_value", None)
1117
+ if default_value:
1118
+ if default_value.source == "literal":
1119
+ raw_value = base64.b64decode(default_value.data)
1120
+ value_type = default_value.type or str(param.type)
1121
+ decoded_value = get_abi_decoded_value(raw_value, value_type, self._app_spec.structs)
1122
+ results.append(decoded_value)
1123
+ else:
1124
+ raise ValueError(
1125
+ f"Cannot provide default value from source={default_value.source} "
1126
+ "for a contract creation call."
1127
+ )
1128
+ else:
1129
+ param_name = param.name or f"arg{i + 1}"
1130
+ raise ValueError(
1131
+ f"No value provided for required argument {param_name} " f"in call to method {method.name}"
1132
+ )
1133
+
1134
+ return results