algokit-utils 4.0.1b3__py3-none-any.whl → 4.1.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.

@@ -32,7 +32,7 @@ _DEFAULT_ACCOUNT_MINIMUM_BALANCE = 1_000_000_000
32
32
 
33
33
 
34
34
  @deprecated(
35
- "Use `algorand.account.from_mnemonic()` instead. Example: " "`account = algorand.account.from_mnemonic(mnemonic)`"
35
+ "Use `algorand.account.from_mnemonic()` instead. Example: `account = algorand.account.from_mnemonic(mnemonic)`"
36
36
  )
37
37
  def get_account_from_mnemonic(mnemonic: str) -> Account:
38
38
  """Convert a mnemonic (25 word passphrase) into an Account"""
@@ -127,7 +127,7 @@ def get_dispenser_account(client: "AlgodClient") -> Account:
127
127
 
128
128
 
129
129
  @deprecated(
130
- "Use `algorand.account.from_kmd()` instead. Example: " "`account = algorand.account.from_kmd(name, predicate)`"
130
+ "Use `algorand.account.from_kmd()` instead. Example: `account = algorand.account.from_kmd(name, predicate)`"
131
131
  )
132
132
  def get_kmd_wallet_account(
133
133
  client: "AlgodClient",
@@ -243,9 +243,7 @@ class ApplicationClient:
243
243
  """Creates a copy of this ApplicationClient, using the new signer, sender and app_id values if provided.
244
244
  Will also substitute provided template_values into the associated app_spec in the copy"""
245
245
  new_client: ApplicationClient = copy.copy(self)
246
- new_client._prepare( # noqa: SLF001
247
- new_client, signer=signer, sender=sender, app_id=app_id, template_values=template_values
248
- )
246
+ new_client._prepare(new_client, signer=signer, sender=sender, app_id=app_id, template_values=template_values)
249
247
  return new_client
250
248
 
251
249
  def _prepare(
@@ -641,8 +641,7 @@ class Deployer:
641
641
  elif self.on_update == OnUpdate.ReplaceApp:
642
642
  if self.existing_app_metadata_or_reference.updatable is False:
643
643
  logger.warning(
644
- "App is not updatable and on_update=ReplaceApp, "
645
- "will attempt to create new app and delete old app"
644
+ "App is not updatable and on_update=ReplaceApp, will attempt to create new app and delete old app"
646
645
  )
647
646
  else:
648
647
  logger.warning(
@@ -95,6 +95,9 @@ __all__ = [
95
95
  # TEAL opcodes for constant blocks
96
96
  BYTE_CBLOCK = 38 # bytecblock opcode
97
97
  INT_CBLOCK = 32 # intcblock opcode
98
+ MAX_SIMULATE_OPCODE_BUDGET = (
99
+ 20_000 * 16
100
+ ) # https://github.com/algorand/go-algorand/blob/807b29a91c371d225e12b9287c5d56e9b33c4e4c/ledger/simulation/trace.go#L104
98
101
 
99
102
  T = TypeVar("T") # For generic return type in _handle_call_errors
100
103
 
@@ -449,21 +452,16 @@ class _StateAccessor:
449
452
  if not box.name_raw.startswith(prefix):
450
453
  continue
451
454
 
452
- encoded_key = prefix + box.name_raw
453
- base64_key = base64.b64encode(encoded_key).decode("utf-8")
454
-
455
455
  try:
456
456
  key = get_abi_decoded_value(box.name_raw[len(prefix) :], metadata.key_type, self._app_spec.structs)
457
457
  value = get_abi_decoded_value(
458
- self._algorand.app.get_box_value(self._app_id, base64.b64decode(base64_key)),
458
+ self._algorand.app.get_box_value(self._app_id, box.name_raw),
459
459
  metadata.value_type,
460
460
  self._app_spec.structs,
461
461
  )
462
462
  result[str(key)] = value
463
463
  except Exception as e:
464
- if "Failed to decode key" in str(e):
465
- raise ValueError(f"Failed to decode key {base64_key}") from e
466
- raise ValueError(f"Failed to decode value for key {base64_key}") from e
464
+ raise ValueError(f"Failed to decode value for key {box.name_raw.decode('utf-8')}") from e
467
465
 
468
466
  return result
469
467
 
@@ -1183,7 +1181,6 @@ class _TransactionSender:
1183
1181
  :param params: Parameters for the application call including method and transaction options
1184
1182
  :param send_params: Send parameters
1185
1183
  :return: The result of sending or simulating the transaction, including ABI return value if applicable
1186
- :raises ValueError: If the transaction is read-only and `max_fee` is not provided
1187
1184
  """
1188
1185
  is_read_only_call = (
1189
1186
  params.on_complete == algosdk.transaction.OnComplete.NoOpOC or params.on_complete is None
@@ -1194,14 +1191,10 @@ class _TransactionSender:
1194
1191
  readonly_send_params = send_params or SendParams()
1195
1192
 
1196
1193
  # Read-only calls do not require fees to be paid, as they are only simulated on the network.
1197
- # Therefore there is no value in calculating the minimum fee needed for a successful app call with inners.
1198
- # As a a result we only need to send a single simulate call,
1199
- # however to do this successfully we need to ensure fees for the transaction are fully covered using maxFee.
1200
- if readonly_send_params.get("cover_app_call_inner_transaction_fees"):
1201
- if params.max_fee is None:
1202
- raise ValueError(
1203
- "Please provide a `max_fee` for the transaction when `cover_app_call_inner_transaction_fees` is enabled." # noqa: E501
1204
- )
1194
+ # With maximum opcode budget provided, ensure_budget won't create inner transactions,
1195
+ # so fee coverage is no longer a concern for read-only calls.
1196
+ # If max_fee is provided, use it as static_fee for potential benefits.
1197
+ if readonly_send_params.get("cover_app_call_inner_transaction_fees") and params.max_fee is not None:
1205
1198
  readonly_params = replace(readonly_params, static_fee=params.max_fee, extra_fee=None)
1206
1199
 
1207
1200
  method_call_to_simulate = self._algorand.new_group().add_app_call_method_call(
@@ -1215,11 +1208,13 @@ class _TransactionSender:
1215
1208
  skip_signatures=True,
1216
1209
  allow_more_logs=True,
1217
1210
  allow_empty_signatures=True,
1218
- extra_opcode_budget=None,
1211
+ extra_opcode_budget=MAX_SIMULATE_OPCODE_BUDGET,
1219
1212
  exec_trace_config=None,
1220
1213
  simulation_round=None,
1221
1214
  )
1222
1215
  except Exception as e:
1216
+ # For read-only calls with max opcode budget, fee issues should be rare
1217
+ # but we can still provide helpful error message if they occur
1223
1218
  if readonly_send_params.get("cover_app_call_inner_transaction_fees") and "fee too small" in str(e):
1224
1219
  raise ValueError(
1225
1220
  "Fees were too small. You may need to increase the transaction `maxFee`."
@@ -2047,7 +2042,7 @@ class AppClient:
2047
2042
  if not value:
2048
2043
  raise ValueError(
2049
2044
  f"Key '{default_value.data}' not found in {default_value.source} "
2050
- f"storage for argument {method_arg.name or f'arg{i+1}'}"
2045
+ f"storage for argument {method_arg.name or f'arg{i + 1}'}"
2051
2046
  )
2052
2047
 
2053
2048
  if value.value_raw:
@@ -2065,7 +2060,7 @@ class AppClient:
2065
2060
  elif not algosdk.abi.is_abi_transaction_type(method_arg.type):
2066
2061
  raise ValueError(
2067
2062
  f"No value provided for required argument "
2068
- f"{method_arg.name or f'arg{i+1}'} in call to method {method.name}"
2063
+ f"{method_arg.name or f'arg{i + 1}'} in call to method {method.name}"
2069
2064
  )
2070
2065
  elif arg_value is None and default_value is None:
2071
2066
  # At this point only allow explicit None values if no default value was identified
@@ -239,7 +239,7 @@ class AppDeployer:
239
239
  suppress_log = send_params.get("suppress_log") or False
240
240
 
241
241
  config.logger.info(
242
- f"Idempotently deploying app \"{deployment.metadata.name}\" from creator "
242
+ f'Idempotently deploying app "{deployment.metadata.name}" from creator '
243
243
  f"{deployment.create_params.sender} using {len(deployment.create_params.approval_program)} bytes of "
244
244
  f"{'teal code' if isinstance(deployment.create_params.approval_program, str) else 'AVM bytecode'} and "
245
245
  f"{len(deployment.create_params.clear_state_program)} bytes of "
@@ -447,7 +447,8 @@ class AppDeployer:
447
447
  composer = self._transaction_sender.new_group()
448
448
 
449
449
  # Add create transaction
450
- if isinstance(deployment.create_params, AppCreateMethodCallParams):
450
+ has_abi_create = isinstance(deployment.create_params, AppCreateMethodCallParams)
451
+ if has_abi_create:
451
452
  composer.add_app_create_method_call(
452
453
  AppCreateMethodCallParams(
453
454
  **{
@@ -467,10 +468,10 @@ class AppDeployer:
467
468
  }
468
469
  )
469
470
  )
470
- create_txn_index = composer.count() - 1
471
471
 
472
472
  # Add delete transaction
473
- if isinstance(deployment.delete_params, AppDeleteMethodCallParams):
473
+ has_abi_delete = isinstance(deployment.delete_params, AppDeleteMethodCallParams)
474
+ if has_abi_delete:
474
475
  delete_call_params = AppDeleteMethodCallParams(
475
476
  **{
476
477
  **deployment.delete_params.__dict__,
@@ -486,12 +487,15 @@ class AppDeployer:
486
487
  }
487
488
  )
488
489
  composer.add_app_delete(delete_params)
489
- delete_txn_index = composer.count() - 1
490
490
 
491
- result = composer.send()
491
+ result = composer.send(deployment.send_params)
492
492
 
493
- create_result = SendAppCreateTransactionResult[ABIReturn].from_composer_result(result, create_txn_index)
494
- delete_result = SendAppTransactionResult[ABIReturn].from_composer_result(result, delete_txn_index)
493
+ create_result = SendAppCreateTransactionResult[ABIReturn].from_composer_result(
494
+ result, is_abi=has_abi_create, index=0
495
+ )
496
+ delete_result = SendAppTransactionResult[ABIReturn].from_composer_result(
497
+ result, is_abi=has_abi_delete, index=-1
498
+ )
495
499
 
496
500
  app_id = int(result.confirmations[0]["application-index"]) # type: ignore[call-overload]
497
501
  app_metadata = ApplicationMetaData(
@@ -623,8 +627,7 @@ class AppDeployer:
623
627
 
624
628
  if deployment.on_update in (OnUpdate.Fail, "fail") or deployment.on_update is None:
625
629
  raise ValueError(
626
- "Update detected and on_update=Fail, stopping deployment. "
627
- "Try a different on_update value to not fail."
630
+ "Update detected and on_update=Fail, stopping deployment. Try a different on_update value to not fail."
628
631
  )
629
632
 
630
633
  if deployment.on_update in (OnUpdate.AppendApp, "append"):
@@ -1122,13 +1122,12 @@ class AppFactory:
1122
1122
  results.append(decoded_value)
1123
1123
  else:
1124
1124
  raise ValueError(
1125
- f"Cannot provide default value from source={default_value.source} "
1126
- "for a contract creation call."
1125
+ f"Cannot provide default value from source={default_value.source} for a contract creation call."
1127
1126
  )
1128
1127
  else:
1129
1128
  param_name = param.name or f"arg{i + 1}"
1130
1129
  raise ValueError(
1131
- f"No value provided for required argument {param_name} " f"in call to method {method.name}"
1130
+ f"No value provided for required argument {param_name} in call to method {method.name}"
1132
1131
  )
1133
1132
 
1134
1133
  return results
@@ -17,7 +17,7 @@ from algosdk.atomic_transaction_composer import (
17
17
  TransactionSigner,
18
18
  TransactionWithSigner,
19
19
  )
20
- from algosdk.transaction import ApplicationCallTxn, OnComplete, SuggestedParams
20
+ from algosdk.transaction import OnComplete, SuggestedParams
21
21
  from algosdk.v2client.algod import AlgodClient
22
22
  from algosdk.v2client.models.simulate_request import SimulateRequest
23
23
  from typing_extensions import deprecated
@@ -1833,8 +1833,7 @@ class TransactionComposer:
1833
1833
  group = self.build().transactions
1834
1834
 
1835
1835
  if not params:
1836
- has_app_call = any(isinstance(txn.txn, ApplicationCallTxn) for txn in group)
1837
- params = SendParams() if has_app_call else SendParams()
1836
+ params = SendParams()
1838
1837
 
1839
1838
  cover_app_call_inner_transaction_fees = params.get("cover_app_call_inner_transaction_fees")
1840
1839
  populate_app_call_resources = params.get("populate_app_call_resources")
@@ -81,7 +81,9 @@ class SendSingleTransactionResult:
81
81
  """The ABI return value if applicable"""
82
82
 
83
83
  @classmethod
84
- def from_composer_result(cls, result: SendAtomicTransactionComposerResults, index: int = -1) -> Self:
84
+ def from_composer_result(
85
+ cls, result: SendAtomicTransactionComposerResults, *, is_abi: bool = False, index: int = -1
86
+ ) -> Self:
85
87
  # Get base parameters
86
88
  base_params = {
87
89
  "transaction": result.transactions[index],
@@ -104,12 +106,12 @@ class SendSingleTransactionResult:
104
106
  {
105
107
  "app_id": app_id,
106
108
  "app_address": algosdk.logic.get_application_address(app_id),
107
- "abi_return": result.returns[index] if result.returns else None, # type: ignore[dict-item]
109
+ "abi_return": result.returns[index] if result.returns and is_abi else None, # type: ignore[dict-item]
108
110
  }
109
111
  )
110
112
  # For regular app transactions, just add abi_return
111
113
  elif cls is SendAppTransactionResult:
112
- base_params["abi_return"] = result.returns[index] if result.returns else None # type: ignore[assignment]
114
+ base_params["abi_return"] = result.returns[index] if result.returns and is_abi else None # type: ignore[assignment]
113
115
 
114
116
  return cls(**base_params) # type: ignore[arg-type]
115
117
 
@@ -650,8 +652,7 @@ class AlgorandClientTransactionSender:
650
652
  return self._send(
651
653
  lambda c: c.add_asset_opt_in,
652
654
  pre_log=lambda params, transaction: (
653
- f"Opting in {params.sender} to asset with ID {params.asset_id} via transaction "
654
- f"{transaction.get_txid()}"
655
+ f"Opting in {params.sender} to asset with ID {params.asset_id} via transaction {transaction.get_txid()}"
655
656
  ),
656
657
  )(params, send_params)
657
658
 
@@ -714,7 +715,7 @@ class AlgorandClientTransactionSender:
714
715
  )
715
716
  except Exception as e:
716
717
  raise ValueError(
717
- f"Account {params.sender} is not opted-in to Asset {params.asset_id}; " "can't opt-out."
718
+ f"Account {params.sender} is not opted-in to Asset {params.asset_id}; can't opt-out."
718
719
  ) from e
719
720
 
720
721
  if not hasattr(params, "creator"):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: algokit-utils
3
- Version: 4.0.1b3
3
+ Version: 4.1.0
4
4
  Summary: Utilities for Algorand development for use by AlgoKit
5
5
  License: MIT
6
6
  Author: Algorand Foundation
@@ -3,12 +3,12 @@ algokit_utils/_debugging.py,sha256=nAiC10WXiZsvc0RPWOrMLpjJQZT_ItgcMl7D9Z4DfYc,1
3
3
  algokit_utils/_legacy_v2/__init__.py,sha256=WcRE30axWjGnBB09bJCeTw9NT-2_jDN_CVJITFcIDc8,4689
4
4
  algokit_utils/_legacy_v2/_ensure_funded.py,sha256=k52b56CfWttPiu2gy09HIEiXl0eIz5WKQy-iuhxpSQg,6909
5
5
  algokit_utils/_legacy_v2/_transfer.py,sha256=nMHm3jXKJLLjOLqjMK_B3bFsslDCx45EPJ5yt7Ex-8k,6464
6
- algokit_utils/_legacy_v2/account.py,sha256=mv5GjHlIPKNr3dx1FI1aAYFiJqeVpCbf0mTUXeYu0UU,8314
7
- algokit_utils/_legacy_v2/application_client.py,sha256=Gb7WldXLi0V92YfeU19HP1rJ-L4Rmz2Lxm2q45tQJ2s,59610
6
+ algokit_utils/_legacy_v2/account.py,sha256=9rLtA67cDLJZozCbeMzQS0aZPHXh-z5MxTKEUcbzDtw,8308
7
+ algokit_utils/_legacy_v2/application_client.py,sha256=nSTnT98WRWhxo4xjtQACJW7hmIwhb9EMzyhkUcX50CM,59572
8
8
  algokit_utils/_legacy_v2/application_specification.py,sha256=wp2Y9ou2_F-bSFbDnm6AEhFexybmD7-fAT0CuWtO26g,521
9
9
  algokit_utils/_legacy_v2/asset.py,sha256=vuSmqwEp2W6bpLB34_fUkzZ8VnLDXC__d-rqI4bmkDM,7574
10
10
  algokit_utils/_legacy_v2/common.py,sha256=lB6zHUDJSjYiZ41hvcG0P5TZk_t-n2Iy0OXuQcJosm0,823
11
- algokit_utils/_legacy_v2/deploy.py,sha256=uoRaUTIYzLZdUucW3DOIbD3qwa9CvLgo1GSJ1Ibfmsw,32778
11
+ algokit_utils/_legacy_v2/deploy.py,sha256=RMQ3AG5cNRzcYHzpFM9fQJo2f4dQeFDBKPIvx4K2e6Q,32755
12
12
  algokit_utils/_legacy_v2/logic_error.py,sha256=pmaMTuvbOD7PHukSY4epzJRptSivc4O0vFZdW_zzZ38,345
13
13
  algokit_utils/_legacy_v2/models.py,sha256=hH7aO50E4po4EgxXI9zdX5HTthn1HLfSLvkuPfgAATc,7403
14
14
  algokit_utils/_legacy_v2/network_clients.py,sha256=z_zm1da9CVBG2TOAnXeYkHBh6a8HtXsSdNrlEizc8J0,5928
@@ -21,9 +21,9 @@ algokit_utils/application_client.py,sha256=5UIxXIBjukjRyjZPCeXmaNlAftbb3TziV7EfB
21
21
  algokit_utils/application_specification.py,sha256=wV0H088IudMqlxsW-gsZIfJyKA4e-zVwxJ-cR__ouBA,1379
22
22
  algokit_utils/applications/__init__.py,sha256=NGjhpBeExsQZOAYCT2QUFag1xuKoFiX-Ux5SR2GNzd8,452
23
23
  algokit_utils/applications/abi.py,sha256=OjTdn4szJPPeC8XmosdDYtkIIVgQSWAnqz2DHw5OH9g,10117
24
- algokit_utils/applications/app_client.py,sha256=wM3tH0-oW6uQG_W0RLiL3iULnvAPpNM-O9BiPw5wfiU,88470
25
- algokit_utils/applications/app_deployer.py,sha256=kAypS20VM-yAwosCcYdMwTmXntGOt_6EN_i5nIYN5rw,30627
26
- algokit_utils/applications/app_factory.py,sha256=PPJnR-fs1hNkEMlIqwY7jx63hPnOkdyiIK6l4-A3d10,45383
24
+ algokit_utils/applications/app_client.py,sha256=891BMnUOIOenEWgeelSIMgi0Wj4kggluWiKAu6-GYrE,88208
25
+ algokit_utils/applications/app_deployer.py,sha256=xJCu7SU66OTg5misSbSF0QI8abRB-DWAwAVKd1kNcPI,30685
26
+ algokit_utils/applications/app_factory.py,sha256=jVAzoK1J9S-BTGHA5BLxT-cl0pWhPdf222W4fYpFihE,45352
27
27
  algokit_utils/applications/app_manager.py,sha256=8bboIswlwBQhPIqilSBMaxd83yHjIpkloezmtgcAdZY,22301
28
28
  algokit_utils/applications/app_spec/__init__.py,sha256=HtjAhAqHNFml9WbRKGmhJnwyJeW8AztPRO_BriQ84vs,140
29
29
  algokit_utils/applications/app_spec/arc32.py,sha256=8MMGUopPzkWq48rl5sYbc2Awf-RKnxSX8F0P0UibK5M,7523
@@ -61,10 +61,10 @@ algokit_utils/protocols/account.py,sha256=CowaVY7ErBP84TWBHNvBjkZy18whPb8HIlMZtJ
61
61
  algokit_utils/protocols/typed_clients.py,sha256=UrQrHbN2SvS8pEFJ8JQodvouoWeBrQOQGZGyBQx1KLM,3322
62
62
  algokit_utils/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
63
63
  algokit_utils/transactions/__init__.py,sha256=7fYF3m6DyOGzbV36MT5svo0wSkj9AIz496kWgIWSAlk,225
64
- algokit_utils/transactions/transaction_composer.py,sha256=66_vY6DNs4gDhLRAamtzzo7oeLM8nxS9QKwZY_tidYc,104138
64
+ algokit_utils/transactions/transaction_composer.py,sha256=eK-6l2W1CkbKID1ezi-zCs_TxA2i5ZTm3YTl8sT2Zfw,103995
65
65
  algokit_utils/transactions/transaction_creator.py,sha256=cuP6Xm-fhGoCc2FNSbLiEg3iQRwW38rfdTzsqPyEcpM,29053
66
- algokit_utils/transactions/transaction_sender.py,sha256=foK_2S-gUl9D7xkWG3lD526qIKz5mVibHNKVREQCgoA,50079
67
- algokit_utils-4.0.1b3.dist-info/LICENSE,sha256=J5i7U1Q9Q2c7saUzlvFRmrCCFhQyXb5Juz_LO5omNUw,1076
68
- algokit_utils-4.0.1b3.dist-info/METADATA,sha256=Wuty91rhnlGVlhzljvqS9TzoCJu5vJTVJ2eniZGacQ0,2421
69
- algokit_utils-4.0.1b3.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
70
- algokit_utils-4.0.1b3.dist-info/RECORD,,
66
+ algokit_utils/transactions/transaction_sender.py,sha256=Wi3ws9S-Df1JeTlaSTXmq-WS24Gsq7WGsKk1B0z23ao,50117
67
+ algokit_utils-4.1.0.dist-info/LICENSE,sha256=J5i7U1Q9Q2c7saUzlvFRmrCCFhQyXb5Juz_LO5omNUw,1076
68
+ algokit_utils-4.1.0.dist-info/METADATA,sha256=9Et8L9-7fqp3dk3J_N8X6_6AWHm4EZf7qy7IIyuYEA4,2419
69
+ algokit_utils-4.1.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
70
+ algokit_utils-4.1.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.1.2
2
+ Generator: poetry-core 2.1.3
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any