algokit-utils 5.0.0a3__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.
Files changed (337) hide show
  1. algokit_abi/__init__.py +9 -0
  2. algokit_abi/_arc32_to_arc56.py +242 -0
  3. algokit_abi/_arc56_serde.py +161 -0
  4. algokit_abi/abi.py +667 -0
  5. algokit_abi/arc32.py +210 -0
  6. algokit_abi/arc56.py +821 -0
  7. algokit_abi/py.typed +0 -0
  8. algokit_algo25/__init__.py +38 -0
  9. algokit_algo25/_encoding.py +46 -0
  10. algokit_algo25/_wordlist.py +2065 -0
  11. algokit_algo25/exceptions.py +29 -0
  12. algokit_algo25/mnemonic.py +128 -0
  13. algokit_algo25/py.typed +0 -0
  14. algokit_algod_client/__init__.py +10 -0
  15. algokit_algod_client/client.py +1585 -0
  16. algokit_algod_client/config.py +36 -0
  17. algokit_algod_client/exceptions.py +59 -0
  18. algokit_algod_client/models/__init__.py +229 -0
  19. algokit_algod_client/models/_account.py +150 -0
  20. algokit_algod_client/models/_account_application_response.py +25 -0
  21. algokit_algod_client/models/_account_asset_response.py +25 -0
  22. algokit_algod_client/models/_account_participation.py +53 -0
  23. algokit_algod_client/models/_account_state_delta.py +30 -0
  24. algokit_algod_client/models/_allocations_for_genesis_file.py +23 -0
  25. algokit_algod_client/models/_allocations_for_genesis_file_state_model.py +42 -0
  26. algokit_algod_client/models/_application.py +23 -0
  27. algokit_algod_client/models/_application_initial_states.py +37 -0
  28. algokit_algod_client/models/_application_kvstorage.py +29 -0
  29. algokit_algod_client/models/_application_local_state.py +33 -0
  30. algokit_algod_client/models/_application_params.py +63 -0
  31. algokit_algod_client/models/_application_state_operation.py +41 -0
  32. algokit_algod_client/models/_application_state_schema.py +22 -0
  33. algokit_algod_client/models/_asset.py +23 -0
  34. algokit_algod_client/models/_asset_holding.py +29 -0
  35. algokit_algod_client/models/_asset_params.py +102 -0
  36. algokit_algod_client/models/_avm_key_value.py +28 -0
  37. algokit_algod_client/models/_avm_value.py +32 -0
  38. algokit_algod_client/models/_block.py +363 -0
  39. algokit_algod_client/models/_block_hash_response.py +14 -0
  40. algokit_algod_client/models/_block_txids_response.py +14 -0
  41. algokit_algod_client/models/_box.py +36 -0
  42. algokit_algod_client/models/_box_descriptor.py +24 -0
  43. algokit_algod_client/models/_boxes_response.py +21 -0
  44. algokit_algod_client/models/_build_version_contains_the_current_algod_build_version_information.py +34 -0
  45. algokit_algod_client/models/_compile_response.py +24 -0
  46. algokit_algod_client/models/_disassemble_response.py +14 -0
  47. algokit_algod_client/models/_error_response.py +22 -0
  48. algokit_algod_client/models/_eval_delta.py +32 -0
  49. algokit_algod_client/models/_eval_delta_key_value.py +28 -0
  50. algokit_algod_client/models/_genesis_file_in_json.py +53 -0
  51. algokit_algod_client/models/_get_block_time_stamp_offset_response.py +14 -0
  52. algokit_algod_client/models/_get_sync_round_response.py +14 -0
  53. algokit_algod_client/models/_ledger_state_delta.py +389 -0
  54. algokit_algod_client/models/_light_block_header_proof.py +32 -0
  55. algokit_algod_client/models/_node_status_response.py +118 -0
  56. algokit_algod_client/models/_pending_transaction_response.py +91 -0
  57. algokit_algod_client/models/_pending_transactions_response.py +29 -0
  58. algokit_algod_client/models/_post_transactions_response.py +14 -0
  59. algokit_algod_client/models/_scratch_change.py +23 -0
  60. algokit_algod_client/models/_serde_helpers.py +241 -0
  61. algokit_algod_client/models/_simulate_initial_states.py +25 -0
  62. algokit_algod_client/models/_simulate_request.py +54 -0
  63. algokit_algod_client/models/_simulate_request_transaction_group.py +25 -0
  64. algokit_algod_client/models/_simulate_response.py +44 -0
  65. algokit_algod_client/models/_simulate_trace_config.py +30 -0
  66. algokit_algod_client/models/_simulate_transaction_group_result.py +46 -0
  67. algokit_algod_client/models/_simulate_transaction_result.py +41 -0
  68. algokit_algod_client/models/_simulate_unnamed_resources_accessed.py +64 -0
  69. algokit_algod_client/models/_simulation_eval_overrides.py +40 -0
  70. algokit_algod_client/models/_simulation_opcode_trace_unit.py +55 -0
  71. algokit_algod_client/models/_simulation_transaction_exec_trace.py +82 -0
  72. algokit_algod_client/models/_source_map.py +30 -0
  73. algokit_algod_client/models/_state_delta.py +6 -0
  74. algokit_algod_client/models/_state_proof.py +28 -0
  75. algokit_algod_client/models/_state_proof_message.py +44 -0
  76. algokit_algod_client/models/_supply_response.py +26 -0
  77. algokit_algod_client/models/_teal_key_value.py +28 -0
  78. algokit_algod_client/models/_teal_key_value_store.py +6 -0
  79. algokit_algod_client/models/_teal_value.py +32 -0
  80. algokit_algod_client/models/_transaction_group_ledger_state_deltas_for_round_response.py +21 -0
  81. algokit_algod_client/models/_transaction_parameters_response.py +45 -0
  82. algokit_algod_client/models/_transaction_proof.py +44 -0
  83. algokit_algod_client/models/_version_contains_the_current_algod_version.py +38 -0
  84. algokit_algod_client/models/suggested_params.py +42 -0
  85. algokit_algod_client/py.typed +1 -0
  86. algokit_algod_client/types.py +7 -0
  87. algokit_algosdk/__init__.py +38 -0
  88. algokit_algosdk/account.py +32 -0
  89. algokit_algosdk/app_access.py +228 -0
  90. algokit_algosdk/box_reference.py +100 -0
  91. algokit_algosdk/constants.py +147 -0
  92. algokit_algosdk/encoding.py +89 -0
  93. algokit_algosdk/error.py +180 -0
  94. algokit_algosdk/logic.py +61 -0
  95. algokit_algosdk/logicsig.py +218 -0
  96. algokit_algosdk/mnemonic.py +216 -0
  97. algokit_algosdk/multisig.py +161 -0
  98. algokit_algosdk/py.typed +0 -0
  99. algokit_algosdk/transaction.py +596 -0
  100. algokit_algosdk/wordlist.py +2054 -0
  101. algokit_common/__init__.py +50 -0
  102. algokit_common/address.py +34 -0
  103. algokit_common/constants.py +47 -0
  104. algokit_common/hashing.py +25 -0
  105. algokit_common/py.typed +0 -0
  106. algokit_common/serde/__init__.py +40 -0
  107. algokit_common/serde/_core.py +610 -0
  108. algokit_common/serde/_primitives.py +135 -0
  109. algokit_common/source_map.py +158 -0
  110. algokit_indexer_client/__init__.py +10 -0
  111. algokit_indexer_client/client.py +1456 -0
  112. algokit_indexer_client/config.py +36 -0
  113. algokit_indexer_client/exceptions.py +59 -0
  114. algokit_indexer_client/models/__init__.py +148 -0
  115. algokit_indexer_client/models/_account.py +161 -0
  116. algokit_indexer_client/models/_account_participation.py +53 -0
  117. algokit_indexer_client/models/_account_response.py +19 -0
  118. algokit_indexer_client/models/_account_state_delta.py +29 -0
  119. algokit_indexer_client/models/_accounts_response.py +29 -0
  120. algokit_indexer_client/models/_application.py +35 -0
  121. algokit_indexer_client/models/_application_local_state.py +45 -0
  122. algokit_indexer_client/models/_application_local_states_response.py +29 -0
  123. algokit_indexer_client/models/_application_log_data.py +28 -0
  124. algokit_indexer_client/models/_application_logs_response.py +33 -0
  125. algokit_indexer_client/models/_application_params.py +62 -0
  126. algokit_indexer_client/models/_application_response.py +20 -0
  127. algokit_indexer_client/models/_application_state_schema.py +22 -0
  128. algokit_indexer_client/models/_applications_response.py +29 -0
  129. algokit_indexer_client/models/_asset.py +35 -0
  130. algokit_indexer_client/models/_asset_balances_response.py +29 -0
  131. algokit_indexer_client/models/_asset_holding.py +41 -0
  132. algokit_indexer_client/models/_asset_holdings_response.py +29 -0
  133. algokit_indexer_client/models/_asset_params.py +102 -0
  134. algokit_indexer_client/models/_asset_response.py +19 -0
  135. algokit_indexer_client/models/_assets_response.py +29 -0
  136. algokit_indexer_client/models/_block.py +150 -0
  137. algokit_indexer_client/models/_block_headers_response.py +29 -0
  138. algokit_indexer_client/models/_block_rewards.py +38 -0
  139. algokit_indexer_client/models/_block_upgrade_state.py +34 -0
  140. algokit_indexer_client/models/_block_upgrade_vote.py +26 -0
  141. algokit_indexer_client/models/_box.py +36 -0
  142. algokit_indexer_client/models/_box_descriptor.py +24 -0
  143. algokit_indexer_client/models/_box_reference.py +28 -0
  144. algokit_indexer_client/models/_boxes_response.py +29 -0
  145. algokit_indexer_client/models/_error_response.py +18 -0
  146. algokit_indexer_client/models/_eval_delta.py +32 -0
  147. algokit_indexer_client/models/_eval_delta_key_value.py +28 -0
  148. algokit_indexer_client/models/_hash_factory.py +14 -0
  149. algokit_indexer_client/models/_hb_proof_fields.py +57 -0
  150. algokit_indexer_client/models/_health_check.py +42 -0
  151. algokit_indexer_client/models/_holding_ref.py +23 -0
  152. algokit_indexer_client/models/_indexer_state_proof_message.py +40 -0
  153. algokit_indexer_client/models/_locals_ref.py +23 -0
  154. algokit_indexer_client/models/_merkle_array_proof.py +29 -0
  155. algokit_indexer_client/models/_mini_asset_holding.py +38 -0
  156. algokit_indexer_client/models/_on_completion.py +25 -0
  157. algokit_indexer_client/models/_participation_updates.py +22 -0
  158. algokit_indexer_client/models/_resource_ref.py +42 -0
  159. algokit_indexer_client/models/_serde_helpers.py +241 -0
  160. algokit_indexer_client/models/_state_delta.py +6 -0
  161. algokit_indexer_client/models/_state_proof_fields.py +57 -0
  162. algokit_indexer_client/models/_state_proof_participant.py +20 -0
  163. algokit_indexer_client/models/_state_proof_reveal.py +25 -0
  164. algokit_indexer_client/models/_state_proof_sig_slot.py +20 -0
  165. algokit_indexer_client/models/_state_proof_signature.py +37 -0
  166. algokit_indexer_client/models/_state_proof_tracking.py +32 -0
  167. algokit_indexer_client/models/_state_proof_verifier.py +24 -0
  168. algokit_indexer_client/models/_state_schema.py +25 -0
  169. algokit_indexer_client/models/_teal_key_value.py +28 -0
  170. algokit_indexer_client/models/_teal_key_value_store.py +6 -0
  171. algokit_indexer_client/models/_teal_value.py +32 -0
  172. algokit_indexer_client/models/_transaction.py +213 -0
  173. algokit_indexer_client/models/_transaction_application.py +105 -0
  174. algokit_indexer_client/models/_transaction_asset_config.py +31 -0
  175. algokit_indexer_client/models/_transaction_asset_freeze.py +29 -0
  176. algokit_indexer_client/models/_transaction_asset_transfer.py +41 -0
  177. algokit_indexer_client/models/_transaction_heartbeat.py +52 -0
  178. algokit_indexer_client/models/_transaction_keyreg.py +59 -0
  179. algokit_indexer_client/models/_transaction_payment.py +33 -0
  180. algokit_indexer_client/models/_transaction_response.py +19 -0
  181. algokit_indexer_client/models/_transaction_signature.py +35 -0
  182. algokit_indexer_client/models/_transaction_signature_logicsig.py +59 -0
  183. algokit_indexer_client/models/_transaction_signature_multisig.py +36 -0
  184. algokit_indexer_client/models/_transaction_signature_multisig_subsignature.py +28 -0
  185. algokit_indexer_client/models/_transaction_state_proof.py +32 -0
  186. algokit_indexer_client/models/_transactions_response.py +29 -0
  187. algokit_indexer_client/py.typed +1 -0
  188. algokit_indexer_client/types.py +7 -0
  189. algokit_kmd_client/__init__.py +10 -0
  190. algokit_kmd_client/client.py +1240 -0
  191. algokit_kmd_client/config.py +36 -0
  192. algokit_kmd_client/exceptions.py +59 -0
  193. algokit_kmd_client/models/__init__.py +112 -0
  194. algokit_kmd_client/models/_classical_signatures.py +4 -0
  195. algokit_kmd_client/models/_create_wallet_request.py +30 -0
  196. algokit_kmd_client/models/_create_wallet_response.py +19 -0
  197. algokit_kmd_client/models/_delete_key_request.py +27 -0
  198. algokit_kmd_client/models/_delete_multisig_request.py +27 -0
  199. algokit_kmd_client/models/_digest_represents_a32_byte_value_holding_the256_bit_hash_digest.py +4 -0
  200. algokit_kmd_client/models/_ed25519_public_key.py +4 -0
  201. algokit_kmd_client/models/_export_key_request.py +27 -0
  202. algokit_kmd_client/models/_export_key_response.py +24 -0
  203. algokit_kmd_client/models/_export_master_key_request.py +22 -0
  204. algokit_kmd_client/models/_export_master_key_response.py +18 -0
  205. algokit_kmd_client/models/_export_multisig_request.py +23 -0
  206. algokit_kmd_client/models/_export_multisig_response.py +26 -0
  207. algokit_kmd_client/models/_generate_key_request.py +18 -0
  208. algokit_kmd_client/models/_generate_key_response.py +19 -0
  209. algokit_kmd_client/models/_import_key_request.py +28 -0
  210. algokit_kmd_client/models/_import_key_response.py +19 -0
  211. algokit_kmd_client/models/_import_multisig_request.py +30 -0
  212. algokit_kmd_client/models/_import_multisig_response.py +19 -0
  213. algokit_kmd_client/models/_init_wallet_handle_token_request.py +22 -0
  214. algokit_kmd_client/models/_init_wallet_handle_token_response.py +18 -0
  215. algokit_kmd_client/models/_list_keys_request.py +18 -0
  216. algokit_kmd_client/models/_list_keys_response.py +18 -0
  217. algokit_kmd_client/models/_list_multisig_request.py +18 -0
  218. algokit_kmd_client/models/_list_multisig_response.py +18 -0
  219. algokit_kmd_client/models/_list_wallets_request.py +11 -0
  220. algokit_kmd_client/models/_list_wallets_response.py +25 -0
  221. algokit_kmd_client/models/_master_derivation_key.py +4 -0
  222. algokit_kmd_client/models/_multisig_sig.py +33 -0
  223. algokit_kmd_client/models/_multisig_subsig.py +23 -0
  224. algokit_kmd_client/models/_public_key.py +4 -0
  225. algokit_kmd_client/models/_release_wallet_handle_token_request.py +18 -0
  226. algokit_kmd_client/models/_rename_wallet_request.py +26 -0
  227. algokit_kmd_client/models/_rename_wallet_response.py +19 -0
  228. algokit_kmd_client/models/_renew_wallet_handle_token_request.py +18 -0
  229. algokit_kmd_client/models/_renew_wallet_handle_token_response.py +19 -0
  230. algokit_kmd_client/models/_serde_helpers.py +241 -0
  231. algokit_kmd_client/models/_sign_multisig_response.py +24 -0
  232. algokit_kmd_client/models/_sign_multisig_txn_request.py +45 -0
  233. algokit_kmd_client/models/_sign_program_multisig_request.py +50 -0
  234. algokit_kmd_client/models/_sign_program_multisig_response.py +24 -0
  235. algokit_kmd_client/models/_sign_program_request.py +37 -0
  236. algokit_kmd_client/models/_sign_program_response.py +24 -0
  237. algokit_kmd_client/models/_sign_transaction_response.py +24 -0
  238. algokit_kmd_client/models/_sign_txn_request.py +36 -0
  239. algokit_kmd_client/models/_signature.py +4 -0
  240. algokit_kmd_client/models/_tx_type.py +4 -0
  241. algokit_kmd_client/models/_versions_request.py +11 -0
  242. algokit_kmd_client/models/_versions_response.py +19 -0
  243. algokit_kmd_client/models/_wallet.py +38 -0
  244. algokit_kmd_client/models/_wallet_handle.py +24 -0
  245. algokit_kmd_client/models/_wallet_info_request.py +18 -0
  246. algokit_kmd_client/models/_wallet_info_response.py +19 -0
  247. algokit_kmd_client/py.typed +1 -0
  248. algokit_kmd_client/types.py +7 -0
  249. algokit_transact/__init__.py +190 -0
  250. algokit_transact/codec/__init__.py +0 -0
  251. algokit_transact/codec/msgpack.py +11 -0
  252. algokit_transact/codec/serde.py +7 -0
  253. algokit_transact/codec/signed.py +57 -0
  254. algokit_transact/codec/transaction.py +65 -0
  255. algokit_transact/exceptions.py +17 -0
  256. algokit_transact/logicsig.py +220 -0
  257. algokit_transact/models/__init__.py +0 -0
  258. algokit_transact/models/app_call.py +447 -0
  259. algokit_transact/models/asset_config.py +19 -0
  260. algokit_transact/models/asset_freeze.py +11 -0
  261. algokit_transact/models/asset_transfer.py +13 -0
  262. algokit_transact/models/common.py +17 -0
  263. algokit_transact/models/heartbeat.py +21 -0
  264. algokit_transact/models/key_registration.py +14 -0
  265. algokit_transact/models/payment.py +14 -0
  266. algokit_transact/models/signed_transaction.py +21 -0
  267. algokit_transact/models/state_proof.py +150 -0
  268. algokit_transact/models/transaction.py +88 -0
  269. algokit_transact/multisig.py +93 -0
  270. algokit_transact/ops/__init__.py +0 -0
  271. algokit_transact/ops/fees.py +47 -0
  272. algokit_transact/ops/group.py +28 -0
  273. algokit_transact/ops/ids.py +14 -0
  274. algokit_transact/ops/validate.py +503 -0
  275. algokit_transact/py.typed +0 -0
  276. algokit_transact/signer.py +195 -0
  277. algokit_transact/signing/__init__.py +0 -0
  278. algokit_transact/signing/logic_signature.py +19 -0
  279. algokit_transact/signing/multisig.py +84 -0
  280. algokit_transact/signing/types.py +39 -0
  281. algokit_transact/signing/validation.py +63 -0
  282. algokit_utils/__init__.py +23 -0
  283. algokit_utils/_debugging.py +304 -0
  284. algokit_utils/accounts/__init__.py +2 -0
  285. algokit_utils/accounts/account_manager.py +1051 -0
  286. algokit_utils/accounts/kmd_account_manager.py +206 -0
  287. algokit_utils/algo25.py +46 -0
  288. algokit_utils/algorand.py +383 -0
  289. algokit_utils/applications/__init__.py +7 -0
  290. algokit_utils/applications/abi.py +280 -0
  291. algokit_utils/applications/app_client.py +2193 -0
  292. algokit_utils/applications/app_deployer.py +788 -0
  293. algokit_utils/applications/app_factory.py +1140 -0
  294. algokit_utils/applications/app_manager.py +575 -0
  295. algokit_utils/applications/app_spec/__init__.py +6 -0
  296. algokit_utils/applications/enums.py +40 -0
  297. algokit_utils/assets/__init__.py +1 -0
  298. algokit_utils/assets/asset_manager.py +344 -0
  299. algokit_utils/clients/__init__.py +41 -0
  300. algokit_utils/clients/client_manager.py +756 -0
  301. algokit_utils/clients/dispenser_api_client.py +212 -0
  302. algokit_utils/common.py +40 -0
  303. algokit_utils/config.py +159 -0
  304. algokit_utils/errors/__init__.py +1 -0
  305. algokit_utils/errors/logic_error.py +160 -0
  306. algokit_utils/models/__init__.py +7 -0
  307. algokit_utils/models/account.py +12 -0
  308. algokit_utils/models/amount.py +198 -0
  309. algokit_utils/models/application.py +90 -0
  310. algokit_utils/models/network.py +29 -0
  311. algokit_utils/models/simulate.py +7 -0
  312. algokit_utils/models/state.py +53 -0
  313. algokit_utils/models/transaction.py +49 -0
  314. algokit_utils/protocols/__init__.py +3 -0
  315. algokit_utils/protocols/account.py +11 -0
  316. algokit_utils/protocols/signer.py +17 -0
  317. algokit_utils/protocols/typed_clients.py +110 -0
  318. algokit_utils/py.typed +0 -0
  319. algokit_utils/transact.py +195 -0
  320. algokit_utils/transactions/__init__.py +3 -0
  321. algokit_utils/transactions/builders/__init__.py +67 -0
  322. algokit_utils/transactions/builders/app.py +248 -0
  323. algokit_utils/transactions/builders/asset.py +256 -0
  324. algokit_utils/transactions/builders/common.py +263 -0
  325. algokit_utils/transactions/builders/keyreg.py +103 -0
  326. algokit_utils/transactions/builders/method_call.py +380 -0
  327. algokit_utils/transactions/builders/payment.py +43 -0
  328. algokit_utils/transactions/composer_resources.py +409 -0
  329. algokit_utils/transactions/fee_coverage.py +79 -0
  330. algokit_utils/transactions/helpers.py +9 -0
  331. algokit_utils/transactions/transaction_composer.py +1574 -0
  332. algokit_utils/transactions/transaction_creator.py +699 -0
  333. algokit_utils/transactions/transaction_sender.py +1240 -0
  334. algokit_utils/transactions/types.py +262 -0
  335. algokit_utils-5.0.0a3.dist-info/METADATA +105 -0
  336. algokit_utils-5.0.0a3.dist-info/RECORD +337 -0
  337. algokit_utils-5.0.0a3.dist-info/WHEEL +4 -0
@@ -0,0 +1,409 @@
1
+ from dataclasses import dataclass, replace
2
+ from enum import Enum
3
+ from typing import Any
4
+
5
+ from algokit_algod_client import models as algod_models
6
+ from algokit_common import get_application_address
7
+ from algokit_common.constants import MAX_ACCOUNT_REFERENCES, MAX_OVERALL_REFERENCES
8
+ from algokit_transact.models.app_call import AppCallTransactionFields, BoxReference
9
+ from algokit_transact.models.transaction import Transaction, TransactionType
10
+
11
+
12
+ def populate_transaction_resources( # noqa: C901, PLR0912
13
+ transaction: Transaction,
14
+ resources_accessed: algod_models.SimulateUnnamedResourcesAccessed,
15
+ group_index: int,
16
+ ) -> Transaction:
17
+ """
18
+ Populate transaction-level resources for app call transactions
19
+ """
20
+ if transaction.transaction_type != TransactionType.AppCall or transaction.application_call is None:
21
+ return transaction
22
+
23
+ # Check for unexpected resources at transaction level
24
+ if resources_accessed.boxes or resources_accessed.extra_box_refs:
25
+ raise ValueError("Unexpected boxes at the transaction level")
26
+ if resources_accessed.app_locals:
27
+ raise ValueError("Unexpected app locals at the transaction level")
28
+ if resources_accessed.asset_holdings:
29
+ raise ValueError("Unexpected asset holdings at the transaction level")
30
+
31
+ app_call = transaction.application_call
32
+ updated_app_call = app_call
33
+ accounts_count = len(app_call.account_references or [])
34
+ apps_count = len(app_call.app_references or [])
35
+ assets_count = len(app_call.asset_references or [])
36
+ boxes_count = len(app_call.box_references or [])
37
+
38
+ # Populate accounts
39
+ if resources_accessed.accounts:
40
+ current_accounts = list(app_call.account_references or [])
41
+ for account in resources_accessed.accounts:
42
+ normalized = _normalize_address(account)
43
+ if normalized not in current_accounts:
44
+ current_accounts.append(normalized)
45
+ if len(current_accounts) != accounts_count:
46
+ updated_app_call = replace(updated_app_call, account_references=current_accounts)
47
+ accounts_count = len(current_accounts)
48
+
49
+ # Populate apps
50
+ if resources_accessed.apps:
51
+ current_apps = list(updated_app_call.app_references or [])
52
+ for app_id in resources_accessed.apps:
53
+ if app_id not in current_apps:
54
+ current_apps.append(app_id)
55
+ if len(current_apps) != apps_count:
56
+ updated_app_call = replace(updated_app_call, app_references=current_apps)
57
+ apps_count = len(current_apps)
58
+
59
+ # Populate assets
60
+ if resources_accessed.assets:
61
+ current_assets = list(updated_app_call.asset_references or [])
62
+ for asset_id in resources_accessed.assets:
63
+ if asset_id not in current_assets:
64
+ current_assets.append(asset_id)
65
+ if len(current_assets) != assets_count:
66
+ updated_app_call = replace(updated_app_call, asset_references=current_assets)
67
+ assets_count = len(current_assets)
68
+
69
+ # Validate reference limits
70
+ if accounts_count + assets_count + apps_count + boxes_count > MAX_OVERALL_REFERENCES:
71
+ raise ValueError(f"Resource reference limit of {MAX_OVERALL_REFERENCES} exceeded in transaction {group_index}")
72
+
73
+ if updated_app_call is app_call:
74
+ return transaction
75
+ return replace(transaction, application_call=updated_app_call)
76
+
77
+
78
+ class GroupResourceType(Enum):
79
+ """Describes different group resources"""
80
+
81
+ Account = "Account"
82
+ App = "App"
83
+ Asset = "Asset"
84
+ Box = "Box"
85
+ ExtraBoxRef = "ExtraBoxRef"
86
+ AssetHolding = "AssetHolding"
87
+ AppLocal = "AppLocal"
88
+
89
+
90
+ @dataclass(slots=True)
91
+ class GroupResourceToPopulate:
92
+ type: GroupResourceType
93
+ data: Any
94
+
95
+
96
+ def populate_group_resources( # noqa: C901, PLR0912
97
+ transactions: list[Transaction],
98
+ group_resources: algod_models.SimulateUnnamedResourcesAccessed,
99
+ ) -> None:
100
+ """
101
+ Populate group-level resources for app call transactions
102
+ """
103
+ remaining_accounts = list(group_resources.accounts or [])
104
+ remaining_apps = list(group_resources.apps or [])
105
+ remaining_assets = list(group_resources.assets or [])
106
+ remaining_boxes = list(group_resources.boxes or [])
107
+
108
+ # Process cross-reference resources first (app locals and asset holdings) as they are most restrictive
109
+ if group_resources.app_locals:
110
+ for app_local in group_resources.app_locals:
111
+ _populate_group_resource(transactions, GroupResourceToPopulate(GroupResourceType.AppLocal, app_local))
112
+ # Remove resources from remaining if we're adding them here
113
+ if app_local.address in remaining_accounts:
114
+ remaining_accounts.remove(app_local.address)
115
+ if app_local.app_id in remaining_apps:
116
+ remaining_apps.remove(app_local.app_id)
117
+
118
+ if group_resources.asset_holdings:
119
+ for asset_holding in group_resources.asset_holdings:
120
+ _populate_group_resource(
121
+ transactions, GroupResourceToPopulate(GroupResourceType.AssetHolding, asset_holding)
122
+ )
123
+ # Remove resources from remaining if we're adding them here
124
+ if asset_holding.address in remaining_accounts:
125
+ remaining_accounts.remove(asset_holding.address)
126
+ if asset_holding.asset_id in remaining_assets:
127
+ remaining_assets.remove(asset_holding.asset_id)
128
+
129
+ # Process accounts next
130
+ for account in remaining_accounts:
131
+ _populate_group_resource(transactions, GroupResourceToPopulate(GroupResourceType.Account, account))
132
+
133
+ # Process boxes
134
+ for box_ref in remaining_boxes:
135
+ _populate_group_resource(
136
+ transactions,
137
+ GroupResourceToPopulate(
138
+ GroupResourceType.Box,
139
+ BoxReference(app_id=box_ref.app_id, name=box_ref.name),
140
+ ),
141
+ )
142
+ # Remove apps as resource if we're adding it here
143
+ if box_ref.app_id in remaining_apps:
144
+ remaining_apps.remove(box_ref.app_id)
145
+
146
+ # Process assets
147
+ for asset in remaining_assets:
148
+ _populate_group_resource(transactions, GroupResourceToPopulate(GroupResourceType.Asset, asset))
149
+
150
+ # Process remaining apps
151
+ for app in remaining_apps:
152
+ _populate_group_resource(transactions, GroupResourceToPopulate(GroupResourceType.App, app))
153
+
154
+ # Handle extra box refs
155
+ if group_resources.extra_box_refs:
156
+ for _ in range(group_resources.extra_box_refs):
157
+ _populate_group_resource(transactions, GroupResourceToPopulate(GroupResourceType.ExtraBoxRef, None))
158
+
159
+
160
+ def _is_app_call_below_resource_limit(txn: Transaction) -> bool:
161
+ if txn.transaction_type != TransactionType.AppCall or txn.application_call is None:
162
+ return False
163
+ if txn.application_call.access_references:
164
+ return False
165
+
166
+ accounts_count = len(txn.application_call.account_references or [])
167
+ assets_count = len(txn.application_call.asset_references or [])
168
+ apps_count = len(txn.application_call.app_references or [])
169
+ boxes_count = len(txn.application_call.box_references or [])
170
+
171
+ return accounts_count + assets_count + apps_count + boxes_count < MAX_OVERALL_REFERENCES
172
+
173
+
174
+ def _get_app_address(app_id: int) -> str:
175
+ return get_application_address(app_id)
176
+
177
+
178
+ def _populate_group_resource( # noqa: C901, PLR0912, PLR0915
179
+ transactions: list[Transaction],
180
+ resource: GroupResourceToPopulate,
181
+ ) -> None:
182
+ # For asset holdings and app locals, first try to find a transaction that already has the account available
183
+ if resource.type in (GroupResourceType.AssetHolding, GroupResourceType.AppLocal):
184
+ account = _normalize_address(resource.data.address)
185
+
186
+ # Try to find a transaction that already has the account available
187
+ group_index1 = -1
188
+ for i, txn in enumerate(transactions):
189
+ if not _is_app_call_below_resource_limit(txn):
190
+ continue
191
+
192
+ app_call = txn.application_call
193
+ assert app_call is not None
194
+
195
+ # Check if account is in foreign accounts array
196
+ if app_call.account_references and account in app_call.account_references:
197
+ group_index1 = i
198
+ break
199
+
200
+ # Check if account is available as an app account
201
+ if app_call.app_references:
202
+ found = False
203
+ for app_id in app_call.app_references:
204
+ if account == _get_app_address(app_id):
205
+ found = True
206
+ break
207
+ if found:
208
+ group_index1 = i
209
+ break
210
+
211
+ # Check if account appears in any app call transaction fields
212
+ if txn.sender == account:
213
+ group_index1 = i
214
+ break
215
+
216
+ if group_index1 != -1:
217
+ app_call = transactions[group_index1].application_call
218
+ assert app_call is not None
219
+ if resource.type == GroupResourceType.AssetHolding:
220
+ current_assets = list(app_call.asset_references or [])
221
+ if resource.data.asset_id not in current_assets:
222
+ current_assets.append(resource.data.asset_id)
223
+ app_call = replace(app_call, asset_references=current_assets)
224
+ _set_app_call(transactions, group_index1, app_call)
225
+ else:
226
+ current_apps = list(app_call.app_references or [])
227
+ if resource.data.app_id not in current_apps:
228
+ current_apps.append(resource.data.app_id)
229
+ app_call = replace(app_call, app_references=current_apps)
230
+ _set_app_call(transactions, group_index1, app_call)
231
+ return
232
+
233
+ # Try to find a transaction that has the asset/app available and space for account
234
+ group_index2 = -1
235
+ for i, txn in enumerate(transactions):
236
+ if not _is_app_call_below_resource_limit(txn):
237
+ continue
238
+
239
+ app_call = txn.application_call
240
+ assert app_call is not None
241
+ if len(app_call.account_references or []) >= MAX_ACCOUNT_REFERENCES:
242
+ continue
243
+
244
+ if resource.type == GroupResourceType.AssetHolding:
245
+ if app_call.asset_references and resource.data.asset_id in app_call.asset_references:
246
+ group_index2 = i
247
+ break
248
+ elif (
249
+ app_call.app_references and resource.data.app_id in app_call.app_references
250
+ ) or app_call.app_id == resource.data.app_id:
251
+ group_index2 = i
252
+ break
253
+
254
+ if group_index2 != -1:
255
+ app_call = transactions[group_index2].application_call
256
+ assert app_call is not None
257
+ current_accounts = list(app_call.account_references or [])
258
+ if account not in current_accounts:
259
+ current_accounts.append(account)
260
+ app_call = replace(app_call, account_references=current_accounts)
261
+ _set_app_call(transactions, group_index2, app_call)
262
+ return
263
+
264
+ # For boxes, first try to find a transaction that already has the app available
265
+ if resource.type == GroupResourceType.Box:
266
+ group_index = -1
267
+ for i, txn in enumerate(transactions):
268
+ if not _is_app_call_below_resource_limit(txn):
269
+ continue
270
+
271
+ app_call = txn.application_call
272
+ assert app_call is not None
273
+ if (
274
+ app_call.app_references and resource.data.app_id in app_call.app_references
275
+ ) or app_call.app_id == resource.data.app_id:
276
+ group_index = i
277
+ break
278
+
279
+ if group_index != -1:
280
+ app_call = transactions[group_index].application_call
281
+ assert app_call is not None
282
+ current_boxes = list(app_call.box_references or [])
283
+ exists = any(b.app_id == resource.data.app_id and b.name == resource.data.name for b in current_boxes)
284
+ if not exists:
285
+ current_boxes.append(BoxReference(app_id=resource.data.app_id, name=resource.data.name))
286
+ app_call = replace(app_call, box_references=current_boxes)
287
+ _set_app_call(transactions, group_index, app_call)
288
+ return
289
+
290
+ # Find the first transaction that can accommodate the resource
291
+ group_index = -1
292
+ for i, txn in enumerate(transactions):
293
+ if txn.transaction_type != TransactionType.AppCall or txn.application_call is None:
294
+ continue
295
+ if txn.application_call.access_references:
296
+ continue
297
+
298
+ app_call = txn.application_call
299
+ accounts_count = len(app_call.account_references or [])
300
+ assets_count = len(app_call.asset_references or [])
301
+ apps_count = len(app_call.app_references or [])
302
+ boxes_count = len(app_call.box_references or [])
303
+
304
+ if resource.type == GroupResourceType.Account:
305
+ if accounts_count + assets_count + apps_count + boxes_count < MAX_OVERALL_REFERENCES:
306
+ group_index = i
307
+ break
308
+ elif resource.type in (GroupResourceType.AssetHolding, GroupResourceType.AppLocal):
309
+ if accounts_count + assets_count + apps_count + boxes_count < MAX_OVERALL_REFERENCES - 1:
310
+ group_index = i
311
+ break
312
+ elif resource.type == GroupResourceType.Box:
313
+ if resource.data.app_id != 0:
314
+ if accounts_count + assets_count + apps_count + boxes_count < MAX_OVERALL_REFERENCES - 1:
315
+ group_index = i
316
+ break
317
+ elif accounts_count + assets_count + apps_count + boxes_count < MAX_OVERALL_REFERENCES:
318
+ group_index = i
319
+ break
320
+ elif accounts_count + assets_count + apps_count + boxes_count < MAX_OVERALL_REFERENCES:
321
+ group_index = i
322
+ break
323
+
324
+ if group_index == -1:
325
+ raise ValueError("No more transactions below reference limit. Add another app call to the group.")
326
+
327
+ app_call = transactions[group_index].application_call
328
+ assert app_call is not None
329
+
330
+ if resource.type == GroupResourceType.Account:
331
+ current_accounts = list(app_call.account_references or [])
332
+ account = _normalize_address(resource.data)
333
+ if account not in current_accounts:
334
+ current_accounts.append(account)
335
+ app_call = replace(app_call, account_references=current_accounts)
336
+ _set_app_call(transactions, group_index, app_call)
337
+
338
+ elif resource.type == GroupResourceType.App:
339
+ current_apps = list(app_call.app_references or [])
340
+ if resource.data not in current_apps:
341
+ current_apps.append(resource.data)
342
+ app_call = replace(app_call, app_references=current_apps)
343
+ _set_app_call(transactions, group_index, app_call)
344
+
345
+ elif resource.type == GroupResourceType.Box:
346
+ current_boxes = list(app_call.box_references or [])
347
+ exists = any(b.app_id == resource.data.app_id and b.name == resource.data.name for b in current_boxes)
348
+ if not exists:
349
+ current_boxes.append(BoxReference(app_id=resource.data.app_id, name=resource.data.name))
350
+ app_call = replace(app_call, box_references=current_boxes)
351
+ _set_app_call(transactions, group_index, app_call)
352
+
353
+ if resource.data.app_id != 0:
354
+ current_apps = list(app_call.app_references or [])
355
+ if resource.data.app_id not in current_apps:
356
+ current_apps.append(resource.data.app_id)
357
+ app_call = replace(app_call, app_references=current_apps)
358
+ _set_app_call(transactions, group_index, app_call)
359
+
360
+ elif resource.type == GroupResourceType.ExtraBoxRef:
361
+ current_boxes = list(app_call.box_references or [])
362
+ current_boxes.append(BoxReference(app_id=0, name=b""))
363
+ app_call = replace(app_call, box_references=current_boxes)
364
+ _set_app_call(transactions, group_index, app_call)
365
+
366
+ elif resource.type == GroupResourceType.AssetHolding:
367
+ current_assets = list(app_call.asset_references or [])
368
+ if resource.data.asset_id not in current_assets:
369
+ current_assets.append(resource.data.asset_id)
370
+ app_call = replace(app_call, asset_references=current_assets)
371
+ _set_app_call(transactions, group_index, app_call)
372
+
373
+ current_accounts = list(app_call.account_references or [])
374
+ account = _normalize_address(resource.data.address)
375
+ if account not in current_accounts:
376
+ current_accounts.append(account)
377
+ app_call = replace(app_call, account_references=current_accounts)
378
+ _set_app_call(transactions, group_index, app_call)
379
+
380
+ elif resource.type == GroupResourceType.AppLocal:
381
+ current_apps = list(app_call.app_references or [])
382
+ if resource.data.app_id not in current_apps:
383
+ current_apps.append(resource.data.app_id)
384
+ app_call = replace(app_call, app_references=current_apps)
385
+ _set_app_call(transactions, group_index, app_call)
386
+
387
+ current_accounts = list(app_call.account_references or [])
388
+ account = _normalize_address(resource.data.address)
389
+ if account not in current_accounts:
390
+ current_accounts.append(account)
391
+ app_call = replace(app_call, account_references=current_accounts)
392
+ _set_app_call(transactions, group_index, app_call)
393
+
394
+ elif resource.type == GroupResourceType.Asset:
395
+ current_assets = list(app_call.asset_references or [])
396
+ if resource.data not in current_assets:
397
+ current_assets.append(resource.data)
398
+ app_call = replace(app_call, asset_references=current_assets)
399
+ _set_app_call(transactions, group_index, app_call)
400
+
401
+
402
+ def _set_app_call(transactions: list[Transaction], index: int, app_call: AppCallTransactionFields) -> None:
403
+ transactions[index] = replace(transactions[index], application_call=app_call)
404
+
405
+
406
+ def _normalize_address(value: str | bytes) -> str:
407
+ if isinstance(value, bytes):
408
+ return value.decode("utf-8")
409
+ return value
@@ -0,0 +1,79 @@
1
+ from collections.abc import Callable
2
+ from dataclasses import dataclass
3
+ from enum import Enum, auto
4
+ from typing import ClassVar
5
+
6
+
7
+ class FeeDeltaType(Enum):
8
+ """Describes the type of fee delta"""
9
+
10
+ DEFICIT = auto()
11
+ SURPLUS = auto()
12
+
13
+
14
+ @dataclass(slots=True, frozen=True)
15
+ class FeeDelta:
16
+ """Represents a difference between required and provided fee amounts."""
17
+
18
+ type: FeeDeltaType
19
+ data: int
20
+
21
+ @staticmethod
22
+ def from_int(value: int) -> "FeeDelta | None":
23
+ if value > 0:
24
+ return FeeDelta(FeeDeltaType.DEFICIT, value)
25
+ if value < 0:
26
+ return FeeDelta(FeeDeltaType.SURPLUS, -value)
27
+ return None
28
+
29
+ @staticmethod
30
+ def add(lhs: "FeeDelta | None", rhs: "FeeDelta | None") -> "FeeDelta | None":
31
+ if lhs is None:
32
+ return rhs
33
+ if rhs is None:
34
+ return lhs
35
+ return FeeDelta.from_int(FeeDelta.to_int(lhs) + FeeDelta.to_int(rhs))
36
+
37
+ @staticmethod
38
+ def to_int(delta: "FeeDelta") -> int:
39
+ return delta.data if delta.type is FeeDeltaType.DEFICIT else -delta.data
40
+
41
+ @staticmethod
42
+ def amount(delta: "FeeDelta") -> int:
43
+ return delta.data
44
+
45
+ @staticmethod
46
+ def is_deficit(delta: "FeeDelta") -> bool:
47
+ return delta.type is FeeDeltaType.DEFICIT
48
+
49
+ @staticmethod
50
+ def is_surplus(delta: "FeeDelta") -> bool:
51
+ return delta.type is FeeDeltaType.SURPLUS
52
+
53
+
54
+ @dataclass(slots=True, frozen=True, order=True)
55
+ class FeePriority:
56
+ """Priority wrapper used when deciding which transactions need additional fees applied first."""
57
+
58
+ priority_level: int
59
+ deficit_amount: int
60
+ Covered: ClassVar["FeePriority"]
61
+ ModifiableDeficit: ClassVar[Callable[[int], "FeePriority"]]
62
+ ImmutableDeficit: ClassVar[Callable[[int], "FeePriority"]]
63
+
64
+ @staticmethod
65
+ def covered() -> "FeePriority":
66
+ return FeePriority(0, 0)
67
+
68
+ @staticmethod
69
+ def modifiable_deficit(amount: int) -> "FeePriority":
70
+ return FeePriority(1, amount)
71
+
72
+ @staticmethod
73
+ def immutable_deficit(amount: int) -> "FeePriority":
74
+ return FeePriority(2, amount)
75
+
76
+
77
+ FeePriority.Covered = FeePriority.covered()
78
+ FeePriority.ModifiableDeficit = staticmethod(FeePriority.modifiable_deficit)
79
+ FeePriority.ImmutableDeficit = staticmethod(FeePriority.immutable_deficit)
@@ -0,0 +1,9 @@
1
+ from algokit_common import PROGRAM_PAGE_SIZE
2
+
3
+ __all__ = ["calculate_extra_program_pages"]
4
+
5
+
6
+ def calculate_extra_program_pages(approval: bytes | None, clear: bytes | None) -> int:
7
+ """Calculate minimum number of extra_pages required for provided approval and clear programs."""
8
+ total = len(approval or b"") + len(clear or b"")
9
+ return max(0, (total - 1) // PROGRAM_PAGE_SIZE)