algokit-utils 2.4.0b1__py3-none-any.whl → 3.0.0__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 -181
  2. algokit_utils/_debugging.py +89 -45
  3. algokit_utils/_legacy_v2/__init__.py +177 -0
  4. algokit_utils/{_ensure_funded.py → _legacy_v2/_ensure_funded.py} +21 -24
  5. algokit_utils/{_transfer.py → _legacy_v2/_transfer.py} +26 -23
  6. algokit_utils/_legacy_v2/account.py +203 -0
  7. algokit_utils/_legacy_v2/application_client.py +1472 -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} +16 -45
  14. algokit_utils/_legacy_v2/network_clients.py +144 -0
  15. algokit_utils/account.py +12 -183
  16. algokit_utils/accounts/__init__.py +2 -0
  17. algokit_utils/accounts/account_manager.py +912 -0
  18. algokit_utils/accounts/kmd_account_manager.py +161 -0
  19. algokit_utils/algorand.py +359 -0
  20. algokit_utils/application_client.py +9 -1447
  21. algokit_utils/application_specification.py +39 -197
  22. algokit_utils/applications/__init__.py +7 -0
  23. algokit_utils/applications/abi.py +275 -0
  24. algokit_utils/applications/app_client.py +2108 -0
  25. algokit_utils/applications/app_deployer.py +725 -0
  26. algokit_utils/applications/app_factory.py +1134 -0
  27. algokit_utils/applications/app_manager.py +578 -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 +989 -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 +336 -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 +738 -0
  42. algokit_utils/clients/dispenser_api_client.py +224 -0
  43. algokit_utils/common.py +8 -26
  44. algokit_utils/config.py +76 -29
  45. algokit_utils/deploy.py +7 -894
  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 -82
  50. algokit_utils/models/__init__.py +8 -0
  51. algokit_utils/models/account.py +217 -0
  52. algokit_utils/models/amount.py +200 -0
  53. algokit_utils/models/application.py +91 -0
  54. algokit_utils/models/network.py +29 -0
  55. algokit_utils/models/simulate.py +11 -0
  56. algokit_utils/models/state.py +68 -0
  57. algokit_utils/models/transaction.py +100 -0
  58. algokit_utils/network_clients.py +7 -128
  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 +2499 -0
  64. algokit_utils/transactions/transaction_creator.py +688 -0
  65. algokit_utils/transactions/transaction_sender.py +1219 -0
  66. {algokit_utils-2.4.0b1.dist-info → algokit_utils-3.0.0.dist-info}/METADATA +11 -7
  67. algokit_utils-3.0.0.dist-info/RECORD +70 -0
  68. {algokit_utils-2.4.0b1.dist-info → algokit_utils-3.0.0.dist-info}/WHEEL +1 -1
  69. algokit_utils-2.4.0b1.dist-info/RECORD +0 -24
  70. {algokit_utils-2.4.0b1.dist-info → algokit_utils-3.0.0.dist-info}/LICENSE +0 -0
@@ -6,30 +6,29 @@ import algosdk.transaction
6
6
  from algosdk.account import address_from_private_key
7
7
  from algosdk.atomic_transaction_composer import AccountTransactionSigner
8
8
  from algosdk.transaction import AssetTransferTxn, PaymentTxn, SuggestedParams
9
+ from typing_extensions import deprecated
9
10
 
10
- from algokit_utils.models import Account
11
+ from algokit_utils._legacy_v2.models import Account
11
12
 
12
13
  if TYPE_CHECKING:
13
14
  from algosdk.v2client.algod import AlgodClient
14
15
 
15
- __all__ = ["TransferParameters", "transfer", "TransferAssetParameters", "transfer_asset"]
16
+ __all__ = ["TransferAssetParameters", "TransferParameters", "transfer", "transfer_asset"]
16
17
  logger = logging.getLogger(__name__)
17
18
 
18
19
 
19
20
  @dataclasses.dataclass(kw_only=True)
20
21
  class TransferParametersBase:
21
- """Parameters for transferring µALGOs between accounts
22
-
23
- Args:
24
- from_account (Account | AccountTransactionSigner): The account (with private key) or signer that will send
25
- the µALGOs
26
- to_address (str): The account address that will receive the µALGOs
27
- suggested_params (SuggestedParams | None): (optional) transaction parameters
28
- note (str | bytes | None): (optional) transaction note
29
- fee_micro_algos (int | None): (optional) The flat fee you want to pay, useful for covering extra fees in a
30
- transaction group or app call
31
- max_fee_micro_algos (int | None): (optional) The maximum fee that you are happy to pay (default: unbounded)
32
- - if this is set it's possible the transaction could get rejected during network congestion
22
+ """Parameters for transferring µALGOs between accounts.
23
+
24
+ This class contains the base parameters needed for transferring µALGOs between Algorand accounts.
25
+
26
+ :ivar from_account: The account (with private key) or signer that will send the µALGOs
27
+ :ivar to_address: The account address that will receive the µALGOs
28
+ :ivar suggested_params: Transaction parameters, defaults to None
29
+ :ivar note: Transaction note, defaults to None
30
+ :ivar fee_micro_algos: The flat fee you want to pay, useful for covering extra fees in a transaction group or app call, defaults to None
31
+ :ivar max_fee_micro_algos: The maximum fee that you are happy to pay - if this is set it's possible the transaction could get rejected during network congestion, defaults to None
33
32
  """
34
33
 
35
34
  from_account: Account | AccountTransactionSigner
@@ -40,6 +39,7 @@ class TransferParametersBase:
40
39
  max_fee_micro_algos: int | None = None
41
40
 
42
41
 
42
+ @deprecated("Use `algorand.send.payment(...)` / `algorand.create_transaction.payment(...)` instead")
43
43
  @dataclasses.dataclass(kw_only=True)
44
44
  class TransferParameters(TransferParametersBase):
45
45
  """Parameters for transferring µALGOs between accounts"""
@@ -47,15 +47,16 @@ class TransferParameters(TransferParametersBase):
47
47
  micro_algos: int
48
48
 
49
49
 
50
+ @deprecated("Use `algorand.send.asset_transfer(...)` / `algorand.create_transaction.asset_transfer(...)` instead")
50
51
  @dataclasses.dataclass(kw_only=True)
51
52
  class TransferAssetParameters(TransferParametersBase):
52
- """Parameters for transferring assets between accounts
53
+ """Parameters for transferring assets between accounts.
54
+
55
+ Defines the parameters needed to transfer Algorand Standard Assets (ASAs) between accounts.
53
56
 
54
- Args:
55
- asset_id (int): The asset id that will be transfered
56
- amount (int): The amount to send
57
- clawback_from (str | None): An address of a target account from which to perform a clawback operation. Please
58
- note, in such cases senderAccount must be equal to clawback field on ASA metadata.
57
+ :param asset_id: The asset id that will be transferred
58
+ :param amount: The amount of the asset to send
59
+ :param clawback_from: An address of a target account from which to perform a clawback operation. Please note, in such cases senderAccount must be equal to clawback field on ASA metadata, defaults to None
59
60
  """
60
61
 
61
62
  asset_id: int
@@ -80,6 +81,7 @@ def _check_fee(transaction: PaymentTxn | AssetTransferTxn, max_fee: int | None)
80
81
  )
81
82
 
82
83
 
84
+ @deprecated("Use `algorand.send.payment(...)` / `algorand.create_transaction.payment(...)` instead")
83
85
  def transfer(client: "AlgodClient", parameters: TransferParameters) -> PaymentTxn:
84
86
  """Transfer µALGOs between accounts"""
85
87
 
@@ -93,13 +95,14 @@ def transfer(client: "AlgodClient", parameters: TransferParameters) -> PaymentTx
93
95
  amt=params.micro_algos,
94
96
  note=params.note.encode("utf-8") if isinstance(params.note, str) else params.note,
95
97
  sp=params.suggested_params,
96
- ) # type: ignore[no-untyped-call]
98
+ )
97
99
 
98
100
  result = _send_transaction(client=client, transaction=transaction, parameters=params)
99
101
  assert isinstance(result, PaymentTxn)
100
102
  return result
101
103
 
102
104
 
105
+ @deprecated("Use `algorand.send.asset_transfer(...)` / `algorand.create_transaction.asset_transfer(...)` instead")
103
106
  def transfer_asset(client: "AlgodClient", parameters: TransferAssetParameters) -> AssetTransferTxn:
104
107
  """Transfer assets between accounts"""
105
108
 
@@ -117,7 +120,7 @@ def transfer_asset(client: "AlgodClient", parameters: TransferAssetParameters) -
117
120
  note=params.note,
118
121
  index=params.asset_id,
119
122
  rekey_to=None,
120
- ) # type: ignore[no-untyped-call]
123
+ )
121
124
 
122
125
  result = _send_transaction(client=client, transaction=xfer_txn, parameters=params)
123
126
  assert isinstance(result, AssetTransferTxn)
@@ -148,5 +151,5 @@ def _get_address(account: Account | AccountTransactionSigner) -> str:
148
151
  if type(account) is Account:
149
152
  return account.address
150
153
  else:
151
- address = address_from_private_key(account.private_key) # type: ignore[no-untyped-call]
154
+ address = address_from_private_key(account.private_key)
152
155
  return str(address)
@@ -0,0 +1,203 @@
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
+ from typing_extensions import deprecated
9
+
10
+ from algokit_utils._legacy_v2._transfer import TransferParameters, transfer
11
+ from algokit_utils._legacy_v2.models import Account
12
+ from algokit_utils._legacy_v2.network_clients import get_kmd_client_from_algod_client, is_localnet
13
+
14
+ if TYPE_CHECKING:
15
+ from collections.abc import Callable
16
+
17
+ from algosdk.kmd import KMDClient
18
+ from algosdk.v2client.algod import AlgodClient
19
+
20
+ __all__ = [
21
+ "create_kmd_wallet_account",
22
+ "get_account",
23
+ "get_account_from_mnemonic",
24
+ "get_dispenser_account",
25
+ "get_kmd_wallet_account",
26
+ "get_localnet_default_account",
27
+ "get_or_create_kmd_wallet_account",
28
+ ]
29
+
30
+ logger = logging.getLogger(__name__)
31
+ _DEFAULT_ACCOUNT_MINIMUM_BALANCE = 1_000_000_000
32
+
33
+
34
+ @deprecated(
35
+ "Use `algorand.account.from_mnemonic()` instead. Example: " "`account = algorand.account.from_mnemonic(mnemonic)`"
36
+ )
37
+ def get_account_from_mnemonic(mnemonic: str) -> Account:
38
+ """Convert a mnemonic (25 word passphrase) into an Account"""
39
+ private_key = to_private_key(mnemonic)
40
+ address = str(address_from_private_key(private_key))
41
+ return Account(private_key=private_key, address=address)
42
+
43
+
44
+ @deprecated(
45
+ "Use `algorand.account.kmd.get_or_create_wallet_account(name, fund_with)` or `KMDAccountManager(clientManager).get_or_create_wallet_account(name, fund_with)` instead"
46
+ )
47
+ def create_kmd_wallet_account(kmd_client: "KMDClient", name: str) -> Account:
48
+ """Creates a wallet with specified name"""
49
+ wallet_id = kmd_client.create_wallet(name, "")["id"]
50
+ wallet_handle = kmd_client.init_wallet_handle(wallet_id, "")
51
+ kmd_client.generate_key(wallet_handle)
52
+
53
+ key_ids: list[str] = kmd_client.list_keys(wallet_handle)
54
+ account_key = key_ids[0]
55
+
56
+ private_account_key = kmd_client.export_key(wallet_handle, "", account_key)
57
+ return get_account_from_mnemonic(from_private_key(private_account_key))
58
+
59
+
60
+ @deprecated(
61
+ "Use `algorand.account.kmd.get_or_create_wallet_account(name, fund_with)` or `KMDAccountManager(clientManager).get_or_create_wallet_account(name, fund_with)` instead"
62
+ )
63
+ def get_or_create_kmd_wallet_account(
64
+ client: "AlgodClient", name: str, fund_with_algos: float = 1000, kmd_client: "KMDClient | None" = None
65
+ ) -> Account:
66
+ """Returns a wallet with specified name, or creates one if not found"""
67
+ kmd_client = kmd_client or get_kmd_client_from_algod_client(client)
68
+ account = get_kmd_wallet_account(client, kmd_client, name)
69
+
70
+ if account:
71
+ account_info = client.account_info(account.address)
72
+ assert isinstance(account_info, dict)
73
+ if account_info["amount"] > 0:
74
+ return account
75
+ logger.debug(f"Found existing account in LocalNet with name '{name}', but no funds in the account.")
76
+ else:
77
+ account = create_kmd_wallet_account(kmd_client, name)
78
+
79
+ logger.debug(
80
+ f"Couldn't find existing account in LocalNet with name '{name}'. "
81
+ f"So created account {account.address} with keys stored in KMD."
82
+ )
83
+
84
+ logger.debug(f"Funding account {account.address} with {fund_with_algos} ALGOs")
85
+
86
+ if fund_with_algos:
87
+ transfer(
88
+ client,
89
+ TransferParameters(
90
+ from_account=get_dispenser_account(client),
91
+ to_address=account.address,
92
+ micro_algos=algos_to_microalgos(fund_with_algos),
93
+ ),
94
+ )
95
+
96
+ return account
97
+
98
+
99
+ def _is_default_account(account: dict[str, Any]) -> bool:
100
+ return bool(account["status"] != "Offline" and account["amount"] > _DEFAULT_ACCOUNT_MINIMUM_BALANCE)
101
+
102
+
103
+ @deprecated(
104
+ "Use `algorand.account.localnet_dispenser()` or `algorand.account.from_kmd('unencrypted-default-wallet', lambda a: a['status'] != 'Offline' and a['amount'] > 1_000_000_000)`"
105
+ )
106
+ def get_localnet_default_account(client: "AlgodClient") -> Account:
107
+ """Returns the default Account in a LocalNet instance"""
108
+ if not is_localnet(client):
109
+ raise Exception("Can't get a default account from non LocalNet network")
110
+
111
+ account = get_kmd_wallet_account(
112
+ client, get_kmd_client_from_algod_client(client), "unencrypted-default-wallet", _is_default_account
113
+ )
114
+ assert account
115
+ return account
116
+
117
+
118
+ @deprecated(
119
+ "Use `algorand.account.dispenser_from_environment()` or `algorand.account.localnet_dispenser()` instead. "
120
+ "Example: `dispenser = algorand.account.dispenser_from_environment()`"
121
+ )
122
+ def get_dispenser_account(client: "AlgodClient") -> Account:
123
+ """Returns an Account based on DISPENSER_MNENOMIC environment variable or the default account on LocalNet"""
124
+ if is_localnet(client):
125
+ return get_localnet_default_account(client)
126
+ return get_account(client, "DISPENSER")
127
+
128
+
129
+ @deprecated(
130
+ "Use `algorand.account.from_kmd()` instead. Example: " "`account = algorand.account.from_kmd(name, predicate)`"
131
+ )
132
+ def get_kmd_wallet_account(
133
+ client: "AlgodClient",
134
+ kmd_client: "KMDClient",
135
+ name: str,
136
+ predicate: "Callable[[dict[str, Any]], bool] | None" = None,
137
+ ) -> Account | None:
138
+ """Returns wallet matching specified name and predicate or None if not found"""
139
+ wallets: list[dict] = kmd_client.list_wallets()
140
+
141
+ wallet = next((w for w in wallets if w["name"] == name), None)
142
+ if wallet is None:
143
+ return None
144
+
145
+ wallet_id = wallet["id"]
146
+ wallet_handle = kmd_client.init_wallet_handle(wallet_id, "")
147
+ key_ids: list[str] = kmd_client.list_keys(wallet_handle)
148
+ matched_account_key = None
149
+ if predicate:
150
+ for key in key_ids:
151
+ account = client.account_info(key)
152
+ assert isinstance(account, dict)
153
+ if predicate(account):
154
+ matched_account_key = key
155
+ else:
156
+ matched_account_key = next(key_ids.__iter__(), None)
157
+
158
+ if not matched_account_key:
159
+ return None
160
+
161
+ private_account_key = kmd_client.export_key(wallet_handle, "", matched_account_key)
162
+ return get_account_from_mnemonic(from_private_key(private_account_key))
163
+
164
+
165
+ @deprecated(
166
+ "Use `algorand.account.from_environment()` or `algorand.account.from_kmd()` or `algorand.account.random()` instead. "
167
+ "Example: "
168
+ "`account = algorand.account.from_environment('ACCOUNT', AlgoAmount.from_algo(1000))`"
169
+ )
170
+ def get_account(
171
+ client: "AlgodClient", name: str, fund_with_algos: float = 1000, kmd_client: "KMDClient | None" = None
172
+ ) -> Account:
173
+ """Returns an Algorand account with private key loaded by convention based on the given name identifier.
174
+ Returns an Algorand account with private key loaded by convention based on the given name identifier.
175
+
176
+ For non-LocalNet environments, loads the mnemonic secret from environment variable {name}_MNEMONIC.
177
+ For LocalNet environments, loads or creates an account from a KMD wallet named {name}.
178
+
179
+ :example:
180
+ >>> # If you have a mnemonic secret loaded into `os.environ["ACCOUNT_MNEMONIC"]` then you can call:
181
+ >>> account = get_account('ACCOUNT', algod)
182
+ >>> # If that code runs against LocalNet then a wallet called 'ACCOUNT' will automatically be created
183
+ >>> # with an account that is automatically funded with 1000 (default) ALGOs from the default LocalNet dispenser.
184
+
185
+ :param client: The Algorand client to use
186
+ :param name: The name identifier to use for loading/creating the account
187
+ :param fund_with_algos: Amount of Algos to fund new LocalNet accounts with, defaults to 1000
188
+ :param kmd_client: Optional KMD client to use for LocalNet wallet operations
189
+ :raises Exception: If required environment variable is missing in non-LocalNet environment
190
+ :return: An Account object with loaded private key
191
+ """
192
+
193
+ mnemonic_key = f"{name.upper()}_MNEMONIC"
194
+ mnemonic = os.getenv(mnemonic_key)
195
+ if mnemonic:
196
+ return get_account_from_mnemonic(mnemonic)
197
+
198
+ if is_localnet(client):
199
+ account = get_or_create_kmd_wallet_account(client, name, fund_with_algos, kmd_client)
200
+ os.environ[mnemonic_key] = from_private_key(account.private_key)
201
+ return account
202
+
203
+ raise Exception(f"Missing environment variable '{mnemonic_key}' when looking for account '{name}'")