diff --git a/bindings/python/CHANGELOG.md b/bindings/python/CHANGELOG.md index 4d79545358..2182e5f268 100644 --- a/bindings/python/CHANGELOG.md +++ b/bindings/python/CHANGELOG.md @@ -35,6 +35,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `RegularTransactionEssence`; - `Unlock` types; - `PreparedTransactionData, SignedTransactionData, InputSigningData, RemainderData`; +- `UtxoChanges`; +- `TreasuryOutput, BasicOutput, AliasOutput, FoundryOutput, NftOutput`; +- `TokenScheme`; +- `Signature`; +- `BlockBuilderOptions`; ### Changed @@ -61,6 +66,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Switched order of `AddressAndAmount` init params; - Renamed `PreparedTransactionData` to `PreparedTransaction`; - `{Client, SecretManager}::sign_transaction` return type from `SignedTransactionData` to `TransactionPayload`; +- Split `Output` into multiple classes; +- Renamed `TokenScheme` to `SimpleTokenScheme`; ### Removed diff --git a/bindings/python/examples/client/build_foundry.py b/bindings/python/examples/client/build_foundry.py index f8bb32ad55..25b1cd9857 100644 --- a/bindings/python/examples/client/build_foundry.py +++ b/bindings/python/examples/client/build_foundry.py @@ -13,7 +13,7 @@ # Configure foundry output # Replace with your own values serial_number = 1 -token_scheme = TokenScheme(32, 0, 64) +token_scheme = SimpleTokenScheme(32, 0, 64) unlock_conditions = [ ImmutableAliasAddressUnlockCondition( AliasAddress( diff --git a/bindings/python/examples/client/submit_and_read_block.py b/bindings/python/examples/client/submit_and_read_block.py index 09e3f1bba8..fac01b9397 100644 --- a/bindings/python/examples/client/submit_and_read_block.py +++ b/bindings/python/examples/client/submit_and_read_block.py @@ -81,8 +81,8 @@ # Get the whole block block = client.get_block_data(block_id) payload_out = block.payload -tag_hex_out = block.payload['tag'] -message_hex_out = block.payload['data'] +tag_hex_out = block.payload.tag +message_hex_out = block.payload.data # Unpackage the payload (from hex to text) message_out = hex_to_utf8(message_hex_out) diff --git a/bindings/python/examples/how_tos/outputs/unlock_conditions.py b/bindings/python/examples/how_tos/outputs/unlock_conditions.py index 9ff0aa357b..8c78b12599 100644 --- a/bindings/python/examples/how_tos/outputs/unlock_conditions.py +++ b/bindings/python/examples/how_tos/outputs/unlock_conditions.py @@ -16,7 +16,7 @@ Ed25519Address(hex_address) ) -token_scheme = TokenScheme(50, 0, 100) +token_scheme = SimpleTokenScheme(50, 0, 100) # Most simple output basic_output = client.build_basic_output( @@ -83,4 +83,4 @@ ) outputs.append(foundry_output) -print(json.dumps(outputs, indent=2)) +print(json.dumps(list(map(lambda o: o.as_dict(), outputs)), indent=2)) diff --git a/bindings/python/iota_sdk/__init__.py b/bindings/python/iota_sdk/__init__.py index 4b8da0fc8b..c268ffe34f 100644 --- a/bindings/python/iota_sdk/__init__.py +++ b/bindings/python/iota_sdk/__init__.py @@ -13,6 +13,7 @@ from .types.address import * from .types.balance import * from .types.block import * +from .types.block_builder_options import * from .types.burn import * from .types.client_options import * from .types.common import * @@ -35,3 +36,4 @@ from .types.transaction_options import * from .types.unlock import * from .types.unlock_condition import * +from .types.utxo_changes import * diff --git a/bindings/python/iota_sdk/client/_node_core_api.py b/bindings/python/iota_sdk/client/_node_core_api.py index dd257c3ee0..c455937578 100644 --- a/bindings/python/iota_sdk/client/_node_core_api.py +++ b/bindings/python/iota_sdk/client/_node_core_api.py @@ -7,6 +7,7 @@ from iota_sdk.types.output import OutputWithMetadata, OutputMetadata from iota_sdk.types.output_id import OutputId from iota_sdk.types.payload import MilestonePayload +from iota_sdk.types.utxo_changes import UtxoChanges from typing import List from dacite import from_dict @@ -96,24 +97,29 @@ def post_block_raw(self, block_bytes: List[int]) -> HexStr: 'blockBytes': block_bytes }) - def get_output(self, output_id: OutputId) -> OutputWithMetadata: + def get_output(self, output_id: OutputId | HexStr) -> OutputWithMetadata: """Get the output corresponding to the given output id. Returns: The output itself with its metadata. """ + output_id_str = output_id.output_id if isinstance( + output_id, OutputId) else output_id return from_dict(OutputWithMetadata, self._call_method('getOutput', { - 'outputId': output_id + 'outputId': output_id_str })) - def get_output_metadata(self, output_id: OutputId) -> OutputMetadata: + def get_output_metadata(self, output_id: OutputId | + HexStr) -> OutputMetadata: """Get the output metadata corresponding to the given output id. Returns: The output metadata. """ + output_id_str = output_id.output_id if isinstance( + output_id, OutputId) else output_id return from_dict(OutputMetadata, self._call_method('getOutputMetadata', { - 'outputId': output_id + 'outputId': output_id_str })) def get_milestone_by_id(self, milestone_id: HexStr) -> MilestonePayload: @@ -158,19 +164,19 @@ def get_milestone_by_index_raw(self, index: int) -> List[int]: 'index': index }) - def get_utxo_changes_by_id(self, milestone_id: HexStr): + def get_utxo_changes_by_id(self, milestone_id: HexStr) -> UtxoChanges: """Get the UTXO changes applied in the given milestone. """ - return self._call_method('getUtxoChangesById', { + return from_dict(UtxoChanges, self._call_method('getUtxoChangesById', { 'milestoneId': milestone_id - }) + })) - def get_utxo_changes_by_index(self, index: int): + def get_utxo_changes_by_index(self, index: int) -> UtxoChanges: """Get the UTXO changes applied at the given milestone index. """ - return self._call_method('getUtxoChangesByIndex', { + return from_dict(UtxoChanges, self._call_method('getUtxoChangesByIndex', { 'index': index - }) + })) def get_receipts(self): """Get all receipts. diff --git a/bindings/python/iota_sdk/client/client.py b/bindings/python/iota_sdk/client/client.py index 930a07bbe7..a15fad1064 100644 --- a/bindings/python/iota_sdk/client/client.py +++ b/bindings/python/iota_sdk/client/client.py @@ -15,7 +15,7 @@ from iota_sdk.types.network_info import NetworkInfo from iota_sdk.types.output import Output from iota_sdk.types.payload import Payload, TransactionPayload -from iota_sdk.types.token_scheme import TokenScheme +from iota_sdk.types.token_scheme import SimpleTokenScheme from iota_sdk.types.unlock_condition import UnlockCondition from iota_sdk.types.transaction_data import PreparedTransactionData from json import dumps, loads @@ -265,7 +265,7 @@ def build_basic_output(self, def build_foundry_output(self, serial_number: int, - token_scheme: TokenScheme, + token_scheme: SimpleTokenScheme, unlock_conditions: List[UnlockCondition], amount: Optional[int] = None, native_tokens: Optional[List[NativeToken]] = None, diff --git a/bindings/python/iota_sdk/types/address.py b/bindings/python/iota_sdk/types/address.py index f068838a3a..3c37041f59 100644 --- a/bindings/python/iota_sdk/types/address.py +++ b/bindings/python/iota_sdk/types/address.py @@ -2,9 +2,8 @@ # SPDX-License-Identifier: Apache-2.0 from iota_sdk.types.common import HexStr -from dataclasses import dataclass +from dataclasses import dataclass, field from enum import IntEnum -from typing import Optional class AddressType(IntEnum): @@ -23,59 +22,47 @@ class AddressType(IntEnum): @dataclass class Address(): """Base class for addresses. - - Attributes: - type: The address type. - pubKeyHash: The hex encoded public key hash. - aliasId: The hex encoded alias id. - nftId: The hex encoded nft id. """ type: int - pubKeyHash: Optional[HexStr] = None - aliasId: Optional[HexStr] = None - nftId: Optional[HexStr] = None def as_dict(self): return {k: v for k, v in self.__dict__.items() if v is not None} +@dataclass class Ed25519Address(Address): """Represents an Ed25519 address. + Attributes: + pubKeyHash: The hex encoded Ed25519 public key hash. """ - - def __init__(self, address: HexStr): - """Initialize an Ed25519Address - - Args: - address: The hex encoded address to use. - """ - super().__init__(AddressType.ED25519, pubKeyHash=address) + pubKeyHash: HexStr + type: int = field( + default_factory=lambda: int( + AddressType.ED25519), + init=False) +@dataclass class AliasAddress(Address): """Represents an Alias address. + Attributes: + aliasId: The hex encoded alias id. """ - - def __init__(self, address_or_id: HexStr): - """Initialize an AliasAddress - - Args: - address_or_id: The hex encoded address to use. - """ - super().__init__(AddressType.ALIAS, aliasId=address_or_id) + aliasId: HexStr + type: int = field( + default_factory=lambda: int( + AddressType.ALIAS), + init=False) +@dataclass class NFTAddress(Address): """Represents an NFT address. + Attributes: + nftId: The hex encoded NFT id. """ - - def __init__(self, address_or_id: HexStr): - """Initialize an NFTAddress - - Args: - address_or_id: The hex encoded address to use. - """ - super().__init__(AddressType.NFT, nftId=address_or_id) + nftId: HexStr + type: int = field(default_factory=lambda: int(AddressType.NFT), init=False) @dataclass diff --git a/bindings/python/iota_sdk/types/block_builder_options.py b/bindings/python/iota_sdk/types/block_builder_options.py new file mode 100644 index 0000000000..984f2f6ab9 --- /dev/null +++ b/bindings/python/iota_sdk/types/block_builder_options.py @@ -0,0 +1,32 @@ +# Copyright 2023 IOTA Stiftung +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations +from dataclasses import dataclass +from typing import Any, Dict, List, Optional +from iota_sdk.types.common import HexStr, AddressAndAmount +from iota_sdk.client._high_level_api import Range +from iota_sdk.types.burn import Burn +from iota_sdk.types.output import Output +from iota_sdk.types.input import UtxoInput + + +@dataclass +class BlockBuilderOptions: + """Options to build a block. + """ + total: str + available: str + coinType: Optional[int] = None + accountInde: Optional[int] = None + initialAddressIndex: Optional[int] = None + inputs: Optional[List[UtxoInput]] = None + inputRange: Optional[Range] = None + output: Optional[AddressAndAmount] = None + outputHex: Optional[List[Dict[str, Any]]] = None + outputs: Optional[List[Output]] = None + customRemainderAddress: Optional[str] = None + tag: Optional[HexStr] = None + data: Optional[HexStr] = None + parents: Optional[List[HexStr]] = None + burn: Optional[Burn] = None diff --git a/bindings/python/iota_sdk/types/feature.py b/bindings/python/iota_sdk/types/feature.py index bc51c29cd5..3ceae1a5a2 100644 --- a/bindings/python/iota_sdk/types/feature.py +++ b/bindings/python/iota_sdk/types/feature.py @@ -1,11 +1,10 @@ # Copyright 2023 IOTA Stiftung # SPDX-License-Identifier: Apache-2.0 -from iota_sdk.types.address import Address +from iota_sdk.types.address import Ed25519Address, AliasAddress, NFTAddress from iota_sdk.types.common import HexStr -from dataclasses import dataclass +from dataclasses import dataclass, field from enum import IntEnum -from typing import Optional class FeatureType(IntEnum): @@ -26,30 +25,8 @@ class FeatureType(IntEnum): @dataclass class Feature(): """Base class of a feature. - - Attributes: - type: The type of feature. - address: Holds either an issuer or a sender address. - data: Some hex encoded metadata. - tag: A hex encoded tag used to index an output. """ type: int - address: Optional[Address] = None - data: Optional[HexStr] = None - tag: Optional[HexStr] = None - - def into(self): - """Downcast to the actual feature type. - """ - match FeatureType(self.type): - case FeatureType.Sender: - return SenderFeature(self.address) - case FeatureType.Issuer: - return IssuerFeature(self.address) - case FeatureType.Metadata: - return MetadataFeature(self.data) - case FeatureType.Tag: - return TagFeature(self.tag) def as_dict(self): res = {k: v for k, v in self.__dict__.items() if v is not None} @@ -58,53 +35,50 @@ def as_dict(self): return res +@dataclass class SenderFeature(Feature): """Sender feature. + Attributes: + address: A given sender address. """ - - def __init__(self, sender): - """Initialize a SenderFeature - - Args: - sender: A given sender address. - """ - super().__init__(int(FeatureType.Sender), address=sender) + address: Ed25519Address | AliasAddress | NFTAddress + type: int = field( + default_factory=lambda: int( + FeatureType.Sender), + init=False) +@dataclass class IssuerFeature(Feature): """Issuer feature. + Attributes: + address: A given issuer address. """ - - def __init__(self, issuer): - """Initialize an IssuerFeature - - Args: - issuer: A given issuer address. - """ - super().__init__(int(FeatureType.Issuer), address=issuer) + address: Ed25519Address | AliasAddress | NFTAddress + type: int = field( + default_factory=lambda: int( + FeatureType.Issuer), + init=False) +@dataclass class MetadataFeature(Feature): """Metadata feature. + Attributes: + data: Some hex encoded metadata. """ - - def __init__(self, data: HexStr): - """Initialize a MetadataFeature - - Args: - data: Some hex encoded metadata. - """ - super().__init__(int(FeatureType.Metadata), data=data) + data: HexStr + type: int = field( + default_factory=lambda: int( + FeatureType.Metadata), + init=False) +@dataclass class TagFeature(Feature): """Tag feature. + Attributes: + tag: A hex encoded tag used to index the output. """ - - def __init__(self, tag: HexStr): - """Initialize a TagFeature - - Args: - tag: A hex encoded tag used to index the output. - """ - super().__init__(int(FeatureType.Tag), tag=tag) + tag: HexStr + type: int = field(default_factory=lambda: int(FeatureType.Tag), init=False) diff --git a/bindings/python/iota_sdk/types/output.py b/bindings/python/iota_sdk/types/output.py index 1d01cdcdbb..2aceadc0d2 100644 --- a/bindings/python/iota_sdk/types/output.py +++ b/bindings/python/iota_sdk/types/output.py @@ -2,14 +2,14 @@ # SPDX-License-Identifier: Apache-2.0 from __future__ import annotations -from dataclasses import dataclass +from dataclasses import dataclass, field from enum import IntEnum from typing import Dict, Optional, List from iota_sdk.types.common import HexStr -from iota_sdk.types.feature import Feature +from iota_sdk.types.feature import SenderFeature, IssuerFeature, MetadataFeature, TagFeature from iota_sdk.types.native_token import NativeToken -from iota_sdk.types.token_scheme import TokenScheme -from iota_sdk.types.unlock_condition import UnlockCondition +from iota_sdk.types.token_scheme import SimpleTokenScheme +from iota_sdk.types.unlock_condition import AddressUnlockCondition, StorageDepositReturnUnlockCondition, TimelockUnlockCondition, ExpirationUnlockCondition, StateControllerAddressUnlockCondition, GovernorAddressUnlockCondition, ImmutableAliasAddressUnlockCondition class OutputType(IntEnum): @@ -32,55 +32,15 @@ class OutputType(IntEnum): @dataclass class Output(): """An output in a UTXO ledger. - - **Attributes** - type : - The type of output. - amount : - The base coin amount of the output. - unlockConditions : - The conditions to unlock the output. - aliasId : - The alias ID if it's an alias output. - nftId : - The NFT ID if it's an NFT output. - stateIndex : - A counter that must increase by 1 every time the alias is state transitioned. - stateMetadata : - Metadata that can only be changed by the state controller. - foundryCounter : - A counter that denotes the number of foundries created by this alias account. - features : - Features that add utility to the output but do not impose unlocking conditions. - nativeTokens : - Native tokens added to the new output. - immutableFeatures : - Features that add utility to the output but do not impose unlocking conditions. These features need to be kept in future transitions of the UTXO state machine. - serialNumber : - The serial number of the foundry with respect to the controlling alias. - tokenScheme : - Defines the supply control scheme of the tokens controlled by the foundry. Currently only a simple scheme is supported. """ type: int - # TODO: split into different outputs - amount: str - unlockConditions: List[UnlockCondition] - aliasId: Optional[HexStr] = None - nftId: Optional[HexStr] = None - stateIndex: Optional[int] = None - stateMetadata: Optional[HexStr] = None - foundryCounter: Optional[int] = None - features: Optional[List[Feature]] = None - nativeTokens: Optional[List[NativeToken]] = None - immutableFeatures: Optional[List[Feature]] = None - serialNumber: Optional[int] = None - tokenScheme: Optional[TokenScheme] = None def as_dict(self): config = {k: v for k, v in self.__dict__.items() if v is not None} - config['unlockConditions'] = list(map( - lambda x: x.as_dict(), config['unlockConditions'])) + if 'unlockConditions' in config: + config['unlockConditions'] = list(map( + lambda x: x.as_dict(), config['unlockConditions'])) if 'nativeTokens' in config: config['nativeTokens'] = list(map( lambda x: x.__dict__, config['nativeTokens'])) @@ -96,6 +56,157 @@ def as_dict(self): return config +@dataclass +class TreasuryOutput(Output): + """Describes a treasury output. + Attributes: + amount : + The base coin amount of the output. + type : + The type of output. + """ + amount: str + type: int = field( + default_factory=lambda: int( + OutputType.Treasury), + init=False) + + +@dataclass +class BasicOutput(Output): + """Describes a basic output. + Attributes: + amount : + The base coin amount of the output. + unlockConditions : + The conditions to unlock the output. + features : + Features that add utility to the output but do not impose unlocking conditions. + nativeTokens : + Native tokens added to the new output. + type : + The type of output. + """ + amount: str + unlockConditions: List[AddressUnlockCondition | ExpirationUnlockCondition | StorageDepositReturnUnlockCondition | + TimelockUnlockCondition] + features: Optional[List[SenderFeature | + MetadataFeature | TagFeature]] = None + nativeTokens: Optional[List[NativeToken]] = None + type: int = field( + default_factory=lambda: int( + OutputType.Basic), + init=False) + + +@dataclass +class AliasOutput(Output): + """Describes an alias output. + Attributes: + amount : + The base coin amount of the output. + unlockConditions : + The conditions to unlock the output. + aliasId : + The alias ID if it's an alias output. + stateIndex : + A counter that must increase by 1 every time the alias is state transitioned. + stateMetadata : + Metadata that can only be changed by the state controller. + foundryCounter : + A counter that denotes the number of foundries created by this alias account. + features : + Features that add utility to the output but do not impose unlocking conditions. + nativeTokens : + Native tokens added to the new output. + immutableFeatures : + Features that add utility to the output but do not impose unlocking conditions. These features need to be kept in future transitions of the UTXO state machine. + type : + The type of output. + """ + amount: str + aliasId: HexStr + stateIndex: int + foundryCounter: int + unlockConditions: List[StateControllerAddressUnlockCondition | + GovernorAddressUnlockCondition] + features: Optional[List[SenderFeature | + MetadataFeature]] = None + immutableFeatures: Optional[List[IssuerFeature | + MetadataFeature]] = None + stateMetadata: Optional[HexStr] = None + nativeTokens: Optional[List[NativeToken]] = None + type: int = field( + default_factory=lambda: int( + OutputType.Alias), + init=False) + + +@dataclass +class FoundryOutput(Output): + """Describes a foundry output. + Attributes: + amount : + The base coin amount of the output. + unlockConditions : + The conditions to unlock the output. + features : + Features that add utility to the output but do not impose unlocking conditions. + nativeTokens : + Native tokens added to the new output. + immutableFeatures : + Features that add utility to the output but do not impose unlocking conditions. These features need to be kept in future transitions of the UTXO state machine. + serialNumber : + The serial number of the foundry with respect to the controlling alias. + tokenScheme : + Defines the supply control scheme of the tokens controlled by the foundry. Currently only a simple scheme is supported. + type : + The type of output. + """ + amount: str + serialNumber: int + tokenScheme: SimpleTokenScheme + unlockConditions: List[ImmutableAliasAddressUnlockCondition] + features: Optional[List[MetadataFeature]] = None + immutableFeatures: Optional[List[MetadataFeature]] = None + nativeTokens: Optional[List[NativeToken]] = None + type: int = field( + default_factory=lambda: int( + OutputType.Foundry), + init=False) + + +@dataclass +class NftOutput(Output): + """Describes an NFT output. + Attributes: + amount : + The base coin amount of the output. + unlockConditions : + The conditions to unlock the output. + nftId : + The NFT ID if it's an NFT output. + features : + Features that add utility to the output but do not impose unlocking conditions. + nativeTokens : + Native tokens added to the new output. + immutableFeatures : + Features that add utility to the output but do not impose unlocking conditions. These features need to be kept in future transitions of the UTXO state machine. + type : + The type of output. + """ + amount: str + nftId: HexStr + unlockConditions: List[AddressUnlockCondition | ExpirationUnlockCondition | + StorageDepositReturnUnlockCondition | TimelockUnlockCondition] + features: Optional[List[SenderFeature | + MetadataFeature | TagFeature]] = None + immutableFeatures: Optional[List[ + IssuerFeature | MetadataFeature]] = None + nativeTokens: Optional[List[NativeToken]] = None + type: int = field(default_factory=lambda: int(OutputType.Nft), init=False) + + @dataclass class OutputMetadata: """Metadata about an output. @@ -145,7 +256,7 @@ class OutputWithMetadata: """ metadata: OutputMetadata - output: Output + output: AliasOutput | FoundryOutput | NftOutput | BasicOutput @classmethod def from_dict(cls, dict: Dict) -> OutputWithMetadata: diff --git a/bindings/python/iota_sdk/types/output_data.py b/bindings/python/iota_sdk/types/output_data.py index 051accafe8..0b92c5e93a 100644 --- a/bindings/python/iota_sdk/types/output_data.py +++ b/bindings/python/iota_sdk/types/output_data.py @@ -4,9 +4,9 @@ from __future__ import annotations from dataclasses import dataclass from typing import Optional -from iota_sdk.types.address import Address +from iota_sdk.types.address import Ed25519Address, AliasAddress, NFTAddress from iota_sdk.types.common import HexStr -from iota_sdk.types.output import Output, OutputMetadata +from iota_sdk.types.output import BasicOutput, AliasOutput, FoundryOutput, NftOutput, OutputMetadata from iota_sdk.types.signature import Bip44 @@ -27,9 +27,9 @@ class OutputData(): outputId: HexStr metadata: OutputMetadata - output: Output + output: AliasOutput | FoundryOutput | NftOutput | BasicOutput isSpent: bool - address: Address + address: Ed25519Address | AliasAddress | NFTAddress networkId: str remainder: bool chain: Optional[Bip44] = None diff --git a/bindings/python/iota_sdk/types/payload.py b/bindings/python/iota_sdk/types/payload.py index 68b2e16f93..080d654df6 100644 --- a/bindings/python/iota_sdk/types/payload.py +++ b/bindings/python/iota_sdk/types/payload.py @@ -3,7 +3,7 @@ from __future__ import annotations from iota_sdk.types.common import HexStr -from iota_sdk.types.output import Output +from iota_sdk.types.output import BasicOutput, AliasOutput, FoundryOutput, NftOutput from iota_sdk.types.input import UtxoInput from iota_sdk.types.signature import Ed25519Signature from iota_sdk.types.unlock import SignatureUnlock, ReferenceUnlock @@ -38,9 +38,9 @@ class RegularTransactionEssence(TransactionEssence): networkId: str inputsCommitment: HexStr inputs: List[UtxoInput] - outputs: List[Output] + outputs: List[AliasOutput | FoundryOutput | NftOutput | BasicOutput] payload: Optional[TaggedDataPayload] = None - type: int = field(default=1, init=False) + type: int = field(default_factory=lambda: 1, init=False) def as_dict(self): config = {k: v for k, v in self.__dict__.items() if v is not None} @@ -108,7 +108,10 @@ class MilestonePayload(Payload): signatures: List[Ed25519Signature] options: Optional[List[Any]] = None metadata: Optional[HexStr] = None - type: int = field(default=int(PayloadType.Milestone), init=False) + type: int = field( + default_factory=lambda: int( + PayloadType.Milestone), + init=False) @classmethod def from_dict(cls, milestone_dict) -> MilestonePayload: @@ -125,7 +128,10 @@ class TaggedDataPayload(Payload): """ tag: HexStr data: HexStr - type: int = field(default=int(PayloadType.TaggedData), init=False) + type: int = field( + default_factory=lambda: int( + PayloadType.TaggedData), + init=False) @dataclass @@ -138,4 +144,7 @@ class TransactionPayload(Payload): """ essence: RegularTransactionEssence unlocks: List[SignatureUnlock | ReferenceUnlock] - type: int = field(default=int(PayloadType.Transaction), init=False) + type: int = field( + default_factory=lambda: int( + PayloadType.Transaction), + init=False) diff --git a/bindings/python/iota_sdk/types/send_params.py b/bindings/python/iota_sdk/types/send_params.py index 2b3713f679..df1fe5bc1a 100644 --- a/bindings/python/iota_sdk/types/send_params.py +++ b/bindings/python/iota_sdk/types/send_params.py @@ -47,13 +47,7 @@ class SendNativeTokensParams(): expiration: Optional[int] = None def as_dict(self): - config = {k: v for k, v in self.__dict__.items() if v is not None} - - config['amount'] = str(config['amount']) - config['native_tokens'] = [native_token.as_dict() - for native_token in config['native_tokens']] - - return config + return {k: v for k, v in self.__dict__.items() if v is not None} @dataclass diff --git a/bindings/python/iota_sdk/types/signature.py b/bindings/python/iota_sdk/types/signature.py index 1b4327a23b..26acc9692a 100644 --- a/bindings/python/iota_sdk/types/signature.py +++ b/bindings/python/iota_sdk/types/signature.py @@ -1,12 +1,19 @@ # Copyright 2023 IOTA Stiftung # SPDX-License-Identifier: Apache-2.0 -from dataclasses import dataclass +from dataclasses import dataclass, field from iota_sdk.types.common import HexStr, CoinType @dataclass -class Ed25519Signature(): +class Signature(): + """Base class of a signature. + """ + type: int + + +@dataclass +class Ed25519Signature(Signature): """An Ed25519 signature. Attributes: @@ -16,7 +23,7 @@ class Ed25519Signature(): """ publicKey: HexStr signature: HexStr - type: int = 0 + type: int = field(default_factory=lambda: 0, init=False) @dataclass diff --git a/bindings/python/iota_sdk/types/token_scheme.py b/bindings/python/iota_sdk/types/token_scheme.py index d392dc1ebf..78aca01a07 100644 --- a/bindings/python/iota_sdk/types/token_scheme.py +++ b/bindings/python/iota_sdk/types/token_scheme.py @@ -1,13 +1,20 @@ # Copyright 2023 IOTA Stiftung # SPDX-License-Identifier: Apache-2.0 -from dataclasses import dataclass +from dataclasses import dataclass, field from iota_sdk.types.common import HexStr @dataclass class TokenScheme(): - """A token scheme. + """Base class of a token scheme. + """ + type: int + + +@dataclass +class SimpleTokenScheme(TokenScheme): + """A simple token scheme. Attributes: mintedTokens: The number of tokens that were minted. @@ -18,7 +25,7 @@ class TokenScheme(): mintedTokens: HexStr meltedTokens: HexStr maximumSupply: HexStr - type: int = 0 + type: int = field(default_factory=lambda: 0, init=False) def as_dict(self): config = dict(self.__dict__) diff --git a/bindings/python/iota_sdk/types/transaction_data.py b/bindings/python/iota_sdk/types/transaction_data.py index bd3abe2e32..58b8c2e272 100644 --- a/bindings/python/iota_sdk/types/transaction_data.py +++ b/bindings/python/iota_sdk/types/transaction_data.py @@ -4,8 +4,8 @@ from __future__ import annotations from dataclasses import dataclass, asdict from typing import Optional, List -from iota_sdk.types.address import Address -from iota_sdk.types.output import Output, OutputMetadata +from iota_sdk.types.address import Ed25519Address, AliasAddress, NFTAddress +from iota_sdk.types.output import BasicOutput, AliasOutput, FoundryOutput, NftOutput, OutputMetadata from iota_sdk.types.payload import RegularTransactionEssence, TransactionPayload from iota_sdk.types.signature import Bip44 @@ -19,7 +19,7 @@ class InputSigningData: outputMetadata: The output metadata. chain: The BIP44 chain for the address to unlock the output. """ - output: Output + output: AliasOutput | FoundryOutput | NftOutput | BasicOutput outputMetadata: OutputMetadata chain: Optional[Bip44] = None @@ -43,8 +43,8 @@ class RemainderData: address: The remainder address. chain: The BIP44 chain for the remainder address. """ - output: Output - address: Address + output: AliasOutput | FoundryOutput | NftOutput | BasicOutput + address: Ed25519Address | AliasAddress | NFTAddress chain: Optional[Bip44] = None def as_dict(self): diff --git a/bindings/python/iota_sdk/types/unlock.py b/bindings/python/iota_sdk/types/unlock.py index e3ad3d1d75..0ed69ad0b0 100644 --- a/bindings/python/iota_sdk/types/unlock.py +++ b/bindings/python/iota_sdk/types/unlock.py @@ -35,7 +35,10 @@ class SignatureUnlock(Unlock): """An unlock holding a signature unlocking one or more inputs. """ signature: Ed25519Signature - type: int = field(default=int(UnlockType.Signature), init=False) + type: int = field( + default_factory=lambda: int( + UnlockType.Signature), + init=False) @dataclass @@ -43,7 +46,10 @@ class ReferenceUnlock(Unlock): """An unlock which must reference a previous unlock which unlocks also the input at the same index as this Reference Unlock. """ reference: int - type: int = field(default=int(UnlockType.Reference), init=False) + type: int = field( + default_factory=lambda: int( + UnlockType.Reference), + init=False) @dataclass @@ -51,7 +57,10 @@ class AliasUnlock: """An unlock which must reference a previous unlock which unlocks the alias that the input is locked to. """ reference: int - type: int = field(default=int(UnlockType.Alias), init=False) + type: int = field( + default_factory=lambda: int( + UnlockType.Alias), + init=False) @dataclass @@ -59,4 +68,4 @@ class NftUnlock: """An unlock which must reference a previous unlock which unlocks the NFT that the input is locked to. """ reference: int - type: int = field(default=int(UnlockType.Nft), init=False) + type: int = field(default_factory=lambda: int(UnlockType.Nft), init=False) diff --git a/bindings/python/iota_sdk/types/unlock_condition.py b/bindings/python/iota_sdk/types/unlock_condition.py index afee3c65c5..de02e74f8b 100644 --- a/bindings/python/iota_sdk/types/unlock_condition.py +++ b/bindings/python/iota_sdk/types/unlock_condition.py @@ -1,10 +1,9 @@ # Copyright 2023 IOTA Stiftung # SPDX-License-Identifier: Apache-2.0 -from iota_sdk.types.address import Address +from iota_sdk.types.address import Ed25519Address, AliasAddress, NFTAddress from enum import IntEnum -from typing import Optional -from dataclasses import dataclass +from dataclasses import dataclass, field class UnlockConditionType(IntEnum): @@ -31,24 +30,16 @@ class UnlockConditionType(IntEnum): @dataclass class UnlockCondition(): """Base class for unlock conditions. - - Attributes: - type: The type code of the unlock condition. - amount: Some amount depending on the unlock condition type. - address: Some address depending on the unlock condition type. - unixTime: Some Unix timestamp depending on the unlock condition type. - return_address: An address to return funds to. """ - type: int - amount: Optional[str] = None - address: Optional[Address] = None - unixTime: Optional[int] = None - returnAddress: Optional[Address] = None def as_dict(self): config = {k: v for k, v in self.__dict__.items() if v is not None} + if 'amount' in config: + if isinstance(config['amount'], int): + config['amount'] = str(config['amount']) + if 'address' in config: config['address'] = config['address'].as_dict() @@ -58,96 +49,89 @@ def as_dict(self): return config +@dataclass class AddressUnlockCondition(UnlockCondition): """An address unlock condition. - """ - - def __init__(self, address): - """Initialize `Self`. - Args: - address: An address unlocked with a private key. - """ - super().__init__(type=UnlockConditionType.Address, address=address) + Args: + address: An address unlocked with a private key. + """ + address: Ed25519Address | AliasAddress | NFTAddress + type: int = field( + default_factory=lambda: int( + UnlockConditionType.Address), + init=False) +@dataclass class StorageDepositReturnUnlockCondition(UnlockCondition): """A storage-deposit-return unlock condition. + Args: + amount: The amount of base coins the consuming transaction must deposit to `return_address`. + return_address: The address to return the amount to. """ - - def __init__(self, amount, return_address): - """Initialize `Self`. - - Args: - amount: The amount of base coins the consuming transaction must deposit to `return_address`. - return_address: The address to return the amount to. - """ - super().__init__(type=UnlockConditionType.StorageDepositReturn, - amount=str(amount), returnAddress=return_address) + amount: str + returnAddress: Ed25519Address | AliasAddress | NFTAddress + type: int = field(default_factory=lambda: int( + UnlockConditionType.StorageDepositReturn), init=False) +@dataclass class TimelockUnlockCondition(UnlockCondition): """A timelock unlock condition. + Args: + unix_time: The Unix timestamp marking the end of the timelock. """ - - def __init__(self, unix_time): - """Initialize `Self`. - - Args: - unix_time: The Unix timestamp marking the end of the timelock. - """ - super().__init__(type=UnlockConditionType.Timelock, unixTime=unix_time) + unixTime: int + type: int = field( + default_factory=lambda: int( + UnlockConditionType.Timelock), + init=False) +@dataclass class ExpirationUnlockCondition(UnlockCondition): """An expiration unlock condition. + Args: + unix_time: Unix timestamp marking the expiration of the claim. + return_address: The return address if the output was not claimed in time. """ - - def __init__(self, unix_time, return_address): - """Initialize an ExpirationUnlockCondition - - Args: - unix_time: Unix timestamp marking the expiration of the claim. - return_address: The return address if the output was not claimed in time. - """ - super().__init__(type=UnlockConditionType.Expiration, - unixTime=unix_time, returnAddress=return_address) + unixTime: int + returnAddress: Ed25519Address | AliasAddress | NFTAddress + type: int = field( + default_factory=lambda: int( + UnlockConditionType.Expiration), + init=False) +@dataclass class StateControllerAddressUnlockCondition(UnlockCondition): """A state controller address unlock condition. + Args: + address: The state controller address that owns the output. """ - - def __init__(self, address): - """Initialize `Self`. - - Args: - address: The state controller address that owns the output. - """ - super().__init__(type=UnlockConditionType.StateControllerAddress, address=address) + address: Ed25519Address | AliasAddress | NFTAddress + type: int = field(default_factory=lambda: int( + UnlockConditionType.StateControllerAddress), init=False) +@dataclass class GovernorAddressUnlockCondition(UnlockCondition): """A governor address unlock condition. + Args: + address: The governor address that owns the output. """ - - def __init__(self, address): - """Initialize `Self`. - - Args: - address: The governor address that owns the output. - """ - super().__init__(type=UnlockConditionType.GovernorAddress, address=address) + address: Ed25519Address | AliasAddress | NFTAddress + type: int = field(default_factory=lambda: int( + UnlockConditionType.GovernorAddress), init=False) +@dataclass class ImmutableAliasAddressUnlockCondition(UnlockCondition): """An immutable alias address unlock condition. + Args: + address: The permanent alias address that owns this output. """ - - def __init__(self, address): - """Initialize `Self`. - - Args: - address: The permanent alias address that owns this output. - """ - super().__init__(type=UnlockConditionType.ImmutableAliasAddress, address=address) + address: AliasAddress + type: int = field(default_factory=lambda: int( + UnlockConditionType.ImmutableAliasAddress), init=False) diff --git a/bindings/python/iota_sdk/types/utxo_changes.py b/bindings/python/iota_sdk/types/utxo_changes.py new file mode 100644 index 0000000000..342e36f7ae --- /dev/null +++ b/bindings/python/iota_sdk/types/utxo_changes.py @@ -0,0 +1,16 @@ +# Copyright 2023 IOTA Stiftung +# SPDX-License-Identifier: Apache-2.0 + +from iota_sdk.types.common import HexStr +from typing import List +from dataclasses import dataclass + + +@dataclass +class UtxoChanges(): + """Response of GET /api/core/v2/milestone/{milestone_index}/utxo-changes. + Returns all UTXO changes that happened at a specific milestone. + """ + index: int + createdOutputs: List[HexStr] + consumedOutputs: List[HexStr] diff --git a/bindings/python/tests/test_block.py b/bindings/python/tests/test_block.py index ed57b81740..4c76d98d49 100644 --- a/bindings/python/tests/test_block.py +++ b/bindings/python/tests/test_block.py @@ -1,4 +1,3 @@ - # Copyright 2023 IOTA Stiftung # SPDX-License-Identifier: Apache-2.0 diff --git a/bindings/python/tests/test_output.py b/bindings/python/tests/test_output.py index 951027a42f..201d4059a2 100644 --- a/bindings/python/tests/test_output.py +++ b/bindings/python/tests/test_output.py @@ -2,7 +2,7 @@ # Copyright 2023 IOTA Stiftung # SPDX-License-Identifier: Apache-2.0 -from iota_sdk import Output, Feature, IssuerFeature, MetadataFeature +from iota_sdk import BasicOutput, AliasOutput, FoundryOutput, NftOutput, IssuerFeature, MetadataFeature from dacite import from_dict @@ -11,10 +11,8 @@ def test_feature(): "type": 2, "data": "0x426c61" } - feature = from_dict(Feature, feature_dict) - sender_feature = feature.into() - assert isinstance(sender_feature, MetadataFeature) - assert sender_feature.as_dict() == feature_dict + metadata_feature = from_dict(MetadataFeature, feature_dict) + assert metadata_feature.as_dict() == feature_dict issuer_dict = { "type": 1, @@ -23,9 +21,7 @@ def test_feature(): "pubKeyHash": "0xd970bcafdc18859b3fd3380f759bb520c36a29bd682b130623c6604ce3526ea1" } } - feature = from_dict(Feature, issuer_dict) - issuer_feature = feature.into() - assert isinstance(issuer_feature, IssuerFeature) + issuer_feature = from_dict(IssuerFeature, issuer_dict) assert issuer_feature.as_dict() == issuer_dict @@ -43,7 +39,7 @@ def test_output(): } ] } - basic_output = from_dict(Output, basic_output_dict) + basic_output = from_dict(BasicOutput, basic_output_dict) assert basic_output.as_dict() == basic_output_dict basic_output_dict = { @@ -81,7 +77,7 @@ def test_output(): } ] } - basic_output = from_dict(Output, basic_output_dict) + basic_output = from_dict(BasicOutput, basic_output_dict) assert basic_output.as_dict() == basic_output_dict basic_output_dict = { @@ -107,7 +103,7 @@ def test_output(): } ] } - basic_output = from_dict(Output, basic_output_dict) + basic_output = from_dict(BasicOutput, basic_output_dict) assert basic_output.as_dict() == basic_output_dict alias_output_dict = { @@ -143,7 +139,7 @@ def test_output(): } ] } - alias_output = from_dict(Output, alias_output_dict) + alias_output = from_dict(AliasOutput, alias_output_dict) assert alias_output.as_dict() == alias_output_dict alias_output_dict = { @@ -183,7 +179,7 @@ def test_output(): } ] } - alias_output = from_dict(Output, alias_output_dict) + alias_output = from_dict(AliasOutput, alias_output_dict) assert alias_output.as_dict() == alias_output_dict foundry_output_dict = { @@ -212,7 +208,7 @@ def test_output(): } ] } - foundry_output = from_dict(Output, foundry_output_dict) + foundry_output = from_dict(FoundryOutput, foundry_output_dict) assert foundry_output.as_dict() == foundry_output_dict nft_output_dict = { @@ -241,5 +237,5 @@ def test_output(): } ] } - nft_output = from_dict(Output, nft_output_dict) + nft_output = from_dict(NftOutput, nft_output_dict) assert nft_output.as_dict() == nft_output_dict