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
|
File without changes
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import functools
|
|
4
|
+
import typing
|
|
5
|
+
|
|
6
|
+
import algosdk
|
|
7
|
+
|
|
8
|
+
from algopy_testing.constants import UINT64_SIZE
|
|
9
|
+
from algopy_testing.utils import (
|
|
10
|
+
abi_return_type_annotation_for_arg,
|
|
11
|
+
abi_type_name_for_arg,
|
|
12
|
+
int_to_bytes,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
if typing.TYPE_CHECKING:
|
|
16
|
+
import algopy
|
|
17
|
+
|
|
18
|
+
from algopy_testing.context import AlgopyTestContext
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
if typing.TYPE_CHECKING:
|
|
22
|
+
import algopy
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
_P = typing.ParamSpec("_P")
|
|
26
|
+
_R = typing.TypeVar("_R")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _generate_arc4_signature(
|
|
30
|
+
fn: typing.Callable[_P, _R], args: tuple[typing.Any, ...]
|
|
31
|
+
) -> algopy.Bytes:
|
|
32
|
+
import algopy
|
|
33
|
+
|
|
34
|
+
args_without_txns = [
|
|
35
|
+
arg
|
|
36
|
+
for arg in args
|
|
37
|
+
if not isinstance(arg, algopy.gtxn.TransactionBase) # type: ignore[arg-type, unused-ignore]
|
|
38
|
+
]
|
|
39
|
+
arg_types = [algosdk.abi.Argument(abi_type_name_for_arg(arg=arg)) for arg in args_without_txns]
|
|
40
|
+
return_type = algosdk.abi.Returns(
|
|
41
|
+
abi_return_type_annotation_for_arg(fn.__annotations__.get("return"))
|
|
42
|
+
)
|
|
43
|
+
method = algosdk.abi.Method(name=fn.__name__, args=arg_types, returns=return_type)
|
|
44
|
+
return algopy.Bytes(method.get_selector())
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _extract_refs_from_args(
|
|
48
|
+
args: tuple[typing.Any, ...], kwargs: dict[str, typing.Any], ref_type: type
|
|
49
|
+
) -> list[typing.Any]:
|
|
50
|
+
import algopy
|
|
51
|
+
|
|
52
|
+
refs: list[typing.Any] = []
|
|
53
|
+
for arg in list(args) + list(kwargs.values()):
|
|
54
|
+
match arg:
|
|
55
|
+
case algopy.gtxn.TransactionBase() if ref_type is algopy.gtxn.TransactionBase:
|
|
56
|
+
refs.append(arg)
|
|
57
|
+
case algopy.Account() if ref_type is algopy.Account:
|
|
58
|
+
refs.append(arg)
|
|
59
|
+
case algopy.Asset() if ref_type is algopy.Asset:
|
|
60
|
+
refs.append(arg)
|
|
61
|
+
case algopy.Application() if ref_type is algopy.Application:
|
|
62
|
+
refs.append(arg)
|
|
63
|
+
case (
|
|
64
|
+
algopy.String()
|
|
65
|
+
| algopy.BigUInt()
|
|
66
|
+
| algopy.UInt64()
|
|
67
|
+
| algopy.BigUInt()
|
|
68
|
+
| algopy.arc4.Bool()
|
|
69
|
+
| algopy.arc4.BigUIntN()
|
|
70
|
+
| algopy.arc4.BigUFixedNxM()
|
|
71
|
+
| algopy.arc4.StaticArray()
|
|
72
|
+
| algopy.arc4.DynamicArray()
|
|
73
|
+
| algopy.arc4.DynamicBytes()
|
|
74
|
+
| algopy.arc4.Address()
|
|
75
|
+
| algopy.arc4.String()
|
|
76
|
+
| algopy.arc4.Byte()
|
|
77
|
+
| algopy.arc4.UIntN()
|
|
78
|
+
| int()
|
|
79
|
+
) if ref_type is algopy.Bytes:
|
|
80
|
+
refs.append(_extract_bytes(arg))
|
|
81
|
+
case _:
|
|
82
|
+
continue
|
|
83
|
+
return refs
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _extract_bytes(value: object) -> algopy.Bytes:
|
|
87
|
+
import algopy
|
|
88
|
+
|
|
89
|
+
if isinstance(value, algopy.Bytes):
|
|
90
|
+
return value
|
|
91
|
+
if isinstance(
|
|
92
|
+
value,
|
|
93
|
+
(
|
|
94
|
+
algopy.String
|
|
95
|
+
| algopy.BigUInt
|
|
96
|
+
| algopy.arc4.Bool
|
|
97
|
+
| algopy.arc4.BigUIntN
|
|
98
|
+
| algopy.arc4.BigUFixedNxM
|
|
99
|
+
| algopy.arc4.StaticArray
|
|
100
|
+
| algopy.arc4.DynamicArray
|
|
101
|
+
| algopy.arc4.DynamicBytes
|
|
102
|
+
| algopy.arc4.Address
|
|
103
|
+
| algopy.arc4.String
|
|
104
|
+
| algopy.arc4.Byte
|
|
105
|
+
| algopy.arc4.UIntN
|
|
106
|
+
),
|
|
107
|
+
):
|
|
108
|
+
return value.bytes # type: ignore[union-attr, unused-ignore]
|
|
109
|
+
if isinstance(value, (algopy.UInt64 | int)):
|
|
110
|
+
return algopy.Bytes(int_to_bytes(int(value), UINT64_SIZE))
|
|
111
|
+
raise ValueError(f"Unsupported type: {type(value).__name__}")
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _extract_and_append_txn_to_context(
|
|
115
|
+
context: AlgopyTestContext,
|
|
116
|
+
args: tuple[typing.Any, ...],
|
|
117
|
+
kwargs: dict[str, typing.Any],
|
|
118
|
+
fn: typing.Callable[_P, _R],
|
|
119
|
+
) -> None:
|
|
120
|
+
import algopy
|
|
121
|
+
|
|
122
|
+
context.add_transactions(_extract_refs_from_args(args, kwargs, algopy.gtxn.TransactionBase))
|
|
123
|
+
|
|
124
|
+
context.add_transactions(
|
|
125
|
+
[
|
|
126
|
+
context.any_application_call_transaction(
|
|
127
|
+
sender=context.default_creator,
|
|
128
|
+
app_id=context.default_application,
|
|
129
|
+
accounts=_extract_refs_from_args(args, kwargs, algopy.Account),
|
|
130
|
+
assets=_extract_refs_from_args(args, kwargs, algopy.Asset),
|
|
131
|
+
apps=_extract_refs_from_args(args, kwargs, algopy.Application),
|
|
132
|
+
app_args=[
|
|
133
|
+
_generate_arc4_signature(fn, args),
|
|
134
|
+
*_extract_refs_from_args(args, kwargs, algopy.Bytes),
|
|
135
|
+
],
|
|
136
|
+
approval_program_pages=_extract_refs_from_args(args, kwargs, algopy.Bytes),
|
|
137
|
+
clear_state_program_pages=_extract_refs_from_args(args, kwargs, algopy.Bytes),
|
|
138
|
+
),
|
|
139
|
+
]
|
|
140
|
+
)
|
|
141
|
+
new_active_txn_index = len(context.get_transaction_group()) - 1
|
|
142
|
+
context.set_active_transaction_index(new_active_txn_index if new_active_txn_index > 0 else 0)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@typing.overload
|
|
146
|
+
def abimethod(fn: typing.Callable[_P, _R], /) -> typing.Callable[_P, _R]: ...
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@typing.overload
|
|
150
|
+
def abimethod(
|
|
151
|
+
*,
|
|
152
|
+
name: str | None = None,
|
|
153
|
+
create: typing.Literal["allow", "require", "disallow"] = "disallow",
|
|
154
|
+
allow_actions: typing.Sequence[
|
|
155
|
+
algopy.OnCompleteAction
|
|
156
|
+
| typing.Literal[
|
|
157
|
+
"NoOp",
|
|
158
|
+
"OptIn",
|
|
159
|
+
"CloseOut",
|
|
160
|
+
# ClearState has its own program, so is not considered as part of ARC4 routing
|
|
161
|
+
"UpdateApplication",
|
|
162
|
+
"DeleteApplication",
|
|
163
|
+
]
|
|
164
|
+
] = ("NoOp",),
|
|
165
|
+
readonly: bool = False,
|
|
166
|
+
default_args: typing.Mapping[str, str | object] = {},
|
|
167
|
+
) -> typing.Callable[[typing.Callable[_P, _R]], typing.Callable[_P, _R]]: ...
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def abimethod( # noqa: PLR0913
|
|
171
|
+
fn: typing.Callable[_P, _R] | None = None,
|
|
172
|
+
*,
|
|
173
|
+
name: str | None = None, # noqa: ARG001
|
|
174
|
+
create: typing.Literal["allow", "require", "disallow"] = "disallow", # noqa: ARG001
|
|
175
|
+
allow_actions: typing.Sequence[ # noqa: ARG001
|
|
176
|
+
algopy.OnCompleteAction
|
|
177
|
+
| typing.Literal[
|
|
178
|
+
"NoOp",
|
|
179
|
+
"OptIn",
|
|
180
|
+
"CloseOut",
|
|
181
|
+
"UpdateApplication",
|
|
182
|
+
"DeleteApplication",
|
|
183
|
+
]
|
|
184
|
+
] = ("NoOp",),
|
|
185
|
+
readonly: bool = False, # noqa: ARG001
|
|
186
|
+
default_args: typing.Mapping[str, str | object] | None = None, # noqa: ARG001
|
|
187
|
+
) -> typing.Callable[[typing.Callable[_P, _R]], typing.Callable[_P, _R]] | typing.Callable[_P, _R]:
|
|
188
|
+
def decorator(fn: typing.Callable[_P, _R]) -> typing.Callable[_P, _R]:
|
|
189
|
+
@functools.wraps(fn)
|
|
190
|
+
def wrapper(*args: _P.args, **kwargs: _P.kwargs) -> _R:
|
|
191
|
+
from algopy_testing import get_test_context
|
|
192
|
+
|
|
193
|
+
context = get_test_context()
|
|
194
|
+
if context is None or context._active_transaction_index is not None:
|
|
195
|
+
return fn(*args, **kwargs)
|
|
196
|
+
|
|
197
|
+
_extract_and_append_txn_to_context(context, args[1:], kwargs, fn)
|
|
198
|
+
return fn(*args, **kwargs)
|
|
199
|
+
|
|
200
|
+
return wrapper
|
|
201
|
+
|
|
202
|
+
if fn is not None:
|
|
203
|
+
return decorator(fn)
|
|
204
|
+
return decorator
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import functools
|
|
4
|
+
import typing
|
|
5
|
+
|
|
6
|
+
if typing.TYPE_CHECKING:
|
|
7
|
+
import algopy
|
|
8
|
+
|
|
9
|
+
from algopy_testing.context import AlgopyTestContext
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
_P = typing.ParamSpec("_P")
|
|
13
|
+
_R = typing.TypeVar("_R")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _append_bare_txn_to_context(
|
|
17
|
+
context: AlgopyTestContext,
|
|
18
|
+
) -> None:
|
|
19
|
+
context.add_transactions(
|
|
20
|
+
[
|
|
21
|
+
context.any_application_call_transaction(
|
|
22
|
+
sender=context.default_creator,
|
|
23
|
+
app_id=context.default_application,
|
|
24
|
+
),
|
|
25
|
+
]
|
|
26
|
+
)
|
|
27
|
+
context.set_active_transaction_index(len(context.get_transaction_group()) - 1)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@typing.overload
|
|
31
|
+
def baremethod(fn: typing.Callable[_P, _R], /) -> typing.Callable[_P, _R]: ...
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@typing.overload
|
|
35
|
+
def baremethod(
|
|
36
|
+
*,
|
|
37
|
+
create: typing.Literal["allow", "require", "disallow"] = "disallow",
|
|
38
|
+
allow_actions: typing.Sequence[
|
|
39
|
+
algopy.OnCompleteAction
|
|
40
|
+
| typing.Literal[
|
|
41
|
+
"NoOp",
|
|
42
|
+
"OptIn",
|
|
43
|
+
"CloseOut",
|
|
44
|
+
"UpdateApplication",
|
|
45
|
+
"DeleteApplication",
|
|
46
|
+
]
|
|
47
|
+
] = ("NoOp",),
|
|
48
|
+
) -> typing.Callable[[typing.Callable[_P, _R]], typing.Callable[_P, _R]]: ...
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def baremethod(
|
|
52
|
+
fn: typing.Callable[_P, _R] | None = None,
|
|
53
|
+
*,
|
|
54
|
+
create: typing.Literal["allow", "require", "disallow"] = "disallow", # noqa: ARG001
|
|
55
|
+
allow_actions: typing.Sequence[ # noqa: ARG001
|
|
56
|
+
algopy.OnCompleteAction
|
|
57
|
+
| typing.Literal[
|
|
58
|
+
"NoOp",
|
|
59
|
+
"OptIn",
|
|
60
|
+
"CloseOut",
|
|
61
|
+
"UpdateApplication",
|
|
62
|
+
"DeleteApplication",
|
|
63
|
+
]
|
|
64
|
+
] = ("NoOp",),
|
|
65
|
+
) -> typing.Callable[[typing.Callable[_P, _R]], typing.Callable[_P, _R]] | typing.Callable[_P, _R]:
|
|
66
|
+
def decorator(fn: typing.Callable[_P, _R]) -> typing.Callable[_P, _R]:
|
|
67
|
+
@functools.wraps(fn)
|
|
68
|
+
def wrapper(*args: _P.args, **kwargs: _P.kwargs) -> _R:
|
|
69
|
+
from algopy_testing import get_test_context
|
|
70
|
+
|
|
71
|
+
context = get_test_context()
|
|
72
|
+
if context is None or context._active_transaction_index is not None:
|
|
73
|
+
return fn(*args, **kwargs)
|
|
74
|
+
|
|
75
|
+
# Custom logic for baremethod can be added here
|
|
76
|
+
_append_bare_txn_to_context(context)
|
|
77
|
+
return fn(*args, **kwargs)
|
|
78
|
+
|
|
79
|
+
return wrapper
|
|
80
|
+
|
|
81
|
+
if fn is not None:
|
|
82
|
+
return decorator(fn)
|
|
83
|
+
return decorator
|
algopy_testing/enums.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from enum import Enum, IntEnum, StrEnum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class OnCompleteAction(Enum):
|
|
5
|
+
NoOp = 0
|
|
6
|
+
OptIn = 1
|
|
7
|
+
CloseOut = 2
|
|
8
|
+
ClearState = 3
|
|
9
|
+
UpdateApplication = 4
|
|
10
|
+
DeleteApplication = 5
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TransactionType(IntEnum):
|
|
14
|
+
Payment = 0
|
|
15
|
+
KeyRegistration = 1
|
|
16
|
+
AssetConfig = 2
|
|
17
|
+
AssetTransfer = 3
|
|
18
|
+
AssetFreeze = 4
|
|
19
|
+
ApplicationCall = 5
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ECDSA(Enum):
|
|
23
|
+
Secp256k1 = 0
|
|
24
|
+
Secp256r1 = 1
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class VrfVerify(Enum):
|
|
28
|
+
VrfAlgorand = 0
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class Base64(Enum):
|
|
32
|
+
URLEncoding = 0
|
|
33
|
+
StdEncoding = 1
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class EC(StrEnum):
|
|
37
|
+
"""Available values for the `EC` enum"""
|
|
38
|
+
|
|
39
|
+
BN254g1 = "BN254g1"
|
|
40
|
+
BN254g2 = "BN254g2"
|
|
41
|
+
BLS12_381g1 = "BLS12_381g1"
|
|
42
|
+
BLS12_381g2 = "BLS12_381g2"
|
algopy_testing/gtxn.py
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import typing
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from algopy_testing.models.transactions import (
|
|
8
|
+
ApplicationCallFields,
|
|
9
|
+
AssetConfigFields,
|
|
10
|
+
AssetFreezeFields,
|
|
11
|
+
AssetTransferFields,
|
|
12
|
+
KeyRegistrationFields,
|
|
13
|
+
PaymentFields,
|
|
14
|
+
_ApplicationCallBaseFields,
|
|
15
|
+
_AssetConfigBaseFields,
|
|
16
|
+
_AssetFreezeBaseFields,
|
|
17
|
+
_AssetTransferBaseFields,
|
|
18
|
+
_KeyRegistrationBaseFields,
|
|
19
|
+
_PaymentBaseFields,
|
|
20
|
+
_TransactionBaseFields,
|
|
21
|
+
)
|
|
22
|
+
from algopy_testing.utils import txn_type_to_bytes
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
import algopy
|
|
26
|
+
|
|
27
|
+
# NOTE: The actual access by group_index is not used, its there to comply with stubs
|
|
28
|
+
# but user has no need to deal with that as we can reason on the index based
|
|
29
|
+
# on position in gtxn group
|
|
30
|
+
NULL_GTXN_GROUP_INDEX = -1
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass
|
|
34
|
+
class _GroupTransaction:
|
|
35
|
+
_fields: dict[str, typing.Any] = field(default_factory=dict)
|
|
36
|
+
|
|
37
|
+
group_index: algopy.UInt64 | int = field(default=NULL_GTXN_GROUP_INDEX)
|
|
38
|
+
|
|
39
|
+
def __init__(self, group_index: algopy.UInt64 | int) -> None:
|
|
40
|
+
self.group_index = group_index
|
|
41
|
+
self._fields = {}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class TransactionFields(
|
|
45
|
+
_TransactionBaseFields,
|
|
46
|
+
_AssetTransferBaseFields,
|
|
47
|
+
_PaymentBaseFields,
|
|
48
|
+
_ApplicationCallBaseFields,
|
|
49
|
+
_KeyRegistrationBaseFields,
|
|
50
|
+
_AssetConfigBaseFields,
|
|
51
|
+
_AssetFreezeBaseFields,
|
|
52
|
+
total=False,
|
|
53
|
+
):
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class TransactionBase(_GroupTransaction):
|
|
59
|
+
def __init__(
|
|
60
|
+
self, group_index: algopy.UInt64 | int, **kwargs: typing.Unpack[_TransactionBaseFields]
|
|
61
|
+
):
|
|
62
|
+
from algopy_testing import get_test_context
|
|
63
|
+
|
|
64
|
+
context = get_test_context()
|
|
65
|
+
try:
|
|
66
|
+
existing_txn = context._gtxns[group_index]
|
|
67
|
+
except IndexError:
|
|
68
|
+
existing_txn = None
|
|
69
|
+
|
|
70
|
+
if (
|
|
71
|
+
existing_txn
|
|
72
|
+
and isinstance(existing_txn, self.__class__)
|
|
73
|
+
or self.__class__ == Transaction
|
|
74
|
+
):
|
|
75
|
+
self.__dict__.update(existing_txn.__dict__)
|
|
76
|
+
else:
|
|
77
|
+
super().__init__(group_index=group_index)
|
|
78
|
+
self._fields.update(kwargs)
|
|
79
|
+
|
|
80
|
+
def set(self, **kwargs: typing.Unpack[_TransactionBaseFields]) -> None:
|
|
81
|
+
"""Updates inner transaction parameter values"""
|
|
82
|
+
self._fields.update(kwargs)
|
|
83
|
+
|
|
84
|
+
def __getattr__(self, name: str) -> object:
|
|
85
|
+
if name not in _TransactionBaseFields.__annotations__:
|
|
86
|
+
raise AttributeError(f"'{type(self)}' object has no attribute '{name}'")
|
|
87
|
+
|
|
88
|
+
return self._fields.get(name)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@dataclass
|
|
92
|
+
class AssetTransferTransaction(TransactionBase):
|
|
93
|
+
def __init__(
|
|
94
|
+
self, group_index: algopy.UInt64 | int, **kwargs: typing.Unpack[AssetTransferFields]
|
|
95
|
+
):
|
|
96
|
+
super().__init__(group_index=group_index)
|
|
97
|
+
self._fields.update(kwargs)
|
|
98
|
+
|
|
99
|
+
def set(self, **kwargs: typing.Unpack[AssetTransferFields]) -> None:
|
|
100
|
+
"""Updates inner transaction parameter values"""
|
|
101
|
+
self._fields.update(kwargs)
|
|
102
|
+
|
|
103
|
+
def __getattr__(self, name: str) -> typing.Any:
|
|
104
|
+
if name in AssetTransferFields.__annotations__:
|
|
105
|
+
return self._fields.get(name)
|
|
106
|
+
|
|
107
|
+
raise AttributeError(f"'{type(self)}' object has no attribute '{name}'")
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@dataclass
|
|
111
|
+
class PaymentTransaction(TransactionBase):
|
|
112
|
+
def __init__(self, group_index: algopy.UInt64 | int, **kwargs: typing.Unpack[PaymentFields]):
|
|
113
|
+
import algopy
|
|
114
|
+
|
|
115
|
+
super().__init__(group_index=group_index)
|
|
116
|
+
self._fields.update(
|
|
117
|
+
{
|
|
118
|
+
"type": algopy.TransactionType.Payment,
|
|
119
|
+
"type_bytes": txn_type_to_bytes(int(algopy.TransactionType.Payment)),
|
|
120
|
+
**kwargs,
|
|
121
|
+
}
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
def __getattr__(self, name: str) -> typing.Any:
|
|
125
|
+
if name in PaymentFields.__annotations__:
|
|
126
|
+
return self._fields.get(name)
|
|
127
|
+
|
|
128
|
+
raise AttributeError(f"'{type(self)}' object has no attribute '{name}'")
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@dataclass
|
|
132
|
+
class ApplicationCallTransaction(TransactionBase):
|
|
133
|
+
def __init__(
|
|
134
|
+
self, group_index: algopy.UInt64 | int, **kwargs: typing.Unpack[ApplicationCallFields]
|
|
135
|
+
):
|
|
136
|
+
import algopy
|
|
137
|
+
|
|
138
|
+
super().__init__(group_index=group_index)
|
|
139
|
+
self._fields.update(
|
|
140
|
+
{
|
|
141
|
+
"type": algopy.TransactionType.ApplicationCall,
|
|
142
|
+
"type_bytes": txn_type_to_bytes(int(algopy.TransactionType.ApplicationCall)),
|
|
143
|
+
**kwargs,
|
|
144
|
+
}
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
def __getattr__(self, name: str) -> typing.Any:
|
|
148
|
+
if name in ApplicationCallFields.__annotations__:
|
|
149
|
+
return self._fields.get(name)
|
|
150
|
+
|
|
151
|
+
raise AttributeError(f"'{type(self)}' object has no attribute '{name}'")
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class KeyRegistrationTransaction(TransactionBase):
|
|
155
|
+
def __init__(
|
|
156
|
+
self, group_index: algopy.UInt64 | int, **kwargs: typing.Unpack[KeyRegistrationFields]
|
|
157
|
+
):
|
|
158
|
+
import algopy
|
|
159
|
+
|
|
160
|
+
super().__init__(group_index=group_index)
|
|
161
|
+
self._fields.update(
|
|
162
|
+
{
|
|
163
|
+
"type": algopy.TransactionType.KeyRegistration,
|
|
164
|
+
"type_bytes": txn_type_to_bytes(int(algopy.TransactionType.KeyRegistration)),
|
|
165
|
+
**kwargs,
|
|
166
|
+
}
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
def __getattr__(self, name: str) -> typing.Any:
|
|
170
|
+
if name in KeyRegistrationFields.__annotations__:
|
|
171
|
+
return self._fields.get(name)
|
|
172
|
+
|
|
173
|
+
raise AttributeError(f"'{type(self)}' object has no attribute '{name}'")
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@dataclass
|
|
177
|
+
class AssetConfigTransaction(TransactionBase):
|
|
178
|
+
def __init__(
|
|
179
|
+
self, group_index: algopy.UInt64 | int, **kwargs: typing.Unpack[AssetConfigFields]
|
|
180
|
+
):
|
|
181
|
+
import algopy
|
|
182
|
+
|
|
183
|
+
super().__init__(group_index=group_index)
|
|
184
|
+
self._fields.update(
|
|
185
|
+
{
|
|
186
|
+
"type": algopy.TransactionType.AssetConfig,
|
|
187
|
+
"type_bytes": txn_type_to_bytes(int(algopy.TransactionType.AssetConfig)),
|
|
188
|
+
**kwargs,
|
|
189
|
+
}
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
def __getattr__(self, name: str) -> typing.Any:
|
|
193
|
+
if name in AssetConfigFields.__annotations__:
|
|
194
|
+
return self._fields.get(name)
|
|
195
|
+
|
|
196
|
+
raise AttributeError(f"'{type(self)}' object has no attribute '{name}'")
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@dataclass
|
|
200
|
+
class AssetFreezeTransaction(TransactionBase):
|
|
201
|
+
def __init__(
|
|
202
|
+
self,
|
|
203
|
+
group_index: algopy.UInt64 | int,
|
|
204
|
+
**kwargs: typing.Unpack[AssetFreezeFields],
|
|
205
|
+
):
|
|
206
|
+
import algopy
|
|
207
|
+
|
|
208
|
+
super().__init__(group_index=group_index)
|
|
209
|
+
self._fields.update(
|
|
210
|
+
{
|
|
211
|
+
"type": algopy.TransactionType.AssetFreeze,
|
|
212
|
+
"type_bytes": txn_type_to_bytes(int(algopy.TransactionType.AssetFreeze)),
|
|
213
|
+
**kwargs,
|
|
214
|
+
}
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
def __getattr__(self, name: str) -> typing.Any:
|
|
218
|
+
if name in AssetFreezeFields.__annotations__:
|
|
219
|
+
return self._fields.get(name)
|
|
220
|
+
|
|
221
|
+
raise AttributeError(f"'{type(self)}' object has no attribute '{name}'")
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
@dataclass
|
|
225
|
+
class Transaction(TransactionBase):
|
|
226
|
+
def __init__(
|
|
227
|
+
self,
|
|
228
|
+
group_index: algopy.UInt64 | int,
|
|
229
|
+
**kwargs: typing.Unpack[TransactionFields],
|
|
230
|
+
):
|
|
231
|
+
super().__init__(group_index=group_index)
|
|
232
|
+
self._fields.update(kwargs)
|
|
233
|
+
# in case we still got no type after super attempts to load
|
|
234
|
+
# existing txn fro _gtxns then fail
|
|
235
|
+
if "type" not in self._fields:
|
|
236
|
+
raise ValueError("Transaction 'type' field is required")
|
|
237
|
+
|
|
238
|
+
def __getattr__(self, name: str) -> typing.Any:
|
|
239
|
+
if name in TransactionFields.__annotations__:
|
|
240
|
+
return self._fields.get(name)
|
|
241
|
+
|
|
242
|
+
raise AttributeError(f"'{type(self)}' object has no attribute '{name}'")
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
__all__ = [
|
|
246
|
+
"ApplicationCallFields",
|
|
247
|
+
"ApplicationCallTransaction",
|
|
248
|
+
"AssetConfigFields",
|
|
249
|
+
"AssetConfigTransaction",
|
|
250
|
+
"AssetFreezeFields",
|
|
251
|
+
"AssetFreezeTransaction",
|
|
252
|
+
"AssetTransferFields",
|
|
253
|
+
"AssetTransferTransaction",
|
|
254
|
+
"KeyRegistrationFields",
|
|
255
|
+
"KeyRegistrationTransaction",
|
|
256
|
+
"NULL_GTXN_GROUP_INDEX",
|
|
257
|
+
"PaymentFields",
|
|
258
|
+
"PaymentTransaction",
|
|
259
|
+
"Transaction",
|
|
260
|
+
"TransactionBase",
|
|
261
|
+
]
|