From d1a0a484acabb98af57139a8647cefc1a3ec66d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thoralf=20M=C3=BCller?= Date: Tue, 20 Jun 2023 16:03:25 +0200 Subject: [PATCH] Wen not ValueError: Circular reference detected --- .../simple_transaction/simple_transaction.py | 7 +- bindings/python/iota_sdk/__init__.py | 2 + bindings/python/iota_sdk/types/payload.py | 63 ++++++++++++++++++ .../iota_sdk/types/transaction_options.py | 64 +++++++++++++++++++ bindings/python/iota_sdk/wallet/account.py | 55 ++++++++-------- bindings/python/iota_sdk/wallet/common.py | 10 ++- 6 files changed, 168 insertions(+), 33 deletions(-) create mode 100644 bindings/python/iota_sdk/types/payload.py create mode 100644 bindings/python/iota_sdk/types/transaction_options.py diff --git a/bindings/python/examples/how_tos/simple_transaction/simple_transaction.py b/bindings/python/examples/how_tos/simple_transaction/simple_transaction.py index 3326ad0f41..86bc8ef0a2 100644 --- a/bindings/python/examples/how_tos/simple_transaction/simple_transaction.py +++ b/bindings/python/examples/how_tos/simple_transaction/simple_transaction.py @@ -1,4 +1,4 @@ -from iota_sdk import Wallet +from iota_sdk import Wallet, TransactionOptions, TaggedDataPayload from dotenv import load_dotenv import os @@ -23,5 +23,6 @@ "amount": "1000000", }] -transaction = account.send_amount(outputs) -print(f'Block sent: {os.environ["EXPLORER_URL"]}/block/{transaction["blockId"]}') \ No newline at end of file +transaction = account.send_amount(outputs, TransactionOptions(note="my first tx", tagged_data_payload=TaggedDataPayload("tag", "data"))) +print(transaction) +# print(f'Block sent: {os.environ["EXPLORER_URL"]}/block/{transaction["blockId"]}') \ No newline at end of file diff --git a/bindings/python/iota_sdk/__init__.py b/bindings/python/iota_sdk/__init__.py index 610afe11d4..aa815b24bb 100644 --- a/bindings/python/iota_sdk/__init__.py +++ b/bindings/python/iota_sdk/__init__.py @@ -14,5 +14,7 @@ from .types.feature import * from .types.native_token import * from .types.output_id import * +from .types.payload import * from .types.token_scheme import * +from .types.transaction_options import * from .types.unlock_condition import * diff --git a/bindings/python/iota_sdk/types/payload.py b/bindings/python/iota_sdk/types/payload.py new file mode 100644 index 0000000000..95a760df70 --- /dev/null +++ b/bindings/python/iota_sdk/types/payload.py @@ -0,0 +1,63 @@ +# Copyright 2023 IOTA Stiftung +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum +from typing import Any, Optional + + +class PayloadType(Enum): + TreasuryTransaction = 4 + TaggedData = 5 + Transaction = 6 + Milestone = 7 + + +class Payload(): + def __init__(self, type, milestone: Optional[Any] = None, tagged_data=None, transaction=None, treasury_transaction: Optional[Any] = None): + """Initialize a payload + """ + self.type = type + self.milestone = milestone + self.tagged_data = tagged_data + self.transaction = transaction + self.treasury_transaction = treasury_transaction + + def as_dict(self): + config = {k: v for k, v in self.__dict__.items() if v != None} + + config['type'] = config['type'].value + + if 'sender' in config: + config['address'] = config.pop('sender').as_dict() + + if 'issuer' in config: + config['address'] = config.pop('issuer').as_dict() + + return config + + +class MilestonePayload(Payload): + def __init__(self, essence, signatures): + """Initialize a MilestonePayload + """ + self.essence = essence + self.signatures = signatures + super().__init__(PayloadType.Milestone, milestone=self) + + +class TaggedDataPayload(Payload): + def __init__(self, tag, data): + """Initialize a TaggedDataPayload + """ + self.tag = tag + self.data = data + super().__init__(PayloadType.TaggedData, tagged_data=self) + + +class TransactionPayload(Payload): + def __init__(self, essence, unlocks): + """Initialize a TransactionPayload + """ + self.essence = essence + self.unlocks = unlocks + super().__init__(PayloadType.Transaction, transaction=self) diff --git a/bindings/python/iota_sdk/types/transaction_options.py b/bindings/python/iota_sdk/types/transaction_options.py new file mode 100644 index 0000000000..90ef7e608f --- /dev/null +++ b/bindings/python/iota_sdk/types/transaction_options.py @@ -0,0 +1,64 @@ +# Copyright 2023 IOTA Stiftung +# SPDX-License-Identifier: Apache-2.0 + +from iota_sdk.types.burn import Burn +from iota_sdk.types.output_id import OutputId +from iota_sdk.types.payload import TaggedDataPayload +from enum import Enum +from typing import Optional, List + +class AccountAddress: + def __int__(self, + address: str, + key_index: int, + internal: bool, + used: bool): + self.address = address + self.keyIndex = key_index + self.internal = internal + self.used = used + + +class CustomAddress(Enum): + AccountAddress=AccountAddress + +class RemainderValueStrategy(Enum): + ChangeAddress = "ChangeAddress", + ReuseAddress = "ReuseAddress", + CustomAddress = CustomAddress + + def __int__(self): + return self.value + + +class TransactionOptions(): + def __init__(self, remainder_value_strategy: RemainderValueStrategy = RemainderValueStrategy.ReuseAddress, + tagged_data_payload: Optional[TaggedDataPayload] = None, + custom_inputs: Optional[List[OutputId]] = None, + mandatory_inputs: Optional[List[OutputId]] = None, + burn: Optional[Burn] = None, + note: Optional[str] = None, + allow_micro_amount: Optional[bool] = None): + """Initialize TransactionOptions + """ + self.remainder_value_strategy = remainder_value_strategy + self.tagged_data_payload = tagged_data_payload + self.custom_inputs = custom_inputs + self.mandatory_inputs = mandatory_inputs + self.burn = burn + self.note = note + self.allow_micro_amount = allow_micro_amount + + def as_dict(self): + config = dict(self.__dict__) + + # config['type'] = config['type'].value + + # if self.type == AddressType.ED25519: + # config['pubKeyHash'] = config.pop('address_or_id') + # elif self.type == AddressType.ALIAS: + # config['aliasId'] = config.pop('address_or_id') + # elif self.type == AddressType.NFT: + # config['nftId'] = config.pop('address_or_id') + + return {k: v for k, v in config.items() if v} \ No newline at end of file diff --git a/bindings/python/iota_sdk/wallet/account.py b/bindings/python/iota_sdk/wallet/account.py index 1cd582366e..c9f25dce4b 100644 --- a/bindings/python/iota_sdk/wallet/account.py +++ b/bindings/python/iota_sdk/wallet/account.py @@ -7,6 +7,7 @@ from iota_sdk.types.common import HexStr from iota_sdk.types.native_token import NativeToken from iota_sdk.types.output_id import OutputId +from iota_sdk.types.transaction_options import TransactionOptions from typing import List, Optional @@ -41,7 +42,7 @@ def _call_account_method(self, method, data=None): return message - def prepare_burn(self, burn: Burn, options=None): + def prepare_burn(self, burn: Burn, options: Optional[TransactionOptions] = None): """ A generic `prepare_burn()` function that can be used to prepare the burn of native tokens, nfts, foundries and aliases. """ @@ -54,9 +55,9 @@ def prepare_burn(self, burn: Burn, options=None): return PreparedTransactionData(self, prepared) def prepare_burn_native_token(self, - token_id: HexStr, - burn_amount: int, - options=None): + token_id: HexStr, + burn_amount: int, + options: Optional[TransactionOptions] = None): """Burn native tokens. This doesn't require the foundry output which minted them, but will not increase the foundries `melted_tokens` field, which makes it impossible to destroy the foundry output. Therefore it's recommended to use melting, if the foundry output is available. @@ -70,8 +71,8 @@ def prepare_burn_native_token(self, return PreparedTransactionData(self, prepared) def prepare_burn_nft(self, - nft_id: HexStr, - options=None): + nft_id: HexStr, + options: Optional[TransactionOptions] = None): """Burn an nft output. """ prepared = self._call_account_method( @@ -83,8 +84,8 @@ def prepare_burn_nft(self, return PreparedTransactionData(self, prepared) def prepare_consolidate_outputs(self, - force: bool, - output_consolidation_threshold: Optional[int] = None): + force: bool, + output_consolidation_threshold: Optional[int] = None): """Consolidate outputs. """ prepared = self._call_account_method( @@ -96,8 +97,8 @@ def prepare_consolidate_outputs(self, return PreparedTransactionData(self, prepared) def prepare_create_alias_output(self, - params, - options): + params, + options: Optional[TransactionOptions] = None): """Create an alias output. """ prepared = self._call_account_method( @@ -109,8 +110,8 @@ def prepare_create_alias_output(self, return PreparedTransactionData(self, prepared) def prepare_destroy_alias(self, - alias_id: HexStr, - options=None): + alias_id: HexStr, + options: Optional[TransactionOptions] = None): """Destroy an alias output. """ @@ -123,8 +124,8 @@ def prepare_destroy_alias(self, return PreparedTransactionData(self, prepared) def prepare_destroy_foundry(self, - foundry_id: HexStr, - options=None): + foundry_id: HexStr, + options: Optional[TransactionOptions] = None): """Destroy a foundry output with a circulating supply of 0. """ prepared = self._call_account_method( @@ -226,9 +227,9 @@ def pending_transactions(self): ) def prepare_decrease_native_token_supply(self, - token_id: HexStr, - melt_amount: int, - options=None): + token_id: HexStr, + melt_amount: int, + options: Optional[TransactionOptions] = None): """Melt native tokens. This happens with the foundry output which minted them, by increasing it's `melted_tokens` field. """ @@ -241,7 +242,7 @@ def prepare_decrease_native_token_supply(self, ) return PreparedTransactionData(self, prepared) - def prepare_increase_native_token_supply(self, token_id: HexStr, mint_amount: int, options=None): + def prepare_increase_native_token_supply(self, token_id: HexStr, mint_amount: int, options: Optional[TransactionOptions] = None): """Mint more native token. """ prepared = self._call_account_method( @@ -253,7 +254,7 @@ def prepare_increase_native_token_supply(self, token_id: HexStr, mint_amount: in ) return PreparedMintTokenTransaction(account=self, prepared_transaction_data=prepared) - def prepare_mint_native_token(self, params, options=None): + def prepare_mint_native_token(self, params, options: Optional[TransactionOptions] = None): """Mint native token. """ prepared = self._call_account_method( @@ -273,7 +274,7 @@ def minimum_required_storage_deposit(self, output): } ) - def prepare_mint_nfts(self, params, options=None): + def prepare_mint_nfts(self, params, options: Optional[TransactionOptions] = None): """Mint nfts. """ prepared = self._call_account_method( @@ -291,7 +292,7 @@ def get_balance(self): 'getBalance' ) - def prepare_output(self, output_options, transaction_options=None): + def prepare_output(self, output_options, transaction_options: Optional[TransactionOptions] = None): """Prepare an output for sending If the amount is below the minimum required storage deposit, by default the remaining amount will automatically be added with a StorageDepositReturn UnlockCondition, when setting the ReturnStrategy to `gift`, the full @@ -306,7 +307,7 @@ def prepare_output(self, output_options, transaction_options=None): } ) - def prepare_send_amount(self, params, options=None): + def prepare_send_amount(self, params, options: Optional[TransactionOptions] = None): """Prepare send amount. """ prepared = self._call_account_method( @@ -317,7 +318,7 @@ def prepare_send_amount(self, params, options=None): ) return PreparedTransactionData(self, prepared) - def prepare_transaction(self, outputs, options=None): + def prepare_transaction(self, outputs, options: Optional[TransactionOptions] = None): """Prepare transaction. """ prepared = self._call_account_method( @@ -351,7 +352,7 @@ def sync(self, options=None): } ) - def send_amount(self, params, options=None): + def send_amount(self, params, options: Optional[TransactionOptions] = None): """Send amount. """ return self._call_account_method( @@ -361,7 +362,7 @@ def send_amount(self, params, options=None): } ) - def prepare_send_native_tokens(self, params, options=None): + def prepare_send_native_tokens(self, params, options: Optional[TransactionOptions] = None): """Send native tokens. """ prepared = self._call_account_method( @@ -372,7 +373,7 @@ def prepare_send_native_tokens(self, params, options=None): ) return PreparedTransactionData(self, prepared) - def prepare_send_nft(self, params, options=None): + def prepare_send_nft(self, params, options: Optional[TransactionOptions] = None): """Send nft. """ prepared = self._call_account_method( @@ -438,7 +439,7 @@ def claim_outputs(self, output_ids_to_claim: List[OutputId]): } ) - def send_outputs(self, outputs, options=None): + def send_outputs(self, outputs, options: Optional[TransactionOptions] = None): """Send outputs in a transaction. """ return self._call_account_method( diff --git a/bindings/python/iota_sdk/wallet/common.py b/bindings/python/iota_sdk/wallet/common.py index 0a7d5b45bf..26e99f7bc5 100644 --- a/bindings/python/iota_sdk/wallet/common.py +++ b/bindings/python/iota_sdk/wallet/common.py @@ -2,17 +2,21 @@ # SPDX-License-Identifier: Apache-2.0 from iota_sdk import call_wallet_method +import humps import json -from json import dumps +from json import dumps, JSONEncoder def _call_method_routine(func): """The routine of dump json string and call call_wallet_method() """ def wrapper(*args, **kwargs): - message = func(*args, **kwargs) - message = dumps(message) + class MyEncoder(JSONEncoder): + def default(self, obj): + return humps.camelize({k: v for k, v in obj.__dict__.items() if v} ) + message = func(*args, **kwargs) + message = dumps(message, cls=MyEncoder) # Send message to the Rust library response = call_wallet_method(args[0].handle, message)