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,19 @@
1
+ from dataclasses import dataclass, field
2
+
3
+ from algokit_transact.codec.serde import bytes_seq, nested, wire
4
+ from algokit_transact.signing.types import MultisigSignature
5
+
6
+
7
+ @dataclass(slots=True, frozen=True)
8
+ class LogicSignature:
9
+ logic: bytes = field(metadata=wire("l"))
10
+ args: list[bytes] | None = field(default=None, metadata=bytes_seq("arg"))
11
+ sig: bytes | None = field(default=None, metadata=wire("sig"))
12
+ msig: MultisigSignature | None = field(
13
+ default=None,
14
+ metadata=nested("msig", MultisigSignature),
15
+ )
16
+ lmsig: MultisigSignature | None = field(
17
+ default=None,
18
+ metadata=nested("lmsig", MultisigSignature),
19
+ )
@@ -0,0 +1,84 @@
1
+ from collections.abc import Iterable
2
+ from dataclasses import replace
3
+
4
+ from algokit_common import (
5
+ MULTISIG_DOMAIN_SEPARATOR,
6
+ PUBLIC_KEY_BYTE_LENGTH,
7
+ address_from_public_key,
8
+ public_key_from_address,
9
+ sha512_256,
10
+ )
11
+ from algokit_transact.signing.types import MultisigSignature, MultisigSubsignature
12
+
13
+
14
+ def new_multisig_signature(version: int, threshold: int, participants: Iterable[str]) -> MultisigSignature:
15
+ participants = list(participants)
16
+ if version == 0:
17
+ raise ValueError("Version cannot be zero")
18
+ if not participants:
19
+ raise ValueError("Participants cannot be empty")
20
+ if threshold == 0 or threshold > len(participants):
21
+ raise ValueError("Threshold must be greater than zero and less than or equal to the number of participants")
22
+
23
+ subsigs = [MultisigSubsignature(public_key=public_key_from_address(address)) for address in participants]
24
+ return MultisigSignature(version=version, threshold=threshold, subsigs=subsigs)
25
+
26
+
27
+ def participants_from_multisig_signature(multisig_signature: MultisigSignature) -> list[str]:
28
+ return [address_from_public_key(subsig.public_key) for subsig in multisig_signature.subsigs]
29
+
30
+
31
+ def address_from_multisig_signature(multisig_signature: MultisigSignature) -> str:
32
+ prefix = MULTISIG_DOMAIN_SEPARATOR.encode()
33
+ participant_keys = [subsig.public_key for subsig in multisig_signature.subsigs]
34
+
35
+ buffer = bytearray()
36
+ buffer.extend(prefix)
37
+ buffer.append(multisig_signature.version)
38
+ buffer.append(multisig_signature.threshold)
39
+ for pk in participant_keys:
40
+ if len(pk) != PUBLIC_KEY_BYTE_LENGTH:
41
+ raise ValueError("Invalid participant public key length")
42
+ buffer.extend(pk)
43
+
44
+ public_key = sha512_256(bytes(buffer))
45
+ return address_from_public_key(public_key)
46
+
47
+
48
+ def apply_multisig_subsignature(
49
+ multisig_signature: MultisigSignature, participant: str, signature: bytes
50
+ ) -> MultisigSignature:
51
+ found = False
52
+ updated = []
53
+ participant_pk = public_key_from_address(participant)
54
+ for subsig in multisig_signature.subsigs:
55
+ if subsig.public_key == participant_pk:
56
+ found = True
57
+ updated.append(MultisigSubsignature(public_key=subsig.public_key, sig=signature))
58
+ else:
59
+ updated.append(subsig)
60
+ if not found:
61
+ raise ValueError("Address not found in multisig signature")
62
+ return replace(multisig_signature, subsigs=updated)
63
+
64
+
65
+ def merge_multisignatures(multisig_a: MultisigSignature, multisig_b: MultisigSignature) -> MultisigSignature:
66
+ if multisig_a.version != multisig_b.version:
67
+ raise ValueError("Cannot merge multisig signatures with different versions")
68
+ if multisig_a.threshold != multisig_b.threshold:
69
+ raise ValueError("Cannot merge multisig signatures with different thresholds")
70
+
71
+ participants_a = participants_from_multisig_signature(multisig_a)
72
+ participants_b = participants_from_multisig_signature(multisig_b)
73
+ if participants_a != participants_b:
74
+ raise ValueError("Cannot merge multisig signatures with different participants")
75
+
76
+ merged_subsigs = []
77
+ for subsig_a, subsig_b in zip(multisig_a.subsigs, multisig_b.subsigs, strict=False):
78
+ sig = subsig_b.sig if subsig_b.sig is not None else subsig_a.sig
79
+ merged_subsigs.append(MultisigSubsignature(public_key=subsig_a.public_key, sig=sig))
80
+ return MultisigSignature(
81
+ version=multisig_a.version,
82
+ threshold=multisig_a.threshold,
83
+ subsigs=merged_subsigs,
84
+ )
@@ -0,0 +1,39 @@
1
+ from collections.abc import Mapping
2
+ from dataclasses import dataclass, field
3
+ from typing import cast
4
+
5
+ from algokit_transact.codec.serde import from_wire, to_wire, wire
6
+
7
+
8
+ def _encode_subsig_seq(value: object) -> object:
9
+ if value is None:
10
+ return None
11
+ if isinstance(value, tuple | list):
12
+ payload = [to_wire(subsig) for subsig in value]
13
+ return payload if payload else None
14
+ return value
15
+
16
+
17
+ def _decode_subsig_seq(value: object) -> object:
18
+ if isinstance(value, list):
19
+ decoded: list[MultisigSubsignature] = []
20
+ for entry in value:
21
+ if isinstance(entry, Mapping):
22
+ decoded.append(from_wire(MultisigSubsignature, cast(Mapping[str, object], entry)))
23
+ return tuple(decoded)
24
+ return value
25
+
26
+
27
+ @dataclass(slots=True, frozen=True)
28
+ class MultisigSubsignature:
29
+ public_key: bytes = field(metadata=wire("pk"))
30
+ sig: bytes | None = field(default=None, metadata=wire("s"))
31
+
32
+
33
+ @dataclass(slots=True, frozen=True)
34
+ class MultisigSignature:
35
+ version: int = field(metadata=wire("v", keep_zero=True))
36
+ threshold: int = field(metadata=wire("thr", keep_zero=True))
37
+ subsigs: list[MultisigSubsignature] = field(
38
+ metadata=wire("subsig", encode=_encode_subsig_seq, decode=_decode_subsig_seq)
39
+ )
@@ -0,0 +1,63 @@
1
+ """Program validation utilities for TEAL bytecode.
2
+
3
+ This module provides sanity checks for compiled TEAL programs to help
4
+ detect common errors such as passing source code instead of bytecode,
5
+ or passing base64-encoded programs.
6
+ """
7
+
8
+ import base64
9
+
10
+ from algokit_common.address import public_key_from_address
11
+
12
+
13
+ def sanity_check_program(program: bytes) -> None:
14
+ """Perform sanity checks on a compiled TEAL program.
15
+
16
+ This function validates that the provided bytes appear to be compiled
17
+ TEAL bytecode rather than common mistakes like:
18
+ - Empty program
19
+ - An Algorand address string
20
+ - A base64-encoded string
21
+ - TEAL source code (ASCII text)
22
+
23
+ Args:
24
+ program: The compiled TEAL program bytes to validate.
25
+
26
+ Raises:
27
+ ValueError: If the program fails any sanity check:
28
+ - "empty program" if the program is None or empty
29
+ - "requesting program bytes, get Algorand address" if the bytes
30
+ decode to a valid Algorand address
31
+ - "program should not be b64 encoded" if the bytes appear to be
32
+ a base64-encoded string
33
+ - "program bytes are all ASCII printable characters, not looking
34
+ like Teal byte code" if all bytes are printable ASCII
35
+ """
36
+ if not program:
37
+ raise ValueError("empty program")
38
+
39
+ try:
40
+ ascii_str = program.decode("ascii")
41
+ except UnicodeDecodeError:
42
+ # not ascii, probably bytecode
43
+ return
44
+
45
+ if any(not line.isprintable() for line in ascii_str.splitlines()):
46
+ # not printable, probably bytecode
47
+ return
48
+
49
+ try:
50
+ public_key_from_address(ascii_str)
51
+ except (TypeError, ValueError):
52
+ pass
53
+ else:
54
+ raise ValueError("requesting program bytes, get Algorand address")
55
+
56
+ try:
57
+ base64.b64decode(ascii_str)
58
+ except (TypeError, ValueError):
59
+ pass
60
+ else:
61
+ raise ValueError("program should not be b64 encoded")
62
+
63
+ raise ValueError("program bytes are all ASCII printable characters, not looking like Teal byte code")
@@ -0,0 +1,23 @@
1
+ """AlgoKit Python Utilities - a set of utilities for building solutions on Algorand
2
+
3
+ This module provides commonly used utilities and types at the root level for convenience.
4
+ For more specific functionality, import directly from the relevant submodules:
5
+
6
+ from algokit_utils.accounts import KmdAccountManager
7
+ from algokit_utils.applications import AppClient
8
+ from algokit_utils.applications.app_spec import Arc52Contract
9
+ from algokit_utils.transact import Transaction, TransactionSigner
10
+ etc.
11
+ """
12
+
13
+ # Core types and utilities that are commonly used
14
+ from algokit_utils.applications import * # noqa: F403
15
+ from algokit_utils.assets import * # noqa: F403
16
+ from algokit_utils.protocols import * # noqa: F403
17
+ from algokit_utils.models import * # noqa: F403
18
+ from algokit_utils.accounts import * # noqa: F403
19
+ from algokit_utils.clients import * # noqa: F403
20
+ from algokit_utils.transactions import * # noqa: F403
21
+ from algokit_utils.errors import * # noqa: F403
22
+ from algokit_utils.algorand import * # noqa: F403
23
+ from algokit_utils.transact import * # noqa: F403
@@ -0,0 +1,304 @@
1
+ import base64
2
+ import json
3
+ import logging
4
+ import typing
5
+ from dataclasses import dataclass, field
6
+ from datetime import datetime, timezone
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+ from algokit_common import ProgramSourceMap, sha512_256
11
+ from algokit_common.serde import to_wire
12
+ from algokit_utils.applications.app_manager import AppManager
13
+ from algokit_utils.config import config
14
+ from algokit_utils.models.application import CompiledTeal
15
+ from algokit_utils.transactions.transaction_composer import SendTransactionComposerResults, TransactionComposer
16
+
17
+ if typing.TYPE_CHECKING:
18
+ from algosdk.atomic_transaction_composer import SimulateAtomicTransactionResponse # type: ignore[import-not-found]
19
+
20
+ from algokit_algod_client import AlgodClient
21
+ else:
22
+ SimulateAtomicTransactionResponse = typing.Any # type: ignore[assignment]
23
+ AlgodClient = typing.Any # type: ignore[assignment]
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+ ALGOKIT_DIR = ".algokit"
28
+ SOURCES_DIR = "sources"
29
+ SOURCES_FILE = "sources.avm.json"
30
+ TRACES_FILE_EXT = ".trace.avm.json"
31
+ DEBUG_TRACES_DIR = "debug_traces"
32
+ TEAL_FILE_EXT = ".teal"
33
+ TEAL_SOURCEMAP_EXT = ".teal.map"
34
+ TRACE_FILENAME_DATE_FORMAT = "%Y%m%d_%H%M%S"
35
+
36
+
37
+ @dataclass
38
+ class AVMDebuggerSourceMapEntry:
39
+ location: str = field(metadata={"json": "sourcemap-location"})
40
+ program_hash: str = field(metadata={"json": "hash"})
41
+
42
+ def __eq__(self, other: object) -> bool:
43
+ if isinstance(other, AVMDebuggerSourceMapEntry):
44
+ return self.location == other.location and self.program_hash == other.program_hash
45
+ return False
46
+
47
+ def __str__(self) -> str:
48
+ return json.dumps({"sourcemap-location": self.location, "hash": self.program_hash})
49
+
50
+
51
+ @dataclass
52
+ class AVMDebuggerSourceMap:
53
+ txn_group_sources: list[AVMDebuggerSourceMapEntry] = field(metadata={"json": "txn-group-sources"})
54
+
55
+ @classmethod
56
+ def from_dict(cls, data: dict) -> "AVMDebuggerSourceMap":
57
+ return cls(
58
+ txn_group_sources=[
59
+ AVMDebuggerSourceMapEntry(location=item["sourcemap-location"], program_hash=item["hash"])
60
+ for item in data.get("txn-group-sources", [])
61
+ ]
62
+ )
63
+
64
+ def to_dict(self) -> dict:
65
+ return {"txn-group-sources": [json.loads(str(item)) for item in self.txn_group_sources]}
66
+
67
+
68
+ @dataclass
69
+ class PersistSourceMapInput:
70
+ def __init__(
71
+ self,
72
+ app_name: str,
73
+ file_name: str,
74
+ raw_teal: str | None = None,
75
+ compiled_teal: CompiledTeal | None = None,
76
+ ):
77
+ self.compiled_teal = compiled_teal
78
+ self.app_name = app_name
79
+ self._raw_teal = raw_teal
80
+ self._file_name = self.strip_teal_extension(file_name)
81
+
82
+ @classmethod
83
+ def from_raw_teal(cls, raw_teal: str, app_name: str, file_name: str) -> "PersistSourceMapInput":
84
+ return cls(app_name, file_name, raw_teal=raw_teal)
85
+
86
+ @classmethod
87
+ def from_compiled_teal(cls, compiled_teal: CompiledTeal, app_name: str, file_name: str) -> "PersistSourceMapInput":
88
+ return cls(app_name, file_name, compiled_teal=compiled_teal)
89
+
90
+ @property
91
+ def raw_teal(self) -> str:
92
+ if self._raw_teal:
93
+ return self._raw_teal
94
+ elif self.compiled_teal:
95
+ return self.compiled_teal.teal
96
+ else:
97
+ raise ValueError("No teal content found")
98
+
99
+ @property
100
+ def file_name(self) -> str:
101
+ return self._file_name
102
+
103
+ @staticmethod
104
+ def strip_teal_extension(file_name: str) -> str:
105
+ if file_name.endswith(".teal"):
106
+ return file_name[:-5]
107
+ return file_name
108
+
109
+
110
+ def _write_to_file(path: Path, content: str) -> None:
111
+ path.parent.mkdir(parents=True, exist_ok=True)
112
+ path.write_text(content)
113
+
114
+
115
+ def _compile_raw_teal(raw_teal: str, client: AlgodClient) -> tuple[bytes, ProgramSourceMap, str]:
116
+ teal_to_compile = AppManager.strip_teal_comments(raw_teal)
117
+ compiled = client.teal_compile(teal_to_compile.encode("utf-8"), sourcemap=True)
118
+ compiled_bytes = base64.b64decode(compiled.result)
119
+ sourcemap_dict = to_wire(compiled.sourcemap) if compiled.sourcemap else {}
120
+ return compiled_bytes, ProgramSourceMap(sourcemap_dict), raw_teal
121
+
122
+
123
+ def _build_avm_sourcemap(
124
+ *,
125
+ app_name: str,
126
+ file_name: str,
127
+ output_path: Path,
128
+ client: AlgodClient,
129
+ raw_teal: str | None = None,
130
+ compiled_teal: CompiledTeal | None = None,
131
+ with_sources: bool = True,
132
+ ) -> AVMDebuggerSourceMapEntry:
133
+ if not raw_teal and not compiled_teal:
134
+ raise ValueError("Either raw teal or compiled teal must be provided")
135
+
136
+ if isinstance(compiled_teal, CompiledTeal):
137
+ program_hash = base64.b64encode(sha512_256(compiled_teal.compiled_base64_to_bytes)).decode()
138
+ source_map = compiled_teal.source_map.__dict__ if compiled_teal.source_map else {}
139
+ teal_content = compiled_teal.teal
140
+ else:
141
+ compiled_bytes, source_map_obj, teal_content = _compile_raw_teal(str(raw_teal), client)
142
+ program_hash = base64.b64encode(sha512_256(compiled_bytes)).decode()
143
+ source_map = source_map_obj.__dict__
144
+
145
+ source_map["sources"] = [f"{file_name}{TEAL_FILE_EXT}"] if with_sources else []
146
+
147
+ output_dir_path = output_path / ALGOKIT_DIR / SOURCES_DIR / app_name
148
+ source_map_output_path = output_dir_path / f"{file_name}{TEAL_SOURCEMAP_EXT}"
149
+ teal_output_path = output_dir_path / f"{file_name}{TEAL_FILE_EXT}"
150
+ _write_to_file(source_map_output_path, json.dumps(source_map))
151
+
152
+ if with_sources:
153
+ _write_to_file(teal_output_path, teal_content)
154
+
155
+ return AVMDebuggerSourceMapEntry(str(source_map_output_path), program_hash)
156
+
157
+
158
+ def cleanup_old_trace_files(output_dir: Path, buffer_size_mb: float) -> None:
159
+ """
160
+ Cleanup old trace files if total size exceeds buffer size limit.
161
+
162
+ Args:
163
+ output_dir (Path): Directory containing trace files
164
+ buffer_size_mb (float): Maximum allowed size in megabytes
165
+ """
166
+ total_size = sum(f.stat().st_size for f in output_dir.glob("*") if f.is_file())
167
+ if total_size > buffer_size_mb * 1024 * 1024:
168
+ sorted_files = sorted(output_dir.glob("*"), key=lambda p: p.stat().st_mtime)
169
+ while total_size > buffer_size_mb * 1024 * 1024 and sorted_files:
170
+ oldest_file = sorted_files.pop(0)
171
+ total_size -= oldest_file.stat().st_size
172
+ oldest_file.unlink()
173
+
174
+
175
+ def _summarize_txn_types(trace: dict[str, Any]) -> str:
176
+ counts: dict[str, int] = {}
177
+ for group in trace.get("txn-groups", []):
178
+ for txn_result in group.get("txn-results", []):
179
+ txn = txn_result.get("txn-result", {}).get("txn", {}).get("txn", {})
180
+ txn_type = txn.get("type")
181
+ if txn_type and not isinstance(txn_type, str):
182
+ txn_type = getattr(txn_type, "value", str(txn_type))
183
+ if not txn_type:
184
+ continue
185
+ counts[txn_type] = counts.get(txn_type, 0) + 1
186
+ return "_".join(f"{count}{txn_type}" for txn_type, count in counts.items())
187
+
188
+
189
+ def _persist_simulation_trace(
190
+ trace: dict[str, Any],
191
+ project_root: Path,
192
+ *,
193
+ timestamp: datetime | None = None,
194
+ buffer_size_mb: float | None = None,
195
+ ) -> Path:
196
+ project_root.mkdir(parents=True, exist_ok=True)
197
+ trace_dir = project_root / DEBUG_TRACES_DIR
198
+ trace_dir.mkdir(parents=True, exist_ok=True)
199
+
200
+ now = timestamp or datetime.now(timezone.utc)
201
+ last_round = trace.get("last-round", 0)
202
+ txn_part = _summarize_txn_types(trace)
203
+ filename = (
204
+ f"{now.astimezone(timezone.utc).strftime(TRACE_FILENAME_DATE_FORMAT)}_lr{last_round}_{txn_part}"
205
+ f"{TRACES_FILE_EXT}"
206
+ )
207
+ output_path = trace_dir / filename
208
+
209
+ def _default_encoder(value: object) -> str:
210
+ if isinstance(value, (bytes | bytearray | memoryview)):
211
+ return base64.b64encode(bytes(value)).decode("utf-8")
212
+ return getattr(value, "value", str(value))
213
+
214
+ _write_to_file(output_path, json.dumps(trace, default=_default_encoder))
215
+
216
+ if buffer_size_mb is not None:
217
+ cleanup_old_trace_files(trace_dir, buffer_size_mb)
218
+
219
+ return output_path
220
+
221
+
222
+ def _extract_simulation_trace_from_algokit(result: SendTransactionComposerResults) -> dict[str, Any]:
223
+ if result.simulate_response is None:
224
+ raise ValueError("No simulate_response available to persist")
225
+ return to_wire(result.simulate_response)
226
+
227
+
228
+ def _extract_simulation_trace_from_atc(response: SimulateAtomicTransactionResponse) -> dict[str, Any]:
229
+ # algosdk simulate responses are already dict-like
230
+ return dict(response.simulate_response) if hasattr(response, "simulate_response") else dict(response)
231
+
232
+
233
+ def simulate_and_persist_response(
234
+ composer: TransactionComposer | object,
235
+ project_root: Path,
236
+ algod: AlgodClient,
237
+ *,
238
+ buffer_size_mb: float | None = None,
239
+ result: SendTransactionComposerResults | SimulateAtomicTransactionResponse | None = None,
240
+ ) -> Path:
241
+ """
242
+ Run a simulation on the provided composer and persist the trace to disk.
243
+
244
+ :param composer: Transaction composer (AlgoKit or algosdk AtomicTransactionComposer)
245
+ :param project_root: Root directory where traces should be stored
246
+ :param algod: Algod client to use for simulation
247
+ :param buffer_size_mb: Optional buffer size to enforce via cleanup_old_trace_files
248
+ :param result: Optional existing simulation result to persist instead of re-running simulation
249
+ :return: Path to the persisted trace file
250
+ :raises TypeError: If the composer does not implement a compatible ``simulate`` method
251
+ """
252
+ if result is None and isinstance(composer, TransactionComposer):
253
+ result = composer.simulate(_persist_trace=False)
254
+ trace = _extract_simulation_trace_from_algokit(result)
255
+ elif result is None and hasattr(composer, "simulate"):
256
+ result = composer.simulate(algod)
257
+ trace = _extract_simulation_trace_from_atc(result)
258
+ elif result is not None:
259
+ trace = (
260
+ _extract_simulation_trace_from_algokit(result)
261
+ if isinstance(result, SendTransactionComposerResults)
262
+ else _extract_simulation_trace_from_atc(result)
263
+ )
264
+ else:
265
+ raise TypeError("Composer must support simulate()")
266
+
267
+ effective_root = config.project_root or project_root
268
+ effective_buffer = buffer_size_mb
269
+ if config.trace_all and buffer_size_mb is None:
270
+ effective_buffer = config.trace_buffer_size_mb
271
+
272
+ return _persist_simulation_trace(
273
+ trace,
274
+ effective_root,
275
+ buffer_size_mb=effective_buffer,
276
+ )
277
+
278
+
279
+ def persist_sourcemaps(
280
+ *,
281
+ sources: list[PersistSourceMapInput],
282
+ project_root: Path,
283
+ client: AlgodClient,
284
+ with_sources: bool = True,
285
+ ) -> None:
286
+ """
287
+ Persist the sourcemaps for the given sources as an AlgoKit AVM Debugger compliant artifacts.
288
+
289
+ :param sources: A list of PersistSourceMapInput objects.
290
+ :param project_root: The root directory of the project.
291
+ :param client: An AlgodClient instance for interacting with the Algorand blockchain.
292
+ :param with_sources: If True, it will dump teal source files along with sourcemaps.
293
+ """
294
+
295
+ for source in sources:
296
+ _build_avm_sourcemap(
297
+ raw_teal=source.raw_teal,
298
+ compiled_teal=source.compiled_teal,
299
+ app_name=source.app_name,
300
+ file_name=source.file_name,
301
+ output_path=project_root,
302
+ client=client,
303
+ with_sources=with_sources,
304
+ )
@@ -0,0 +1,2 @@
1
+ from algokit_utils.accounts.account_manager import * # noqa: F403
2
+ from algokit_utils.accounts.kmd_account_manager import * # noqa: F403