diff --git a/Cargo.lock b/Cargo.lock index fd75fce4da..516b0c9c6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1528,6 +1528,12 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "indoc" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8" + [[package]] name = "inout" version = "0.1.3" @@ -1688,6 +1694,18 @@ dependencies = [ "tokio", ] +[[package]] +name = "iota-sdk-python" +version = "1.1.2" +dependencies = [ + "futures", + "iota-sdk-bindings-core", + "once_cell", + "pyo3", + "serde_json", + "tokio", +] + [[package]] name = "iota-sdk-wasm" version = "0.1.0" @@ -2165,6 +2183,29 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.48.5", +] + [[package]] name = "paste" version = "1.0.14" @@ -2377,6 +2418,67 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "pyo3" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04e8453b658fe480c3e70c8ed4e3d3ec33eb74988bd186561b0cc66b85c3bc4b" +dependencies = [ + "cfg-if", + "indoc", + "libc", + "memoffset 0.9.0", + "parking_lot", + "pyo3-build-config", + "pyo3-ffi", + "pyo3-macros", + "unindent", +] + +[[package]] +name = "pyo3-build-config" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96fe70b176a89cff78f2fa7b3c930081e163d5379b4dcdf993e3ae29ca662e5" +dependencies = [ + "once_cell", + "target-lexicon", +] + +[[package]] +name = "pyo3-ffi" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "214929900fd25e6604661ed9cf349727c8920d47deff196c4e28165a6ef2a96b" +dependencies = [ + "libc", + "pyo3-build-config", +] + +[[package]] +name = "pyo3-macros" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dac53072f717aa1bfa4db832b39de8c875b7c7af4f4a6fe93cdbf9264cf8383b" +dependencies = [ + "proc-macro2", + "pyo3-macros-backend", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "pyo3-macros-backend" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7774b5a8282bd4f25f803b1f0d945120be959a36c72e08e7cd031c792fdfd424" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.41", +] + [[package]] name = "quote" version = "1.0.33" @@ -3092,6 +3194,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "target-lexicon" +version = "0.12.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a" + [[package]] name = "thiserror" version = "1.0.51" @@ -3370,6 +3478,12 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +[[package]] +name = "unindent" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" + [[package]] name = "universal-hash" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index 51fea11cf3..99647d0d92 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,8 +3,7 @@ resolver = "2" members = [ "bindings/core", "bindings/nodejs", - # TODO: issue #1423 - #"bindings/python", + "bindings/python", "bindings/wasm", "cli", "sdk", diff --git a/bindings/python/.pylintrc b/bindings/python/.pylintrc index cb914f0ddd..fb59d293c6 100644 --- a/bindings/python/.pylintrc +++ b/bindings/python/.pylintrc @@ -9,7 +9,7 @@ disable=missing-module-docstring, fixme, # TODOS too-many-instance-attributes, too-many-arguments, - too-few-public-methods + too-few-public-methods, too-many-public-methods, too-many-locals diff --git a/bindings/python/README.md b/bindings/python/README.md index d8cea197eb..d5d3df0ac8 100644 --- a/bindings/python/README.md +++ b/bindings/python/README.md @@ -96,7 +96,7 @@ The following example creates a Client instance connected to the Shimmer Testnet ## Wallet Usage -The following example will create a new Wallet Account using a StrongholdSecretManager, and then print the account's information. +The following example will create a new Wallet using a StrongholdSecretManager, and then print the wallet's information. [examples/wallet/getting_started.py](examples/wallet/getting_started.py) diff --git a/bindings/python/examples/client/04_get_output.py b/bindings/python/examples/client/04_get_output.py index 7c3b10e87d..8e1edde5ec 100644 --- a/bindings/python/examples/client/04_get_output.py +++ b/bindings/python/examples/client/04_get_output.py @@ -14,5 +14,5 @@ # Get an outputs by its id output_with_metadata = client.get_output( - '0x022aefa73dff09b35b21ab5493412b0d354ad07a970a12b71e8087c6f3a7b8660000') + '0x022aefa73dff09b35b21ab5493412b0d354ad07a970a12b71e8087c6f3a7b866000000000000') print(json.dumps(output_with_metadata.to_dict(), indent=4)) diff --git a/bindings/python/examples/exchange/1_create_account.py b/bindings/python/examples/exchange/1_create_wallet.py similarity index 68% rename from bindings/python/examples/exchange/1_create_account.py rename to bindings/python/examples/exchange/1_create_wallet.py index d870accd6d..c013112e88 100644 --- a/bindings/python/examples/exchange/1_create_account.py +++ b/bindings/python/examples/exchange/1_create_wallet.py @@ -8,7 +8,7 @@ from dotenv import load_dotenv from iota_sdk import (ClientOptions, CoinType, StrongholdSecretManager, - SyncOptions, Wallet) + SecretManager, SyncOptions, Wallet, WalletOptions, Bip44) # This example uses secrets in environment variables for simplicity which # should not be done in production. @@ -24,19 +24,27 @@ secret_manager = StrongholdSecretManager( os.environ.get('STRONGHOLD_SNAPSHOT_PATH'), os.environ['STRONGHOLD_PASSWORD']) -wallet = Wallet(os.environ.get('WALLET_DB_PATH'), - client_options, CoinType.IOTA, secret_manager) - # Store the mnemonic in the Stronghold snapshot, this only needs to be # done once. -wallet.store_mnemonic( - os.environ['MNEMONIC']) - -account = wallet.create_account('Alice') +SecretManager(secret_manager).store_mnemonic(os.environ['MNEMONIC']) + +bip_path = Bip44( + coin_type=CoinType.SHIMMER +) +wallet_options = WalletOptions( + None, + None, + bip_path, + client_options, + secret_manager, + os.environ.get('WALLET_DB_PATH')) +wallet = Wallet(wallet_options) # Set sync_only_most_basic_outputs to True if not interested in outputs that are timelocked, # have a storage deposit return, expiration or are nft/account/foundry outputs. -account.set_default_sync_options( +wallet.set_default_sync_options( SyncOptions(sync_only_most_basic_outputs=True)) -print(account.get_metadata()) +# Update the wallet to the latest state +balance = wallet.sync() +print('Generated new wallet') diff --git a/bindings/python/examples/exchange/2_generate_address.py b/bindings/python/examples/exchange/2_generate_address.py index 36143e754c..4a329a0d52 100644 --- a/bindings/python/examples/exchange/2_generate_address.py +++ b/bindings/python/examples/exchange/2_generate_address.py @@ -1,27 +1,24 @@ # Copyright 2023 IOTA Stiftung # SPDX-License-Identifier: Apache-2.0 -# This example generates an address for an account. +# This example generates an address for a wallet. import os from dotenv import load_dotenv -from iota_sdk import Wallet +from iota_sdk import StrongholdSecretManager, SecretManager # This example uses secrets in environment variables for simplicity which # should not be done in production. load_dotenv() -for env_var in ['WALLET_DB_PATH', 'STRONGHOLD_PASSWORD']: +for env_var in ['STRONGHOLD_SNAPSHOT_PATH', 'STRONGHOLD_PASSWORD']: if env_var not in os.environ: raise Exception(f'.env {env_var} is undefined, see .env.example') -wallet = Wallet(os.environ.get('WALLET_DB_PATH')) +secret_manager = SecretManager(StrongholdSecretManager( + os.environ.get('STRONGHOLD_SNAPSHOT_PATH'), os.environ['STRONGHOLD_PASSWORD'])) -wallet.set_stronghold_password(os.environ["STRONGHOLD_PASSWORD"]) - -account = wallet.get_account('Alice') - -address = account.generate_ed25519_addresses(1)[0] -print('Address:', address.address) +address = secret_manager.generate_ed25519_addresses(1)[0] +print('Address:', address) diff --git a/bindings/python/examples/exchange/3_check_balance.py b/bindings/python/examples/exchange/3_check_balance.py index 1949e38f6c..dadcf3ed17 100644 --- a/bindings/python/examples/exchange/3_check_balance.py +++ b/bindings/python/examples/exchange/3_check_balance.py @@ -1,32 +1,31 @@ # Copyright 2023 IOTA Stiftung # SPDX-License-Identifier: Apache-2.0 -# This example gets the balance of an account. +# This example gets the balance of a wallet. import os from dotenv import load_dotenv -from iota_sdk import SyncOptions, Wallet +from iota_sdk import SyncOptions, Wallet, WalletOptions # This example uses secrets in environment variables for simplicity which # should not be done in production. load_dotenv() -if 'WALLET_DB_PATH' not in os.environ: - raise Exception(".env WALLET_DB_PATH is undefined, see .env.example") +for env_var in ['WALLET_DB_PATH', 'FAUCET_URL']: + if env_var not in os.environ: + raise Exception(f'.env {env_var} is undefined, see .env.example') -wallet = Wallet(os.environ.get('WALLET_DB_PATH')) +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) -account = wallet.get_account('Alice') - -addresses = account.addresses() -print('Addresses:', addresses) +address = wallet.address() +print('Address:', address) # Set sync_only_most_basic_outputs to True if not interested in outputs that are timelocked, # have a storage deposit return, expiration or are nft/account/foundry outputs. -balance = account.sync(SyncOptions(sync_only_most_basic_outputs=True)) +balance = wallet.sync(SyncOptions(sync_only_most_basic_outputs=True)) print('Balance', balance) # Use the faucet to send tokens to your address. -print('Fill your address with the Faucet: https://faucet.testnet.shimmer.network/') +print(f'Fill your address with the Faucet: {os.environ["FAUCET_URL"]}') diff --git a/bindings/python/examples/exchange/4_listen_events.py b/bindings/python/examples/exchange/4_listen_events.py index 0da6091589..ca89373e93 100644 --- a/bindings/python/examples/exchange/4_listen_events.py +++ b/bindings/python/examples/exchange/4_listen_events.py @@ -10,18 +10,17 @@ from dotenv import load_dotenv -from iota_sdk import SyncOptions, Wallet, WalletEventType +from iota_sdk import SyncOptions, Wallet, WalletOptions, WalletEventType # This example uses secrets in environment variables for simplicity which # should not be done in production. load_dotenv() -if 'WALLET_DB_PATH' not in os.environ: - raise Exception(".env WALLET_DB_PATH is undefined, see .env.example") +for env_var in ['WALLET_DB_PATH', 'FAUCET_URL']: + if env_var not in os.environ: + raise Exception(".env WALLET_DB_PATH is undefined, see .env.example") -wallet = Wallet(os.environ.get('WALLET_DB_PATH')) - -account = wallet.get_account('Alice') +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) received_event = False @@ -41,13 +40,11 @@ def callback(event): # Only interested in new outputs here. wallet.listen(callback, [WalletEventType.NewOutput]) -account = wallet.get_account('Alice') - # Use the faucet to send testnet tokens to your address. -print('Fill your address with the faucet: https://faucet.testnet.shimmer.network/') +print(f'Fill your address with the Faucet: {os.environ["FAUCET_URL"]}') -addresses = account.addresses() -print('Send funds to:', addresses[0].address) +address = wallet.address() +print('Send funds to:', address) # Sync every 5 seconds until the faucet transaction gets confirmed. for _ in range(100): @@ -59,4 +56,4 @@ def callback(event): # Set sync_only_most_basic_outputs to True if not interested in outputs that are timelocked, # have a storage deposit return , expiration or are nft/account/foundry # outputs. - account.sync(SyncOptions(sync_only_most_basic_outputs=True)) + wallet.sync(SyncOptions(sync_only_most_basic_outputs=True)) diff --git a/bindings/python/examples/exchange/5_send_amount.py b/bindings/python/examples/exchange/5_send_amount.py index 2f0a2fecb0..62465aa605 100644 --- a/bindings/python/examples/exchange/5_send_amount.py +++ b/bindings/python/examples/exchange/5_send_amount.py @@ -7,7 +7,7 @@ from dotenv import load_dotenv -from iota_sdk import SyncOptions, Wallet +from iota_sdk import SyncOptions, Wallet, WalletOptions # This example uses secrets in environment variables for simplicity which # should not be done in production. @@ -17,18 +17,16 @@ if env_var not in os.environ: raise Exception(f'.env {env_var} is undefined, see .env.example') -wallet = Wallet(os.environ.get('WALLET_DB_PATH')) - -account = wallet.get_account('Alice') +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) wallet.set_stronghold_password(os.environ["STRONGHOLD_PASSWORD"]) # Set sync_only_most_basic_outputs to True if not interested in outputs that are timelocked, # have a storage deposit return, expiration or are nft/account/foundry outputs. -balance = account.sync(SyncOptions(sync_only_most_basic_outputs=True)) +balance = wallet.sync(SyncOptions(sync_only_most_basic_outputs=True)) print('Balance', balance) -transaction = account.send( +transaction = wallet.send( 1000000, "rms1qpszqzadsym6wpppd6z037dvlejmjuke7s24hm95s9fg9vpua7vluaw60xu", ) diff --git a/bindings/python/examples/how_tos/account_output/create.py b/bindings/python/examples/how_tos/account_output/create.py index c4cdb3cb96..a162f62b4e 100644 --- a/bindings/python/examples/how_tos/account_output/create.py +++ b/bindings/python/examples/how_tos/account_output/create.py @@ -1,25 +1,31 @@ import os +import time from dotenv import load_dotenv -from iota_sdk import Wallet - -load_dotenv() +from iota_sdk import Wallet, WalletOptions # In this example we will create an account output -wallet = Wallet(os.environ['WALLET_DB_PATH']) +load_dotenv() -account = wallet.get_account('Alice') +for env_var in ['WALLET_DB_PATH', 'STRONGHOLD_PASSWORD', 'EXPLORER_URL']: + if env_var not in os.environ: + raise Exception(f".env {env_var} is undefined, see .env.example") -# Sync account with the node -account.sync() +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) -if 'STRONGHOLD_PASSWORD' not in os.environ: - raise Exception(".env STRONGHOLD_PASSWORD is undefined, see .env.example") +# Sync wallet with the node +balance = wallet.sync() +print(f'Accounts BEFORE: {balance.accounts}') wallet.set_stronghold_password(os.environ["STRONGHOLD_PASSWORD"]) # Send transaction. -transaction = account.create_account_output(None, None) +transaction = wallet.create_account_output(None, None) print(f'Block sent: {os.environ["EXPLORER_URL"]}/block/{transaction.block_id}') + +time.sleep(10) + +balance = wallet.sync() +print(f'Accounts AFTER: {balance.accounts}') diff --git a/bindings/python/examples/how_tos/account_output/destroy.py b/bindings/python/examples/how_tos/account_output/destroy.py index 87fe39d91e..0b84209623 100644 --- a/bindings/python/examples/how_tos/account_output/destroy.py +++ b/bindings/python/examples/how_tos/account_output/destroy.py @@ -2,27 +2,26 @@ from dotenv import load_dotenv -from iota_sdk import Wallet - -load_dotenv() +from iota_sdk import Wallet, WalletOptions # In this example we will destroy an account output -wallet = Wallet(os.environ['WALLET_DB_PATH']) +load_dotenv() -account = wallet.get_account('Alice') +for env_var in ['WALLET_DB_PATH', 'STRONGHOLD_PASSWORD', 'EXPLORER_URL']: + if env_var not in os.environ: + raise Exception(f".env {env_var} is undefined, see .env.example") -# Sync account with the node -balance = account.sync() +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) -# We try to destroy the first account in the account -account_id = balance.accounts[0] +# Sync wallet with the node +balance = wallet.sync() -if 'STRONGHOLD_PASSWORD' not in os.environ: - raise Exception(".env STRONGHOLD_PASSWORD is undefined, see .env.example") +# We try to destroy the first account in the wallet +account_id = balance.accounts[0] wallet.set_stronghold_password(os.environ["STRONGHOLD_PASSWORD"]) # Send transaction. -transaction = account.prepare_destroy_account(account_id).send() +transaction = wallet.prepare_destroy_account(account_id).send() print(f'Block sent: {os.environ["EXPLORER_URL"]}/block/{transaction.block_id}') diff --git a/bindings/python/examples/how_tos/account_output/implicit_account_creation.py b/bindings/python/examples/how_tos/account_output/implicit_account_creation.py new file mode 100644 index 0000000000..82a7fe098c --- /dev/null +++ b/bindings/python/examples/how_tos/account_output/implicit_account_creation.py @@ -0,0 +1,18 @@ +import os + +from dotenv import load_dotenv + +from iota_sdk import Wallet, WalletOptions + +# In this example, we create an implicit account creation address. + +load_dotenv() + +if 'WALLET_DB_PATH' not in os.environ: + raise Exception(".env WALLET_DB_PATH is undefined, see .env.example") + +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) + +# Get the implicit account address +address = wallet.implicit_account_creation_address() +print(f'Fund the following address: {address}') diff --git a/bindings/python/examples/how_tos/account_wallet/request_funds.py b/bindings/python/examples/how_tos/account_wallet/request_funds.py index e40c12181c..c8cbb2ab77 100644 --- a/bindings/python/examples/how_tos/account_wallet/request_funds.py +++ b/bindings/python/examples/how_tos/account_wallet/request_funds.py @@ -3,19 +3,21 @@ from dotenv import load_dotenv -from iota_sdk import Wallet, Utils, SyncOptions, AccountSyncOptions +from iota_sdk import Wallet, WalletOptions, Utils, SyncOptions, WalletSyncOptions # In this example we request funds to an account wallet. load_dotenv() +for env_var in ['FAUCET_URL', 'WALLET_DB_PATH', 'EXPLORER_URL']: + if env_var not in os.environ: + raise Exception(f".env {env_var} is undefined, see .env.example") + FAUCET_URL = os.environ.get( 'FAUCET_URL', 'https://faucet.testnet.shimmer.network/api/enqueue') -wallet = Wallet(os.environ['WALLET_DB_PATH']) - -account = wallet.get_account('Alice') -balance = account.sync(None) +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) +balance = wallet.sync(None) total_base_token_balance = balance.base_coin.total print( @@ -33,8 +35,8 @@ time.sleep(10) -sync_options = SyncOptions(alias=AccountSyncOptions(basic_outputs=True)) +sync_options = SyncOptions(wallet=WalletSyncOptions(basic_outputs=True)) -total_base_token_balance = account.sync(sync_options).base_coin.total +total_base_token_balance = wallet.sync(sync_options).base_coin.total print( f'Balance after requesting funds on account address: {total_base_token_balance}') diff --git a/bindings/python/examples/how_tos/account_wallet/transaction.py b/bindings/python/examples/how_tos/account_wallet/transaction.py index 8cf9ba73e7..edf07f1b0b 100644 --- a/bindings/python/examples/how_tos/account_wallet/transaction.py +++ b/bindings/python/examples/how_tos/account_wallet/transaction.py @@ -2,24 +2,22 @@ from dotenv import load_dotenv -from iota_sdk import Wallet, Utils, NodeIndexerAPI, SyncOptions, AccountSyncOptions, SendParams +from iota_sdk import Wallet, WalletOptions, Utils, NodeIndexerAPI, SyncOptions, WalletSyncOptions, SendParams # In this example we send funds from an account wallet. load_dotenv() -sync_options = SyncOptions(alias=AccountSyncOptions(basic_outputs=True)) +for env_var in ['WALLET_DB_PATH', 'STRONGHOLD_PASSWORD']: + if env_var not in os.environ: + raise Exception(f".env {env_var} is undefined, see .env.example") -wallet = Wallet(os.environ['WALLET_DB_PATH']) - -account = wallet.get_account('Alice') - -if 'STRONGHOLD_PASSWORD' not in os.environ: - raise Exception(".env STRONGHOLD_PASSWORD is undefined, see .env.example") +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) wallet.set_stronghold_password(os.environ["STRONGHOLD_PASSWORD"]) -balance = account.sync(sync_options) +sync_options = SyncOptions(wallet=WalletSyncOptions(basic_outputs=True)) +balance = wallet.sync(sync_options) total_base_token_balance = balance.base_coin.total print(f'Balance before sending funds from account: {total_base_token_balance}') @@ -43,11 +41,11 @@ options = { 'mandatoryInputs': inputs, } -transaction = account.send_with_params(params, options) -account.reissue_transaction_until_included( +transaction = wallet.send_with_params(params, options) +wallet.reissue_transaction_until_included( transaction.transaction_id) print( f'Transaction with custom input: https://explorer.shimmer.network/testnet/transaction/{transaction.transaction_id}') -total_base_token_balance = account.sync(sync_options).base_coin.total +total_base_token_balance = wallet.sync(sync_options).base_coin.total print(f'Balance after sending funds from account: {total_base_token_balance}') diff --git a/bindings/python/examples/how_tos/accounts_and_addresses/check_balance.py b/bindings/python/examples/how_tos/accounts_and_addresses/check_balance.py index cac977d98b..4c85509ddc 100644 --- a/bindings/python/examples/how_tos/accounts_and_addresses/check_balance.py +++ b/bindings/python/examples/how_tos/accounts_and_addresses/check_balance.py @@ -3,21 +3,19 @@ from dotenv import load_dotenv -from iota_sdk import Wallet +from iota_sdk import Wallet, WalletOptions -# This example checks the balance of an account. +# This example checks the balance of a wallet. # This example uses secrets in environment variables for simplicity which # should not be done in production. load_dotenv() -wallet = Wallet(os.environ['WALLET_DB_PATH']) +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) -account = wallet.get_account('Alice') - -# Sync account with the node -_balance = account.sync() +# Sync wallet with the node +_balance = wallet.sync() # Just calculate the balance with the known state -balance = account.get_balance() +balance = wallet.get_balance() print(f'Balance {json.dumps(balance.to_dict(), indent=4)}') diff --git a/bindings/python/examples/how_tos/accounts_and_addresses/consolidate_outputs.py b/bindings/python/examples/how_tos/accounts_and_addresses/consolidate_outputs.py index 576c729876..434b3b692c 100644 --- a/bindings/python/examples/how_tos/accounts_and_addresses/consolidate_outputs.py +++ b/bindings/python/examples/how_tos/accounts_and_addresses/consolidate_outputs.py @@ -2,9 +2,9 @@ from dotenv import load_dotenv -from iota_sdk import ConsolidationParams, Utils, Wallet, FeatureType +from iota_sdk import ConsolidationParams, Utils, Wallet, WalletOptions, FeatureType -# In this example we will consolidate basic outputs from an account with only an AddressUnlockCondition by sending +# In this example we will consolidate basic outputs from a wallet with only an AddressUnlockCondition by sending # them to the same address again. # This example uses secrets in environment variables for simplicity which @@ -14,22 +14,20 @@ if 'STRONGHOLD_PASSWORD' not in os.environ: raise Exception('.env STRONGHOLD_PASSWORD is undefined, see .env.example') -wallet = Wallet(os.environ['WALLET_DB_PATH']) +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) wallet.set_stronghold_password(os.environ['STRONGHOLD_PASSWORD']) -account = wallet.get_account('Alice') - -# Sync account to make sure account is updated with outputs from previous +# Sync wallet to make sure it is updated with outputs from the previous # examples. -account.sync() -print('Account synced') +wallet.sync() +print('Wallet synced') # List unspent outputs before consolidation. # The output we created with example `request_funds` and the basic output from `mint` have only one # unlock condition and it is an `AddressUnlockCondition`, and so they are valid for consolidation. They have the # same `AddressUnlockCondition`(the address of the wallet), so they will be consolidated into one # output. -outputs = account.unspent_outputs() +outputs = wallet.unspent_outputs() print('Outputs BEFORE consolidation:') for i, output_data in enumerate(outputs): @@ -37,36 +35,42 @@ print( f'- address: #{Utils.hex_to_bech32(output_data.address.pub_key_hash, "rms")}') print(f'- amount: #{output_data.output.amount}') - print(f'- native tokens: #{}', [feature for feature in output_data.output.features if feature.type - == FeatureType.NativeToken]) + + native_tokens = [ + feature for feature in output_data.output.features if feature.type == FeatureType.NativeToken] + opt_native_token = next(iter(native_tokens), None) + print(f'- native tokens: #{opt_native_token}') print('Sending consolidation transaction...') # Consolidate unspent outputs and print the consolidation transaction ID # Set `force` to true to force the consolidation even though the # `output_threshold` isn't reached. -transaction = account.consolidate_outputs(ConsolidationParams(force=True)) +transaction = wallet.consolidate_outputs(ConsolidationParams(force=True)) print('Transaction sent: ', transaction.transaction_id) # Wait for the consolidation transaction to get confirmed -block_id = account.reissue_transaction_until_included( +block_id = wallet.reissue_transaction_until_included( transaction.transaction_id) print( f'Transaction included: {os.environ["EXPLORER_URL"]}/block/{block_id}' ) -# Sync account -account.sync() -print('Account synced') +# Sync wallet +wallet.sync() +print('Wallet synced') # Outputs after consolidation -outputs = account.unspent_outputs() +outputs = wallet.unspent_outputs() print('Outputs AFTER consolidation:') for i, output_data in enumerate(outputs): print(f'OUTPUT #{i}') print( f'- address: #{Utils.hex_to_bech32(output_data.address.pub_key_hash, "rms")}') print(f'- amount: #{output_data.output.amount}') - print(f'- native tokens: #{}', [feature for feature in output_data.output.features if feature.type - == FeatureType.NativeToken]) + + native_tokens = [ + feature for feature in output_data.output.features if feature.type == FeatureType.NativeToken] + opt_native_token = next(iter(native_tokens), None) + print(f'- native tokens: #{opt_native_token}') diff --git a/bindings/python/examples/how_tos/accounts_and_addresses/create_address.py b/bindings/python/examples/how_tos/accounts_and_addresses/create_address.py index 77a63e98ad..929385a8d4 100644 --- a/bindings/python/examples/how_tos/accounts_and_addresses/create_address.py +++ b/bindings/python/examples/how_tos/accounts_and_addresses/create_address.py @@ -2,20 +2,20 @@ from dotenv import load_dotenv -from iota_sdk import Wallet +from iota_sdk import StrongholdSecretManager, SecretManager load_dotenv() # This example generates a new address. -wallet = Wallet(os.environ['WALLET_DB_PATH']) +for env_var in ['STRONGHOLD_PASSWORD', 'STRONGHOLD_SNAPSHOT_PATH']: + if env_var not in os.environ: + raise Exception(f".env {env_var} is undefined, see .env.example") -if 'STRONGHOLD_PASSWORD' not in os.environ: - raise Exception(".env STRONGHOLD_PASSWORD is undefined, see .env.example") +secret_manager = SecretManager(StrongholdSecretManager( + os.environ.get('STRONGHOLD_SNAPSHOT_PATH'), + os.environ.get('STRONGHOLD_PASSWORD') +)) -wallet.set_stronghold_password(os.environ["STRONGHOLD_PASSWORD"]) - -account = wallet.get_account('Alice') - -address = account.generate_ed25519_addresses(1) +address = secret_manager.generate_ed25519_addresses(1) print('Generated address:', address[0].address) diff --git a/bindings/python/examples/how_tos/accounts_and_addresses/create_account.py b/bindings/python/examples/how_tos/accounts_and_addresses/create_wallet.py similarity index 62% rename from bindings/python/examples/how_tos/accounts_and_addresses/create_account.py rename to bindings/python/examples/how_tos/accounts_and_addresses/create_wallet.py index 09758a6c23..c956909b76 100644 --- a/bindings/python/examples/how_tos/accounts_and_addresses/create_account.py +++ b/bindings/python/examples/how_tos/accounts_and_addresses/create_wallet.py @@ -2,7 +2,7 @@ from dotenv import load_dotenv -from iota_sdk import ClientOptions, CoinType, StrongholdSecretManager, Wallet +from iota_sdk import ClientOptions, CoinType, StrongholdSecretManager, SecretManager, Wallet, WalletOptions, Bip44 load_dotenv() @@ -11,9 +11,6 @@ node_url = os.environ.get('NODE_URL', 'https://api.testnet.shimmer.network') client_options = ClientOptions(nodes=[node_url]) -# Shimmer coin type -coin_type = CoinType.SHIMMER - for env_var in ['STRONGHOLD_PASSWORD', 'MNEMONIC']: if env_var not in os.environ: raise Exception(f".env {env_var} is undefined, see .env.example") @@ -21,15 +18,23 @@ secret_manager = StrongholdSecretManager( os.environ['STRONGHOLD_SNAPSHOT_PATH'], os.environ['STRONGHOLD_PASSWORD']) -wallet = Wallet( - os.environ['WALLET_DB_PATH'], - client_options, - coin_type, - secret_manager) - # Store the mnemonic in the Stronghold snapshot, this only needs to be # done once. -wallet.store_mnemonic(os.environ['MNEMONIC']) +SecretManager(secret_manager).store_mnemonic(os.environ['MNEMONIC']) + +bip_path = Bip44( + coin_type=CoinType.SHIMMER +) + +wallet_options = WalletOptions( + None, + None, + bip_path, + client_options, + secret_manager, + os.environ.get('WALLET_DB_PATH')) +wallet = Wallet(wallet_options) -account = wallet.create_account('Alice') -print("Account created:", account.get_metadata()) +# Update the wallet to the latest state +balance = wallet.sync() +print('Generated new wallet') diff --git a/bindings/python/examples/how_tos/accounts_and_addresses/list_accounts.py b/bindings/python/examples/how_tos/accounts_and_addresses/list_accounts.py deleted file mode 100644 index abbb5756e9..0000000000 --- a/bindings/python/examples/how_tos/accounts_and_addresses/list_accounts.py +++ /dev/null @@ -1,16 +0,0 @@ -import os - -from dotenv import load_dotenv - -from iota_sdk import Wallet - -# This example lists all accounts in the wallet. - -# This example uses secrets in environment variables for simplicity which -# should not be done in production. -load_dotenv() - -wallet = Wallet(os.environ['WALLET_DB_PATH']) - -for account in wallet.get_accounts(): - print(account.get_metadata()) diff --git a/bindings/python/examples/how_tos/accounts_and_addresses/list_addresses.py b/bindings/python/examples/how_tos/accounts_and_addresses/list_addresses.py deleted file mode 100644 index 38cb28430f..0000000000 --- a/bindings/python/examples/how_tos/accounts_and_addresses/list_addresses.py +++ /dev/null @@ -1,20 +0,0 @@ -import os - -from dotenv import load_dotenv - -from iota_sdk import Wallet - -# This example lists all addresses in the account. - -# This example uses secrets in environment variables for simplicity which -# should not be done in production. -load_dotenv() - -wallet = Wallet(os.environ['WALLET_DB_PATH']) - -account = wallet.get_account('Alice') - -addresses = account.addresses() - -for address in addresses: - print(address.address) diff --git a/bindings/python/examples/how_tos/accounts_and_addresses/list_outputs.py b/bindings/python/examples/how_tos/accounts_and_addresses/list_outputs.py index 5e809633ef..aaabc7ee45 100644 --- a/bindings/python/examples/how_tos/accounts_and_addresses/list_outputs.py +++ b/bindings/python/examples/how_tos/accounts_and_addresses/list_outputs.py @@ -2,29 +2,27 @@ from dotenv import load_dotenv -from iota_sdk import Wallet +from iota_sdk import Wallet, WalletOptions -# In this example we will get outputs stored in the account +# In this example we will get outputs stored in the wallet # This example uses secrets in environment variables for simplicity which # should not be done in production. load_dotenv() -wallet = Wallet(os.environ['WALLET_DB_PATH']) +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) +wallet.sync() -account = wallet.get_account('Alice') -account.sync() - -# All outputs stored in the account -outputs = account.outputs() +# All outputs stored in the wallet +outputs = wallet.outputs() # Print all output ids print('Output ids:') for output in outputs: print(output.output_id) -# All unspent outputs stored in the account -outputs = account.unspent_outputs() +# All unspent outputs stored in the wallet +outputs = wallet.unspent_outputs() # Print all unspent output ids print('Unspent output ids:') diff --git a/bindings/python/examples/how_tos/accounts_and_addresses/list_transactions.py b/bindings/python/examples/how_tos/accounts_and_addresses/list_transactions.py index a0e881703f..af354a5d61 100644 --- a/bindings/python/examples/how_tos/accounts_and_addresses/list_transactions.py +++ b/bindings/python/examples/how_tos/accounts_and_addresses/list_transactions.py @@ -2,7 +2,7 @@ from dotenv import load_dotenv -from iota_sdk import Wallet +from iota_sdk import Wallet, WalletOptions # In this example we will list transactions @@ -10,20 +10,18 @@ # should not be done in production. load_dotenv() -wallet = Wallet(os.environ['WALLET_DB_PATH']) +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) +wallet.sync({'syncIncomingTransactions': True}) -account = wallet.get_account('Alice') -account.sync({'syncIncomingTransactions': True}) - -# All transactions sent from the the account -transactions = account.transactions() +# All transactions sent from the wallet +transactions = wallet.transactions() print('Sent transactions:') for transaction in transactions: print(transaction.transaction_id) # Incoming transactions -incoming_transactions = account.incoming_transactions() +incoming_transactions = wallet.incoming_transactions() print('Received transactions:') for transaction in incoming_transactions: print(transaction.transaction_id) diff --git a/bindings/python/examples/how_tos/accounts_and_addresses/print_address.py b/bindings/python/examples/how_tos/accounts_and_addresses/print_address.py new file mode 100644 index 0000000000..bcd71f0974 --- /dev/null +++ b/bindings/python/examples/how_tos/accounts_and_addresses/print_address.py @@ -0,0 +1,20 @@ +import os + +from dotenv import load_dotenv + +from iota_sdk import Wallet, WalletOptions + +# This example uses secrets in environment variables for simplicity which +# should not be done in production. +load_dotenv() + +# This example prints the wallet address. + +if 'WALLET_DB_PATH' not in os.environ: + raise Exception(".env WALLET_DB_PATH is undefined, see .env.example") + +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) + +address = wallet.address() + +print(address) diff --git a/bindings/python/examples/how_tos/advanced_transactions/advanced_transaction.py b/bindings/python/examples/how_tos/advanced_transactions/advanced_transaction.py index 090c7c5050..acc64ca250 100644 --- a/bindings/python/examples/how_tos/advanced_transactions/advanced_transaction.py +++ b/bindings/python/examples/how_tos/advanced_transactions/advanced_transaction.py @@ -9,24 +9,25 @@ Client, Ed25519Address, Wallet, + WalletOptions, Utils, TimelockUnlockCondition, ) - +# This example uses secrets in environment variables for simplicity which +# should not be done in production. load_dotenv() # This example sends a transaction with a timelock. -wallet = Wallet(os.environ['WALLET_DB_PATH']) - -account = wallet.get_account('Alice') +for env_var in ['WALLET_DB_PATH', 'STRONGHOLD_PASSWORD']: + if env_var not in os.environ: + raise Exception(f'.env {env_var} is undefined, see .env.example') -# Sync account with the node -response = account.sync() +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) -if 'STRONGHOLD_PASSWORD' not in os.environ: - raise Exception(".env STRONGHOLD_PASSWORD is undefined, see .env.example") +# Sync wallet with the node +wallet.sync() wallet.set_stronghold_password(os.environ["STRONGHOLD_PASSWORD"]) @@ -46,10 +47,10 @@ ], ) -transaction = account.send_outputs([basic_output]) +transaction = wallet.send_outputs([basic_output]) print(f'Transaction sent: {transaction.transaction_id}') -block_id = account.reissue_transaction_until_included( +block_id = wallet.reissue_transaction_until_included( transaction.transaction_id) print( diff --git a/bindings/python/examples/how_tos/advanced_transactions/claim_transaction.py b/bindings/python/examples/how_tos/advanced_transactions/claim_transaction.py index 32e9f55798..c833932317 100644 --- a/bindings/python/examples/how_tos/advanced_transactions/claim_transaction.py +++ b/bindings/python/examples/how_tos/advanced_transactions/claim_transaction.py @@ -2,35 +2,36 @@ from dotenv import load_dotenv -from iota_sdk import Wallet +from iota_sdk import Wallet, WalletOptions +# This example uses secrets in environment variables for simplicity which +# should not be done in production. load_dotenv() # In this example we will claim outputs that have additional unlock # conditions as expiration or storage deposit return. -wallet = Wallet(os.environ['WALLET_DB_PATH']) +for env_var in ['WALLET_DB_PATH', 'STRONGHOLD_PASSWORD']: + if env_var not in os.environ: + raise Exception(f'.env {env_var} is undefined, see .env.example') -account = wallet.get_account('Alice') - -if 'STRONGHOLD_PASSWORD' not in os.environ: - raise Exception(".env STRONGHOLD_PASSWORD is undefined, see .env.example") +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) wallet.set_stronghold_password(os.environ["STRONGHOLD_PASSWORD"]) -# Sync account with the node -response = account.sync() +# Sync wallet with the node +wallet.sync() -# Only the unspent outputs in the account -output_ids = account.claimable_outputs('All') +# Only the unspent outputs in the wallet +output_ids = wallet.claimable_outputs('All') print('Available outputs to claim:') for output_id in output_ids: print(f'{output_id}') -transaction = account.claim_outputs(output_ids) +transaction = wallet.claim_outputs(output_ids) print(f'Transaction sent: {transaction.transaction_id}') -block_id = account.reissue_transaction_until_included( +block_id = wallet.reissue_transaction_until_included( transaction.transaction_id) print(f'Block sent: {os.environ["EXPLORER_URL"]}/block/{block_id}') diff --git a/bindings/python/examples/how_tos/advanced_transactions/send_micro_transaction.py b/bindings/python/examples/how_tos/advanced_transactions/send_micro_transaction.py index 2c6af5ee3e..47c47e30f8 100644 --- a/bindings/python/examples/how_tos/advanced_transactions/send_micro_transaction.py +++ b/bindings/python/examples/how_tos/advanced_transactions/send_micro_transaction.py @@ -2,21 +2,23 @@ from dotenv import load_dotenv -from iota_sdk import Wallet, SendParams +from iota_sdk import Wallet, WalletOptions, SendParams +# This example uses secrets in environment variables for simplicity which +# should not be done in production. load_dotenv() # In this example we will send an amount below the minimum amount -wallet = Wallet(os.environ['WALLET_DB_PATH']) +for env_var in ['WALLET_DB_PATH', 'STRONGHOLD_PASSWORD']: + if env_var not in os.environ: + raise Exception(f'.env {env_var} is undefined, see .env.example') -account = wallet.get_account('Alice') -# Sync account with the node -response = account.sync() +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) -if 'STRONGHOLD_PASSWORD' not in os.environ: - raise Exception(".env STRONGHOLD_PASSWORD is undefined, see .env.example") +# Sync wallet with the node +wallet.sync() wallet.set_stronghold_password(os.environ["STRONGHOLD_PASSWORD"]) @@ -25,10 +27,10 @@ amount=1, )] -transaction = account.send_with_params(params, {"allowMicroAmount": True}) +transaction = wallet.send_with_params(params, {"allowMicroAmount": True}) print(f'Transaction sent: {transaction.transaction_id}') -block_id = account.reissue_transaction_until_included( +block_id = wallet.reissue_transaction_until_included( transaction.transaction_id) print( diff --git a/bindings/python/examples/how_tos/native_tokens/burn.py b/bindings/python/examples/how_tos/native_tokens/burn.py index 90ff290261..7ad18ceb4e 100644 --- a/bindings/python/examples/how_tos/native_tokens/burn.py +++ b/bindings/python/examples/how_tos/native_tokens/burn.py @@ -1,17 +1,15 @@ import os from dotenv import load_dotenv -from iota_sdk import Wallet +from iota_sdk import Wallet, WalletOptions load_dotenv() # In this example we will burn native tokens -wallet = Wallet(os.environ['WALLET_DB_PATH']) +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) -account = wallet.get_account('Alice') - -# Sync account with the node -balance = account.sync() +# Sync wallet with the node +balance = wallet.sync() if 'STRONGHOLD_PASSWORD' not in os.environ: raise Exception(".env STRONGHOLD_PASSWORD is undefined, see .env.example") @@ -28,16 +26,16 @@ burn_amount = 1 # Send transaction. -transaction = account.prepare_burn_native_token( +transaction = wallet.prepare_burn_native_token( token.token_id, burn_amount).send() print(f'Transaction sent: {transaction.transaction_id}') # Wait for transaction to get included -block_id = account.reissue_transaction_until_included( +block_id = wallet.reissue_transaction_until_included( transaction.transaction_id) print(f'Block included: {os.environ["EXPLORER_URL"]}/block/{block_id}') -balance = account.sync() +balance = wallet.sync() available_balance = int( [native_balance for native_balance in balance.native_tokens if native_balance.token_id == token.token_id][0].available, 0) print(f'Balance after burning: {available_balance}') diff --git a/bindings/python/examples/how_tos/native_tokens/create.py b/bindings/python/examples/how_tos/native_tokens/create.py index 94b3fa5068..6510b4b80d 100644 --- a/bindings/python/examples/how_tos/native_tokens/create.py +++ b/bindings/python/examples/how_tos/native_tokens/create.py @@ -2,39 +2,37 @@ from dotenv import load_dotenv -from iota_sdk import CreateNativeTokenParams, Wallet, Irc30Metadata +from iota_sdk import CreateNativeTokenParams, Wallet, WalletOptions, Irc30Metadata load_dotenv() # In this example we will create native tokens -wallet = Wallet(os.environ['WALLET_DB_PATH']) - -account = wallet.get_account('Alice') +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) if 'STRONGHOLD_PASSWORD' not in os.environ: raise Exception(".env STRONGHOLD_PASSWORD is undefined, see .env.example") wallet.set_stronghold_password(os.environ["STRONGHOLD_PASSWORD"]) -# Sync account with the node -balance = account.sync() +# Sync wallet with the node +balance = wallet.sync() # We can first check if we already have an account output in our account, because # an account can have many foundry outputs and therefore we can reuse an # existing one. if not balance.accounts: # If we don't have an account, we need to create one - transaction = account.create_account_output(None, None) + transaction = wallet.create_account_output(None, None) print(f'Transaction sent: {transaction.transaction_id}') # Wait for transaction to get included - block_id = account.reissue_transaction_until_included( + block_id = wallet.reissue_transaction_until_included( transaction.transaction_id) print(f'Block included: {os.environ["EXPLORER_URL"]}/block/{block_id}') - account.sync() - print("Account synced") + wallet.sync() + print("Wallet synced") print('Preparing transaction to create native token...') @@ -48,17 +46,17 @@ metadata.as_hex(), ) -prepared_transaction = account.prepare_create_native_token(params, None) +prepared_transaction = wallet.prepare_create_native_token(params, None) transaction = prepared_transaction.send() print(f'Transaction sent: {transaction.transaction_id}') # Wait for transaction to get included -block_id = account.reissue_transaction_until_included( +block_id = wallet.reissue_transaction_until_included( transaction.transaction_id) print(f'Block included: {os.environ["EXPLORER_URL"]}/block/{block_id}') print(f'Created token: {prepared_transaction.token_id()}') -# Ensure the account is synced after creating the native token. -account.sync() -print('Account synced') +# Ensure the wallet is synced after creating the native token. +wallet.sync() +print('Wallet synced') diff --git a/bindings/python/examples/how_tos/native_tokens/destroy_foundry.py b/bindings/python/examples/how_tos/native_tokens/destroy_foundry.py index a3e631fc6c..45a04537dc 100644 --- a/bindings/python/examples/how_tos/native_tokens/destroy_foundry.py +++ b/bindings/python/examples/how_tos/native_tokens/destroy_foundry.py @@ -1,17 +1,15 @@ import os from dotenv import load_dotenv -from iota_sdk import Wallet +from iota_sdk import Wallet, WalletOptions load_dotenv() # In this example we will destroy a foundry -wallet = Wallet(os.environ['WALLET_DB_PATH']) +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) -account = wallet.get_account('Alice') - -# Sync account with the node -balance = account.sync() +# Sync wallet with the node +balance = wallet.sync() print(f'Foundries before destroying: {len(balance.foundries)}') if 'STRONGHOLD_PASSWORD' not in os.environ: @@ -19,17 +17,17 @@ wallet.set_stronghold_password(os.environ["STRONGHOLD_PASSWORD"]) -# We try to destroy the first foundry in the account +# We try to destroy the first foundry in the wallet foundry_id = balance.foundries[0] # Send transaction. -transaction = account.prepare_destroy_foundry(foundry_id).send() +transaction = wallet.prepare_destroy_foundry(foundry_id).send() print(f'Transaction sent: {transaction.transaction_id}') # Wait for transaction to get included -block_id = account.reissue_transaction_until_included( +block_id = wallet.reissue_transaction_until_included( transaction.transaction_id) print(f'Block included: {os.environ["EXPLORER_URL"]}/block/{block_id}') -balance = account.sync() +balance = wallet.sync() print(f'Foundries after destroying: {len(balance.foundries)}') diff --git a/bindings/python/examples/how_tos/native_tokens/melt.py b/bindings/python/examples/how_tos/native_tokens/melt.py index 4d67341bbc..dd66fd965d 100644 --- a/bindings/python/examples/how_tos/native_tokens/melt.py +++ b/bindings/python/examples/how_tos/native_tokens/melt.py @@ -2,18 +2,16 @@ from dotenv import load_dotenv -from iota_sdk import Wallet +from iota_sdk import Wallet, WalletOptions load_dotenv() # In this example we will decrease the native token supply -wallet = Wallet(os.environ['WALLET_DB_PATH']) +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) -account = wallet.get_account('Alice') - -# Sync account with the node -balance = account.sync() +# Sync wallet with the node +balance = wallet.sync() # Find first foundry and corresponding token id token_id = balance.foundries[0] @@ -30,15 +28,15 @@ melt_amount = 10 # Send transaction. -transaction = account.melt_native_token(token_id, melt_amount) +transaction = wallet.melt_native_token(token_id, melt_amount) print(f'Transaction sent: {transaction.transaction_id}') # Wait for transaction to get included -block_id = account.reissue_transaction_until_included( +block_id = wallet.reissue_transaction_until_included( transaction.transaction_id) print(f'Block included: {os.environ["EXPLORER_URL"]}/block/{block_id}') -balance = account.sync() +balance = wallet.sync() available_balance = int( [native_balance for native_balance in balance.native_tokens if native_balance.token_id == token_id][0].available, 0) print(f'Balance after melting: {available_balance}') diff --git a/bindings/python/examples/how_tos/native_tokens/mint.py b/bindings/python/examples/how_tos/native_tokens/mint.py index 6f72425ce7..517eab633a 100644 --- a/bindings/python/examples/how_tos/native_tokens/mint.py +++ b/bindings/python/examples/how_tos/native_tokens/mint.py @@ -2,18 +2,16 @@ from dotenv import load_dotenv -from iota_sdk import Wallet +from iota_sdk import Wallet, WalletOptions load_dotenv() # In this example we will mint native tokens -wallet = Wallet(os.environ['WALLET_DB_PATH']) +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) -account = wallet.get_account('Alice') - -# Sync account with the node -balance = account.sync() +# Sync wallet with the node +balance = wallet.sync() # Find first foundry and corresponding token id token_id = balance.foundries[0] @@ -30,15 +28,15 @@ mint_amount = 10 # Send transaction. -transaction = account.mint_native_token(token_id, mint_amount) +transaction = wallet.mint_native_token(token_id, mint_amount) print(f'Transaction sent: {transaction.transaction_id}') # Wait for transaction to get included -block_id = account.reissue_transaction_until_included( +block_id = wallet.reissue_transaction_until_included( transaction.transaction_id) print(f'Block included: {os.environ["EXPLORER_URL"]}/block/{block_id}') -balance = account.sync() +balance = wallet.sync() available_balance = int( [native_balance for native_balance in balance.native_tokens if native_balance.token_id == token_id][0].available, 0) print(f'Balance after minting: {available_balance}') diff --git a/bindings/python/examples/how_tos/native_tokens/send.py b/bindings/python/examples/how_tos/native_tokens/send.py index 7c854fe215..d960dd1191 100644 --- a/bindings/python/examples/how_tos/native_tokens/send.py +++ b/bindings/python/examples/how_tos/native_tokens/send.py @@ -2,18 +2,16 @@ from dotenv import load_dotenv -from iota_sdk import SendNativeTokenParams, Wallet +from iota_sdk import SendNativeTokenParams, Wallet, WalletOptions load_dotenv() # In this example we will send native tokens -wallet = Wallet(os.environ['WALLET_DB_PATH']) +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) -account = wallet.get_account('Alice') - -# Sync account with the node -balance = account.sync() +# Sync wallet with the node +balance = wallet.sync() token = [native_balance for native_balance in balance.native_tokens if int( native_balance.available, 0) >= 10][0] @@ -32,15 +30,15 @@ ), )] -transaction = account.send_native_tokens(outputs, None) +transaction = wallet.send_native_tokens(outputs, None) print(f'Transaction sent: {transaction.transaction_id}') # Wait for transaction to get included -block_id = account.reissue_transaction_until_included( +block_id = wallet.reissue_transaction_until_included( transaction.transaction_id) print(f'Block included: {os.environ["EXPLORER_URL"]}/block/{block_id}') -balance = account.sync() +balance = wallet.sync() available_balance = int( [native_balance for native_balance in balance.native_tokens if native_balance.token_id == token.token_id][0].available, 0) print(f'Balance after sending: {available_balance}') diff --git a/bindings/python/examples/how_tos/nft_collection/00_mint_issuer_nft.py b/bindings/python/examples/how_tos/nft_collection/00_mint_issuer_nft.py index 36e2897b9e..0aadd23767 100644 --- a/bindings/python/examples/how_tos/nft_collection/00_mint_issuer_nft.py +++ b/bindings/python/examples/how_tos/nft_collection/00_mint_issuer_nft.py @@ -2,23 +2,21 @@ from dotenv import load_dotenv -from iota_sdk import MintNftParams, Utils, Wallet, utf8_to_hex +from iota_sdk import MintNftParams, Utils, Wallet, WalletOptions, utf8_to_hex load_dotenv() # In this example we will mint the issuer NFT for the NFT collection. -wallet = Wallet(os.environ['WALLET_DB_PATH']) +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) if 'STRONGHOLD_PASSWORD' not in os.environ: raise Exception(".env STRONGHOLD_PASSWORD is undefined, see .env.example") wallet.set_stronghold_password(os.environ["STRONGHOLD_PASSWORD"]) -account = wallet.get_account('Alice') - -# Sync account with the node -account.sync() +# Sync wallet with the node +wallet.sync() # Issue the minting transaction and wait for its inclusion print('Sending NFT minting transaction...') @@ -28,10 +26,10 @@ ) -tx = account.mint_nfts([params]) +tx = wallet.mint_nfts([params]) # Wait for transaction to get included -block_id = account.reissue_transaction_until_included( +block_id = wallet.reissue_transaction_until_included( tx.transaction_id) print( diff --git a/bindings/python/examples/how_tos/nft_collection/01_mint_collection_nft.py b/bindings/python/examples/how_tos/nft_collection/01_mint_collection_nft.py index fb1acd5c4e..25003d6b41 100644 --- a/bindings/python/examples/how_tos/nft_collection/01_mint_collection_nft.py +++ b/bindings/python/examples/how_tos/nft_collection/01_mint_collection_nft.py @@ -3,7 +3,7 @@ from dotenv import load_dotenv -from iota_sdk import MintNftParams, Utils, Wallet, Irc27Metadata +from iota_sdk import MintNftParams, Utils, Wallet, WalletOptions, Irc27Metadata load_dotenv() @@ -19,17 +19,15 @@ issuer_nft_id = sys.argv[1] -wallet = Wallet(os.environ['WALLET_DB_PATH']) +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) if 'STRONGHOLD_PASSWORD' not in os.environ: raise Exception(".env STRONGHOLD_PASSWORD is undefined, see .env.example") wallet.set_stronghold_password(os.environ["STRONGHOLD_PASSWORD"]) -account = wallet.get_account('Alice') - -# Sync account with the node -account.sync() +# Sync wallet with the node +wallet.sync() bech32_hrp = wallet.get_client().get_bech32_hrp() issuer = Utils.nft_id_to_bech32(issuer_nft_id, bech32_hrp) @@ -65,13 +63,13 @@ def get_immutable_metadata(index: int) -> str: print( f'Minting {len(chunk)} NFTs... ({NFT_COLLECTION_SIZE-len(nft_mint_params)}/{NFT_COLLECTION_SIZE})' ) - transaction = account.mint_nfts(chunk) + transaction = wallet.mint_nfts(chunk) # Wait for transaction to get included - block_id = account.reissue_transaction_until_included( + block_id = wallet.reissue_transaction_until_included( transaction.transaction_id) print(f'Block sent: {os.environ["EXPLORER_URL"]}/block/{block_id}') # Sync so the new outputs are available again for new transactions - account.sync() + wallet.sync() diff --git a/bindings/python/examples/how_tos/nfts/burn_nft.py b/bindings/python/examples/how_tos/nfts/burn_nft.py index 948e7c41a2..aa51158a18 100644 --- a/bindings/python/examples/how_tos/nfts/burn_nft.py +++ b/bindings/python/examples/how_tos/nfts/burn_nft.py @@ -2,25 +2,23 @@ from dotenv import load_dotenv -from iota_sdk import Wallet +from iota_sdk import Wallet, WalletOptions load_dotenv() # In this example we will burn an NFT -wallet = Wallet(os.environ['WALLET_DB_PATH']) +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) if 'STRONGHOLD_PASSWORD' not in os.environ: raise Exception(".env STRONGHOLD_PASSWORD is undefined, see .env.example") wallet.set_stronghold_password(os.environ["STRONGHOLD_PASSWORD"]) -account = wallet.get_account('Alice') - -# Sync account with the node -balance = account.sync() +# Sync wallet with the node +balance = wallet.sync() nftId = balance.nfts[0] # Send transaction. -transaction = account.prepare_burn_nft(nftId).send() +transaction = wallet.prepare_burn_nft(nftId).send() print(f'Block sent: {os.environ["EXPLORER_URL"]}/block/{transaction.block_id}') diff --git a/bindings/python/examples/how_tos/nfts/mint_nft.py b/bindings/python/examples/how_tos/nfts/mint_nft.py index d45db0961d..ea675ab1fc 100644 --- a/bindings/python/examples/how_tos/nfts/mint_nft.py +++ b/bindings/python/examples/how_tos/nfts/mint_nft.py @@ -2,27 +2,29 @@ from dotenv import load_dotenv -from iota_sdk import MintNftParams, Wallet, utf8_to_hex +from iota_sdk import MintNftParams, Wallet, WalletOptions, utf8_to_hex +# This example uses secrets in environment variables for simplicity which +# should not be done in production. load_dotenv() # In this example we will mint an nft -wallet = Wallet(os.environ['WALLET_DB_PATH']) +for env_var in ['WALLET_DB_PATH', 'STRONGHOLD_PASSWORD']: + if env_var not in os.environ: + raise Exception(f'.env {env_var} is undefined, see .env.example') -if 'STRONGHOLD_PASSWORD' not in os.environ: - raise Exception(".env STRONGHOLD_PASSWORD is undefined, see .env.example") -wallet.set_stronghold_password(os.environ["STRONGHOLD_PASSWORD"]) +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) -account = wallet.get_account('Alice') +# Sync wallet with the node +wallet.sync() -# Sync account with the node -response = account.sync() +wallet.set_stronghold_password(os.environ["STRONGHOLD_PASSWORD"]) outputs = [MintNftParams( immutable_metadata=utf8_to_hex("some immutable nft metadata"), )] -transaction = account.mint_nfts(outputs) +transaction = wallet.mint_nfts(outputs) print(f'Block sent: {os.environ["EXPLORER_URL"]}/block/{transaction.block_id}') diff --git a/bindings/python/examples/how_tos/nfts/send_nft.py b/bindings/python/examples/how_tos/nfts/send_nft.py index d980baa520..9908e5ca6a 100644 --- a/bindings/python/examples/how_tos/nfts/send_nft.py +++ b/bindings/python/examples/how_tos/nfts/send_nft.py @@ -2,28 +2,26 @@ from dotenv import load_dotenv -from iota_sdk import SendNftParams, Wallet +from iota_sdk import SendNftParams, Wallet, WalletOptions load_dotenv() # In this example we will send an nft -wallet = Wallet(os.environ['WALLET_DB_PATH']) +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) if 'STRONGHOLD_PASSWORD' not in os.environ: raise Exception(".env STRONGHOLD_PASSWORD is undefined, see .env.example") wallet.set_stronghold_password(os.environ["STRONGHOLD_PASSWORD"]) -account = wallet.get_account('Alice') - -# Sync account with the node -balance = account.sync() +# Sync wallet with the node +balance = wallet.sync() outputs = [SendNftParams( address="rms1qpszqzadsym6wpppd6z037dvlejmjuke7s24hm95s9fg9vpua7vluaw60xu", nft_id=balance.nfts[0], )] -transaction = account.send_nft(outputs) +transaction = wallet.send_nft(outputs) print(f'Block sent: {os.environ["EXPLORER_URL"]}/block/{transaction.block_id}') diff --git a/bindings/python/examples/how_tos/simple_transaction/request_funds.py b/bindings/python/examples/how_tos/simple_transaction/request_funds.py index 583a167d87..e97507b006 100644 --- a/bindings/python/examples/how_tos/simple_transaction/request_funds.py +++ b/bindings/python/examples/how_tos/simple_transaction/request_funds.py @@ -2,7 +2,7 @@ from dotenv import load_dotenv -from iota_sdk import Wallet +from iota_sdk import Wallet, WalletOptions # This example requests funds from the faucet @@ -12,11 +12,9 @@ 'FAUCET_URL', 'https://faucet.testnet.shimmer.network/api/enqueue') -wallet = Wallet(os.environ['WALLET_DB_PATH']) +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) -account = wallet.get_account('Alice') - -address = account.addresses()[0].address +address = wallet.address() print(address) response = wallet.get_client().request_funds_from_faucet(FAUCET_URL, address=address) 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 400667c939..8da4b986bd 100644 --- a/bindings/python/examples/how_tos/simple_transaction/simple_transaction.py +++ b/bindings/python/examples/how_tos/simple_transaction/simple_transaction.py @@ -2,21 +2,23 @@ from dotenv import load_dotenv -from iota_sdk import SendParams, Wallet +from iota_sdk import SendParams, Wallet, WalletOptions +# This example uses secrets in environment variables for simplicity which +# should not be done in production. load_dotenv() # This example sends a transaction. -wallet = Wallet(os.environ['WALLET_DB_PATH']) +for env_var in ['WALLET_DB_PATH', 'STRONGHOLD_PASSWORD']: + if env_var not in os.environ: + raise Exception(f'.env {env_var} is undefined, see .env.example') -account = wallet.get_account('Alice') -# Sync account with the node -response = account.sync() +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) -if 'STRONGHOLD_PASSWORD' not in os.environ: - raise Exception(".env STRONGHOLD_PASSWORD is undefined, see .env.example") +# Sync wallet with the node +wallet.sync() wallet.set_stronghold_password(os.environ["STRONGHOLD_PASSWORD"]) @@ -25,5 +27,5 @@ amount=1000000, )] -transaction = account.send_with_params(params) +transaction = wallet.send_with_params(params) print(f'Block sent: {os.environ["EXPLORER_URL"]}/block/{transaction.block_id}') diff --git a/bindings/python/examples/wallet/12-prepare_output.py b/bindings/python/examples/wallet/12-prepare_output.py index 014d29d9ae..138028d8c1 100644 --- a/bindings/python/examples/wallet/12-prepare_output.py +++ b/bindings/python/examples/wallet/12-prepare_output.py @@ -3,16 +3,14 @@ from dotenv import load_dotenv -from iota_sdk import OutputParams, Unlocks, Wallet +from iota_sdk import OutputParams, Unlocks, Wallet, WalletOptions load_dotenv() # In this example we will prepare an output with an address and expiration # unlock condition and send it. -wallet = Wallet(os.environ['WALLET_DB_PATH']) - -account = wallet.get_account("Alice") +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) if 'STRONGHOLD_PASSWORD' not in os.environ: raise Exception(".env STRONGHOLD_PASSWORD is undefined, see .env.example") @@ -20,7 +18,7 @@ wallet.set_stronghold_password(os.environ["STRONGHOLD_PASSWORD"]) # using prepare_output -output = account.prepare_output( +output = wallet.prepare_output( OutputParams( "rms1qprutadk4uc9rp6h7wh7sech42sl0z40ztpgyygr5tf0cn5jrqshgm8y43d", 1000000, @@ -28,7 +26,7 @@ expiration_slot_index=1676570528))) print(f"Output: {json.dumps(output.to_dict(), indent=4)}") -account.sync() +wallet.sync() -transaction = account.send_outputs([output]) +transaction = wallet.send_outputs([output]) print(f'Block sent: {os.environ["EXPLORER_URL"]}/block/{transaction.block_id}') diff --git a/bindings/python/examples/wallet/13-check-unlock-conditions.py b/bindings/python/examples/wallet/13-check-unlock-conditions.py index 8fac56b70c..bb8bbf7a70 100644 --- a/bindings/python/examples/wallet/13-check-unlock-conditions.py +++ b/bindings/python/examples/wallet/13-check-unlock-conditions.py @@ -2,37 +2,28 @@ from dotenv import load_dotenv -from iota_sdk import OutputParams, Utils, Wallet +from iota_sdk import OutputParams, Utils, Wallet, WalletOptions load_dotenv() # In this example we check if an output has only an address unlock -# condition and that the address is from the account. +# condition and that the address is from the wallet. -wallet = Wallet(os.environ['WALLET_DB_PATH']) - -account = wallet.get_account("Alice") - -accountAddresses = account.addresses() +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) +address = wallet.address() # using prepare_output -output = account.prepare_output(OutputParams( - accountAddresses[0].address, 1000000)) - - -def hexAddress(address): - """Converts an address to hex""" - return Utils.bech32_to_hex(address.address) - +output = wallet.prepare_output(OutputParams( + address, 1000000)) -hexEncodedAccountAddresses = map(hexAddress, accountAddresses) +hexEncodedWalletAddress = Utils.bech32_to_hex(address) -controlled_by_account = False +controlled_by_wallet = False if len( output.unlock_conditions) == 1 and output.unlock_conditions[0].type == 0: - if output.unlock_conditions[0].address.pub_key_hash in hexEncodedAccountAddresses: - controlled_by_account = True + if output.unlock_conditions[0].address.pub_key_hash == hexEncodedWalletAddress: + controlled_by_wallet = True print( - f'The output has only an address unlock condition and the address is from the account: {controlled_by_account}') + f'The output has only an address unlock condition and the address is from the wallet: {controlled_by_wallet}') diff --git a/bindings/python/examples/wallet/backup.py b/bindings/python/examples/wallet/backup.py index b5302bb3df..9c8c9f6ba2 100644 --- a/bindings/python/examples/wallet/backup.py +++ b/bindings/python/examples/wallet/backup.py @@ -2,7 +2,7 @@ from dotenv import load_dotenv -from iota_sdk import ClientOptions, CoinType, StrongholdSecretManager, Wallet +from iota_sdk import ClientOptions, CoinType, StrongholdSecretManager, Wallet, WalletOptions, Bip44 load_dotenv() @@ -11,9 +11,6 @@ node_url = os.environ.get('NODE_URL', 'https://api.testnet.shimmer.network') client_options = ClientOptions(nodes=[node_url]) -# Shimmer coin type -coin_type = CoinType.SHIMMER - for env_var in ['STRONGHOLD_PASSWORD', 'MNEMONIC']: if env_var not in os.environ: raise Exception(f'.env {env_var} is undefined, see .env.example') @@ -21,14 +18,22 @@ secret_manager = StrongholdSecretManager( os.environ['STRONGHOLD_SNAPSHOT_PATH'], os.environ['STRONGHOLD_PASSWORD']) -wallet = Wallet('./backup-database', client_options, - coin_type, secret_manager) +bib_path = Bip44( + coin_type=CoinType.SHIMMER +) + +wallet_options = WalletOptions( + None, + None, + bib_path, + client_options, + secret_manager, + './backup-database') +wallet = Wallet(wallet_options) # Store the mnemonic in the Stronghold snapshot, this only needs to be # done once. -account = wallet.store_mnemonic(os.environ['MNEMONIC']) - -accounts = wallet.create_account('Alice') +wallet.store_mnemonic(os.environ['MNEMONIC']) wallet.backup("backup.stronghold", os.environ['STRONGHOLD_PASSWORD']) print('Created backup') diff --git a/bindings/python/examples/wallet/create_alias.py b/bindings/python/examples/wallet/create_alias.py index c4cdb3cb96..e9d6c4ff34 100644 --- a/bindings/python/examples/wallet/create_alias.py +++ b/bindings/python/examples/wallet/create_alias.py @@ -2,18 +2,16 @@ from dotenv import load_dotenv -from iota_sdk import Wallet +from iota_sdk import Wallet, WalletOptions load_dotenv() # In this example we will create an account output -wallet = Wallet(os.environ['WALLET_DB_PATH']) - -account = wallet.get_account('Alice') +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) # Sync account with the node -account.sync() +wallet.sync() if 'STRONGHOLD_PASSWORD' not in os.environ: raise Exception(".env STRONGHOLD_PASSWORD is undefined, see .env.example") @@ -21,5 +19,5 @@ wallet.set_stronghold_password(os.environ["STRONGHOLD_PASSWORD"]) # Send transaction. -transaction = account.create_account_output(None, None) +transaction = wallet.create_account_output(None, None) print(f'Block sent: {os.environ["EXPLORER_URL"]}/block/{transaction.block_id}') diff --git a/bindings/python/examples/wallet/get_client.py b/bindings/python/examples/wallet/get_client.py index 0e3c8bde03..7b95c00691 100644 --- a/bindings/python/examples/wallet/get_client.py +++ b/bindings/python/examples/wallet/get_client.py @@ -2,13 +2,13 @@ from dotenv import load_dotenv -from iota_sdk import Wallet +from iota_sdk import Wallet, WalletOptions load_dotenv() # This example gets a client from the wallet. -wallet = Wallet(os.environ['WALLET_DB_PATH']) +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) client = wallet.get_client() diff --git a/bindings/python/examples/wallet/getting_started.py b/bindings/python/examples/wallet/getting_started.py index bb001d7d75..bf4e077f69 100644 --- a/bindings/python/examples/wallet/getting_started.py +++ b/bindings/python/examples/wallet/getting_started.py @@ -6,11 +6,11 @@ from dotenv import load_dotenv from iota_sdk import (ClientOptions, CoinType, StrongholdSecretManager, Utils, - Wallet) + Wallet, WalletOptions, Bip44, SecretManager) load_dotenv() -# A name to associate with the created account. +# A name to associate with the created wallet. ACCOUNT_ALIAS = 'Alice' # The node to connect to. @@ -21,31 +21,35 @@ STRONGHOLD_PASSWORD = os.environ.get( 'STRONGHOLD_PASSWORD', 'a-secure-password') -# The path to store the account snapshot. +# The path to store the wallet snapshot. STRONGHOLD_SNAPSHOT_PATH = 'vault.stronghold' # Setup Stronghold secret manager secret_manager = StrongholdSecretManager( STRONGHOLD_SNAPSHOT_PATH, STRONGHOLD_PASSWORD) -# Set up and store the wallet. -client_options = ClientOptions(nodes=[node_url]) - -wallet = Wallet( - client_options=client_options, - coin_type=CoinType.SHIMMER, - secret_manager=secret_manager -) - # Generate a mnemonic and store its seed in the Stronghold vault. # INFO: It is best practice to back up the mnemonic somewhere secure. mnemonic = Utils.generate_mnemonic() print(f'Mnemonic: {mnemonic}') -wallet.store_mnemonic(mnemonic) -# Create an account. -account = wallet.create_account(ACCOUNT_ALIAS) +SecretManager(secret_manager).store_mnemonic(mnemonic) + +# Set up and store the wallet. +client_options = ClientOptions(nodes=[node_url]) + +bib_path = Bip44( + coin_type=CoinType.SHIMMER +) +wallet_options = WalletOptions( + None, + None, + bib_path, + client_options, + secret_manager) + +wallet = Wallet(wallet_options) # Get the first address and print it. -address = account.addresses()[0] -print(f'Address:\n{address.address}') +address = wallet.address() +print(f'Address:\n{address}') diff --git a/bindings/python/examples/wallet/logger.py b/bindings/python/examples/wallet/logger.py index 30c0019a14..0ea4a0313b 100644 --- a/bindings/python/examples/wallet/logger.py +++ b/bindings/python/examples/wallet/logger.py @@ -4,7 +4,7 @@ from dotenv import load_dotenv # pylint: disable=no-name-in-module -from iota_sdk import (ClientOptions, CoinType, StrongholdSecretManager, Wallet, +from iota_sdk import (ClientOptions, CoinType, StrongholdSecretManager, Wallet, WalletOptions, Bip44, init_logger) load_dotenv() @@ -24,9 +24,6 @@ node_url = os.environ.get('NODE_URL', 'https://api.testnet.shimmer.network') client_options = ClientOptions(nodes=[node_url]) -# Shimmer coin type -coin_type = CoinType.SHIMMER - for env_var in ['STRONGHOLD_PASSWORD', 'MNEMONIC']: if env_var not in os.environ: raise Exception(f'.env {env_var} is undefined, see .env.example') @@ -35,12 +32,19 @@ "wallet.stronghold", os.environ["STRONGHOLD_PASSWORD"]) -wallet = Wallet(os.environ['WALLET_DB_PATH'], client_options, - coin_type, secret_manager) +bib_path = Bip44( + coin_type=CoinType.SHIMMER +) + +wallet_options = WalletOptions( + None, + None, + bib_path, + client_options, + secret_manager, + os.environ.get('WALLET_DB_PATH')) +wallet = Wallet(wallet_options) # Store the mnemonic in the Stronghold snapshot, this only needs to be # done once. -account = wallet.store_mnemonic(os.environ["MNEMONIC"]) - -account = wallet.create_account('Alice') -print(account.get_metadata()) +wallet.store_mnemonic(os.environ["MNEMONIC"]) diff --git a/bindings/python/examples/wallet/migrate-stronghold-snapshot-v2-to-v3.py b/bindings/python/examples/wallet/migrate-stronghold-snapshot-v2-to-v3.py index 2af7aa5aa4..2e79b52205 100644 --- a/bindings/python/examples/wallet/migrate-stronghold-snapshot-v2-to-v3.py +++ b/bindings/python/examples/wallet/migrate-stronghold-snapshot-v2-to-v3.py @@ -3,7 +3,7 @@ from dotenv import load_dotenv # pylint: disable=no-name-in-module -from iota_sdk import (ClientOptions, CoinType, StrongholdSecretManager, Wallet, +from iota_sdk import (ClientOptions, CoinType, StrongholdSecretManager, Wallet, WalletOptions, Bip44, migrate_stronghold_snapshot_v2_to_v3) load_dotenv() @@ -12,16 +12,23 @@ v3_path = "./v3.stronghold" node_url = os.environ.get('NODE_URL', 'https://api.testnet.shimmer.network') client_options = ClientOptions(nodes=[node_url]) -coin_type = CoinType.SHIMMER +bib_path = Bip44( + coin_type=CoinType.SHIMMER +) + try: secret_manager = StrongholdSecretManager(v2_path, "current_password") # This should fail with error, migration required. - wallet = Wallet( - os.environ['WALLET_DB_PATH'], + + wallet_options = WalletOptions( + None, + None, + bib_path, client_options, - coin_type, - secret_manager) + secret_manager, + os.environ.get('WALLET_DB_PATH')) + wallet = Wallet(wallet_options) except ValueError as e: print(e) @@ -34,12 +41,14 @@ "new_password") secret_manager = StrongholdSecretManager(v3_path, "new_password") -# This shouldn't fail anymore as snapshot has been migrated. -wallet = Wallet( - os.environ['WALLET_DB_PATH'], + +wallet_options = WalletOptions( + None, + None, + bib_path, client_options, - coin_type, - secret_manager) + secret_manager, + os.environ.get('WALLET_DB_PATH')) -account = wallet.create_account('Alice') -print(account.get_metadata()) +# This shouldn't fail anymore as snapshot has been migrated. +wallet = Wallet(wallet_options) diff --git a/bindings/python/examples/wallet/offline_signing/0_generate_addresses.py b/bindings/python/examples/wallet/offline_signing/0_generate_addresses.py index 19a356e77a..0c2adfbfcb 100644 --- a/bindings/python/examples/wallet/offline_signing/0_generate_addresses.py +++ b/bindings/python/examples/wallet/offline_signing/0_generate_addresses.py @@ -9,7 +9,7 @@ from dotenv import load_dotenv -from iota_sdk import ClientOptions, CoinType, StrongholdSecretManager, Wallet +from iota_sdk import ClientOptions, CoinType, StrongholdSecretManager, Wallet, WalletOptions, Bip44 load_dotenv() @@ -28,20 +28,27 @@ secret_manager = StrongholdSecretManager( STRONGHOLD_SNAPSHOT_PATH, os.environ['STRONGHOLD_PASSWORD']) -wallet = Wallet(OFFLINE_WALLET_DB_PATH, offline_client_options, - CoinType.IOTA, secret_manager) +bib_path = Bip44( + coin_type=CoinType.SHIMMER +) + +wallet_options = WalletOptions( + None, + None, + bib_path, + offline_client_options, + secret_manager, + OFFLINE_WALLET_DB_PATH) +wallet = Wallet(wallet_options) # Store the mnemonic in the Stronghold snapshot, this only needs to be # done once wallet.store_mnemonic(os.environ['MNEMONIC']) -account = wallet.create_account('Alice', "rms") -print("Account created:", account.get_metadata()) - -# Get the addresses from the account (by default only one) -addresses = account.addresses() +# Get the address from the wallet +address = wallet.address() -json_data = json.dumps(list(map(lambda x: x.__dict__, addresses)), indent=4) +json_data = json.dumps(list(map(lambda x: x.__dict__, address)), indent=4) print(f"example.addresses.json:\n{json_data}") f = open(ADDRESSES_FILE_PATH, "w", encoding="utf-8") f.write(json_data) diff --git a/bindings/python/examples/wallet/offline_signing/1_prepare_transaction.py b/bindings/python/examples/wallet/offline_signing/1_prepare_transaction.py index 89741804a4..a36263fd4b 100644 --- a/bindings/python/examples/wallet/offline_signing/1_prepare_transaction.py +++ b/bindings/python/examples/wallet/offline_signing/1_prepare_transaction.py @@ -10,7 +10,7 @@ from dotenv import load_dotenv from iota_sdk import (AccountAddress, ClientOptions, CoinType, SendParams, - Wallet) + Wallet, WalletOptions, Bip44) load_dotenv() @@ -35,15 +35,21 @@ client_options = ClientOptions(nodes=[os.environ.get('NODE_URL')]) -wallet = Wallet(ONLINE_WALLET_DB_PATH, client_options, - CoinType.IOTA, "placeholder") - -account = wallet.create_account('Alice', "rms", addresses) -print("Account created:", account.get_metadata()) - -account.sync() - -prepared_transaction = account.prepare_send(params) +bib_path = Bip44( + coin_type=CoinType.SHIMMER +) +wallet_options = WalletOptions( + None, + None, + bib_path, + client_options, + "placeholder", + ONLINE_WALLET_DB_PATH) +wallet = Wallet(wallet_options) + +wallet.sync() + +prepared_transaction = wallet.prepare_send(params) json_data = json.dumps( prepared_transaction.prepared_transaction_data().to_dict(), diff --git a/bindings/python/examples/wallet/offline_signing/2_sign_transaction.py b/bindings/python/examples/wallet/offline_signing/2_sign_transaction.py index 6bbfb52dc6..1a30f5c14d 100644 --- a/bindings/python/examples/wallet/offline_signing/2_sign_transaction.py +++ b/bindings/python/examples/wallet/offline_signing/2_sign_transaction.py @@ -9,7 +9,7 @@ from dacite import from_dict from dotenv import load_dotenv -from iota_sdk import PreparedTransactionData, Wallet +from iota_sdk import PreparedTransactionData, Wallet, WalletOptions load_dotenv() @@ -24,9 +24,7 @@ prepared_transaction_data = from_dict( PreparedTransactionData, prepared_transaction_data) -wallet = Wallet(OFFLINE_WALLET_DB_PATH) - -account = wallet.get_account("Alice") +wallet = Wallet(WalletOptions(storage_path=OFFLINE_WALLET_DB_PATH)) if 'STRONGHOLD_PASSWORD' not in os.environ: raise Exception(".env STRONGHOLD_PASSWORD is undefined, see .env.example") @@ -34,7 +32,7 @@ wallet.set_stronghold_password(os.environ["STRONGHOLD_PASSWORD"]) # Signs prepared transaction offline. -signed_transaction_data = account.sign_transaction( +signed_transaction_data = wallet.sign_transaction( prepared_transaction_data) print("Signed transaction.") diff --git a/bindings/python/examples/wallet/offline_signing/3_send_transaction.py b/bindings/python/examples/wallet/offline_signing/3_send_transaction.py index bfa3ff8a8a..4cba62f441 100644 --- a/bindings/python/examples/wallet/offline_signing/3_send_transaction.py +++ b/bindings/python/examples/wallet/offline_signing/3_send_transaction.py @@ -9,7 +9,7 @@ from dacite import from_dict from dotenv import load_dotenv -from iota_sdk import SignedTransactionData, Wallet +from iota_sdk import SignedTransactionData, Wallet, WalletOptions load_dotenv() @@ -19,9 +19,7 @@ if 'EXPLORER_URL' not in os.environ: raise Exception(".env EXPLORER_URL is undefined, see .env.example") -wallet = Wallet(ONLINE_WALLET_DB_PATH, None, None, "placeholder") - -account = wallet.get_account("Alice") +wallet = Wallet(WalletOptions(storage_path=ONLINE_WALLET_DB_PATH)) signed_transaction_data = json.load( open(SIGNED_TRANSACTION_FILE_PATH, "r", encoding="utf-8")) @@ -29,9 +27,9 @@ SignedTransactionData, signed_transaction_data) # Sends offline signed transaction online. -transaction = account.submit_and_store_transaction(signed_transaction_data) +transaction = wallet.submit_and_store_transaction(signed_transaction_data) print( f'Transaction sent: {os.environ["EXPLORER_URL"]}/transaction/{transaction.transaction_id}') -block_id = account.reissue_transaction_until_included( +block_id = wallet.reissue_transaction_until_included( transaction.transaction_id) print(f'Block included: {os.environ["EXPLORER_URL"]}/block/{block_id}') diff --git a/bindings/python/examples/wallet/recover_accounts.py b/bindings/python/examples/wallet/recover_accounts.py deleted file mode 100644 index b121402463..0000000000 --- a/bindings/python/examples/wallet/recover_accounts.py +++ /dev/null @@ -1,39 +0,0 @@ -import json -import os - -from dotenv import load_dotenv - -from iota_sdk import ClientOptions, CoinType, StrongholdSecretManager, Wallet - -load_dotenv() - -# This example searches for accounts with unspent outputs. - -node_url = os.environ.get('NODE_URL', 'https://api.testnet.shimmer.network') -client_options = ClientOptions(nodes=[node_url]) - -# Shimmer coin type -coin_type = CoinType.SHIMMER - -for env_var in ['STRONGHOLD_PASSWORD', 'MNEMONIC']: - if env_var not in os.environ: - raise Exception(f'.env {env_var} is undefined, see .env.example') - -secret_manager = StrongholdSecretManager( - os.environ['STRONGHOLD_SNAPSHOT_PATH'], os.environ['STRONGHOLD_PASSWORD']) - -wallet = Wallet( - os.environ['WALLET_DB_PATH'], - client_options, - coin_type, - secret_manager) - -# Store the mnemonic in the Stronghold snapshot, this only needs to be -# done once. -account = wallet.store_mnemonic(os.environ['MNEMONIC']) - -# Searches for unspent outputs until no ones are found for 3 accounts in a row -# and checks the addresses for each account until 10 addresses in a row -# have nothing. -accounts = wallet.recover_accounts(0, 3, 10, None) -print(json.dumps(accounts, indent=4)) diff --git a/bindings/python/examples/wallet/restore_backup.py b/bindings/python/examples/wallet/restore_backup.py index 4d726fea05..4e18db71b7 100644 --- a/bindings/python/examples/wallet/restore_backup.py +++ b/bindings/python/examples/wallet/restore_backup.py @@ -1,10 +1,9 @@ import json import os -from dataclasses import asdict from dotenv import load_dotenv -from iota_sdk import ClientOptions, CoinType, Wallet +from iota_sdk import ClientOptions, CoinType, Wallet, WalletOptions, Bip44 load_dotenv() @@ -13,16 +12,22 @@ node_url = os.environ.get('NODE_URL', 'https://api.testnet.shimmer.network') client_options = ClientOptions(nodes=[node_url]) -# Shimmer coin type -coin_type = CoinType.SHIMMER +bib_path = Bip44( + coin_type=CoinType.SHIMMER +) -wallet = Wallet('./restore-backup-database', client_options, - coin_type, 'Placeholder') +wallet_options = WalletOptions( + None, + None, + bib_path, + client_options, + 'Placeholder', + './restore-backup-database') +wallet = Wallet(wallet_options) if 'STRONGHOLD_PASSWORD' not in os.environ: raise Exception(".env STRONGHOLD_PASSWORD is undefined, see .env.example") wallet.restore_backup("backup.stronghold", os.environ['STRONGHOLD_PASSWORD']) -accounts = wallet.get_accounts() -print(f'Restored accounts: {json.dumps(asdict(accounts), indent=4)}') +print(f'Restored wallet: {json.dumps(wallet, indent=4)}') diff --git a/bindings/python/examples/wallet/transaction_options.py b/bindings/python/examples/wallet/transaction_options.py index 5352e13310..8055406e3a 100644 --- a/bindings/python/examples/wallet/transaction_options.py +++ b/bindings/python/examples/wallet/transaction_options.py @@ -3,18 +3,16 @@ from dotenv import load_dotenv from iota_sdk import (RemainderValueStrategy, TaggedDataPayload, SendParams, - TransactionOptions, Wallet, utf8_to_hex) + TransactionOptions, Wallet, WalletOptions, utf8_to_hex) load_dotenv() # This example sends a transaction with a tagged data payload. -wallet = Wallet(os.environ['WALLET_DB_PATH']) - -account = wallet.get_account('Alice') +wallet = Wallet(WalletOptions(storage_path=os.environ.get('WALLET_DB_PATH'))) # Sync account with the node -response = account.sync() +response = wallet.sync() if 'STRONGHOLD_PASSWORD' not in os.environ: raise Exception(".env STRONGHOLD_PASSWORD is undefined, see .env.example") @@ -26,7 +24,7 @@ amount=1000000, )] -transaction = account.send_with_params( +transaction = wallet.send_with_params( params, TransactionOptions( remainder_value_strategy=RemainderValueStrategy.ReuseAddress, diff --git a/bindings/python/iota_sdk/__init__.py b/bindings/python/iota_sdk/__init__.py index 2d8174022b..2b58358006 100644 --- a/bindings/python/iota_sdk/__init__.py +++ b/bindings/python/iota_sdk/__init__.py @@ -1,13 +1,14 @@ # Copyright 2023 IOTA Stiftung # SPDX-License-Identifier: Apache-2.0 +from .external import * + from .client.client import Client, NodeIndexerAPI, ClientError from .client._high_level_api import GenerateAddressesOptions, GenerateAddressOptions -from .external import * from .utils import Utils -from .wallet.wallet import Wallet, Account +from .wallet.wallet import Wallet, WalletOptions from .wallet.common import WalletError -from .wallet.sync_options import AccountSyncOptions, NftSyncOptions, SyncOptions +from .wallet.sync_options import AccountSyncOptions, NftSyncOptions, SyncOptions, WalletSyncOptions from .secret_manager.secret_manager import * from .prefix_hex import * from .types.address import * diff --git a/bindings/python/iota_sdk/client/_node_core_api.py b/bindings/python/iota_sdk/client/_node_core_api.py index dbbbfa5159..079c7395dd 100644 --- a/bindings/python/iota_sdk/client/_node_core_api.py +++ b/bindings/python/iota_sdk/client/_node_core_api.py @@ -80,7 +80,7 @@ def post_block(self, block: Block) -> HexStr: The block id of the posted block. """ return self._call_method('postBlock', { - 'block': block.__dict__ + 'block': block.to_dict() }) def get_block(self, block_id: HexStr) -> Block: diff --git a/bindings/python/iota_sdk/client/_utils.py b/bindings/python/iota_sdk/client/_utils.py index 41427b8031..79c9e8007e 100644 --- a/bindings/python/iota_sdk/client/_utils.py +++ b/bindings/python/iota_sdk/client/_utils.py @@ -4,6 +4,7 @@ from typing import Optional from abc import ABCMeta, abstractmethod +from iota_sdk.types.block.block import Block from iota_sdk.types.common import HexStr from iota_sdk.types.output import Output @@ -87,7 +88,7 @@ def request_funds_from_faucet(self, url: str, address: str) -> str: } ) - def block_id(block: Block) -> HexStr: + def block_id(self, block: Block) -> HexStr: """ Return a block ID (Blake2b256 hash of block bytes) from a block. """ return self._call_method('blockId', { diff --git a/bindings/python/iota_sdk/secret_manager/secret_manager.py b/bindings/python/iota_sdk/secret_manager/secret_manager.py index 01fd5aad7c..07158e3baa 100644 --- a/bindings/python/iota_sdk/secret_manager/secret_manager.py +++ b/bindings/python/iota_sdk/secret_manager/secret_manager.py @@ -3,12 +3,12 @@ from json import dumps, loads from typing import Optional, Union -from dacite import from_dict import humps from iota_sdk.external import create_secret_manager, call_secret_manager_method from iota_sdk.types.block.block import Block, UnsignedBlock from iota_sdk.types.common import HexStr +from iota_sdk.types.node_info import ProtocolParameters from iota_sdk.types.signature import Ed25519Signature, Bip44 from iota_sdk.types.transaction_data import PreparedTransactionData from iota_sdk.types.payload import SignedTransactionPayload @@ -269,14 +269,16 @@ def sign_secp256k1_ecdsa(self, message: HexStr, chain: Bip44): }) def sign_transaction( - self, prepared_transaction_data: PreparedTransactionData) -> SignedTransactionPayload: + self, prepared_transaction_data: PreparedTransactionData, protocol_parameters: ProtocolParameters) -> SignedTransactionPayload: """Sign a transaction. Args: prepare_transaction_data: The prepared transaction data that needs to be signed. + protocol_parameters: The protocol parameters used in creating the signed transaction. """ - return from_dict(SignedTransactionPayload, self._call_method('signTransaction', { - 'preparedTransactionData': prepared_transaction_data.to_dict() + return SignedTransactionPayload.from_dict(self._call_method('signTransaction', { + 'preparedTransactionData': prepared_transaction_data.to_dict(), + 'protocolParameters': protocol_parameters })) def sign_block( @@ -287,7 +289,7 @@ def sign_block( unsigned_block: The unsigned block data. chain: The Bip44 chain to use. """ - return from_dict(Block, self._call_method('signBlock', { + return Block.from_dict(self._call_method('signBlock', { 'unsignedBlock': unsigned_block.to_dict(), 'chain': chain.to_dict() })) diff --git a/bindings/python/iota_sdk/types/address.py b/bindings/python/iota_sdk/types/address.py index aa55d0fe5f..b2ca48ef4a 100644 --- a/bindings/python/iota_sdk/types/address.py +++ b/bindings/python/iota_sdk/types/address.py @@ -163,17 +163,6 @@ def with_allowed_capabilities(self, capabilities: bytes): self.allowed_capabilities = None -@json -@dataclass -class AddressWithUnspentOutputs(): - """An Address with unspent outputs. - """ - address: str - key_index: int - internal: bool - output_ids: bool - - Address: TypeAlias = Union[Ed25519Address, AccountAddress, NFTAddress, @@ -183,6 +172,7 @@ class AddressWithUnspentOutputs(): RestrictedAddress] +# pylint: disable=too-many-return-statements def deserialize_address(d: Dict[str, Any]) -> Address: """ Takes a dictionary as input and returns an instance of a specific class based on the value of the 'type' key in the dictionary. diff --git a/bindings/python/iota_sdk/types/balance.py b/bindings/python/iota_sdk/types/balance.py index 27785e4155..c13f0f1466 100644 --- a/bindings/python/iota_sdk/types/balance.py +++ b/bindings/python/iota_sdk/types/balance.py @@ -28,7 +28,7 @@ class BaseCoinBalance: @json @dataclass class RequiredStorageDeposit: - """Required storage deposit for the outputs in the account. + """Required storage deposit for the outputs in the wallet. Attributes: basic: The required amount for basic outputs. diff --git a/bindings/python/iota_sdk/types/common.py b/bindings/python/iota_sdk/types/common.py index 3cd9ee4218..2f85d96704 100644 --- a/bindings/python/iota_sdk/types/common.py +++ b/bindings/python/iota_sdk/types/common.py @@ -31,8 +31,17 @@ def custom_to_dict(self, *args, **kwargs): # pylint: disable=protected-access original_dict = to_dict(self, *args, **kwargs) - result = {k: v for k, v in original_dict.items() if v is not None} - return result + # recursive remove the None values + def filter_none(value): + if isinstance(value, dict): + return {k: filter_none(v) + for k, v in value.items() if v is not None} + if isinstance(value, list): + return [filter_none(item) + for item in value if item is not None] + return value + + return filter_none(original_dict) def custom_to_json(self, *args, **kwargs): # Use the custom to_dict method for serialization @@ -79,7 +88,7 @@ class Node(): password: Optional[str] = None disabled: Optional[bool] = None - def to_dict(self): + def to_dict(self) -> dict: """Custom dict conversion. """ diff --git a/bindings/python/iota_sdk/types/feature.py b/bindings/python/iota_sdk/types/feature.py index 4908be3b02..01eb0c4abd 100644 --- a/bindings/python/iota_sdk/types/feature.py +++ b/bindings/python/iota_sdk/types/feature.py @@ -152,6 +152,7 @@ class StakingFeature: MetadataFeature, TagFeature, NativeTokenFeature, BlockIssuerFeature, StakingFeature] +# pylint: disable=too-many-return-statements def deserialize_feature(d: Dict[str, Any]) -> Feature: """ Takes a dictionary as input and returns an instance of a specific class based on the value of the 'type' key in the dictionary. diff --git a/bindings/python/iota_sdk/types/node_info.py b/bindings/python/iota_sdk/types/node_info.py index 9cc94c10fa..8395afc8fe 100644 --- a/bindings/python/iota_sdk/types/node_info.py +++ b/bindings/python/iota_sdk/types/node_info.py @@ -79,12 +79,22 @@ class StorageScoreParameters: offset_staking_feature: Defines the offset to be used for staking feature. offset_delegation: Defines the offset to be used for delegation output. """ - storage_cost: int + storage_cost: int = field(metadata=config( + encoder=str + )) factor_data: int - offset_output_overhead: int - offset_ed25519_block_issuer_key: int - offset_staking_feature: int - offset_delegation: int + offset_output_overhead: int = field(metadata=config( + encoder=str + )) + offset_ed25519_block_issuer_key: int = field(metadata=config( + encoder=str + )) + offset_staking_feature: int = field(metadata=config( + encoder=str + )) + offset_delegation: int = field(metadata=config( + encoder=str + )) @json @@ -175,6 +185,7 @@ class ManaParameters: decay_factors_exponent: The scaling of decay_factors expressed as an exponent of 2. decay_factor_epochs_sum: An integer approximation of the sum of decay over epochs. decay_factor_epochs_sum_exponent: The scaling of decay_factor_epochs_sum expressed as an exponent of 2. + annual_decay_factor_percentage: Decay factor for 1 year. """ bits_count: int generation_rate: int @@ -183,6 +194,7 @@ class ManaParameters: decay_factors_exponent: int decay_factor_epochs_sum: int decay_factor_epochs_sum_exponent: int + annual_decay_factor_percentage: int @json @@ -242,6 +254,7 @@ class ProtocolParameters: version_signaling_parameters: The version signaling parameters. rewards_parameters: Rewards Parameters defines the parameters that are used to calculate Mana rewards. target_committee_size: Defines the target size of the committee. If there's fewer candidates the actual committee size could be smaller in a given epoch. + chain_switching_threshold: Defines the number of heavier slots that a chain needs to be ahead of the current chain to be considered for switching. """ type: int version: int @@ -259,26 +272,19 @@ class ProtocolParameters: slot_duration_in_seconds: int slots_per_epoch_exponent: int mana_parameters: ManaParameters - staking_unbonding_period: int = field(metadata=config( - encoder=str - )) + staking_unbonding_period: int validation_blocks_per_slot: int punishment_epochs: int liveness_threshold_lower_bound: int liveness_threshold_upper_bound: int - min_committable_age: int = field(metadata=config( - encoder=str - )) - max_committable_age: int = field(metadata=config( - encoder=str - )) - epoch_nearing_threshold: int = field(metadata=config( - encoder=str - )) + min_committable_age: int + max_committable_age: int + epoch_nearing_threshold: int congestion_control_parameters: CongestionControlParameters version_signaling_parameters: VersionSignalingParameters rewards_parameters: RewardsParameters target_committee_size: int + chain_switching_threshold: int @json diff --git a/bindings/python/iota_sdk/types/output_metadata.py b/bindings/python/iota_sdk/types/output_metadata.py index d006cdc648..9ac027eebd 100644 --- a/bindings/python/iota_sdk/types/output_metadata.py +++ b/bindings/python/iota_sdk/types/output_metadata.py @@ -65,7 +65,7 @@ def as_dict(self): """ d = {} - d['metadata'] = self.metadata.__dict__ + d['metadata'] = self.metadata.to_dict() d['output'] = self.output.as_dict() return d diff --git a/bindings/python/iota_sdk/types/output_params.py b/bindings/python/iota_sdk/types/output_params.py index 6133a2e704..2303396596 100644 --- a/bindings/python/iota_sdk/types/output_params.py +++ b/bindings/python/iota_sdk/types/output_params.py @@ -62,7 +62,7 @@ class StorageDeposit(): @json @dataclass class OutputParams(): - """Params for `Account.prepare_output()`. + """Params for `Wallet.prepare_output()`. """ recipient_address: str amount: int = field(metadata=config( diff --git a/bindings/python/iota_sdk/types/payload.py b/bindings/python/iota_sdk/types/payload.py index 280e97fb82..5187545c6a 100644 --- a/bindings/python/iota_sdk/types/payload.py +++ b/bindings/python/iota_sdk/types/payload.py @@ -5,6 +5,7 @@ from enum import IntEnum from typing import Any, Dict, List, TypeAlias, Union from dataclasses import dataclass, field +from dataclasses_json import config from iota_sdk.types.common import HexStr, json from iota_sdk.types.transaction import Transaction from iota_sdk.types.unlock import Unlock, deserialize_unlocks diff --git a/bindings/python/iota_sdk/types/transaction_options.py b/bindings/python/iota_sdk/types/transaction_options.py index 9437d99e56..fd4fd0107d 100644 --- a/bindings/python/iota_sdk/types/transaction_options.py +++ b/bindings/python/iota_sdk/types/transaction_options.py @@ -52,7 +52,7 @@ class RemainderValueStrategy(Enum): ChangeAddress = None ReuseAddress = None - def to_dict(self): + def to_dict(self) -> dict: """Custom dict conversion. """ diff --git a/bindings/python/iota_sdk/types/unlock.py b/bindings/python/iota_sdk/types/unlock.py index 5f5f4b3296..507bc35118 100644 --- a/bindings/python/iota_sdk/types/unlock.py +++ b/bindings/python/iota_sdk/types/unlock.py @@ -5,6 +5,7 @@ from dataclasses import dataclass, field from enum import IntEnum from typing import Dict, List, TypeAlias, Union, Any +from dataclasses_json import config from iota_sdk.types.signature import Ed25519Signature from iota_sdk.types.common import json @@ -88,6 +89,13 @@ class NftUnlock: type: int = field(default_factory=lambda: int(UnlockType.Nft), init=False) +# pylint: disable=missing-function-docstring,unused-argument +def deserialize_unlocks(dicts: List[Dict[str, Any]]) -> List[Unlock]: + # Function gets overwritten further below, but needs to be defined here + # already + pass + + @json @dataclass class MultiUnlock: @@ -121,6 +129,8 @@ class EmptyUnlock: MultiUnlock, EmptyUnlock] +# pylint: disable=too-many-return-statements + def deserialize_unlock(d: Dict[str, Any]) -> Unlock: """ @@ -147,6 +157,7 @@ def deserialize_unlock(d: Dict[str, Any]) -> Unlock: raise Exception(f'invalid unlock type: {unlock_type}') +# pylint: disable=function-redefined def deserialize_unlocks(dicts: List[Dict[str, Any]]) -> List[Unlock]: """ Takes a list of dictionaries as input and returns a list with specific instances of classes based on the value of the 'type' key in the dictionary. diff --git a/bindings/python/iota_sdk/utils.py b/bindings/python/iota_sdk/utils.py index 17fdfc9395..e6fc16c330 100644 --- a/bindings/python/iota_sdk/utils.py +++ b/bindings/python/iota_sdk/utils.py @@ -201,7 +201,7 @@ def verify_ed25519_signature( """Verify an Ed25519 signature against a message. """ return _call_method('verifyEd25519Signature', { - 'signature': signature.__dict__, + 'signature': signature.to_dict(), 'message': message, }) @@ -218,13 +218,14 @@ def verify_secp256k1_ecdsa_signature( @staticmethod def verify_transaction_semantic( - transaction: Transaction, inputs: List[InputSigningData], unlocks: Optional[List[Unlock]] = None) -> str: + transaction: Transaction, inputs: List[InputSigningData], protocol_parameters: ProtocolParameters, unlocks: Optional[List[Unlock]] = None) -> str: """Verifies the semantic of a transaction. """ return _call_method('verifyTransactionSemantic', { 'transaction': transaction.as_dict(), 'inputs': [i.as_dict() for i in inputs], 'unlocks': [u.as_dict() for u in unlocks], + 'protocolParameters': protocol_parameters.as_dict(), }) diff --git a/bindings/python/iota_sdk/wallet/account.py b/bindings/python/iota_sdk/wallet/account.py deleted file mode 100644 index e1bec85b46..0000000000 --- a/bindings/python/iota_sdk/wallet/account.py +++ /dev/null @@ -1,634 +0,0 @@ -# Copyright 2023 IOTA Stiftung -# SPDX-License-Identifier: Apache-2.0 - -from typing import List, Optional, Union -from dataclasses import dataclass -from dacite import from_dict -from iota_sdk.wallet.common import _call_method_routine -from iota_sdk.wallet.prepared_transaction import PreparedTransaction, PreparedCreateTokenTransaction -from iota_sdk.wallet.sync_options import SyncOptions -from iota_sdk.types.address import AccountAddress, AddressWithUnspentOutputs -from iota_sdk.types.balance import Balance -from iota_sdk.types.burn import Burn -from iota_sdk.types.common import HexStr -from iota_sdk.types.filter_options import FilterOptions -from iota_sdk.types.native_token import NativeToken -from iota_sdk.types.output_data import OutputData -from iota_sdk.types.output_id import OutputId -from iota_sdk.types.output import BasicOutput, NftOutput, Output, deserialize_output -from iota_sdk.types.output_params import OutputParams -from iota_sdk.types.transaction_data import PreparedTransactionData, SignedTransactionData -from iota_sdk.types.send_params import CreateAccountOutputParams, CreateNativeTokenParams, MintNftParams, SendNativeTokenParams, SendNftParams, SendParams -from iota_sdk.types.transaction_with_metadata import TransactionWithMetadata -from iota_sdk.types.transaction_options import TransactionOptions -from iota_sdk.types.consolidation_params import ConsolidationParams - - -@dataclass -class AccountMetadata: - """Account metadata. - - Attributes: - alias: The alias name of the account. - coinType: The type of coin managed with the account. - index: The account index. - """ - alias: str - coinType: int - index: int - -# pylint: disable=too-many-public-methods - - -# pylint: disable=too-many-public-methods -class Account: - """A wallet account. - - Attributes: - meta: Some account metadata. - handle: The account handle. - """ - - def __init__(self, meta: dict, handle): - """Initializes an account. - - Args: - meta: The account data. - handle: The account handle. - """ - self.meta = meta - self.handle = handle - - @_call_method_routine - def _call_account_method(self, method, data=None): - message = { - 'name': 'callAccountMethod', - 'data': { - 'accountId': self.meta["index"], - 'method': { - 'name': method, - } - } - } - if data: - message['data']['method']['data'] = data - - return message - - def get_metadata(self) -> AccountMetadata: - """Get the accounts metadata. - """ - return AccountMetadata( - self.meta["alias"], self.meta["coinType"], self.meta["index"]) - - def burn( - self, burn: Burn, options: Optional[TransactionOptions] = None) -> TransactionWithMetadata: - """A generic function that can be used to burn native tokens, nfts, foundries and aliases. - """ - return self.prepare_burn(burn, options).send() - - def prepare_burn( - self, burn: Burn, options: Optional[TransactionOptions] = None) -> PreparedTransaction: - """A generic `prepare_burn()` function that can be used to prepare the burn of native tokens, nfts, foundries and accounts. - """ - prepared = self._call_account_method( - 'prepareBurn', { - 'burn': burn.to_dict(), - 'options': options - }, - ) - return PreparedTransaction(self, prepared) - - def prepare_burn_native_token(self, - token_id: HexStr, - burn_amount: int, - options: Optional[TransactionOptions] = None) -> PreparedTransaction: - """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. - """ - prepared = self._call_account_method( - 'prepareBurn', { - 'burn': Burn().add_native_token(NativeToken(token_id, hex(burn_amount))).to_dict(), - 'options': options - }, - ) - return PreparedTransaction(self, prepared) - - def prepare_burn_nft(self, - nft_id: HexStr, - options: Optional[TransactionOptions] = None) -> PreparedTransaction: - """Burn an nft output. - """ - prepared = self._call_account_method( - 'prepareBurn', { - 'burn': Burn().add_nft(nft_id).to_dict(), - 'options': options - }, - ) - return PreparedTransaction(self, prepared) - - def consolidate_outputs( - self, params: ConsolidationParams) -> TransactionWithMetadata: - """Consolidate outputs. - """ - return self.prepare_consolidate_outputs(params).send() - - def prepare_consolidate_outputs( - self, params: ConsolidationParams) -> PreparedTransaction: - """Consolidate outputs. - """ - prepared = self._call_account_method( - 'prepareConsolidateOutputs', { - 'params': params - } - ) - return PreparedTransaction(self, prepared) - - def create_account_output(self, - params: Optional[CreateAccountOutputParams] = None, - options: Optional[TransactionOptions] = None) -> TransactionWithMetadata: - """Create an account output. - """ - return self.prepare_create_account_output(params, options).send() - - def prepare_create_account_output(self, - params: Optional[CreateAccountOutputParams] = None, - options: Optional[TransactionOptions] = None) -> PreparedTransaction: - """Create an account output. - """ - prepared = self._call_account_method( - 'prepareCreateAccountOutput', { - 'params': params, - 'options': options - } - ) - return PreparedTransaction(self, prepared) - - def prepare_destroy_account(self, - account_id: HexStr, - options: Optional[TransactionOptions] = None) -> PreparedTransaction: - """Destroy an account output. - """ - prepared = self._call_account_method( - 'prepareBurn', { - 'burn': Burn().add_account(account_id).to_dict(), - 'options': options - }, - ) - return PreparedTransaction(self, prepared) - - def prepare_destroy_foundry(self, - foundry_id: HexStr, - options: Optional[TransactionOptions] = None) -> PreparedTransaction: - """Destroy a foundry output with a circulating supply of 0. - """ - prepared = self._call_account_method( - 'prepareBurn', { - 'burn': Burn().add_foundry(foundry_id).to_dict(), - 'options': options - }, - ) - return PreparedTransaction(self, prepared) - - def generate_ed25519_addresses( - self, amount: int, options=None) -> List[AccountAddress]: - """Generate new addresses. - """ - addresses = self._call_account_method( - 'generateEd25519Addresses', { - 'amount': amount, - 'options': options - } - ) - return [AccountAddress.from_dict(address) for address in addresses] - - def claimable_outputs(self, outputs_to_claim: List[OutputId]): - """Get outputs with additional unlock conditions. - """ - return self._call_account_method( - 'claimableOutputs', { - 'outputsToClaim': outputs_to_claim - } - ) - - def get_output(self, output_id: OutputId) -> OutputData: - """Get output. - """ - return from_dict(OutputData, self._call_account_method( - 'getOutput', { - 'outputId': output_id - } - )) - - def get_transaction( - self, transaction_id: HexStr) -> TransactionWithMetadata: - """Get transaction. - """ - return TransactionWithMetadata.from_dict(self._call_account_method( - 'getTransaction', { - 'transactionId': transaction_id - } - )) - - def addresses(self) -> List[AccountAddress]: - """List addresses. - """ - addresses = self._call_account_method( - 'addresses' - ) - return [AccountAddress.from_dict(address) for address in addresses] - - def addresses_with_unspent_outputs( - self) -> List[AddressWithUnspentOutputs]: - """Returns only addresses of the account with unspent outputs. - """ - addresses = self._call_account_method( - 'addressesWithUnspentOutputs' - ) - return [AddressWithUnspentOutputs.from_dict(address) - for address in addresses] - - def outputs( - self, filter_options: Optional[FilterOptions] = None) -> List[OutputData]: - """Returns all outputs of the account. - """ - outputs = self._call_account_method( - 'outputs', { - 'filterOptions': filter_options - } - ) - return [OutputData.from_dict(o) for o in outputs] - - def unspent_outputs( - self, filter_options: Optional[FilterOptions] = None) -> List[OutputData]: - """Returns all unspent outputs of the account. - """ - outputs = self._call_account_method( - 'unspentOutputs', { - 'filterOptions': filter_options - } - ) - return [from_dict(OutputData, o) for o in outputs] - - def implicit_account_creation_address(self) -> str: - """Returns the implicit account creation address of the wallet if it is Ed25519 based. - """ - return self._call_account_method( - 'implicitAccountCreationAddress' - ) - - def implicit_account_transition( - self, output_id: OutputId) -> TransactionWithMetadata: - """Transitions an implicit account to an account. - """ - return self.prepare_implicit_account_transition(output_id).send() - - def prepare_implicit_account_transition( - self, output_id: OutputId) -> PreparedTransaction: - """Prepares to transition an implicit account to an account. - """ - prepared = self._call_account_method( - 'implicitAccountTransition', { - 'outputId': output_id - } - ) - return PreparedTransaction( - account=self, prepared_transaction_data=prepared) - - def accounts(self) -> List[OutputData]: - """Returns the accounts of the wallet. - """ - outputs = self._call_account_method( - 'accounts' - ) - return [from_dict(OutputData, o) for o in outputs] - - def implicit_accounts(self) -> List[OutputData]: - """Returns the implicit accounts of the wallet. - """ - outputs = self._call_account_method( - 'implicitAccounts' - ) - return [from_dict(OutputData, o) for o in outputs] - - def incoming_transactions(self) -> List[TransactionWithMetadata]: - """Returns all incoming transactions of the account. - """ - transactions = self._call_account_method( - 'incomingTransactions' - ) - return [TransactionWithMetadata.from_dict(tx) for tx in transactions] - - def transactions(self) -> List[TransactionWithMetadata]: - """Returns all transaction of the account. - """ - transactions = self._call_account_method( - 'transactions' - ) - return [TransactionWithMetadata.from_dict(tx) for tx in transactions] - - def pending_transactions(self): - """Returns all pending transactions of the account. - """ - transactions = self._call_account_method( - 'pendingTransactions' - ) - return [TransactionWithMetadata.from_dict(tx) for tx in transactions] - - def create_native_token(self, params: CreateNativeTokenParams, - options: Optional[TransactionOptions] = None) -> TransactionWithMetadata: - """Create native token. - """ - return self.prepare_create_native_token(params, options).send() - - def prepare_create_native_token(self, params: CreateNativeTokenParams, - options: Optional[TransactionOptions] = None) -> PreparedTransaction: - """Create native token. - """ - prepared = self._call_account_method( - 'prepareCreateNativeToken', { - 'params': params, - 'options': options - } - ) - return PreparedCreateTokenTransaction( - account=self, prepared_transaction_data=prepared) - - def melt_native_token(self, - token_id: HexStr, - melt_amount: int, - options: Optional[TransactionOptions] = None) -> TransactionWithMetadata: - """Melt native tokens. This happens with the foundry output which minted them, by increasing it's - `melted_tokens` field. - """ - return self.prepare_melt_native_token( - token_id, melt_amount, options).send() - - def prepare_melt_native_token(self, - token_id: HexStr, - melt_amount: int, - options: Optional[TransactionOptions] = None) -> PreparedTransaction: - """Melt native tokens. This happens with the foundry output which minted them, by increasing it's - `melted_tokens` field. - """ - prepared = self._call_account_method( - 'prepareMeltNativeToken', { - 'tokenId': token_id, - 'meltAmount': hex(melt_amount), - 'options': options - } - ) - return PreparedTransaction(self, prepared) - - def mint_native_token(self, token_id: HexStr, mint_amount: int, - options: Optional[TransactionOptions] = None) -> TransactionWithMetadata: - """Mint additional native tokens. - """ - return self.prepare_mint_native_token( - token_id, mint_amount, options).send() - - def prepare_mint_native_token(self, token_id: HexStr, mint_amount: int, - options: Optional[TransactionOptions] = None) -> PreparedTransaction: - """Mint additional native tokens. - """ - prepared = self._call_account_method( - 'prepareMintNativeToken', { - 'tokenId': token_id, - 'mintAmount': hex(mint_amount), - 'options': options - } - ) - return PreparedTransaction(self, prepared) - - def mint_nfts(self, params: List[MintNftParams], - options: Optional[TransactionOptions] = None) -> TransactionWithMetadata: - """Mint NFTs. - """ - return self.prepare_mint_nfts(params, options).send() - - def prepare_mint_nfts(self, params: List[MintNftParams], - options: Optional[TransactionOptions] = None) -> PreparedTransaction: - """Mint NFTs. - """ - prepared = self._call_account_method( - 'prepareMintNfts', { - 'params': params, - 'options': options - } - ) - return PreparedTransaction(self, prepared) - - def get_balance(self) -> Balance: - """Get account balance information. - """ - return Balance.from_dict(self._call_account_method( - 'getBalance' - )) - - def prepare_output(self, params: OutputParams, - transaction_options: Optional[TransactionOptions] = None) -> Union[BasicOutput, NftOutput]: - """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 - minimum required storage deposit will be sent to the recipient. - When the assets contain an nft_id, the data from the existing nft output will be used, just with the address - unlock conditions replaced - """ - return deserialize_output(self._call_account_method( - 'prepareOutput', { - 'params': params, - 'transactionOptions': transaction_options - }) - ) - - def prepare_send(self, params: List[SendParams], - options: Optional[TransactionOptions] = None) -> PreparedTransaction: - """Prepare to send base coins. - """ - prepared = self._call_account_method( - 'prepareSend', { - 'params': params, - 'options': options - } - ) - return PreparedTransaction(self, prepared) - - def send_transaction( - self, outputs: List[Output], options: Optional[TransactionOptions] = None) -> TransactionWithMetadata: - """Send a transaction. - """ - return self.prepare_transaction(outputs, options).send() - - def prepare_transaction( - self, outputs: List[Output], options: Optional[TransactionOptions] = None) -> PreparedTransaction: - """Prepare transaction. - """ - prepared = self._call_account_method( - 'prepareTransaction', { - 'outputs': outputs, - 'options': options - } - ) - return PreparedTransaction(self, prepared) - - def reissue_transaction_until_included( - self, transaction_id: HexStr, interval=None, max_attempts=None) -> HexStr: - """Reissues a transaction sent from the account for a provided transaction id until it's - included (referenced by a milestone). Returns the included block id. - """ - return self._call_account_method( - 'reissueTransactionUntilIncluded', { - 'transactionId': transaction_id, - 'interval': interval, - 'maxAttempts': max_attempts - } - ) - - def sync(self, options: Optional[SyncOptions] = None) -> Balance: - """Sync the account by fetching new information from the nodes. - Will also reissue pending transactions and consolidate outputs if necessary. - A custom default can be set using set_default_sync_options. - """ - return from_dict(Balance, self._call_account_method( - 'sync', { - 'options': options, - } - )) - - def send(self, amount: int, address: str, - options: Optional[TransactionOptions] = None) -> TransactionWithMetadata: - """Send base coins. - """ - return TransactionWithMetadata.from_dict(self._call_account_method( - 'send', { - 'amount': str(amount), - 'address': address, - 'options': options - } - )) - - def send_with_params( - self, params: List[SendParams], options: Optional[TransactionOptions] = None) -> TransactionWithMetadata: - """Send base coins to multiple addresses or with additional parameters. - """ - return TransactionWithMetadata.from_dict(self._call_account_method( - 'sendWithParams', { - 'params': [param.to_dict() for param in params], - 'options': options - } - )) - - def send_native_tokens( - self, params: List[SendNativeTokenParams], options: Optional[TransactionOptions] = None) -> TransactionWithMetadata: - """Send native tokens. - """ - return self.prepare_send_native_tokens(params, options).send() - - def prepare_send_native_tokens( - self, - params: List[SendNativeTokenParams], - options: Optional[TransactionOptions] = None) -> PreparedTransaction: - """Send native tokens. - """ - prepared = self._call_account_method( - 'prepareSendNativeTokens', { - 'params': params, - 'options': options - } - ) - return PreparedTransaction(self, prepared) - - def send_nft(self, params: List[SendNftParams], - options: Optional[TransactionOptions] = None) -> TransactionWithMetadata: - """Send nft. - """ - return self.prepare_send_nft(params, options).send() - - def prepare_send_nft(self, params: List[SendNftParams], - options: Optional[TransactionOptions] = None) -> PreparedTransaction: - """Send nft. - """ - prepared = self._call_account_method( - 'prepareSendNft', { - 'params': params, - 'options': options - } - ) - return PreparedTransaction(self, prepared) - - def set_alias(self, alias: str): - """Set alias. - """ - return self._call_account_method( - 'setAlias', { - 'alias': alias - } - ) - - def set_default_sync_options(self, options: SyncOptions): - """Set the fallback SyncOptions for account syncing. - If storage is enabled, will persist during restarts. - """ - return self._call_account_method( - 'setDefaultSyncOptions', { - 'options': options - } - ) - - def sign_transaction( - self, prepared_transaction_data: PreparedTransactionData) -> SignedTransactionData: - """Sign a transaction. - """ - return SignedTransactionData.from_dict(self._call_account_method( - 'signTransaction', { - 'preparedTransactionData': prepared_transaction_data - } - )) - - def sign_and_submit_transaction( - self, prepared_transaction_data: PreparedTransactionData) -> TransactionWithMetadata: - """Validate the transaction, sign it, submit it to a node and store it in the account. - """ - return TransactionWithMetadata.from_dict(self._call_account_method( - 'signAndSubmitTransaction', { - 'preparedTransactionData': prepared_transaction_data - } - )) - - def submit_and_store_transaction( - self, signed_transaction_data: SignedTransactionData) -> TransactionWithMetadata: - """Submit and store transaction. - """ - return TransactionWithMetadata.from_dict(self._call_account_method( - 'submitAndStoreTransaction', { - 'signedTransactionData': signed_transaction_data - } - )) - - def claim_outputs( - self, output_ids_to_claim: List[OutputId]) -> TransactionWithMetadata: - """Claim outputs. - """ - return self.prepare_claim_outputs(output_ids_to_claim).send() - - def prepare_claim_outputs( - self, output_ids_to_claim: List[OutputId]) -> PreparedTransaction: - """Claim outputs. - """ - return PreparedTransaction(self, self._call_account_method( - 'prepareClaimOutputs', { - 'outputIdsToClaim': output_ids_to_claim - } - )) - - def send_outputs( - self, outputs: List[Output], options: Optional[TransactionOptions] = None) -> TransactionWithMetadata: - """Send outputs in a transaction. - """ - return TransactionWithMetadata.from_dict(self._call_account_method( - 'sendOutputs', { - 'outputs': outputs, - 'options': options, - } - )) diff --git a/bindings/python/iota_sdk/wallet/prepared_transaction.py b/bindings/python/iota_sdk/wallet/prepared_transaction.py index 90eb7f835e..fb9c37f48b 100644 --- a/bindings/python/iota_sdk/wallet/prepared_transaction.py +++ b/bindings/python/iota_sdk/wallet/prepared_transaction.py @@ -11,25 +11,25 @@ # Required to prevent circular import if TYPE_CHECKING: - from iota_sdk.wallet.wallet import Account + from iota_sdk.wallet.wallet import Wallet class PreparedTransaction: """A helper class for offline signing. Attributes: - account: An account object used to continue building this transaction. + wallet: A wallet object used to continue building this transaction. prepared_transaction_data_dto: A prepared transaction data object. """ def __init__( self, - account: Account, + wallet: Wallet, prepared_transaction_data: Union[PreparedTransactionData, Dict] ): """Initialize `Self`. """ - self.account = account + self.wallet = wallet self.prepared_transaction_data_dto = prepared_transaction_data def prepared_transaction_data(self) -> PreparedTransactionData: @@ -51,10 +51,10 @@ def send(self) -> TransactionWithMetadata: return self.sign_and_submit_transaction() def sign(self): - """Sign a prepared transaction using the account's private key and returns + """Sign a prepared transaction using the wallet's private key and returns the signed transaction. """ - return self.account.sign_transaction( + return self.wallet.sign_transaction( self.prepared_transaction_data()) def sign_and_submit_transaction(self) -> TransactionWithMetadata: @@ -63,7 +63,7 @@ def sign_and_submit_transaction(self) -> TransactionWithMetadata: Returns: The transaction after it has been signed and submitted. """ - return self.account.sign_and_submit_transaction( + return self.wallet.sign_and_submit_transaction( self.prepared_transaction_data()) diff --git a/bindings/python/iota_sdk/wallet/sync_options.py b/bindings/python/iota_sdk/wallet/sync_options.py index 4e4526ebe7..53be5ea9df 100644 --- a/bindings/python/iota_sdk/wallet/sync_options.py +++ b/bindings/python/iota_sdk/wallet/sync_options.py @@ -8,8 +8,8 @@ @json @dataclass -class AccountSyncOptions(): - """Sync options for addresses from the account. +class WalletSyncOptions(): + """Specifies what outputs should be synced for the ed25519 address from the wallet. Attributes: basic_outputs: Whether to sync basic outputs. @@ -24,8 +24,8 @@ class AccountSyncOptions(): @json @dataclass -class AliasSyncOptions(): - """Sync options for addresses from account outputs. +class AccountSyncOptions(): + """Specifies what outputs should be synced for the address of an account output. Attributes: basic_outputs: Whether to sync basic outputs. @@ -43,7 +43,7 @@ class AliasSyncOptions(): @json @dataclass class NftSyncOptions(): - """Sync options for addresses from NFT outputs. + """Specifies what outputs should be synced for the address of an nft output. Attributes: basic_outputs: Whether to sync basic outputs. @@ -73,25 +73,26 @@ class SyncOptions(): sync_pending_transactions : Checks pending transactions and reissues them if necessary. account : - Specifies what outputs should be synced for the Ed25519 addresses from the account. - alias : + Specifies what outputs should be synced for the address of an account output. + wallet : Specifies what outputs should be synced for the address of an account output. nft : Specifies what outputs should be synced for the address of an nft output. sync_only_most_basic_outputs : Specifies if only basic outputs with just an address unlock condition should be synced. - This will overwrite the `account`, `alias` and `nft` options. + This will overwrite the `wallet`, `alias` and `nft` options. sync_native_token_foundries : Sync native token foundries, so their metadata can be returned in the balance. + sync_implicit_accounts : + Sync implicit accounts. """ force_syncing: Optional[bool] = None sync_incoming_transactions: Optional[bool] = None sync_pending_transactions: Optional[bool] = None account: Optional[AccountSyncOptions] = None - # TODO Rename when we are done with Account changes - # https://github.com/iotaledger/iota-sdk/issues/647. - alias: Optional[AliasSyncOptions] = None + wallet: Optional[WalletSyncOptions] = None nft: Optional[NftSyncOptions] = None sync_only_most_basic_outputs: Optional[bool] = None sync_native_token_foundries: Optional[bool] = None + sync_implicit_accounts: Optional[bool] = None diff --git a/bindings/python/iota_sdk/wallet/wallet.py b/bindings/python/iota_sdk/wallet/wallet.py index ecaa2bf335..eb57979fa0 100644 --- a/bindings/python/iota_sdk/wallet/wallet.py +++ b/bindings/python/iota_sdk/wallet/wallet.py @@ -2,13 +2,45 @@ # SPDX-License-Identifier: Apache-2.0 from json import dumps -from typing import Any, Dict, List, Optional, Union +from typing import List, Optional, Union +from dataclasses import dataclass from iota_sdk import destroy_wallet, create_wallet, listen_wallet, get_client_from_wallet, get_secret_manager_from_wallet, Client from iota_sdk.secret_manager.secret_manager import LedgerNanoSecretManager, MnemonicSecretManager, StrongholdSecretManager, SeedSecretManager, SecretManager -from iota_sdk.types.address import AccountAddress -from iota_sdk.wallet.account import Account, _call_method_routine + +from iota_sdk.wallet.common import _call_method_routine +from iota_sdk.wallet.prepared_transaction import PreparedTransaction, PreparedCreateTokenTransaction from iota_sdk.wallet.sync_options import SyncOptions +from iota_sdk.types.balance import Balance +from iota_sdk.types.burn import Burn +from iota_sdk.types.common import HexStr, json +from iota_sdk.types.client_options import ClientOptions +from iota_sdk.types.filter_options import FilterOptions +from iota_sdk.types.native_token import NativeToken +from iota_sdk.types.output_data import OutputData +from iota_sdk.types.output_id import OutputId +from iota_sdk.types.output import BasicOutput, NftOutput, Output, deserialize_output +from iota_sdk.types.output_params import OutputParams +from iota_sdk.types.transaction_data import PreparedTransactionData, SignedTransactionData +from iota_sdk.types.send_params import CreateAccountOutputParams, CreateNativeTokenParams, MintNftParams, SendNativeTokenParams, SendNftParams, SendParams +from iota_sdk.types.signature import Bip44 +from iota_sdk.types.transaction_with_metadata import TransactionWithMetadata +from iota_sdk.types.transaction_options import TransactionOptions +from iota_sdk.types.consolidation_params import ConsolidationParams + + +@json +@dataclass +class WalletOptions: + """Options for the Wallet builder.""" + address: Optional[str] = None + alias: Optional[str] = None + bip_path: Optional[Bip44] = None + client_options: Optional[ClientOptions] = None + secret_manager: Optional[Union[LedgerNanoSecretManager, + MnemonicSecretManager, SeedSecretManager, StrongholdSecretManager]] = None + storage_path: Optional[str] = None + # pylint: disable=too-many-public-methods @@ -20,74 +52,17 @@ class Wallet(): handle: The wallet handle. """ - def __init__(self, - storage_path: Optional[str] = None, - client_options: Optional[Dict[str, Any]] = None, - coin_type: Optional[int] = None, - secret_manager: Optional[Union[LedgerNanoSecretManager, MnemonicSecretManager, SeedSecretManager, StrongholdSecretManager]] = None): + def __init__(self, options: WalletOptions): """Initialize `self`. """ - - # Setup the options - options: Dict[str, Any] = {'storagePath': storage_path} - if client_options: - options['clientOptions'] = client_options.to_dict() - if coin_type: - options['coinType'] = coin_type - if secret_manager: - options['secretManager'] = secret_manager - - options_str: str = dumps(options) - # Create the message handler - self.handle = create_wallet(options_str) + self.handle = create_wallet(dumps(options.to_dict())) def get_handle(self): """Return the wallet handle. """ return self.handle - def create_account(self, alias: Optional[str] = None, bech32_hrp: Optional[str] - = None, addresses: Optional[AccountAddress] = None) -> Account: - """Create a new account. - - Args: - alias: The alias of the new account. - bech32_hrp: The Bech32 HRP of the new account. - - Returns: - An account object. - """ - account_data = self._call_method( - 'createAccount', { - 'alias': self.__return_str_or_none(alias), - 'bech32Hrp': self.__return_str_or_none(bech32_hrp), - 'addresses': addresses, - } - ) - return Account(account_data, self.handle) - - def get_account(self, account_id: Union[str, int]) -> Account: - """Get the account associated with the given account ID or index. - """ - account_data = self._call_method( - 'getAccount', { - 'accountId': account_id, - } - ) - return Account(account_data, self.handle) - - def get_client(self): - """Get the client associated with the wallet. - """ - return Client(client_handle=get_client_from_wallet(self.handle)) - - def get_secret_manager(self): - """Get the secret manager associated with the wallet. - """ - return SecretManager( - secret_manager_handle=get_secret_manager_from_wallet(self.handle)) - @_call_method_routine def _call_method(self, name: str, data=None): message = { @@ -97,24 +72,6 @@ def _call_method(self, name: str, data=None): message['data'] = data return message - def get_account_data(self, account_id: Union[str, int]): - """Get account data associated with the given account ID or index. - """ - return self._call_method( - 'getAccount', { - 'accountId': account_id - } - ) - - def get_accounts(self): - """Get all accounts. - """ - accounts_data = self._call_method( - 'getAccounts', - ) - return [Account(account_data, self.handle) - for account_data in accounts_data] - def backup(self, destination: str, password: str): """Backup storage. """ @@ -149,30 +106,53 @@ def is_stronghold_password_available(self) -> bool: 'isStrongholdPasswordAvailable' ) - def recover_accounts(self, account_start_index: int, account_gap_limit: int, - address_gap_limit: int, sync_options: Optional[SyncOptions] = None): - """Recover accounts. + def destroy(self): + """Destroys the wallet instance. + """ + return destroy_wallet(self.handle) + + def emit_test_event(self, event) -> bool: + """Helper function to test events. """ return self._call_method( - 'recoverAccounts', { - 'accountStartIndex': account_start_index, - 'accountGapLimit': account_gap_limit, - 'addressGapLimit': address_gap_limit, - 'syncOptions': sync_options - } + 'emitTestEvent', { + 'event': event, + }, ) - def remove_latest_account(self): - """Remove latest account. + def get_client(self): + """Get the client associated with the wallet. + """ + return Client(client_handle=get_client_from_wallet(self.handle)) + + def get_secret_manager(self): + """Get the secret manager associated with the wallet. + """ + return SecretManager( + secret_manager_handle=get_secret_manager_from_wallet(self.handle)) + + def listen(self, handler, events: Optional[List[int]] = None): + """Listen to wallet events, empty array or None will listen to all events. + The default value for events is None. """ + events_array = [] if events is None else events + listen_wallet(self.handle, events_array, handler) + + def clear_listeners(self, events: Optional[List[int]] = None): + """Remove wallet event listeners, empty array or None will remove all listeners. + The default value for events is None. + """ + events_array = [] if events is None else events return self._call_method( - 'removeLatestAccount' + 'clearListeners', { + 'eventTypes': events_array + } ) def restore_backup(self, source: str, password: str): """Restore a backup from a Stronghold file. - Replaces `client_options`, `coin_type`, `secret_manager` and accounts. - Returns an error if accounts were already created. If Stronghold is used + Replaces `client_options`, `coin_type`, `secret_manager` and wallet. + Returns an error if the wallet was already created. If Stronghold is used as the secret_manager, the existing Stronghold file will be overwritten. Be aware that if a mnemonic was stored, it will be lost. """ @@ -184,7 +164,7 @@ def restore_backup(self, source: str, password: str): ) def set_client_options(self, client_options): - """Update the client options for all accounts. + """Update the options of the wallet client. """ return self._call_method( 'setClientOptions', @@ -193,20 +173,6 @@ def set_client_options(self, client_options): } ) - def generate_ed25519_address(self, account_index: int, internal: bool, address_index: int, - options=None, bech32_hrp: Optional[str] = None) -> List[str]: - """Generate an address without storing it. - """ - return self._call_method( - 'generateEd25519Address', { - 'accountIndex': account_index, - 'internal': internal, - 'addressIndex': address_index, - 'options': options, - 'bech32Hrp': bech32_hrp - } - ) - def set_stronghold_password(self, password: str): """Set stronghold password. """ @@ -227,6 +193,24 @@ def set_stronghold_password_clear_interval( } ) + def start_background_sync( + self, options: Optional[SyncOptions] = None, interval_in_milliseconds: Optional[int] = None): + """Start background syncing. + """ + return self._call_method( + 'startBackgroundSync', { + 'options': options, + 'intervalInMilliseconds': interval_in_milliseconds + } + ) + + def stop_background_sync(self): + """Stop background syncing. + """ + return self._call_method( + 'stopBackgroundSync', + ) + def store_mnemonic(self, mnemonic: str): """Store mnemonic. """ @@ -234,53 +218,550 @@ def store_mnemonic(self, mnemonic: str): 'storeMnemonic', { 'mnemonic': mnemonic } + ) + def update_node_auth(self, url: str, auth=None): + """Update the authentication for the provided node. + """ + return self._call_method( + 'updateNodeAuth', { + 'url': url, + 'auth': auth + } ) - def start_background_sync( - self, options: Optional[SyncOptions] = None, interval_in_milliseconds: Optional[int] = None): - """Start background syncing. + def accounts(self) -> List[OutputData]: + """Returns the accounts of the wallet. + """ + outputs = self._call_method( + 'accounts' + ) + return [OutputData.from_dict(o) for o in outputs] + + def burn( + self, burn: Burn, options: Optional[TransactionOptions] = None) -> TransactionWithMetadata: + """A generic function that can be used to burn native tokens, nfts, foundries and aliases. + """ + return self.prepare_burn(burn, options).send() + + def prepare_burn( + self, burn: Burn, options: Optional[TransactionOptions] = None) -> PreparedTransaction: + """A generic `prepare_burn()` function that can be used to prepare the burn of native tokens, nfts, foundries and accounts. + """ + prepared = self._call_method( + 'prepareBurn', { + 'burn': burn.to_dict(), + 'options': options + }, + ) + return PreparedTransaction(self, prepared) + + def prepare_burn_native_token(self, + token_id: HexStr, + burn_amount: int, + options: Optional[TransactionOptions] = None) -> PreparedTransaction: + """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. + """ + prepared = self._call_method( + 'prepareBurn', { + 'burn': Burn().add_native_token(NativeToken(token_id, hex(burn_amount))).to_dict(), + 'options': options + }, + ) + return PreparedTransaction(self, prepared) + + def prepare_burn_nft(self, + nft_id: HexStr, + options: Optional[TransactionOptions] = None) -> PreparedTransaction: + """Burn an nft output. + """ + prepared = self._call_method( + 'prepareBurn', { + 'burn': Burn().add_nft(nft_id).to_dict(), + 'options': options + }, + ) + return PreparedTransaction(self, prepared) + + def claim_outputs( + self, output_ids_to_claim: List[OutputId]) -> TransactionWithMetadata: + """Claim outputs. + """ + return self.prepare_claim_outputs(output_ids_to_claim).send() + + def prepare_claim_outputs( + self, output_ids_to_claim: List[OutputId]) -> PreparedTransaction: + """Claim outputs. + """ + return PreparedTransaction(self, self._call_method( + 'prepareClaimOutputs', { + 'outputIdsToClaim': output_ids_to_claim + } + )) + + def consolidate_outputs( + self, params: ConsolidationParams) -> TransactionWithMetadata: + """Consolidate outputs. + """ + return self.prepare_consolidate_outputs(params).send() + + def prepare_consolidate_outputs( + self, params: ConsolidationParams) -> PreparedTransaction: + """Consolidate outputs. + """ + prepared = self._call_method( + 'prepareConsolidateOutputs', { + 'params': params + } + ) + return PreparedTransaction(self, prepared) + + def create_account_output(self, + params: Optional[CreateAccountOutputParams] = None, + options: Optional[TransactionOptions] = None) -> TransactionWithMetadata: + """Create an account output. + """ + return self.prepare_create_account_output(params, options).send() + + def prepare_create_account_output(self, + params: Optional[CreateAccountOutputParams] = None, + options: Optional[TransactionOptions] = None) -> PreparedTransaction: + """Create an account output. + """ + prepared = self._call_method( + 'prepareCreateAccountOutput', { + 'params': params, + 'options': options + } + ) + return PreparedTransaction(self, prepared) + + def melt_native_token(self, + token_id: HexStr, + melt_amount: int, + options: Optional[TransactionOptions] = None) -> TransactionWithMetadata: + """Melt native tokens. This happens with the foundry output which minted them, by increasing it's + `melted_tokens` field. + """ + return self.prepare_melt_native_token( + token_id, melt_amount, options).send() + + def prepare_melt_native_token(self, + token_id: HexStr, + melt_amount: int, + options: Optional[TransactionOptions] = None) -> PreparedTransaction: + """Melt native tokens. This happens with the foundry output which minted them, by increasing it's + `melted_tokens` field. + """ + prepared = self._call_method( + 'prepareMeltNativeToken', { + 'tokenId': token_id, + 'meltAmount': hex(melt_amount), + 'options': options + } + ) + return PreparedTransaction(self, prepared) + + def prepare_destroy_account(self, + account_id: HexStr, + options: Optional[TransactionOptions] = None) -> PreparedTransaction: + """Destroy an account output. + """ + prepared = self._call_method( + 'prepareBurn', { + 'burn': Burn().add_account(account_id).to_dict(), + 'options': options + }, + ) + return PreparedTransaction(self, prepared) + + def prepare_destroy_foundry(self, + foundry_id: HexStr, + options: Optional[TransactionOptions] = None) -> PreparedTransaction: + """Destroy a foundry output with a circulating supply of 0. + """ + prepared = self._call_method( + 'prepareBurn', { + 'burn': Burn().add_foundry(foundry_id).to_dict(), + 'options': options + }, + ) + return PreparedTransaction(self, prepared) + + def get_balance(self) -> Balance: + """Get wallet balance information. + """ + return Balance.from_dict(self._call_method( + 'getBalance' + )) + + def get_output(self, output_id: OutputId) -> OutputData: + """Get output. + """ + return OutputData.from_dict(self._call_method( + 'getOutput', { + 'outputId': output_id + } + )) + + def get_foundry_output(self, token_id: HexStr): + """Get a `FoundryOutput` by native token ID. It will try to get the foundry from the wallet, if it isn't in the wallet it will try to get it from the node. """ return self._call_method( - 'startBackgroundSync', { - 'options': options, - 'intervalInMilliseconds': interval_in_milliseconds + 'getFoundryOutput', { + 'tokenId': token_id } ) - def stop_background_sync(self): - """Stop background syncing. + def claimable_outputs(self, outputs_to_claim: List[OutputId]): + """Get outputs with additional unlock conditions. """ return self._call_method( - 'stopBackgroundSync', + 'claimableOutputs', { + 'outputsToClaim': outputs_to_claim + } ) - def listen(self, handler, events: Optional[List[int]] = None): - """Listen to wallet events, empty array or None will listen to all events. - The default value for events is None. + def get_transaction( + self, transaction_id: HexStr) -> TransactionWithMetadata: + """Get transaction. """ - events_array = [] if events is None else events - listen_wallet(self.handle, events_array, handler) + return TransactionWithMetadata.from_dict(self._call_method( + 'getTransaction', { + 'transactionId': transaction_id + } + )) - def clear_listeners(self, events: Optional[List[int]] = None): - """Remove wallet event listeners, empty array or None will remove all listeners. - The default value for events is None. + def address(self) -> str: + """Get the address of the wallet. """ - events_array = [] if events is None else events return self._call_method( - 'clearListeners', { - 'eventTypes': events_array + 'getAddress' + ) + + def outputs( + self, filter_options: Optional[FilterOptions] = None) -> List[OutputData]: + """Returns all outputs of the wallet. + """ + outputs = self._call_method( + 'outputs', { + 'filterOptions': filter_options } ) + return [OutputData.from_dict(o) for o in outputs] - def destroy(self): - """Destroys the wallet instance. + def pending_transactions(self): + """Returns all pending transactions of the wallet. """ - return destroy_wallet(self.handle) + transactions = self._call_method( + 'pendingTransactions' + ) + return [TransactionWithMetadata.from_dict(tx) for tx in transactions] + + def implicit_account_creation_address(self) -> str: + """Returns the implicit account creation address of the wallet if it is Ed25519 based. + """ + return self._call_method( + 'implicitAccountCreationAddress' + ) + + def implicit_account_transition( + self, output_id: OutputId) -> TransactionWithMetadata: + """Transitions an implicit account to an account. + """ + return self.prepare_implicit_account_transition(output_id).send() + + def prepare_implicit_account_transition( + self, output_id: OutputId) -> PreparedTransaction: + """Prepares to transition an implicit account to an account. + """ + prepared = self._call_method( + 'implicitAccountTransition', { + 'outputId': output_id + } + ) + return PreparedTransaction(self, prepared) + + def implicit_accounts(self) -> List[OutputData]: + """Returns the implicit accounts of the wallet. + """ + outputs = self._call_method( + 'implicitAccounts' + ) + return [OutputData.from_dict(o) for o in outputs] + + def incoming_transactions(self) -> List[TransactionWithMetadata]: + """Returns all incoming transactions of the wallet. + """ + transactions = self._call_method( + 'incomingTransactions' + ) + return [TransactionWithMetadata.from_dict(tx) for tx in transactions] + + def transactions(self) -> List[TransactionWithMetadata]: + """Returns all transaction of the wallet. + """ + transactions = self._call_method( + 'transactions' + ) + return [TransactionWithMetadata.from_dict(tx) for tx in transactions] + + def unspent_outputs( + self, filter_options: Optional[FilterOptions] = None) -> List[OutputData]: + """Returns all unspent outputs of the wallet. + """ + outputs = self._call_method( + 'unspentOutputs', { + 'filterOptions': filter_options + } + ) + return [OutputData.from_dict(o) for o in outputs] + + def mint_native_token(self, token_id: HexStr, mint_amount: int, + options: Optional[TransactionOptions] = None) -> TransactionWithMetadata: + """Mint additional native tokens. + """ + return self.prepare_mint_native_token( + token_id, mint_amount, options).send() + + def prepare_mint_native_token(self, token_id: HexStr, mint_amount: int, + options: Optional[TransactionOptions] = None) -> PreparedTransaction: + """Mint additional native tokens. + """ + prepared = self._call_method( + 'prepareMintNativeToken', { + 'tokenId': token_id, + 'mintAmount': hex(mint_amount), + 'options': options + } + ) + return PreparedTransaction(self, prepared) + + def create_native_token(self, params: CreateNativeTokenParams, + options: Optional[TransactionOptions] = None) -> TransactionWithMetadata: + """Create native token. + """ + return self.prepare_create_native_token(params, options).send() + + def prepare_create_native_token(self, params: CreateNativeTokenParams, + options: Optional[TransactionOptions] = None) -> PreparedTransaction: + """Create native token. + """ + prepared = self._call_method( + 'prepareCreateNativeToken', { + 'params': params, + 'options': options + } + ) + return PreparedCreateTokenTransaction(self, prepared) - # pylint: disable=redefined-builtin - @staticmethod - def __return_str_or_none(opt_str): - if opt_str: - return opt_str - return None + def mint_nfts(self, params: List[MintNftParams], + options: Optional[TransactionOptions] = None) -> TransactionWithMetadata: + """Mint NFTs. + """ + return self.prepare_mint_nfts(params, options).send() + + def prepare_mint_nfts(self, params: List[MintNftParams], + options: Optional[TransactionOptions] = None) -> PreparedTransaction: + """Mint NFTs. + """ + prepared = self._call_method( + 'prepareMintNfts', { + 'params': params, + 'options': options + } + ) + return PreparedTransaction(self, prepared) + + def prepare_output(self, params: OutputParams, + transaction_options: Optional[TransactionOptions] = None) -> Union[BasicOutput, NftOutput]: + """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 + minimum required storage deposit will be sent to the recipient. + When the assets contain an nft_id, the data from the existing nft output will be used, just with the address + unlock conditions replaced + """ + return deserialize_output(self._call_method( + 'prepareOutput', { + 'params': params, + 'transactionOptions': transaction_options + }) + ) + + def prepare_send(self, params: List[SendParams], + options: Optional[TransactionOptions] = None) -> PreparedTransaction: + """Prepare to send base coins. + """ + prepared = self._call_method( + 'prepareSend', { + 'params': params, + 'options': options + } + ) + return PreparedTransaction(self, prepared) + + def send_transaction( + self, outputs: List[Output], options: Optional[TransactionOptions] = None) -> TransactionWithMetadata: + """Send a transaction. + """ + return self.prepare_transaction(outputs, options).send() + + def prepare_transaction( + self, outputs: List[Output], options: Optional[TransactionOptions] = None) -> PreparedTransaction: + """Prepare transaction. + """ + prepared = self._call_method( + 'prepareTransaction', { + 'outputs': outputs, + 'options': options + } + ) + return PreparedTransaction(self, prepared) + + def reissue_transaction_until_included( + self, transaction_id: HexStr, interval=None, max_attempts=None) -> HexStr: + """Reissues a transaction sent from the wallet for a provided transaction id until it's + included (referenced by a milestone). Returns the included block id. + """ + return self._call_method( + 'reissueTransactionUntilIncluded', { + 'transactionId': transaction_id, + 'interval': interval, + 'maxAttempts': max_attempts + } + ) + + def send(self, amount: int, address: str, + options: Optional[TransactionOptions] = None) -> TransactionWithMetadata: + """Send base coins. + """ + return TransactionWithMetadata.from_dict(self._call_method( + 'send', { + 'amount': str(amount), + 'address': address, + 'options': options + } + )) + + def send_with_params( + self, params: List[SendParams], options: Optional[TransactionOptions] = None) -> TransactionWithMetadata: + """Send base coins to multiple addresses or with additional parameters. + """ + return TransactionWithMetadata.from_dict(self._call_method( + 'sendWithParams', { + 'params': [param.to_dict() for param in params], + 'options': options + } + )) + + def send_native_tokens( + self, params: List[SendNativeTokenParams], options: Optional[TransactionOptions] = None) -> TransactionWithMetadata: + """Send native tokens. + """ + return self.prepare_send_native_tokens(params, options).send() + + def prepare_send_native_tokens( + self, + params: List[SendNativeTokenParams], + options: Optional[TransactionOptions] = None) -> PreparedTransaction: + """Send native tokens. + """ + prepared = self._call_method( + 'prepareSendNativeTokens', { + 'params': params, + 'options': options + } + ) + return PreparedTransaction(self, prepared) + + def send_nft(self, params: List[SendNftParams], + options: Optional[TransactionOptions] = None) -> TransactionWithMetadata: + """Send nft. + """ + return self.prepare_send_nft(params, options).send() + + def prepare_send_nft(self, params: List[SendNftParams], + options: Optional[TransactionOptions] = None) -> PreparedTransaction: + """Send nft. + """ + prepared = self._call_method( + 'prepareSendNft', { + 'params': params, + 'options': options + } + ) + return PreparedTransaction(self, prepared) + + def send_outputs( + self, outputs: List[Output], options: Optional[TransactionOptions] = None) -> TransactionWithMetadata: + """Send outputs in a transaction. + """ + return TransactionWithMetadata.from_dict(self._call_method( + 'sendOutputs', { + 'outputs': outputs, + 'options': options, + } + )) + + def set_alias(self, alias: str): + """Set alias. + """ + return self._call_method( + 'setAlias', { + 'alias': alias + } + ) + + def set_default_sync_options(self, options: SyncOptions): + """Set the fallback SyncOptions for wallet syncing. + If storage is enabled, will persist during restarts. + """ + return self._call_method( + 'setDefaultSyncOptions', { + 'options': options + } + ) + + def sign_transaction( + self, prepared_transaction_data: PreparedTransactionData) -> SignedTransactionData: + """Sign a transaction. + """ + return SignedTransactionData.from_dict(self._call_method( + 'signTransaction', { + 'preparedTransactionData': prepared_transaction_data + } + )) + + def sign_and_submit_transaction( + self, prepared_transaction_data: PreparedTransactionData) -> TransactionWithMetadata: + """Validate the transaction, sign it, submit it to a node and store it in the wallet. + """ + return TransactionWithMetadata.from_dict(self._call_method( + 'signAndSubmitTransaction', { + 'preparedTransactionData': prepared_transaction_data + } + )) + + def submit_and_store_transaction( + self, signed_transaction_data: SignedTransactionData) -> TransactionWithMetadata: + """Submit and store transaction. + """ + return TransactionWithMetadata.from_dict(self._call_method( + 'submitAndStoreTransaction', { + 'signedTransactionData': signed_transaction_data + } + )) + + def sync(self, options: Optional[SyncOptions] = None) -> Balance: + """Sync the wallet by fetching new information from the nodes. + Will also reissue pending transactions and consolidate outputs if necessary. + A custom default can be set using set_default_sync_options. + """ + return Balance.from_dict(self._call_method( + 'sync', { + 'options': options, + } + )) diff --git a/bindings/python/tests/address_generation_test.py b/bindings/python/tests/address_generation_test.py index 8aa145bf67..b335cc0231 100644 --- a/bindings/python/tests/address_generation_test.py +++ b/bindings/python/tests/address_generation_test.py @@ -3,10 +3,9 @@ import shutil import pytest -from iota_sdk import Wallet, MnemonicSecretManager, CoinType, ClientOptions +from iota_sdk import Wallet, MnemonicSecretManager, CoinType, ClientOptions, WalletOptions, Bip44 -@pytest.mark.skip(reason="https://github.com/iotaledger/iota-sdk/issues/1387") def test_address_generation_iota(): db_path = './test_address_generation_iota' shutil.rmtree(db_path, ignore_errors=True) @@ -16,15 +15,21 @@ def test_address_generation_iota(): secret_manager = MnemonicSecretManager( "acoustic trophy damage hint search taste love bicycle foster cradle brown govern endless depend situate athlete pudding blame question genius transfer van random vast") - wallet = Wallet(db_path, - client_options, CoinType.IOTA, secret_manager) - - account = wallet.create_account('Alice') - - addresses = account.addresses() - - assert 'smr1qpg2xkj66wwgn8p2ggnp7p582gj8g6p79us5hve2tsudzpsr2ap4sp36wye' == addresses[ - 0].address + bip_path = Bip44( + coin_type=CoinType.IOTA + ) + wallet_options = WalletOptions( + None, + None, + bip_path, + client_options, + secret_manager, + db_path) + wallet = Wallet(wallet_options) + + address = wallet.address() + + assert 'smr1qpg2xkj66wwgn8p2ggnp7p582gj8g6p79us5hve2tsudzpsr2ap4sp36wye' == address shutil.rmtree(db_path, ignore_errors=True) @@ -38,15 +43,19 @@ def test_address_generation_shimmer(): secret_manager = MnemonicSecretManager( "acoustic trophy damage hint search taste love bicycle foster cradle brown govern endless depend situate athlete pudding blame question genius transfer van random vast") - wallet = Wallet(db_path, - client_options, CoinType.SHIMMER, secret_manager) - - wallet.create_account('Alice') - - account = wallet.get_account('Alice') - - addresses = account.addresses() - - assert 'smr1qzev36lk0gzld0k28fd2fauz26qqzh4hd4cwymlqlv96x7phjxcw6ckj80y' == addresses[ - 0].address + bip_path = Bip44( + coin_type=CoinType.IOTA + ) + wallet_options = WalletOptions( + None, + None, + bip_path, + client_options, + secret_manager, + db_path) + wallet = Wallet(wallet_options) + + address = wallet.address() + + assert 'smr1qzev36lk0gzld0k28fd2fauz26qqzh4hd4cwymlqlv96x7phjxcw6ckj80y' == address shutil.rmtree(db_path, ignore_errors=True) diff --git a/bindings/python/tests/test_block.py b/bindings/python/tests/test_block.py index b1d10b0af8..87c751bc44 100644 --- a/bindings/python/tests/test_block.py +++ b/bindings/python/tests/test_block.py @@ -32,13 +32,15 @@ def test_basic_block_with_tagged_data_payload(): def test_block_with_tagged_data_payload(): block_dict = { - "protocolVersion": 3, - "networkId": "10549460113735494767", - "issuingTime": "1675563954966263210", - "slotCommitmentId": "0x498bf08a5ed287bc87340341ffab28706768cd3a7035ae5e33932d9a12bb30940000000000000000", - "latestFinalizedSlot": 21, - "issuerId": "0x3370746f30705b7d0b42597459714d45241e5a64761b09627c447b751c7e145c", - "block": { + "header": { + "protocolVersion": 3, + "networkId": "10549460113735494767", + "issuingTime": "1675563954966263210", + "slotCommitmentId": "0x498bf08a5ed287bc87340341ffab28706768cd3a7035ae5e33932d9a12bb30940000000000000000", + "latestFinalizedSlot": 21, + "issuerId": "0x3370746f30705b7d0b42597459714d45241e5a64761b09627c447b751c7e145c", + }, + "body": { "type": 0, "strongParents": [ "0x304442486c7a05361408585e4b5f7a67441c437528755a70041e0e557a6d4b2d7d4362083d492b57", diff --git a/bindings/python/tests/test_offline.py b/bindings/python/tests/test_offline.py index 55c95e24f0..e827558113 100644 --- a/bindings/python/tests/test_offline.py +++ b/bindings/python/tests/test_offline.py @@ -3,7 +3,6 @@ import json import unittest -import pytest from iota_sdk import Client, MnemonicSecretManager, Utils, SecretManager, OutputId, hex_to_utf8, utf8_to_hex, Bip44, CoinType, Irc27Metadata, Irc30Metadata @@ -33,17 +32,19 @@ def test_mnemonic_address_generation(): assert test['bech32_address'] == generated_address[0] -@pytest.mark.skip(reason="https://github.com/iotaledger/iota-sdk/issues/1387") def test_sign_verify_ed25519(): secret_manager = MnemonicSecretManager( "acoustic trophy damage hint search taste love bicycle foster cradle brown govern endless depend situate athlete pudding blame question genius transfer van random vast") message = utf8_to_hex('IOTA') + bip_path = Bip44( + coin_type=CoinType.IOTA + ) + secret_manager = SecretManager(secret_manager) signature = secret_manager.sign_ed25519( message, - # IOTA coin type - Bip44(CoinType.IOTA), + bip_path, ) assert signature.signature == '0x72bf2bc8fbc5dc56d657d7de8afa5208be1db025851e81031c754b371c7a29ce9f352d12df8207f9163316f81f59eb7725e5c0e4f3228e71ffe3764a9de6b10e' diff --git a/bindings/python/tests/test_output.py b/bindings/python/tests/test_output.py index dabdffb0c9..848364011e 100644 --- a/bindings/python/tests/test_output.py +++ b/bindings/python/tests/test_output.py @@ -46,8 +46,9 @@ def test_output(): "type": 0, "mana": "57600", "amount": "57600", - "nativeTokens": [ + "features": [ { + "type": 5, "id": "0x086326539ce1b78eb606a75950f31698ddcb51200b4ee6e870050e6ef658cd3bab0100000000", "amount": "0x32" } @@ -85,8 +86,9 @@ def test_output(): "type": 0, "mana": "50100", "amount": "50100", - "nativeTokens": [ + "features": [ { + "type": 5, "id": "0x087f3221adb3be9ef74a69595ef282b4ca47fd98b6bf1142e7d8f9f7b265efeedc0100000000", "amount": "0x1" } diff --git a/bindings/python/tests/test_wallet_destroy.py b/bindings/python/tests/test_wallet_destroy.py index 92d7651749..1e7e7ac712 100644 --- a/bindings/python/tests/test_wallet_destroy.py +++ b/bindings/python/tests/test_wallet_destroy.py @@ -4,7 +4,7 @@ import shutil import unittest import pytest -from iota_sdk import Wallet, MnemonicSecretManager, CoinType, ClientOptions, WalletError +from iota_sdk import Wallet, MnemonicSecretManager, CoinType, ClientOptions, WalletOptions, WalletError, Bip44 class WalletDestroy(unittest.TestCase): @@ -18,27 +18,29 @@ def test_wallet_destroy(self): secret_manager = MnemonicSecretManager( "acoustic trophy damage hint search taste love bicycle foster cradle brown govern endless depend situate athlete pudding blame question genius transfer van random vast") - wallet = Wallet(db_path, - client_options, CoinType.IOTA, secret_manager) - - account = wallet.create_account('Alice') - - addresses = account.addresses() - assert 'smr1qpg2xkj66wwgn8p2ggnp7p582gj8g6p79us5hve2tsudzpsr2ap4sp36wye' == addresses[ - 0].address + bip_path = Bip44( + coin_type=CoinType.SHIMMER + ) + wallet_options = WalletOptions( + None, + None, + bip_path, + client_options, + secret_manager, + db_path) + wallet = Wallet(wallet_options) + + address = wallet.address() + assert 'smr1qzev36lk0gzld0k28fd2fauz26qqzh4hd4cwymlqlv96x7phjxcw6ckj80y' == address # Destroy the wallet wallet.destroy() # Afterwards destroying we can recreate the wallet again - wallet = Wallet(db_path, - client_options, CoinType.IOTA, secret_manager) + wallet = Wallet(wallet_options) - account = wallet.get_account('Alice') - - addresses = account.addresses() - assert 'smr1qpg2xkj66wwgn8p2ggnp7p582gj8g6p79us5hve2tsudzpsr2ap4sp36wye' == addresses[ - 0].address + address = wallet.address() + assert 'smr1qzev36lk0gzld0k28fd2fauz26qqzh4hd4cwymlqlv96x7phjxcw6ckj80y' == address shutil.rmtree(db_path, ignore_errors=True) def test_wallet_destroy_error(self): @@ -46,17 +48,25 @@ def test_wallet_destroy_error(self): shutil.rmtree(db_path, ignore_errors=True) client_options = ClientOptions(nodes=[]) - secret_manager = MnemonicSecretManager( "acoustic trophy damage hint search taste love bicycle foster cradle brown govern endless depend situate athlete pudding blame question genius transfer van random vast") - wallet = Wallet(db_path, - client_options, CoinType.IOTA, secret_manager) + bip_path = Bip44( + coin_type=CoinType.SHIMMER + ) + wallet_options = WalletOptions( + None, + None, + bip_path, + client_options, + secret_manager, + db_path) + wallet = Wallet(wallet_options) # Destroy the wallet wallet.destroy() with self.assertRaises(WalletError): - wallet.create_account('Alice') + wallet.address() shutil.rmtree(db_path, ignore_errors=True)