1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// Copyright 2022 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0
use getset::Getters;
use serde::{Deserialize, Serialize};
use crate::{
client::{api::PreparedTransactionData, secret::SecretManage},
types::block::{
address::Bech32Address,
output::{unlock_condition::AddressUnlockCondition, NftId, NftOutputBuilder, Output},
ConvertTo,
},
wallet::account::{operations::transaction::Transaction, Account, TransactionOptions},
};
/// Params for `send_nft()`
#[derive(Debug, Clone, Serialize, Deserialize, Getters)]
#[serde(rename_all = "camelCase")]
pub struct SendNftParams {
/// Bech32 encoded address
#[getset(get = "pub")]
address: Bech32Address,
/// Nft id
#[getset(get = "pub")]
nft_id: NftId,
}
impl SendNftParams {
/// Creates a new instance of [`SendNftParams`]
pub fn new(
address: impl ConvertTo<Bech32Address>,
nft_id: impl ConvertTo<NftId>,
) -> Result<Self, crate::wallet::Error> {
Ok(Self {
address: address.convert()?,
nft_id: nft_id.convert()?,
})
}
}
impl<S: 'static + SecretManage> Account<S>
where
crate::wallet::Error: From<S::Error>,
{
/// Sends native tokens in basic outputs with a
/// [`StorageDepositReturnUnlockCondition`](crate::types::block::output::unlock_condition::StorageDepositReturnUnlockCondition) and an
/// [`ExpirationUnlockCondition`](crate::types::block::output::unlock_condition::ExpirationUnlockCondition), so that
/// the storage deposit is returned to the sender and the sender gets access to the output again after a
/// predefined time (default 1 day).
/// Calls [Account::send_outputs()](crate::wallet::account::Account::send_outputs) internally. The options may
/// define the remainder value strategy. Note that custom inputs will be replaced with the required nft inputs
/// and addresses need to be bech32-encoded.
/// ```ignore
/// let params = [SendNftParams::new(
/// "rms1qpszqzadsym6wpppd6z037dvlejmjuke7s24hm95s9fg9vpua7vluaw60xu",
/// "0xe645042a8a082957cb4bec4927936699ee8e56048834b090379da64213ce231b",
/// )?];
///
/// let transaction = account.send_nft(params, None).await?;
///
/// println!(
/// "Transaction sent: {}/transaction/{}",
/// std::env::var("EXPLORER_URL").unwrap(),
/// transaction.transaction_id
/// );
/// ```
pub async fn send_nft<I: IntoIterator<Item = SendNftParams> + Send>(
&self,
params: I,
options: impl Into<Option<TransactionOptions>> + Send,
) -> crate::wallet::Result<Transaction>
where
I::IntoIter: Send,
{
let options = options.into();
let prepared_transaction = self.prepare_send_nft(params, options.clone()).await?;
self.sign_and_submit_transaction(prepared_transaction, options).await
}
/// Prepares the transaction for
/// [Account::send_nft()](crate::wallet::Account::send_nft).
pub async fn prepare_send_nft<I: IntoIterator<Item = SendNftParams> + Send>(
&self,
params: I,
options: impl Into<Option<TransactionOptions>> + Send,
) -> crate::wallet::Result<PreparedTransactionData>
where
I::IntoIter: Send,
{
log::debug!("[TRANSACTION] prepare_send_nft");
let unspent_outputs = self.unspent_outputs(None).await?;
let token_supply = self.client().get_token_supply().await?;
let mut outputs = Vec::new();
for SendNftParams { address, nft_id } in params {
self.client().bech32_hrp_matches(address.hrp()).await?;
// Find nft output from the inputs
if let Some(nft_output_data) = unspent_outputs.iter().find(|o| {
if let Output::Nft(nft_output) = &o.output {
nft_id == nft_output.nft_id_non_null(&o.output_id)
} else {
false
}
}) {
if let Output::Nft(nft_output) = &nft_output_data.output {
// Set the nft id and new address unlock condition
let nft_builder = NftOutputBuilder::from(nft_output)
.with_nft_id(nft_id)
.with_unlock_conditions([AddressUnlockCondition::new(address)]);
outputs.push(nft_builder.finish_output(token_supply)?);
}
} else {
return Err(crate::wallet::Error::NftNotFoundInUnspentOutputs);
};
}
self.prepare_transaction(outputs, options).await
}
}