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.
- algokit_abi/__init__.py +9 -0
- algokit_abi/_arc32_to_arc56.py +242 -0
- algokit_abi/_arc56_serde.py +161 -0
- algokit_abi/abi.py +667 -0
- algokit_abi/arc32.py +210 -0
- algokit_abi/arc56.py +821 -0
- algokit_abi/py.typed +0 -0
- algokit_algo25/__init__.py +38 -0
- algokit_algo25/_encoding.py +46 -0
- algokit_algo25/_wordlist.py +2065 -0
- algokit_algo25/exceptions.py +29 -0
- algokit_algo25/mnemonic.py +128 -0
- algokit_algo25/py.typed +0 -0
- algokit_algod_client/__init__.py +10 -0
- algokit_algod_client/client.py +1585 -0
- algokit_algod_client/config.py +36 -0
- algokit_algod_client/exceptions.py +59 -0
- algokit_algod_client/models/__init__.py +229 -0
- algokit_algod_client/models/_account.py +150 -0
- algokit_algod_client/models/_account_application_response.py +25 -0
- algokit_algod_client/models/_account_asset_response.py +25 -0
- algokit_algod_client/models/_account_participation.py +53 -0
- algokit_algod_client/models/_account_state_delta.py +30 -0
- algokit_algod_client/models/_allocations_for_genesis_file.py +23 -0
- algokit_algod_client/models/_allocations_for_genesis_file_state_model.py +42 -0
- algokit_algod_client/models/_application.py +23 -0
- algokit_algod_client/models/_application_initial_states.py +37 -0
- algokit_algod_client/models/_application_kvstorage.py +29 -0
- algokit_algod_client/models/_application_local_state.py +33 -0
- algokit_algod_client/models/_application_params.py +63 -0
- algokit_algod_client/models/_application_state_operation.py +41 -0
- algokit_algod_client/models/_application_state_schema.py +22 -0
- algokit_algod_client/models/_asset.py +23 -0
- algokit_algod_client/models/_asset_holding.py +29 -0
- algokit_algod_client/models/_asset_params.py +102 -0
- algokit_algod_client/models/_avm_key_value.py +28 -0
- algokit_algod_client/models/_avm_value.py +32 -0
- algokit_algod_client/models/_block.py +363 -0
- algokit_algod_client/models/_block_hash_response.py +14 -0
- algokit_algod_client/models/_block_txids_response.py +14 -0
- algokit_algod_client/models/_box.py +36 -0
- algokit_algod_client/models/_box_descriptor.py +24 -0
- algokit_algod_client/models/_boxes_response.py +21 -0
- algokit_algod_client/models/_build_version_contains_the_current_algod_build_version_information.py +34 -0
- algokit_algod_client/models/_compile_response.py +24 -0
- algokit_algod_client/models/_disassemble_response.py +14 -0
- algokit_algod_client/models/_error_response.py +22 -0
- algokit_algod_client/models/_eval_delta.py +32 -0
- algokit_algod_client/models/_eval_delta_key_value.py +28 -0
- algokit_algod_client/models/_genesis_file_in_json.py +53 -0
- algokit_algod_client/models/_get_block_time_stamp_offset_response.py +14 -0
- algokit_algod_client/models/_get_sync_round_response.py +14 -0
- algokit_algod_client/models/_ledger_state_delta.py +389 -0
- algokit_algod_client/models/_light_block_header_proof.py +32 -0
- algokit_algod_client/models/_node_status_response.py +118 -0
- algokit_algod_client/models/_pending_transaction_response.py +91 -0
- algokit_algod_client/models/_pending_transactions_response.py +29 -0
- algokit_algod_client/models/_post_transactions_response.py +14 -0
- algokit_algod_client/models/_scratch_change.py +23 -0
- algokit_algod_client/models/_serde_helpers.py +241 -0
- algokit_algod_client/models/_simulate_initial_states.py +25 -0
- algokit_algod_client/models/_simulate_request.py +54 -0
- algokit_algod_client/models/_simulate_request_transaction_group.py +25 -0
- algokit_algod_client/models/_simulate_response.py +44 -0
- algokit_algod_client/models/_simulate_trace_config.py +30 -0
- algokit_algod_client/models/_simulate_transaction_group_result.py +46 -0
- algokit_algod_client/models/_simulate_transaction_result.py +41 -0
- algokit_algod_client/models/_simulate_unnamed_resources_accessed.py +64 -0
- algokit_algod_client/models/_simulation_eval_overrides.py +40 -0
- algokit_algod_client/models/_simulation_opcode_trace_unit.py +55 -0
- algokit_algod_client/models/_simulation_transaction_exec_trace.py +82 -0
- algokit_algod_client/models/_source_map.py +30 -0
- algokit_algod_client/models/_state_delta.py +6 -0
- algokit_algod_client/models/_state_proof.py +28 -0
- algokit_algod_client/models/_state_proof_message.py +44 -0
- algokit_algod_client/models/_supply_response.py +26 -0
- algokit_algod_client/models/_teal_key_value.py +28 -0
- algokit_algod_client/models/_teal_key_value_store.py +6 -0
- algokit_algod_client/models/_teal_value.py +32 -0
- algokit_algod_client/models/_transaction_group_ledger_state_deltas_for_round_response.py +21 -0
- algokit_algod_client/models/_transaction_parameters_response.py +45 -0
- algokit_algod_client/models/_transaction_proof.py +44 -0
- algokit_algod_client/models/_version_contains_the_current_algod_version.py +38 -0
- algokit_algod_client/models/suggested_params.py +42 -0
- algokit_algod_client/py.typed +1 -0
- algokit_algod_client/types.py +7 -0
- algokit_algosdk/__init__.py +38 -0
- algokit_algosdk/account.py +32 -0
- algokit_algosdk/app_access.py +228 -0
- algokit_algosdk/box_reference.py +100 -0
- algokit_algosdk/constants.py +147 -0
- algokit_algosdk/encoding.py +89 -0
- algokit_algosdk/error.py +180 -0
- algokit_algosdk/logic.py +61 -0
- algokit_algosdk/logicsig.py +218 -0
- algokit_algosdk/mnemonic.py +216 -0
- algokit_algosdk/multisig.py +161 -0
- algokit_algosdk/py.typed +0 -0
- algokit_algosdk/transaction.py +596 -0
- algokit_algosdk/wordlist.py +2054 -0
- algokit_common/__init__.py +50 -0
- algokit_common/address.py +34 -0
- algokit_common/constants.py +47 -0
- algokit_common/hashing.py +25 -0
- algokit_common/py.typed +0 -0
- algokit_common/serde/__init__.py +40 -0
- algokit_common/serde/_core.py +610 -0
- algokit_common/serde/_primitives.py +135 -0
- algokit_common/source_map.py +158 -0
- algokit_indexer_client/__init__.py +10 -0
- algokit_indexer_client/client.py +1456 -0
- algokit_indexer_client/config.py +36 -0
- algokit_indexer_client/exceptions.py +59 -0
- algokit_indexer_client/models/__init__.py +148 -0
- algokit_indexer_client/models/_account.py +161 -0
- algokit_indexer_client/models/_account_participation.py +53 -0
- algokit_indexer_client/models/_account_response.py +19 -0
- algokit_indexer_client/models/_account_state_delta.py +29 -0
- algokit_indexer_client/models/_accounts_response.py +29 -0
- algokit_indexer_client/models/_application.py +35 -0
- algokit_indexer_client/models/_application_local_state.py +45 -0
- algokit_indexer_client/models/_application_local_states_response.py +29 -0
- algokit_indexer_client/models/_application_log_data.py +28 -0
- algokit_indexer_client/models/_application_logs_response.py +33 -0
- algokit_indexer_client/models/_application_params.py +62 -0
- algokit_indexer_client/models/_application_response.py +20 -0
- algokit_indexer_client/models/_application_state_schema.py +22 -0
- algokit_indexer_client/models/_applications_response.py +29 -0
- algokit_indexer_client/models/_asset.py +35 -0
- algokit_indexer_client/models/_asset_balances_response.py +29 -0
- algokit_indexer_client/models/_asset_holding.py +41 -0
- algokit_indexer_client/models/_asset_holdings_response.py +29 -0
- algokit_indexer_client/models/_asset_params.py +102 -0
- algokit_indexer_client/models/_asset_response.py +19 -0
- algokit_indexer_client/models/_assets_response.py +29 -0
- algokit_indexer_client/models/_block.py +150 -0
- algokit_indexer_client/models/_block_headers_response.py +29 -0
- algokit_indexer_client/models/_block_rewards.py +38 -0
- algokit_indexer_client/models/_block_upgrade_state.py +34 -0
- algokit_indexer_client/models/_block_upgrade_vote.py +26 -0
- algokit_indexer_client/models/_box.py +36 -0
- algokit_indexer_client/models/_box_descriptor.py +24 -0
- algokit_indexer_client/models/_box_reference.py +28 -0
- algokit_indexer_client/models/_boxes_response.py +29 -0
- algokit_indexer_client/models/_error_response.py +18 -0
- algokit_indexer_client/models/_eval_delta.py +32 -0
- algokit_indexer_client/models/_eval_delta_key_value.py +28 -0
- algokit_indexer_client/models/_hash_factory.py +14 -0
- algokit_indexer_client/models/_hb_proof_fields.py +57 -0
- algokit_indexer_client/models/_health_check.py +42 -0
- algokit_indexer_client/models/_holding_ref.py +23 -0
- algokit_indexer_client/models/_indexer_state_proof_message.py +40 -0
- algokit_indexer_client/models/_locals_ref.py +23 -0
- algokit_indexer_client/models/_merkle_array_proof.py +29 -0
- algokit_indexer_client/models/_mini_asset_holding.py +38 -0
- algokit_indexer_client/models/_on_completion.py +25 -0
- algokit_indexer_client/models/_participation_updates.py +22 -0
- algokit_indexer_client/models/_resource_ref.py +42 -0
- algokit_indexer_client/models/_serde_helpers.py +241 -0
- algokit_indexer_client/models/_state_delta.py +6 -0
- algokit_indexer_client/models/_state_proof_fields.py +57 -0
- algokit_indexer_client/models/_state_proof_participant.py +20 -0
- algokit_indexer_client/models/_state_proof_reveal.py +25 -0
- algokit_indexer_client/models/_state_proof_sig_slot.py +20 -0
- algokit_indexer_client/models/_state_proof_signature.py +37 -0
- algokit_indexer_client/models/_state_proof_tracking.py +32 -0
- algokit_indexer_client/models/_state_proof_verifier.py +24 -0
- algokit_indexer_client/models/_state_schema.py +25 -0
- algokit_indexer_client/models/_teal_key_value.py +28 -0
- algokit_indexer_client/models/_teal_key_value_store.py +6 -0
- algokit_indexer_client/models/_teal_value.py +32 -0
- algokit_indexer_client/models/_transaction.py +213 -0
- algokit_indexer_client/models/_transaction_application.py +105 -0
- algokit_indexer_client/models/_transaction_asset_config.py +31 -0
- algokit_indexer_client/models/_transaction_asset_freeze.py +29 -0
- algokit_indexer_client/models/_transaction_asset_transfer.py +41 -0
- algokit_indexer_client/models/_transaction_heartbeat.py +52 -0
- algokit_indexer_client/models/_transaction_keyreg.py +59 -0
- algokit_indexer_client/models/_transaction_payment.py +33 -0
- algokit_indexer_client/models/_transaction_response.py +19 -0
- algokit_indexer_client/models/_transaction_signature.py +35 -0
- algokit_indexer_client/models/_transaction_signature_logicsig.py +59 -0
- algokit_indexer_client/models/_transaction_signature_multisig.py +36 -0
- algokit_indexer_client/models/_transaction_signature_multisig_subsignature.py +28 -0
- algokit_indexer_client/models/_transaction_state_proof.py +32 -0
- algokit_indexer_client/models/_transactions_response.py +29 -0
- algokit_indexer_client/py.typed +1 -0
- algokit_indexer_client/types.py +7 -0
- algokit_kmd_client/__init__.py +10 -0
- algokit_kmd_client/client.py +1240 -0
- algokit_kmd_client/config.py +36 -0
- algokit_kmd_client/exceptions.py +59 -0
- algokit_kmd_client/models/__init__.py +112 -0
- algokit_kmd_client/models/_classical_signatures.py +4 -0
- algokit_kmd_client/models/_create_wallet_request.py +30 -0
- algokit_kmd_client/models/_create_wallet_response.py +19 -0
- algokit_kmd_client/models/_delete_key_request.py +27 -0
- algokit_kmd_client/models/_delete_multisig_request.py +27 -0
- algokit_kmd_client/models/_digest_represents_a32_byte_value_holding_the256_bit_hash_digest.py +4 -0
- algokit_kmd_client/models/_ed25519_public_key.py +4 -0
- algokit_kmd_client/models/_export_key_request.py +27 -0
- algokit_kmd_client/models/_export_key_response.py +24 -0
- algokit_kmd_client/models/_export_master_key_request.py +22 -0
- algokit_kmd_client/models/_export_master_key_response.py +18 -0
- algokit_kmd_client/models/_export_multisig_request.py +23 -0
- algokit_kmd_client/models/_export_multisig_response.py +26 -0
- algokit_kmd_client/models/_generate_key_request.py +18 -0
- algokit_kmd_client/models/_generate_key_response.py +19 -0
- algokit_kmd_client/models/_import_key_request.py +28 -0
- algokit_kmd_client/models/_import_key_response.py +19 -0
- algokit_kmd_client/models/_import_multisig_request.py +30 -0
- algokit_kmd_client/models/_import_multisig_response.py +19 -0
- algokit_kmd_client/models/_init_wallet_handle_token_request.py +22 -0
- algokit_kmd_client/models/_init_wallet_handle_token_response.py +18 -0
- algokit_kmd_client/models/_list_keys_request.py +18 -0
- algokit_kmd_client/models/_list_keys_response.py +18 -0
- algokit_kmd_client/models/_list_multisig_request.py +18 -0
- algokit_kmd_client/models/_list_multisig_response.py +18 -0
- algokit_kmd_client/models/_list_wallets_request.py +11 -0
- algokit_kmd_client/models/_list_wallets_response.py +25 -0
- algokit_kmd_client/models/_master_derivation_key.py +4 -0
- algokit_kmd_client/models/_multisig_sig.py +33 -0
- algokit_kmd_client/models/_multisig_subsig.py +23 -0
- algokit_kmd_client/models/_public_key.py +4 -0
- algokit_kmd_client/models/_release_wallet_handle_token_request.py +18 -0
- algokit_kmd_client/models/_rename_wallet_request.py +26 -0
- algokit_kmd_client/models/_rename_wallet_response.py +19 -0
- algokit_kmd_client/models/_renew_wallet_handle_token_request.py +18 -0
- algokit_kmd_client/models/_renew_wallet_handle_token_response.py +19 -0
- algokit_kmd_client/models/_serde_helpers.py +241 -0
- algokit_kmd_client/models/_sign_multisig_response.py +24 -0
- algokit_kmd_client/models/_sign_multisig_txn_request.py +45 -0
- algokit_kmd_client/models/_sign_program_multisig_request.py +50 -0
- algokit_kmd_client/models/_sign_program_multisig_response.py +24 -0
- algokit_kmd_client/models/_sign_program_request.py +37 -0
- algokit_kmd_client/models/_sign_program_response.py +24 -0
- algokit_kmd_client/models/_sign_transaction_response.py +24 -0
- algokit_kmd_client/models/_sign_txn_request.py +36 -0
- algokit_kmd_client/models/_signature.py +4 -0
- algokit_kmd_client/models/_tx_type.py +4 -0
- algokit_kmd_client/models/_versions_request.py +11 -0
- algokit_kmd_client/models/_versions_response.py +19 -0
- algokit_kmd_client/models/_wallet.py +38 -0
- algokit_kmd_client/models/_wallet_handle.py +24 -0
- algokit_kmd_client/models/_wallet_info_request.py +18 -0
- algokit_kmd_client/models/_wallet_info_response.py +19 -0
- algokit_kmd_client/py.typed +1 -0
- algokit_kmd_client/types.py +7 -0
- algokit_transact/__init__.py +190 -0
- algokit_transact/codec/__init__.py +0 -0
- algokit_transact/codec/msgpack.py +11 -0
- algokit_transact/codec/serde.py +7 -0
- algokit_transact/codec/signed.py +57 -0
- algokit_transact/codec/transaction.py +65 -0
- algokit_transact/exceptions.py +17 -0
- algokit_transact/logicsig.py +220 -0
- algokit_transact/models/__init__.py +0 -0
- algokit_transact/models/app_call.py +447 -0
- algokit_transact/models/asset_config.py +19 -0
- algokit_transact/models/asset_freeze.py +11 -0
- algokit_transact/models/asset_transfer.py +13 -0
- algokit_transact/models/common.py +17 -0
- algokit_transact/models/heartbeat.py +21 -0
- algokit_transact/models/key_registration.py +14 -0
- algokit_transact/models/payment.py +14 -0
- algokit_transact/models/signed_transaction.py +21 -0
- algokit_transact/models/state_proof.py +150 -0
- algokit_transact/models/transaction.py +88 -0
- algokit_transact/multisig.py +93 -0
- algokit_transact/ops/__init__.py +0 -0
- algokit_transact/ops/fees.py +47 -0
- algokit_transact/ops/group.py +28 -0
- algokit_transact/ops/ids.py +14 -0
- algokit_transact/ops/validate.py +503 -0
- algokit_transact/py.typed +0 -0
- algokit_transact/signer.py +195 -0
- algokit_transact/signing/__init__.py +0 -0
- algokit_transact/signing/logic_signature.py +19 -0
- algokit_transact/signing/multisig.py +84 -0
- algokit_transact/signing/types.py +39 -0
- algokit_transact/signing/validation.py +63 -0
- algokit_utils/__init__.py +23 -0
- algokit_utils/_debugging.py +304 -0
- algokit_utils/accounts/__init__.py +2 -0
- algokit_utils/accounts/account_manager.py +1051 -0
- algokit_utils/accounts/kmd_account_manager.py +206 -0
- algokit_utils/algo25.py +46 -0
- algokit_utils/algorand.py +383 -0
- algokit_utils/applications/__init__.py +7 -0
- algokit_utils/applications/abi.py +280 -0
- algokit_utils/applications/app_client.py +2193 -0
- algokit_utils/applications/app_deployer.py +788 -0
- algokit_utils/applications/app_factory.py +1140 -0
- algokit_utils/applications/app_manager.py +575 -0
- algokit_utils/applications/app_spec/__init__.py +6 -0
- algokit_utils/applications/enums.py +40 -0
- algokit_utils/assets/__init__.py +1 -0
- algokit_utils/assets/asset_manager.py +344 -0
- algokit_utils/clients/__init__.py +41 -0
- algokit_utils/clients/client_manager.py +756 -0
- algokit_utils/clients/dispenser_api_client.py +212 -0
- algokit_utils/common.py +40 -0
- algokit_utils/config.py +159 -0
- algokit_utils/errors/__init__.py +1 -0
- algokit_utils/errors/logic_error.py +160 -0
- algokit_utils/models/__init__.py +7 -0
- algokit_utils/models/account.py +12 -0
- algokit_utils/models/amount.py +198 -0
- algokit_utils/models/application.py +90 -0
- algokit_utils/models/network.py +29 -0
- algokit_utils/models/simulate.py +7 -0
- algokit_utils/models/state.py +53 -0
- algokit_utils/models/transaction.py +49 -0
- algokit_utils/protocols/__init__.py +3 -0
- algokit_utils/protocols/account.py +11 -0
- algokit_utils/protocols/signer.py +17 -0
- algokit_utils/protocols/typed_clients.py +110 -0
- algokit_utils/py.typed +0 -0
- algokit_utils/transact.py +195 -0
- algokit_utils/transactions/__init__.py +3 -0
- algokit_utils/transactions/builders/__init__.py +67 -0
- algokit_utils/transactions/builders/app.py +248 -0
- algokit_utils/transactions/builders/asset.py +256 -0
- algokit_utils/transactions/builders/common.py +263 -0
- algokit_utils/transactions/builders/keyreg.py +103 -0
- algokit_utils/transactions/builders/method_call.py +380 -0
- algokit_utils/transactions/builders/payment.py +43 -0
- algokit_utils/transactions/composer_resources.py +409 -0
- algokit_utils/transactions/fee_coverage.py +79 -0
- algokit_utils/transactions/helpers.py +9 -0
- algokit_utils/transactions/transaction_composer.py +1574 -0
- algokit_utils/transactions/transaction_creator.py +699 -0
- algokit_utils/transactions/transaction_sender.py +1240 -0
- algokit_utils/transactions/types.py +262 -0
- algokit_utils-5.0.0a3.dist-info/METADATA +105 -0
- algokit_utils-5.0.0a3.dist-info/RECORD +337 -0
- algokit_utils-5.0.0a3.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
from collections.abc import Sequence
|
|
3
|
+
from typing import Protocol
|
|
4
|
+
|
|
5
|
+
from typing_extensions import Self
|
|
6
|
+
|
|
7
|
+
from algokit_common import address_from_public_key, public_key_from_address, sha512_256
|
|
8
|
+
from algokit_common.constants import (
|
|
9
|
+
LOGIC_DATA_DOMAIN_SEPARATOR,
|
|
10
|
+
MULTISIG_PROGRAM_DOMAIN_SEPARATOR,
|
|
11
|
+
PROGRAM_DOMAIN_SEPARATOR,
|
|
12
|
+
)
|
|
13
|
+
from algokit_transact.codec.signed import encode_signed_transaction
|
|
14
|
+
from algokit_transact.models.signed_transaction import SignedTransaction
|
|
15
|
+
from algokit_transact.models.transaction import Transaction as AlgokitTransaction
|
|
16
|
+
from algokit_transact.signer import (
|
|
17
|
+
AddressWithSigners,
|
|
18
|
+
DelegatedLsigSigner,
|
|
19
|
+
ProgramDataSigner,
|
|
20
|
+
TransactionSigner,
|
|
21
|
+
)
|
|
22
|
+
from algokit_transact.signing.logic_signature import LogicSignature
|
|
23
|
+
from algokit_transact.signing.multisig import (
|
|
24
|
+
address_from_multisig_signature,
|
|
25
|
+
apply_multisig_subsignature,
|
|
26
|
+
new_multisig_signature,
|
|
27
|
+
)
|
|
28
|
+
from algokit_transact.signing.types import MultisigSignature
|
|
29
|
+
from algokit_transact.signing.validation import sanity_check_program
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
"LogicSigAccount",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class _MultisigMetadataProtocol(Protocol):
|
|
37
|
+
version: int
|
|
38
|
+
threshold: int
|
|
39
|
+
addrs: list[str]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class _MultisigAccountProtocol(Protocol):
|
|
43
|
+
@property
|
|
44
|
+
def address(self) -> str: ...
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclasses.dataclass(kw_only=True)
|
|
48
|
+
class LogicSigAccount:
|
|
49
|
+
"""Account wrapper for LogicSig signing. Supports delegation including secretless signing."""
|
|
50
|
+
|
|
51
|
+
_program: bytes
|
|
52
|
+
_args: list[bytes] | None
|
|
53
|
+
_signature: bytes | None
|
|
54
|
+
_multisig_signature: MultisigSignature | None
|
|
55
|
+
_delegated_address: str | None
|
|
56
|
+
_signer: TransactionSigner | None
|
|
57
|
+
|
|
58
|
+
def __init__(self, program: bytes, args: list[bytes] | None = None) -> None:
|
|
59
|
+
sanity_check_program(program)
|
|
60
|
+
self._program = program
|
|
61
|
+
self._args = args
|
|
62
|
+
self._signature = None
|
|
63
|
+
self._multisig_signature = None
|
|
64
|
+
self._delegated_address = None
|
|
65
|
+
self._signer = None
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def program(self) -> bytes:
|
|
69
|
+
"""The LogicSig program bytes."""
|
|
70
|
+
return self._program
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def args(self) -> list[bytes] | None:
|
|
74
|
+
"""The arguments to pass to the LogicSig program."""
|
|
75
|
+
return self._args
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def is_delegated(self) -> bool:
|
|
79
|
+
"""Whether this LogicSig is delegated to an account."""
|
|
80
|
+
return self._signature is not None or self._multisig_signature is not None
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def address(self) -> str:
|
|
84
|
+
"""The LogicSig account address (delegated address or escrow address)."""
|
|
85
|
+
if self._delegated_address is not None:
|
|
86
|
+
return self._delegated_address
|
|
87
|
+
|
|
88
|
+
program_hash = sha512_256(PROGRAM_DOMAIN_SEPARATOR.encode() + self._program)
|
|
89
|
+
return address_from_public_key(program_hash)
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def addr(self) -> str:
|
|
93
|
+
"""Alias for address property (matching TypeScript's get addr())."""
|
|
94
|
+
return self.address
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def signer(self) -> TransactionSigner:
|
|
98
|
+
"""Transaction signer callable."""
|
|
99
|
+
if self._signer is None:
|
|
100
|
+
self._signer = self._create_logic_sig_signer()
|
|
101
|
+
return self._signer
|
|
102
|
+
|
|
103
|
+
def _create_logic_sig_signer(self) -> TransactionSigner:
|
|
104
|
+
program = self._program
|
|
105
|
+
args = list(self._args) if self._args else None
|
|
106
|
+
signature = self._signature
|
|
107
|
+
multisig_sig = self._multisig_signature
|
|
108
|
+
lsig_address = self.address
|
|
109
|
+
|
|
110
|
+
def signer(txn_group: Sequence[AlgokitTransaction], indexes_to_sign: Sequence[int]) -> list[bytes]:
|
|
111
|
+
blobs: list[bytes] = []
|
|
112
|
+
for index in indexes_to_sign:
|
|
113
|
+
txn = txn_group[index]
|
|
114
|
+
logic_sig = LogicSignature(
|
|
115
|
+
logic=program,
|
|
116
|
+
args=args,
|
|
117
|
+
sig=signature,
|
|
118
|
+
msig=multisig_sig,
|
|
119
|
+
)
|
|
120
|
+
auth_addr = lsig_address if txn.sender != lsig_address else None
|
|
121
|
+
|
|
122
|
+
signed = SignedTransaction(
|
|
123
|
+
txn=txn,
|
|
124
|
+
sig=None,
|
|
125
|
+
msig=None,
|
|
126
|
+
lsig=logic_sig,
|
|
127
|
+
auth_address=auth_addr,
|
|
128
|
+
)
|
|
129
|
+
blobs.append(encode_signed_transaction(signed))
|
|
130
|
+
return blobs
|
|
131
|
+
|
|
132
|
+
return signer
|
|
133
|
+
|
|
134
|
+
def bytes_to_sign_for_delegation(self, msig: _MultisigAccountProtocol | None = None) -> bytes:
|
|
135
|
+
"""Returns bytes to sign for delegation.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
msig: Optional multisig account for multisig delegation.
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
The bytes that need to be signed for delegation.
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
if msig is not None:
|
|
145
|
+
msig_public_key = public_key_from_address(msig.address)
|
|
146
|
+
return MULTISIG_PROGRAM_DOMAIN_SEPARATOR.encode() + msig_public_key + self._program
|
|
147
|
+
return PROGRAM_DOMAIN_SEPARATOR.encode() + self._program
|
|
148
|
+
|
|
149
|
+
def program_data_to_sign(self, data: bytes) -> bytes:
|
|
150
|
+
"""Returns bytes to sign for program data.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
data: The data to sign.
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
The bytes that need to be signed (ProgData + program_address + data).
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
program_address = public_key_from_address(self.address)
|
|
160
|
+
return LOGIC_DATA_DOMAIN_SEPARATOR.encode() + program_address + data
|
|
161
|
+
|
|
162
|
+
def sign_program_data(self, data: bytes, signer: ProgramDataSigner) -> bytes:
|
|
163
|
+
"""Signs program data with given signer.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
data: The data to sign.
|
|
167
|
+
signer: The program data signer to use.
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
The signature bytes.
|
|
171
|
+
"""
|
|
172
|
+
program_address = public_key_from_address(self.address)
|
|
173
|
+
return signer(data, program_address)
|
|
174
|
+
|
|
175
|
+
def delegate(self, signer: DelegatedLsigSigner, delegating_address: str | None = None) -> Self:
|
|
176
|
+
"""Delegate this LogicSig to a single account. Returns self for chaining.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
signer: The DelegatedLsigSigner callback to sign the program.
|
|
180
|
+
delegating_address: Optional address of the delegating account.
|
|
181
|
+
If not provided, the address must be set separately or
|
|
182
|
+
the LogicSig will use the escrow address.
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
Self for method chaining.
|
|
186
|
+
"""
|
|
187
|
+
self._signature = signer(self._program, None)
|
|
188
|
+
self._delegated_address = delegating_address
|
|
189
|
+
self._multisig_signature = None
|
|
190
|
+
self._signer = None
|
|
191
|
+
return self
|
|
192
|
+
|
|
193
|
+
def delegate_multisig(
|
|
194
|
+
self,
|
|
195
|
+
multisig_params: _MultisigMetadataProtocol,
|
|
196
|
+
signing_accounts: Sequence[AddressWithSigners],
|
|
197
|
+
) -> Self:
|
|
198
|
+
"""Delegate this LogicSig to a multisig account. Returns self for chaining."""
|
|
199
|
+
msig = new_multisig_signature(
|
|
200
|
+
multisig_params.version,
|
|
201
|
+
multisig_params.threshold,
|
|
202
|
+
multisig_params.addrs,
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
msig_address = address_from_multisig_signature(msig)
|
|
206
|
+
msig_public_key = public_key_from_address(msig_address)
|
|
207
|
+
|
|
208
|
+
address_to_signer = {account.addr: account.delegated_lsig_signer for account in signing_accounts}
|
|
209
|
+
|
|
210
|
+
for subsig in msig.subsigs:
|
|
211
|
+
subsig_addr = address_from_public_key(subsig.public_key)
|
|
212
|
+
if subsig_addr in address_to_signer:
|
|
213
|
+
signature = address_to_signer[subsig_addr](self._program, msig_public_key)
|
|
214
|
+
msig = apply_multisig_subsignature(msig, subsig_addr, signature)
|
|
215
|
+
|
|
216
|
+
self._multisig_signature = msig
|
|
217
|
+
self._delegated_address = address_from_multisig_signature(msig)
|
|
218
|
+
self._signature = None
|
|
219
|
+
self._signer = None
|
|
220
|
+
return self
|
|
File without changes
|
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
from collections.abc import Iterable, Mapping, Sequence
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from typing import Literal, cast
|
|
4
|
+
|
|
5
|
+
from algokit_common.address import address_from_public_key
|
|
6
|
+
from algokit_common.constants import ZERO_ADDRESS
|
|
7
|
+
from algokit_transact.codec.serde import (
|
|
8
|
+
DecodeError,
|
|
9
|
+
EncodeError,
|
|
10
|
+
addr,
|
|
11
|
+
addr_seq,
|
|
12
|
+
bytes_seq,
|
|
13
|
+
enum_value,
|
|
14
|
+
int_seq,
|
|
15
|
+
nested,
|
|
16
|
+
to_wire,
|
|
17
|
+
wire,
|
|
18
|
+
)
|
|
19
|
+
from algokit_transact.models.common import OnApplicationComplete, StateSchema
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _decode_address_field(value: object) -> str:
|
|
23
|
+
"""Decode address from wire format (can be bytes or string)"""
|
|
24
|
+
if isinstance(value, str):
|
|
25
|
+
return value
|
|
26
|
+
if isinstance(value, bytes):
|
|
27
|
+
# The API returns address as UTF-8 encoded string, not public key bytes
|
|
28
|
+
return value.decode("utf-8")
|
|
29
|
+
raise DecodeError(f"Invalid address format: {type(value)}")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(slots=True, frozen=True)
|
|
33
|
+
class BoxReference:
|
|
34
|
+
app_id: int = field(default=0, metadata=wire("app"))
|
|
35
|
+
name: bytes = field(default=b"", metadata=wire("name"))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass(slots=True, frozen=True)
|
|
39
|
+
class HoldingReference:
|
|
40
|
+
asset_id: int = field(metadata=wire("asset"))
|
|
41
|
+
address: str = field(metadata=wire("account", decode=_decode_address_field))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass(slots=True, frozen=True)
|
|
45
|
+
class LocalsReference:
|
|
46
|
+
app_id: int = field(metadata=wire("app"))
|
|
47
|
+
address: str = field(metadata=wire("account", decode=_decode_address_field))
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass(slots=True, frozen=True)
|
|
51
|
+
class ResourceReference:
|
|
52
|
+
address: str | None = None
|
|
53
|
+
app_id: int | None = None
|
|
54
|
+
asset_id: int | None = None
|
|
55
|
+
holding: HoldingReference | None = None
|
|
56
|
+
locals: LocalsReference | None = None
|
|
57
|
+
box: BoxReference | None = None
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass(slots=True, frozen=True)
|
|
61
|
+
class _WireBoxReference:
|
|
62
|
+
index: int
|
|
63
|
+
name: bytes
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@dataclass(slots=True, frozen=True)
|
|
67
|
+
class _AddressAccessEntry:
|
|
68
|
+
address: str = field(metadata=addr("d", omit_if_none=False))
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
ByteLike = bytes | bytearray | memoryview
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _coerce_bytes(payload: object) -> bytes | None:
|
|
75
|
+
if isinstance(payload, ByteLike):
|
|
76
|
+
return bytes(payload)
|
|
77
|
+
if isinstance(payload, Sequence) and not isinstance(payload, str | bytes | bytearray | memoryview):
|
|
78
|
+
ints: list[int] = []
|
|
79
|
+
for part in payload:
|
|
80
|
+
if isinstance(part, bool):
|
|
81
|
+
ints.append(int(part))
|
|
82
|
+
continue
|
|
83
|
+
if isinstance(part, int):
|
|
84
|
+
ints.append(part)
|
|
85
|
+
continue
|
|
86
|
+
return None
|
|
87
|
+
try:
|
|
88
|
+
return bytes(ints)
|
|
89
|
+
except ValueError:
|
|
90
|
+
return None
|
|
91
|
+
return None
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _require_bytes(payload: object | None, context: str) -> bytes:
|
|
95
|
+
data = _coerce_bytes(payload) if payload is not None else None
|
|
96
|
+
if data is None:
|
|
97
|
+
raise EncodeError(f"{context} must be bytes-like")
|
|
98
|
+
return data
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _encode_box_references(
|
|
102
|
+
app_call: "AppCallTransactionFields",
|
|
103
|
+
value: object,
|
|
104
|
+
) -> list[dict[str, object]] | None:
|
|
105
|
+
if value is None:
|
|
106
|
+
return None
|
|
107
|
+
if not isinstance(value, Iterable):
|
|
108
|
+
raise EncodeError("Box references must be iterable")
|
|
109
|
+
raw_refs = _coerce_box_sequence(cast(Iterable[BoxReference | _WireBoxReference], value))
|
|
110
|
+
if not raw_refs:
|
|
111
|
+
return None
|
|
112
|
+
|
|
113
|
+
app_refs = app_call.app_references or []
|
|
114
|
+
encoded: list[dict[str, object]] = []
|
|
115
|
+
for raw in raw_refs:
|
|
116
|
+
ref = _normalize_box_reference(app_call, raw)
|
|
117
|
+
index = _map_box_app_id_to_index(ref.app_id, app_call, app_refs)
|
|
118
|
+
encoded.append({"i": index, "n": ref.name})
|
|
119
|
+
return encoded
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _decode_box_references(value: object) -> list[_WireBoxReference] | None:
|
|
123
|
+
if value is None:
|
|
124
|
+
return None
|
|
125
|
+
if isinstance(value, list):
|
|
126
|
+
entries: list[_WireBoxReference] = []
|
|
127
|
+
for item in value:
|
|
128
|
+
if not isinstance(item, Mapping):
|
|
129
|
+
continue
|
|
130
|
+
index = int(item.get("i", 0))
|
|
131
|
+
name_payload = item.get("n", b"")
|
|
132
|
+
name = _coerce_bytes(name_payload) or b""
|
|
133
|
+
entries.append(_WireBoxReference(index=index, name=name))
|
|
134
|
+
return list(entries) if entries else None
|
|
135
|
+
return None
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _encode_access_references(
|
|
139
|
+
app_call: "AppCallTransactionFields",
|
|
140
|
+
value: object,
|
|
141
|
+
) -> list[dict[str, object]] | None:
|
|
142
|
+
if value is None:
|
|
143
|
+
return None
|
|
144
|
+
if not isinstance(value, Iterable):
|
|
145
|
+
raise EncodeError("Access references must be iterable")
|
|
146
|
+
raw_refs = _coerce_resource_sequence(cast(Iterable[ResourceReference], value))
|
|
147
|
+
if not raw_refs:
|
|
148
|
+
return None
|
|
149
|
+
|
|
150
|
+
builder = _AccessListBuilder(app_call)
|
|
151
|
+
for ref in (_normalize_resource_reference(item) for item in raw_refs):
|
|
152
|
+
builder.add(ref)
|
|
153
|
+
|
|
154
|
+
return builder.entries or None
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _decode_access_references(value: object) -> list[ResourceReference] | None: # noqa: C901, PLR0912, PLR0915
|
|
158
|
+
if value is None:
|
|
159
|
+
return None
|
|
160
|
+
if not isinstance(value, list):
|
|
161
|
+
return None
|
|
162
|
+
|
|
163
|
+
result: list[ResourceReference] = []
|
|
164
|
+
for item in value:
|
|
165
|
+
if not isinstance(item, Mapping):
|
|
166
|
+
continue
|
|
167
|
+
if "d" in item:
|
|
168
|
+
address_raw = item.get("d")
|
|
169
|
+
address_bytes = _coerce_bytes(address_raw)
|
|
170
|
+
if address_bytes is None:
|
|
171
|
+
continue
|
|
172
|
+
result.append(ResourceReference(address=address_from_public_key(address_bytes)))
|
|
173
|
+
continue
|
|
174
|
+
if "s" in item:
|
|
175
|
+
asset_raw = item.get("s")
|
|
176
|
+
if not isinstance(asset_raw, int):
|
|
177
|
+
continue
|
|
178
|
+
result.append(ResourceReference(asset_id=asset_raw))
|
|
179
|
+
continue
|
|
180
|
+
if "p" in item:
|
|
181
|
+
app_raw = item.get("p")
|
|
182
|
+
if not isinstance(app_raw, int):
|
|
183
|
+
continue
|
|
184
|
+
result.append(ResourceReference(app_id=app_raw))
|
|
185
|
+
continue
|
|
186
|
+
if "h" in item:
|
|
187
|
+
holding_payload = item.get("h")
|
|
188
|
+
if not isinstance(holding_payload, Mapping):
|
|
189
|
+
continue
|
|
190
|
+
asset_index_raw = holding_payload.get("s")
|
|
191
|
+
if not isinstance(asset_index_raw, int):
|
|
192
|
+
raise DecodeError("Holding missing asset index")
|
|
193
|
+
asset_index = asset_index_raw
|
|
194
|
+
if asset_index <= 0 or asset_index > len(result):
|
|
195
|
+
raise DecodeError("Holding asset index out of bounds")
|
|
196
|
+
asset_entry = result[asset_index - 1]
|
|
197
|
+
if asset_entry.asset_id is None:
|
|
198
|
+
raise DecodeError("Holding asset index does not reference an asset")
|
|
199
|
+
address_index_raw = holding_payload.get("d")
|
|
200
|
+
address_index = int(address_index_raw) if isinstance(address_index_raw, int) else 0
|
|
201
|
+
if address_index == 0:
|
|
202
|
+
address = ZERO_ADDRESS
|
|
203
|
+
else:
|
|
204
|
+
if address_index > len(result):
|
|
205
|
+
raise DecodeError("Holding address index out of bounds")
|
|
206
|
+
address_entry = result[address_index - 1]
|
|
207
|
+
if address_entry.address is None:
|
|
208
|
+
raise DecodeError("Holding address index does not reference an address")
|
|
209
|
+
address = address_entry.address
|
|
210
|
+
result.append(ResourceReference(holding=HoldingReference(asset_id=asset_entry.asset_id, address=address)))
|
|
211
|
+
continue
|
|
212
|
+
if "l" in item:
|
|
213
|
+
locals_payload = item.get("l")
|
|
214
|
+
if not isinstance(locals_payload, Mapping):
|
|
215
|
+
continue
|
|
216
|
+
address_index_raw = locals_payload.get("d")
|
|
217
|
+
address_index = int(address_index_raw) if isinstance(address_index_raw, int) else 0
|
|
218
|
+
if address_index == 0:
|
|
219
|
+
address = ZERO_ADDRESS
|
|
220
|
+
else:
|
|
221
|
+
if address_index > len(result):
|
|
222
|
+
raise DecodeError("Locals address index out of bounds")
|
|
223
|
+
address_entry = result[address_index - 1]
|
|
224
|
+
if address_entry.address is None:
|
|
225
|
+
raise DecodeError("Locals address index does not reference an address")
|
|
226
|
+
address = address_entry.address
|
|
227
|
+
app_index_raw = locals_payload.get("p")
|
|
228
|
+
app_index = int(app_index_raw) if isinstance(app_index_raw, int) else 0
|
|
229
|
+
if app_index == 0:
|
|
230
|
+
app_id = 0
|
|
231
|
+
else:
|
|
232
|
+
if app_index > len(result):
|
|
233
|
+
raise DecodeError("Locals app index out of bounds")
|
|
234
|
+
app_entry = result[app_index - 1]
|
|
235
|
+
if app_entry.app_id is None:
|
|
236
|
+
raise DecodeError("Locals app index does not reference an app")
|
|
237
|
+
app_id = app_entry.app_id
|
|
238
|
+
result.append(ResourceReference(locals=LocalsReference(app_id=app_id, address=address)))
|
|
239
|
+
continue
|
|
240
|
+
if "b" in item:
|
|
241
|
+
box_payload = item.get("b")
|
|
242
|
+
if not isinstance(box_payload, Mapping):
|
|
243
|
+
continue
|
|
244
|
+
name_raw = box_payload.get("n")
|
|
245
|
+
name = _coerce_bytes(name_raw)
|
|
246
|
+
if name is None:
|
|
247
|
+
raise DecodeError("Box missing name")
|
|
248
|
+
app_index_raw = box_payload.get("i")
|
|
249
|
+
app_index = int(app_index_raw) if isinstance(app_index_raw, int) else 0
|
|
250
|
+
if app_index == 0:
|
|
251
|
+
app_id = 0
|
|
252
|
+
else:
|
|
253
|
+
if app_index > len(result):
|
|
254
|
+
raise DecodeError("Box app index out of bounds")
|
|
255
|
+
app_entry = result[app_index - 1]
|
|
256
|
+
if app_entry.app_id is None:
|
|
257
|
+
raise DecodeError("Box app index does not reference an app")
|
|
258
|
+
app_id = app_entry.app_id
|
|
259
|
+
result.append(ResourceReference(box=BoxReference(app_id=app_id, name=name)))
|
|
260
|
+
|
|
261
|
+
return list(result) if result else None
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
@dataclass(slots=True, frozen=True)
|
|
265
|
+
class AppCallTransactionFields:
|
|
266
|
+
app_id: int = field(default=0, metadata=wire("apid"))
|
|
267
|
+
on_complete: OnApplicationComplete = field(
|
|
268
|
+
default=OnApplicationComplete.NoOp, metadata=enum_value("apan", OnApplicationComplete)
|
|
269
|
+
)
|
|
270
|
+
approval_program: bytes | None = field(default=None, metadata=wire("apap"))
|
|
271
|
+
clear_state_program: bytes | None = field(default=None, metadata=wire("apsu"))
|
|
272
|
+
global_state_schema: StateSchema | None = field(default=None, metadata=nested("apgs", StateSchema))
|
|
273
|
+
local_state_schema: StateSchema | None = field(default=None, metadata=nested("apls", StateSchema))
|
|
274
|
+
args: list[bytes] | None = field(default=None, metadata=bytes_seq("apaa"))
|
|
275
|
+
account_references: list[str] | None = field(default=None, metadata=addr_seq("apat"))
|
|
276
|
+
app_references: list[int] | None = field(default=None, metadata=int_seq("apfa"))
|
|
277
|
+
asset_references: list[int] | None = field(default=None, metadata=int_seq("apas"))
|
|
278
|
+
extra_program_pages: int | None = field(default=None, metadata=wire("apep"))
|
|
279
|
+
reject_version: int | None = field(default=None, metadata=wire("aprv"))
|
|
280
|
+
box_references: list[BoxReference] | None = field(
|
|
281
|
+
default=None,
|
|
282
|
+
metadata=wire(
|
|
283
|
+
"apbx",
|
|
284
|
+
encode=_encode_box_references,
|
|
285
|
+
decode=_decode_box_references,
|
|
286
|
+
pass_obj=True,
|
|
287
|
+
),
|
|
288
|
+
)
|
|
289
|
+
access_references: list[ResourceReference] | None = field(
|
|
290
|
+
default=None,
|
|
291
|
+
metadata=wire(
|
|
292
|
+
"al",
|
|
293
|
+
encode=_encode_access_references,
|
|
294
|
+
decode=_decode_access_references,
|
|
295
|
+
pass_obj=True,
|
|
296
|
+
),
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
def __post_init__(self) -> None:
|
|
300
|
+
if self.box_references:
|
|
301
|
+
normalized_boxes = [
|
|
302
|
+
_normalize_box_reference(self, item) for item in _coerce_box_sequence(self.box_references)
|
|
303
|
+
]
|
|
304
|
+
object.__setattr__(self, "box_references", normalized_boxes or None)
|
|
305
|
+
if self.access_references:
|
|
306
|
+
normalized_access = [
|
|
307
|
+
_normalize_resource_reference(item) for item in _coerce_resource_sequence(self.access_references)
|
|
308
|
+
]
|
|
309
|
+
object.__setattr__(self, "access_references", normalized_access or None)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def _coerce_box_sequence(
|
|
313
|
+
boxes: Iterable[BoxReference | _WireBoxReference],
|
|
314
|
+
) -> list[BoxReference | _WireBoxReference]:
|
|
315
|
+
if isinstance(boxes, list):
|
|
316
|
+
return boxes
|
|
317
|
+
return list(boxes)
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def _coerce_resource_sequence(
|
|
321
|
+
resources: Iterable[ResourceReference],
|
|
322
|
+
) -> list[ResourceReference]:
|
|
323
|
+
if isinstance(resources, list):
|
|
324
|
+
return resources
|
|
325
|
+
return list(resources)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def _normalize_box_reference(
|
|
329
|
+
app_call: AppCallTransactionFields,
|
|
330
|
+
ref: BoxReference | _WireBoxReference,
|
|
331
|
+
) -> BoxReference:
|
|
332
|
+
if isinstance(ref, BoxReference):
|
|
333
|
+
return ref
|
|
334
|
+
if isinstance(ref, _WireBoxReference):
|
|
335
|
+
app_id = _map_box_index_to_app_id(ref.index, app_call)
|
|
336
|
+
return BoxReference(app_id=app_id, name=ref.name)
|
|
337
|
+
raise TypeError("Unsupported box reference payload")
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def _normalize_resource_reference(ref: ResourceReference) -> ResourceReference:
|
|
341
|
+
if isinstance(ref, ResourceReference):
|
|
342
|
+
return ref
|
|
343
|
+
raise TypeError("Unsupported resource reference payload")
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def _map_box_index_to_app_id(index: int, app_call: AppCallTransactionFields) -> int:
|
|
347
|
+
if index == 0:
|
|
348
|
+
return app_call.app_id
|
|
349
|
+
app_refs = app_call.app_references or []
|
|
350
|
+
pos = index - 1
|
|
351
|
+
if pos < 0 or pos >= len(app_refs):
|
|
352
|
+
raise DecodeError("Box reference index is out of bounds for application references")
|
|
353
|
+
return app_refs[pos]
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def _map_box_app_id_to_index(
|
|
357
|
+
app_id: int,
|
|
358
|
+
app_call: AppCallTransactionFields,
|
|
359
|
+
app_refs: list[int],
|
|
360
|
+
) -> int:
|
|
361
|
+
if app_id in (0, app_call.app_id):
|
|
362
|
+
return 0
|
|
363
|
+
try:
|
|
364
|
+
pos = app_refs.index(app_id)
|
|
365
|
+
except ValueError as exc:
|
|
366
|
+
raise EncodeError("Box reference app id must exist in application references") from exc
|
|
367
|
+
return pos + 1
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
class _AccessListBuilder:
|
|
371
|
+
def __init__(self, app_call: "AppCallTransactionFields") -> None:
|
|
372
|
+
self._app_call = app_call
|
|
373
|
+
self._entries: list[dict[str, object]] = []
|
|
374
|
+
|
|
375
|
+
@property
|
|
376
|
+
def entries(self) -> list[dict[str, object]]:
|
|
377
|
+
return self._entries
|
|
378
|
+
|
|
379
|
+
def add(self, ref: ResourceReference) -> None:
|
|
380
|
+
if self._register_direct(ref):
|
|
381
|
+
return
|
|
382
|
+
if self._register_holding(ref):
|
|
383
|
+
return
|
|
384
|
+
if self._register_locals(ref):
|
|
385
|
+
return
|
|
386
|
+
self._register_box(ref)
|
|
387
|
+
|
|
388
|
+
def ensure(self, target: ResourceReference) -> int:
|
|
389
|
+
if target.address:
|
|
390
|
+
address_entry = to_wire(_AddressAccessEntry(address=target.address))
|
|
391
|
+
encoded_address = _require_bytes(address_entry.get("d"), "Address access entry")
|
|
392
|
+
return self._ensure_entry("d", encoded_address)
|
|
393
|
+
|
|
394
|
+
if target.asset_id is not None:
|
|
395
|
+
return self._ensure_entry("s", int(target.asset_id))
|
|
396
|
+
|
|
397
|
+
if target.app_id is not None:
|
|
398
|
+
return self._ensure_entry("p", int(target.app_id))
|
|
399
|
+
|
|
400
|
+
return len(self._entries)
|
|
401
|
+
|
|
402
|
+
def _ensure_entry(self, key: Literal["d", "s", "p"], value: bytes | int) -> int:
|
|
403
|
+
for idx, entry in enumerate(self._entries):
|
|
404
|
+
if entry.get(key) == value:
|
|
405
|
+
return idx + 1
|
|
406
|
+
self._entries.append({key: value})
|
|
407
|
+
return len(self._entries)
|
|
408
|
+
|
|
409
|
+
def _register_direct(self, ref: ResourceReference) -> bool:
|
|
410
|
+
if not (ref.address or ref.asset_id is not None or ref.app_id is not None):
|
|
411
|
+
return False
|
|
412
|
+
self.ensure(ref)
|
|
413
|
+
return True
|
|
414
|
+
|
|
415
|
+
def _register_holding(self, ref: ResourceReference) -> bool:
|
|
416
|
+
holding = ref.holding
|
|
417
|
+
if holding is None:
|
|
418
|
+
return False
|
|
419
|
+
address_index = 0
|
|
420
|
+
if holding.address and holding.address != ZERO_ADDRESS:
|
|
421
|
+
address_index = self.ensure(ResourceReference(address=holding.address))
|
|
422
|
+
asset_index = self.ensure(ResourceReference(asset_id=holding.asset_id))
|
|
423
|
+
self._entries.append({"h": {"d": address_index, "s": asset_index}})
|
|
424
|
+
return True
|
|
425
|
+
|
|
426
|
+
def _register_locals(self, ref: ResourceReference) -> bool:
|
|
427
|
+
locals_ref = ref.locals
|
|
428
|
+
if locals_ref is None:
|
|
429
|
+
return False
|
|
430
|
+
address_index = 0
|
|
431
|
+
if locals_ref.address and locals_ref.address != ZERO_ADDRESS:
|
|
432
|
+
address_index = self.ensure(ResourceReference(address=locals_ref.address))
|
|
433
|
+
app_index = 0
|
|
434
|
+
if locals_ref.app_id and locals_ref.app_id != self._app_call.app_id:
|
|
435
|
+
app_index = self.ensure(ResourceReference(app_id=locals_ref.app_id))
|
|
436
|
+
self._entries.append({"l": {"d": address_index, "p": app_index}})
|
|
437
|
+
return True
|
|
438
|
+
|
|
439
|
+
def _register_box(self, ref: ResourceReference) -> bool:
|
|
440
|
+
box_ref = ref.box
|
|
441
|
+
if box_ref is None:
|
|
442
|
+
return False
|
|
443
|
+
app_index = 0
|
|
444
|
+
if box_ref.app_id not in (0, self._app_call.app_id):
|
|
445
|
+
app_index = self.ensure(ResourceReference(app_id=box_ref.app_id))
|
|
446
|
+
self._entries.append({"b": {"i": app_index, "n": box_ref.name}})
|
|
447
|
+
return True
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
|
|
3
|
+
from algokit_transact.codec.serde import addr, wire
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass(slots=True, frozen=True)
|
|
7
|
+
class AssetConfigTransactionFields:
|
|
8
|
+
asset_id: int = field(default=0, metadata=wire("caid"))
|
|
9
|
+
total: int | None = field(default=None, metadata=wire("apar.t"))
|
|
10
|
+
decimals: int | None = field(default=None, metadata=wire("apar.dc"))
|
|
11
|
+
default_frozen: bool | None = field(default=None, metadata=wire("apar.df"))
|
|
12
|
+
unit_name: str | None = field(default=None, metadata=wire("apar.un"))
|
|
13
|
+
asset_name: str | None = field(default=None, metadata=wire("apar.an"))
|
|
14
|
+
url: str | None = field(default=None, metadata=wire("apar.au"))
|
|
15
|
+
metadata_hash: bytes | None = field(default=None, metadata=wire("apar.am"))
|
|
16
|
+
manager: str | None = field(default=None, metadata=addr("apar.m", omit_if_none=True))
|
|
17
|
+
reserve: str | None = field(default=None, metadata=addr("apar.r", omit_if_none=True))
|
|
18
|
+
freeze: str | None = field(default=None, metadata=addr("apar.f", omit_if_none=True))
|
|
19
|
+
clawback: str | None = field(default=None, metadata=addr("apar.c", omit_if_none=True))
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
|
|
3
|
+
from algokit_common.constants import ZERO_ADDRESS
|
|
4
|
+
from algokit_transact.codec.serde import addr, wire
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass(slots=True, frozen=True)
|
|
8
|
+
class AssetFreezeTransactionFields:
|
|
9
|
+
asset_id: int = field(default=0, metadata=wire("faid"))
|
|
10
|
+
freeze_target: str = field(default=ZERO_ADDRESS, metadata=addr("fadd"))
|
|
11
|
+
frozen: bool = field(default=False, metadata=wire("afrz"))
|