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.
- algopy/__init__.py +58 -0
- algopy/arc4.py +1 -0
- algopy/gtxn.py +1 -0
- algopy/itxn.py +1 -0
- algopy/op.py +1 -0
- algopy/py.typed +0 -0
- algopy_testing/__init__.py +55 -0
- algopy_testing/arc4.py +1533 -0
- algopy_testing/constants.py +22 -0
- algopy_testing/context.py +1194 -0
- algopy_testing/decorators/__init__.py +0 -0
- algopy_testing/decorators/abimethod.py +204 -0
- algopy_testing/decorators/baremethod.py +83 -0
- algopy_testing/decorators/subroutine.py +9 -0
- algopy_testing/enums.py +42 -0
- algopy_testing/gtxn.py +261 -0
- algopy_testing/itxn.py +665 -0
- algopy_testing/models/__init__.py +31 -0
- algopy_testing/models/account.py +128 -0
- algopy_testing/models/application.py +72 -0
- algopy_testing/models/asset.py +109 -0
- algopy_testing/models/block.py +34 -0
- algopy_testing/models/box.py +158 -0
- algopy_testing/models/contract.py +82 -0
- algopy_testing/models/gitxn.py +42 -0
- algopy_testing/models/global_values.py +72 -0
- algopy_testing/models/gtxn.py +56 -0
- algopy_testing/models/itxn.py +85 -0
- algopy_testing/models/logicsig.py +44 -0
- algopy_testing/models/template_variable.py +23 -0
- algopy_testing/models/transactions.py +158 -0
- algopy_testing/models/txn.py +113 -0
- algopy_testing/models/unsigned_builtins.py +36 -0
- algopy_testing/op.py +1098 -0
- algopy_testing/primitives/__init__.py +6 -0
- algopy_testing/primitives/biguint.py +148 -0
- algopy_testing/primitives/bytes.py +174 -0
- algopy_testing/primitives/string.py +68 -0
- algopy_testing/primitives/uint64.py +213 -0
- algopy_testing/protocols.py +18 -0
- algopy_testing/py.typed +0 -0
- algopy_testing/state/__init__.py +4 -0
- algopy_testing/state/global_state.py +73 -0
- algopy_testing/state/local_state.py +54 -0
- algopy_testing/utilities/__init__.py +3 -0
- algopy_testing/utilities/budget.py +23 -0
- algopy_testing/utilities/log.py +55 -0
- algopy_testing/utils.py +249 -0
- algorand_python_testing-0.0.0b1.dist-info/METADATA +81 -0
- algorand_python_testing-0.0.0b1.dist-info/RECORD +52 -0
- algorand_python_testing-0.0.0b1.dist-info/WHEEL +4 -0
- 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
|
+
]
|