pub struct AccountHandle {
    account: Arc<RwLock<Account>>,
    pub(crate) client: Client,
    pub(crate) secret_manager: Arc<RwLock<SecretManager>>,
    pub(crate) last_synced: Arc<Mutex<u128>>,
    pub(crate) event_emitter: Arc<Mutex<EventEmitter>>,
    pub(crate) storage_manager: Arc<Mutex<StorageManager>>,
}
Expand description

A thread guard over an account, so we can lock the account during operations.

Fields

account: Arc<RwLock<Account>>client: Clientsecret_manager: Arc<RwLock<SecretManager>>last_synced: Arc<Mutex<u128>>event_emitter: Arc<Mutex<EventEmitter>>storage_manager: Arc<Mutex<StorageManager>>

Implementations

Create a new AccountHandle with an Account

Get the OutputData of an output stored in the account

Get the [Output] that minted a native token by the token ID. First try to get it from the account, if it isn’t in the account try to get it from the node

Get the Transaction of a transaction stored in the account

Get the transaction with inputs of an incoming transaction stored in the account List might not be complete, if the node pruned the data already

Returns all addresses of the account

Returns only addresses of the account with balance

Returns outputs of the account

Returns unspent outputs of the account

Returns all incoming transactions of the account

Returns all transactions of the account

Returns all pending transactions of the account

Save the account to the database, accepts the updated_account as option so we don’t need to drop it before saving

Generate addresses and stores them in the account

let public_addresses = account_handle.generate_addresses(2, None).await?;
// internal addresses are used for remainder outputs, if the RemainderValueStrategy for transactions is set to ChangeAddress
let internal_addresses = account_handle
    .generate_addresses(
        1,
        Some(AddressGenerationOptions {
            internal: true,
            ..Default::default()
        }),
    )
    .await?;

Generate an internal address and store in the account, internal addresses are used for remainder outputs

Get the AccountBalance

Get basic and nft outputs that have [ExpirationUnlockCondition], [StorageDepositReturnUnlockCondition] or [TimelockUnlockCondition] and can be unlocked now and also get basic outputs with only an [AddressUnlockCondition] unlock condition, for additional inputs

Get basic outputs that have only one unlock condition which is [AddressUnlockCondition], so they can be used as additional inputs

Try to claim basic or nft outputs that have additional unlock conditions to their [AddressUnlockCondition] from AccountHandle::get_unlockable_outputs_with_additional_unlock_conditions().

Try to claim basic outputs that have additional unlock conditions to their [AddressUnlockCondition].

Consolidate basic outputs with only an [AddressUnlockCondition] from an account by sending them to an own address again if the output amount is >= the output_consolidation_threshold. When force is set to true, the threshold is ignored. Only consolidates the amount of outputs that fit into a single transaction.

Search addresses with unspent outputs address_gap_limit: The number of addresses to search for, after the last address with unspent outputs Addresses that got crated during this operation and have a higher key_index than the latest one with outputs, will be removed again, to keep the account size smaller

During search_addresses_with_outputs we created new addresses that don’t have funds, so we remove them again.

Get the addresses that should be synced with the current known unspent output ids Also adds alias and nft addresses from unspent alias or nft outputs that have no Timelock, Expiration or StorageDepositReturn [UnlockCondition]

Get the current output ids for provided addresses and only returns addresses that have unspent outputs and return spent outputs separated

Get outputs from addresses

Returns output ids for outputs that are directly (Ed25519 address in AddressUnlockCondition) or indirectly (alias/nft address in AddressUnlockCondition and the alias/nft output is controlled with the Ed25519 address) connected to

Convert OutputResponse to OutputData with the network_id added

Gets outputs by their id, already known outputs are not requested again, but loaded from the account set as unspent, because we wouldn’t get them from the node if they were spent New requested output responses are returned and old ones separated with their accumulated balance

Sync transactions and reattach them if unconfirmed. Returns the transaction with updated metadata and spent output ids that don’t need to be locked anymore

Retries (promotes or reattaches) a block for provided block id until it’s included (referenced by a milestone). This function is re-exported from the client library and default interval is as defined in iota.rs. Returns the included block at first position and additional reattached blocks

Sync the account by fetching new information from the nodes. Will also retry pending transactions if necessary.

Function to build the transaction essence from the selected in and outputs

Function to 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.

Aggregate foundry and alias outputs that sum up to required burn_token_amount, if controlling alias doesn’t have the specified native token it’s added anyways for unlock purpose

Function to burn an nft output. Outputs controlled by it will be sweeped before if they don’t have a storage deposit return, timelock or expiration unlock condition.

Function to melt native tokens. This happens with the foundry output which minted them, by increasing it’s melted_tokens field. This should be preferred over burning, because after burning, the foundry can never be destroyed anymore.

Function to destroy an alias output. Outputs controlled by it will be sweeped before if they don’t have a storage deposit return, timelock or expiration unlock condition. The amount and possible native tokens will be sent to the governor address.

Function to destroy a foundry output with a circulating supply of 0. Native tokens in the foundry (minted by other foundries) will be transactioned to the controlling alias

Destroy all foundries in the given set foundry_ids

Find and return unspent OutputData for given alias_id and foundry_id

Fetches alias outputs with address set as Governor unlock condition

Fetches basic outputs with address unlock conditions only

Fetches nft outputs with address unlock conditions only

Update unspent output, this function is originally intended for updating recursively synced alias and nft address output

Function to mint more native tokens when the max supply isn’t reached yet. The foundry needs to be controlled by this account. Address needs to be Bech32 encoded. This will not change the max supply.

let tx = account_handle.increase_native_token_supply(
            TokenId::from_str("08e68f7616cd4948efebc6a77c4f93aed770ac53860100000000000000000000000000000000")?,
            U256::from(100),
            native_token_options,
            None
        ).await?;
println!("Transaction created: {}", tx.transaction_id);
if let Some(block_id) = tx.block_id {
    println!("Block sent: {}", block_id);
}

Function to mint native tokens This happens in a two step process:

  1. Create or get an existing alias output
  2. Create a new foundry output with native tokens minted to the account address Calls AccountHandle.send() internally, the options can define the RemainderValueStrategy or custom inputs. Address needs to be Bech32 encoded
let native_token_options = NativeTokenOptions {
    account_address: None,
    circulating_supply: U256::from(100),
    maximum_supply: U256::from(100),
    foundry_metadata: None
};

let tx = account_handle.mint_native_token(native_token_options, None,).await?;
println!("Transaction created: {}", tx.transaction_id);
if let Some(block_id) = tx.block_id {
    println!("Block sent: {}", block_id);
}

Get an existing alias output or create a new one

Function to mint nfts. Calls AccountHandle.send() internally, the options can define the RemainderValueStrategy or custom inputs. Address needs to be Bech32 encoded

let nft_id: [u8; 38] =
    prefix_hex::decode("08e68f7616cd4948efebc6a77c4f93aed770ac53860100000000000000000000000000000000")?
        .try_into()
        .unwrap();
let nft_options = vec![NftOptions {
    address: Some("rms1qpszqzadsym6wpppd6z037dvlejmjuke7s24hm95s9fg9vpua7vluaw60xu".to_string()),
    immutable_metadata: Some(b"some immutable nft metadata".to_vec()),
    metadata: Some(b"some nft metadata".to_vec()),
}];

let transaction = account.mint_nfts(nft_options, None).await?;
println!(
    "Transaction: {} Block sent: http://localhost:14265/api/core/v2/blocks/{}",
    transaction.transaction_id,
    transaction.block_id.expect("no block created yet")
);

Function to prepare the transaction for AccountHandle.mint_nfts()

Function to create basic outputs with which we then will call AccountHandle.send(), the options can define the RemainderValueStrategy or custom inputs. Address needs to be Bech32 encoded

let outputs = vec![AddressWithAmount{
    address: "rms1qpszqzadsym6wpppd6z037dvlejmjuke7s24hm95s9fg9vpua7vluaw60xu".to_string(),
    amount: 1_000_000,
}];

let tx = account_handle.send_amount(outputs, None ).await?;
println!("Transaction created: {}", tx.transaction_id);
if let Some(block_id) = tx.block_id {
    println!("Block sent: {}", block_id);
}

Function to prepare the transaction for AccountHandle.send_amount()

Function to send micro transactions by using the [StorageDepositReturnUnlockCondition] with an [ExpirationUnlockCondition]. Will call AccountHandle.send(), the options can define the RemainderValueStrategy or custom inputs. Address needs to be Bech32 encoded

let outputs = vec![AddressWithMicroAmount{
   address: "rms1qpszqzadsym6wpppd6z037dvlejmjuke7s24hm95s9fg9vpua7vluaw60xu".to_string(),
   amount: 1,
   return_address: None,
   expiration: None,
}];

let transaction = account_handle.send_micro_transaction(outputs, None ).await?;

println!(
   "Transaction: {} Block sent: http://localhost:14265/api/core/v2/blocks/{}",
   transaction.transaction_id,
   transaction.block_id.expect("no block created yet")
);

Function to prepare the transaction for AccountHandle.send_micro_transaction()

Function to send native tokens in basic outputs with a [StorageDepositReturnUnlockCondition] and [ExpirationUnlockCondition], so the storage deposit gets back to the sender and also that the sender gets access to the output again after a defined time (default 1 day), Calls AccountHandle.send() internally, the options can define the RemainderValueStrategy or custom inputs. Address needs to be Bech32 encoded

let outputs = vec![AddressNativeTokens {
    address: "rms1qpszqzadsym6wpppd6z037dvlejmjuke7s24hm95s9fg9vpua7vluaw60xu".to_string(),
    native_tokens: vec![(
        TokenId::from_str("08e68f7616cd4948efebc6a77c4f93aed770ac53860100000000000000000000000000000000")?,
        U256::from(50),
    )],
    ..Default::default()
}];

let tx = account_handle.send_native_tokens(outputs, None).await?;
println!("Transaction created: {}", tx.transaction_id);
if let Some(block_id) = tx.block_id {
    println!("Block sent: {}", block_id);
}

Function to prepare the transaction for AccountHandle.send_native_tokens()

Function to send native tokens in basic outputs with a [StorageDepositReturnUnlockCondition](iota_client::block::output::unlock_condition:: StorageDepositReturnUnlockCondition) and [ExpirationUnlockCondition](iota_client::block::output:: unlock_condition::ExpirationUnlockCondition), so the storage deposit gets back to the sender and also that the sender gets access to the output again after a defined time (default 1 day), Calls AccountHandle.send() internally, the options can define the RemainderValueStrategy. Custom inputs will be replaced with the required nft inputs. Address needs to be Bech32 encoded

let outputs = vec![AddressAndNftId {
    address: "rms1qpszqzadsym6wpppd6z037dvlejmjuke7s24hm95s9fg9vpua7vluaw60xu".to_string(),
    nft_id: NftId::from_str("04f9b54d488d2e83a6c90db08ae4b39651bbba8a")?,
}];

let transaction = account.send_nft(outputs, None).await?;

println!(
"Transaction: {} Block sent: http://localhost:14265/api/core/v2/blocks/{}",
transaction.transaction_id,
transaction.block_id.expect("no block created yet")
);

Function to prepare the transaction for AccountHandle.send_nft()

Selects inputs for a transaction and locks them in the account, so they don’t get used again

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

Prepare an nft output

Get inputs and build the transaction essence

Function to sign a transaction essence

Submits a payload in a block

Send a transaction, if sending a block fails, the function will return None for the block_id, but the wallet will retry sending the transaction during syncing.

let outputs = vec![TransactionOutput {
    address: "rms1qpszqzadsym6wpppd6z037dvlejmjuke7s24hm95s9fg9vpua7vluaw60xu".to_string(),
    amount: 1_000_000,
    output_kind: None,
}];

let tx = account_handle
    .send(
        outputs,
        Some(TransactionOptions {
            remainder_value_strategy: RemainderValueStrategy::ReuseAddress,
            ..Default::default()
        }),
    )
    .await?;
println!("Transaction created: {}", tx.transaction_id);
if let Some(block_id) = tx.block_id {
    println!("Block sent: {}", block_id);
}

Separated function from send, so syncing isn’t called recursively with the consolidation function, which sends transactions

Sign a transaction, submit it to a node and store it in the account

Validate the transaction, submit it to a node and store it in the account

Update account with newly synced data and emit events for outputs

Update account with newly synced transactions

Update account with newly generated addresses

Update unspent outputs, this function is originally intended for updating recursively synced alias and nft address outputs

Methods from Deref<Target = RwLock<Account>>

Locks this RwLock with shared read access, causing the current task to yield until the lock has been acquired.

The calling task will yield until there are no writers which hold the lock. There may be other readers inside the lock when the task resumes.

Note that under the priority policy of [RwLock], read locks are not granted until prior write locks, to prevent starvation. Therefore deadlock may occur if a read lock is held by the current task, a write lock attempt is made, and then a subsequent read lock attempt is made by the current task.

Returns an RAII guard which will drop this read access of the RwLock when dropped.

Cancel safety

This method uses a queue to fairly distribute locks in the order they were requested. Cancelling a call to read makes you lose your place in the queue.

Examples
use std::sync::Arc;
use tokio::sync::RwLock;

#[tokio::main]
async fn main() {
    let lock = Arc::new(RwLock::new(1));
    let c_lock = lock.clone();

    let n = lock.read().await;
    assert_eq!(*n, 1);

    tokio::spawn(async move {
        // While main has an active read lock, we acquire one too.
        let r = c_lock.read().await;
        assert_eq!(*r, 1);
    }).await.expect("The spawned task has panicked");

    // Drop the guard after the spawned task finishes.
    drop(n);
}

Blockingly locks this RwLock with shared read access.

This method is intended for use cases where you need to use this rwlock in asynchronous code as well as in synchronous code.

Returns an RAII guard which will drop the read access of this RwLock when dropped.

Panics

This function panics if called within an asynchronous execution context.

  • If you find yourself in an asynchronous execution context and needing to call some (synchronous) function which performs one of these blocking_ operations, then consider wrapping that call inside [spawn_blocking()][crate::runtime::Handle::spawn_blocking] (or [block_in_place()][crate::task::block_in_place]).
Examples
use std::sync::Arc;
use tokio::sync::RwLock;

#[tokio::main]
async fn main() {
    let rwlock = Arc::new(RwLock::new(1));
    let mut write_lock = rwlock.write().await;

    let blocking_task = tokio::task::spawn_blocking({
        let rwlock = Arc::clone(&rwlock);
        move || {
            // This shall block until the `write_lock` is released.
            let read_lock = rwlock.blocking_read();
            assert_eq!(*read_lock, 0);
        }
    });

    *write_lock -= 1;
    drop(write_lock); // release the lock.

    // Await the completion of the blocking task.
    blocking_task.await.unwrap();

    // Assert uncontended.
    assert!(rwlock.try_write().is_ok());
}

Locks this RwLock with shared read access, causing the current task to yield until the lock has been acquired.

The calling task will yield until there are no writers which hold the lock. There may be other readers inside the lock when the task resumes.

This method is identical to [RwLock::read], except that the returned guard references the RwLock with an Arc rather than by borrowing it. Therefore, the RwLock must be wrapped in an Arc to call this method, and the guard will live for the 'static lifetime, as it keeps the RwLock alive by holding an Arc.

Note that under the priority policy of [RwLock], read locks are not granted until prior write locks, to prevent starvation. Therefore deadlock may occur if a read lock is held by the current task, a write lock attempt is made, and then a subsequent read lock attempt is made by the current task.

Returns an RAII guard which will drop this read access of the RwLock when dropped.

Cancel safety

This method uses a queue to fairly distribute locks in the order they were requested. Cancelling a call to read_owned makes you lose your place in the queue.

Examples
use std::sync::Arc;
use tokio::sync::RwLock;

#[tokio::main]
async fn main() {
    let lock = Arc::new(RwLock::new(1));
    let c_lock = lock.clone();

    let n = lock.read_owned().await;
    assert_eq!(*n, 1);

    tokio::spawn(async move {
        // While main has an active read lock, we acquire one too.
        let r = c_lock.read_owned().await;
        assert_eq!(*r, 1);
    }).await.expect("The spawned task has panicked");

    // Drop the guard after the spawned task finishes.
    drop(n);
}

Attempts to acquire this RwLock with shared read access.

If the access couldn’t be acquired immediately, returns TryLockError. Otherwise, an RAII guard is returned which will release read access when dropped.

Examples
use std::sync::Arc;
use tokio::sync::RwLock;

#[tokio::main]
async fn main() {
    let lock = Arc::new(RwLock::new(1));
    let c_lock = lock.clone();

    let v = lock.try_read().unwrap();
    assert_eq!(*v, 1);

    tokio::spawn(async move {
        // While main has an active read lock, we acquire one too.
        let n = c_lock.read().await;
        assert_eq!(*n, 1);
    }).await.expect("The spawned task has panicked");

    // Drop the guard when spawned task finishes.
    drop(v);
}

Attempts to acquire this RwLock with shared read access.

If the access couldn’t be acquired immediately, returns TryLockError. Otherwise, an RAII guard is returned which will release read access when dropped.

This method is identical to [RwLock::try_read], except that the returned guard references the RwLock with an Arc rather than by borrowing it. Therefore, the RwLock must be wrapped in an Arc to call this method, and the guard will live for the 'static lifetime, as it keeps the RwLock alive by holding an Arc.

Examples
use std::sync::Arc;
use tokio::sync::RwLock;

#[tokio::main]
async fn main() {
    let lock = Arc::new(RwLock::new(1));
    let c_lock = lock.clone();

    let v = lock.try_read_owned().unwrap();
    assert_eq!(*v, 1);

    tokio::spawn(async move {
        // While main has an active read lock, we acquire one too.
        let n = c_lock.read_owned().await;
        assert_eq!(*n, 1);
    }).await.expect("The spawned task has panicked");

    // Drop the guard when spawned task finishes.
    drop(v);
}

Locks this RwLock with exclusive write access, causing the current task to yield until the lock has been acquired.

The calling task will yield while other writers or readers currently have access to the lock.

Returns an RAII guard which will drop the write access of this RwLock when dropped.

Cancel safety

This method uses a queue to fairly distribute locks in the order they were requested. Cancelling a call to write makes you lose your place in the queue.

Examples
use tokio::sync::RwLock;

#[tokio::main]
async fn main() {
  let lock = RwLock::new(1);

  let mut n = lock.write().await;
  *n = 2;
}

Blockingly locks this RwLock with exclusive write access.

This method is intended for use cases where you need to use this rwlock in asynchronous code as well as in synchronous code.

Returns an RAII guard which will drop the write access of this RwLock when dropped.

Panics

This function panics if called within an asynchronous execution context.

  • If you find yourself in an asynchronous execution context and needing to call some (synchronous) function which performs one of these blocking_ operations, then consider wrapping that call inside [spawn_blocking()][crate::runtime::Handle::spawn_blocking] (or [block_in_place()][crate::task::block_in_place]).
Examples
use std::sync::Arc;
use tokio::{sync::RwLock};

#[tokio::main]
async fn main() {
    let rwlock =  Arc::new(RwLock::new(1));
    let read_lock = rwlock.read().await;

    let blocking_task = tokio::task::spawn_blocking({
        let rwlock = Arc::clone(&rwlock);
        move || {
            // This shall block until the `read_lock` is released.
            let mut write_lock = rwlock.blocking_write();
            *write_lock = 2;
        }
    });

    assert_eq!(*read_lock, 1);
    // Release the last outstanding read lock.
    drop(read_lock);

    // Await the completion of the blocking task.
    blocking_task.await.unwrap();

    // Assert uncontended.
    let read_lock = rwlock.try_read().unwrap();
    assert_eq!(*read_lock, 2);
}

Locks this RwLock with exclusive write access, causing the current task to yield until the lock has been acquired.

The calling task will yield while other writers or readers currently have access to the lock.

This method is identical to [RwLock::write], except that the returned guard references the RwLock with an Arc rather than by borrowing it. Therefore, the RwLock must be wrapped in an Arc to call this method, and the guard will live for the 'static lifetime, as it keeps the RwLock alive by holding an Arc.

Returns an RAII guard which will drop the write access of this RwLock when dropped.

Cancel safety

This method uses a queue to fairly distribute locks in the order they were requested. Cancelling a call to write_owned makes you lose your place in the queue.

Examples
use std::sync::Arc;
use tokio::sync::RwLock;

#[tokio::main]
async fn main() {
  let lock = Arc::new(RwLock::new(1));

  let mut n = lock.write_owned().await;
  *n = 2;
}

Attempts to acquire this RwLock with exclusive write access.

If the access couldn’t be acquired immediately, returns TryLockError. Otherwise, an RAII guard is returned which will release write access when dropped.

Examples
use tokio::sync::RwLock;

#[tokio::main]
async fn main() {
    let rw = RwLock::new(1);

    let v = rw.read().await;
    assert_eq!(*v, 1);

    assert!(rw.try_write().is_err());
}

Attempts to acquire this RwLock with exclusive write access.

If the access couldn’t be acquired immediately, returns TryLockError. Otherwise, an RAII guard is returned which will release write access when dropped.

This method is identical to [RwLock::try_write], except that the returned guard references the RwLock with an Arc rather than by borrowing it. Therefore, the RwLock must be wrapped in an Arc to call this method, and the guard will live for the 'static lifetime, as it keeps the RwLock alive by holding an Arc.

Examples
use std::sync::Arc;
use tokio::sync::RwLock;

#[tokio::main]
async fn main() {
    let rw = Arc::new(RwLock::new(1));

    let v = Arc::clone(&rw).read_owned().await;
    assert_eq!(*v, 1);

    assert!(rw.try_write_owned().is_err());
}

Trait Implementations

Returns a copy of the value. Read more

Performs copy-assignment from source. Read more

Formats the value using the given formatter. Read more

The resulting type after dereferencing.

Dereferences the value.

Auto Trait Implementations

Blanket Implementations

Gets the TypeId of self. Read more

Immutably borrows from an owned value. Read more

Mutably borrows from an owned value. Read more

Returns the argument unchanged.

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more

Instruments this type with the current Span, returning an Instrumented wrapper. Read more

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Should always be Self

The resulting type after obtaining ownership.

Creates owned data from borrowed data, usually by cloning. Read more

Uses borrowed data to replace owned data, usually by cloning. Read more

The type returned in the event of a conversion error.

Performs the conversion.

The type returned in the event of a conversion error.

Performs the conversion.

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more