algokit-utils 4.2.1b1__py3-none-any.whl → 4.2.2__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.

@@ -20,7 +20,7 @@ __all__ = [
20
20
 
21
21
 
22
22
  LOGIC_ERROR = (
23
- ".*transaction (?P<transaction_id>[A-Z0-9]+): logic eval error: (?P<message>.*). Details: .*pc=(?P<pc>[0-9]+).*"
23
+ ".*transaction (?P<transaction_id>[A-Z0-9]+): (logic eval error: )?(?P<message>.*). Details: .*pc=(?P<pc>[0-9]+).*"
24
24
  )
25
25
 
26
26
 
@@ -21,7 +21,7 @@ from algosdk.atomic_transaction_composer import (
21
21
  from algosdk.transaction import OnComplete, SuggestedParams
22
22
  from algosdk.v2client.algod import AlgodClient
23
23
  from algosdk.v2client.models.simulate_request import SimulateRequest
24
- from typing_extensions import deprecated
24
+ from typing_extensions import Never, deprecated
25
25
 
26
26
  from algokit_utils.applications.abi import ABIReturn, ABIValue
27
27
  from algokit_utils.applications.app_manager import AppManager
@@ -667,7 +667,7 @@ def _encode_lease(lease: str | bytes | None) -> bytes | None:
667
667
  raise TypeError(f"Unknown lease type received of {type(lease)}")
668
668
 
669
669
 
670
- def _get_group_execution_info( # noqa: C901, PLR0912
670
+ def _get_group_execution_info(
671
671
  atc: AtomicTransactionComposer,
672
672
  algod: AlgodClient,
673
673
  populate_app_call_resources: bool | None = None,
@@ -682,6 +682,7 @@ def _get_group_execution_info( # noqa: C901, PLR0912
682
682
  txn_groups=[],
683
683
  allow_unnamed_resources=True,
684
684
  allow_empty_signatures=True,
685
+ exec_trace_config=algosdk.v2client.models.SimulateTraceConfig(enable=True),
685
686
  )
686
687
 
687
688
  # Clone ATC with null signers
@@ -710,26 +711,14 @@ def _get_group_execution_info( # noqa: C901, PLR0912
710
711
  f"Required for transactions: {', '.join(str(i) for i in app_call_indexes_without_max_fees)}"
711
712
  )
712
713
 
713
- # Get fee parameters
714
- per_byte_txn_fee = suggested_params.fee if suggested_params else 0
715
- min_txn_fee = int(suggested_params.min_fee) if suggested_params else 1000 # type: ignore[unused-ignore]
716
-
717
714
  # Simulate transactions
718
715
  result = empty_signer_atc.simulate(algod, simulate_request)
719
716
 
720
717
  group_response = result.simulate_response["txn-groups"][0]
721
718
 
722
719
  if group_response.get("failure-message"):
723
- msg = group_response["failure-message"]
724
- if cover_app_call_inner_transaction_fees and "fee too small" in msg:
725
- raise ValueError(
726
- "Fees were too small to resolve execution info via simulate. "
727
- "You may need to increase an app call transaction maxFee."
728
- )
729
- failed_at = group_response.get("failed-at", [0])[0]
730
- raise ValueError(
731
- f"Error resolving execution info via simulate in transaction {failed_at}: "
732
- f"{group_response['failure-message']}"
720
+ _handle_simulation_error(
721
+ group_response, cover_app_call_inner_transaction_fees=cover_app_call_inner_transaction_fees
733
722
  )
734
723
 
735
724
  # Build execution info
@@ -743,27 +732,12 @@ def _get_group_execution_info( # noqa: C901, PLR0912
743
732
 
744
733
  required_fee_delta = 0
745
734
  if cover_app_call_inner_transaction_fees:
746
- # Calculate parent transaction fee
747
- parent_per_byte_fee = per_byte_txn_fee * (original_txn.estimate_size() + 75)
748
- parent_min_fee = max(parent_per_byte_fee, min_txn_fee)
749
- parent_fee_delta = parent_min_fee - original_txn.fee
750
-
751
- if isinstance(original_txn, algosdk.transaction.ApplicationCallTxn):
752
- # Calculate inner transaction fees recursively
753
- def calculate_inner_fee_delta(inner_txns: list[dict], acc: int = 0) -> int:
754
- for inner_txn in reversed(inner_txns):
755
- current_fee_delta = (
756
- calculate_inner_fee_delta(inner_txn["inner-txns"], acc)
757
- if inner_txn.get("inner-txns")
758
- else acc
759
- ) + (min_txn_fee - inner_txn["txn"]["txn"].get("fee", 0))
760
- acc = max(0, current_fee_delta)
761
- return acc
762
-
763
- inner_fee_delta = calculate_inner_fee_delta(txn_result.get("inner-txns", []))
764
- required_fee_delta = inner_fee_delta + parent_fee_delta
765
- else:
766
- required_fee_delta = parent_fee_delta
735
+ required_fee_delta = _calculate_required_fee_delta(
736
+ original_txn,
737
+ txn_result,
738
+ per_byte_txn_fee=suggested_params.fee if suggested_params else 0,
739
+ min_txn_fee=int(suggested_params.min_fee) if suggested_params else 1000,
740
+ )
767
741
 
768
742
  txn_results.append(
769
743
  ExecutionInfoTxn(
@@ -782,6 +756,64 @@ def _get_group_execution_info( # noqa: C901, PLR0912
782
756
  )
783
757
 
784
758
 
759
+ def _handle_simulation_error(
760
+ group_response: dict[str, Any], *, cover_app_call_inner_transaction_fees: bool | None
761
+ ) -> Never:
762
+ msg = group_response["failure-message"]
763
+ if cover_app_call_inner_transaction_fees and "fee too small" in msg:
764
+ raise ValueError(
765
+ "Fees were too small to resolve execution info via simulate. "
766
+ "You may need to increase an app call transaction maxFee."
767
+ )
768
+ failed_at = group_response.get("failed-at", [0])[0]
769
+ details = ""
770
+ if "logic eval error" not in msg:
771
+ # extract last pc from trace so we can format an error that can be parsed into a LogicError
772
+ try:
773
+ trace = group_response["txn-results"][failed_at]["exec-trace"]
774
+ except (KeyError, IndexError):
775
+ pass
776
+ else:
777
+ try:
778
+ program_trace = trace["approval-program-trace"]
779
+ except KeyError:
780
+ program_trace = trace["clear-program-trace"]
781
+ pc = program_trace[-1]["pc"]
782
+ details = f". Details: pc={pc}"
783
+ raise ValueError(
784
+ f"Error resolving execution info via simulate in transaction {failed_at}: "
785
+ f"{group_response['failure-message']}{details}"
786
+ )
787
+
788
+
789
+ def _calculate_required_fee_delta(
790
+ txn: transaction.Transaction, txn_result: dict[str, Any], *, per_byte_txn_fee: int, min_txn_fee: int
791
+ ) -> int:
792
+ # Calculate parent transaction fee
793
+ original_txn_size = txn.estimate_size()
794
+ assert isinstance(original_txn_size, int), "expected txn size to be an int"
795
+ parent_per_byte_fee = per_byte_txn_fee * (original_txn_size + 75)
796
+ parent_min_fee = max(parent_per_byte_fee, min_txn_fee)
797
+ original_txn_fee = txn.fee
798
+ assert isinstance(original_txn_fee, int), "expected original txn fee to be an int"
799
+ parent_fee_delta = parent_min_fee - original_txn_fee
800
+
801
+ if isinstance(txn, algosdk.transaction.ApplicationCallTxn):
802
+ # Calculate inner transaction fees recursively
803
+ def calculate_inner_fee_delta(inner_txns: list[dict], acc: int = 0) -> int:
804
+ for inner_txn in reversed(inner_txns):
805
+ current_fee_delta = (
806
+ calculate_inner_fee_delta(inner_txn["inner-txns"], acc) if inner_txn.get("inner-txns") else acc
807
+ ) + (min_txn_fee - inner_txn["txn"]["txn"].get("fee", 0))
808
+ acc = max(0, current_fee_delta)
809
+ return acc
810
+
811
+ inner_fee_delta = calculate_inner_fee_delta(txn_result.get("inner-txns", []))
812
+ return inner_fee_delta + parent_fee_delta
813
+ else:
814
+ return parent_fee_delta
815
+
816
+
785
817
  def _find_available_transaction_index(
786
818
  txns: list[TransactionWithSigner], reference_type: str, reference: str | dict[str, Any] | int
787
819
  ) -> int:
@@ -1041,13 +1073,15 @@ def prepare_group_for_sending( # noqa: C901, PLR0912, PLR0915
1041
1073
  foreign_apps.append(app_id)
1042
1074
  app_txn.foreign_apps = foreign_apps
1043
1075
  elif ref_type == "box":
1076
+ # ensure app_id is added before calling translate_box_reference
1077
+ app_id = box_ref[0]
1078
+ if app_id != 0:
1079
+ foreign_apps = list(getattr(app_txn, "foreign_apps", []) or [])
1080
+ foreign_apps.append(app_id)
1081
+ app_txn.foreign_apps = foreign_apps
1044
1082
  boxes = list(getattr(app_txn, "boxes", []) or [])
1045
1083
  boxes.append(BoxReference.translate_box_reference(box_ref, app_txn.foreign_apps or [], app_txn.index)) # type: ignore[arg-type]
1046
1084
  app_txn.boxes = boxes
1047
- if box_ref[0] != 0:
1048
- foreign_apps = list(getattr(app_txn, "foreign_apps", []) or [])
1049
- foreign_apps.append(box_ref[0])
1050
- app_txn.foreign_apps = foreign_apps
1051
1085
  elif ref_type == "asset":
1052
1086
  asset_id = int(cast(str | int, reference))
1053
1087
  foreign_assets = list(getattr(app_txn, "foreign_assets", []) or [])
@@ -1812,7 +1846,7 @@ class TransactionComposer:
1812
1846
  txn_with_signers: list[TransactionWithSignerAndContext] = []
1813
1847
 
1814
1848
  for txn in self._txns:
1815
- txn_with_signers.extend(self._build_txn(txn, suggested_params))
1849
+ txn_with_signers.extend(self._build_txn(txn, suggested_params, include_signer=True))
1816
1850
 
1817
1851
  for ts in txn_with_signers:
1818
1852
  self._atc.add_transaction(ts)
@@ -1852,9 +1886,9 @@ class TransactionComposer:
1852
1886
  txn_with_signers: list[TransactionWithSigner] = []
1853
1887
 
1854
1888
  if isinstance(txn, MethodCallParams):
1855
- txn_with_signers.extend(self._build_method_call(txn, suggested_params))
1889
+ txn_with_signers.extend(self._build_method_call(txn, suggested_params, include_signer=False))
1856
1890
  else:
1857
- txn_with_signers.extend(self._build_txn(txn, suggested_params))
1891
+ txn_with_signers.extend(self._build_txn(txn, suggested_params, include_signer=False))
1858
1892
 
1859
1893
  for ts in txn_with_signers:
1860
1894
  transactions.append(ts.txn)
@@ -2129,7 +2163,11 @@ class TransactionComposer:
2129
2163
  )
2130
2164
 
2131
2165
  def _build_method_call( # noqa: C901, PLR0912, PLR0915
2132
- self, params: MethodCallParams, suggested_params: algosdk.transaction.SuggestedParams
2166
+ self,
2167
+ params: MethodCallParams,
2168
+ suggested_params: algosdk.transaction.SuggestedParams,
2169
+ *,
2170
+ include_signer: bool,
2133
2171
  ) -> list[TransactionWithSignerAndContext]:
2134
2172
  method_args: list[ABIValue | TransactionWithSigner] = []
2135
2173
  txns_for_group: list[TransactionWithSignerAndContext] = []
@@ -2159,7 +2197,9 @@ class TransactionComposer:
2159
2197
  method_args.append(
2160
2198
  TransactionWithSignerAndContext(
2161
2199
  txn=arg,
2162
- signer=signer if signer is not None else self._get_signer(params.sender),
2200
+ signer=signer
2201
+ if signer is not None
2202
+ else (NULL_SIGNER if not include_signer else self._get_signer(params.sender)),
2163
2203
  context=TransactionContext(abi_method=None),
2164
2204
  )
2165
2205
  )
@@ -2171,7 +2211,9 @@ class TransactionComposer:
2171
2211
  | AppUpdateMethodCallParams()
2172
2212
  | AppDeleteMethodCallParams()
2173
2213
  ):
2174
- temp_txn_with_signers = self._build_method_call(arg, suggested_params)
2214
+ temp_txn_with_signers = self._build_method_call(
2215
+ arg, suggested_params, include_signer=include_signer
2216
+ )
2175
2217
  # Add all transactions except the last one in reverse order
2176
2218
  txns_for_group.extend(temp_txn_with_signers[:-1])
2177
2219
  # Add the last transaction to method_args
@@ -2208,7 +2250,7 @@ class TransactionComposer:
2208
2250
  method_args.append(
2209
2251
  TransactionWithSignerAndContext(
2210
2252
  txn=txn.txn,
2211
- signer=signer or self._get_signer(params.sender),
2253
+ signer=signer or (NULL_SIGNER if not include_signer else self._get_signer(params.sender)),
2212
2254
  context=TransactionContext(abi_method=params.method),
2213
2255
  )
2214
2256
  )
@@ -2255,7 +2297,8 @@ class TransactionComposer:
2255
2297
  "sp": suggested_params,
2256
2298
  "signer": params.signer
2257
2299
  if params.signer is not None
2258
- else self._get_signer(params.sender) or algosdk.atomic_transaction_composer.EmptySigner(),
2300
+ else (NULL_SIGNER if not include_signer else self._get_signer(params.sender))
2301
+ or algosdk.atomic_transaction_composer.EmptySigner(),
2259
2302
  "method_args": list(reversed(method_args)),
2260
2303
  "on_complete": params.on_complete or algosdk.transaction.OnComplete.NoOpOC,
2261
2304
  "boxes": [AppManager.get_box_reference(ref) for ref in params.box_references]
@@ -2496,6 +2539,8 @@ class TransactionComposer:
2496
2539
  self,
2497
2540
  txn: TransactionWithSigner | TxnParams | AtomicTransactionComposer,
2498
2541
  suggested_params: algosdk.transaction.SuggestedParams,
2542
+ *,
2543
+ include_signer: bool,
2499
2544
  ) -> list[TransactionWithSignerAndContext]:
2500
2545
  match txn:
2501
2546
  case TransactionWithSigner():
@@ -2505,7 +2550,7 @@ class TransactionComposer:
2505
2550
  case AtomicTransactionComposer():
2506
2551
  return self._build_atc(txn)
2507
2552
  case algosdk.transaction.Transaction():
2508
- signer = self._get_signer(txn.sender)
2553
+ signer = NULL_SIGNER if not include_signer else self._get_signer(txn.sender)
2509
2554
  return [TransactionWithSignerAndContext(txn=txn, signer=signer, context=TransactionContext.empty())]
2510
2555
  case (
2511
2556
  AppCreateMethodCallParams()
@@ -2513,10 +2558,10 @@ class TransactionComposer:
2513
2558
  | AppUpdateMethodCallParams()
2514
2559
  | AppDeleteMethodCallParams()
2515
2560
  ):
2516
- return self._build_method_call(txn, suggested_params)
2561
+ return self._build_method_call(txn, suggested_params, include_signer=include_signer)
2517
2562
 
2518
2563
  signer = txn.signer.signer if isinstance(txn.signer, TransactionSignerAccountProtocol) else txn.signer # type: ignore[assignment]
2519
- signer = signer or self._get_signer(txn.sender)
2564
+ signer = signer or (NULL_SIGNER if not include_signer else self._get_signer(txn.sender))
2520
2565
 
2521
2566
  match txn:
2522
2567
  case PaymentParams():
@@ -1,8 +1,9 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: algokit-utils
3
- Version: 4.2.1b1
3
+ Version: 4.2.2
4
4
  Summary: Utilities for Algorand development for use by AlgoKit
5
5
  License: MIT
6
+ License-File: LICENSE
6
7
  Author: Algorand Foundation
7
8
  Author-email: contact@algorand.foundation
8
9
  Requires-Python: >=3.10,<4.0
@@ -12,8 +13,9 @@ Classifier: Programming Language :: Python :: 3.10
12
13
  Classifier: Programming Language :: Python :: 3.11
13
14
  Classifier: Programming Language :: Python :: 3.12
14
15
  Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
15
17
  Requires-Dist: httpx (>=0.23.1,<=0.28.1)
16
- Requires-Dist: py-algorand-sdk (>=2.4.0,<3.0.0)
18
+ Requires-Dist: py-algorand-sdk (>=2.11.0,<3.0.0)
17
19
  Requires-Dist: typing-extensions (>=4.6.0)
18
20
  Description-Content-Type: text/markdown
19
21
 
@@ -45,7 +45,7 @@ algokit_utils/config.py,sha256=CvDH5B8uPWnm6wCHHlMsl-0lONzq26vPLvwmnbw7c-k,6048
45
45
  algokit_utils/deploy.py,sha256=UUtSDI6JcBUuto62FuirhUlDcjZwQyLkiERgDMx8P7A,330
46
46
  algokit_utils/dispenser_api.py,sha256=-EO4Dq3q_v4kSMey43kXJfoX8uCBPJpjEMTlLI7xn_I,324
47
47
  algokit_utils/errors/__init__.py,sha256=CmuiLVjzMAOYxPaIIwmYCNArsso_RtS2ssFoNdp5CMs,61
48
- algokit_utils/errors/logic_error.py,sha256=uxqUOU9-D1R5TrKturCbmmWRVlB024Ca4CfVi8x_sgo,4104
48
+ algokit_utils/errors/logic_error.py,sha256=ksRsYjo7a3_8scf9UhByoQ4x3BJ7WpVYRQoiEScV3pM,4107
49
49
  algokit_utils/logic_error.py,sha256=3duw-l6tBr-DeapO0e0tYHoa9rOxP-QZZ6QWmN8L9tc,305
50
50
  algokit_utils/models/__init__.py,sha256=0aB_c5pnkqKl1Z0hkxM9qbKn2qVdizZE2DvziN9ObqM,465
51
51
  algokit_utils/models/account.py,sha256=eqGJvExzd7gDm3--DBDaIq6pJarxMPHZ-UySxZ9Qznk,6778
@@ -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=iOIa_9hknbpCQc2hRyX-aBoHG-NGRxGnEAJstwGVKgw,106645
64
+ algokit_utils/transactions/transaction_composer.py,sha256=aDYo9SGjMr-cKUEwZUJx2Cyw85rZz77n_LVe2fGINH8,108435
65
65
  algokit_utils/transactions/transaction_creator.py,sha256=cuP6Xm-fhGoCc2FNSbLiEg3iQRwW38rfdTzsqPyEcpM,29053
66
66
  algokit_utils/transactions/transaction_sender.py,sha256=Wi3ws9S-Df1JeTlaSTXmq-WS24Gsq7WGsKk1B0z23ao,50117
67
- algokit_utils-4.2.1b1.dist-info/LICENSE,sha256=J5i7U1Q9Q2c7saUzlvFRmrCCFhQyXb5Juz_LO5omNUw,1076
68
- algokit_utils-4.2.1b1.dist-info/METADATA,sha256=pP2yk78rbXNtBjxqXtSi85z9EpJsDQZ7C9LYH9Efp3E,2421
69
- algokit_utils-4.2.1b1.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
70
- algokit_utils-4.2.1b1.dist-info/RECORD,,
67
+ algokit_utils-4.2.2.dist-info/METADATA,sha256=CK6pN2Zc0QUkk3FAvEhptDopTjCL-YzWmXnkCvhpcd4,2493
68
+ algokit_utils-4.2.2.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
69
+ algokit_utils-4.2.2.dist-info/licenses/LICENSE,sha256=J5i7U1Q9Q2c7saUzlvFRmrCCFhQyXb5Juz_LO5omNUw,1076
70
+ algokit_utils-4.2.2.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.1.3
2
+ Generator: poetry-core 2.2.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any