diff --git a/bindings/nodejs/examples/exchange/0-generate-mnemonic.ts b/bindings/nodejs/examples/exchange/0-generate-mnemonic.ts index 8ec9485a54..2a19cf623a 100644 --- a/bindings/nodejs/examples/exchange/0-generate-mnemonic.ts +++ b/bindings/nodejs/examples/exchange/0-generate-mnemonic.ts @@ -1,12 +1,12 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { Utils } from '@iota/sdk'; - +// This example generates a new random mnemonic. // Run with command: // yarn run-example ./exchange/0-generate-mnemonic.ts -// This example generates a new random mnemonic. +import { Utils } from '@iota/sdk'; + async function run() { try { // Set the generated mnemonic as env variable MNEMONIC so it can be used in the next examples. diff --git a/bindings/nodejs/examples/exchange/1-create-account.ts b/bindings/nodejs/examples/exchange/1-create-account.ts index 60cd726f92..6d0c6ac2f4 100644 --- a/bindings/nodejs/examples/exchange/1-create-account.ts +++ b/bindings/nodejs/examples/exchange/1-create-account.ts @@ -1,15 +1,15 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +// This example creates a new database and account. +// Run with command: +// yarn run-example ./exchange/1-create-account.ts + import { Wallet, WalletOptions, CoinType } from '@iota/sdk'; // This example uses secrets in environment variables for simplicity which should not be done in production. require('dotenv').config({ path: '.env' }); -// Run with command: -// yarn run-example ./exchange/1-create-account.ts - -// This example creates a new database and account. async function run() { try { if (!process.env.WALLET_DB_PATH) { diff --git a/bindings/nodejs/examples/exchange/2-generate-address.ts b/bindings/nodejs/examples/exchange/2-generate-address.ts index 41daed4983..2795ff18f2 100644 --- a/bindings/nodejs/examples/exchange/2-generate-address.ts +++ b/bindings/nodejs/examples/exchange/2-generate-address.ts @@ -1,15 +1,15 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +// This example generates an address for an account. +// Run with command: +// yarn run-example ./exchange/2-generate-address.ts + import { Wallet } from '@iota/sdk'; // This example uses secrets in environment variables for simplicity which should not be done in production. require('dotenv').config({ path: '.env' }); -// Run with command: -// yarn run-example ./exchange/2-generate-address.ts - -// This example generates an address for an account. async function run() { try { if (!process.env.WALLET_DB_PATH) { diff --git a/bindings/nodejs/examples/exchange/3-check-balance.ts b/bindings/nodejs/examples/exchange/3-check-balance.ts index 2dac1debac..ba3deb9ad8 100644 --- a/bindings/nodejs/examples/exchange/3-check-balance.ts +++ b/bindings/nodejs/examples/exchange/3-check-balance.ts @@ -1,15 +1,15 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +// This example gets the balance of an account. +// Run with command: +// yarn run-example ./exchange/3-check-balance.ts + import { Wallet } from '@iota/sdk'; // This example uses secrets in environment variables for simplicity which should not be done in production. require('dotenv').config({ path: '.env' }); -// Run with command: -// yarn run-example ./exchange/3-check-balance.ts - -// This example gets the balance of an account. async function run() { try { if (!process.env.WALLET_DB_PATH) { diff --git a/bindings/nodejs/examples/exchange/4-listen-events.ts b/bindings/nodejs/examples/exchange/4-listen-events.ts index 8c2f8bd411..5cef0aab28 100644 --- a/bindings/nodejs/examples/exchange/4-listen-events.ts +++ b/bindings/nodejs/examples/exchange/4-listen-events.ts @@ -1,15 +1,15 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +// This example listens to the NewOutput event. +// Run with command: +// yarn run-example ./exchange/4-listen-events.ts + import { Wallet, Event, WalletEventType } from '@iota/sdk'; // This example uses secrets in environment variables for simplicity which should not be done in production. require('dotenv').config({ path: '.env' }); -// Run with command: -// yarn run-example ./exchange/4-listen-events.ts - -// This example listens to the NewOutput event. async function run() { try { if (!process.env.WALLET_DB_PATH) { diff --git a/bindings/nodejs/examples/exchange/5-send-amount.ts b/bindings/nodejs/examples/exchange/5-send-amount.ts index 1d6cc7b69f..a10b53f1b1 100644 --- a/bindings/nodejs/examples/exchange/5-send-amount.ts +++ b/bindings/nodejs/examples/exchange/5-send-amount.ts @@ -1,15 +1,15 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +// This example sends tokens to an address. +// Run with command: +// yarn run-example ./exchange/5-send-amount.ts + import { Wallet } from '@iota/sdk'; // This example uses secrets in environment variables for simplicity which should not be done in production. require('dotenv').config({ path: '.env' }); -// Run with command: -// yarn run-example ./exchange/5-send-amount.ts - -// This example sends tokens to an address. async function run() { try { if (!process.env.WALLET_DB_PATH) { diff --git a/bindings/python/examples/exchange/0_generate_mnemonic.py b/bindings/python/examples/exchange/0_generate_mnemonic.py new file mode 100644 index 0000000000..11418f6bc2 --- /dev/null +++ b/bindings/python/examples/exchange/0_generate_mnemonic.py @@ -0,0 +1,10 @@ +# Copyright 2023 IOTA Stiftung +# SPDX-License-Identifier: Apache-2.0 + +# This example generates a new random mnemonic. + +from iota_sdk import Utils + +# Set the generated mnemonic as env variable MNEMONIC so it can be used in the next examples. +mnemonic = Utils.generate_mnemonic() +print(f'Mnemonic: {mnemonic}') diff --git a/bindings/python/examples/exchange/1_create_account.py b/bindings/python/examples/exchange/1_create_account.py new file mode 100644 index 0000000000..1412b9ade2 --- /dev/null +++ b/bindings/python/examples/exchange/1_create_account.py @@ -0,0 +1,47 @@ +# Copyright 2023 IOTA Stiftung +# SPDX-License-Identifier: Apache-2.0 + +# This example creates a new database and account. + +from iota_sdk import Wallet, StrongholdSecretManager, SyncOptions, CoinType +from dotenv import load_dotenv +import os + +# 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") +if 'NODE_URL' not in os.environ: + raise Exception(".env NODE_URL is undefined, see .env.example") +if 'STRONGHOLD_SNAPSHOT_PATH' not in os.environ: + raise Exception( + ".env STRONGHOLD_SNAPSHOT_PATH is undefined, see .env.example") +if 'STRONGHOLD_PASSWORD' not in os.environ: + raise Exception(".env STRONGHOLD_PASSWORD is undefined, see .env.example") +if 'MNEMONIC' not in os.environ: + raise Exception(".env MNEMONIC is undefined, see .env.example") + +client_options = { + 'nodes': [os.environ.get('NODE_URL')], +} + +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']) + +created_account = wallet.create_account('Alice') +account = wallet.get_account('Alice') + +# 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/alias/foundry outputs. +account.set_default_sync_options( + SyncOptions(sync_only_most_basic_outputs=True)) + +print(created_account) diff --git a/bindings/python/examples/exchange/2_generate_address.py b/bindings/python/examples/exchange/2_generate_address.py new file mode 100644 index 0000000000..2aae67185f --- /dev/null +++ b/bindings/python/examples/exchange/2_generate_address.py @@ -0,0 +1,25 @@ +# Copyright 2023 IOTA Stiftung +# SPDX-License-Identifier: Apache-2.0 + +# This example generates an address for an account. + +from iota_sdk import Wallet +from dotenv import load_dotenv +import os + +# 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") +if 'STRONGHOLD_PASSWORD' not in os.environ: + raise Exception(".env STRONGHOLD_PASSWORD is undefined, see .env.example") + +wallet = Wallet(os.environ.get('WALLET_DB_PATH')) + +wallet.set_stronghold_password(os.environ["STRONGHOLD_PASSWORD"]) + +account = wallet.get_account('Alice') + +address = account.generate_ed25519_addresses(1)[0] +print(f'Address:', address['address']) diff --git a/bindings/python/examples/exchange/3_check_balance.py b/bindings/python/examples/exchange/3_check_balance.py new file mode 100644 index 0000000000..49f061e1a1 --- /dev/null +++ b/bindings/python/examples/exchange/3_check_balance.py @@ -0,0 +1,29 @@ +# Copyright 2023 IOTA Stiftung +# SPDX-License-Identifier: Apache-2.0 + +# This example gets the balance of an account. + +from iota_sdk import Wallet, SyncOptions +from dotenv import load_dotenv +import os + +# 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") + +wallet = Wallet(os.environ.get('WALLET_DB_PATH')) + +account = wallet.get_account('Alice') + +addresses = account.addresses() +print(f'Addresses:', addresses) + +# 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/alias/foundry outputs. +balance = account.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/') diff --git a/bindings/python/examples/exchange/4_listen_events.py b/bindings/python/examples/exchange/4_listen_events.py new file mode 100644 index 0000000000..6454da71a9 --- /dev/null +++ b/bindings/python/examples/exchange/4_listen_events.py @@ -0,0 +1,55 @@ +# Copyright 2023 IOTA Stiftung +# SPDX-License-Identifier: Apache-2.0 + +# This example listens to the NewOutput event. + +from iota_sdk import Wallet, SyncOptions +from dotenv import load_dotenv +import json +import os +import time + +# 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") + +wallet = Wallet(os.environ.get('WALLET_DB_PATH')) + +account = wallet.get_account('Alice') + +received_event = False + + +def callback(event): + event_dict = json.loads(event) + print('AccountIndex:', event_dict["accountIndex"]) + print('Event:', event_dict["event"]) + + # Exit after receiving an event. + global received_event + received_event = True + + +# Only interested in new outputs here. +wallet.listen(callback, [2]) + +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/') + +addresses = account.addresses() +print('Send funds to:', addresses[0]["address"]) + +# Sync every 5 seconds until the faucet transaction gets confirmed. +for _ in range(100): + if received_event: + exit() + time.sleep(5) + + # Sync to detect new outputs + # 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/alias/foundry outputs. + account.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 new file mode 100644 index 0000000000..ca4aa779c8 --- /dev/null +++ b/bindings/python/examples/exchange/5_send_amount.py @@ -0,0 +1,37 @@ +# Copyright 2023 IOTA Stiftung +# SPDX-License-Identifier: Apache-2.0 + +# This example sends tokens to an address. + +from iota_sdk import Wallet, SyncOptions +from dotenv import load_dotenv +import os + +# 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") +if 'STRONGHOLD_PASSWORD' not in os.environ: + raise Exception(".env STRONGHOLD_PASSWORD is undefined, see .env.example") +if 'EXPLORER_URL' not in os.environ: + raise Exception(".env EXPLORER_URL is undefined, see .env.example") + +wallet = Wallet(os.environ.get('WALLET_DB_PATH')) + +account = wallet.get_account('Alice') + +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/alias/foundry outputs. +balance = account.sync(SyncOptions(sync_only_most_basic_outputs=True)) +print('Balance', balance) + +transaction = account.send([{ + "address": "rms1qpszqzadsym6wpppd6z037dvlejmjuke7s24hm95s9fg9vpua7vluaw60xu", + "amount": "1000000", +}]) +print(transaction) +print( + f'Check your block on: {os.environ["EXPLORER_URL"]}/block/{transaction.blockId}') diff --git a/bindings/python/iota_sdk/utils.py b/bindings/python/iota_sdk/utils.py index 08377de024..0288849ddb 100644 --- a/bindings/python/iota_sdk/utils.py +++ b/bindings/python/iota_sdk/utils.py @@ -94,7 +94,7 @@ def compute_alias_id(output_id: OutputId) -> HexStr: """Computes the alias id for the given alias output id. """ return _call_method('computeAliasId', { - 'outputId': output_id + 'outputId': repr(output_id) }) @staticmethod diff --git a/bindings/python/iota_sdk/wallet/wallet.py b/bindings/python/iota_sdk/wallet/wallet.py index df4530781c..afb92e07d5 100644 --- a/bindings/python/iota_sdk/wallet/wallet.py +++ b/bindings/python/iota_sdk/wallet/wallet.py @@ -232,14 +232,14 @@ def stop_background_sync(self): 'stopBackgroundSync', ) - def listen(self, handler, events: Optional[List[str]] = None): + 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[str]] = None): + 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 """ diff --git a/bindings/python/src/wallet.rs b/bindings/python/src/wallet.rs index 6401e08484..3a5e1ed57e 100644 --- a/bindings/python/src/wallet.rs +++ b/bindings/python/src/wallet.rs @@ -8,7 +8,7 @@ use iota_sdk_bindings_core::{ iota_sdk::wallet::{events::types::WalletEventType, Wallet as RustWallet}, Response, WalletMethod, WalletOptions, }; -use pyo3::prelude::*; +use pyo3::{prelude::*, types::PyTuple}; use tokio::sync::RwLock; use crate::{ @@ -58,11 +58,11 @@ pub fn call_wallet_method(wallet: &Wallet, method: String) -> Result { /// Listen to wallet events. #[pyfunction] -pub fn listen_wallet(wallet: &Wallet, events: Vec, handler: PyObject) { +pub fn listen_wallet(wallet: &Wallet, events: Vec, handler: PyObject) { let mut rust_events = Vec::with_capacity(events.len()); for event in events { - let event = match serde_json::from_str::(&event) { + let event = match WalletEventType::try_from(event) { Ok(event) => event, Err(e) => { panic!("Wrong event to listen! {e:?}"); @@ -78,9 +78,11 @@ pub fn listen_wallet(wallet: &Wallet, events: Vec, handler: PyObject) { .await .as_ref() .expect("wallet got destroyed") - .listen(rust_events, move |_| { + .listen(rust_events, move |event| { + let event_string = serde_json::to_string(&event).expect("json to string error"); Python::with_gil(|py| { - handler.call0(py).unwrap(); + let args = PyTuple::new(py, &[event_string]); + handler.call1(py, args).expect("failed to call python callback"); }); }) .await;