algokit-utils 2.4.0b1__py3-none-any.whl → 3.0.0b2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of algokit-utils might be problematic. Click here for more details.
- algokit_utils/__init__.py +23 -181
- algokit_utils/_debugging.py +89 -45
- algokit_utils/_legacy_v2/__init__.py +177 -0
- algokit_utils/{_ensure_funded.py → _legacy_v2/_ensure_funded.py} +19 -18
- algokit_utils/{_transfer.py → _legacy_v2/_transfer.py} +24 -23
- algokit_utils/_legacy_v2/account.py +203 -0
- algokit_utils/_legacy_v2/application_client.py +1471 -0
- algokit_utils/_legacy_v2/application_specification.py +21 -0
- algokit_utils/_legacy_v2/asset.py +168 -0
- algokit_utils/_legacy_v2/common.py +28 -0
- algokit_utils/_legacy_v2/deploy.py +822 -0
- algokit_utils/_legacy_v2/logic_error.py +14 -0
- algokit_utils/{models.py → _legacy_v2/models.py} +16 -45
- algokit_utils/_legacy_v2/network_clients.py +140 -0
- algokit_utils/account.py +12 -183
- algokit_utils/accounts/__init__.py +2 -0
- algokit_utils/accounts/account_manager.py +909 -0
- algokit_utils/accounts/kmd_account_manager.py +159 -0
- algokit_utils/algorand.py +265 -0
- algokit_utils/application_client.py +9 -1447
- algokit_utils/application_specification.py +39 -197
- algokit_utils/applications/__init__.py +7 -0
- algokit_utils/applications/abi.py +276 -0
- algokit_utils/applications/app_client.py +2056 -0
- algokit_utils/applications/app_deployer.py +600 -0
- algokit_utils/applications/app_factory.py +826 -0
- algokit_utils/applications/app_manager.py +470 -0
- algokit_utils/applications/app_spec/__init__.py +2 -0
- algokit_utils/applications/app_spec/arc32.py +207 -0
- algokit_utils/applications/app_spec/arc56.py +1023 -0
- algokit_utils/applications/enums.py +40 -0
- algokit_utils/asset.py +32 -168
- algokit_utils/assets/__init__.py +1 -0
- algokit_utils/assets/asset_manager.py +320 -0
- algokit_utils/beta/_utils.py +36 -0
- algokit_utils/beta/account_manager.py +4 -195
- algokit_utils/beta/algorand_client.py +4 -314
- algokit_utils/beta/client_manager.py +5 -74
- algokit_utils/beta/composer.py +5 -712
- algokit_utils/clients/__init__.py +2 -0
- algokit_utils/clients/client_manager.py +656 -0
- algokit_utils/clients/dispenser_api_client.py +192 -0
- algokit_utils/common.py +8 -26
- algokit_utils/config.py +71 -18
- algokit_utils/deploy.py +7 -894
- algokit_utils/dispenser_api.py +8 -176
- algokit_utils/errors/__init__.py +1 -0
- algokit_utils/errors/logic_error.py +121 -0
- algokit_utils/logic_error.py +7 -82
- algokit_utils/models/__init__.py +8 -0
- algokit_utils/models/account.py +193 -0
- algokit_utils/models/amount.py +198 -0
- algokit_utils/models/application.py +61 -0
- algokit_utils/models/network.py +25 -0
- algokit_utils/models/simulate.py +11 -0
- algokit_utils/models/state.py +59 -0
- algokit_utils/models/transaction.py +100 -0
- algokit_utils/network_clients.py +7 -128
- algokit_utils/protocols/__init__.py +2 -0
- algokit_utils/protocols/account.py +22 -0
- algokit_utils/protocols/typed_clients.py +108 -0
- algokit_utils/transactions/__init__.py +3 -0
- algokit_utils/transactions/transaction_composer.py +2293 -0
- algokit_utils/transactions/transaction_creator.py +156 -0
- algokit_utils/transactions/transaction_sender.py +574 -0
- {algokit_utils-2.4.0b1.dist-info → algokit_utils-3.0.0b2.dist-info}/METADATA +11 -7
- algokit_utils-3.0.0b2.dist-info/RECORD +70 -0
- {algokit_utils-2.4.0b1.dist-info → algokit_utils-3.0.0b2.dist-info}/WHEEL +1 -1
- algokit_utils-2.4.0b1.dist-info/RECORD +0 -24
- {algokit_utils-2.4.0b1.dist-info → algokit_utils-3.0.0b2.dist-info}/LICENSE +0 -0
|
@@ -1,1449 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
from typing import Any, Literal, cast, overload
|
|
10
|
-
|
|
11
|
-
import algosdk
|
|
12
|
-
from algosdk import transaction
|
|
13
|
-
from algosdk.abi import ABIType, Method, Returns
|
|
14
|
-
from algosdk.account import address_from_private_key
|
|
15
|
-
from algosdk.atomic_transaction_composer import (
|
|
16
|
-
ABI_RETURN_HASH,
|
|
17
|
-
ABIResult,
|
|
18
|
-
AccountTransactionSigner,
|
|
19
|
-
AtomicTransactionComposer,
|
|
20
|
-
AtomicTransactionResponse,
|
|
21
|
-
LogicSigTransactionSigner,
|
|
22
|
-
MultisigTransactionSigner,
|
|
23
|
-
SimulateAtomicTransactionResponse,
|
|
24
|
-
TransactionSigner,
|
|
25
|
-
TransactionWithSigner,
|
|
26
|
-
)
|
|
27
|
-
from algosdk.constants import APP_PAGE_MAX_SIZE
|
|
28
|
-
from algosdk.logic import get_application_address
|
|
29
|
-
from algosdk.source_map import SourceMap
|
|
30
|
-
|
|
31
|
-
import algokit_utils.application_specification as au_spec
|
|
32
|
-
import algokit_utils.deploy as au_deploy
|
|
33
|
-
from algokit_utils._debugging import (
|
|
34
|
-
PersistSourceMapInput,
|
|
35
|
-
persist_sourcemaps,
|
|
36
|
-
simulate_and_persist_response,
|
|
37
|
-
simulate_response,
|
|
1
|
+
import warnings
|
|
2
|
+
|
|
3
|
+
warnings.warn(
|
|
4
|
+
"""The legacy v2 application_client module is deprecated and will be removed in a future version.
|
|
5
|
+
Use `AppClient` abstraction from `algokit_utils.applications` instead.
|
|
6
|
+
""",
|
|
7
|
+
DeprecationWarning,
|
|
8
|
+
stacklevel=2,
|
|
38
9
|
)
|
|
39
|
-
from algokit_utils.common import Program
|
|
40
|
-
from algokit_utils.config import config
|
|
41
|
-
from algokit_utils.logic_error import LogicError, parse_logic_error
|
|
42
|
-
from algokit_utils.models import (
|
|
43
|
-
ABIArgsDict,
|
|
44
|
-
ABIArgType,
|
|
45
|
-
ABIMethod,
|
|
46
|
-
ABITransactionResponse,
|
|
47
|
-
Account,
|
|
48
|
-
CreateCallParameters,
|
|
49
|
-
CreateCallParametersDict,
|
|
50
|
-
OnCompleteCallParameters,
|
|
51
|
-
OnCompleteCallParametersDict,
|
|
52
|
-
SimulationTrace,
|
|
53
|
-
TransactionParameters,
|
|
54
|
-
TransactionParametersDict,
|
|
55
|
-
TransactionResponse,
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
if typing.TYPE_CHECKING:
|
|
59
|
-
from algosdk.v2client.algod import AlgodClient
|
|
60
|
-
from algosdk.v2client.indexer import IndexerClient
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
logger = logging.getLogger(__name__)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
"""A dictionary `dict[str, Any]` representing ABI argument names and values"""
|
|
67
|
-
|
|
68
|
-
__all__ = [
|
|
69
|
-
"ApplicationClient",
|
|
70
|
-
"execute_atc_with_logic_error",
|
|
71
|
-
"get_next_version",
|
|
72
|
-
"get_sender_from_signer",
|
|
73
|
-
"num_extra_program_pages",
|
|
74
|
-
]
|
|
75
|
-
|
|
76
|
-
"""Alias for {py:class}`pyteal.ABIReturnSubroutine`, {py:class}`algosdk.abi.method.Method` or a {py:class}`str`
|
|
77
|
-
representing an ABI method name or signature"""
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
def num_extra_program_pages(approval: bytes, clear: bytes) -> int:
|
|
81
|
-
"""Calculate minimum number of extra_pages required for provided approval and clear programs"""
|
|
82
|
-
|
|
83
|
-
return ceil(((len(approval) + len(clear)) - APP_PAGE_MAX_SIZE) / APP_PAGE_MAX_SIZE)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
class ApplicationClient:
|
|
87
|
-
"""A class that wraps an ARC-0032 app spec and provides high productivity methods to deploy and call the app"""
|
|
88
|
-
|
|
89
|
-
@overload
|
|
90
|
-
def __init__(
|
|
91
|
-
self,
|
|
92
|
-
algod_client: "AlgodClient",
|
|
93
|
-
app_spec: au_spec.ApplicationSpecification | Path,
|
|
94
|
-
*,
|
|
95
|
-
app_id: int = 0,
|
|
96
|
-
signer: TransactionSigner | Account | None = None,
|
|
97
|
-
sender: str | None = None,
|
|
98
|
-
suggested_params: transaction.SuggestedParams | None = None,
|
|
99
|
-
template_values: au_deploy.TemplateValueMapping | None = None,
|
|
100
|
-
): ...
|
|
101
|
-
|
|
102
|
-
@overload
|
|
103
|
-
def __init__(
|
|
104
|
-
self,
|
|
105
|
-
algod_client: "AlgodClient",
|
|
106
|
-
app_spec: au_spec.ApplicationSpecification | Path,
|
|
107
|
-
*,
|
|
108
|
-
creator: str | Account,
|
|
109
|
-
indexer_client: "IndexerClient | None" = None,
|
|
110
|
-
existing_deployments: au_deploy.AppLookup | None = None,
|
|
111
|
-
signer: TransactionSigner | Account | None = None,
|
|
112
|
-
sender: str | None = None,
|
|
113
|
-
suggested_params: transaction.SuggestedParams | None = None,
|
|
114
|
-
template_values: au_deploy.TemplateValueMapping | None = None,
|
|
115
|
-
app_name: str | None = None,
|
|
116
|
-
): ...
|
|
117
|
-
|
|
118
|
-
def __init__( # noqa: PLR0913
|
|
119
|
-
self,
|
|
120
|
-
algod_client: "AlgodClient",
|
|
121
|
-
app_spec: au_spec.ApplicationSpecification | Path,
|
|
122
|
-
*,
|
|
123
|
-
app_id: int = 0,
|
|
124
|
-
creator: str | Account | None = None,
|
|
125
|
-
indexer_client: "IndexerClient | None" = None,
|
|
126
|
-
existing_deployments: au_deploy.AppLookup | None = None,
|
|
127
|
-
signer: TransactionSigner | Account | None = None,
|
|
128
|
-
sender: str | None = None,
|
|
129
|
-
suggested_params: transaction.SuggestedParams | None = None,
|
|
130
|
-
template_values: au_deploy.TemplateValueMapping | None = None,
|
|
131
|
-
app_name: str | None = None,
|
|
132
|
-
):
|
|
133
|
-
"""ApplicationClient can be created with an app_id to interact with an existing application, alternatively
|
|
134
|
-
it can be created with a creator and indexer_client specified to find existing applications by name and creator.
|
|
135
|
-
|
|
136
|
-
:param AlgodClient algod_client: AlgoSDK algod client
|
|
137
|
-
:param ApplicationSpecification | Path app_spec: An Application Specification or the path to one
|
|
138
|
-
:param int app_id: The app_id of an existing application, to instead find the application by creator and name
|
|
139
|
-
use the creator and indexer_client parameters
|
|
140
|
-
:param str | Account creator: The address or Account of the app creator to resolve the app_id
|
|
141
|
-
:param IndexerClient indexer_client: AlgoSDK indexer client, only required if deploying or finding app_id by
|
|
142
|
-
creator and app name
|
|
143
|
-
:param AppLookup existing_deployments:
|
|
144
|
-
:param TransactionSigner | Account signer: Account or signer to use to sign transactions, if not specified and
|
|
145
|
-
creator was passed as an Account will use that.
|
|
146
|
-
:param str sender: Address to use as the sender for all transactions, will use the address associated with the
|
|
147
|
-
signer if not specified.
|
|
148
|
-
:param TemplateValueMapping template_values: Values to use for TMPL_* template variables, dictionary keys should
|
|
149
|
-
*NOT* include the TMPL_ prefix
|
|
150
|
-
:param str | None app_name: Name of application to use when deploying, defaults to name defined on the
|
|
151
|
-
Application Specification
|
|
152
|
-
"""
|
|
153
|
-
self.algod_client = algod_client
|
|
154
|
-
self.app_spec = (
|
|
155
|
-
au_spec.ApplicationSpecification.from_json(app_spec.read_text()) if isinstance(app_spec, Path) else app_spec
|
|
156
|
-
)
|
|
157
|
-
self._app_name = app_name
|
|
158
|
-
self._approval_program: Program | None = None
|
|
159
|
-
self._approval_source_map: SourceMap | None = None
|
|
160
|
-
self._clear_program: Program | None = None
|
|
161
|
-
|
|
162
|
-
self.template_values: au_deploy.TemplateValueMapping = template_values or {}
|
|
163
|
-
self.existing_deployments = existing_deployments
|
|
164
|
-
self._indexer_client = indexer_client
|
|
165
|
-
if creator is not None:
|
|
166
|
-
if not self.existing_deployments and not self._indexer_client:
|
|
167
|
-
raise Exception(
|
|
168
|
-
"If using the creator parameter either existing_deployments or indexer_client must also be provided"
|
|
169
|
-
)
|
|
170
|
-
self._creator: str | None = creator.address if isinstance(creator, Account) else creator
|
|
171
|
-
if self.existing_deployments and self.existing_deployments.creator != self._creator:
|
|
172
|
-
raise Exception(
|
|
173
|
-
"Attempt to create application client with invalid existing_deployments against"
|
|
174
|
-
f"a different creator ({self.existing_deployments.creator} instead of "
|
|
175
|
-
f"expected creator {self._creator}"
|
|
176
|
-
)
|
|
177
|
-
self.app_id = 0
|
|
178
|
-
else:
|
|
179
|
-
self.app_id = app_id
|
|
180
|
-
self._creator = None
|
|
181
|
-
|
|
182
|
-
self.signer: TransactionSigner | None
|
|
183
|
-
if signer:
|
|
184
|
-
self.signer = (
|
|
185
|
-
signer if isinstance(signer, TransactionSigner) else AccountTransactionSigner(signer.private_key)
|
|
186
|
-
)
|
|
187
|
-
elif isinstance(creator, Account):
|
|
188
|
-
self.signer = AccountTransactionSigner(creator.private_key)
|
|
189
|
-
else:
|
|
190
|
-
self.signer = None
|
|
191
|
-
|
|
192
|
-
self.sender = sender
|
|
193
|
-
self.suggested_params = suggested_params
|
|
194
|
-
|
|
195
|
-
@property
|
|
196
|
-
def app_name(self) -> str:
|
|
197
|
-
return self._app_name or self.app_spec.contract.name
|
|
198
|
-
|
|
199
|
-
@app_name.setter
|
|
200
|
-
def app_name(self, value: str) -> None:
|
|
201
|
-
self._app_name = value
|
|
202
|
-
|
|
203
|
-
@property
|
|
204
|
-
def app_address(self) -> str:
|
|
205
|
-
return get_application_address(self.app_id)
|
|
206
|
-
|
|
207
|
-
@property
|
|
208
|
-
def approval(self) -> Program | None:
|
|
209
|
-
return self._approval_program
|
|
210
|
-
|
|
211
|
-
@property
|
|
212
|
-
def approval_source_map(self) -> SourceMap | None:
|
|
213
|
-
if self._approval_source_map:
|
|
214
|
-
return self._approval_source_map
|
|
215
|
-
if self._approval_program:
|
|
216
|
-
return self._approval_program.source_map
|
|
217
|
-
return None
|
|
218
|
-
|
|
219
|
-
@approval_source_map.setter
|
|
220
|
-
def approval_source_map(self, value: SourceMap) -> None:
|
|
221
|
-
self._approval_source_map = value
|
|
222
|
-
|
|
223
|
-
@property
|
|
224
|
-
def clear(self) -> Program | None:
|
|
225
|
-
return self._clear_program
|
|
226
|
-
|
|
227
|
-
def prepare(
|
|
228
|
-
self,
|
|
229
|
-
signer: TransactionSigner | Account | None = None,
|
|
230
|
-
sender: str | None = None,
|
|
231
|
-
app_id: int | None = None,
|
|
232
|
-
template_values: au_deploy.TemplateValueDict | None = None,
|
|
233
|
-
) -> "ApplicationClient":
|
|
234
|
-
"""Creates a copy of this ApplicationClient, using the new signer, sender and app_id values if provided.
|
|
235
|
-
Will also substitute provided template_values into the associated app_spec in the copy"""
|
|
236
|
-
new_client: ApplicationClient = copy.copy(self)
|
|
237
|
-
new_client._prepare( # noqa: SLF001
|
|
238
|
-
new_client, signer=signer, sender=sender, app_id=app_id, template_values=template_values
|
|
239
|
-
)
|
|
240
|
-
return new_client
|
|
241
|
-
|
|
242
|
-
def _prepare( # noqa: PLR0913
|
|
243
|
-
self,
|
|
244
|
-
target: "ApplicationClient",
|
|
245
|
-
*,
|
|
246
|
-
signer: TransactionSigner | Account | None = None,
|
|
247
|
-
sender: str | None = None,
|
|
248
|
-
app_id: int | None = None,
|
|
249
|
-
template_values: au_deploy.TemplateValueDict | None = None,
|
|
250
|
-
) -> None:
|
|
251
|
-
target.app_id = self.app_id if app_id is None else app_id
|
|
252
|
-
target.signer, target.sender = target.get_signer_sender(
|
|
253
|
-
AccountTransactionSigner(signer.private_key) if isinstance(signer, Account) else signer, sender
|
|
254
|
-
)
|
|
255
|
-
target.template_values = {**self.template_values, **(template_values or {})}
|
|
256
|
-
|
|
257
|
-
def deploy( # noqa: PLR0913
|
|
258
|
-
self,
|
|
259
|
-
version: str | None = None,
|
|
260
|
-
*,
|
|
261
|
-
signer: TransactionSigner | None = None,
|
|
262
|
-
sender: str | None = None,
|
|
263
|
-
allow_update: bool | None = None,
|
|
264
|
-
allow_delete: bool | None = None,
|
|
265
|
-
on_update: au_deploy.OnUpdate = au_deploy.OnUpdate.Fail,
|
|
266
|
-
on_schema_break: au_deploy.OnSchemaBreak = au_deploy.OnSchemaBreak.Fail,
|
|
267
|
-
template_values: au_deploy.TemplateValueMapping | None = None,
|
|
268
|
-
create_args: au_deploy.ABICreateCallArgs
|
|
269
|
-
| au_deploy.ABICreateCallArgsDict
|
|
270
|
-
| au_deploy.DeployCreateCallArgs
|
|
271
|
-
| None = None,
|
|
272
|
-
update_args: au_deploy.ABICallArgs | au_deploy.ABICallArgsDict | au_deploy.DeployCallArgs | None = None,
|
|
273
|
-
delete_args: au_deploy.ABICallArgs | au_deploy.ABICallArgsDict | au_deploy.DeployCallArgs | None = None,
|
|
274
|
-
) -> au_deploy.DeployResponse:
|
|
275
|
-
"""Deploy an application and update client to reference it.
|
|
276
|
-
|
|
277
|
-
Idempotently deploy (create, update/delete if changed) an app against the given name via the given creator
|
|
278
|
-
account, including deploy-time template placeholder substitutions.
|
|
279
|
-
To understand the architecture decisions behind this functionality please see
|
|
280
|
-
<https://github.com/algorandfoundation/algokit-cli/blob/main/docs/architecture-decisions/2023-01-12_smart-contract-deployment.md>
|
|
281
|
-
|
|
282
|
-
```{note}
|
|
283
|
-
If there is a breaking state schema change to an existing app (and `on_schema_break` is set to
|
|
284
|
-
'ReplaceApp' the existing app will be deleted and re-created.
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
```{note}
|
|
288
|
-
If there is an update (different TEAL code) to an existing app (and `on_update` is set to 'ReplaceApp')
|
|
289
|
-
the existing app will be deleted and re-created.
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
:param str version: version to use when creating or updating app, if None version will be auto incremented
|
|
293
|
-
:param algosdk.atomic_transaction_composer.TransactionSigner signer: signer to use when deploying app
|
|
294
|
-
, if None uses self.signer
|
|
295
|
-
:param str sender: sender address to use when deploying app, if None uses self.sender
|
|
296
|
-
:param bool allow_delete: Used to set the `TMPL_DELETABLE` template variable to conditionally control if an app
|
|
297
|
-
can be deleted
|
|
298
|
-
:param bool allow_update: Used to set the `TMPL_UPDATABLE` template variable to conditionally control if an app
|
|
299
|
-
can be updated
|
|
300
|
-
:param OnUpdate on_update: Determines what action to take if an application update is required
|
|
301
|
-
:param OnSchemaBreak on_schema_break: Determines what action to take if an application schema requirements
|
|
302
|
-
has increased beyond the current allocation
|
|
303
|
-
:param dict[str, int|str|bytes] template_values: Values to use for `TMPL_*` template variables, dictionary keys
|
|
304
|
-
should *NOT* include the TMPL_ prefix
|
|
305
|
-
:param ABICreateCallArgs create_args: Arguments used when creating an application
|
|
306
|
-
:param ABICallArgs | ABICallArgsDict update_args: Arguments used when updating an application
|
|
307
|
-
:param ABICallArgs | ABICallArgsDict delete_args: Arguments used when deleting an application
|
|
308
|
-
:return DeployResponse: details action taken and relevant transactions
|
|
309
|
-
:raises DeploymentError: If the deployment failed
|
|
310
|
-
"""
|
|
311
|
-
# check inputs
|
|
312
|
-
if self.app_id:
|
|
313
|
-
raise au_deploy.DeploymentFailedError(
|
|
314
|
-
f"Attempt to deploy app which already has an app index of {self.app_id}"
|
|
315
|
-
)
|
|
316
|
-
try:
|
|
317
|
-
resolved_signer, resolved_sender = self.resolve_signer_sender(signer, sender)
|
|
318
|
-
except ValueError as ex:
|
|
319
|
-
raise au_deploy.DeploymentFailedError(f"{ex}, unable to deploy app") from None
|
|
320
|
-
if not self._creator:
|
|
321
|
-
raise au_deploy.DeploymentFailedError("No creator provided, unable to deploy app")
|
|
322
|
-
if self._creator != resolved_sender:
|
|
323
|
-
raise au_deploy.DeploymentFailedError(
|
|
324
|
-
f"Attempt to deploy contract with a sender address {resolved_sender} that differs "
|
|
325
|
-
f"from the given creator address for this application client: {self._creator}"
|
|
326
|
-
)
|
|
327
|
-
|
|
328
|
-
# make a copy and prepare variables
|
|
329
|
-
template_values = {**self.template_values, **(template_values or {})}
|
|
330
|
-
au_deploy.add_deploy_template_variables(template_values, allow_update=allow_update, allow_delete=allow_delete)
|
|
331
|
-
|
|
332
|
-
existing_app_metadata_or_reference = self._load_app_reference()
|
|
333
|
-
|
|
334
|
-
self._approval_program, self._clear_program = substitute_template_and_compile(
|
|
335
|
-
self.algod_client, self.app_spec, template_values
|
|
336
|
-
)
|
|
337
|
-
|
|
338
|
-
if config.debug and config.project_root:
|
|
339
|
-
persist_sourcemaps(
|
|
340
|
-
sources=[
|
|
341
|
-
PersistSourceMapInput(
|
|
342
|
-
compiled_teal=self._approval_program, app_name=self.app_name, file_name="approval.teal"
|
|
343
|
-
),
|
|
344
|
-
PersistSourceMapInput(
|
|
345
|
-
compiled_teal=self._clear_program, app_name=self.app_name, file_name="clear.teal"
|
|
346
|
-
),
|
|
347
|
-
],
|
|
348
|
-
project_root=config.project_root,
|
|
349
|
-
client=self.algod_client,
|
|
350
|
-
with_sources=True,
|
|
351
|
-
)
|
|
352
|
-
|
|
353
|
-
deployer = au_deploy.Deployer(
|
|
354
|
-
app_client=self,
|
|
355
|
-
creator=self._creator,
|
|
356
|
-
signer=resolved_signer,
|
|
357
|
-
sender=resolved_sender,
|
|
358
|
-
new_app_metadata=self._get_app_deploy_metadata(version, allow_update, allow_delete),
|
|
359
|
-
existing_app_metadata_or_reference=existing_app_metadata_or_reference,
|
|
360
|
-
on_update=on_update,
|
|
361
|
-
on_schema_break=on_schema_break,
|
|
362
|
-
create_args=create_args,
|
|
363
|
-
update_args=update_args,
|
|
364
|
-
delete_args=delete_args,
|
|
365
|
-
)
|
|
366
|
-
|
|
367
|
-
return deployer.deploy()
|
|
368
|
-
|
|
369
|
-
def compose_create(
|
|
370
|
-
self,
|
|
371
|
-
atc: AtomicTransactionComposer,
|
|
372
|
-
/,
|
|
373
|
-
call_abi_method: ABIMethod | bool | None = None,
|
|
374
|
-
transaction_parameters: CreateCallParameters | CreateCallParametersDict | None = None,
|
|
375
|
-
**abi_kwargs: ABIArgType,
|
|
376
|
-
) -> None:
|
|
377
|
-
"""Adds a signed transaction with application id == 0 and the schema and source of client's app_spec to atc"""
|
|
378
|
-
approval_program, clear_program = self._check_is_compiled()
|
|
379
|
-
transaction_parameters = _convert_transaction_parameters(transaction_parameters)
|
|
380
|
-
|
|
381
|
-
extra_pages = transaction_parameters.extra_pages or num_extra_program_pages(
|
|
382
|
-
approval_program.raw_binary, clear_program.raw_binary
|
|
383
|
-
)
|
|
384
|
-
|
|
385
|
-
self.add_method_call(
|
|
386
|
-
atc,
|
|
387
|
-
app_id=0,
|
|
388
|
-
abi_method=call_abi_method,
|
|
389
|
-
abi_args=abi_kwargs,
|
|
390
|
-
on_complete=transaction_parameters.on_complete or transaction.OnComplete.NoOpOC,
|
|
391
|
-
call_config=au_spec.CallConfig.CREATE,
|
|
392
|
-
parameters=transaction_parameters,
|
|
393
|
-
approval_program=approval_program.raw_binary,
|
|
394
|
-
clear_program=clear_program.raw_binary,
|
|
395
|
-
global_schema=self.app_spec.global_state_schema,
|
|
396
|
-
local_schema=self.app_spec.local_state_schema,
|
|
397
|
-
extra_pages=extra_pages,
|
|
398
|
-
)
|
|
399
|
-
|
|
400
|
-
@overload
|
|
401
|
-
def create(
|
|
402
|
-
self,
|
|
403
|
-
call_abi_method: Literal[False],
|
|
404
|
-
transaction_parameters: CreateCallParameters | CreateCallParametersDict | None = ...,
|
|
405
|
-
) -> TransactionResponse: ...
|
|
406
|
-
|
|
407
|
-
@overload
|
|
408
|
-
def create(
|
|
409
|
-
self,
|
|
410
|
-
call_abi_method: ABIMethod | Literal[True],
|
|
411
|
-
transaction_parameters: CreateCallParameters | CreateCallParametersDict | None = ...,
|
|
412
|
-
**abi_kwargs: ABIArgType,
|
|
413
|
-
) -> ABITransactionResponse: ...
|
|
414
|
-
|
|
415
|
-
@overload
|
|
416
|
-
def create(
|
|
417
|
-
self,
|
|
418
|
-
call_abi_method: ABIMethod | bool | None = ...,
|
|
419
|
-
transaction_parameters: CreateCallParameters | CreateCallParametersDict | None = ...,
|
|
420
|
-
**abi_kwargs: ABIArgType,
|
|
421
|
-
) -> TransactionResponse | ABITransactionResponse: ...
|
|
422
|
-
|
|
423
|
-
def create(
|
|
424
|
-
self,
|
|
425
|
-
call_abi_method: ABIMethod | bool | None = None,
|
|
426
|
-
transaction_parameters: CreateCallParameters | CreateCallParametersDict | None = None,
|
|
427
|
-
**abi_kwargs: ABIArgType,
|
|
428
|
-
) -> TransactionResponse | ABITransactionResponse:
|
|
429
|
-
"""Submits a signed transaction with application id == 0 and the schema and source of client's app_spec"""
|
|
430
|
-
|
|
431
|
-
atc = AtomicTransactionComposer()
|
|
432
|
-
|
|
433
|
-
self.compose_create(
|
|
434
|
-
atc,
|
|
435
|
-
call_abi_method,
|
|
436
|
-
transaction_parameters,
|
|
437
|
-
**abi_kwargs,
|
|
438
|
-
)
|
|
439
|
-
create_result = self._execute_atc_tr(atc)
|
|
440
|
-
self.app_id = au_deploy.get_app_id_from_tx_id(self.algod_client, create_result.tx_id)
|
|
441
|
-
return create_result
|
|
442
|
-
|
|
443
|
-
def compose_update(
|
|
444
|
-
self,
|
|
445
|
-
atc: AtomicTransactionComposer,
|
|
446
|
-
/,
|
|
447
|
-
call_abi_method: ABIMethod | bool | None = None,
|
|
448
|
-
transaction_parameters: TransactionParameters | TransactionParametersDict | None = None,
|
|
449
|
-
**abi_kwargs: ABIArgType,
|
|
450
|
-
) -> None:
|
|
451
|
-
"""Adds a signed transaction with on_complete=UpdateApplication to atc"""
|
|
452
|
-
approval_program, clear_program = self._check_is_compiled()
|
|
453
|
-
|
|
454
|
-
self.add_method_call(
|
|
455
|
-
atc=atc,
|
|
456
|
-
abi_method=call_abi_method,
|
|
457
|
-
abi_args=abi_kwargs,
|
|
458
|
-
parameters=transaction_parameters,
|
|
459
|
-
on_complete=transaction.OnComplete.UpdateApplicationOC,
|
|
460
|
-
approval_program=approval_program.raw_binary,
|
|
461
|
-
clear_program=clear_program.raw_binary,
|
|
462
|
-
)
|
|
463
|
-
|
|
464
|
-
@overload
|
|
465
|
-
def update(
|
|
466
|
-
self,
|
|
467
|
-
call_abi_method: ABIMethod | Literal[True],
|
|
468
|
-
transaction_parameters: TransactionParameters | TransactionParametersDict | None = ...,
|
|
469
|
-
**abi_kwargs: ABIArgType,
|
|
470
|
-
) -> ABITransactionResponse: ...
|
|
471
|
-
|
|
472
|
-
@overload
|
|
473
|
-
def update(
|
|
474
|
-
self,
|
|
475
|
-
call_abi_method: Literal[False],
|
|
476
|
-
transaction_parameters: TransactionParameters | TransactionParametersDict | None = ...,
|
|
477
|
-
) -> TransactionResponse: ...
|
|
478
|
-
|
|
479
|
-
@overload
|
|
480
|
-
def update(
|
|
481
|
-
self,
|
|
482
|
-
call_abi_method: ABIMethod | bool | None = ...,
|
|
483
|
-
transaction_parameters: TransactionParameters | TransactionParametersDict | None = ...,
|
|
484
|
-
**abi_kwargs: ABIArgType,
|
|
485
|
-
) -> TransactionResponse | ABITransactionResponse: ...
|
|
486
|
-
|
|
487
|
-
def update(
|
|
488
|
-
self,
|
|
489
|
-
call_abi_method: ABIMethod | bool | None = None,
|
|
490
|
-
transaction_parameters: TransactionParameters | TransactionParametersDict | None = None,
|
|
491
|
-
**abi_kwargs: ABIArgType,
|
|
492
|
-
) -> TransactionResponse | ABITransactionResponse:
|
|
493
|
-
"""Submits a signed transaction with on_complete=UpdateApplication"""
|
|
494
|
-
|
|
495
|
-
atc = AtomicTransactionComposer()
|
|
496
|
-
self.compose_update(
|
|
497
|
-
atc,
|
|
498
|
-
call_abi_method,
|
|
499
|
-
transaction_parameters=transaction_parameters,
|
|
500
|
-
**abi_kwargs,
|
|
501
|
-
)
|
|
502
|
-
return self._execute_atc_tr(atc)
|
|
503
|
-
|
|
504
|
-
def compose_delete(
|
|
505
|
-
self,
|
|
506
|
-
atc: AtomicTransactionComposer,
|
|
507
|
-
/,
|
|
508
|
-
call_abi_method: ABIMethod | bool | None = None,
|
|
509
|
-
transaction_parameters: TransactionParameters | TransactionParametersDict | None = None,
|
|
510
|
-
**abi_kwargs: ABIArgType,
|
|
511
|
-
) -> None:
|
|
512
|
-
"""Adds a signed transaction with on_complete=DeleteApplication to atc"""
|
|
513
|
-
|
|
514
|
-
self.add_method_call(
|
|
515
|
-
atc,
|
|
516
|
-
call_abi_method,
|
|
517
|
-
abi_args=abi_kwargs,
|
|
518
|
-
parameters=transaction_parameters,
|
|
519
|
-
on_complete=transaction.OnComplete.DeleteApplicationOC,
|
|
520
|
-
)
|
|
521
|
-
|
|
522
|
-
@overload
|
|
523
|
-
def delete(
|
|
524
|
-
self,
|
|
525
|
-
call_abi_method: ABIMethod | Literal[True],
|
|
526
|
-
transaction_parameters: TransactionParameters | TransactionParametersDict | None = ...,
|
|
527
|
-
**abi_kwargs: ABIArgType,
|
|
528
|
-
) -> ABITransactionResponse: ...
|
|
529
|
-
|
|
530
|
-
@overload
|
|
531
|
-
def delete(
|
|
532
|
-
self,
|
|
533
|
-
call_abi_method: Literal[False],
|
|
534
|
-
transaction_parameters: TransactionParameters | TransactionParametersDict | None = ...,
|
|
535
|
-
) -> TransactionResponse: ...
|
|
536
|
-
|
|
537
|
-
@overload
|
|
538
|
-
def delete(
|
|
539
|
-
self,
|
|
540
|
-
call_abi_method: ABIMethod | bool | None = ...,
|
|
541
|
-
transaction_parameters: TransactionParameters | TransactionParametersDict | None = ...,
|
|
542
|
-
**abi_kwargs: ABIArgType,
|
|
543
|
-
) -> TransactionResponse | ABITransactionResponse: ...
|
|
544
|
-
|
|
545
|
-
def delete(
|
|
546
|
-
self,
|
|
547
|
-
call_abi_method: ABIMethod | bool | None = None,
|
|
548
|
-
transaction_parameters: TransactionParameters | TransactionParametersDict | None = None,
|
|
549
|
-
**abi_kwargs: ABIArgType,
|
|
550
|
-
) -> TransactionResponse | ABITransactionResponse:
|
|
551
|
-
"""Submits a signed transaction with on_complete=DeleteApplication"""
|
|
552
|
-
|
|
553
|
-
atc = AtomicTransactionComposer()
|
|
554
|
-
self.compose_delete(
|
|
555
|
-
atc,
|
|
556
|
-
call_abi_method,
|
|
557
|
-
transaction_parameters=transaction_parameters,
|
|
558
|
-
**abi_kwargs,
|
|
559
|
-
)
|
|
560
|
-
return self._execute_atc_tr(atc)
|
|
561
|
-
|
|
562
|
-
def compose_call(
|
|
563
|
-
self,
|
|
564
|
-
atc: AtomicTransactionComposer,
|
|
565
|
-
/,
|
|
566
|
-
call_abi_method: ABIMethod | bool | None = None,
|
|
567
|
-
transaction_parameters: OnCompleteCallParameters | OnCompleteCallParametersDict | None = None,
|
|
568
|
-
**abi_kwargs: ABIArgType,
|
|
569
|
-
) -> None:
|
|
570
|
-
"""Adds a signed transaction with specified parameters to atc"""
|
|
571
|
-
_parameters = _convert_transaction_parameters(transaction_parameters)
|
|
572
|
-
self.add_method_call(
|
|
573
|
-
atc,
|
|
574
|
-
abi_method=call_abi_method,
|
|
575
|
-
abi_args=abi_kwargs,
|
|
576
|
-
parameters=_parameters,
|
|
577
|
-
on_complete=_parameters.on_complete or transaction.OnComplete.NoOpOC,
|
|
578
|
-
)
|
|
579
|
-
|
|
580
|
-
@overload
|
|
581
|
-
def call(
|
|
582
|
-
self,
|
|
583
|
-
call_abi_method: ABIMethod | Literal[True],
|
|
584
|
-
transaction_parameters: OnCompleteCallParameters | OnCompleteCallParametersDict | None = ...,
|
|
585
|
-
**abi_kwargs: ABIArgType,
|
|
586
|
-
) -> ABITransactionResponse: ...
|
|
587
|
-
|
|
588
|
-
@overload
|
|
589
|
-
def call(
|
|
590
|
-
self,
|
|
591
|
-
call_abi_method: Literal[False],
|
|
592
|
-
transaction_parameters: OnCompleteCallParameters | OnCompleteCallParametersDict | None = ...,
|
|
593
|
-
) -> TransactionResponse: ...
|
|
594
|
-
|
|
595
|
-
@overload
|
|
596
|
-
def call(
|
|
597
|
-
self,
|
|
598
|
-
call_abi_method: ABIMethod | bool | None = ...,
|
|
599
|
-
transaction_parameters: OnCompleteCallParameters | OnCompleteCallParametersDict | None = ...,
|
|
600
|
-
**abi_kwargs: ABIArgType,
|
|
601
|
-
) -> TransactionResponse | ABITransactionResponse: ...
|
|
602
|
-
|
|
603
|
-
def call(
|
|
604
|
-
self,
|
|
605
|
-
call_abi_method: ABIMethod | bool | None = None,
|
|
606
|
-
transaction_parameters: OnCompleteCallParameters | OnCompleteCallParametersDict | None = None,
|
|
607
|
-
**abi_kwargs: ABIArgType,
|
|
608
|
-
) -> TransactionResponse | ABITransactionResponse:
|
|
609
|
-
"""Submits a signed transaction with specified parameters"""
|
|
610
|
-
atc = AtomicTransactionComposer()
|
|
611
|
-
_parameters = _convert_transaction_parameters(transaction_parameters)
|
|
612
|
-
self.compose_call(
|
|
613
|
-
atc,
|
|
614
|
-
call_abi_method=call_abi_method,
|
|
615
|
-
transaction_parameters=_parameters,
|
|
616
|
-
**abi_kwargs,
|
|
617
|
-
)
|
|
618
|
-
|
|
619
|
-
method = self._resolve_method(
|
|
620
|
-
call_abi_method, abi_kwargs, _parameters.on_complete or transaction.OnComplete.NoOpOC
|
|
621
|
-
)
|
|
622
|
-
if method:
|
|
623
|
-
hints = self._method_hints(method)
|
|
624
|
-
if hints and hints.read_only:
|
|
625
|
-
if config.debug and config.project_root and config.trace_all:
|
|
626
|
-
simulate_and_persist_response(
|
|
627
|
-
atc, config.project_root, self.algod_client, config.trace_buffer_size_mb
|
|
628
|
-
)
|
|
629
|
-
|
|
630
|
-
return self._simulate_readonly_call(method, atc)
|
|
631
|
-
|
|
632
|
-
return self._execute_atc_tr(atc)
|
|
633
|
-
|
|
634
|
-
def compose_opt_in(
|
|
635
|
-
self,
|
|
636
|
-
atc: AtomicTransactionComposer,
|
|
637
|
-
/,
|
|
638
|
-
call_abi_method: ABIMethod | bool | None = None,
|
|
639
|
-
transaction_parameters: TransactionParameters | TransactionParametersDict | None = None,
|
|
640
|
-
**abi_kwargs: ABIArgType,
|
|
641
|
-
) -> None:
|
|
642
|
-
"""Adds a signed transaction with on_complete=OptIn to atc"""
|
|
643
|
-
self.add_method_call(
|
|
644
|
-
atc,
|
|
645
|
-
abi_method=call_abi_method,
|
|
646
|
-
abi_args=abi_kwargs,
|
|
647
|
-
parameters=transaction_parameters,
|
|
648
|
-
on_complete=transaction.OnComplete.OptInOC,
|
|
649
|
-
)
|
|
650
|
-
|
|
651
|
-
@overload
|
|
652
|
-
def opt_in(
|
|
653
|
-
self,
|
|
654
|
-
call_abi_method: ABIMethod | Literal[True] = ...,
|
|
655
|
-
transaction_parameters: TransactionParameters | TransactionParametersDict | None = None,
|
|
656
|
-
**abi_kwargs: ABIArgType,
|
|
657
|
-
) -> ABITransactionResponse: ...
|
|
658
|
-
|
|
659
|
-
@overload
|
|
660
|
-
def opt_in(
|
|
661
|
-
self,
|
|
662
|
-
call_abi_method: Literal[False] = ...,
|
|
663
|
-
transaction_parameters: TransactionParameters | TransactionParametersDict | None = None,
|
|
664
|
-
) -> TransactionResponse: ...
|
|
665
|
-
|
|
666
|
-
@overload
|
|
667
|
-
def opt_in(
|
|
668
|
-
self,
|
|
669
|
-
call_abi_method: ABIMethod | bool | None = ...,
|
|
670
|
-
transaction_parameters: TransactionParameters | TransactionParametersDict | None = ...,
|
|
671
|
-
**abi_kwargs: ABIArgType,
|
|
672
|
-
) -> TransactionResponse | ABITransactionResponse: ...
|
|
673
|
-
|
|
674
|
-
def opt_in(
|
|
675
|
-
self,
|
|
676
|
-
call_abi_method: ABIMethod | bool | None = None,
|
|
677
|
-
transaction_parameters: TransactionParameters | TransactionParametersDict | None = None,
|
|
678
|
-
**abi_kwargs: ABIArgType,
|
|
679
|
-
) -> TransactionResponse | ABITransactionResponse:
|
|
680
|
-
"""Submits a signed transaction with on_complete=OptIn"""
|
|
681
|
-
atc = AtomicTransactionComposer()
|
|
682
|
-
self.compose_opt_in(
|
|
683
|
-
atc,
|
|
684
|
-
call_abi_method=call_abi_method,
|
|
685
|
-
transaction_parameters=transaction_parameters,
|
|
686
|
-
**abi_kwargs,
|
|
687
|
-
)
|
|
688
|
-
return self._execute_atc_tr(atc)
|
|
689
|
-
|
|
690
|
-
def compose_close_out(
|
|
691
|
-
self,
|
|
692
|
-
atc: AtomicTransactionComposer,
|
|
693
|
-
/,
|
|
694
|
-
call_abi_method: ABIMethod | bool | None = None,
|
|
695
|
-
transaction_parameters: TransactionParameters | TransactionParametersDict | None = None,
|
|
696
|
-
**abi_kwargs: ABIArgType,
|
|
697
|
-
) -> None:
|
|
698
|
-
"""Adds a signed transaction with on_complete=CloseOut to ac"""
|
|
699
|
-
self.add_method_call(
|
|
700
|
-
atc,
|
|
701
|
-
abi_method=call_abi_method,
|
|
702
|
-
abi_args=abi_kwargs,
|
|
703
|
-
parameters=transaction_parameters,
|
|
704
|
-
on_complete=transaction.OnComplete.CloseOutOC,
|
|
705
|
-
)
|
|
706
|
-
|
|
707
|
-
@overload
|
|
708
|
-
def close_out(
|
|
709
|
-
self,
|
|
710
|
-
call_abi_method: ABIMethod | Literal[True],
|
|
711
|
-
transaction_parameters: TransactionParameters | TransactionParametersDict | None = ...,
|
|
712
|
-
**abi_kwargs: ABIArgType,
|
|
713
|
-
) -> ABITransactionResponse: ...
|
|
714
|
-
|
|
715
|
-
@overload
|
|
716
|
-
def close_out(
|
|
717
|
-
self,
|
|
718
|
-
call_abi_method: Literal[False],
|
|
719
|
-
transaction_parameters: TransactionParameters | TransactionParametersDict | None = ...,
|
|
720
|
-
) -> TransactionResponse: ...
|
|
721
|
-
|
|
722
|
-
@overload
|
|
723
|
-
def close_out(
|
|
724
|
-
self,
|
|
725
|
-
call_abi_method: ABIMethod | bool | None = ...,
|
|
726
|
-
transaction_parameters: TransactionParameters | TransactionParametersDict | None = ...,
|
|
727
|
-
**abi_kwargs: ABIArgType,
|
|
728
|
-
) -> TransactionResponse | ABITransactionResponse: ...
|
|
729
|
-
|
|
730
|
-
def close_out(
|
|
731
|
-
self,
|
|
732
|
-
call_abi_method: ABIMethod | bool | None = None,
|
|
733
|
-
transaction_parameters: TransactionParameters | TransactionParametersDict | None = None,
|
|
734
|
-
**abi_kwargs: ABIArgType,
|
|
735
|
-
) -> TransactionResponse | ABITransactionResponse:
|
|
736
|
-
"""Submits a signed transaction with on_complete=CloseOut"""
|
|
737
|
-
atc = AtomicTransactionComposer()
|
|
738
|
-
self.compose_close_out(
|
|
739
|
-
atc,
|
|
740
|
-
call_abi_method=call_abi_method,
|
|
741
|
-
transaction_parameters=transaction_parameters,
|
|
742
|
-
**abi_kwargs,
|
|
743
|
-
)
|
|
744
|
-
return self._execute_atc_tr(atc)
|
|
745
|
-
|
|
746
|
-
def compose_clear_state(
|
|
747
|
-
self,
|
|
748
|
-
atc: AtomicTransactionComposer,
|
|
749
|
-
/,
|
|
750
|
-
transaction_parameters: TransactionParameters | TransactionParametersDict | None = None,
|
|
751
|
-
app_args: list[bytes] | None = None,
|
|
752
|
-
) -> None:
|
|
753
|
-
"""Adds a signed transaction with on_complete=ClearState to atc"""
|
|
754
|
-
return self.add_method_call(
|
|
755
|
-
atc,
|
|
756
|
-
parameters=transaction_parameters,
|
|
757
|
-
on_complete=transaction.OnComplete.ClearStateOC,
|
|
758
|
-
app_args=app_args,
|
|
759
|
-
)
|
|
760
|
-
|
|
761
|
-
def clear_state(
|
|
762
|
-
self,
|
|
763
|
-
transaction_parameters: TransactionParameters | TransactionParametersDict | None = None,
|
|
764
|
-
app_args: list[bytes] | None = None,
|
|
765
|
-
) -> TransactionResponse:
|
|
766
|
-
"""Submits a signed transaction with on_complete=ClearState"""
|
|
767
|
-
atc = AtomicTransactionComposer()
|
|
768
|
-
self.compose_clear_state(
|
|
769
|
-
atc,
|
|
770
|
-
transaction_parameters=transaction_parameters,
|
|
771
|
-
app_args=app_args,
|
|
772
|
-
)
|
|
773
|
-
return self._execute_atc_tr(atc)
|
|
774
|
-
|
|
775
|
-
def get_global_state(self, *, raw: bool = False) -> dict[bytes | str, bytes | str | int]:
|
|
776
|
-
"""Gets the global state info associated with app_id"""
|
|
777
|
-
global_state = self.algod_client.application_info(self.app_id)
|
|
778
|
-
assert isinstance(global_state, dict)
|
|
779
|
-
return cast(
|
|
780
|
-
dict[bytes | str, bytes | str | int],
|
|
781
|
-
_decode_state(global_state.get("params", {}).get("global-state", {}), raw=raw),
|
|
782
|
-
)
|
|
783
|
-
|
|
784
|
-
def get_local_state(self, account: str | None = None, *, raw: bool = False) -> dict[bytes | str, bytes | str | int]:
|
|
785
|
-
"""Gets the local state info for associated app_id and account/sender"""
|
|
786
|
-
|
|
787
|
-
if account is None:
|
|
788
|
-
_, account = self.resolve_signer_sender(self.signer, self.sender)
|
|
789
|
-
|
|
790
|
-
acct_state = self.algod_client.account_application_info(account, self.app_id)
|
|
791
|
-
assert isinstance(acct_state, dict)
|
|
792
|
-
return cast(
|
|
793
|
-
dict[bytes | str, bytes | str | int],
|
|
794
|
-
_decode_state(acct_state.get("app-local-state", {}).get("key-value", {}), raw=raw),
|
|
795
|
-
)
|
|
796
|
-
|
|
797
|
-
def resolve(self, to_resolve: au_spec.DefaultArgumentDict) -> int | str | bytes:
|
|
798
|
-
"""Resolves the default value for an ABI method, based on app_spec"""
|
|
799
|
-
|
|
800
|
-
def _data_check(value: object) -> int | str | bytes:
|
|
801
|
-
if isinstance(value, int | str | bytes):
|
|
802
|
-
return value
|
|
803
|
-
raise ValueError(f"Unexpected type for constant data: {value}")
|
|
804
|
-
|
|
805
|
-
match to_resolve:
|
|
806
|
-
case {"source": "constant", "data": data}:
|
|
807
|
-
return _data_check(data)
|
|
808
|
-
case {"source": "global-state", "data": str() as key}:
|
|
809
|
-
global_state = self.get_global_state(raw=True)
|
|
810
|
-
return global_state[key.encode()]
|
|
811
|
-
case {"source": "local-state", "data": str() as key}:
|
|
812
|
-
_, sender = self.resolve_signer_sender(self.signer, self.sender)
|
|
813
|
-
acct_state = self.get_local_state(sender, raw=True)
|
|
814
|
-
return acct_state[key.encode()]
|
|
815
|
-
case {"source": "abi-method", "data": dict() as method_dict}:
|
|
816
|
-
method = Method.undictify(method_dict)
|
|
817
|
-
response = self.call(method)
|
|
818
|
-
assert isinstance(response, ABITransactionResponse)
|
|
819
|
-
return _data_check(response.return_value)
|
|
820
|
-
|
|
821
|
-
case {"source": source}:
|
|
822
|
-
raise ValueError(f"Unrecognized default argument source: {source}")
|
|
823
|
-
case _:
|
|
824
|
-
raise TypeError("Unable to interpret default argument specification")
|
|
825
|
-
|
|
826
|
-
def _get_app_deploy_metadata(
|
|
827
|
-
self, version: str | None, allow_update: bool | None, allow_delete: bool | None
|
|
828
|
-
) -> au_deploy.AppDeployMetaData:
|
|
829
|
-
updatable = (
|
|
830
|
-
allow_update
|
|
831
|
-
if allow_update is not None
|
|
832
|
-
else au_deploy.get_deploy_control(
|
|
833
|
-
self.app_spec, au_deploy.UPDATABLE_TEMPLATE_NAME, transaction.OnComplete.UpdateApplicationOC
|
|
834
|
-
)
|
|
835
|
-
)
|
|
836
|
-
deletable = (
|
|
837
|
-
allow_delete
|
|
838
|
-
if allow_delete is not None
|
|
839
|
-
else au_deploy.get_deploy_control(
|
|
840
|
-
self.app_spec, au_deploy.DELETABLE_TEMPLATE_NAME, transaction.OnComplete.DeleteApplicationOC
|
|
841
|
-
)
|
|
842
|
-
)
|
|
843
|
-
|
|
844
|
-
app = self._load_app_reference()
|
|
845
|
-
|
|
846
|
-
if version is None:
|
|
847
|
-
if app.app_id == 0:
|
|
848
|
-
version = "v1.0"
|
|
849
|
-
else:
|
|
850
|
-
assert isinstance(app, au_deploy.AppDeployMetaData)
|
|
851
|
-
version = get_next_version(app.version)
|
|
852
|
-
return au_deploy.AppDeployMetaData(self.app_name, version, updatable=updatable, deletable=deletable)
|
|
853
|
-
|
|
854
|
-
def _check_is_compiled(self) -> tuple[Program, Program]:
|
|
855
|
-
if self._approval_program is None or self._clear_program is None:
|
|
856
|
-
self._approval_program, self._clear_program = substitute_template_and_compile(
|
|
857
|
-
self.algod_client, self.app_spec, self.template_values
|
|
858
|
-
)
|
|
859
|
-
|
|
860
|
-
if config.debug and config.project_root:
|
|
861
|
-
persist_sourcemaps(
|
|
862
|
-
sources=[
|
|
863
|
-
PersistSourceMapInput(
|
|
864
|
-
compiled_teal=self._approval_program, app_name=self.app_name, file_name="approval.teal"
|
|
865
|
-
),
|
|
866
|
-
PersistSourceMapInput(
|
|
867
|
-
compiled_teal=self._clear_program, app_name=self.app_name, file_name="clear.teal"
|
|
868
|
-
),
|
|
869
|
-
],
|
|
870
|
-
project_root=config.project_root,
|
|
871
|
-
client=self.algod_client,
|
|
872
|
-
with_sources=True,
|
|
873
|
-
)
|
|
874
|
-
|
|
875
|
-
return self._approval_program, self._clear_program
|
|
876
|
-
|
|
877
|
-
def _simulate_readonly_call(
|
|
878
|
-
self, method: Method, atc: AtomicTransactionComposer
|
|
879
|
-
) -> ABITransactionResponse | TransactionResponse:
|
|
880
|
-
response = simulate_response(atc, self.algod_client)
|
|
881
|
-
traces = None
|
|
882
|
-
if config.debug:
|
|
883
|
-
traces = _create_simulate_traces(response)
|
|
884
|
-
if response.failure_message:
|
|
885
|
-
raise _try_convert_to_logic_error(
|
|
886
|
-
response.failure_message,
|
|
887
|
-
self.app_spec.approval_program,
|
|
888
|
-
self._get_approval_source_map,
|
|
889
|
-
traces,
|
|
890
|
-
) or Exception(f"Simulate failed for readonly method {method.get_signature()}: {response.failure_message}")
|
|
891
|
-
|
|
892
|
-
return TransactionResponse.from_atr(response)
|
|
893
|
-
|
|
894
|
-
def _load_reference_and_check_app_id(self) -> None:
|
|
895
|
-
self._load_app_reference()
|
|
896
|
-
self._check_app_id()
|
|
897
|
-
|
|
898
|
-
def _load_app_reference(self) -> au_deploy.AppReference | au_deploy.AppMetaData:
|
|
899
|
-
if not self.existing_deployments and self._creator:
|
|
900
|
-
assert self._indexer_client
|
|
901
|
-
self.existing_deployments = au_deploy.get_creator_apps(self._indexer_client, self._creator)
|
|
902
|
-
|
|
903
|
-
if self.existing_deployments:
|
|
904
|
-
app = self.existing_deployments.apps.get(self.app_name)
|
|
905
|
-
if app:
|
|
906
|
-
if self.app_id == 0:
|
|
907
|
-
self.app_id = app.app_id
|
|
908
|
-
return app
|
|
909
|
-
|
|
910
|
-
return au_deploy.AppReference(self.app_id, self.app_address)
|
|
911
|
-
|
|
912
|
-
def _check_app_id(self) -> None:
|
|
913
|
-
if self.app_id == 0:
|
|
914
|
-
raise Exception(
|
|
915
|
-
"ApplicationClient is not associated with an app instance, to resolve either:\n"
|
|
916
|
-
"1.) provide an app_id on construction OR\n"
|
|
917
|
-
"2.) provide a creator address so an app can be searched for OR\n"
|
|
918
|
-
"3.) create an app first using create or deploy methods"
|
|
919
|
-
)
|
|
920
|
-
|
|
921
|
-
def _resolve_method(
|
|
922
|
-
self,
|
|
923
|
-
abi_method: ABIMethod | bool | None,
|
|
924
|
-
args: ABIArgsDict | None,
|
|
925
|
-
on_complete: transaction.OnComplete,
|
|
926
|
-
call_config: au_spec.CallConfig = au_spec.CallConfig.CALL,
|
|
927
|
-
) -> Method | None:
|
|
928
|
-
matches: list[Method | None] = []
|
|
929
|
-
match abi_method:
|
|
930
|
-
case str() | Method(): # abi method specified
|
|
931
|
-
return self._resolve_abi_method(abi_method)
|
|
932
|
-
case bool() | None: # find abi method
|
|
933
|
-
has_bare_config = (
|
|
934
|
-
call_config in au_deploy.get_call_config(self.app_spec.bare_call_config, on_complete)
|
|
935
|
-
or on_complete == transaction.OnComplete.ClearStateOC
|
|
936
|
-
)
|
|
937
|
-
abi_methods = self._find_abi_methods(args, on_complete, call_config)
|
|
938
|
-
if abi_method is not False:
|
|
939
|
-
matches += abi_methods
|
|
940
|
-
if has_bare_config and abi_method is not True:
|
|
941
|
-
matches += [None]
|
|
942
|
-
case _:
|
|
943
|
-
return abi_method.method_spec()
|
|
944
|
-
|
|
945
|
-
if len(matches) == 1: # exact match
|
|
946
|
-
return matches[0]
|
|
947
|
-
elif len(matches) > 1: # ambiguous match
|
|
948
|
-
signatures = ", ".join((m.get_signature() if isinstance(m, Method) else "bare") for m in matches)
|
|
949
|
-
raise Exception(
|
|
950
|
-
f"Could not find an exact method to use for {on_complete.name} with call_config of {call_config.name}, "
|
|
951
|
-
f"specify the exact method using abi_method and args parameters, considered: {signatures}"
|
|
952
|
-
)
|
|
953
|
-
else: # no match
|
|
954
|
-
raise Exception(
|
|
955
|
-
f"Could not find any methods to use for {on_complete.name} with call_config of {call_config.name}"
|
|
956
|
-
)
|
|
957
|
-
|
|
958
|
-
def _get_approval_source_map(self) -> SourceMap | None:
|
|
959
|
-
if self.approval_source_map:
|
|
960
|
-
return self.approval_source_map
|
|
961
|
-
|
|
962
|
-
try:
|
|
963
|
-
approval, _ = self._check_is_compiled()
|
|
964
|
-
except au_deploy.DeploymentFailedError:
|
|
965
|
-
return None
|
|
966
|
-
return approval.source_map
|
|
967
|
-
|
|
968
|
-
def export_source_map(self) -> str | None:
|
|
969
|
-
"""Export approval source map to JSON, can be later re-imported with `import_source_map`"""
|
|
970
|
-
source_map = self._get_approval_source_map()
|
|
971
|
-
if source_map:
|
|
972
|
-
return json.dumps(
|
|
973
|
-
{
|
|
974
|
-
"version": source_map.version,
|
|
975
|
-
"sources": source_map.sources,
|
|
976
|
-
"mappings": source_map.mappings,
|
|
977
|
-
}
|
|
978
|
-
)
|
|
979
|
-
return None
|
|
980
|
-
|
|
981
|
-
def import_source_map(self, source_map_json: str) -> None:
|
|
982
|
-
"""Import approval source from JSON exported by `export_source_map`"""
|
|
983
|
-
source_map = json.loads(source_map_json)
|
|
984
|
-
self._approval_source_map = SourceMap(source_map)
|
|
985
|
-
|
|
986
|
-
def add_method_call( # noqa: PLR0913
|
|
987
|
-
self,
|
|
988
|
-
atc: AtomicTransactionComposer,
|
|
989
|
-
abi_method: ABIMethod | bool | None = None,
|
|
990
|
-
*,
|
|
991
|
-
abi_args: ABIArgsDict | None = None,
|
|
992
|
-
app_id: int | None = None,
|
|
993
|
-
parameters: TransactionParameters | TransactionParametersDict | None = None,
|
|
994
|
-
on_complete: transaction.OnComplete = transaction.OnComplete.NoOpOC,
|
|
995
|
-
local_schema: transaction.StateSchema | None = None,
|
|
996
|
-
global_schema: transaction.StateSchema | None = None,
|
|
997
|
-
approval_program: bytes | None = None,
|
|
998
|
-
clear_program: bytes | None = None,
|
|
999
|
-
extra_pages: int | None = None,
|
|
1000
|
-
app_args: list[bytes] | None = None,
|
|
1001
|
-
call_config: au_spec.CallConfig = au_spec.CallConfig.CALL,
|
|
1002
|
-
) -> None:
|
|
1003
|
-
"""Adds a transaction to the AtomicTransactionComposer passed"""
|
|
1004
|
-
if app_id is None:
|
|
1005
|
-
self._load_reference_and_check_app_id()
|
|
1006
|
-
app_id = self.app_id
|
|
1007
|
-
parameters = _convert_transaction_parameters(parameters)
|
|
1008
|
-
method = self._resolve_method(abi_method, abi_args, on_complete, call_config)
|
|
1009
|
-
sp = parameters.suggested_params or self.suggested_params or self.algod_client.suggested_params()
|
|
1010
|
-
signer, sender = self.resolve_signer_sender(parameters.signer, parameters.sender)
|
|
1011
|
-
if parameters.boxes is not None:
|
|
1012
|
-
# TODO: algosdk actually does this, but it's type hints say otherwise...
|
|
1013
|
-
encoded_boxes = [(id_, algosdk.encoding.encode_as_bytes(name)) for id_, name in parameters.boxes]
|
|
1014
|
-
else:
|
|
1015
|
-
encoded_boxes = None
|
|
1016
|
-
|
|
1017
|
-
encoded_lease = parameters.lease.encode("utf-8") if isinstance(parameters.lease, str) else parameters.lease
|
|
1018
|
-
|
|
1019
|
-
if not method: # not an abi method, treat as a regular call
|
|
1020
|
-
if abi_args:
|
|
1021
|
-
raise Exception(f"ABI arguments specified on a bare call: {', '.join(abi_args)}")
|
|
1022
|
-
atc.add_transaction(
|
|
1023
|
-
TransactionWithSigner(
|
|
1024
|
-
txn=transaction.ApplicationCallTxn( # type: ignore[no-untyped-call]
|
|
1025
|
-
sender=sender,
|
|
1026
|
-
sp=sp,
|
|
1027
|
-
index=app_id,
|
|
1028
|
-
on_complete=on_complete,
|
|
1029
|
-
approval_program=approval_program,
|
|
1030
|
-
clear_program=clear_program,
|
|
1031
|
-
global_schema=global_schema,
|
|
1032
|
-
local_schema=local_schema,
|
|
1033
|
-
extra_pages=extra_pages,
|
|
1034
|
-
accounts=parameters.accounts,
|
|
1035
|
-
foreign_apps=parameters.foreign_apps,
|
|
1036
|
-
foreign_assets=parameters.foreign_assets,
|
|
1037
|
-
boxes=encoded_boxes,
|
|
1038
|
-
note=parameters.note,
|
|
1039
|
-
lease=encoded_lease,
|
|
1040
|
-
rekey_to=parameters.rekey_to,
|
|
1041
|
-
app_args=app_args,
|
|
1042
|
-
),
|
|
1043
|
-
signer=signer,
|
|
1044
|
-
)
|
|
1045
|
-
)
|
|
1046
|
-
return
|
|
1047
|
-
# resolve ABI method args
|
|
1048
|
-
args = self._get_abi_method_args(abi_args, method)
|
|
1049
|
-
atc.add_method_call(
|
|
1050
|
-
app_id,
|
|
1051
|
-
method,
|
|
1052
|
-
sender,
|
|
1053
|
-
sp,
|
|
1054
|
-
signer,
|
|
1055
|
-
method_args=args,
|
|
1056
|
-
on_complete=on_complete,
|
|
1057
|
-
local_schema=local_schema,
|
|
1058
|
-
global_schema=global_schema,
|
|
1059
|
-
approval_program=approval_program,
|
|
1060
|
-
clear_program=clear_program,
|
|
1061
|
-
extra_pages=extra_pages or 0,
|
|
1062
|
-
accounts=parameters.accounts,
|
|
1063
|
-
foreign_apps=parameters.foreign_apps,
|
|
1064
|
-
foreign_assets=parameters.foreign_assets,
|
|
1065
|
-
boxes=encoded_boxes,
|
|
1066
|
-
note=parameters.note.encode("utf-8") if isinstance(parameters.note, str) else parameters.note,
|
|
1067
|
-
lease=encoded_lease,
|
|
1068
|
-
rekey_to=parameters.rekey_to,
|
|
1069
|
-
)
|
|
1070
|
-
|
|
1071
|
-
def _get_abi_method_args(self, abi_args: ABIArgsDict | None, method: Method) -> list:
|
|
1072
|
-
args: list = []
|
|
1073
|
-
hints = self._method_hints(method)
|
|
1074
|
-
# copy args so we don't mutate original
|
|
1075
|
-
abi_args = dict(abi_args or {})
|
|
1076
|
-
for method_arg in method.args:
|
|
1077
|
-
name = method_arg.name
|
|
1078
|
-
if name in abi_args:
|
|
1079
|
-
argument = abi_args.pop(name)
|
|
1080
|
-
if isinstance(argument, dict):
|
|
1081
|
-
if hints.structs is None or name not in hints.structs:
|
|
1082
|
-
raise Exception(f"Argument missing struct hint: {name}. Check argument name and type")
|
|
1083
|
-
|
|
1084
|
-
elements = hints.structs[name]["elements"]
|
|
1085
|
-
|
|
1086
|
-
argument_tuple = tuple(argument[field_name] for field_name, field_type in elements)
|
|
1087
|
-
args.append(argument_tuple)
|
|
1088
|
-
else:
|
|
1089
|
-
args.append(argument)
|
|
1090
|
-
|
|
1091
|
-
elif hints.default_arguments is not None and name in hints.default_arguments:
|
|
1092
|
-
default_arg = hints.default_arguments[name]
|
|
1093
|
-
if default_arg is not None:
|
|
1094
|
-
args.append(self.resolve(default_arg))
|
|
1095
|
-
else:
|
|
1096
|
-
raise Exception(f"Unspecified argument: {name}")
|
|
1097
|
-
if abi_args:
|
|
1098
|
-
raise Exception(f"Unused arguments specified: {', '.join(abi_args)}")
|
|
1099
|
-
return args
|
|
1100
|
-
|
|
1101
|
-
def _method_matches(
|
|
1102
|
-
self,
|
|
1103
|
-
method: Method,
|
|
1104
|
-
args: ABIArgsDict | None,
|
|
1105
|
-
on_complete: transaction.OnComplete,
|
|
1106
|
-
call_config: au_spec.CallConfig,
|
|
1107
|
-
) -> bool:
|
|
1108
|
-
hints = self._method_hints(method)
|
|
1109
|
-
if call_config not in au_deploy.get_call_config(hints.call_config, on_complete):
|
|
1110
|
-
return False
|
|
1111
|
-
method_args = {m.name for m in method.args}
|
|
1112
|
-
provided_args = set(args or {}) | set(hints.default_arguments)
|
|
1113
|
-
|
|
1114
|
-
# TODO: also match on types?
|
|
1115
|
-
return method_args == provided_args
|
|
1116
|
-
|
|
1117
|
-
def _find_abi_methods(
|
|
1118
|
-
self, args: ABIArgsDict | None, on_complete: transaction.OnComplete, call_config: au_spec.CallConfig
|
|
1119
|
-
) -> list[Method]:
|
|
1120
|
-
return [
|
|
1121
|
-
method
|
|
1122
|
-
for method in self.app_spec.contract.methods
|
|
1123
|
-
if self._method_matches(method, args, on_complete, call_config)
|
|
1124
|
-
]
|
|
1125
|
-
|
|
1126
|
-
def _resolve_abi_method(self, method: ABIMethod) -> Method:
|
|
1127
|
-
if isinstance(method, str):
|
|
1128
|
-
try:
|
|
1129
|
-
return next(iter(m for m in self.app_spec.contract.methods if m.get_signature() == method))
|
|
1130
|
-
except StopIteration:
|
|
1131
|
-
pass
|
|
1132
|
-
return self.app_spec.contract.get_method_by_name(method)
|
|
1133
|
-
elif hasattr(method, "method_spec"):
|
|
1134
|
-
return method.method_spec()
|
|
1135
|
-
else:
|
|
1136
|
-
return method
|
|
1137
|
-
|
|
1138
|
-
def _method_hints(self, method: Method) -> au_spec.MethodHints:
|
|
1139
|
-
sig = method.get_signature()
|
|
1140
|
-
if sig not in self.app_spec.hints:
|
|
1141
|
-
return au_spec.MethodHints()
|
|
1142
|
-
return self.app_spec.hints[sig]
|
|
1143
|
-
|
|
1144
|
-
def _execute_atc_tr(self, atc: AtomicTransactionComposer) -> TransactionResponse:
|
|
1145
|
-
result = self.execute_atc(atc)
|
|
1146
|
-
return TransactionResponse.from_atr(result)
|
|
1147
|
-
|
|
1148
|
-
def execute_atc(self, atc: AtomicTransactionComposer) -> AtomicTransactionResponse:
|
|
1149
|
-
return execute_atc_with_logic_error(
|
|
1150
|
-
atc,
|
|
1151
|
-
self.algod_client,
|
|
1152
|
-
approval_program=self.app_spec.approval_program,
|
|
1153
|
-
approval_source_map=self._get_approval_source_map,
|
|
1154
|
-
)
|
|
1155
|
-
|
|
1156
|
-
def get_signer_sender(
|
|
1157
|
-
self, signer: TransactionSigner | None = None, sender: str | None = None
|
|
1158
|
-
) -> tuple[TransactionSigner | None, str | None]:
|
|
1159
|
-
"""Return signer and sender, using default values on client if not specified
|
|
1160
|
-
|
|
1161
|
-
Will use provided values if given, otherwise will fall back to values defined on client.
|
|
1162
|
-
If no sender is specified then will attempt to obtain sender from signer"""
|
|
1163
|
-
resolved_signer = signer or self.signer
|
|
1164
|
-
resolved_sender = sender or get_sender_from_signer(signer) or self.sender or get_sender_from_signer(self.signer)
|
|
1165
|
-
return resolved_signer, resolved_sender
|
|
1166
|
-
|
|
1167
|
-
def resolve_signer_sender(
|
|
1168
|
-
self, signer: TransactionSigner | None = None, sender: str | None = None
|
|
1169
|
-
) -> tuple[TransactionSigner, str]:
|
|
1170
|
-
"""Return signer and sender, using default values on client if not specified
|
|
1171
|
-
|
|
1172
|
-
Will use provided values if given, otherwise will fall back to values defined on client.
|
|
1173
|
-
If no sender is specified then will attempt to obtain sender from signer
|
|
1174
|
-
|
|
1175
|
-
:raises ValueError: Raised if a signer or sender is not provided. See `get_signer_sender`
|
|
1176
|
-
for variant with no exception"""
|
|
1177
|
-
resolved_signer, resolved_sender = self.get_signer_sender(signer, sender)
|
|
1178
|
-
if not resolved_signer:
|
|
1179
|
-
raise ValueError("No signer provided")
|
|
1180
|
-
if not resolved_sender:
|
|
1181
|
-
raise ValueError("No sender provided")
|
|
1182
|
-
return resolved_signer, resolved_sender
|
|
1183
|
-
|
|
1184
|
-
# TODO: remove private implementation, kept in the 1.0.2 release to not impact existing beaker 1.0 installs
|
|
1185
|
-
_resolve_signer_sender = resolve_signer_sender
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
def substitute_template_and_compile(
|
|
1189
|
-
algod_client: "AlgodClient",
|
|
1190
|
-
app_spec: au_spec.ApplicationSpecification,
|
|
1191
|
-
template_values: au_deploy.TemplateValueMapping,
|
|
1192
|
-
) -> tuple[Program, Program]:
|
|
1193
|
-
"""Substitutes the provided template_values into app_spec and compiles"""
|
|
1194
|
-
template_values = dict(template_values or {})
|
|
1195
|
-
clear = au_deploy.replace_template_variables(app_spec.clear_program, template_values)
|
|
1196
|
-
|
|
1197
|
-
au_deploy.check_template_variables(app_spec.approval_program, template_values)
|
|
1198
|
-
approval = au_deploy.replace_template_variables(app_spec.approval_program, template_values)
|
|
1199
|
-
|
|
1200
|
-
approval_app, clear_app = Program(approval, algod_client), Program(clear, algod_client)
|
|
1201
|
-
|
|
1202
|
-
return approval_app, clear_app
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
def get_next_version(current_version: str) -> str:
|
|
1206
|
-
"""Calculates the next version from `current_version`
|
|
1207
|
-
|
|
1208
|
-
Next version is calculated by finding a semver like
|
|
1209
|
-
version string and incrementing the lower. This function is used by {py:meth}`ApplicationClient.deploy` when
|
|
1210
|
-
a version is not specified, and is intended mostly for convenience during local development.
|
|
1211
|
-
|
|
1212
|
-
:params str current_version: An existing version string with a semver like version contained within it,
|
|
1213
|
-
some valid inputs and incremented outputs:
|
|
1214
|
-
`1` -> `2`
|
|
1215
|
-
`1.0` -> `1.1`
|
|
1216
|
-
`v1.1` -> `v1.2`
|
|
1217
|
-
`v1.1-beta1` -> `v1.2-beta1`
|
|
1218
|
-
`v1.2.3.4567` -> `v1.2.3.4568`
|
|
1219
|
-
`v1.2.3.4567-alpha` -> `v1.2.3.4568-alpha`
|
|
1220
|
-
:raises DeploymentFailedError: If `current_version` cannot be parsed"""
|
|
1221
|
-
pattern = re.compile(r"(?P<prefix>\w*)(?P<version>(?:\d+\.)*\d+)(?P<suffix>\w*)")
|
|
1222
|
-
match = pattern.match(current_version)
|
|
1223
|
-
if match:
|
|
1224
|
-
version = match.group("version")
|
|
1225
|
-
new_version = _increment_version(version)
|
|
1226
|
-
|
|
1227
|
-
def replacement(m: re.Match) -> str:
|
|
1228
|
-
return f"{m.group('prefix')}{new_version}{m.group('suffix')}"
|
|
1229
|
-
|
|
1230
|
-
return re.sub(pattern, replacement, current_version)
|
|
1231
|
-
raise au_deploy.DeploymentFailedError(
|
|
1232
|
-
f"Could not auto increment {current_version}, please specify the next version using the version parameter"
|
|
1233
|
-
)
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
def _try_convert_to_logic_error(
|
|
1237
|
-
source_ex: Exception | str,
|
|
1238
|
-
approval_program: str,
|
|
1239
|
-
approval_source_map: SourceMap | typing.Callable[[], SourceMap | None] | None = None,
|
|
1240
|
-
simulate_traces: list[SimulationTrace] | None = None,
|
|
1241
|
-
) -> Exception | None:
|
|
1242
|
-
source_ex_str = str(source_ex)
|
|
1243
|
-
logic_error_data = parse_logic_error(source_ex_str)
|
|
1244
|
-
if logic_error_data:
|
|
1245
|
-
return LogicError(
|
|
1246
|
-
logic_error_str=source_ex_str,
|
|
1247
|
-
logic_error=source_ex if isinstance(source_ex, Exception) else None,
|
|
1248
|
-
program=approval_program,
|
|
1249
|
-
source_map=approval_source_map() if callable(approval_source_map) else approval_source_map,
|
|
1250
|
-
**logic_error_data,
|
|
1251
|
-
traces=simulate_traces,
|
|
1252
|
-
)
|
|
1253
|
-
|
|
1254
|
-
return None
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
def execute_atc_with_logic_error(
|
|
1258
|
-
atc: AtomicTransactionComposer,
|
|
1259
|
-
algod_client: "AlgodClient",
|
|
1260
|
-
approval_program: str,
|
|
1261
|
-
wait_rounds: int = 4,
|
|
1262
|
-
approval_source_map: SourceMap | typing.Callable[[], SourceMap | None] | None = None,
|
|
1263
|
-
) -> AtomicTransactionResponse:
|
|
1264
|
-
"""Calls {py:meth}`AtomicTransactionComposer.execute` on provided `atc`, but will parse any errors
|
|
1265
|
-
and raise a {py:class}`LogicError` if possible
|
|
1266
|
-
|
|
1267
|
-
```{note}
|
|
1268
|
-
`approval_program` and `approval_source_map` are required to be able to parse any errors into a
|
|
1269
|
-
{py:class}`LogicError`
|
|
1270
|
-
```
|
|
1271
|
-
"""
|
|
1272
|
-
try:
|
|
1273
|
-
if config.debug and config.project_root and config.trace_all:
|
|
1274
|
-
simulate_and_persist_response(atc, config.project_root, algod_client, config.trace_buffer_size_mb)
|
|
1275
|
-
|
|
1276
|
-
return atc.execute(algod_client, wait_rounds=wait_rounds)
|
|
1277
|
-
except Exception as ex:
|
|
1278
|
-
if config.debug:
|
|
1279
|
-
simulate = None
|
|
1280
|
-
if config.project_root and not config.trace_all:
|
|
1281
|
-
# if trace_all is enabled, we already have the traces executed above
|
|
1282
|
-
# hence we only need to simulate if trace_all is disabled and
|
|
1283
|
-
# project_root is set
|
|
1284
|
-
simulate = simulate_and_persist_response(
|
|
1285
|
-
atc, config.project_root, algod_client, config.trace_buffer_size_mb
|
|
1286
|
-
)
|
|
1287
|
-
else:
|
|
1288
|
-
simulate = simulate_response(atc, algod_client)
|
|
1289
|
-
traces = _create_simulate_traces(simulate)
|
|
1290
|
-
else:
|
|
1291
|
-
traces = None
|
|
1292
|
-
logger.info("An error occurred while executing the transaction.")
|
|
1293
|
-
logger.info("To see more details, enable debug mode by setting config.debug = True ")
|
|
1294
|
-
|
|
1295
|
-
logic_error = _try_convert_to_logic_error(ex, approval_program, approval_source_map, traces)
|
|
1296
|
-
if logic_error:
|
|
1297
|
-
raise logic_error from ex
|
|
1298
|
-
raise ex
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
def _create_simulate_traces(simulate: SimulateAtomicTransactionResponse) -> list[SimulationTrace]:
|
|
1302
|
-
traces = []
|
|
1303
|
-
if hasattr(simulate, "simulate_response") and hasattr(simulate, "failed_at") and simulate.failed_at:
|
|
1304
|
-
for txn_group in simulate.simulate_response["txn-groups"]:
|
|
1305
|
-
app_budget_added = txn_group.get("app-budget-added", None)
|
|
1306
|
-
app_budget_consumed = txn_group.get("app-budget-consumed", None)
|
|
1307
|
-
failure_message = txn_group.get("failure-message", None)
|
|
1308
|
-
txn_result = txn_group.get("txn-results", [{}])[0]
|
|
1309
|
-
exec_trace = txn_result.get("exec-trace", {})
|
|
1310
|
-
traces.append(
|
|
1311
|
-
SimulationTrace(
|
|
1312
|
-
app_budget_added=app_budget_added,
|
|
1313
|
-
app_budget_consumed=app_budget_consumed,
|
|
1314
|
-
failure_message=failure_message,
|
|
1315
|
-
exec_trace=exec_trace,
|
|
1316
|
-
)
|
|
1317
|
-
)
|
|
1318
|
-
return traces
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
def _convert_transaction_parameters(
|
|
1322
|
-
args: TransactionParameters | TransactionParametersDict | None,
|
|
1323
|
-
) -> CreateCallParameters:
|
|
1324
|
-
_args = args.__dict__ if isinstance(args, TransactionParameters) else (args or {})
|
|
1325
|
-
return CreateCallParameters(**_args)
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
def get_sender_from_signer(signer: TransactionSigner | None) -> str | None:
|
|
1329
|
-
"""Returns the associated address of a signer, return None if no address found"""
|
|
1330
|
-
|
|
1331
|
-
if isinstance(signer, AccountTransactionSigner):
|
|
1332
|
-
sender = address_from_private_key(signer.private_key) # type: ignore[no-untyped-call]
|
|
1333
|
-
assert isinstance(sender, str)
|
|
1334
|
-
return sender
|
|
1335
|
-
elif isinstance(signer, MultisigTransactionSigner):
|
|
1336
|
-
sender = signer.msig.address() # type: ignore[no-untyped-call]
|
|
1337
|
-
assert isinstance(sender, str)
|
|
1338
|
-
return sender
|
|
1339
|
-
elif isinstance(signer, LogicSigTransactionSigner):
|
|
1340
|
-
return signer.lsig.address()
|
|
1341
|
-
return None
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
# TEMPORARY, use SDK one when available
|
|
1345
|
-
def _parse_result(
|
|
1346
|
-
methods: dict[int, Method],
|
|
1347
|
-
txns: list[dict[str, Any]],
|
|
1348
|
-
txids: list[str],
|
|
1349
|
-
) -> list[ABIResult]:
|
|
1350
|
-
method_results = []
|
|
1351
|
-
for i, tx_info in enumerate(txns):
|
|
1352
|
-
raw_value = b""
|
|
1353
|
-
return_value = None
|
|
1354
|
-
decode_error = None
|
|
1355
|
-
|
|
1356
|
-
if i not in methods:
|
|
1357
|
-
continue
|
|
1358
|
-
|
|
1359
|
-
# Parse log for ABI method return value
|
|
1360
|
-
try:
|
|
1361
|
-
if methods[i].returns.type == Returns.VOID:
|
|
1362
|
-
method_results.append(
|
|
1363
|
-
ABIResult(
|
|
1364
|
-
tx_id=txids[i],
|
|
1365
|
-
raw_value=raw_value,
|
|
1366
|
-
return_value=return_value,
|
|
1367
|
-
decode_error=decode_error,
|
|
1368
|
-
tx_info=tx_info,
|
|
1369
|
-
method=methods[i],
|
|
1370
|
-
)
|
|
1371
|
-
)
|
|
1372
|
-
continue
|
|
1373
|
-
|
|
1374
|
-
logs = tx_info.get("logs", [])
|
|
1375
|
-
|
|
1376
|
-
# Look for the last returned value in the log
|
|
1377
|
-
if not logs:
|
|
1378
|
-
raise Exception("No logs")
|
|
1379
|
-
|
|
1380
|
-
result = logs[-1]
|
|
1381
|
-
# Check that the first four bytes is the hash of "return"
|
|
1382
|
-
result_bytes = base64.b64decode(result)
|
|
1383
|
-
if len(result_bytes) < len(ABI_RETURN_HASH) or result_bytes[: len(ABI_RETURN_HASH)] != ABI_RETURN_HASH:
|
|
1384
|
-
raise Exception("no logs")
|
|
1385
|
-
|
|
1386
|
-
raw_value = result_bytes[4:]
|
|
1387
|
-
abi_return_type = methods[i].returns.type
|
|
1388
|
-
if isinstance(abi_return_type, ABIType):
|
|
1389
|
-
return_value = abi_return_type.decode(raw_value)
|
|
1390
|
-
else:
|
|
1391
|
-
return_value = raw_value
|
|
1392
|
-
|
|
1393
|
-
except Exception as e:
|
|
1394
|
-
decode_error = e
|
|
1395
|
-
|
|
1396
|
-
method_results.append(
|
|
1397
|
-
ABIResult(
|
|
1398
|
-
tx_id=txids[i],
|
|
1399
|
-
raw_value=raw_value,
|
|
1400
|
-
return_value=return_value,
|
|
1401
|
-
decode_error=decode_error,
|
|
1402
|
-
tx_info=tx_info,
|
|
1403
|
-
method=methods[i],
|
|
1404
|
-
)
|
|
1405
|
-
)
|
|
1406
|
-
|
|
1407
|
-
return method_results
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
def _increment_version(version: str) -> str:
|
|
1411
|
-
split = list(map(int, version.split(".")))
|
|
1412
|
-
split[-1] = split[-1] + 1
|
|
1413
|
-
return ".".join(str(x) for x in split)
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
def _str_or_hex(v: bytes) -> str:
|
|
1417
|
-
decoded: str
|
|
1418
|
-
try:
|
|
1419
|
-
decoded = v.decode("utf-8")
|
|
1420
|
-
except UnicodeDecodeError:
|
|
1421
|
-
decoded = v.hex()
|
|
1422
|
-
|
|
1423
|
-
return decoded
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
def _decode_state(state: list[dict[str, Any]], *, raw: bool = False) -> dict[str | bytes, bytes | str | int | None]:
|
|
1427
|
-
decoded_state: dict[str | bytes, bytes | str | int | None] = {}
|
|
1428
|
-
|
|
1429
|
-
for state_value in state:
|
|
1430
|
-
raw_key = base64.b64decode(state_value["key"])
|
|
1431
|
-
|
|
1432
|
-
key: str | bytes = raw_key if raw else _str_or_hex(raw_key)
|
|
1433
|
-
val: str | bytes | int | None
|
|
1434
|
-
|
|
1435
|
-
action = state_value["value"]["action"] if "action" in state_value["value"] else state_value["value"]["type"]
|
|
1436
|
-
|
|
1437
|
-
match action:
|
|
1438
|
-
case 1:
|
|
1439
|
-
raw_val = base64.b64decode(state_value["value"]["bytes"])
|
|
1440
|
-
val = raw_val if raw else _str_or_hex(raw_val)
|
|
1441
|
-
case 2:
|
|
1442
|
-
val = state_value["value"]["uint"]
|
|
1443
|
-
case 3:
|
|
1444
|
-
val = None
|
|
1445
|
-
case _:
|
|
1446
|
-
raise NotImplementedError
|
|
1447
10
|
|
|
1448
|
-
|
|
1449
|
-
return decoded_state
|
|
11
|
+
from algokit_utils._legacy_v2.application_client import * # noqa: F403, E402
|