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
algokit_abi/abi.py
ADDED
|
@@ -0,0 +1,667 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import dataclasses
|
|
3
|
+
import decimal
|
|
4
|
+
import typing
|
|
5
|
+
from collections.abc import Iterator, Mapping, Sequence
|
|
6
|
+
from functools import cached_property
|
|
7
|
+
|
|
8
|
+
from algokit_common import address_from_public_key, public_key_from_address
|
|
9
|
+
|
|
10
|
+
BytesLike = bytes | bytearray | memoryview
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
_ABI_BOOL_TRUE_UINT = 0x80
|
|
14
|
+
_ABI_BOOL_TRUE = _ABI_BOOL_TRUE_UINT.to_bytes(length=1, byteorder="big")
|
|
15
|
+
_ABI_BOOL_FALSE = b"\x00"
|
|
16
|
+
_MAX_UINT_N = 512
|
|
17
|
+
_U16_NUM_BYTES = 2
|
|
18
|
+
_MAX_U16 = 2**16 - 1
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ABIType(abc.ABC):
|
|
22
|
+
@property
|
|
23
|
+
def display_name(self) -> str:
|
|
24
|
+
return self.name
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
@abc.abstractmethod
|
|
28
|
+
def name(self) -> str: ...
|
|
29
|
+
|
|
30
|
+
@abc.abstractmethod
|
|
31
|
+
def encode(self, value: typing.Any) -> bytes: ... # noqa: ANN401
|
|
32
|
+
|
|
33
|
+
@abc.abstractmethod
|
|
34
|
+
def decode(self, value: BytesLike) -> typing.Any: ... # noqa: ANN401
|
|
35
|
+
|
|
36
|
+
def is_dynamic(self) -> bool:
|
|
37
|
+
return self.byte_len() is None
|
|
38
|
+
|
|
39
|
+
@abc.abstractmethod
|
|
40
|
+
def byte_len(self) -> int | None: ...
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def from_string(cls, value: str) -> "ABIType":
|
|
44
|
+
try:
|
|
45
|
+
return _COMMON_TYPES[value]
|
|
46
|
+
except KeyError:
|
|
47
|
+
pass
|
|
48
|
+
if value.startswith("(") and value.endswith(")"):
|
|
49
|
+
tup_inner = value[1:-1]
|
|
50
|
+
tup_elements = [cls.from_string(t) for t in split_tuple_str(tup_inner)]
|
|
51
|
+
return TupleType(tup_elements)
|
|
52
|
+
if value.endswith("[]"):
|
|
53
|
+
element = cls.from_string(value[:-2])
|
|
54
|
+
return DynamicArrayType(element)
|
|
55
|
+
if value.endswith("]"):
|
|
56
|
+
array_start = value.rindex("[")
|
|
57
|
+
size_str = value[array_start + 1 : -1]
|
|
58
|
+
try:
|
|
59
|
+
size = int(size_str)
|
|
60
|
+
except ValueError:
|
|
61
|
+
pass # fall through to error
|
|
62
|
+
else:
|
|
63
|
+
element = cls.from_string(value[:array_start])
|
|
64
|
+
return StaticArrayType(element, size)
|
|
65
|
+
elif value.startswith("ufixed"):
|
|
66
|
+
n_m_str = value.removeprefix("ufixed")
|
|
67
|
+
try:
|
|
68
|
+
n, m = map(int, n_m_str.split("x"))
|
|
69
|
+
except ValueError:
|
|
70
|
+
pass # fall through to error
|
|
71
|
+
else:
|
|
72
|
+
return UfixedType(n, m)
|
|
73
|
+
raise ValueError(f"unknown abi type: {value}")
|
|
74
|
+
|
|
75
|
+
def __eq__(self, other: object) -> bool:
|
|
76
|
+
if isinstance(other, StructType):
|
|
77
|
+
# structs can only equal structs
|
|
78
|
+
return False
|
|
79
|
+
if isinstance(other, ABIType):
|
|
80
|
+
return self.name == other.name
|
|
81
|
+
else:
|
|
82
|
+
return False
|
|
83
|
+
|
|
84
|
+
def __hash__(self) -> int:
|
|
85
|
+
return hash(self.display_name)
|
|
86
|
+
|
|
87
|
+
def __str__(self) -> str:
|
|
88
|
+
return self.display_name
|
|
89
|
+
|
|
90
|
+
def _check_num_bytes(self, value: BytesLike) -> None:
|
|
91
|
+
expected_bytes_len = self.byte_len()
|
|
92
|
+
if expected_bytes_len is not None and len(value) != expected_bytes_len:
|
|
93
|
+
raise ValueError(f"expected {expected_bytes_len} bytes for {self.display_name}, got {len(value)} bytes")
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@typing.final
|
|
97
|
+
class BoolType(ABIType):
|
|
98
|
+
@property
|
|
99
|
+
def name(self) -> str:
|
|
100
|
+
return "bool"
|
|
101
|
+
|
|
102
|
+
def byte_len(self) -> int:
|
|
103
|
+
return 1
|
|
104
|
+
|
|
105
|
+
def encode(self, value: bool) -> bytes: # noqa: FBT001
|
|
106
|
+
# note: bool in an array or tuple is handled separately
|
|
107
|
+
# intentionally comparing to True and False to handle invalid types
|
|
108
|
+
if value is True:
|
|
109
|
+
return _ABI_BOOL_TRUE
|
|
110
|
+
elif value is False:
|
|
111
|
+
return _ABI_BOOL_FALSE
|
|
112
|
+
else:
|
|
113
|
+
raise ValueError(f"expected a bool, got: {_error_str(value)}")
|
|
114
|
+
|
|
115
|
+
def decode(self, value: BytesLike) -> bool:
|
|
116
|
+
if value == _ABI_BOOL_TRUE:
|
|
117
|
+
return True
|
|
118
|
+
elif value == _ABI_BOOL_FALSE:
|
|
119
|
+
return False
|
|
120
|
+
else:
|
|
121
|
+
raise ValueError(f"bool value could not be decoded: {_error_str(value)}")
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@typing.final
|
|
125
|
+
@dataclasses.dataclass(frozen=True)
|
|
126
|
+
class UintType(ABIType):
|
|
127
|
+
bit_size: int = dataclasses.field()
|
|
128
|
+
|
|
129
|
+
def __post_init__(self) -> None:
|
|
130
|
+
if (
|
|
131
|
+
not isinstance(self.bit_size, int)
|
|
132
|
+
or self.bit_size <= 0
|
|
133
|
+
or self.bit_size > _MAX_UINT_N
|
|
134
|
+
or self.bit_size % 8 != 0
|
|
135
|
+
):
|
|
136
|
+
raise ValueError(f"bit_size must be between 8 and {_MAX_UINT_N} and divisible by 8")
|
|
137
|
+
|
|
138
|
+
@property
|
|
139
|
+
def name(self) -> str:
|
|
140
|
+
return f"uint{self.bit_size}"
|
|
141
|
+
|
|
142
|
+
def byte_len(self) -> int:
|
|
143
|
+
return self.bit_size // 8
|
|
144
|
+
|
|
145
|
+
def encode(self, value: int) -> bytes:
|
|
146
|
+
if not isinstance(value, int):
|
|
147
|
+
raise TypeError("expected int")
|
|
148
|
+
return value.to_bytes(self.byte_len(), byteorder="big", signed=False)
|
|
149
|
+
|
|
150
|
+
def decode(self, value: BytesLike) -> int:
|
|
151
|
+
self._check_num_bytes(value)
|
|
152
|
+
return int.from_bytes(value, byteorder="big", signed=False)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
_MAX_PRECISION = 160
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@typing.final
|
|
159
|
+
@dataclasses.dataclass(frozen=True)
|
|
160
|
+
class UfixedType(ABIType):
|
|
161
|
+
bit_size: int
|
|
162
|
+
precision: int
|
|
163
|
+
|
|
164
|
+
def __post_init__(self) -> None:
|
|
165
|
+
if (
|
|
166
|
+
not isinstance(self.bit_size, int)
|
|
167
|
+
or self.bit_size <= 0
|
|
168
|
+
or self.bit_size > _MAX_UINT_N
|
|
169
|
+
or self.bit_size % 8 != 0
|
|
170
|
+
):
|
|
171
|
+
raise ValueError(f"bit_size must be between 8 and {_MAX_UINT_N} and divisible by 8")
|
|
172
|
+
if not isinstance(self.precision, int) or self.precision <= 0 or self.precision > _MAX_PRECISION:
|
|
173
|
+
raise ValueError(f"precision must be between 0 and {_MAX_PRECISION}")
|
|
174
|
+
|
|
175
|
+
@property
|
|
176
|
+
def name(self) -> str:
|
|
177
|
+
return f"ufixed{self.bit_size}x{self.precision}"
|
|
178
|
+
|
|
179
|
+
def byte_len(self) -> int:
|
|
180
|
+
return self._int_type.byte_len()
|
|
181
|
+
|
|
182
|
+
@cached_property
|
|
183
|
+
def _int_type(self) -> UintType:
|
|
184
|
+
return UintType(bit_size=self.bit_size)
|
|
185
|
+
|
|
186
|
+
def encode(self, value: decimal.Decimal | int) -> bytes:
|
|
187
|
+
if isinstance(value, decimal.Decimal):
|
|
188
|
+
value = value.normalize()
|
|
189
|
+
decimal_tuple = value.as_tuple()
|
|
190
|
+
exponent = decimal_tuple.exponent
|
|
191
|
+
if not isinstance(exponent, int):
|
|
192
|
+
raise ValueError(f"unsupported decimal: {_error_str(value)}")
|
|
193
|
+
if -exponent > self.precision:
|
|
194
|
+
raise ValueError(f"precision exceeds {self.precision}: {_error_str(value)}")
|
|
195
|
+
value_int = int(value * 10**self.precision)
|
|
196
|
+
else:
|
|
197
|
+
value_int = value
|
|
198
|
+
return self._int_type.encode(value_int)
|
|
199
|
+
|
|
200
|
+
def decode(self, value: BytesLike) -> decimal.Decimal:
|
|
201
|
+
int_value = self._int_type.decode(value)
|
|
202
|
+
int_str = str(int_value).zfill(self.precision)
|
|
203
|
+
decimal_str = int_str[: -self.precision] + "." + int_str[-self.precision :]
|
|
204
|
+
return decimal.Decimal(decimal_str)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
@typing.final
|
|
208
|
+
@dataclasses.dataclass(frozen=True)
|
|
209
|
+
class ByteType(ABIType):
|
|
210
|
+
_uint_type: UintType = dataclasses.field(default=UintType(bit_size=8), init=False)
|
|
211
|
+
|
|
212
|
+
@property
|
|
213
|
+
def name(self) -> str:
|
|
214
|
+
return "byte"
|
|
215
|
+
|
|
216
|
+
def byte_len(self) -> int:
|
|
217
|
+
return self._uint_type.byte_len()
|
|
218
|
+
|
|
219
|
+
def encode(self, value: int | bytes | bytearray) -> bytes:
|
|
220
|
+
if isinstance(value, int):
|
|
221
|
+
return self._uint_type.encode(value)
|
|
222
|
+
elif len(value) == 1:
|
|
223
|
+
return bytes(value)
|
|
224
|
+
else:
|
|
225
|
+
raise ValueError(f"expected 1 byte: {_error_str(value)}")
|
|
226
|
+
|
|
227
|
+
def decode(self, value: BytesLike) -> bytes:
|
|
228
|
+
self._check_num_bytes(value)
|
|
229
|
+
return bytes(value)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
@dataclasses.dataclass(frozen=True)
|
|
233
|
+
class DynamicArrayType(ABIType):
|
|
234
|
+
element: ABIType
|
|
235
|
+
|
|
236
|
+
@property
|
|
237
|
+
def display_name(self) -> str:
|
|
238
|
+
return f"{self.element.display_name}[]"
|
|
239
|
+
|
|
240
|
+
@property
|
|
241
|
+
def name(self) -> str:
|
|
242
|
+
return f"{self.element.name}[]"
|
|
243
|
+
|
|
244
|
+
def byte_len(self) -> None:
|
|
245
|
+
return None
|
|
246
|
+
|
|
247
|
+
def encode(self, value: Sequence | bytes | bytearray) -> bytes:
|
|
248
|
+
static_type = StaticArrayType(element=self.element, size=len(value))
|
|
249
|
+
try:
|
|
250
|
+
len_bytes = _int_to_u16_bytes(len(value))
|
|
251
|
+
except OverflowError:
|
|
252
|
+
raise ValueError(f"array length exceeds {_MAX_U16}") from None
|
|
253
|
+
return len_bytes + static_type.encode(value)
|
|
254
|
+
|
|
255
|
+
def decode(self, value: BytesLike) -> list | bytes:
|
|
256
|
+
data = memoryview(value)
|
|
257
|
+
if data.nbytes < _U16_NUM_BYTES:
|
|
258
|
+
raise ValueError(f"not enough bytes to decode {self}: {_error_str(value)}")
|
|
259
|
+
|
|
260
|
+
array_len = int.from_bytes(data[:_U16_NUM_BYTES], byteorder="big")
|
|
261
|
+
|
|
262
|
+
static_type = StaticArrayType(element=self.element, size=array_len)
|
|
263
|
+
return static_type.decode(data[_U16_NUM_BYTES:])
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
@dataclasses.dataclass(frozen=True)
|
|
267
|
+
class _RepeatedSequence(Sequence[ABIType]):
|
|
268
|
+
element: ABIType
|
|
269
|
+
size: int
|
|
270
|
+
|
|
271
|
+
def __len__(self) -> int:
|
|
272
|
+
return self.size
|
|
273
|
+
|
|
274
|
+
def __iter__(self) -> Iterator[ABIType]:
|
|
275
|
+
for _ in range(self.size):
|
|
276
|
+
yield self.element
|
|
277
|
+
|
|
278
|
+
def __getitem__(self, item: int) -> ABIType: # type: ignore[override]
|
|
279
|
+
if item >= self.size or -item > self.size:
|
|
280
|
+
raise IndexError("index out of range")
|
|
281
|
+
return self.element
|
|
282
|
+
|
|
283
|
+
def __contains__(self, x: object, /) -> bool:
|
|
284
|
+
return x == self.element
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
@typing.final
|
|
288
|
+
@dataclasses.dataclass(frozen=True)
|
|
289
|
+
class StaticArrayType(ABIType):
|
|
290
|
+
element: ABIType
|
|
291
|
+
size: int
|
|
292
|
+
|
|
293
|
+
@property
|
|
294
|
+
def display_name(self) -> str:
|
|
295
|
+
return f"{self.element.display_name}[{self.size}]"
|
|
296
|
+
|
|
297
|
+
@property
|
|
298
|
+
def name(self) -> str:
|
|
299
|
+
return f"{self.element.name}[{self.size}]"
|
|
300
|
+
|
|
301
|
+
@cached_property
|
|
302
|
+
def _tuple_type(self) -> "TupleType":
|
|
303
|
+
return TupleType(elements=_RepeatedSequence(self.element, self.size))
|
|
304
|
+
|
|
305
|
+
def byte_len(self) -> int | None:
|
|
306
|
+
return self._tuple_type.byte_len()
|
|
307
|
+
|
|
308
|
+
def encode(self, value: Sequence | bytes | bytearray) -> bytes:
|
|
309
|
+
return self._tuple_type.encode(value)
|
|
310
|
+
|
|
311
|
+
def decode(self, value: BytesLike) -> list | bytes:
|
|
312
|
+
result = self._tuple_type.decode(value)
|
|
313
|
+
if isinstance(result, bytes):
|
|
314
|
+
return result
|
|
315
|
+
else:
|
|
316
|
+
return list(result)
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
@typing.final
|
|
320
|
+
@dataclasses.dataclass(frozen=True)
|
|
321
|
+
class TupleType(ABIType):
|
|
322
|
+
elements: Sequence[ABIType]
|
|
323
|
+
|
|
324
|
+
@cached_property
|
|
325
|
+
def display_name(self) -> str:
|
|
326
|
+
if self._homogenous_element:
|
|
327
|
+
return f"{self._homogenous_element.display_name}[{len(self.elements)}]"
|
|
328
|
+
else:
|
|
329
|
+
return f"({','.join(v.display_name for v in self.elements)})"
|
|
330
|
+
|
|
331
|
+
@cached_property
|
|
332
|
+
def name(self) -> str:
|
|
333
|
+
return f"({','.join(v.name for v in self.elements)})"
|
|
334
|
+
|
|
335
|
+
def byte_len(self) -> int | None:
|
|
336
|
+
return self._byte_len
|
|
337
|
+
|
|
338
|
+
@cached_property
|
|
339
|
+
def _byte_len(self) -> int | None:
|
|
340
|
+
total_bits = 0
|
|
341
|
+
for el in self.elements:
|
|
342
|
+
if el.name == "bool":
|
|
343
|
+
total_bits += 1
|
|
344
|
+
else:
|
|
345
|
+
el_byte_len = el.byte_len()
|
|
346
|
+
if el_byte_len is None:
|
|
347
|
+
return None
|
|
348
|
+
total_bits = _round_bits_to_nearest_byte(total_bits) + el_byte_len * 8
|
|
349
|
+
if self._homogenous_element:
|
|
350
|
+
total_bits *= len(self.elements)
|
|
351
|
+
break
|
|
352
|
+
return _bits_to_byte(total_bits)
|
|
353
|
+
|
|
354
|
+
@cached_property
|
|
355
|
+
def _homogenous_element(self) -> ABIType | None:
|
|
356
|
+
if isinstance(self.elements, _RepeatedSequence):
|
|
357
|
+
return self.elements.element
|
|
358
|
+
elif len({e.name for e in self.elements}) == 1:
|
|
359
|
+
return next(iter(self.elements))
|
|
360
|
+
else:
|
|
361
|
+
return None
|
|
362
|
+
|
|
363
|
+
@cached_property
|
|
364
|
+
def _head_num_bytes(self) -> int:
|
|
365
|
+
num_bits = 0
|
|
366
|
+
for element in self.elements:
|
|
367
|
+
if _is_bool(element):
|
|
368
|
+
num_bits += 1
|
|
369
|
+
else:
|
|
370
|
+
num_bits = _round_bits_to_nearest_byte(num_bits)
|
|
371
|
+
el_byte_len = element.byte_len()
|
|
372
|
+
if el_byte_len is None:
|
|
373
|
+
el_byte_len = _U16_NUM_BYTES
|
|
374
|
+
num_bits += el_byte_len * 8
|
|
375
|
+
if self._homogenous_element:
|
|
376
|
+
num_bits *= len(self.elements)
|
|
377
|
+
break
|
|
378
|
+
return _bits_to_byte(num_bits)
|
|
379
|
+
|
|
380
|
+
def encode(self, value: Sequence | bytes | bytearray) -> bytes:
|
|
381
|
+
if len(value) != len(self.elements):
|
|
382
|
+
raise ValueError(f"expected {len(self.elements)} elements: {_error_str(value)}")
|
|
383
|
+
if _is_byte(self._homogenous_element):
|
|
384
|
+
return bytes(value)
|
|
385
|
+
head = bytearray()
|
|
386
|
+
tail = bytearray()
|
|
387
|
+
bit_index = 0
|
|
388
|
+
tail_offset = self._head_num_bytes
|
|
389
|
+
for el, el_type in zip(value, self.elements, strict=True):
|
|
390
|
+
# there are 3 kinds of elements to consider when encoding a tuple
|
|
391
|
+
# 1. bool, these require packing consecutive values into a byte in the head
|
|
392
|
+
# 2. dynamically sized types, these require a pointer in the head and the actual data in the tail
|
|
393
|
+
# 3. statically sized types, these are stored directly in the head
|
|
394
|
+
if _is_bool(el_type):
|
|
395
|
+
# append a new value if start of a new byte
|
|
396
|
+
if bit_index % 8 == 0:
|
|
397
|
+
head.append(0)
|
|
398
|
+
if el is True:
|
|
399
|
+
head[-1] = _set_bit(head[-1], bit_index % 8)
|
|
400
|
+
elif el is False:
|
|
401
|
+
pass
|
|
402
|
+
else:
|
|
403
|
+
raise ValueError("expected bool")
|
|
404
|
+
bit_index += 1
|
|
405
|
+
else:
|
|
406
|
+
bit_index = 0
|
|
407
|
+
el_bytes = el_type.encode(el)
|
|
408
|
+
if el_type.is_dynamic():
|
|
409
|
+
try:
|
|
410
|
+
head.extend(_int_to_u16_bytes(tail_offset))
|
|
411
|
+
except OverflowError:
|
|
412
|
+
raise ValueError(f"encoded bytes length exceeds {_MAX_U16}") from None
|
|
413
|
+
tail_offset += len(el_bytes)
|
|
414
|
+
tail.extend(el_bytes)
|
|
415
|
+
else:
|
|
416
|
+
head.extend(el_bytes)
|
|
417
|
+
return bytes((*head, *tail))
|
|
418
|
+
|
|
419
|
+
@cached_property
|
|
420
|
+
def _tuple_head_offsets(self) -> Mapping[int, int]:
|
|
421
|
+
offsets = {}
|
|
422
|
+
num_bits = 0
|
|
423
|
+
for idx, element in enumerate(self.elements):
|
|
424
|
+
if element.is_dynamic():
|
|
425
|
+
offsets[idx] = _bits_to_byte(num_bits)
|
|
426
|
+
if _is_bool(element):
|
|
427
|
+
num_bits += 1
|
|
428
|
+
else:
|
|
429
|
+
num_bits = _round_bits_to_nearest_byte(num_bits)
|
|
430
|
+
el_byte_len = element.byte_len()
|
|
431
|
+
if el_byte_len is None:
|
|
432
|
+
el_byte_len = _U16_NUM_BYTES
|
|
433
|
+
num_bits += el_byte_len * 8
|
|
434
|
+
return offsets
|
|
435
|
+
|
|
436
|
+
def _get_next_dynamic_head_offset(self, index: int, value: memoryview) -> int | None:
|
|
437
|
+
# the last element reads to end
|
|
438
|
+
if index == len(self.elements) - 1:
|
|
439
|
+
return None
|
|
440
|
+
if self._homogenous_element:
|
|
441
|
+
assert self._homogenous_element.is_dynamic()
|
|
442
|
+
next_head_offset = _U16_NUM_BYTES * (index + 1)
|
|
443
|
+
else:
|
|
444
|
+
for idx in range(index + 1, len(self.elements)):
|
|
445
|
+
try:
|
|
446
|
+
next_head_offset = self._tuple_head_offsets[idx]
|
|
447
|
+
except KeyError:
|
|
448
|
+
continue
|
|
449
|
+
else:
|
|
450
|
+
break
|
|
451
|
+
else:
|
|
452
|
+
return None
|
|
453
|
+
return _u16_bytes_to_int(value[next_head_offset : next_head_offset + _U16_NUM_BYTES])
|
|
454
|
+
|
|
455
|
+
def decode(self, value: BytesLike) -> tuple | bytes:
|
|
456
|
+
self._check_num_bytes(value)
|
|
457
|
+
if _is_byte(self._homogenous_element):
|
|
458
|
+
return bytes(value)
|
|
459
|
+
|
|
460
|
+
value = memoryview(value)
|
|
461
|
+
result = []
|
|
462
|
+
head_offset = 0
|
|
463
|
+
bit_index = 0
|
|
464
|
+
expected_tail_offset = self._head_num_bytes
|
|
465
|
+
for el_idx, el_type in enumerate(self.elements):
|
|
466
|
+
if _is_bool(el_type):
|
|
467
|
+
current_byte = value[head_offset + bit_index // 8]
|
|
468
|
+
bool_value = _get_bit(current_byte, bit_index % 8)
|
|
469
|
+
result.append(bool_value)
|
|
470
|
+
bit_index += 1
|
|
471
|
+
else:
|
|
472
|
+
head_offset += _bits_to_byte(bit_index)
|
|
473
|
+
bit_index = 0
|
|
474
|
+
el_byte_len = el_type.byte_len()
|
|
475
|
+
if el_byte_len is None:
|
|
476
|
+
el_offset = _u16_bytes_to_int(value[head_offset : head_offset + _U16_NUM_BYTES])
|
|
477
|
+
if el_offset != expected_tail_offset:
|
|
478
|
+
raise ValueError(f"expected tail offset of {expected_tail_offset} got: {el_offset}")
|
|
479
|
+
head_offset += _U16_NUM_BYTES
|
|
480
|
+
next_el_offset = self._get_next_dynamic_head_offset(el_idx, value)
|
|
481
|
+
el_bytes = value[el_offset:next_el_offset]
|
|
482
|
+
expected_tail_offset += len(el_bytes)
|
|
483
|
+
result.append(el_type.decode(el_bytes))
|
|
484
|
+
else:
|
|
485
|
+
result.append(el_type.decode(value[head_offset : head_offset + el_byte_len]))
|
|
486
|
+
head_offset += el_byte_len
|
|
487
|
+
if self.is_dynamic() and expected_tail_offset != len(value):
|
|
488
|
+
raise ValueError(f"expected {expected_tail_offset} bytes for {self.display_name}, got {len(value)} bytes")
|
|
489
|
+
return tuple(result)
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
@typing.final
|
|
493
|
+
@dataclasses.dataclass(frozen=True, kw_only=True)
|
|
494
|
+
class StructType(ABIType):
|
|
495
|
+
struct_name: str
|
|
496
|
+
fields: Mapping[str, ABIType]
|
|
497
|
+
decode_type: type = dataclasses.field(default=dict)
|
|
498
|
+
|
|
499
|
+
@property
|
|
500
|
+
def display_name(self) -> str:
|
|
501
|
+
return self.struct_name
|
|
502
|
+
|
|
503
|
+
@cached_property
|
|
504
|
+
def name(self) -> str:
|
|
505
|
+
return self._tuple_type.name
|
|
506
|
+
|
|
507
|
+
@cached_property
|
|
508
|
+
def _tuple_type(self) -> TupleType:
|
|
509
|
+
return TupleType(elements=tuple(self.fields.values()))
|
|
510
|
+
|
|
511
|
+
def byte_len(self) -> int | None:
|
|
512
|
+
return self._tuple_type.byte_len()
|
|
513
|
+
|
|
514
|
+
def encode(self, value: dict[str, typing.Any] | tuple | object) -> bytes:
|
|
515
|
+
if isinstance(value, dict): # structs as a dictionary mapped by field name
|
|
516
|
+
field_values = tuple(value[field_name] for field_name in self.fields)
|
|
517
|
+
elif isinstance(value, tuple): # structs that have already been converted to a tuple
|
|
518
|
+
field_values = value
|
|
519
|
+
else: # objects with struct field names
|
|
520
|
+
field_values = tuple(getattr(value, field_name) for field_name in self.fields)
|
|
521
|
+
return self._tuple_type.encode(field_values)
|
|
522
|
+
|
|
523
|
+
def decode(self, value: BytesLike) -> typing.Any: # noqa: ANN401
|
|
524
|
+
field_values = self._tuple_type.decode(value)
|
|
525
|
+
fields = dict(zip(self.fields, field_values, strict=True))
|
|
526
|
+
return self.decode_type(**fields)
|
|
527
|
+
|
|
528
|
+
def __hash__(self) -> int:
|
|
529
|
+
return hash(self.display_name)
|
|
530
|
+
|
|
531
|
+
def __eq__(self, other: object) -> bool:
|
|
532
|
+
if not isinstance(other, StructType):
|
|
533
|
+
return False
|
|
534
|
+
return (
|
|
535
|
+
self.display_name == other.display_name
|
|
536
|
+
and self.fields.keys() == other.fields.keys()
|
|
537
|
+
and self._tuple_type == other._tuple_type
|
|
538
|
+
)
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
@typing.final
|
|
542
|
+
@dataclasses.dataclass(frozen=True)
|
|
543
|
+
class StringType(ABIType):
|
|
544
|
+
_array_type: DynamicArrayType = dataclasses.field(default=DynamicArrayType(element=ByteType()), init=False)
|
|
545
|
+
|
|
546
|
+
@property
|
|
547
|
+
def name(self) -> str:
|
|
548
|
+
return "string"
|
|
549
|
+
|
|
550
|
+
def byte_len(self) -> None:
|
|
551
|
+
return None
|
|
552
|
+
|
|
553
|
+
def encode(self, value: str) -> bytes:
|
|
554
|
+
if not isinstance(value, str):
|
|
555
|
+
raise TypeError("expected str")
|
|
556
|
+
return self._array_type.encode(value.encode("utf-8"))
|
|
557
|
+
|
|
558
|
+
def decode(self, value: BytesLike) -> str:
|
|
559
|
+
bytes_ = self._array_type.decode(value)
|
|
560
|
+
assert isinstance(bytes_, bytes)
|
|
561
|
+
return bytes_.decode("utf-8")
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
@typing.final
|
|
565
|
+
@dataclasses.dataclass(frozen=True)
|
|
566
|
+
class AddressType(ABIType):
|
|
567
|
+
_array_type: StaticArrayType = dataclasses.field(default=StaticArrayType(element=ByteType(), size=32), init=False)
|
|
568
|
+
|
|
569
|
+
@property
|
|
570
|
+
def name(self) -> str:
|
|
571
|
+
return "address"
|
|
572
|
+
|
|
573
|
+
def byte_len(self) -> int | None:
|
|
574
|
+
return self._array_type.byte_len()
|
|
575
|
+
|
|
576
|
+
def encode(self, value: str | bytes | bytearray) -> bytes:
|
|
577
|
+
if isinstance(value, str):
|
|
578
|
+
value = public_key_from_address(value)
|
|
579
|
+
return self._array_type.encode(value)
|
|
580
|
+
|
|
581
|
+
def decode(self, value: BytesLike) -> str:
|
|
582
|
+
public_key = self._array_type.decode(value)
|
|
583
|
+
assert isinstance(public_key, bytes)
|
|
584
|
+
return address_from_public_key(public_key)
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
def _is_bool(typ: ABIType | None) -> bool:
|
|
588
|
+
return type(typ) is BoolType
|
|
589
|
+
|
|
590
|
+
|
|
591
|
+
def _is_byte(typ: ABIType | None) -> bool:
|
|
592
|
+
return type(typ) is ByteType
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
_COMMON_TYPES = {
|
|
596
|
+
**{f"uint{n}": UintType(n) for n in range(8, _MAX_UINT_N + 1, 8)},
|
|
597
|
+
"byte": ByteType(),
|
|
598
|
+
"bool": BoolType(),
|
|
599
|
+
"address": AddressType(),
|
|
600
|
+
"string": StringType(),
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
|
|
604
|
+
def split_tuple_str(s: str) -> Iterator[str]:
|
|
605
|
+
"""
|
|
606
|
+
Split a well-formed tuple into it's top level elements
|
|
607
|
+
|
|
608
|
+
e.g. "(uint64,(bool,uint8),uint32)"
|
|
609
|
+
"""
|
|
610
|
+
if not s:
|
|
611
|
+
return
|
|
612
|
+
|
|
613
|
+
if s.startswith(",") or s.endswith(","):
|
|
614
|
+
raise ValueError(f"cannot have leading or trailing commas in ({s})")
|
|
615
|
+
|
|
616
|
+
depth = 0
|
|
617
|
+
current_element = ""
|
|
618
|
+
for char in s:
|
|
619
|
+
if char == "(":
|
|
620
|
+
depth += 1
|
|
621
|
+
elif char == ")":
|
|
622
|
+
depth -= 1
|
|
623
|
+
elif char == "," and depth == 0:
|
|
624
|
+
# yield only top level tuple elements, nested elements will be recursively parsed
|
|
625
|
+
if not current_element:
|
|
626
|
+
raise ValueError(f"commas must follow a tuple element: ({s})")
|
|
627
|
+
yield current_element
|
|
628
|
+
current_element = ""
|
|
629
|
+
continue
|
|
630
|
+
current_element += char
|
|
631
|
+
if current_element:
|
|
632
|
+
yield current_element
|
|
633
|
+
if depth != 0:
|
|
634
|
+
raise ValueError(f"parenthesis mismatch: ({s})")
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
def _set_bit(value: int, bit_index: int) -> int:
|
|
638
|
+
return value | (_ABI_BOOL_TRUE_UINT >> bit_index)
|
|
639
|
+
|
|
640
|
+
|
|
641
|
+
def _get_bit(value: int, bit_index: int) -> bool:
|
|
642
|
+
return (value & (_ABI_BOOL_TRUE_UINT >> bit_index)) != 0
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
def _int_to_u16_bytes(value: int) -> bytes:
|
|
646
|
+
return value.to_bytes(length=_U16_NUM_BYTES, byteorder="big", signed=False)
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
def _u16_bytes_to_int(value: memoryview) -> int:
|
|
650
|
+
if value.nbytes != _U16_NUM_BYTES:
|
|
651
|
+
raise ValueError("expected uint16 bytes")
|
|
652
|
+
return int.from_bytes(value, byteorder="big", signed=False)
|
|
653
|
+
|
|
654
|
+
|
|
655
|
+
def _bits_to_byte(bits: int) -> int:
|
|
656
|
+
return (bits + 7) // 8
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
def _round_bits_to_nearest_byte(bits: int) -> int:
|
|
660
|
+
return _bits_to_byte(bits) * 8
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
def _error_str(value: object) -> str:
|
|
664
|
+
if isinstance(value, bytes | bytearray | memoryview):
|
|
665
|
+
return f"0x{value.hex()}"
|
|
666
|
+
else:
|
|
667
|
+
return str(value)
|