algorand-python-testing 0.0.0b1__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.
Files changed (52) hide show
  1. algopy/__init__.py +58 -0
  2. algopy/arc4.py +1 -0
  3. algopy/gtxn.py +1 -0
  4. algopy/itxn.py +1 -0
  5. algopy/op.py +1 -0
  6. algopy/py.typed +0 -0
  7. algopy_testing/__init__.py +55 -0
  8. algopy_testing/arc4.py +1533 -0
  9. algopy_testing/constants.py +22 -0
  10. algopy_testing/context.py +1194 -0
  11. algopy_testing/decorators/__init__.py +0 -0
  12. algopy_testing/decorators/abimethod.py +204 -0
  13. algopy_testing/decorators/baremethod.py +83 -0
  14. algopy_testing/decorators/subroutine.py +9 -0
  15. algopy_testing/enums.py +42 -0
  16. algopy_testing/gtxn.py +261 -0
  17. algopy_testing/itxn.py +665 -0
  18. algopy_testing/models/__init__.py +31 -0
  19. algopy_testing/models/account.py +128 -0
  20. algopy_testing/models/application.py +72 -0
  21. algopy_testing/models/asset.py +109 -0
  22. algopy_testing/models/block.py +34 -0
  23. algopy_testing/models/box.py +158 -0
  24. algopy_testing/models/contract.py +82 -0
  25. algopy_testing/models/gitxn.py +42 -0
  26. algopy_testing/models/global_values.py +72 -0
  27. algopy_testing/models/gtxn.py +56 -0
  28. algopy_testing/models/itxn.py +85 -0
  29. algopy_testing/models/logicsig.py +44 -0
  30. algopy_testing/models/template_variable.py +23 -0
  31. algopy_testing/models/transactions.py +158 -0
  32. algopy_testing/models/txn.py +113 -0
  33. algopy_testing/models/unsigned_builtins.py +36 -0
  34. algopy_testing/op.py +1098 -0
  35. algopy_testing/primitives/__init__.py +6 -0
  36. algopy_testing/primitives/biguint.py +148 -0
  37. algopy_testing/primitives/bytes.py +174 -0
  38. algopy_testing/primitives/string.py +68 -0
  39. algopy_testing/primitives/uint64.py +213 -0
  40. algopy_testing/protocols.py +18 -0
  41. algopy_testing/py.typed +0 -0
  42. algopy_testing/state/__init__.py +4 -0
  43. algopy_testing/state/global_state.py +73 -0
  44. algopy_testing/state/local_state.py +54 -0
  45. algopy_testing/utilities/__init__.py +3 -0
  46. algopy_testing/utilities/budget.py +23 -0
  47. algopy_testing/utilities/log.py +55 -0
  48. algopy_testing/utils.py +249 -0
  49. algorand_python_testing-0.0.0b1.dist-info/METADATA +81 -0
  50. algorand_python_testing-0.0.0b1.dist-info/RECORD +52 -0
  51. algorand_python_testing-0.0.0b1.dist-info/WHEEL +4 -0
  52. algorand_python_testing-0.0.0b1.dist-info/licenses/LICENSE +14 -0
algopy_testing/itxn.py ADDED
@@ -0,0 +1,665 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ import typing
5
+ from copy import deepcopy
6
+
7
+ import algosdk
8
+
9
+ from algopy_testing.constants import MAX_UINT64
10
+ from algopy_testing.context import get_test_context
11
+ from algopy_testing.models.transactions import (
12
+ ApplicationCallFields,
13
+ AssetConfigFields,
14
+ AssetFreezeFields,
15
+ AssetTransferFields,
16
+ KeyRegistrationFields,
17
+ PaymentFields,
18
+ _ApplicationCallFields,
19
+ _TransactionFields,
20
+ )
21
+ from algopy_testing.utils import dummy_transaction_id, txn_type_to_bytes
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+ if typing.TYPE_CHECKING:
26
+ import algopy
27
+
28
+ InnerTransactionResultType = (
29
+ algopy.itxn.InnerTransactionResult
30
+ | algopy.itxn.PaymentInnerTransaction
31
+ | algopy.itxn.KeyRegistrationInnerTransaction
32
+ | algopy.itxn.AssetConfigInnerTransaction
33
+ | algopy.itxn.AssetTransferInnerTransaction
34
+ | algopy.itxn.AssetFreezeInnerTransaction
35
+ | algopy.itxn.ApplicationCallInnerTransaction
36
+ )
37
+
38
+
39
+ def _create_inner_transaction_result( # noqa: PLR0911
40
+ txn: _BaseInnerTransaction,
41
+ ) -> InnerTransactionResultType:
42
+ import algopy
43
+
44
+ match txn:
45
+ case algopy.itxn.Payment():
46
+ return algopy.itxn.PaymentInnerTransaction(**txn.fields)
47
+ case algopy.itxn.KeyRegistration():
48
+ return algopy.itxn.KeyRegistrationInnerTransaction(**txn.fields)
49
+ case algopy.itxn.AssetConfig():
50
+ return algopy.itxn.AssetConfigInnerTransaction(**txn.fields)
51
+ case algopy.itxn.AssetTransfer():
52
+ return algopy.itxn.AssetTransferInnerTransaction(**txn.fields)
53
+ case algopy.itxn.AssetFreeze():
54
+ return algopy.itxn.AssetFreezeInnerTransaction(**txn.fields)
55
+ case algopy.itxn.ApplicationCall():
56
+ return algopy.itxn.ApplicationCallInnerTransaction(**txn.fields)
57
+ case algopy.itxn.InnerTransaction():
58
+ return algopy.itxn.InnerTransactionResult(**txn.fields)
59
+ case _:
60
+ raise ValueError(f"Invalid inner transaction type: {type(txn)}")
61
+
62
+
63
+ class _BaseInnerTransaction:
64
+ fields: dict[str, typing.Any]
65
+
66
+ def submit(self) -> typing.Any:
67
+ import algopy
68
+
69
+ context = get_test_context()
70
+
71
+ if not context:
72
+ raise RuntimeError("No test context found")
73
+
74
+ result = _create_inner_transaction_result(self)
75
+
76
+ if not result:
77
+ raise RuntimeError("Invalid inner transaction type")
78
+
79
+ # if its an asset config then ensure to create an asset and add to context
80
+ if isinstance(result, algopy.itxn.AssetConfigInnerTransaction): # type: ignore[attr-defined, unused-ignore]
81
+ # TODO: refine
82
+ created_asset = context.any_asset(
83
+ total=self.fields.get("total", None),
84
+ decimals=self.fields.get("decimals", None),
85
+ default_frozen=self.fields.get("default_frozen", None),
86
+ unit_name=self.fields.get("unit_name", None),
87
+ name=self.fields.get("asset_name", None),
88
+ url=self.fields.get("url", b""),
89
+ metadata_hash=self.fields.get("metadata_hash", ""),
90
+ manager=self.fields.get("manager", algosdk.constants.ZERO_ADDRESS),
91
+ reserve=self.fields.get("reserve", algosdk.constants.ZERO_ADDRESS),
92
+ freeze=self.fields.get("freeze", algosdk.constants.ZERO_ADDRESS),
93
+ clawback=self.fields.get("clawback", algosdk.constants.ZERO_ADDRESS),
94
+ creator=self.fields.get("creator", algosdk.constants.ZERO_ADDRESS),
95
+ )
96
+ result.fields["xfer_asset"] = created_asset
97
+
98
+ context._append_inner_transaction_group([result])
99
+ return result
100
+
101
+ def copy(self) -> typing.Self:
102
+ return deepcopy(self)
103
+
104
+ def get_field(self, type_dict: object, name: str) -> typing.Any:
105
+ if name in type_dict.__annotations__:
106
+ return self.fields.get(name)
107
+
108
+ raise AttributeError(f"{type(self).__name__!r} object has no attribute {name!r}")
109
+
110
+ def __eq__(self, other: object) -> bool:
111
+ if isinstance(other, _BaseInnerTransaction):
112
+ return bool(self.fields == other.fields)
113
+ return False
114
+
115
+ def __hash__(self) -> int:
116
+ return hash(self.fields)
117
+
118
+
119
+ class _InnerTransactionInitFields(typing.TypedDict, total=False):
120
+ type: algopy.TransactionType
121
+ ## payment
122
+ receiver: algopy.Account | str
123
+ amount: algopy.UInt64 | int
124
+ close_remainder_to: algopy.Account | str
125
+ ## key registration
126
+ vote_key: algopy.Bytes | bytes
127
+ selection_key: algopy.Bytes | bytes
128
+ vote_first: algopy.UInt64 | int
129
+ vote_last: algopy.UInt64 | int
130
+ vote_key_dilution: algopy.UInt64 | int
131
+ non_participation: algopy.UInt64 | int | bool
132
+ state_proof_key: algopy.Bytes | bytes
133
+ ## asset config
134
+ config_asset: algopy.Asset | algopy.UInt64 | int
135
+ total: algopy.UInt64 | int
136
+ unit_name: algopy.String | algopy.Bytes | str | bytes
137
+ asset_name: algopy.String | algopy.Bytes | str | bytes
138
+ decimals: algopy.UInt64 | int
139
+ default_frozen: bool
140
+ url: algopy.String | algopy.Bytes | bytes | str
141
+ metadata_hash: algopy.Bytes | bytes
142
+ manager: algopy.Account | str
143
+ reserve: algopy.Account | str
144
+ freeze: algopy.Account | str
145
+ clawback: algopy.Account | str
146
+ ## asset transfer
147
+ xfer_asset: algopy.Asset | algopy.UInt64 | int
148
+ asset_amount: algopy.UInt64 | int
149
+ asset_sender: algopy.Account | str
150
+ asset_receiver: algopy.Account | str
151
+ asset_close_to: algopy.Account | str
152
+ ## asset freeze
153
+ freeze_asset: algopy.Asset | algopy.UInt64 | int
154
+ freeze_account: algopy.Account | str
155
+ frozen: bool
156
+ ## application call
157
+ app_id: algopy.Application | algopy.UInt64 | int
158
+ approval_program: algopy.Bytes | bytes | tuple[algopy.Bytes, ...]
159
+ clear_state_program: algopy.Bytes | bytes | tuple[algopy.Bytes, ...]
160
+ on_completion: algopy.OnCompleteAction | algopy.UInt64 | int
161
+ global_num_uint: algopy.UInt64 | int
162
+ global_num_bytes: algopy.UInt64 | int
163
+ local_num_uint: algopy.UInt64 | int
164
+ local_num_bytes: algopy.UInt64 | int
165
+ extra_program_pages: algopy.UInt64 | int
166
+ app_args: tuple[algopy.Bytes, ...]
167
+ accounts: tuple[algopy.Account, ...]
168
+ assets: tuple[algopy.Asset, ...]
169
+ apps: tuple[algopy.Application, ...]
170
+ ## shared
171
+ sender: algopy.Account | str
172
+ fee: algopy.UInt64 | int
173
+ note: algopy.String | algopy.Bytes | str | bytes
174
+ rekey_to: algopy.Account | str
175
+
176
+
177
+ class InnerTransaction(_BaseInnerTransaction):
178
+ def __init__(
179
+ self,
180
+ **kwargs: typing.Unpack[_InnerTransactionInitFields],
181
+ ):
182
+ self.fields = {
183
+ "type": kwargs["type"],
184
+ "type_bytes": txn_type_to_bytes(int(kwargs["type"])),
185
+ **kwargs,
186
+ }
187
+
188
+ def set(self, **kwargs: typing.Unpack[_InnerTransactionInitFields]) -> None:
189
+ """Updates inner transaction parameter values"""
190
+ self.fields.update(kwargs)
191
+
192
+ def __getattr__(self, name: str) -> object:
193
+ return self.get_field(_InnerTransactionInitFields, name)
194
+
195
+
196
+ class _PaymentInitFields(typing.TypedDict, total=False):
197
+ receiver: algopy.Account | str
198
+ amount: algopy.UInt64 | int
199
+ close_remainder_to: algopy.Account | str
200
+ sender: algopy.Account | str
201
+ fee: algopy.UInt64 | int
202
+ note: algopy.String | algopy.Bytes | str | bytes
203
+ rekey_to: algopy.Account | str
204
+
205
+
206
+ class Payment(_BaseInnerTransaction):
207
+ def __init__(self, **kwargs: typing.Unpack[_PaymentInitFields]):
208
+ import algopy
209
+
210
+ self.fields = {**kwargs, "type": algopy.TransactionType.Payment}
211
+
212
+ def set(self, **kwargs: typing.Unpack[_PaymentInitFields]) -> None:
213
+ """Updates inner transaction parameter values"""
214
+ import algopy
215
+
216
+ self.fields.update({**kwargs, "type": algopy.TransactionType.Payment})
217
+
218
+ def __getattr__(self, name: str) -> object:
219
+ return self.get_field(_PaymentInitFields, name)
220
+
221
+
222
+ class _KeyRegistrationInitFields(typing.TypedDict, total=False):
223
+ vote_key: algopy.Bytes | bytes
224
+ selection_key: algopy.Bytes | bytes
225
+ vote_first: algopy.UInt64 | int
226
+ vote_last: algopy.UInt64 | int
227
+ vote_key_dilution: algopy.UInt64 | int
228
+ non_participation: algopy.UInt64 | int | bool
229
+ state_proof_key: algopy.Bytes | bytes
230
+ sender: algopy.Account | str
231
+ fee: algopy.UInt64 | int
232
+ note: algopy.String | algopy.Bytes | str | bytes
233
+ rekey_to: algopy.Account | str
234
+
235
+
236
+ class KeyRegistration(_BaseInnerTransaction):
237
+ def __init__(self, **kwargs: typing.Unpack[_KeyRegistrationInitFields]):
238
+ import algopy
239
+
240
+ self.fields = {**kwargs, "type": algopy.TransactionType.KeyRegistration}
241
+
242
+ def set(self, **kwargs: typing.Unpack[_KeyRegistrationInitFields]) -> None:
243
+ """Updates inner transaction parameter values"""
244
+ import algopy
245
+
246
+ self.fields.update({**kwargs, "type": algopy.TransactionType.KeyRegistration})
247
+
248
+ def __getattr__(self, name: str) -> object:
249
+ return self.get_field(_KeyRegistrationInitFields, name)
250
+
251
+
252
+ class _AssetConfigInitFields(typing.TypedDict, total=False):
253
+ config_asset: algopy.Asset | algopy.UInt64 | int
254
+ total: algopy.UInt64 | int
255
+ unit_name: algopy.String | algopy.Bytes | str | bytes
256
+ asset_name: algopy.String | algopy.Bytes | str | bytes
257
+ decimals: algopy.UInt64 | int
258
+ default_frozen: bool
259
+ url: algopy.String | algopy.Bytes | str | bytes
260
+ metadata_hash: algopy.Bytes | bytes
261
+ manager: algopy.Account | str
262
+ reserve: algopy.Account | str
263
+ freeze: algopy.Account | str
264
+ clawback: algopy.Account | str
265
+ sender: algopy.Account | str
266
+ fee: algopy.UInt64 | int
267
+ note: algopy.String | algopy.Bytes | str | bytes
268
+ rekey_to: algopy.Account | str
269
+
270
+
271
+ class AssetConfig(_BaseInnerTransaction):
272
+ def __init__(self, **kwargs: typing.Unpack[_AssetConfigInitFields]):
273
+ import algopy
274
+
275
+ self.fields = {**kwargs, "type": algopy.TransactionType.AssetConfig}
276
+
277
+ def set(self, **kwargs: typing.Unpack[_AssetConfigInitFields]) -> None:
278
+ """Updates inner transaction parameter values"""
279
+ import algopy
280
+
281
+ self.fields.update({**kwargs, "type": algopy.TransactionType.AssetConfig})
282
+
283
+ def __getattr__(self, name: str) -> object:
284
+ return self.get_field(_AssetConfigInitFields, name)
285
+
286
+
287
+ class _AssetTransferInitFields(typing.TypedDict, total=False):
288
+ xfer_asset: algopy.Asset | algopy.UInt64 | int
289
+ asset_receiver: algopy.Account | str
290
+ asset_amount: algopy.UInt64 | int
291
+ asset_sender: algopy.Account | str
292
+ asset_close_to: algopy.Account | str
293
+ sender: algopy.Account | str
294
+ fee: algopy.UInt64 | int
295
+ note: algopy.String | algopy.Bytes | str | bytes
296
+ rekey_to: algopy.Account | str
297
+
298
+
299
+ class AssetTransfer(_BaseInnerTransaction):
300
+ def __init__(self, **kwargs: typing.Unpack[_AssetTransferInitFields]):
301
+ import algopy
302
+
303
+ from algopy_testing import get_test_context
304
+
305
+ context = get_test_context()
306
+ self.fields = {
307
+ "type": algopy.TransactionType.AssetTransfer,
308
+ "asset_sender": context.default_application.address if context else None,
309
+ "amount": 0,
310
+ **kwargs,
311
+ }
312
+
313
+ def set(self, **kwargs: typing.Unpack[_AssetTransferInitFields]) -> None:
314
+ """Updates inner transaction parameter values"""
315
+ import algopy
316
+
317
+ self.fields.update({**kwargs, "type": algopy.TransactionType.AssetTransfer})
318
+
319
+ def __getattr__(self, name: str) -> object:
320
+ return self.get_field(_AssetTransferInitFields, name)
321
+
322
+
323
+ class _AssetFreezeInitFields(typing.TypedDict, total=False):
324
+ freeze_asset: algopy.Asset | algopy.UInt64 | int
325
+ freeze_account: algopy.Account | str
326
+ frozen: bool
327
+ sender: algopy.Account | str
328
+ fee: algopy.UInt64 | int
329
+ note: algopy.String | algopy.Bytes | str | bytes
330
+ rekey_to: algopy.Account | str
331
+
332
+
333
+ class AssetFreeze(_BaseInnerTransaction):
334
+ def __init__(self, **kwargs: typing.Unpack[_AssetFreezeInitFields]):
335
+ import algopy
336
+
337
+ self.fields = {**kwargs, "type": algopy.TransactionType.AssetFreeze}
338
+
339
+ def set(self, **kwargs: typing.Unpack[_AssetFreezeInitFields]) -> None:
340
+ """Updates inner transaction parameter values"""
341
+ import algopy
342
+
343
+ self.fields.update({**kwargs, "type": algopy.TransactionType.AssetFreeze})
344
+
345
+ def __getattr__(self, name: str) -> object:
346
+ return self.get_field(_AssetFreezeInitFields, name)
347
+
348
+
349
+ class _ApplicationCallInitFields(typing.TypedDict, total=False):
350
+ app_id: algopy.Application | algopy.UInt64 | int
351
+ approval_program: algopy.Bytes | bytes | tuple[algopy.Bytes, ...]
352
+ clear_state_program: algopy.Bytes | bytes | tuple[algopy.Bytes, ...]
353
+ on_completion: algopy.OnCompleteAction | algopy.UInt64 | int
354
+ global_num_uint: algopy.UInt64 | int
355
+ global_num_bytes: algopy.UInt64 | int
356
+ local_num_uint: algopy.UInt64 | int
357
+ local_num_bytes: algopy.UInt64 | int
358
+ extra_program_pages: algopy.UInt64 | int
359
+ app_args: tuple[algopy.Bytes | algopy.BytesBacked, ...]
360
+ accounts: tuple[algopy.Account, ...]
361
+ assets: tuple[algopy.Asset, ...]
362
+ apps: tuple[algopy.Application, ...]
363
+ sender: algopy.Account | str
364
+ fee: algopy.UInt64 | int
365
+ note: algopy.String | algopy.Bytes | str | bytes
366
+ rekey_to: algopy.Account | str
367
+
368
+
369
+ class ApplicationCall(_BaseInnerTransaction):
370
+ def __init__(self, **kwargs: typing.Unpack[_ApplicationCallInitFields]):
371
+ import algopy
372
+
373
+ self.fields = {**kwargs, "type": algopy.TransactionType.ApplicationCall}
374
+
375
+ def set(self, **kwargs: typing.Unpack[_ApplicationCallInitFields]) -> None:
376
+ """Updates inner transaction parameter values"""
377
+ import algopy
378
+
379
+ self.fields.update({**kwargs, "type": algopy.TransactionType.ApplicationCall})
380
+
381
+ def __getattr__(self, name: str) -> object:
382
+ return self.get_field(_ApplicationCallInitFields, name)
383
+
384
+
385
+ # ==== Inner Transaction Results ====
386
+ # These are used to represent finalized transactions submitted to the network
387
+ # and are created by the `submit` method of each inner transaction class
388
+
389
+
390
+ class _BaseInnerTransactionResult:
391
+ fields: dict[str, typing.Any]
392
+
393
+ @typing.overload
394
+ def __init__(
395
+ self,
396
+ txn_type: algopy.TransactionType,
397
+ **kwargs: typing.Unpack[_TransactionFields],
398
+ ): ...
399
+
400
+ @typing.overload
401
+ def __init__(
402
+ self,
403
+ **kwargs: typing.Unpack[_TransactionFields],
404
+ ): ...
405
+
406
+ def __init__(
407
+ self,
408
+ txn_type: algopy.TransactionType | None = None,
409
+ **kwargs: typing.Unpack[_TransactionFields],
410
+ ):
411
+ import algopy
412
+
413
+ if txn_type is None and kwargs.get("type") is None:
414
+ raise ValueError("No transaction type provided to `algopy.itxn.InnerTransaction`")
415
+
416
+ txn_type = txn_type if txn_type is not None else kwargs.get("type")
417
+ txn_type_bytes = txn_type_to_bytes(int(txn_type)) # type: ignore[arg-type]
418
+
419
+ assert txn_type is not None
420
+
421
+ self.fields = {
422
+ "type": txn_type,
423
+ "type_bytes": txn_type_bytes,
424
+ "first_valid": algopy.UInt64(0),
425
+ "first_valid_time": algopy.UInt64(0),
426
+ "last_valid": algopy.UInt64(MAX_UINT64),
427
+ "note": algopy.Bytes(b""),
428
+ "lease": algopy.Bytes(bytes(algosdk.constants.ZERO_ADDRESS, encoding="utf-8")),
429
+ "close_remainder_to": algopy.Bytes(
430
+ bytes(algosdk.constants.ZERO_ADDRESS, encoding="utf-8")
431
+ ),
432
+ "txn_id": algopy.Bytes(dummy_transaction_id()),
433
+ **kwargs,
434
+ }
435
+ self._parse_covariant_types(txn_type)
436
+
437
+ def get_field(self, type_dict: object, name: str) -> typing.Any:
438
+ if name in type_dict.__annotations__ or name in _TransactionFields.__annotations__:
439
+ return self.fields.get(name)
440
+
441
+ raise AttributeError(f"{type(self).__name__!r} object has no attribute {name!r}")
442
+
443
+ def _parse_covariant_types(
444
+ self,
445
+ txn_type: algopy.TransactionType,
446
+ ) -> None:
447
+ import algopy
448
+
449
+ # Define the covariant fields for each transaction type
450
+ covariant_fields = {
451
+ algopy.TransactionType.Payment: ["receiver", "close_remainder_to", "sender"],
452
+ algopy.TransactionType.KeyRegistration: [
453
+ "vote_key",
454
+ "selection_key",
455
+ "state_proof_key",
456
+ "sender",
457
+ ],
458
+ algopy.TransactionType.AssetConfig: [
459
+ "config_asset",
460
+ "unit_name",
461
+ "asset_name",
462
+ "url",
463
+ "metadata_hash",
464
+ "manager",
465
+ "reserve",
466
+ "freeze",
467
+ "clawback",
468
+ "sender",
469
+ ],
470
+ algopy.TransactionType.AssetTransfer: [
471
+ "xfer_asset",
472
+ "asset_sender",
473
+ "asset_receiver",
474
+ "asset_close_to",
475
+ "sender",
476
+ ],
477
+ algopy.TransactionType.AssetFreeze: ["freeze_asset", "freeze_account", "sender"],
478
+ algopy.TransactionType.ApplicationCall: [
479
+ "app_id",
480
+ "approval_program",
481
+ "clear_state_program",
482
+ "app_args",
483
+ "accounts",
484
+ "assets",
485
+ "apps",
486
+ "sender",
487
+ ],
488
+ }
489
+
490
+ # Get the relevant fields for the given transaction type
491
+ relevant_fields = covariant_fields.get(txn_type, [])
492
+
493
+ for name, value in self.fields.items():
494
+ if name in relevant_fields:
495
+ if isinstance(value, int):
496
+ self.fields[name] = algopy.UInt64(value)
497
+ elif isinstance(value, bytes):
498
+ self.fields[name] = algopy.Bytes(value)
499
+ elif isinstance(value, str):
500
+ self.fields[name] = algopy.Bytes(value.encode("utf-8"))
501
+ elif isinstance(value, tuple):
502
+ # Convert each element in the tuple to algopy.Bytes
503
+ self.fields[name] = tuple(
504
+ (
505
+ algopy.Bytes(item)
506
+ if isinstance(item, bytes)
507
+ else (
508
+ algopy.Bytes(item.encode("utf-8"))
509
+ if isinstance(item, str)
510
+ else item
511
+ )
512
+ )
513
+ for item in value
514
+ )
515
+ elif isinstance(value, tuple) and all(
516
+ isinstance(
517
+ v,
518
+ algopy.Account
519
+ | algopy.String
520
+ | algopy.BigUInt
521
+ | algopy.arc4.String
522
+ | algopy.arc4.Bool
523
+ | algopy.arc4.Address,
524
+ )
525
+ for v in value
526
+ ):
527
+ self.fields[name] = [v.bytes() for v in value]
528
+
529
+
530
+ class PaymentInnerTransaction(_BaseInnerTransactionResult):
531
+ def __init__(self, **kwargs: typing.Unpack[PaymentFields]):
532
+ import algopy
533
+
534
+ super().__init__(algopy.TransactionType.Payment, **kwargs)
535
+
536
+ def __getattr__(self, name: str) -> object:
537
+ return self.get_field(PaymentFields, name)
538
+
539
+
540
+ class KeyRegistrationInnerTransaction(_BaseInnerTransactionResult):
541
+ def __init__(self, **kwargs: typing.Unpack[KeyRegistrationFields]):
542
+ import algopy
543
+
544
+ super().__init__(algopy.TransactionType.KeyRegistration, **kwargs)
545
+
546
+ def __getattr__(self, name: str) -> object:
547
+ return self.get_field(KeyRegistrationFields, name)
548
+
549
+
550
+ class AssetConfigInnerTransaction(_BaseInnerTransactionResult):
551
+ def __init__(self, **kwargs: typing.Unpack[AssetConfigFields]):
552
+ import algopy
553
+
554
+ super().__init__(algopy.TransactionType.AssetConfig, **kwargs)
555
+
556
+ def __getattr__(self, name: str) -> object:
557
+ return self.get_field(AssetConfigFields, name)
558
+
559
+ @property
560
+ def created_asset(self) -> algopy.Asset:
561
+ # forward xfer asset that is auto set by submit()
562
+ # for the asset config itxn type
563
+ import algopy
564
+
565
+ created_asset = self.fields["xfer_asset"]
566
+ if not created_asset:
567
+ raise ValueError("No created asset found")
568
+ return typing.cast(algopy.Asset, created_asset)
569
+
570
+
571
+ class AssetTransferInnerTransaction(_BaseInnerTransactionResult):
572
+ def __init__(self, **kwargs: typing.Unpack[AssetTransferFields]):
573
+ import algopy
574
+
575
+ super().__init__(algopy.TransactionType.AssetTransfer, **kwargs)
576
+
577
+ def __getattr__(self, name: str) -> object:
578
+ return self.get_field(AssetTransferFields, name)
579
+
580
+
581
+ class AssetFreezeInnerTransaction(_BaseInnerTransactionResult):
582
+ def __init__(self, **kwargs: typing.Unpack[AssetFreezeFields]):
583
+ import algopy
584
+
585
+ super().__init__(algopy.TransactionType.AssetFreeze, **kwargs)
586
+
587
+ def __getattr__(self, name: str) -> object:
588
+ return self.get_field(AssetFreezeFields, name)
589
+
590
+
591
+ class ApplicationCallInnerTransaction(_BaseInnerTransactionResult):
592
+ def __init__(self, **kwargs: typing.Unpack[ApplicationCallFields]):
593
+ import algopy
594
+
595
+ super().__init__(algopy.TransactionType.ApplicationCall, **kwargs)
596
+
597
+ def __getattr__(self, name: str) -> object:
598
+ from algopy_testing import get_test_context
599
+
600
+ context = get_test_context()
601
+
602
+ if name == "last_log" and context:
603
+ return context.get_application_logs(self.get_field(_ApplicationCallFields, "app_id"))[
604
+ -1
605
+ ]
606
+
607
+ return self.get_field(_ApplicationCallFields, name)
608
+
609
+
610
+ class InnerTransactionResult(_BaseInnerTransactionResult):
611
+ def __init__(self, **kwargs: typing.Unpack[_TransactionFields]):
612
+ super().__init__(**kwargs)
613
+
614
+ def __getattr__(self, name: str) -> object:
615
+ return self.get_field(_TransactionFields, name)
616
+
617
+
618
+ _InnerTransactionsType = (
619
+ InnerTransactionResult
620
+ | PaymentInnerTransaction
621
+ | KeyRegistrationInnerTransaction
622
+ | AssetConfigInnerTransaction
623
+ | AssetTransferInnerTransaction
624
+ | AssetFreezeInnerTransaction
625
+ | ApplicationCallInnerTransaction
626
+ )
627
+
628
+
629
+ def submit_txns(transactions: list[_BaseInnerTransaction]) -> tuple[InnerTransactionResultType]:
630
+ """Submits a group of up to 16 inner transactions parameters
631
+
632
+ :returns: A tuple of the resulting inner transactions
633
+ """
634
+ from algopy_testing import get_test_context
635
+
636
+ context = get_test_context()
637
+
638
+ if len(transactions) > algosdk.constants.TX_GROUP_LIMIT:
639
+ raise ValueError("Cannot submit more than 16 inner transactions at once")
640
+
641
+ results = tuple(_create_inner_transaction_result(tx) for tx in transactions)
642
+ context._append_inner_transaction_group(results)
643
+
644
+ return results # type: ignore[return-value]
645
+
646
+
647
+ __all__ = [
648
+ "ApplicationCall",
649
+ "ApplicationCallInnerTransaction",
650
+ "AssetConfig",
651
+ "AssetConfigInnerTransaction",
652
+ "AssetFreeze",
653
+ "AssetFreezeInnerTransaction",
654
+ "AssetTransfer",
655
+ "AssetTransferInnerTransaction",
656
+ "InnerTransaction",
657
+ "InnerTransactionResult",
658
+ "KeyRegistration",
659
+ "KeyRegistrationInnerTransaction",
660
+ "Payment",
661
+ "PaymentInnerTransaction",
662
+ "_BaseInnerTransaction",
663
+ "_InnerTransactionsType",
664
+ "submit_txns",
665
+ ]
@@ -0,0 +1,31 @@
1
+ from algopy_testing.models.account import Account
2
+ from algopy_testing.models.application import Application
3
+ from algopy_testing.models.asset import Asset
4
+ from algopy_testing.models.block import Block
5
+ from algopy_testing.models.contract import ARC4Contract, Contract, StateTotals
6
+ from algopy_testing.models.global_values import Global
7
+ from algopy_testing.models.gtxn import GTxn
8
+ from algopy_testing.models.itxn import ITxn
9
+ from algopy_testing.models.logicsig import LogicSig, logicsig
10
+ from algopy_testing.models.template_variable import TemplateVar
11
+ from algopy_testing.models.txn import Txn
12
+ from algopy_testing.models.unsigned_builtins import uenumerate, urange
13
+
14
+ __all__ = [
15
+ "ARC4Contract",
16
+ "Account",
17
+ "Application",
18
+ "Asset",
19
+ "Block",
20
+ "Contract",
21
+ "Global",
22
+ "GTxn",
23
+ "ITxn",
24
+ "LogicSig",
25
+ "StateTotals",
26
+ "TemplateVar",
27
+ "Txn",
28
+ "logicsig",
29
+ "uenumerate",
30
+ "urange",
31
+ ]