algokit-utils 3.0.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.

Files changed (70) hide show
  1. algokit_utils/__init__.py +23 -183
  2. algokit_utils/_debugging.py +123 -97
  3. algokit_utils/_legacy_v2/__init__.py +177 -0
  4. algokit_utils/{_ensure_funded.py → _legacy_v2/_ensure_funded.py} +19 -18
  5. algokit_utils/{_transfer.py → _legacy_v2/_transfer.py} +24 -23
  6. algokit_utils/_legacy_v2/account.py +203 -0
  7. algokit_utils/_legacy_v2/application_client.py +1471 -0
  8. algokit_utils/_legacy_v2/application_specification.py +21 -0
  9. algokit_utils/_legacy_v2/asset.py +168 -0
  10. algokit_utils/_legacy_v2/common.py +28 -0
  11. algokit_utils/_legacy_v2/deploy.py +822 -0
  12. algokit_utils/_legacy_v2/logic_error.py +14 -0
  13. algokit_utils/{models.py → _legacy_v2/models.py} +19 -142
  14. algokit_utils/_legacy_v2/network_clients.py +140 -0
  15. algokit_utils/account.py +12 -183
  16. algokit_utils/accounts/__init__.py +2 -0
  17. algokit_utils/accounts/account_manager.py +909 -0
  18. algokit_utils/accounts/kmd_account_manager.py +159 -0
  19. algokit_utils/algorand.py +265 -0
  20. algokit_utils/application_client.py +9 -1453
  21. algokit_utils/application_specification.py +39 -197
  22. algokit_utils/applications/__init__.py +7 -0
  23. algokit_utils/applications/abi.py +276 -0
  24. algokit_utils/applications/app_client.py +2056 -0
  25. algokit_utils/applications/app_deployer.py +600 -0
  26. algokit_utils/applications/app_factory.py +826 -0
  27. algokit_utils/applications/app_manager.py +470 -0
  28. algokit_utils/applications/app_spec/__init__.py +2 -0
  29. algokit_utils/applications/app_spec/arc32.py +207 -0
  30. algokit_utils/applications/app_spec/arc56.py +1023 -0
  31. algokit_utils/applications/enums.py +40 -0
  32. algokit_utils/asset.py +32 -168
  33. algokit_utils/assets/__init__.py +1 -0
  34. algokit_utils/assets/asset_manager.py +320 -0
  35. algokit_utils/beta/_utils.py +36 -0
  36. algokit_utils/beta/account_manager.py +4 -195
  37. algokit_utils/beta/algorand_client.py +4 -314
  38. algokit_utils/beta/client_manager.py +5 -74
  39. algokit_utils/beta/composer.py +5 -712
  40. algokit_utils/clients/__init__.py +2 -0
  41. algokit_utils/clients/client_manager.py +656 -0
  42. algokit_utils/clients/dispenser_api_client.py +192 -0
  43. algokit_utils/common.py +8 -26
  44. algokit_utils/config.py +71 -18
  45. algokit_utils/deploy.py +7 -892
  46. algokit_utils/dispenser_api.py +8 -176
  47. algokit_utils/errors/__init__.py +1 -0
  48. algokit_utils/errors/logic_error.py +121 -0
  49. algokit_utils/logic_error.py +7 -80
  50. algokit_utils/models/__init__.py +8 -0
  51. algokit_utils/models/account.py +193 -0
  52. algokit_utils/models/amount.py +198 -0
  53. algokit_utils/models/application.py +61 -0
  54. algokit_utils/models/network.py +25 -0
  55. algokit_utils/models/simulate.py +11 -0
  56. algokit_utils/models/state.py +59 -0
  57. algokit_utils/models/transaction.py +100 -0
  58. algokit_utils/network_clients.py +7 -152
  59. algokit_utils/protocols/__init__.py +2 -0
  60. algokit_utils/protocols/account.py +22 -0
  61. algokit_utils/protocols/typed_clients.py +108 -0
  62. algokit_utils/transactions/__init__.py +3 -0
  63. algokit_utils/transactions/transaction_composer.py +2293 -0
  64. algokit_utils/transactions/transaction_creator.py +156 -0
  65. algokit_utils/transactions/transaction_sender.py +574 -0
  66. {algokit_utils-3.0.0b1.dist-info → algokit_utils-3.0.0b2.dist-info}/METADATA +12 -7
  67. algokit_utils-3.0.0b2.dist-info/RECORD +70 -0
  68. {algokit_utils-3.0.0b1.dist-info → algokit_utils-3.0.0b2.dist-info}/WHEEL +1 -1
  69. algokit_utils-3.0.0b1.dist-info/RECORD +0 -24
  70. {algokit_utils-3.0.0b1.dist-info → algokit_utils-3.0.0b2.dist-info}/LICENSE +0 -0
@@ -0,0 +1,14 @@
1
+ from typing_extensions import deprecated
2
+
3
+ from algokit_utils.errors.logic_error import LogicError as NewLogicError
4
+ from algokit_utils.errors.logic_error import parse_logic_error
5
+
6
+ __all__ = [
7
+ "LogicError",
8
+ "parse_logic_error",
9
+ ]
10
+
11
+
12
+ @deprecated("Use algokit_utils.models.error.LogicError instead")
13
+ class LogicError(NewLogicError):
14
+ pass
@@ -2,17 +2,20 @@ import dataclasses
2
2
  from collections.abc import Sequence
3
3
  from typing import Any, Generic, Protocol, TypeAlias, TypedDict, TypeVar
4
4
 
5
- import algosdk.account
6
5
  from algosdk import transaction
7
6
  from algosdk.abi import Method
8
7
  from algosdk.atomic_transaction_composer import (
9
- AccountTransactionSigner,
10
8
  AtomicTransactionResponse,
11
9
  SimulateAtomicTransactionResponse,
12
10
  TransactionSigner,
13
11
  )
14
- from algosdk.encoding import decode_address
15
- from deprecated import deprecated
12
+ from typing_extensions import deprecated
13
+
14
+ from algokit_utils.models.account import SigningAccount
15
+ from algokit_utils.models.simulate import SimulationTrace
16
+
17
+ # Imports from latest sdk version that rely on models previously used in legacy v2 (but moved to root models/*)
18
+
16
19
 
17
20
  __all__ = [
18
21
  "ABIArgsDict",
@@ -24,6 +27,7 @@ __all__ = [
24
27
  "CreateTransactionParameters",
25
28
  "OnCompleteCallParameters",
26
29
  "OnCompleteCallParametersDict",
30
+ "SimulationTrace",
27
31
  "TransactionParameters",
28
32
  "TransactionResponse",
29
33
  ]
@@ -31,35 +35,10 @@ __all__ = [
31
35
  ReturnType = TypeVar("ReturnType")
32
36
 
33
37
 
38
+ @deprecated("Use 'SigningAccount' instead")
34
39
  @dataclasses.dataclass(kw_only=True)
35
- class Account:
36
- """Holds the private_key and address for an account"""
37
-
38
- private_key: str
39
- """Base64 encoded private key"""
40
- address: str = dataclasses.field(default="")
41
- """Address for this account"""
42
-
43
- def __post_init__(self) -> None:
44
- if not self.address:
45
- self.address = algosdk.account.address_from_private_key(self.private_key) # type: ignore[no-untyped-call]
46
-
47
- @property
48
- def public_key(self) -> bytes:
49
- """The public key for this account"""
50
- public_key = decode_address(self.address) # type: ignore[no-untyped-call]
51
- assert isinstance(public_key, bytes)
52
- return public_key
53
-
54
- @property
55
- def signer(self) -> AccountTransactionSigner:
56
- """An AccountTransactionSigner for this account"""
57
- return AccountTransactionSigner(self.private_key)
58
-
59
- @staticmethod
60
- def new_account() -> "Account":
61
- private_key, address = algosdk.account.generate_account() # type: ignore[no-untyped-call]
62
- return Account(private_key=private_key)
40
+ class Account(SigningAccount):
41
+ """An account that can be used to sign transactions"""
63
42
 
64
43
 
65
44
  @dataclasses.dataclass(kw_only=True)
@@ -155,86 +134,25 @@ class TransactionParameters:
155
134
 
156
135
  # CreateTransactionParameters is used by algokit-client-generator clients
157
136
  @dataclasses.dataclass(kw_only=True)
158
- class CreateTransactionParameters:
137
+ class CreateTransactionParameters(TransactionParameters):
159
138
  """Additional parameters that can be included in a transaction when calling a create method"""
160
139
 
161
- signer: TransactionSigner | None = None
162
- """Signer to use when signing this transaction"""
163
- sender: str | None = None
164
- """Sender of this transaction"""
165
- suggested_params: transaction.SuggestedParams | None = None
166
- """SuggestedParams to use for this transaction"""
167
- note: bytes | str | None = None
168
- """Note for this transaction"""
169
- lease: bytes | str | None = None
170
- """Lease value for this transaction"""
171
- boxes: Sequence[tuple[int, bytes | bytearray | str | int]] | None = None
172
- """Box references to include in transaction. A sequence of (app id, box key) tuples"""
173
- accounts: list[str] | None = None
174
- """Accounts to include in transaction"""
175
- foreign_apps: list[int] | None = None
176
- """List of foreign apps (by app id) to include in transaction"""
177
- foreign_assets: list[int] | None = None
178
- """List of foreign assets (by asset id) to include in transaction"""
179
- rekey_to: str | None = None
180
- """Address to rekey to"""
181
140
  extra_pages: int | None = None
182
141
 
183
142
 
184
143
  @dataclasses.dataclass(kw_only=True)
185
- class OnCompleteCallParameters:
144
+ class OnCompleteCallParameters(TransactionParameters):
186
145
  """Additional parameters that can be included in a transaction when using the
187
146
  ApplicationClient.call/compose_call methods"""
188
147
 
189
- signer: TransactionSigner | None = None
190
- """Signer to use when signing this transaction"""
191
- sender: str | None = None
192
- """Sender of this transaction"""
193
- suggested_params: transaction.SuggestedParams | None = None
194
- """SuggestedParams to use for this transaction"""
195
- note: bytes | str | None = None
196
- """Note for this transaction"""
197
- lease: bytes | str | None = None
198
- """Lease value for this transaction"""
199
- boxes: Sequence[tuple[int, bytes | bytearray | str | int]] | None = None
200
- """Box references to include in transaction. A sequence of (app id, box key) tuples"""
201
- accounts: list[str] | None = None
202
- """Accounts to include in transaction"""
203
- foreign_apps: list[int] | None = None
204
- """List of foreign apps (by app id) to include in transaction"""
205
- foreign_assets: list[int] | None = None
206
- """List of foreign assets (by asset id) to include in transaction"""
207
- rekey_to: str | None = None
208
- """Address to rekey to"""
209
148
  on_complete: transaction.OnComplete | None = None
210
149
 
211
150
 
212
151
  @dataclasses.dataclass(kw_only=True)
213
- class CreateCallParameters:
152
+ class CreateCallParameters(OnCompleteCallParameters):
214
153
  """Additional parameters that can be included in a transaction when using the
215
154
  ApplicationClient.create/compose_create methods"""
216
155
 
217
- signer: TransactionSigner | None = None
218
- """Signer to use when signing this transaction"""
219
- sender: str | None = None
220
- """Sender of this transaction"""
221
- suggested_params: transaction.SuggestedParams | None = None
222
- """SuggestedParams to use for this transaction"""
223
- note: bytes | str | None = None
224
- """Note for this transaction"""
225
- lease: bytes | str | None = None
226
- """Lease value for this transaction"""
227
- boxes: Sequence[tuple[int, bytes | bytearray | str | int]] | None = None
228
- """Box references to include in transaction. A sequence of (app id, box key) tuples"""
229
- accounts: list[str] | None = None
230
- """Accounts to include in transaction"""
231
- foreign_apps: list[int] | None = None
232
- """List of foreign apps (by app id) to include in transaction"""
233
- foreign_assets: list[int] | None = None
234
- """List of foreign assets (by asset id) to include in transaction"""
235
- rekey_to: str | None = None
236
- """Address to rekey to"""
237
- on_complete: transaction.OnComplete | None = None
238
156
  extra_pages: int | None = None
239
157
 
240
158
 
@@ -263,72 +181,31 @@ class TransactionParametersDict(TypedDict, total=False):
263
181
  """Address to rekey to"""
264
182
 
265
183
 
266
- class OnCompleteCallParametersDict(TypedDict, total=False):
184
+ class OnCompleteCallParametersDict(TransactionParametersDict, total=False):
267
185
  """Additional parameters that can be included in a transaction when using the
268
186
  ApplicationClient.call/compose_call methods"""
269
187
 
270
- signer: TransactionSigner
271
- """Signer to use when signing this transaction"""
272
- sender: str
273
- """Sender of this transaction"""
274
- suggested_params: transaction.SuggestedParams
275
- """SuggestedParams to use for this transaction"""
276
- note: bytes | str
277
- """Note for this transaction"""
278
- lease: bytes | str
279
- """Lease value for this transaction"""
280
- boxes: Sequence[tuple[int, bytes | bytearray | str | int]]
281
- """Box references to include in transaction. A sequence of (app id, box key) tuples"""
282
- accounts: list[str]
283
- """Accounts to include in transaction"""
284
- foreign_apps: list[int]
285
- """List of foreign apps (by app id) to include in transaction"""
286
- foreign_assets: list[int]
287
- """List of foreign assets (by asset id) to include in transaction"""
288
- rekey_to: str
289
- """Address to rekey to"""
290
188
  on_complete: transaction.OnComplete
291
189
 
292
190
 
293
- class CreateCallParametersDict(TypedDict, total=False):
191
+ class CreateCallParametersDict(OnCompleteCallParametersDict, total=False):
294
192
  """Additional parameters that can be included in a transaction when using the
295
193
  ApplicationClient.create/compose_create methods"""
296
194
 
297
- signer: TransactionSigner
298
- """Signer to use when signing this transaction"""
299
- sender: str
300
- """Sender of this transaction"""
301
- suggested_params: transaction.SuggestedParams
302
- """SuggestedParams to use for this transaction"""
303
- note: bytes | str
304
- """Note for this transaction"""
305
- lease: bytes | str
306
- """Lease value for this transaction"""
307
- boxes: Sequence[tuple[int, bytes | bytearray | str | int]]
308
- """Box references to include in transaction. A sequence of (app id, box key) tuples"""
309
- accounts: list[str]
310
- """Accounts to include in transaction"""
311
- foreign_apps: list[int]
312
- """List of foreign apps (by app id) to include in transaction"""
313
- foreign_assets: list[int]
314
- """List of foreign assets (by asset id) to include in transaction"""
315
- rekey_to: str
316
- """Address to rekey to"""
317
- on_complete: transaction.OnComplete
318
195
  extra_pages: int
319
196
 
320
197
 
321
198
  # Pre 1.3.1 backwards compatibility
322
- @deprecated(reason="Use TransactionParameters instead", version="1.3.1")
199
+ @deprecated("Use TransactionParameters instead")
323
200
  class RawTransactionParameters(TransactionParameters):
324
201
  """Deprecated, use TransactionParameters instead"""
325
202
 
326
203
 
327
- @deprecated(reason="Use TransactionParameters instead", version="1.3.1")
204
+ @deprecated("Use TransactionParameters instead")
328
205
  class CommonCallParameters(TransactionParameters):
329
206
  """Deprecated, use TransactionParameters instead"""
330
207
 
331
208
 
332
- @deprecated(reason="Use TransactionParametersDict instead", version="1.3.1")
209
+ @deprecated("Use TransactionParametersDict instead")
333
210
  class CommonCallParametersDict(TransactionParametersDict):
334
211
  """Deprecated, use TransactionParametersDict instead"""
@@ -0,0 +1,140 @@
1
+ import dataclasses
2
+ import os
3
+ from typing import Literal
4
+ from urllib import parse
5
+
6
+ from algosdk.kmd import KMDClient
7
+ from algosdk.v2client.algod import AlgodClient
8
+ from algosdk.v2client.indexer import IndexerClient
9
+ from typing_extensions import deprecated
10
+
11
+ __all__ = [
12
+ "AlgoClientConfig",
13
+ "AlgoClientConfigs",
14
+ "get_algod_client",
15
+ "get_algonode_config",
16
+ "get_default_localnet_config",
17
+ "get_indexer_client",
18
+ "get_kmd_client",
19
+ "get_kmd_client_from_algod_client",
20
+ "is_localnet",
21
+ "is_mainnet",
22
+ "is_testnet",
23
+ ]
24
+
25
+
26
+ @dataclasses.dataclass
27
+ class AlgoClientConfig:
28
+ """Connection details for connecting to an {py:class}`algosdk.v2client.algod.AlgodClient` or
29
+ {py:class}`algosdk.v2client.indexer.IndexerClient`"""
30
+
31
+ server: str
32
+ """URL for the service e.g. `http://localhost:4001` or `https://testnet-api.algonode.cloud`"""
33
+ token: str
34
+ """API Token to authenticate with the service"""
35
+
36
+
37
+ @dataclasses.dataclass
38
+ class AlgoClientConfigs:
39
+ algod_config: AlgoClientConfig
40
+ indexer_config: AlgoClientConfig
41
+ kmd_config: AlgoClientConfig | None
42
+
43
+
44
+ @deprecated("Use AlgorandClient.client.algod")
45
+ def get_default_localnet_config(config: Literal["algod", "indexer", "kmd"]) -> AlgoClientConfig:
46
+ """Returns the client configuration to point to the default LocalNet"""
47
+ port = {"algod": 4001, "indexer": 8980, "kmd": 4002}[config]
48
+ return AlgoClientConfig(server=f"http://localhost:{port}", token="a" * 64)
49
+
50
+
51
+ @deprecated("Use AlgorandClient.testnet() or AlgorandClient.mainnet() instead")
52
+ def get_algonode_config(
53
+ network: Literal["testnet", "mainnet"], config: Literal["algod", "indexer"], token: str
54
+ ) -> AlgoClientConfig:
55
+ client = "api" if config == "algod" else "idx"
56
+ return AlgoClientConfig(
57
+ server=f"https://{network}-{client}.algonode.cloud",
58
+ token=token,
59
+ )
60
+
61
+
62
+ @deprecated("Use AlgorandClient.from_environment() instead.")
63
+ def get_algod_client(config: AlgoClientConfig | None = None) -> AlgodClient:
64
+ """Returns an {py:class}`algosdk.v2client.algod.AlgodClient` from `config` or environment
65
+
66
+ If no configuration provided will use environment variables `ALGOD_SERVER`, `ALGOD_PORT` and `ALGOD_TOKEN`"""
67
+ config = config or _get_config_from_environment("ALGOD")
68
+ headers = {"X-Algo-API-Token": config.token}
69
+ return AlgodClient(config.token, config.server, headers)
70
+
71
+
72
+ @deprecated("Use AlgorandClient.default_localnet().kmd instead")
73
+ def get_kmd_client(config: AlgoClientConfig | None = None) -> KMDClient:
74
+ """Returns an {py:class}`algosdk.kmd.KMDClient` from `config` or environment
75
+
76
+ If no configuration provided will use environment variables `KMD_SERVER`, `KMD_PORT` and `KMD_TOKEN`"""
77
+ config = config or _get_config_from_environment("KMD")
78
+ return KMDClient(config.token, config.server)
79
+
80
+
81
+ @deprecated("Use AlgorandClient.client.from_environment().indexer instead")
82
+ def get_indexer_client(config: AlgoClientConfig | None = None) -> IndexerClient:
83
+ """Returns an {py:class}`algosdk.v2client.indexer.IndexerClient` from `config` or environment.
84
+
85
+ If no configuration provided will use environment variables `INDEXER_SERVER`, `INDEXER_PORT` and `INDEXER_TOKEN`"""
86
+ config = config or _get_config_from_environment("INDEXER")
87
+ headers = {"X-Indexer-API-Token": config.token}
88
+ return IndexerClient(config.token, config.server, headers)
89
+
90
+
91
+ @deprecated("Use AlgorandClient.client.is_localnet() instead")
92
+ def is_localnet(client: AlgodClient) -> bool:
93
+ """Returns True if client genesis is `devnet-v1` or `sandnet-v1`"""
94
+ params = client.suggested_params()
95
+ return params.gen in ["devnet-v1", "sandnet-v1", "dockernet-v1"]
96
+
97
+
98
+ @deprecated("Use AlgorandClient.client.is_mainnet() instead")
99
+ def is_mainnet(client: AlgodClient) -> bool:
100
+ """Returns True if client genesis is `mainnet-v1`"""
101
+ params = client.suggested_params()
102
+ return params.gen in ["mainnet-v1.0", "mainnet-v1", "mainnet"]
103
+
104
+
105
+ @deprecated("Use AlgorandClient.client.is_testnet() instead")
106
+ def is_testnet(client: AlgodClient) -> bool:
107
+ """Returns True if client genesis is `testnet-v1`"""
108
+ params = client.suggested_params()
109
+ return params.gen in ["testnet-v1.0", "testnet-v1", "testnet"]
110
+
111
+
112
+ @deprecated("Use AlgorandClient.default_localnet().kmd instead")
113
+ def get_kmd_client_from_algod_client(client: AlgodClient) -> KMDClient:
114
+ """Returns an {py:class}`algosdk.kmd.KMDClient` from supplied `client`
115
+
116
+ Will use the same address as provided `client` but on port specified by `KMD_PORT` environment variable,
117
+ or 4002 by default"""
118
+ # We can only use Kmd on the LocalNet otherwise it's not exposed so this makes some assumptions
119
+ # (e.g. same token and server as algod and port 4002 by default)
120
+ port = os.getenv("KMD_PORT", "4002")
121
+ server = _replace_kmd_port(client.algod_address, port)
122
+ return KMDClient(client.algod_token, server)
123
+
124
+
125
+ def _replace_kmd_port(address: str, port: str) -> str:
126
+ parsed_algod = parse.urlparse(address)
127
+ kmd_host = parsed_algod.netloc.split(":", maxsplit=1)[0] + f":{port}"
128
+ kmd_parsed = parsed_algod._replace(netloc=kmd_host)
129
+ return parse.urlunparse(kmd_parsed)
130
+
131
+
132
+ def _get_config_from_environment(environment_prefix: str) -> AlgoClientConfig:
133
+ server = os.getenv(f"{environment_prefix}_SERVER")
134
+ if server is None:
135
+ raise Exception(f"Server environment variable not set: {environment_prefix}_SERVER")
136
+ port = os.getenv(f"{environment_prefix}_PORT")
137
+ if port:
138
+ parsed = parse.urlparse(server)
139
+ server = parsed._replace(netloc=f"{parsed.hostname}:{port}").geturl()
140
+ return AlgoClientConfig(server, os.getenv(f"{environment_prefix}_TOKEN", ""))
algokit_utils/account.py CHANGED
@@ -1,183 +1,12 @@
1
- import logging
2
- import os
3
- from typing import TYPE_CHECKING, Any
4
-
5
- from algosdk.account import address_from_private_key
6
- from algosdk.mnemonic import from_private_key, to_private_key
7
- from algosdk.util import algos_to_microalgos
8
-
9
- from algokit_utils._transfer import TransferParameters, transfer
10
- from algokit_utils.models import Account
11
- from algokit_utils.network_clients import get_kmd_client_from_algod_client, is_localnet
12
-
13
- if TYPE_CHECKING:
14
- from collections.abc import Callable
15
-
16
- from algosdk.kmd import KMDClient
17
- from algosdk.v2client.algod import AlgodClient
18
-
19
- __all__ = [
20
- "create_kmd_wallet_account",
21
- "get_account",
22
- "get_account_from_mnemonic",
23
- "get_dispenser_account",
24
- "get_kmd_wallet_account",
25
- "get_localnet_default_account",
26
- "get_or_create_kmd_wallet_account",
27
- ]
28
-
29
- logger = logging.getLogger(__name__)
30
- _DEFAULT_ACCOUNT_MINIMUM_BALANCE = 1_000_000_000
31
-
32
-
33
- def get_account_from_mnemonic(mnemonic: str) -> Account:
34
- """Convert a mnemonic (25 word passphrase) into an Account"""
35
- private_key = to_private_key(mnemonic) # type: ignore[no-untyped-call]
36
- address = address_from_private_key(private_key) # type: ignore[no-untyped-call]
37
- return Account(private_key=private_key, address=address)
38
-
39
-
40
- def create_kmd_wallet_account(kmd_client: "KMDClient", name: str) -> Account:
41
- """Creates a wallet with specified name"""
42
- wallet_id = kmd_client.create_wallet(name, "")["id"] # type: ignore[no-untyped-call]
43
- wallet_handle = kmd_client.init_wallet_handle(wallet_id, "") # type: ignore[no-untyped-call]
44
- kmd_client.generate_key(wallet_handle) # type: ignore[no-untyped-call]
45
-
46
- key_ids: list[str] = kmd_client.list_keys(wallet_handle) # type: ignore[no-untyped-call]
47
- account_key = key_ids[0]
48
-
49
- private_account_key = kmd_client.export_key(wallet_handle, "", account_key) # type: ignore[no-untyped-call]
50
- return get_account_from_mnemonic(from_private_key(private_account_key)) # type: ignore[no-untyped-call]
51
-
52
-
53
- def get_or_create_kmd_wallet_account(
54
- client: "AlgodClient", name: str, fund_with_algos: float = 1000, kmd_client: "KMDClient | None" = None
55
- ) -> Account:
56
- """Returns a wallet with specified name, or creates one if not found"""
57
- kmd_client = kmd_client or get_kmd_client_from_algod_client(client)
58
- account = get_kmd_wallet_account(client, kmd_client, name)
59
-
60
- if account:
61
- account_info = client.account_info(account.address)
62
- assert isinstance(account_info, dict)
63
- if account_info["amount"] > 0:
64
- return account
65
- logger.debug(f"Found existing account in LocalNet with name '{name}', but no funds in the account.")
66
- else:
67
- account = create_kmd_wallet_account(kmd_client, name)
68
-
69
- logger.debug(
70
- f"Couldn't find existing account in LocalNet with name '{name}'. "
71
- f"So created account {account.address} with keys stored in KMD."
72
- )
73
-
74
- logger.debug(f"Funding account {account.address} with {fund_with_algos} ALGOs")
75
-
76
- if fund_with_algos:
77
- transfer(
78
- client,
79
- TransferParameters(
80
- from_account=get_dispenser_account(client),
81
- to_address=account.address,
82
- micro_algos=algos_to_microalgos(fund_with_algos), # type: ignore[no-untyped-call]
83
- ),
84
- )
85
-
86
- return account
87
-
88
-
89
- def _is_default_account(account: dict[str, Any]) -> bool:
90
- return bool(account["status"] != "Offline" and account["amount"] > _DEFAULT_ACCOUNT_MINIMUM_BALANCE)
91
-
92
-
93
- def get_localnet_default_account(client: "AlgodClient") -> Account:
94
- """Returns the default Account in a LocalNet instance"""
95
- if not is_localnet(client):
96
- raise Exception("Can't get a default account from non LocalNet network")
97
-
98
- account = get_kmd_wallet_account(
99
- client, get_kmd_client_from_algod_client(client), "unencrypted-default-wallet", _is_default_account
100
- )
101
- assert account
102
- return account
103
-
104
-
105
- def get_dispenser_account(client: "AlgodClient") -> Account:
106
- """Returns an Account based on DISPENSER_MNENOMIC environment variable or the default account on LocalNet"""
107
- if is_localnet(client):
108
- return get_localnet_default_account(client)
109
- return get_account(client, "DISPENSER")
110
-
111
-
112
- def get_kmd_wallet_account(
113
- client: "AlgodClient",
114
- kmd_client: "KMDClient",
115
- name: str,
116
- predicate: "Callable[[dict[str, Any]], bool] | None" = None,
117
- ) -> Account | None:
118
- """Returns wallet matching specified name and predicate or None if not found"""
119
- wallets: list[dict] = kmd_client.list_wallets() # type: ignore[no-untyped-call]
120
-
121
- wallet = next((w for w in wallets if w["name"] == name), None)
122
- if wallet is None:
123
- return None
124
-
125
- wallet_id = wallet["id"]
126
- wallet_handle = kmd_client.init_wallet_handle(wallet_id, "") # type: ignore[no-untyped-call]
127
- key_ids: list[str] = kmd_client.list_keys(wallet_handle) # type: ignore[no-untyped-call]
128
- matched_account_key = None
129
- if predicate:
130
- for key in key_ids:
131
- account = client.account_info(key)
132
- assert isinstance(account, dict)
133
- if predicate(account):
134
- matched_account_key = key
135
- else:
136
- matched_account_key = next(key_ids.__iter__(), None)
137
-
138
- if not matched_account_key:
139
- return None
140
-
141
- private_account_key = kmd_client.export_key(wallet_handle, "", matched_account_key) # type: ignore[no-untyped-call]
142
- return get_account_from_mnemonic(from_private_key(private_account_key)) # type: ignore[no-untyped-call]
143
-
144
-
145
- def get_account(
146
- client: "AlgodClient", name: str, fund_with_algos: float = 1000, kmd_client: "KMDClient | None" = None
147
- ) -> Account:
148
- """Returns an Algorand account with private key loaded by convention based on the given name identifier.
149
-
150
- # Convention
151
-
152
- **Non-LocalNet:** will load `os.environ[f"{name}_MNEMONIC"]` as a mnemonic secret
153
- Be careful how the mnemonic is handled, never commit it into source control and ideally load it via a
154
- secret storage service rather than the file system.
155
-
156
- **LocalNet:** will load the account from a KMD wallet called {name} and if that wallet doesn't exist it will
157
- create it and fund the account for you
158
-
159
- This allows you to write code that will work seamlessly in production and local development (LocalNet) without
160
- manual config locally (including when you reset the LocalNet).
161
-
162
- # Example
163
- If you have a mnemonic secret loaded into `os.environ["ACCOUNT_MNEMONIC"]` then you can call the following to get
164
- that private key loaded into an account object:
165
- ```python
166
- account = get_account('ACCOUNT', algod)
167
- ```
168
-
169
- If that code runs against LocalNet then a wallet called 'ACCOUNT' will automatically be created with an account
170
- that is automatically funded with 1000 (default) ALGOs from the default LocalNet dispenser.
171
- """
172
-
173
- mnemonic_key = f"{name.upper()}_MNEMONIC"
174
- mnemonic = os.getenv(mnemonic_key)
175
- if mnemonic:
176
- return get_account_from_mnemonic(mnemonic)
177
-
178
- if is_localnet(client):
179
- account = get_or_create_kmd_wallet_account(client, name, fund_with_algos, kmd_client)
180
- os.environ[mnemonic_key] = from_private_key(account.private_key) # type: ignore[no-untyped-call]
181
- return account
182
-
183
- raise Exception(f"Missing environment variable '{mnemonic_key}' when looking for account '{name}'")
1
+ import warnings
2
+
3
+ warnings.warn(
4
+ """The legacy v2 account module is deprecated and will be removed in a future version.
5
+ Use `SigningAccount` abstraction from `algokit_utils.models` instead or
6
+ classes compliant with `TransactionSignerAccountProtocol` obtained from `AccountManager`.
7
+ """,
8
+ DeprecationWarning,
9
+ stacklevel=2,
10
+ )
11
+
12
+ from algokit_utils._legacy_v2.account import * # noqa: F403, E402
@@ -0,0 +1,2 @@
1
+ from algokit_utils.accounts.account_manager import * # noqa: F403
2
+ from algokit_utils.accounts.kmd_account_manager import * # noqa: F403