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
|
@@ -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
|
|
15
|
-
|
|
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
|
-
"""
|
|
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)
|
|
@@ -202,14 +181,14 @@ class TransactionParametersDict(TypedDict, total=False):
|
|
|
202
181
|
"""Address to rekey to"""
|
|
203
182
|
|
|
204
183
|
|
|
205
|
-
class OnCompleteCallParametersDict(
|
|
184
|
+
class OnCompleteCallParametersDict(TransactionParametersDict, total=False):
|
|
206
185
|
"""Additional parameters that can be included in a transaction when using the
|
|
207
186
|
ApplicationClient.call/compose_call methods"""
|
|
208
187
|
|
|
209
188
|
on_complete: transaction.OnComplete
|
|
210
189
|
|
|
211
190
|
|
|
212
|
-
class CreateCallParametersDict(
|
|
191
|
+
class CreateCallParametersDict(OnCompleteCallParametersDict, total=False):
|
|
213
192
|
"""Additional parameters that can be included in a transaction when using the
|
|
214
193
|
ApplicationClient.create/compose_create methods"""
|
|
215
194
|
|
|
@@ -217,24 +196,16 @@ class CreateCallParametersDict(TypedDict, OnCompleteCallParametersDict, total=Fa
|
|
|
217
196
|
|
|
218
197
|
|
|
219
198
|
# Pre 1.3.1 backwards compatibility
|
|
220
|
-
@deprecated(
|
|
199
|
+
@deprecated("Use TransactionParameters instead")
|
|
221
200
|
class RawTransactionParameters(TransactionParameters):
|
|
222
201
|
"""Deprecated, use TransactionParameters instead"""
|
|
223
202
|
|
|
224
203
|
|
|
225
|
-
@deprecated(
|
|
204
|
+
@deprecated("Use TransactionParameters instead")
|
|
226
205
|
class CommonCallParameters(TransactionParameters):
|
|
227
206
|
"""Deprecated, use TransactionParameters instead"""
|
|
228
207
|
|
|
229
208
|
|
|
230
|
-
@deprecated(
|
|
209
|
+
@deprecated("Use TransactionParametersDict instead")
|
|
231
210
|
class CommonCallParametersDict(TransactionParametersDict):
|
|
232
211
|
"""Deprecated, use TransactionParametersDict instead"""
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
@dataclasses.dataclass
|
|
236
|
-
class SimulationTrace:
|
|
237
|
-
app_budget_added: int | None
|
|
238
|
-
app_budget_consumed: int | None
|
|
239
|
-
failure_message: str | None
|
|
240
|
-
exec_trace: dict[str, object]
|
|
@@ -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
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
from
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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"]
|
|
43
|
-
wallet_handle = kmd_client.init_wallet_handle(wallet_id, "")
|
|
44
|
-
kmd_client.generate_key(wallet_handle)
|
|
45
|
-
|
|
46
|
-
key_ids: list[str] = kmd_client.list_keys(wallet_handle)
|
|
47
|
-
account_key = key_ids[0]
|
|
48
|
-
|
|
49
|
-
private_account_key = kmd_client.export_key(wallet_handle, "", account_key)
|
|
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()
|
|
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, "")
|
|
127
|
-
key_ids: list[str] = kmd_client.list_keys(wallet_handle)
|
|
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)
|
|
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
|