diff --git a/bindings/core/src/method_handler/account.rs b/bindings/core/src/method_handler/account.rs index 804a0c20cb..c54b3d1dbd 100644 --- a/bindings/core/src/method_handler/account.rs +++ b/bindings/core/src/method_handler/account.rs @@ -17,7 +17,7 @@ use crate::{method::AccountMethod, Response, Result}; pub(crate) async fn call_account_method_internal(account: &Account, method: AccountMethod) -> Result { let response = match method { AccountMethod::Addresses => { - let addresses = account.addresses().await?; + let addresses = account.addresses().await; Response::Addresses(addresses) } AccountMethod::AddressesWithUnspentOutputs => { diff --git a/bindings/core/src/method_handler/utils.rs b/bindings/core/src/method_handler/utils.rs index 9b35265845..17d7011540 100644 --- a/bindings/core/src/method_handler/utils.rs +++ b/bindings/core/src/method_handler/utils.rs @@ -30,7 +30,7 @@ pub(crate) fn call_utils_method_internal(method: UtilsMethod) -> Result { Response::Bech32Address(hex_public_key_to_bech32_address(&hex, bech32_hrp)?) } - UtilsMethod::ParseBech32Address { address } => Response::ParsedBech32Address(Address::from(address.inner())), + UtilsMethod::ParseBech32Address { address } => Response::ParsedBech32Address(address.into_inner()), UtilsMethod::IsAddressValid { address } => Response::Bool(Address::is_valid_bech32(&address)), UtilsMethod::GenerateMnemonic => Response::GeneratedMnemonic(Client::generate_mnemonic()?.to_string()), UtilsMethod::MnemonicToHexSeed { mnemonic } => { diff --git a/bindings/core/tests/utils.rs b/bindings/core/tests/utils.rs index 61899cf260..049d1b6ad3 100644 --- a/bindings/core/tests/utils.rs +++ b/bindings/core/tests/utils.rs @@ -14,7 +14,9 @@ async fn utils() -> Result<()> { let bech32_address = Bech32Address::try_from_str("rms1qpllaj0pyveqfkwxmnngz2c488hfdtmfrj3wfkgxtk4gtyrax0jaxzt70zy")?; - let method = UtilsMethod::Bech32ToHex { bech32: bech32_address }; + let method = UtilsMethod::Bech32ToHex { + bech32: bech32_address.clone(), + }; let response = call_utils_method(method); match response { diff --git a/cli/src/command/account.rs b/cli/src/command/account.rs index 5ea180908c..9539c876a6 100644 --- a/cli/src/command/account.rs +++ b/cli/src/command/account.rs @@ -284,7 +284,7 @@ impl FromStr for TransactionSelector { /// `addresses` command pub async fn addresses_command(account: &Account) -> Result<(), Error> { - let addresses = account.addresses().await?; + let addresses = account.addresses().await; if addresses.is_empty() { println_log_info!("No addresses found"); @@ -547,8 +547,8 @@ pub async fn faucet_command( let address = if let Some(address) = address { address } else { - match account.addresses().await?.last() { - Some(address) => *address.address(), + match account.addresses().await.into_iter().rev().next() { + Some(address) => address.into_bech32(), None => return Err(Error::NoAddressForFaucet), } }; diff --git a/sdk/examples/client/02_address_balance.rs b/sdk/examples/client/02_address_balance.rs index 9dbc3e0eb6..0f2fdc060c 100644 --- a/sdk/examples/client/02_address_balance.rs +++ b/sdk/examples/client/02_address_balance.rs @@ -31,19 +31,20 @@ async fn main() -> Result<()> { let secret_manager = SecretManager::try_from_mnemonic(std::env::var("MNEMONIC").unwrap())?; // Generate the first address - let addresses = secret_manager + let first_address = secret_manager .generate_ed25519_addresses( GetAddressesOptions::from_client(&client) .await? .with_account_index(0) .with_range(0..1), ) - .await?; + .await?[0] + .clone(); // Get output ids of outputs that can be controlled by this address without further unlock constraints let output_ids_response = client .basic_output_ids([ - QueryParameter::Address(addresses[0]), + QueryParameter::Address(first_address.clone()), QueryParameter::HasExpiration(false), QueryParameter::HasTimelock(false), QueryParameter::HasStorageDepositReturn(false), @@ -65,7 +66,7 @@ async fn main() -> Result<()> { println!( "Outputs controlled by {} have: {:?}i and native tokens:\n{:#?}", - addresses[0], + first_address, total_amount, total_native_tokens.finish_vec()? ); diff --git a/sdk/examples/client/node_api_indexer/02_get_account_outputs.rs b/sdk/examples/client/node_api_indexer/02_get_account_outputs.rs index 3a1eee84a3..ba1a6e770d 100644 --- a/sdk/examples/client/node_api_indexer/02_get_account_outputs.rs +++ b/sdk/examples/client/node_api_indexer/02_get_account_outputs.rs @@ -42,7 +42,7 @@ async fn main() -> Result<()> { // Get output IDs of account outputs that can be controlled by this address. let output_ids_response = client .account_output_ids([ - QueryParameter::Governor(address), + QueryParameter::Governor(address.clone()), QueryParameter::StateController(address), ]) .await?; diff --git a/sdk/examples/client/output/build_account_output.rs b/sdk/examples/client/output/build_account_output.rs index a4c22583b5..746b9d015c 100644 --- a/sdk/examples/client/output/build_account_output.rs +++ b/sdk/examples/client/output/build_account_output.rs @@ -45,11 +45,11 @@ async fn main() -> Result<()> { // Account id needs to be null the first time let account_output = AccountOutputBuilder::new_with_minimum_storage_deposit(rent_structure, AccountId::null()) .with_state_metadata(metadata) - .add_feature(SenderFeature::new(address)) + .add_feature(SenderFeature::new(address.clone())) .add_feature(MetadataFeature::new(metadata)?) - .add_immutable_feature(IssuerFeature::new(address)) + .add_immutable_feature(IssuerFeature::new(address.clone())) .add_immutable_feature(MetadataFeature::new(metadata)?) - .add_unlock_condition(StateControllerAddressUnlockCondition::new(address)) + .add_unlock_condition(StateControllerAddressUnlockCondition::new(address.clone())) .add_unlock_condition(GovernorAddressUnlockCondition::new(address)) .finish_output(token_supply)?; diff --git a/sdk/examples/client/output/build_basic_output.rs b/sdk/examples/client/output/build_basic_output.rs index 900cadfa19..489ae2bbe6 100644 --- a/sdk/examples/client/output/build_basic_output.rs +++ b/sdk/examples/client/output/build_basic_output.rs @@ -43,8 +43,8 @@ async fn main() -> Result<()> { .unwrap_or("rms1qpllaj0pyveqfkwxmnngz2c488hfdtmfrj3wfkgxtk4gtyrax0jaxzt70zy".to_string()); let address = Address::try_from_bech32(address)?; - let basic_output_builder = - BasicOutputBuilder::new_with_amount(1_000_000).add_unlock_condition(AddressUnlockCondition::new(address)); + let basic_output_builder = BasicOutputBuilder::new_with_amount(1_000_000) + .add_unlock_condition(AddressUnlockCondition::new(address.clone())); let outputs = [ // most simple output @@ -58,7 +58,7 @@ async fn main() -> Result<()> { basic_output_builder .clone() .add_unlock_condition(StorageDepositReturnUnlockCondition::new( - address, + address.clone(), 1_000_000, token_supply, )?) @@ -66,7 +66,7 @@ async fn main() -> Result<()> { // with expiration basic_output_builder .clone() - .add_unlock_condition(ExpirationUnlockCondition::new(address, 1)?) + .add_unlock_condition(ExpirationUnlockCondition::new(address.clone(), 1)?) .finish_output(token_supply)?, // with timelock basic_output_builder diff --git a/sdk/examples/client/output/build_nft_output.rs b/sdk/examples/client/output/build_nft_output.rs index e75aa942ba..5dfafd8772 100644 --- a/sdk/examples/client/output/build_nft_output.rs +++ b/sdk/examples/client/output/build_nft_output.rs @@ -57,8 +57,8 @@ async fn main() -> Result<()> { // NftId needs to be null the first time let nft_output = NftOutputBuilder::new_with_minimum_storage_deposit(rent_structure, NftId::null()) - .add_unlock_condition(AddressUnlockCondition::new(address)) - .add_feature(SenderFeature::new(address)) + .add_unlock_condition(AddressUnlockCondition::new(address.clone())) + .add_feature(SenderFeature::new(address.clone())) .add_feature(MetadataFeature::new(MUTABLE_METADATA)?) .add_feature(TagFeature::new(TAG)?) .add_immutable_feature(IssuerFeature::new(address)) diff --git a/sdk/examples/how_tos/accounts_and_addresses/check_balance.rs b/sdk/examples/how_tos/accounts_and_addresses/check_balance.rs index f06f913a7f..87b28a24f0 100644 --- a/sdk/examples/how_tos/accounts_and_addresses/check_balance.rs +++ b/sdk/examples/how_tos/accounts_and_addresses/check_balance.rs @@ -31,7 +31,7 @@ async fn main() -> Result<()> { println!("ADDRESSES:"); let explorer_url = std::env::var("EXPLORER_URL").ok(); let prepended = explorer_url.map(|url| format!("{url}/addr/")).unwrap_or_default(); - for address in account.addresses().await? { + for address in account.addresses().await { println!(" - {prepended}{}", address.address()); } diff --git a/sdk/examples/how_tos/accounts_and_addresses/consolidate_outputs.rs b/sdk/examples/how_tos/accounts_and_addresses/consolidate_outputs.rs index 919022d4ad..ae79dea54f 100644 --- a/sdk/examples/how_tos/accounts_and_addresses/consolidate_outputs.rs +++ b/sdk/examples/how_tos/accounts_and_addresses/consolidate_outputs.rs @@ -49,7 +49,7 @@ async fn main() -> Result<()> { println!("OUTPUT #{i}"); println!( "- address: {:?}\n- amount: {:?}\n- native tokens: {:?}", - output_data.address.to_bech32_unchecked("rms"), + output_data.address.clone().to_bech32_unchecked("rms"), output_data.output.amount(), output_data.output.native_tokens() ) @@ -85,7 +85,7 @@ async fn main() -> Result<()> { println!("OUTPUT #{i}"); println!( "- address: {:?}\n- amount: {:?}\n- native tokens: {:?}", - output_data.address.to_bech32_unchecked("rms"), + output_data.address.clone().to_bech32_unchecked("rms"), output_data.output.amount(), output_data.output.native_tokens() ) diff --git a/sdk/examples/how_tos/accounts_and_addresses/create_address.rs b/sdk/examples/how_tos/accounts_and_addresses/create_address.rs index 0ea29cb5e8..3cd40dac03 100644 --- a/sdk/examples/how_tos/accounts_and_addresses/create_address.rs +++ b/sdk/examples/how_tos/accounts_and_addresses/create_address.rs @@ -36,7 +36,7 @@ async fn main() -> Result<()> { let address_url = explorer_url.map(|url| format!("{url}/addr/")).unwrap_or_default(); println!("Current addresses:"); - for address in account.addresses().await? { + for address in account.addresses().await { println!(" - {address_url}{}", address.address()); } @@ -45,7 +45,7 @@ async fn main() -> Result<()> { .generate_ed25519_addresses(NUM_ADDRESSES_TO_GENERATE, None) .await?; println!("Generated {} new addresses:", new_addresses.len()); - let account_addresses = account.addresses().await?; + let account_addresses = account.addresses().await; for new_address in new_addresses.iter() { assert!(account_addresses.contains(new_address)); println!(" - {address_url}{}", new_address.address()); diff --git a/sdk/examples/how_tos/accounts_and_addresses/list_addresses.rs b/sdk/examples/how_tos/accounts_and_addresses/list_addresses.rs index b4ef1f315f..ebcc539c71 100644 --- a/sdk/examples/how_tos/accounts_and_addresses/list_addresses.rs +++ b/sdk/examples/how_tos/accounts_and_addresses/list_addresses.rs @@ -21,7 +21,7 @@ async fn main() -> Result<()> { .await?; let account = wallet.get_account("Alice").await?; - for address in account.addresses().await? { + for address in account.addresses().await { println!("{}", address.address()); } diff --git a/sdk/examples/how_tos/nft_collection/01_mint_collection_nft.rs b/sdk/examples/how_tos/nft_collection/01_mint_collection_nft.rs index 456b444f40..aed4013de9 100644 --- a/sdk/examples/how_tos/nft_collection/01_mint_collection_nft.rs +++ b/sdk/examples/how_tos/nft_collection/01_mint_collection_nft.rs @@ -62,7 +62,7 @@ async fn main() -> Result<()> { MintNftParams::new() .with_immutable_metadata(get_immutable_metadata(index).to_bytes()) // The NFT address from the NFT we minted in mint_issuer_nft example - .with_issuer(issuer) + .with_issuer(issuer.clone()) }) .collect::>(); diff --git a/sdk/examples/how_tos/nfts/mint_nft.rs b/sdk/examples/how_tos/nfts/mint_nft.rs index 9f828a72b7..91b7c9cbaf 100644 --- a/sdk/examples/how_tos/nfts/mint_nft.rs +++ b/sdk/examples/how_tos/nfts/mint_nft.rs @@ -48,7 +48,7 @@ async fn main() -> Result<()> { account.sync(None).await?; // We send from the first address in the account. - let sender_address = *account.addresses().await?[0].address(); + let sender_address = account.first_address_bech32().await; // Set the stronghold password wallet @@ -66,10 +66,10 @@ async fn main() -> Result<()> { let nft_params = [MintNftParams::new() .try_with_address(NFT1_OWNER_ADDRESS)? - .try_with_sender(sender_address)? + .try_with_sender(sender_address.clone())? .with_metadata(NFT1_METADATA.as_bytes().to_vec()) .with_tag(NFT1_TAG.as_bytes().to_vec()) - .try_with_issuer(sender_address)? + .try_with_issuer(sender_address.clone())? .with_immutable_metadata(metadata.to_bytes())]; let transaction = account.mint_nfts(nft_params, None).await?; @@ -91,8 +91,8 @@ async fn main() -> Result<()> { let outputs = [ // address of the owner of the NFT NftOutputBuilder::new_with_amount(NFT2_AMOUNT, NftId::null()) - .add_unlock_condition(AddressUnlockCondition::new(sender_address)) - .add_feature(SenderFeature::new(sender_address)) + .add_unlock_condition(AddressUnlockCondition::new(sender_address.clone())) + .add_feature(SenderFeature::new(sender_address.clone())) .add_immutable_feature(IssuerFeature::new(sender_address)) .finish_output(token_supply)?, ]; diff --git a/sdk/examples/how_tos/outputs/features.rs b/sdk/examples/how_tos/outputs/features.rs index ca8367531e..97ae44ceb9 100644 --- a/sdk/examples/how_tos/outputs/features.rs +++ b/sdk/examples/how_tos/outputs/features.rs @@ -37,13 +37,13 @@ async fn main() -> Result<()> { let address = Address::try_from_bech32("rms1qpllaj0pyveqfkwxmnngz2c488hfdtmfrj3wfkgxtk4gtyrax0jaxzt70zy")?; let nft_output_builder = NftOutputBuilder::new_with_minimum_storage_deposit(rent_structure, NftId::null()) - .add_unlock_condition(AddressUnlockCondition::new(address)); + .add_unlock_condition(AddressUnlockCondition::new(address.clone())); let outputs = [ // with sender feature nft_output_builder .clone() - .add_feature(SenderFeature::new(address)) + .add_feature(SenderFeature::new(address.clone())) .finish_output(token_supply)?, // with issuer feature nft_output_builder diff --git a/sdk/examples/how_tos/outputs/unlock_conditions.rs b/sdk/examples/how_tos/outputs/unlock_conditions.rs index 9d28bff850..d2b3569da4 100644 --- a/sdk/examples/how_tos/outputs/unlock_conditions.rs +++ b/sdk/examples/how_tos/outputs/unlock_conditions.rs @@ -43,7 +43,7 @@ async fn main() -> Result<()> { let token_scheme = TokenScheme::Simple(SimpleTokenScheme::new(50, 0, 100)?); let basic_output_builder = BasicOutputBuilder::new_with_minimum_storage_deposit(rent_structure) - .add_unlock_condition(AddressUnlockCondition::new(address)); + .add_unlock_condition(AddressUnlockCondition::new(address.clone())); let account_output_builder = AccountOutputBuilder::new_with_minimum_storage_deposit(rent_structure, AccountId::null()); let foundry_output_builder = @@ -56,7 +56,7 @@ async fn main() -> Result<()> { basic_output_builder .clone() .add_unlock_condition(StorageDepositReturnUnlockCondition::new( - address, + address.clone(), 1000000, token_supply, )?) @@ -68,11 +68,11 @@ async fn main() -> Result<()> { .finish_output(token_supply)?, // with expiration unlock condition basic_output_builder - .add_unlock_condition(ExpirationUnlockCondition::new(address, 1)?) + .add_unlock_condition(ExpirationUnlockCondition::new(address.clone(), 1)?) .finish_output(token_supply)?, // with governor and state controller unlock condition account_output_builder - .add_unlock_condition(GovernorAddressUnlockCondition::new(address)) + .add_unlock_condition(GovernorAddressUnlockCondition::new(address.clone())) .add_unlock_condition(StateControllerAddressUnlockCondition::new(address)) .finish_output(token_supply)?, // with immutable account unlock condition diff --git a/sdk/examples/how_tos/simple_transaction/request_funds.rs b/sdk/examples/how_tos/simple_transaction/request_funds.rs index 9eba872978..6203f8829b 100644 --- a/sdk/examples/how_tos/simple_transaction/request_funds.rs +++ b/sdk/examples/how_tos/simple_transaction/request_funds.rs @@ -27,7 +27,7 @@ async fn main() -> Result<()> { let balance = account.sync(None).await?; println!("Account synced"); - let addresses = account.addresses().await?; + let addresses = account.addresses().await; let funds_before = balance.base_coin().available(); println!("Current available funds: {funds_before}"); diff --git a/sdk/examples/wallet/17_check_unlock_conditions.rs b/sdk/examples/wallet/17_check_unlock_conditions.rs index 6ffdb3e445..4c26147502 100644 --- a/sdk/examples/wallet/17_check_unlock_conditions.rs +++ b/sdk/examples/wallet/17_check_unlock_conditions.rs @@ -35,15 +35,15 @@ async fn main() -> Result<()> { let account_addresses = account .addresses() - .await? + .await .into_iter() - .map(|a| *a.address()) + .map(|a| a.into_bech32()) .collect::>(); println!("ADDRESSES:\n{:#?}", account_addresses); let output = BasicOutputBuilder::new_with_amount(AMOUNT) - .add_unlock_condition(AddressUnlockCondition::new(*account_addresses[0].as_ref())) + .add_unlock_condition(AddressUnlockCondition::new(account_addresses[0].as_ref().clone())) .finish_output(account.client().get_token_supply().await?)?; let controlled_by_account = if let [UnlockCondition::Address(address_unlock_condition)] = output diff --git a/sdk/examples/wallet/accounts.rs b/sdk/examples/wallet/accounts.rs index 21d9541bbe..28230f697b 100644 --- a/sdk/examples/wallet/accounts.rs +++ b/sdk/examples/wallet/accounts.rs @@ -88,7 +88,7 @@ async fn main() -> Result<()> { println!("New available funds: {}", balance.base_coin().available()); - let addresses = account2.addresses().await?; + let addresses = account2.addresses().await; println!("Number of addresses in {alias2}'s account: {}", addresses.len()); println!("{alias2}'s base coin balance:\n{:#?}", balance.base_coin()); diff --git a/sdk/examples/wallet/background_syncing.rs b/sdk/examples/wallet/background_syncing.rs index eda91075d0..0f8217feb8 100644 --- a/sdk/examples/wallet/background_syncing.rs +++ b/sdk/examples/wallet/background_syncing.rs @@ -35,7 +35,7 @@ async fn main() -> Result<()> { // Get or create new account let account = wallet.get_or_create_account("Alice").await?; - let addresses = account.addresses().await?; + let addresses = account.addresses().await; // Manually sync to ensure we have the correct funds to start with let balance = account.sync(None).await?; diff --git a/sdk/examples/wallet/getting_started.rs b/sdk/examples/wallet/getting_started.rs index 69468337fb..1ec1488ebf 100644 --- a/sdk/examples/wallet/getting_started.rs +++ b/sdk/examples/wallet/getting_started.rs @@ -49,7 +49,7 @@ async fn main() -> Result<()> { .finish() .await?; - let first_address = &account.addresses().await?[0]; + let first_address = &account.addresses().await[0]; println!("{}", first_address.address()); Ok(()) diff --git a/sdk/examples/wallet/offline_signing/0_generate_addresses.rs b/sdk/examples/wallet/offline_signing/0_generate_addresses.rs index 138bc23fb2..30357e2819 100644 --- a/sdk/examples/wallet/offline_signing/0_generate_addresses.rs +++ b/sdk/examples/wallet/offline_signing/0_generate_addresses.rs @@ -63,7 +63,7 @@ async fn main() -> Result<()> { async fn write_addresses_to_file(account: &Account) -> Result<()> { use tokio::io::AsyncWriteExt; - let addresses = account.addresses().await?; + let addresses = account.addresses().await; let json = serde_json::to_string_pretty(&addresses)?; let mut file = tokio::io::BufWriter::new(tokio::fs::File::create(ADDRESSES_FILE_PATH).await?); println!("example.addresses.json:\n{json}"); diff --git a/sdk/examples/wallet/spammer.rs b/sdk/examples/wallet/spammer.rs index 8624608603..4efb588156 100644 --- a/sdk/examples/wallet/spammer.rs +++ b/sdk/examples/wallet/spammer.rs @@ -47,7 +47,7 @@ async fn main() -> Result<()> { .await?; let account = wallet.get_or_create_account(ACCOUNT_ALIAS).await?; - let recv_address = *account.addresses().await?[0].address(); + let recv_address = account.first_address_bech32().await; println!("Recv address: {}", recv_address); // Ensure there are enough available funds for spamming. @@ -71,7 +71,7 @@ async fn main() -> Result<()> { println!("Creating unspent outputs..."); let transaction = account - .send_with_params(vec![SendParams::new(SEND_AMOUNT, recv_address)?; 127], None) + .send_with_params(vec![SendParams::new(SEND_AMOUNT, recv_address.clone())?; 127], None) .await?; wait_for_inclusion(&transaction.transaction_id, &account).await?; @@ -87,6 +87,7 @@ async fn main() -> Result<()> { for n in 0..num_simultaneous_txs { let account_clone = account.clone(); + let recv_address = recv_address.clone(); tasks.spawn(async move { println!("Thread {n}: sending {SEND_AMOUNT} coins to own address"); diff --git a/sdk/examples/wallet/split_funds.rs b/sdk/examples/wallet/split_funds.rs index 74a8c8a2a0..49f3be624d 100644 --- a/sdk/examples/wallet/split_funds.rs +++ b/sdk/examples/wallet/split_funds.rs @@ -47,7 +47,7 @@ async fn main() -> Result<()> { let _ = ensure_enough_addresses(&account, ADDRESSES_TO_SPLIT_FUNDS).await?; - let addresses = account.addresses().await?; + let addresses = account.addresses().await; println!("Total address count: {}", addresses.len()); sync_print_balance(&account).await?; @@ -120,12 +120,12 @@ async fn sync_print_balance(account: &Account) -> Result<()> { async fn ensure_enough_addresses(account: &Account, limit: usize) -> Result> { let alias = account.alias().await; - if account.addresses().await?.len() < limit { - let num_addresses_to_generate = limit - account.addresses().await?.len(); + if account.addresses().await.len() < limit { + let num_addresses_to_generate = limit - account.addresses().await.len(); println!("Generating {num_addresses_to_generate} addresses for account '{alias}'..."); account .generate_ed25519_addresses(num_addresses_to_generate as u32, None) .await?; } - account.addresses().await + Ok(account.addresses().await) } diff --git a/sdk/examples/wallet/storage.rs b/sdk/examples/wallet/storage.rs index 0fdf7c713b..98c3bcdf66 100644 --- a/sdk/examples/wallet/storage.rs +++ b/sdk/examples/wallet/storage.rs @@ -45,7 +45,7 @@ async fn main() -> Result<()> { .map(|address| address.into_bech32()) .collect::>(); - println!("Total address count:\n{:?}", account.addresses().await?.len()); + println!("Total address count:\n{:?}", account.addresses().await.len()); println!("ADDRESSES:\n{bech32_addresses:#?}"); sync_print_balance(&account).await?; @@ -59,14 +59,14 @@ async fn main() -> Result<()> { async fn generate_max_addresses(account: &Account, max: usize) -> Result> { let alias = account.alias().await; - if account.addresses().await?.len() < max { - let num_addresses_to_generate = max - account.addresses().await?.len(); + if account.addresses().await.len() < max { + let num_addresses_to_generate = max - account.addresses().await.len(); println!("Generating {num_addresses_to_generate} addresses for account '{alias}'..."); account .generate_ed25519_addresses(num_addresses_to_generate as u32, None) .await?; } - account.addresses().await + Ok(account.addresses().await) } async fn sync_print_balance(account: &Account) -> Result<()> { diff --git a/sdk/examples/wallet/wallet.rs b/sdk/examples/wallet/wallet.rs index 9c2562b8a7..1a06c18c22 100644 --- a/sdk/examples/wallet/wallet.rs +++ b/sdk/examples/wallet/wallet.rs @@ -83,8 +83,8 @@ async fn print_accounts(wallet: &Wallet) -> Result<()> { } async fn generate_addresses(account: &Account, max: usize) -> Result<()> { - if account.addresses().await?.len() < max { - let num_addresses_to_generate = max - account.addresses().await?.len(); + if account.addresses().await.len() < max { + let num_addresses_to_generate = max - account.addresses().await.len(); println!("Generating {num_addresses_to_generate} addresses ..."); let now = tokio::time::Instant::now(); account @@ -96,7 +96,7 @@ async fn generate_addresses(account: &Account, max: usize) -> Result<()> { } async fn print_addresses(account: &Account) -> Result<()> { - let addresses = account.addresses().await?; + let addresses = account.addresses().await; println!("{}'s addresses:", account.alias().await); for address in addresses { println!("- {}", address.address()); diff --git a/sdk/src/client/api/address.rs b/sdk/src/client/api/address.rs index 4fb96464e4..173de7289f 100644 --- a/sdk/src/client/api/address.rs +++ b/sdk/src/client/api/address.rs @@ -164,7 +164,7 @@ pub async fn search_address( } } Err(crate::client::Error::InputAddressNotFound { - address: address.to_bech32(bech32_hrp).to_string(), + address: address.clone().to_bech32(bech32_hrp).to_string(), range: format!("{range:?}"), }) } diff --git a/sdk/src/client/api/block_builder/input_selection/mod.rs b/sdk/src/client/api/block_builder/input_selection/mod.rs index 4baa80fc55..d20cb8b4be 100644 --- a/sdk/src/client/api/block_builder/input_selection/mod.rs +++ b/sdk/src/client/api/block_builder/input_selection/mod.rs @@ -81,6 +81,7 @@ impl InputSelection { AccountTransition::State, ))), Address::Nft(nft_address) => Ok(Some(Requirement::Nft(*nft_address.nft_id()))), + _ => todo!("What do we do here?"), } } diff --git a/sdk/src/client/api/block_builder/input_selection/remainder.rs b/sdk/src/client/api/block_builder/input_selection/remainder.rs index 5b2e39caa9..b8d9d4d5d8 100644 --- a/sdk/src/client/api/block_builder/input_selection/remainder.rs +++ b/sdk/src/client/api/block_builder/input_selection/remainder.rs @@ -23,7 +23,7 @@ impl InputSelection { // Gets the remainder address from configuration of finds one from the inputs. fn get_remainder_address(&self) -> Option<(Address, Option)> { if self.remainder_address.is_some() { - return self.remainder_address.map(|address| (address, None)); + return self.remainder_address.as_ref().map(|address| (address.clone(), None)); } for input in &self.selected_inputs { @@ -95,7 +95,7 @@ impl InputSelection { if amount > output_sdr_amount { let diff = amount - output_sdr_amount; let srd_output = BasicOutputBuilder::new_with_amount(diff) - .with_unlock_conditions([AddressUnlockCondition::new(address)]) + .with_unlock_conditions([AddressUnlockCondition::new(address.clone())]) .finish_output(self.protocol_parameters.token_supply())?; // TODO verify_storage_deposit ? @@ -132,7 +132,8 @@ impl InputSelection { let diff = inputs_sum - outputs_sum; let mut remainder_builder = BasicOutputBuilder::new_with_amount(diff); - remainder_builder = remainder_builder.add_unlock_condition(AddressUnlockCondition::new(remainder_address)); + remainder_builder = + remainder_builder.add_unlock_condition(AddressUnlockCondition::new(remainder_address.clone())); if let Some(native_tokens) = native_tokens_diff { log::debug!("Adding {native_tokens:?} to remainder output for {remainder_address:?}"); diff --git a/sdk/src/client/api/block_builder/input_selection/requirement/amount.rs b/sdk/src/client/api/block_builder/input_selection/requirement/amount.rs index 549bc43e62..96f31d9162 100644 --- a/sdk/src/client/api/block_builder/input_selection/requirement/amount.rs +++ b/sdk/src/client/api/block_builder/input_selection/requirement/amount.rs @@ -49,7 +49,7 @@ pub(crate) fn amount_sums( inputs_sum += selected_input.output.amount(); if let Some(sdruc) = sdruc_not_expired(&selected_input.output, slot_index) { - *inputs_sdr.entry(*sdruc.return_address()).or_default() += sdruc.amount(); + *inputs_sdr.entry(sdruc.return_address().clone()).or_default() += sdruc.amount(); } } @@ -58,7 +58,7 @@ pub(crate) fn amount_sums( if let Output::Basic(output) = output { if let Some(address) = output.simple_deposit_address() { - *outputs_sdr.entry(*address).or_default() += output.amount(); + *outputs_sdr.entry(address.clone()).or_default() += output.amount(); } } } @@ -144,10 +144,10 @@ impl AmountSelection { if input_sdr > output_sdr { let diff = input_sdr - output_sdr; self.outputs_sum += diff; - *self.outputs_sdr.entry(*sdruc.return_address()).or_default() += sdruc.amount(); + *self.outputs_sdr.entry(sdruc.return_address().clone()).or_default() += sdruc.amount(); } - *self.inputs_sdr.entry(*sdruc.return_address()).or_default() += sdruc.amount(); + *self.inputs_sdr.entry(sdruc.return_address().clone()).or_default() += sdruc.amount(); } self.inputs_sum += input.output.amount(); diff --git a/sdk/src/client/api/block_builder/input_selection/requirement/ed25519.rs b/sdk/src/client/api/block_builder/input_selection/requirement/ed25519.rs index f9d56c91e1..a3cbb6693a 100644 --- a/sdk/src/client/api/block_builder/input_selection/requirement/ed25519.rs +++ b/sdk/src/client/api/block_builder/input_selection/requirement/ed25519.rs @@ -68,13 +68,13 @@ impl InputSelection { /// Fulfills an ed25519 sender requirement by selecting an available input that unlocks its address. pub(crate) fn fulfill_ed25519_requirement( &mut self, - address: Address, + address: &Address, ) -> Result)>, Error> { // Checks if the requirement is already fulfilled. if let Some(input) = self .selected_inputs .iter() - .find(|input| self.selected_unlocks_ed25519_address(input, &address)) + .find(|input| self.selected_unlocks_ed25519_address(input, address)) { log::debug!( "{address:?} sender requirement already fulfilled by {:?}", @@ -88,14 +88,14 @@ impl InputSelection { .available_inputs .iter() .enumerate() - .find(|(_, input)| input.output.is_basic() && self.available_has_ed25519_address(input, &address).0) + .find(|(_, input)| input.output.is_basic() && self.available_has_ed25519_address(input, address).0) { Some((index, None)) } else { // Otherwise, checks if the requirement can be fulfilled by a non-basic output. self.available_inputs.iter().enumerate().find_map(|(index, input)| { if !input.output.is_basic() { - if let (true, account_transition) = self.available_has_ed25519_address(input, &address) { + if let (true, account_transition) = self.available_has_ed25519_address(input, address) { Some((index, account_transition)) } else { None @@ -119,7 +119,7 @@ impl InputSelection { Ok(vec![(input, account_transition)]) } - None => Err(Error::UnfulfillableRequirement(Requirement::Ed25519(address))), + None => Err(Error::UnfulfillableRequirement(Requirement::Ed25519(address.clone()))), } } } diff --git a/sdk/src/client/api/block_builder/input_selection/requirement/issuer.rs b/sdk/src/client/api/block_builder/input_selection/requirement/issuer.rs index 425811c5fa..696c6f1d6f 100644 --- a/sdk/src/client/api/block_builder/input_selection/requirement/issuer.rs +++ b/sdk/src/client/api/block_builder/input_selection/requirement/issuer.rs @@ -12,14 +12,14 @@ impl InputSelection { /// Potentially converts the error for a more accurate one. pub(crate) fn fulfill_issuer_requirement( &mut self, - address: Address, + address: &Address, ) -> Result)>, Error> { log::debug!("Treating {address:?} issuer requirement as a sender requirement"); match self.fulfill_sender_requirement(address) { Ok(res) => Ok(res), Err(Error::UnfulfillableRequirement(Requirement::Sender(_))) => { - Err(Error::UnfulfillableRequirement(Requirement::Issuer(address))) + Err(Error::UnfulfillableRequirement(Requirement::Issuer(address.clone()))) } Err(e) => Err(e), } diff --git a/sdk/src/client/api/block_builder/input_selection/requirement/mod.rs b/sdk/src/client/api/block_builder/input_selection/requirement/mod.rs index 383e17a1cf..2fdd181b6a 100644 --- a/sdk/src/client/api/block_builder/input_selection/requirement/mod.rs +++ b/sdk/src/client/api/block_builder/input_selection/requirement/mod.rs @@ -22,7 +22,7 @@ use crate::{ }; /// A requirement, imposed by outputs, that needs to be resolved by selected inputs. -#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq)] pub enum Requirement { /// Sender requirement. Sender(Address), @@ -52,9 +52,9 @@ impl InputSelection { log::debug!("Fulfilling requirement {requirement:?}"); match requirement { - Requirement::Sender(address) => self.fulfill_sender_requirement(address), - Requirement::Issuer(address) => self.fulfill_issuer_requirement(address), - Requirement::Ed25519(address) => self.fulfill_ed25519_requirement(address), + Requirement::Sender(address) => self.fulfill_sender_requirement(&address), + Requirement::Issuer(address) => self.fulfill_issuer_requirement(&address), + Requirement::Ed25519(address) => self.fulfill_ed25519_requirement(&address), Requirement::Foundry(foundry_id) => self.fulfill_foundry_requirement(foundry_id), Requirement::Account(account_id, account_transition) => { self.fulfill_account_requirement(account_id, account_transition) @@ -127,7 +127,7 @@ impl InputSelection { // Add a sender requirement if the sender feature is present. if let Some(sender) = output.features().and_then(Features::sender) { - let requirement = Requirement::Sender(*sender.address()); + let requirement = Requirement::Sender(sender.address().clone()); log::debug!("Adding {requirement:?} from output"); self.requirements.push(requirement); } @@ -135,7 +135,7 @@ impl InputSelection { // Add an issuer requirement if the issuer feature is present and the chain output is created. if is_created { if let Some(issuer) = output.immutable_features().and_then(Features::issuer) { - let requirement = Requirement::Issuer(*issuer.address()); + let requirement = Requirement::Issuer(issuer.address().clone()); log::debug!("Adding {requirement:?} from output"); self.requirements.push(requirement); } diff --git a/sdk/src/client/api/block_builder/input_selection/requirement/sender.rs b/sdk/src/client/api/block_builder/input_selection/requirement/sender.rs index 9fc5f5267f..3f1954526f 100644 --- a/sdk/src/client/api/block_builder/input_selection/requirement/sender.rs +++ b/sdk/src/client/api/block_builder/input_selection/requirement/sender.rs @@ -11,7 +11,7 @@ impl InputSelection { /// Fulfills a sender requirement by selecting an available input that unlocks its address. pub(crate) fn fulfill_sender_requirement( &mut self, - address: Address, + address: &Address, ) -> Result)>, Error> { match address { Address::Ed25519(_) => { @@ -20,7 +20,7 @@ impl InputSelection { match self.fulfill_ed25519_requirement(address) { Ok(res) => Ok(res), Err(Error::UnfulfillableRequirement(Requirement::Ed25519(_))) => { - Err(Error::UnfulfillableRequirement(Requirement::Sender(address))) + Err(Error::UnfulfillableRequirement(Requirement::Sender(address.clone()))) } Err(e) => Err(e), } @@ -32,7 +32,7 @@ impl InputSelection { match self.fulfill_account_requirement(account_address.into_account_id(), AccountTransition::State) { Ok(res) => Ok(res), Err(Error::UnfulfillableRequirement(Requirement::Account(_, _))) => { - Err(Error::UnfulfillableRequirement(Requirement::Sender(address))) + Err(Error::UnfulfillableRequirement(Requirement::Sender(address.clone()))) } Err(e) => Err(e), } @@ -43,11 +43,12 @@ impl InputSelection { match self.fulfill_nft_requirement(nft_address.into_nft_id()) { Ok(res) => Ok(res), Err(Error::UnfulfillableRequirement(Requirement::Nft(_))) => { - Err(Error::UnfulfillableRequirement(Requirement::Sender(address))) + Err(Error::UnfulfillableRequirement(Requirement::Sender(address.clone()))) } Err(e) => Err(e), } } + _ => todo!("What do we do here?"), } } } diff --git a/sdk/src/client/api/types.rs b/sdk/src/client/api/types.rs index c1dba38224..b5f2cc036f 100644 --- a/sdk/src/client/api/types.rs +++ b/sdk/src/client/api/types.rs @@ -168,7 +168,7 @@ impl From<&RemainderData> for RemainderDataDto { Self { output: OutputDto::from(&remainder.output), chain: remainder.chain, - address: remainder.address, + address: remainder.address.clone(), } } } diff --git a/sdk/src/client/secret/ledger_nano.rs b/sdk/src/client/secret/ledger_nano.rs index 409ab571a0..d76396a270 100644 --- a/sdk/src/client/secret/ledger_nano.rs +++ b/sdk/src/client/secret/ledger_nano.rs @@ -544,6 +544,7 @@ fn merge_unlocks( merged_unlocks.push(Unlock::Reference(ReferenceUnlock::new(*block_index as u16)?)); } Address::Nft(_nft) => merged_unlocks.push(Unlock::Nft(NftUnlock::new(*block_index as u16)?)), + _ => todo!("What do we do here?"), }, None => { // We can only sign ed25519 addresses and block_indexes needs to contain the account or nft @@ -617,7 +618,7 @@ mod tests { .unwrap(); assert_eq!( - addresses[0].to_bech32_unchecked("atoi").to_string(), + addresses[0].clone().to_bech32_unchecked("atoi").to_string(), "atoi1qqdnv60ryxynaeyu8paq3lp9rkll7d7d92vpumz88fdj4l0pn5mru50gvd8" ); } diff --git a/sdk/src/client/secret/mod.rs b/sdk/src/client/secret/mod.rs index 1815419905..62008273d9 100644 --- a/sdk/src/client/secret/mod.rs +++ b/sdk/src/client/secret/mod.rs @@ -534,6 +534,7 @@ where blocks.push(Unlock::Reference(ReferenceUnlock::new(*block_index as u16)?)); } Address::Nft(_nft) => blocks.push(Unlock::Nft(NftUnlock::new(*block_index as u16)?)), + _ => todo!("What do we do here?"), }, None => { // We can only sign ed25519 addresses and block_indexes needs to contain the account or nft diff --git a/sdk/src/client/utils.rs b/sdk/src/client/utils.rs index bc0b8ec97a..c864748105 100644 --- a/sdk/src/client/utils.rs +++ b/sdk/src/client/utils.rs @@ -31,6 +31,8 @@ pub fn bech32_to_hex(bech32: impl ConvertTo) -> Result { Address::Ed25519(ed) => ed.to_string(), Address::Account(account) => account.to_string(), Address::Nft(nft) => nft.to_string(), + Address::ImplicitAccountCreation(implicit) => implicit.to_string(), + Address::Restricted(restricted) => restricted.to_string(), }) } diff --git a/sdk/src/types/block/address/bech32.rs b/sdk/src/types/block/address/bech32.rs index d9dfaec2d8..2c5b55e141 100644 --- a/sdk/src/types/block/address/bech32.rs +++ b/sdk/src/types/block/address/bech32.rs @@ -18,9 +18,11 @@ use packable::{ use crate::types::block::{address::Address, ConvertTo, Error}; +const HRP_MAX: u8 = 83; + #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct Hrp { - inner: [u8; 83], + inner: [u8; HRP_MAX as usize], len: u8, } @@ -38,7 +40,7 @@ impl Hrp { /// Convert a string to an Hrp without checking validity. pub const fn from_str_unchecked(hrp: &str) -> Self { let len = hrp.len(); - let mut bytes = [0; 83]; + let mut bytes = [0; HRP_MAX as usize]; let hrp = hrp.as_bytes(); let mut i = 0; while i < len { @@ -57,8 +59,8 @@ impl FromStr for Hrp { fn from_str(hrp: &str) -> Result { let len = hrp.len(); - if hrp.is_ascii() && len <= 83 { - let mut bytes = [0; 83]; + if hrp.is_ascii() && len <= HRP_MAX as usize { + let mut bytes = [0; HRP_MAX as usize]; bytes[..len].copy_from_slice(hrp.as_bytes()); Ok(Self { inner: bytes, @@ -99,7 +101,7 @@ impl Packable for Hrp { ) -> Result> { let len = u8::unpack::<_, VERIFY>(unpacker, visitor).coerce()?; - if len > 83 { + if len > HRP_MAX { return Err(UnpackError::Packable(Error::InvalidBech32Hrp( "hrp len above 83".to_string(), ))); @@ -147,7 +149,7 @@ impl + Send> ConvertTo for T { } /// An address and its network type. -#[derive(Copy, Clone, Eq, PartialEq, Hash, AsRef, Deref, Ord, PartialOrd)] +#[derive(Clone, Eq, PartialEq, Hash, AsRef, Deref, Ord, PartialOrd)] pub struct Bech32Address { pub(crate) hrp: Hrp, #[as_ref] @@ -251,7 +253,7 @@ impl PartialEq for Bech32Address { impl> From for Address { fn from(value: T) -> Self { - value.borrow().inner + value.borrow().inner.clone() } } diff --git a/sdk/src/types/block/address/ed25519.rs b/sdk/src/types/block/address/ed25519.rs index 2adc90ed19..2d9064f837 100644 --- a/sdk/src/types/block/address/ed25519.rs +++ b/sdk/src/types/block/address/ed25519.rs @@ -3,13 +3,17 @@ use core::str::FromStr; -use crypto::signatures::ed25519::PublicKey; +use crypto::{ + hashes::{blake2b::Blake2b256, Digest}, + signatures::ed25519::PublicKey, +}; use derive_more::{AsRef, Deref, From}; +use packable::Packable; use crate::types::block::Error; /// An Ed25519 address. -#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, From, AsRef, Deref, packable::Packable)] +#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, From, AsRef, Deref, Packable)] #[as_ref(forward)] pub struct Ed25519Address([u8; Self::LENGTH]); @@ -24,6 +28,11 @@ impl Ed25519Address { pub fn new(address: [u8; Self::LENGTH]) -> Self { Self::from(address) } + + /// Creates a new [`Ed25519Address`] from the bytes of a [`PublicKey`]. + pub fn from_public_key_bytes(public_key_bytes: [u8; PublicKey::LENGTH]) -> Result { + Ok(Self::new(Blake2b256::digest(public_key_bytes).try_into()?)) + } } impl FromStr for Ed25519Address { @@ -53,7 +62,6 @@ pub(crate) mod dto { use super::*; use crate::utils::serde::prefix_hex_bytes; - /// Describes an Ed25519 address. #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] struct Ed25519AddressDto { diff --git a/sdk/src/types/block/address/implicit_account_creation.rs b/sdk/src/types/block/address/implicit_account_creation.rs new file mode 100644 index 0000000000..5330c5fbd6 --- /dev/null +++ b/sdk/src/types/block/address/implicit_account_creation.rs @@ -0,0 +1,63 @@ +// Copyright 2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use derive_more::{AsRef, Deref, Display, From, FromStr}; +use packable::Packable; + +use crate::types::block::address::Ed25519Address; + +/// An implicit account creation address that can be used to transition an account. +#[derive(Copy, Clone, Debug, Display, Eq, PartialEq, Ord, PartialOrd, Hash, FromStr, AsRef, Deref, From, Packable)] +#[as_ref(forward)] +pub struct ImplicitAccountCreationAddress(Ed25519Address); + +impl ImplicitAccountCreationAddress { + /// The [`Address`](crate::types::block::address::Address) kind of an [`ImplicitAccountCreationAddress`]. + pub const KIND: u8 = 24; + /// The length of an [`ImplicitAccountCreationAddress`]. + pub const LENGTH: usize = Ed25519Address::LENGTH; + + /// Creates a new [`ImplicitAccountCreationAddress`]. + #[inline(always)] + pub fn new(address: [u8; Self::LENGTH]) -> Self { + Self(Ed25519Address::new(address)) + } +} + +#[cfg(feature = "serde")] +pub(crate) mod dto { + use serde::{Deserialize, Serialize}; + + use super::*; + use crate::utils::serde::prefix_hex_bytes; + + #[derive(Serialize, Deserialize)] + #[serde(rename_all = "camelCase")] + struct ImplicitAccountCreationAddressDto { + #[serde(rename = "type")] + kind: u8, + #[serde(with = "prefix_hex_bytes")] + pub_key_hash: [u8; Ed25519Address::LENGTH], + } + + impl From<&ImplicitAccountCreationAddress> for ImplicitAccountCreationAddressDto { + fn from(value: &ImplicitAccountCreationAddress) -> Self { + Self { + kind: ImplicitAccountCreationAddress::KIND, + pub_key_hash: *value.0, + } + } + } + + impl From for ImplicitAccountCreationAddress { + fn from(value: ImplicitAccountCreationAddressDto) -> Self { + Self::new(value.pub_key_hash) + } + } + + impl_serde_typed_dto!( + ImplicitAccountCreationAddress, + ImplicitAccountCreationAddressDto, + "implicit account creation address" + ); +} diff --git a/sdk/src/types/block/address/mod.rs b/sdk/src/types/block/address/mod.rs index a870d399b7..91a2f357de 100644 --- a/sdk/src/types/block/address/mod.rs +++ b/sdk/src/types/block/address/mod.rs @@ -4,15 +4,22 @@ mod account; mod bech32; mod ed25519; +mod implicit_account_creation; mod nft; +mod restricted; + +use alloc::boxed::Box; use derive_more::From; +use packable::Packable; pub use self::{ account::AccountAddress, bech32::{Bech32Address, Hrp}, ed25519::Ed25519Address, + implicit_account_creation::ImplicitAccountCreationAddress, nft::NftAddress, + restricted::{AddressCapabilities, AddressCapabilityFlag, RestrictedAddress}, }; use crate::types::block::{ output::{Output, OutputId}, @@ -23,7 +30,7 @@ use crate::types::block::{ }; /// A generic address supporting different address kinds. -#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, From, packable::Packable)] +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, From, Packable)] #[packable(tag_type = u8, with_error = Error::InvalidAddressKind)] #[packable(unpack_error = Error)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(untagged))] @@ -37,6 +44,19 @@ pub enum Address { /// An NFT address. #[packable(tag = NftAddress::KIND)] Nft(NftAddress), + /// An implicit account creation address. + #[packable(tag = ImplicitAccountCreationAddress::KIND)] + ImplicitAccountCreation(ImplicitAccountCreationAddress), + /// An address with restricted capabilities. + #[packable(tag = RestrictedAddress::KIND)] + #[from(ignore)] + Restricted(Box), +} + +impl From for Address { + fn from(value: RestrictedAddress) -> Self { + Self::Restricted(value.into()) + } } impl core::fmt::Debug for Address { @@ -45,6 +65,8 @@ impl core::fmt::Debug for Address { Self::Ed25519(address) => address.fmt(f), Self::Account(address) => address.fmt(f), Self::Nft(address) => address.fmt(f), + Self::ImplicitAccountCreation(address) => address.fmt(f), + Self::Restricted(address) => address.fmt(f), } } } @@ -56,6 +78,8 @@ impl Address { Self::Ed25519(_) => Ed25519Address::KIND, Self::Account(_) => AccountAddress::KIND, Self::Nft(_) => NftAddress::KIND, + Self::ImplicitAccountCreation(_) => ImplicitAccountCreationAddress::KIND, + Self::Restricted(_) => RestrictedAddress::KIND, } } @@ -134,7 +158,7 @@ impl Address { return Err(TransactionFailureReason::InvalidUnlockBlockSignature); } - context.unlocked_addresses.insert(*self); + context.unlocked_addresses.insert(self.clone()); } (Self::Ed25519(_ed25519_address), Unlock::Reference(_unlock)) => { // TODO actually check that it was unlocked by the same signature. @@ -188,25 +212,15 @@ pub trait ToBech32Ext: Sized { } impl> ToBech32Ext for T { - /// Try to encode this address to a bech32 string with the given Human Readable Part as prefix. fn try_to_bech32(self, hrp: impl ConvertTo) -> Result { Bech32Address::try_new(hrp, self) } - /// Encodes this address to a bech32 string with the given Human Readable Part as prefix. fn to_bech32(self, hrp: Hrp) -> Bech32Address { Bech32Address::new(hrp, self) } - /// Encodes this address to a bech32 string with the given Human Readable Part as prefix without checking - /// validity. fn to_bech32_unchecked(self, hrp: impl ConvertTo) -> Bech32Address { Bech32Address::new(hrp.convert_unchecked(), self) } } - -impl From<&Self> for Address { - fn from(value: &Self) -> Self { - *value - } -} diff --git a/sdk/src/types/block/address/restricted.rs b/sdk/src/types/block/address/restricted.rs new file mode 100644 index 0000000000..1c6485e898 --- /dev/null +++ b/sdk/src/types/block/address/restricted.rs @@ -0,0 +1,327 @@ +// Copyright 2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use alloc::boxed::Box; + +use derive_more::Deref; +use getset::Getters; +use packable::{error::UnpackErrorExt, prefix::BoxedSlicePrefix, Packable, PackableExt}; + +use super::Address; +use crate::types::block::Error; + +#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Getters, Packable)] +#[getset(get = "pub")] +pub struct RestrictedAddress { + address: Address, + allowed_capabilities: AddressCapabilities, +} + +impl RestrictedAddress { + /// The [`Address`](crate::types::block::address::Address) kind of a [`RestrictedAddress`]. + pub const KIND: u8 = 40; + + /// Creates a new [`RestrictedAddress`] address from an [`Address`] with default allowed capabilities. + #[inline(always)] + pub fn new(address: impl Into
) -> Result { + let address = address.into(); + if matches!(address, Address::Restricted(_)) { + return Err(Error::InvalidAddressKind(Self::KIND)); + } + Ok(Self { + address, + allowed_capabilities: Default::default(), + }) + } + + /// Sets the allowed capabilities flags. + #[inline(always)] + pub fn with_allowed_capabilities(mut self, allowed_capabilities: impl Into) -> Self { + self.allowed_capabilities = allowed_capabilities.into(); + self + } + + /// Sets the allowed capabilities flags. + #[inline(always)] + pub fn set_allowed_capabilities(&mut self, allowed_capabilities: impl Into) -> &mut Self { + self.allowed_capabilities = allowed_capabilities.into(); + self + } +} + +impl TryFrom
for RestrictedAddress { + type Error = Error; + + fn try_from(value: Address) -> Result { + Self::new(value) + } +} + +impl core::fmt::Display for RestrictedAddress { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{}", prefix_hex::encode(self.pack_to_vec())) + } +} + +/// All possible capabilities that an [`Address`] can have. +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[non_exhaustive] +pub enum AddressCapabilityFlag { + /// Can receive Outputs with Native Tokens. + OutputsWithNativeTokens, + /// Can receive Outputs with Mana. + OutputsWithMana, + /// Can receive Outputs with a Timelock Unlock Condition. + OutputsWithTimelock, + /// Can receive Outputs with an Expiration Unlock Condition. + OutputsWithExpiration, + /// Can receive Outputs with a Storage Deposit Return Unlock Condition. + OutputsWithStorageDepositReturn, + /// Can receive Account Outputs. + AccountOutputs, + /// Can receive NFT Outputs. + NftOutputs, + /// Can receive Delegation Outputs. + DelegationOutputs, +} + +impl AddressCapabilityFlag { + const OUTPUTS_WITH_NATIVE_TOKENS: u8 = 0b00000001; + const OUTPUTS_WITH_MANA: u8 = 0b00000010; + const OUTPUTS_WITH_TIMELOCK: u8 = 0b00000100; + const OUTPUTS_WITH_EXPIRATION: u8 = 0b00001000; + const OUTPUTS_WITH_STORAGE_DEPOSIT_RETURN: u8 = 0b00010000; + const ACCOUNT_OUTPUTS: u8 = 0b00100000; + const NFT_OUTPUTS: u8 = 0b01000000; + const DELEGATION_OUTPUTS: u8 = 0b10000000; + + /// Converts the flag into the byte representation. + pub fn as_byte(&self) -> u8 { + match self { + Self::OutputsWithNativeTokens => Self::OUTPUTS_WITH_NATIVE_TOKENS, + Self::OutputsWithMana => Self::OUTPUTS_WITH_MANA, + Self::OutputsWithTimelock => Self::OUTPUTS_WITH_TIMELOCK, + Self::OutputsWithExpiration => Self::OUTPUTS_WITH_EXPIRATION, + Self::OutputsWithStorageDepositReturn => Self::OUTPUTS_WITH_STORAGE_DEPOSIT_RETURN, + Self::AccountOutputs => Self::ACCOUNT_OUTPUTS, + Self::NftOutputs => Self::NFT_OUTPUTS, + Self::DelegationOutputs => Self::DELEGATION_OUTPUTS, + } + } + + /// Returns the index in [`AddressCapabilities`] to which this flag is applied. + pub fn index(&self) -> usize { + match self { + Self::OutputsWithNativeTokens + | Self::OutputsWithMana + | Self::OutputsWithTimelock + | Self::OutputsWithExpiration + | Self::OutputsWithStorageDepositReturn + | Self::AccountOutputs + | Self::NftOutputs + | Self::DelegationOutputs => 0, + } + } + + /// Returns an iterator over all flags. + pub fn all() -> impl Iterator { + [ + Self::OutputsWithNativeTokens, + Self::OutputsWithMana, + Self::OutputsWithTimelock, + Self::OutputsWithExpiration, + Self::OutputsWithStorageDepositReturn, + Self::AccountOutputs, + Self::NftOutputs, + Self::DelegationOutputs, + ] + .into_iter() + } +} + +/// A list of bitflags that represent the capabilities of an [`Address`]. +/// If an output is created by a transaction with an +/// [`UnlockCondition`](crate::types::block::output::UnlockCondition) containing a [`RestrictedAddress`], the +/// transaction is valid only if the specified conditions, corresponding to the [`AddressCapabilityFlag`]s, hold for +/// that output. +#[derive(Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash, Deref)] +#[repr(transparent)] +pub struct AddressCapabilities(BoxedSlicePrefix); + +impl AddressCapabilities { + /// Returns an [`AddressCapabilities`] with every possible flag enabled. + pub fn all() -> Self { + let mut res = Self::default(); + res.set_all(); + res + } + + /// Returns an [`AddressCapabilities`] with every possible flag disabled. + pub fn none() -> Self { + Self::default() + } + + /// Returns whether every possible [`AddressCapabilityFlag`] is enabled. + pub fn is_all(&self) -> bool { + AddressCapabilityFlag::all().all(|flag| self.has_capability(flag)) + } + + /// Returns whether every possible [`AddressCapabilityFlag`] is disabled. + pub fn is_none(&self) -> bool { + self.0.iter().all(|b| 0.eq(b)) + } + + /// Enables every possible [`AddressCapabilityFlag`]. + pub fn set_all(&mut self) -> &mut Self { + for flag in AddressCapabilityFlag::all() { + self.add_capability(flag); + } + self + } + + /// Disabled every possible [`AddressCapabilityFlag`]. + pub fn set_none(&mut self) -> &mut Self { + *self = Default::default(); + self + } + + /// Enables a given [`AddressCapabilityFlag`]. + pub fn add_capability(&mut self, flag: AddressCapabilityFlag) -> &mut Self { + if self.0.len() <= flag.index() { + let mut v = Box::<[_]>::from(self.0.clone()).into_vec(); + v.resize(flag.index() + 1, 0); + // Unwrap: safe because the indexes are within u8 bounds + self.0 = v.into_boxed_slice().try_into().unwrap(); + } + self.0[flag.index()] |= flag.as_byte(); + self + } + + /// Enables a given set of [`AddressCapabilityFlag`]s. + pub fn add_capabilities(&mut self, flags: impl IntoIterator) -> &mut Self { + for flag in flags { + self.add_capability(flag); + } + self + } + + /// Enables a given set of [`AddressCapabilityFlag`]s. + pub fn with_capabilities(mut self, flags: impl IntoIterator) -> Self { + self.add_capabilities(flags); + self + } + + /// Enables a given set of [`AddressCapabilityFlag`]s. + pub fn set_capabilities(&mut self, flags: impl IntoIterator) -> &mut Self { + *self = Self::default().with_capabilities(flags); + self + } + + /// Returns whether a given [`AddressCapabilityFlag`] is enabled. + pub fn has_capability(&self, flag: AddressCapabilityFlag) -> bool { + self.0 + .get(flag.index()) + .map(|byte| byte & flag.as_byte() == flag.as_byte()) + .unwrap_or_default() + } + + /// Returns whether a given set of [`AddressCapabilityFlag`]s are enabled. + pub fn has_capabilities(&self, flags: impl IntoIterator) -> bool { + flags.into_iter().all(|flag| self.has_capability(flag)) + } + + /// Returns an iterator over all enabled [`AddressCapabilityFlag`]s. + pub fn capabilities_iter(&self) -> impl Iterator + '_ { + self.0.iter().enumerate().flat_map(|(idx, byte)| { + AddressCapabilityFlag::all().filter(move |f| (idx == f.index() && byte & f.as_byte() == f.as_byte())) + }) + } +} + +impl> From for AddressCapabilities { + fn from(value: I) -> Self { + Self::default().with_capabilities(value) + } +} + +impl Packable for AddressCapabilities { + type UnpackError = Error; + type UnpackVisitor = (); + + fn pack(&self, packer: &mut P) -> Result<(), P::Error> { + if !self.is_none() { + self.0.pack(packer)?; + } else { + 0_u8.pack(packer)?; + } + Ok(()) + } + + fn unpack( + unpacker: &mut U, + visitor: &Self::UnpackVisitor, + ) -> Result> { + use packable::prefix::UnpackPrefixError; + Ok(Self( + BoxedSlicePrefix::unpack::<_, VERIFY>(unpacker, visitor) + // TODO: not sure if this is the best way to do this + .map_packable_err(|e| match e { + UnpackPrefixError::Item(i) | UnpackPrefixError::Prefix(i) => i, + }) + .coerce()?, + )) + } +} + +#[cfg(feature = "serde")] +pub(crate) mod dto { + use alloc::boxed::Box; + + use serde::{Deserialize, Serialize}; + + use super::*; + use crate::utils::serde::prefix_hex_bytes; + + #[derive(Serialize, Deserialize)] + #[serde(rename_all = "camelCase")] + pub struct RestrictedAddressDto { + #[serde(rename = "type")] + kind: u8, + pub address: Address, + #[serde(with = "prefix_hex_bytes")] + pub allowed_capabilities: Box<[u8]>, + } + + impl core::ops::Deref for RestrictedAddressDto { + type Target = Address; + + fn deref(&self) -> &Self::Target { + &self.address + } + } + + impl From<&RestrictedAddress> for RestrictedAddressDto { + fn from(value: &RestrictedAddress) -> Self { + Self { + kind: RestrictedAddress::KIND, + address: value.address.clone(), + allowed_capabilities: value.allowed_capabilities.iter().copied().collect(), + } + } + } + + impl TryFrom for RestrictedAddress { + type Error = Error; + + fn try_from(value: RestrictedAddressDto) -> Result { + Ok(Self::new(value.address)?.with_allowed_capabilities(AddressCapabilities( + value + .allowed_capabilities + .try_into() + .map_err(Error::InvalidAddressCapabilitiesCount)?, + ))) + } + } + + impl_serde_typed_dto!(RestrictedAddress, RestrictedAddressDto, "restricted address"); +} diff --git a/sdk/src/types/block/error.rs b/sdk/src/types/block/error.rs index bc889c0898..10368d0a33 100644 --- a/sdk/src/types/block/error.rs +++ b/sdk/src/types/block/error.rs @@ -80,6 +80,7 @@ pub enum Error { InvalidInputCount(>::Error), InvalidInputOutputIndex(>::Error), InvalidBech32Hrp(String), + InvalidAddressCapabilitiesCount(>::Error), InvalidBlockWrapperLength(usize), InvalidStateMetadataLength(>::Error), InvalidManaValue(u64), @@ -212,6 +213,7 @@ impl fmt::Display for Error { Self::InvalidAddressKind(k) => write!(f, "invalid address kind: {k}"), Self::InvalidAccountIndex(index) => write!(f, "invalid account index: {index}"), Self::InvalidBech32Hrp(err) => write!(f, "invalid bech32 hrp: {err}"), + Self::InvalidAddressCapabilitiesCount(e) => write!(f, "invalid capabilities count: {e}"), Self::InvalidBlockKind(k) => write!(f, "invalid block kind: {k}"), Self::InvalidRewardInputIndex(idx) => write!(f, "invalid reward input index: {idx}"), Self::InvalidStorageDepositAmount(amount) => { diff --git a/sdk/src/types/block/output/account.rs b/sdk/src/types/block/output/account.rs index f4ba0d9a7d..337f301c69 100644 --- a/sdk/src/types/block/output/account.rs +++ b/sdk/src/types/block/output/account.rs @@ -971,8 +971,8 @@ mod tests { let builder = AccountOutput::build_with_amount(100, account_id) .add_native_token(NativeToken::new(TokenId::from(foundry_id), 1000).unwrap()) - .add_unlock_condition(gov_address) - .add_unlock_condition(state_address) + .add_unlock_condition(gov_address.clone()) + .add_unlock_condition(state_address.clone()) .with_features(rand_allowed_features(AccountOutput::ALLOWED_FEATURES)) .with_immutable_features(rand_allowed_features(AccountOutput::ALLOWED_IMMUTABLE_FEATURES)); test_split_dto(builder); diff --git a/sdk/src/types/block/output/basic.rs b/sdk/src/types/block/output/basic.rs index d557b28b1f..b5bd4e0127 100644 --- a/sdk/src/types/block/output/basic.rs +++ b/sdk/src/types/block/output/basic.rs @@ -513,7 +513,7 @@ mod tests { let builder = BasicOutput::build_with_amount(100) .add_native_token(NativeToken::new(TokenId::from(foundry_id), 1000).unwrap()) - .add_unlock_condition(address) + .add_unlock_condition(address.clone()) .with_features(rand_allowed_features(BasicOutput::ALLOWED_FEATURES)); test_split_dto(builder); diff --git a/sdk/src/types/block/output/feature/issuer.rs b/sdk/src/types/block/output/feature/issuer.rs index 7e7b2b1d78..909239b883 100644 --- a/sdk/src/types/block/output/feature/issuer.rs +++ b/sdk/src/types/block/output/feature/issuer.rs @@ -6,7 +6,7 @@ use derive_more::From; use crate::types::block::address::Address; /// Identifies the validated issuer of the UTXO state machine. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, From, packable::Packable)] +#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, From, packable::Packable)] pub struct IssuerFeature(pub(crate) Address); impl IssuerFeature { @@ -43,7 +43,7 @@ pub(crate) mod dto { fn from(value: &IssuerFeature) -> Self { Self { kind: IssuerFeature::KIND, - address: value.0, + address: value.0.clone(), } } } diff --git a/sdk/src/types/block/output/feature/sender.rs b/sdk/src/types/block/output/feature/sender.rs index 1faee6314a..6bb8d20c08 100644 --- a/sdk/src/types/block/output/feature/sender.rs +++ b/sdk/src/types/block/output/feature/sender.rs @@ -6,7 +6,7 @@ use derive_more::From; use crate::types::block::address::Address; /// Identifies the validated sender of an output. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, From, packable::Packable)] +#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, From, packable::Packable)] pub struct SenderFeature(pub(crate) Address); impl SenderFeature { @@ -43,7 +43,7 @@ pub(crate) mod dto { fn from(value: &SenderFeature) -> Self { Self { kind: SenderFeature::KIND, - address: value.0, + address: value.0.clone(), } } } diff --git a/sdk/src/types/block/output/mod.rs b/sdk/src/types/block/output/mod.rs index c9829eb5a0..17549a2a24 100644 --- a/sdk/src/types/block/output/mod.rs +++ b/sdk/src/types/block/output/mod.rs @@ -315,27 +315,36 @@ impl Output { ) -> Result<(Address, Option
), Error> { match self { Self::Basic(output) => Ok(( - *output.unlock_conditions().locked_address(output.address(), slot_index), + output + .unlock_conditions() + .locked_address(output.address(), slot_index) + .clone(), None, )), Self::Account(output) => { if account_transition.unwrap_or(AccountTransition::State) == AccountTransition::State { // Account address is only unlocked if it's a state transition Ok(( - *output.state_controller_address(), + output.state_controller_address().clone(), Some(Address::Account(output.account_address(output_id))), )) } else { - Ok((*output.governor_address(), None)) + Ok((output.governor_address().clone(), None)) } } Self::Foundry(output) => Ok((Address::Account(*output.account_address()), None)), Self::Nft(output) => Ok(( - *output.unlock_conditions().locked_address(output.address(), slot_index), + output + .unlock_conditions() + .locked_address(output.address(), slot_index) + .clone(), Some(Address::Nft(output.nft_address(output_id))), )), Self::Delegation(output) => Ok(( - *output.unlock_conditions().locked_address(output.address(), slot_index), + output + .unlock_conditions() + .locked_address(output.address(), slot_index) + .clone(), None, )), } @@ -511,7 +520,7 @@ fn minimum_storage_deposit(address: &Address, rent_structure: RentStructure, tok // PANIC: This can never fail because the amount will always be within the valid range. Also, the actual value is // not important, we are only interested in the storage requirements of the type. BasicOutputBuilder::new_with_minimum_storage_deposit(rent_structure) - .add_unlock_condition(AddressUnlockCondition::new(*address)) + .add_unlock_condition(AddressUnlockCondition::new(address.clone())) .finish_with_params(token_supply) .unwrap() .amount() diff --git a/sdk/src/types/block/output/unlock_condition/address.rs b/sdk/src/types/block/output/unlock_condition/address.rs index f1ad3005aa..5488226b44 100644 --- a/sdk/src/types/block/output/unlock_condition/address.rs +++ b/sdk/src/types/block/output/unlock_condition/address.rs @@ -6,7 +6,7 @@ use derive_more::From; use crate::types::block::address::Address; /// Defines the Address that owns this output, that is, it can unlock it with the proper Unlock in a transaction. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, From, packable::Packable)] +#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, From, packable::Packable)] pub struct AddressUnlockCondition(Address); impl AddressUnlockCondition { @@ -43,7 +43,7 @@ pub(crate) mod dto { fn from(value: &AddressUnlockCondition) -> Self { Self { kind: AddressUnlockCondition::KIND, - address: value.0, + address: value.0.clone(), } } } diff --git a/sdk/src/types/block/output/unlock_condition/expiration.rs b/sdk/src/types/block/output/unlock_condition/expiration.rs index 185de6eb6a..fff1ad333c 100644 --- a/sdk/src/types/block/output/unlock_condition/expiration.rs +++ b/sdk/src/types/block/output/unlock_condition/expiration.rs @@ -7,7 +7,7 @@ use crate::types::block::{address::Address, slot::SlotIndex, Error}; /// Defines an expiration slot index. Before the slot index is reached, only the Address defined in the Address /// Unlock Condition is allowed to unlock the output. Afterward, only the Return Address can unlock it. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, From, packable::Packable)] +#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, From, packable::Packable)] pub struct ExpirationUnlockCondition { // The address that can unlock the expired output. return_address: Address, @@ -84,7 +84,7 @@ pub(crate) mod dto { fn from(value: &ExpirationUnlockCondition) -> Self { Self { kind: ExpirationUnlockCondition::KIND, - return_address: *value.return_address(), + return_address: value.return_address().clone(), slot_index: value.slot_index(), } } diff --git a/sdk/src/types/block/output/unlock_condition/governor_address.rs b/sdk/src/types/block/output/unlock_condition/governor_address.rs index 6ebb6cda8c..c4fe935ae4 100644 --- a/sdk/src/types/block/output/unlock_condition/governor_address.rs +++ b/sdk/src/types/block/output/unlock_condition/governor_address.rs @@ -7,7 +7,7 @@ use crate::types::block::address::Address; /// Defines the Governor Address that owns this output, that is, it can unlock it with the proper Unlock in a /// transaction that governance transitions the account output. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, From, packable::Packable)] +#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, From, packable::Packable)] pub struct GovernorAddressUnlockCondition(Address); impl GovernorAddressUnlockCondition { @@ -45,7 +45,7 @@ pub(crate) mod dto { fn from(value: &GovernorAddressUnlockCondition) -> Self { Self { kind: GovernorAddressUnlockCondition::KIND, - address: value.0, + address: value.0.clone(), } } } diff --git a/sdk/src/types/block/output/unlock_condition/mod.rs b/sdk/src/types/block/output/unlock_condition/mod.rs index de473e337f..28c7a00fa6 100644 --- a/sdk/src/types/block/output/unlock_condition/mod.rs +++ b/sdk/src/types/block/output/unlock_condition/mod.rs @@ -528,12 +528,12 @@ pub mod dto { impl From<&UnlockCondition> for UnlockConditionDto { fn from(value: &UnlockCondition) -> Self { match value { - UnlockCondition::Address(v) => Self::Address(*v), + UnlockCondition::Address(v) => Self::Address(v.clone()), UnlockCondition::StorageDepositReturn(v) => Self::StorageDepositReturn(v.into()), UnlockCondition::Timelock(v) => Self::Timelock(*v), - UnlockCondition::Expiration(v) => Self::Expiration(*v), - UnlockCondition::StateControllerAddress(v) => Self::StateControllerAddress(*v), - UnlockCondition::GovernorAddress(v) => Self::GovernorAddress(*v), + UnlockCondition::Expiration(v) => Self::Expiration(v.clone()), + UnlockCondition::StateControllerAddress(v) => Self::StateControllerAddress(v.clone()), + UnlockCondition::GovernorAddress(v) => Self::GovernorAddress(v.clone()), UnlockCondition::ImmutableAccountAddress(v) => Self::ImmutableAccountAddress(*v), } } diff --git a/sdk/src/types/block/output/unlock_condition/state_controller_address.rs b/sdk/src/types/block/output/unlock_condition/state_controller_address.rs index 88281e0ac1..dda21632c6 100644 --- a/sdk/src/types/block/output/unlock_condition/state_controller_address.rs +++ b/sdk/src/types/block/output/unlock_condition/state_controller_address.rs @@ -7,7 +7,7 @@ use crate::types::block::address::Address; /// Defines the State Controller Address that owns this output, that is, it can unlock it with the proper Unlock in a /// transaction that state transitions the account output. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, From, packable::Packable)] +#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, From, packable::Packable)] pub struct StateControllerAddressUnlockCondition(Address); impl StateControllerAddressUnlockCondition { @@ -45,7 +45,7 @@ pub(crate) mod dto { fn from(value: &StateControllerAddressUnlockCondition) -> Self { Self { kind: StateControllerAddressUnlockCondition::KIND, - address: value.address().into(), + address: value.address().clone(), } } } diff --git a/sdk/src/types/block/output/unlock_condition/storage_deposit_return.rs b/sdk/src/types/block/output/unlock_condition/storage_deposit_return.rs index bec25f3eca..f10022849f 100644 --- a/sdk/src/types/block/output/unlock_condition/storage_deposit_return.rs +++ b/sdk/src/types/block/output/unlock_condition/storage_deposit_return.rs @@ -4,7 +4,7 @@ use crate::types::block::{address::Address, output::verify_output_amount, protocol::ProtocolParameters, Error}; /// Defines the amount of IOTAs used as storage deposit that have to be returned to the return [`Address`]. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, packable::Packable)] +#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, packable::Packable)] #[packable(unpack_visitor = ProtocolParameters)] pub struct StorageDepositReturnUnlockCondition { // The [`Address`] to return the amount to. @@ -99,7 +99,7 @@ pub(crate) mod dto { fn from(value: &StorageDepositReturnUnlockCondition) -> Self { Self { kind: StorageDepositReturnUnlockCondition::KIND, - return_address: value.return_address, + return_address: value.return_address.clone(), amount: value.amount, } } diff --git a/sdk/src/types/block/semantic.rs b/sdk/src/types/block/semantic.rs index b98b916bcb..65f5fddc4e 100644 --- a/sdk/src/types/block/semantic.rs +++ b/sdk/src/types/block/semantic.rs @@ -282,7 +282,7 @@ pub fn semantic_validation( if let Some(storage_deposit_return) = unlock_conditions.storage_deposit_return() { let amount = context .storage_deposit_returns - .entry(*storage_deposit_return.return_address()) + .entry(storage_deposit_return.return_address().clone()) .or_default(); *amount = amount @@ -312,7 +312,7 @@ pub fn semantic_validation( let (amount, created_native_tokens, features) = match created_output { Output::Basic(output) => { if let Some(address) = output.simple_deposit_address() { - let amount = context.simple_deposits.entry(*address).or_default(); + let amount = context.simple_deposits.entry(address.clone()).or_default(); *amount = amount .checked_add(output.amount()) diff --git a/sdk/src/wallet/account/mod.rs b/sdk/src/wallet/account/mod.rs index f8ab9a8e44..5da357fcd5 100644 --- a/sdk/src/wallet/account/mod.rs +++ b/sdk/src/wallet/account/mod.rs @@ -62,6 +62,7 @@ use crate::{ types::{ api::core::response::OutputWithMetadataResponse, block::{ + address::Bech32Address, output::{dto::FoundryOutputDto, AccountId, FoundryId, FoundryOutput, NftId, Output, OutputId, TokenId}, payload::{transaction::TransactionId, TransactionPayload}, }, @@ -289,11 +290,18 @@ impl AccountInner { } /// Returns all addresses of the account - pub async fn addresses(&self) -> Result> { + pub async fn addresses(&self) -> Vec { let account_details = self.details().await; let mut all_addresses = account_details.public_addresses().clone(); all_addresses.extend(account_details.internal_addresses().clone()); - Ok(all_addresses.to_vec()) + + all_addresses.to_vec() + } + + /// Returns the first address of the account as bech32 + pub async fn first_address_bech32(&self) -> Bech32Address { + // PANIC: unwrap is fine as one address is always generated during account creation. + self.addresses().await.into_iter().next().unwrap().into_bech32() } /// Returns all public addresses of the account diff --git a/sdk/src/wallet/account/operations/balance.rs b/sdk/src/wallet/account/operations/balance.rs index 2d71126820..82cd9ad673 100644 --- a/sdk/src/wallet/account/operations/balance.rs +++ b/sdk/src/wallet/account/operations/balance.rs @@ -164,7 +164,7 @@ where // if we have multiple unlock conditions for basic or nft outputs, then we might can't // spend the balance at the moment or in the future - let account_addresses = self.addresses().await?; + let account_addresses = self.addresses().await; let slot_index = self.client().get_slot_index().await?; let is_claimable = self.claimable_outputs(OutputsToClaim::All).await?.contains(output_id); diff --git a/sdk/src/wallet/account/operations/output_claiming.rs b/sdk/src/wallet/account/operations/output_claiming.rs index 000b26513f..dbaff2eea4 100644 --- a/sdk/src/wallet/account/operations/output_claiming.rs +++ b/sdk/src/wallet/account/operations/output_claiming.rs @@ -259,7 +259,9 @@ where available_amount += output_data.output.amount() - sdr.amount(); // Insert for return output - *required_address_returns.entry(*sdr.return_address()).or_default() += sdr.amount(); + *required_address_returns + .entry(sdr.return_address().clone()) + .or_default() += sdr.amount(); } else { available_amount += output_data.output.amount(); } @@ -273,13 +275,17 @@ where // deposit for the remaining amount and possible NTs NftOutputBuilder::from(nft_output) .with_nft_id(nft_output.nft_id_non_null(&output_data.output_id)) - .with_unlock_conditions([AddressUnlockCondition::new(first_account_address.address.inner)]) + .with_unlock_conditions([AddressUnlockCondition::new( + first_account_address.address.inner.clone(), + )]) .finish_output(token_supply)? } else { NftOutputBuilder::from(nft_output) .with_minimum_storage_deposit(rent_structure) .with_nft_id(nft_output.nft_id_non_null(&output_data.output_id)) - .with_unlock_conditions([AddressUnlockCondition::new(first_account_address.address.inner)]) + .with_unlock_conditions([AddressUnlockCondition::new( + first_account_address.address.inner.clone(), + )]) // Set native tokens empty, we will collect them from all inputs later .with_native_tokens([]) .finish_output(token_supply)? @@ -371,7 +377,7 @@ where if available_amount - required_amount_for_nfts > 0 { outputs_to_send.push( BasicOutputBuilder::new_with_amount(available_amount - required_amount_for_nfts) - .add_unlock_condition(AddressUnlockCondition::new(first_account_address.address.inner)) + .add_unlock_condition(AddressUnlockCondition::new(first_account_address.address.inner.clone())) .with_native_tokens(new_native_tokens.finish()?) .finish_output(token_supply)?, ); diff --git a/sdk/src/wallet/account/operations/output_consolidation.rs b/sdk/src/wallet/account/operations/output_consolidation.rs index acf4577198..7b9adda9e3 100644 --- a/sdk/src/wallet/account/operations/output_consolidation.rs +++ b/sdk/src/wallet/account/operations/output_consolidation.rs @@ -254,7 +254,7 @@ where params .target_address .map(|bech32| bech32.into_inner()) - .unwrap_or(outputs_to_consolidate[0].address), + .unwrap_or_else(|| outputs_to_consolidate[0].address.clone()), )) .with_native_tokens(total_native_tokens.finish()?) .finish_output(token_supply)?]; diff --git a/sdk/src/wallet/account/operations/participation/voting_power.rs b/sdk/src/wallet/account/operations/participation/voting_power.rs index bc14c01f80..0b95341107 100644 --- a/sdk/src/wallet/account/operations/participation/voting_power.rs +++ b/sdk/src/wallet/account/operations/participation/voting_power.rs @@ -80,7 +80,8 @@ where .add_unlock_condition(AddressUnlockCondition::new( self.public_addresses() .await - .first() + .into_iter() + .next() .expect("account needs to have a public address") .address .inner, diff --git a/sdk/src/wallet/account/operations/syncing/addresses/mod.rs b/sdk/src/wallet/account/operations/syncing/addresses/mod.rs index c9db51394b..a4e75d13c5 100644 --- a/sdk/src/wallet/account/operations/syncing/addresses/mod.rs +++ b/sdk/src/wallet/account/operations/syncing/addresses/mod.rs @@ -24,7 +24,7 @@ where ) -> crate::wallet::Result> { log::debug!("[SYNC] get_addresses_to_sync"); - let mut addresses_before_syncing = self.addresses().await?; + let mut addresses_before_syncing = self.addresses().await; // If custom addresses are provided check if they are in the account and only use them if !options.addresses.is_empty() { @@ -35,7 +35,7 @@ where specific_addresses_to_sync.insert(address.clone()); } None => { - return Err(crate::wallet::Error::AddressNotFoundInAccount(*bech32_address)); + return Err(crate::wallet::Error::AddressNotFoundInAccount(bech32_address.clone())); } } } diff --git a/sdk/src/wallet/account/operations/syncing/addresses/output_ids/account_foundry.rs b/sdk/src/wallet/account/operations/syncing/addresses/output_ids/account_foundry.rs index 3b541fd44b..67e5c1df22 100644 --- a/sdk/src/wallet/account/operations/syncing/addresses/output_ids/account_foundry.rs +++ b/sdk/src/wallet/account/operations/syncing/addresses/output_ids/account_foundry.rs @@ -42,7 +42,7 @@ where { output_ids.extend( client - .account_output_ids([QueryParameter::Governor(bech32_address)]) + .account_output_ids([QueryParameter::Governor(bech32_address.clone())]) .await? .items, ); @@ -58,7 +58,8 @@ where { let tasks = [ // Get outputs where the address is in the governor address unlock condition - async move { + async { + let bech32_address = bech32_address.clone(); let client = client.clone(); task::spawn(async move { client @@ -70,7 +71,8 @@ where } .boxed(), // Get outputs where the address is in the state controller unlock condition - async move { + async { + let bech32_address = bech32_address.clone(); let client = client.clone(); task::spawn(async move { client diff --git a/sdk/src/wallet/account/operations/syncing/addresses/output_ids/basic.rs b/sdk/src/wallet/account/operations/syncing/addresses/output_ids/basic.rs index cbd9c36659..084799fe51 100644 --- a/sdk/src/wallet/account/operations/syncing/addresses/output_ids/basic.rs +++ b/sdk/src/wallet/account/operations/syncing/addresses/output_ids/basic.rs @@ -51,13 +51,13 @@ where let mut output_ids = Vec::new(); output_ids.extend( self.client() - .basic_output_ids([QueryParameter::Address(bech32_address)]) + .basic_output_ids([QueryParameter::Address(bech32_address.clone())]) .await? .items, ); output_ids.extend( self.client() - .basic_output_ids([QueryParameter::StorageDepositReturnAddress(bech32_address)]) + .basic_output_ids([QueryParameter::StorageDepositReturnAddress(bech32_address.clone())]) .await? .items, ); @@ -76,7 +76,8 @@ where let client = self.client(); let tasks = [ // Get basic outputs - async move { + async { + let bech32_address = bech32_address.clone(); let client = client.clone(); tokio::spawn(async move { client @@ -88,7 +89,8 @@ where } .boxed(), // Get outputs where the address is in the storage deposit return unlock condition - async move { + async { + let bech32_address = bech32_address.clone(); let client = client.clone(); tokio::spawn(async move { client @@ -100,7 +102,8 @@ where } .boxed(), // Get outputs where the address is in an expired expiration unlock condition - async move { + async { + let bech32_address = bech32_address.clone(); let client = client.clone(); tokio::spawn(async move { client diff --git a/sdk/src/wallet/account/operations/syncing/addresses/output_ids/mod.rs b/sdk/src/wallet/account/operations/syncing/addresses/output_ids/mod.rs index ae10e9aa92..3efe237de8 100644 --- a/sdk/src/wallet/account/operations/syncing/addresses/output_ids/mod.rs +++ b/sdk/src/wallet/account/operations/syncing/addresses/output_ids/mod.rs @@ -32,14 +32,14 @@ where /// address) connected to pub(crate) async fn get_output_ids_for_address( &self, - address: Address, + address: &Address, sync_options: &SyncOptions, ) -> crate::wallet::Result> { - let bech32_address = Bech32Address::new(self.client().get_bech32_hrp().await?, address); + let bech32_address = Bech32Address::new(self.client().get_bech32_hrp().await?, address.clone()); if sync_options.sync_only_most_basic_outputs { let output_ids = self - .get_basic_output_ids_with_address_unlock_condition_only(bech32_address) + .get_basic_output_ids_with_address_unlock_condition_only(bech32_address.clone()) .await?; return Ok(output_ids); } @@ -51,7 +51,7 @@ where { return Ok(self .client() - .output_ids([QueryParameter::UnlockableByAddress(bech32_address)]) + .output_ids([QueryParameter::UnlockableByAddress(bech32_address.clone())]) .await? .items); } @@ -70,7 +70,7 @@ where #[cfg(target_family = "wasm")] { results.push( - self.get_basic_output_ids_with_any_unlock_condition(bech32_address) + self.get_basic_output_ids_with_any_unlock_condition(bech32_address.clone()) .await, ) } @@ -78,7 +78,8 @@ where #[cfg(not(target_family = "wasm"))] { tasks.push( - async move { + async { + let bech32_address = bech32_address.clone(); let account = self.clone(); tokio::spawn(async move { account @@ -99,13 +100,17 @@ where // nfts #[cfg(target_family = "wasm")] { - results.push(self.get_nft_output_ids_with_any_unlock_condition(bech32_address).await) + results.push( + self.get_nft_output_ids_with_any_unlock_condition(bech32_address.clone()) + .await, + ) } #[cfg(not(target_family = "wasm"))] { tasks.push( - async move { + async { + let bech32_address = bech32_address.clone(); let account = self.clone(); tokio::spawn(async move { account @@ -127,7 +132,7 @@ where #[cfg(target_family = "wasm")] { results.push( - self.get_account_and_foundry_output_ids(bech32_address, sync_options) + self.get_account_and_foundry_output_ids(bech32_address.clone(), sync_options) .await, ) } @@ -135,7 +140,8 @@ where #[cfg(not(target_family = "wasm"))] { tasks.push( - async move { + async { + let bech32_address = bech32_address.clone(); let sync_options = sync_options.clone(); let account = self.clone(); tokio::spawn(async move { @@ -162,7 +168,8 @@ where #[cfg(not(target_family = "wasm"))] { tasks.push( - async move { + async { + let bech32_address = bech32_address.clone(); let client = self.client().clone(); tokio::spawn(async move { Ok(client @@ -213,7 +220,9 @@ where { let mut tasks = Vec::new(); for address in addresses_chunk { - let output_ids = self.get_output_ids_for_address(address.address.inner, &options).await?; + let output_ids = self + .get_output_ids_for_address(&address.address.inner, &options) + .await?; tasks.push(crate::wallet::Result::Ok((address, output_ids))); } results = tasks; @@ -228,7 +237,7 @@ where tasks.push(async move { tokio::spawn(async move { let output_ids = account - .get_output_ids_for_address(address.address.inner, &sync_options) + .get_output_ids_for_address(&address.address.inner, &sync_options) .await?; crate::wallet::Result::Ok((address, output_ids)) }) diff --git a/sdk/src/wallet/account/operations/syncing/addresses/output_ids/nft.rs b/sdk/src/wallet/account/operations/syncing/addresses/output_ids/nft.rs index a2ceb9acb5..794ec64acb 100644 --- a/sdk/src/wallet/account/operations/syncing/addresses/output_ids/nft.rs +++ b/sdk/src/wallet/account/operations/syncing/addresses/output_ids/nft.rs @@ -30,13 +30,13 @@ where let mut output_ids = Vec::new(); output_ids.extend( self.client() - .nft_output_ids([QueryParameter::Address(bech32_address)]) + .nft_output_ids([QueryParameter::Address(bech32_address.clone())]) .await? .items, ); output_ids.extend( self.client() - .nft_output_ids([QueryParameter::StorageDepositReturnAddress(bech32_address)]) + .nft_output_ids([QueryParameter::StorageDepositReturnAddress(bech32_address.clone())]) .await? .items, ); @@ -53,7 +53,8 @@ where { let client = self.client(); let tasks = [ - async move { + async { + let bech32_address = bech32_address.clone(); let client = client.clone(); tokio::spawn(async move { // Get nft outputs where the address is in the address unlock condition @@ -65,7 +66,8 @@ where .await } .boxed(), - async move { + async { + let bech32_address = bech32_address.clone(); let client = client.clone(); tokio::spawn(async move { // Get outputs where the address is in the storage deposit return unlock condition @@ -77,7 +79,8 @@ where .await } .boxed(), - async move { + async { + let bech32_address = bech32_address.clone(); let client = client.clone(); tokio::spawn(async move { // Get outputs where the address is in the expiration unlock condition diff --git a/sdk/src/wallet/account/operations/syncing/mod.rs b/sdk/src/wallet/account/operations/syncing/mod.rs index 84019b4641..92e31b358e 100644 --- a/sdk/src/wallet/account/operations/syncing/mod.rs +++ b/sdk/src/wallet/account/operations/syncing/mod.rs @@ -187,7 +187,9 @@ where let bech32_hrp = self.client().get_bech32_hrp().await?; let mut new_outputs_data = Vec::new(); for (account_or_nft_address, ed25519_address) in new_account_and_nft_addresses { - let output_ids = self.get_output_ids_for_address(account_or_nft_address, options).await?; + let output_ids = self + .get_output_ids_for_address(&account_or_nft_address, options) + .await?; // Update address with unspent outputs let address_with_unspent_outputs = addresses_with_unspent_outputs @@ -213,8 +215,8 @@ where // Clear, so we only get new addresses new_account_and_nft_addresses = HashMap::new(); // Add new account and nft addresses - for output_data in new_outputs_data.iter() { - match &output_data.output { + for output_data in new_outputs_data { + match output_data.output { Output::Account(account_output) => { let account_address = AccountAddress::from(account_output.account_id_non_null(&output_data.output_id)); diff --git a/sdk/src/wallet/account/operations/syncing/outputs.rs b/sdk/src/wallet/account/operations/syncing/outputs.rs index c97cf594f2..74d7ebca9c 100644 --- a/sdk/src/wallet/account/operations/syncing/outputs.rs +++ b/sdk/src/wallet/account/operations/syncing/outputs.rs @@ -57,7 +57,7 @@ where metadata: *output_with_meta.metadata(), output: output_with_meta.output().clone(), is_spent: output_with_meta.metadata().is_spent(), - address: associated_address.address.inner, + address: associated_address.address.inner.clone(), network_id, remainder, chain: Some(chain), diff --git a/sdk/src/wallet/account/operations/transaction/high_level/create_account.rs b/sdk/src/wallet/account/operations/transaction/high_level/create_account.rs index 4259eb6b63..386adcfafc 100644 --- a/sdk/src/wallet/account/operations/transaction/high_level/create_account.rs +++ b/sdk/src/wallet/account/operations/transaction/high_level/create_account.rs @@ -81,12 +81,13 @@ where let controller_address = match params.as_ref().and_then(|options| options.address.as_ref()) { Some(bech32_address) => { self.client().bech32_hrp_matches(bech32_address.hrp()).await?; - *bech32_address.inner() + bech32_address.inner().clone() } None => { self.public_addresses() .await - .first() + .into_iter() + .next() .expect("first address is generated during account creation") .address .inner @@ -97,7 +98,7 @@ where AccountOutputBuilder::new_with_minimum_storage_deposit(rent_structure, AccountId::null()) .with_state_index(0) .with_foundry_counter(0) - .add_unlock_condition(StateControllerAddressUnlockCondition::new(controller_address)) + .add_unlock_condition(StateControllerAddressUnlockCondition::new(controller_address.clone())) .add_unlock_condition(GovernorAddressUnlockCondition::new(controller_address)); if let Some(CreateAccountParams { immutable_metadata, diff --git a/sdk/src/wallet/account/operations/transaction/high_level/minting/mint_nfts.rs b/sdk/src/wallet/account/operations/transaction/high_level/minting/mint_nfts.rs index 7bbdd202d5..1d002d8adc 100644 --- a/sdk/src/wallet/account/operations/transaction/high_level/minting/mint_nfts.rs +++ b/sdk/src/wallet/account/operations/transaction/high_level/minting/mint_nfts.rs @@ -163,7 +163,7 @@ where log::debug!("[TRANSACTION] prepare_mint_nfts"); let rent_structure = self.client().get_rent_structure().await?; let token_supply = self.client().get_token_supply().await?; - let account_addresses = self.addresses().await?; + let account_addresses = self.addresses().await; let mut outputs = Vec::new(); for MintNftParams { @@ -181,12 +181,11 @@ where address } // todo other error message - None => { - account_addresses - .first() - .ok_or(WalletError::FailedToGetRemainder)? - .address - } + None => account_addresses + .first() + .ok_or(WalletError::FailedToGetRemainder)? + .address + .clone(), }; // NftId needs to be set to 0 for the creation diff --git a/sdk/src/wallet/account/operations/transaction/high_level/send.rs b/sdk/src/wallet/account/operations/transaction/high_level/send.rs index 1da4896136..3bb51bc945 100644 --- a/sdk/src/wallet/account/operations/transaction/high_level/send.rs +++ b/sdk/src/wallet/account/operations/transaction/high_level/send.rs @@ -143,8 +143,11 @@ where let rent_structure = self.client().get_rent_structure().await?; let token_supply = self.client().get_token_supply().await?; - let account_addresses = self.addresses().await?; - let default_return_address = account_addresses.first().ok_or(Error::FailedToGetRemainder)?; + let account_addresses = self.addresses().await; + let default_return_address = account_addresses + .into_iter() + .next() + .ok_or(Error::FailedToGetRemainder)?; let slot_index = self.client().get_slot_index().await?; @@ -168,7 +171,7 @@ where Ok::<_, Error>(return_address) }) .transpose()? - .unwrap_or(default_return_address.address); + .unwrap_or_else(|| default_return_address.address.clone()); // Get the minimum required amount for an output assuming it does not need a storage deposit. let output = BasicOutputBuilder::new_with_minimum_storage_deposit(rent_structure) @@ -209,7 +212,7 @@ where // We send the storage_deposit_amount back to the sender, so only the additional amount is // sent StorageDepositReturnUnlockCondition::new( - return_address, + return_address.clone(), storage_deposit_amount, token_supply, )?, diff --git a/sdk/src/wallet/account/operations/transaction/high_level/send_native_tokens.rs b/sdk/src/wallet/account/operations/transaction/high_level/send_native_tokens.rs index f59c3d2144..c5eca80305 100644 --- a/sdk/src/wallet/account/operations/transaction/high_level/send_native_tokens.rs +++ b/sdk/src/wallet/account/operations/transaction/high_level/send_native_tokens.rs @@ -133,8 +133,11 @@ where let rent_structure = self.client().get_rent_structure().await?; let token_supply = self.client().get_token_supply().await?; - let account_addresses = self.addresses().await?; - let default_return_address = account_addresses.first().ok_or(Error::FailedToGetRemainder)?; + let account_addresses = self.addresses().await; + let default_return_address = account_addresses + .into_iter() + .next() + .ok_or(Error::FailedToGetRemainder)?; let slot_index = self.client().get_slot_index().await?; @@ -158,7 +161,7 @@ where Ok::<_, Error>(addr) }) .transpose()? - .unwrap_or(default_return_address.address); + .unwrap_or_else(|| default_return_address.address.clone()); let native_tokens = NativeTokens::from_vec( native_tokens @@ -190,7 +193,11 @@ where .add_unlock_condition( // We send the full storage_deposit_amount back to the sender, so only the native tokens are // sent - StorageDepositReturnUnlockCondition::new(return_address, storage_deposit_amount, token_supply)?, + StorageDepositReturnUnlockCondition::new( + return_address.clone(), + storage_deposit_amount, + token_supply, + )?, ) .add_unlock_condition(ExpirationUnlockCondition::new(return_address, expiration_slot_index)?) .finish_output(token_supply)?, diff --git a/sdk/src/wallet/account/operations/transaction/input_selection.rs b/sdk/src/wallet/account/operations/transaction/input_selection.rs index 7674e1eeaf..eaef7dc9ac 100644 --- a/sdk/src/wallet/account/operations/transaction/input_selection.rs +++ b/sdk/src/wallet/account/operations/transaction/input_selection.rs @@ -56,7 +56,7 @@ where .public_addresses() .iter() .chain(account_details.internal_addresses().iter()) - .map(|address| *address.address.as_ref()) + .map(|address| address.address.as_ref().clone()) .collect::>(); // Prevent consuming the voting output if not actually wanted diff --git a/sdk/src/wallet/account/operations/transaction/prepare_output.rs b/sdk/src/wallet/account/operations/transaction/prepare_output.rs index b904acdb9f..fc9b596cf6 100644 --- a/sdk/src/wallet/account/operations/transaction/prepare_output.rs +++ b/sdk/src/wallet/account/operations/transaction/prepare_output.rs @@ -135,7 +135,7 @@ where if return_strategy == ReturnStrategy::Return { second_output_builder = second_output_builder.add_unlock_condition(StorageDepositReturnUnlockCondition::new( - remainder_address, + remainder_address.clone(), // Return minimum storage deposit min_storage_deposit_basic_output, token_supply, @@ -157,7 +157,7 @@ where // Add the additional amount to the SDR second_output_builder = second_output_builder.replace_unlock_condition(StorageDepositReturnUnlockCondition::new( - remainder_address, + remainder_address.clone(), // Return minimum storage deposit min_storage_deposit_basic_output + additional_required_amount, token_supply, @@ -294,9 +294,9 @@ where } RemainderValueStrategy::ChangeAddress => { let remainder_address = self.generate_remainder_address().await?; - Some(remainder_address.address().inner) + Some(remainder_address.address.inner) } - RemainderValueStrategy::CustomAddress(address) => Some(*address), + RemainderValueStrategy::CustomAddress(address) => Some(address.clone()), } } None => None, @@ -305,10 +305,11 @@ where Some(address) => address, None => { self.addresses() - .await? - .first() + .await + .into_iter() + .next() .ok_or(crate::wallet::Error::FailedToGetRemainder)? - .address() + .address .inner } }; diff --git a/sdk/src/wallet/account/operations/transaction/prepare_transaction.rs b/sdk/src/wallet/account/operations/transaction/prepare_transaction.rs index 39086d7b2d..44b7a8629e 100644 --- a/sdk/src/wallet/account/operations/transaction/prepare_transaction.rs +++ b/sdk/src/wallet/account/operations/transaction/prepare_transaction.rs @@ -87,15 +87,15 @@ where account_index, WalletEvent::TransactionProgress( TransactionProgressEvent::GeneratingRemainderDepositAddress(AddressData { - address: remainder_address.address, + address: remainder_address.address.clone(), }), ), ) .await; } - Some(remainder_address.address().inner) + Some(remainder_address.address.inner) } - RemainderValueStrategy::CustomAddress(address) => Some(*address), + RemainderValueStrategy::CustomAddress(address) => Some(address.clone()), } } None => None, diff --git a/sdk/src/wallet/account/types/mod.rs b/sdk/src/wallet/account/types/mod.rs index fa22cd1b14..da4f52ba0d 100644 --- a/sdk/src/wallet/account/types/mod.rs +++ b/sdk/src/wallet/account/types/mod.rs @@ -123,7 +123,7 @@ impl From<&OutputData> for OutputDataDto { metadata: value.metadata, output: OutputDto::from(&value.output), is_spent: value.is_spent, - address: value.address, + address: value.address.clone(), network_id: value.network_id.to_string(), remainder: value.remainder, chain: value.chain, diff --git a/sdk/tests/client/high_level.rs b/sdk/tests/client/high_level.rs index c78a58a6f9..30c8d1cb76 100644 --- a/sdk/tests/client/high_level.rs +++ b/sdk/tests/client/high_level.rs @@ -41,6 +41,7 @@ async fn test_find_inputs() { .address() .unwrap() .address() + .clone() .to_bech32(client.get_bech32_hrp().await.unwrap()); let input = client.find_inputs(vec![address], 1_000_000).await.unwrap(); diff --git a/sdk/tests/client/node_api/mod.rs b/sdk/tests/client/node_api/mod.rs index f4839038af..5d27084a7b 100644 --- a/sdk/tests/client/node_api/mod.rs +++ b/sdk/tests/client/node_api/mod.rs @@ -72,7 +72,7 @@ pub async fn setup_transaction_block(client: &Client) -> (BlockId, TransactionId tokio::time::sleep(std::time::Duration::from_secs(1)).await; let output_ids_response = client .basic_output_ids([ - QueryParameter::Address(addresses[0]), + QueryParameter::Address(addresses[0].clone()), QueryParameter::HasExpiration(false), QueryParameter::HasTimelock(false), QueryParameter::HasStorageDepositReturn(false), diff --git a/sdk/tests/client/secret_manager/private_key.rs b/sdk/tests/client/secret_manager/private_key.rs index 31c8d6f10b..1e9bc2a5d8 100644 --- a/sdk/tests/client/secret_manager/private_key.rs +++ b/sdk/tests/client/secret_manager/private_key.rs @@ -21,7 +21,8 @@ async fn private_key_secret_manager_hex() -> Result<()> { .with_range(0..1), ) .await - .unwrap()[0]; + .unwrap()[0] + .clone(); // Changing range generates the same address. let address_1 = secret_manager .generate_ed25519_addresses( @@ -31,7 +32,8 @@ async fn private_key_secret_manager_hex() -> Result<()> { .with_range(1..2), ) .await - .unwrap()[0]; + .unwrap()[0] + .clone(); // Changing account generates the same address. let address_2 = secret_manager .generate_ed25519_addresses( @@ -41,7 +43,8 @@ async fn private_key_secret_manager_hex() -> Result<()> { .with_range(0..1), ) .await - .unwrap()[0]; + .unwrap()[0] + .clone(); assert_eq!( address_0, @@ -73,7 +76,8 @@ async fn private_key_secret_manager_bs58() -> Result<()> { .with_range(0..1), ) .await - .unwrap()[0]; + .unwrap()[0] + .clone(); assert_eq!( address, diff --git a/sdk/tests/client/signing/account.rs b/sdk/tests/client/signing/account.rs index 5dbc140e31..2684a2e6ec 100644 --- a/sdk/tests/client/signing/account.rs +++ b/sdk/tests/client/signing/account.rs @@ -45,6 +45,7 @@ async fn sign_account_state_transition() -> Result<()> { .with_range(0..1), ) .await?[0] + .clone() .to_bech32(SHIMMER_TESTNET_BECH32_HRP); let bech32_address_1 = &secret_manager .generate_ed25519_addresses( @@ -53,6 +54,7 @@ async fn sign_account_state_transition() -> Result<()> { .with_range(1..2), ) .await?[0] + .clone() .to_bech32(SHIMMER_TESTNET_BECH32_HRP); let protocol_parameters = protocol_parameters(); @@ -135,6 +137,7 @@ async fn sign_account_governance_transition() -> Result<()> { .with_range(0..1), ) .await?[0] + .clone() .to_bech32(SHIMMER_TESTNET_BECH32_HRP); let bech32_address_1 = &secret_manager .generate_ed25519_addresses( @@ -143,6 +146,7 @@ async fn sign_account_governance_transition() -> Result<()> { .with_range(1..2), ) .await?[0] + .clone() .to_bech32(SHIMMER_TESTNET_BECH32_HRP); let protocol_parameters = protocol_parameters(); @@ -225,6 +229,7 @@ async fn account_reference_unlocks() -> Result<()> { .with_range(0..1), ) .await?[0] + .clone() .to_bech32(SHIMMER_TESTNET_BECH32_HRP); let bech32_address_1 = &secret_manager .generate_ed25519_addresses( @@ -233,6 +238,7 @@ async fn account_reference_unlocks() -> Result<()> { .with_range(1..2), ) .await?[0] + .clone() .to_bech32(SHIMMER_TESTNET_BECH32_HRP); let protocol_parameters = protocol_parameters(); diff --git a/sdk/tests/client/signing/basic.rs b/sdk/tests/client/signing/basic.rs index 908d296179..e89c521b51 100644 --- a/sdk/tests/client/signing/basic.rs +++ b/sdk/tests/client/signing/basic.rs @@ -39,6 +39,7 @@ async fn single_ed25519_unlock() -> Result<()> { .with_range(0..1), ) .await?[0] + .clone() .to_bech32(SHIMMER_TESTNET_BECH32_HRP); let protocol_parameters = protocol_parameters(); @@ -118,6 +119,7 @@ async fn ed25519_reference_unlocks() -> Result<()> { .with_range(0..1), ) .await?[0] + .clone() .to_bech32(SHIMMER_TESTNET_BECH32_HRP); let protocol_parameters = protocol_parameters(); @@ -231,6 +233,7 @@ async fn two_signature_unlocks() -> Result<()> { .with_range(0..1), ) .await?[0] + .clone() .to_bech32(SHIMMER_TESTNET_BECH32_HRP); let bech32_address_1 = &secret_manager .generate_ed25519_addresses( @@ -239,6 +242,7 @@ async fn two_signature_unlocks() -> Result<()> { .with_range(1..2), ) .await?[0] + .clone() .to_bech32(SHIMMER_TESTNET_BECH32_HRP); let protocol_parameters = protocol_parameters(); diff --git a/sdk/tests/client/signing/mod.rs b/sdk/tests/client/signing/mod.rs index 95e1497fbd..c7519ae09b 100644 --- a/sdk/tests/client/signing/mod.rs +++ b/sdk/tests/client/signing/mod.rs @@ -55,9 +55,15 @@ async fn all_combined() -> Result<()> { .with_range(0..3), ) .await?; - let ed25519_bech32_address_0 = &ed25519_bech32_addresses[0].to_bech32(SHIMMER_TESTNET_BECH32_HRP); - let ed25519_bech32_address_1 = &ed25519_bech32_addresses[1].to_bech32(SHIMMER_TESTNET_BECH32_HRP); - let ed25519_bech32_address_2 = &ed25519_bech32_addresses[2].to_bech32(SHIMMER_TESTNET_BECH32_HRP); + let ed25519_bech32_address_0 = ed25519_bech32_addresses[0] + .clone() + .to_bech32(SHIMMER_TESTNET_BECH32_HRP); + let ed25519_bech32_address_1 = ed25519_bech32_addresses[1] + .clone() + .to_bech32(SHIMMER_TESTNET_BECH32_HRP); + let ed25519_bech32_address_2 = ed25519_bech32_addresses[2] + .clone() + .to_bech32(SHIMMER_TESTNET_BECH32_HRP); let account_id_1 = AccountId::from_str(ACCOUNT_ID_1)?; let account_id_2 = AccountId::from_str(ACCOUNT_ID_2)?; @@ -360,9 +366,9 @@ async fn all_combined() -> Result<()> { inputs.clone(), outputs.clone(), [ - *ed25519_bech32_address_0.inner(), - *ed25519_bech32_address_1.inner(), - *ed25519_bech32_address_2.inner(), + ed25519_bech32_address_0.into_inner(), + ed25519_bech32_address_1.into_inner(), + ed25519_bech32_address_2.into_inner(), ], protocol_parameters.clone(), ) diff --git a/sdk/tests/client/signing/nft.rs b/sdk/tests/client/signing/nft.rs index 5473d5bed8..070d6da1ed 100644 --- a/sdk/tests/client/signing/nft.rs +++ b/sdk/tests/client/signing/nft.rs @@ -45,6 +45,7 @@ async fn nft_reference_unlocks() -> Result<()> { .with_range(0..1), ) .await?[0] + .clone() .to_bech32(SHIMMER_TESTNET_BECH32_HRP); let protocol_parameters = protocol_parameters(); diff --git a/sdk/tests/types/address/account.rs b/sdk/tests/types/address/account.rs index 708616899c..0c269cd597 100644 --- a/sdk/tests/types/address/account.rs +++ b/sdk/tests/types/address/account.rs @@ -90,7 +90,7 @@ fn bech32() { #[test] fn bech32_roundtrip() { let address = Address::from(AccountAddress::from_str(ACCOUNT_ID).unwrap()); - let bech32 = address.to_bech32_unchecked("rms").to_string(); + let bech32 = address.clone().to_bech32_unchecked("rms").to_string(); assert_eq!( Bech32Address::try_from_str(bech32), diff --git a/sdk/tests/types/address/bech32.rs b/sdk/tests/types/address/bech32.rs index 86d1c704c7..96e1e109b6 100644 --- a/sdk/tests/types/address/bech32.rs +++ b/sdk/tests/types/address/bech32.rs @@ -26,7 +26,7 @@ fn debug() { fn ctors() { let ed25519_address = ED25519_ADDRESS.parse::().unwrap(); let address = Address::Ed25519(ed25519_address); - let bech32_address = Bech32Address::try_new("rms", address).unwrap(); + let bech32_address = Bech32Address::try_new("rms", address.clone()).unwrap(); assert_eq!(bech32_address.inner(), &address); assert_eq!(bech32_address.hrp(), "rms"); diff --git a/sdk/tests/types/address/ed25519.rs b/sdk/tests/types/address/ed25519.rs index c96aa81f85..63ef62c4d7 100644 --- a/sdk/tests/types/address/ed25519.rs +++ b/sdk/tests/types/address/ed25519.rs @@ -79,7 +79,7 @@ fn bech32() { #[test] fn bech32_roundtrip() { let address = Address::from(Ed25519Address::from_str(ED25519_ADDRESS).unwrap()); - let bech32 = address.to_bech32_unchecked("rms").to_string(); + let bech32 = address.clone().to_bech32_unchecked("rms").to_string(); assert_eq!( Bech32Address::try_from_str(bech32), diff --git a/sdk/tests/types/address/mod.rs b/sdk/tests/types/address/mod.rs index 41b4666eb5..32c8e17332 100644 --- a/sdk/tests/types/address/mod.rs +++ b/sdk/tests/types/address/mod.rs @@ -5,6 +5,7 @@ mod account; mod bech32; mod ed25519; mod nft; +mod restricted; use core::str::FromStr; diff --git a/sdk/tests/types/address/nft.rs b/sdk/tests/types/address/nft.rs index 56a426e59b..49c329d620 100644 --- a/sdk/tests/types/address/nft.rs +++ b/sdk/tests/types/address/nft.rs @@ -90,7 +90,7 @@ fn bech32() { #[test] fn bech32_roundtrip() { let address = Address::from(NftAddress::from_str(NFT_ID).unwrap()); - let bech32 = address.to_bech32_unchecked("rms").to_string(); + let bech32 = address.clone().to_bech32_unchecked("rms").to_string(); assert_eq!( Bech32Address::try_from_str(bech32), diff --git a/sdk/tests/types/address/restricted.rs b/sdk/tests/types/address/restricted.rs new file mode 100644 index 0000000000..f9dc5defd9 --- /dev/null +++ b/sdk/tests/types/address/restricted.rs @@ -0,0 +1,169 @@ +// Copyright 2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use iota_sdk::types::block::{ + address::{Address, AddressCapabilities, AddressCapabilityFlag, Ed25519Address, RestrictedAddress, ToBech32Ext}, + rand::address::rand_ed25519_address, +}; +use packable::PackableExt; + +#[test] +fn capabilities() { + use AddressCapabilityFlag as Flag; + let address = RestrictedAddress::new(rand_ed25519_address()).unwrap(); + let mut capabilities = address.allowed_capabilities().clone(); + + assert!(!capabilities.has_capability(Flag::OutputsWithNativeTokens)); + capabilities.add_capability(Flag::OutputsWithNativeTokens); + assert!(capabilities.has_capabilities([Flag::OutputsWithNativeTokens])); + assert!(!capabilities.has_capabilities(AddressCapabilities::all().capabilities_iter())); + assert!(!capabilities.is_none()); + assert!(!capabilities.is_all()); + capabilities.set_all(); + assert!(capabilities.is_all()); + assert!(capabilities.has_capabilities(Flag::all())); + capabilities.set_none(); + + assert!(!capabilities.has_capability(Flag::OutputsWithMana)); + capabilities.set_capabilities([Flag::OutputsWithMana, Flag::DelegationOutputs]); + assert!(capabilities.has_capability(Flag::OutputsWithMana)); + assert!(capabilities.has_capabilities([Flag::OutputsWithMana, Flag::DelegationOutputs])); + assert!(!capabilities.has_capability(Flag::OutputsWithNativeTokens)); + assert!(!capabilities.has_capabilities([ + Flag::OutputsWithMana, + Flag::DelegationOutputs, + Flag::OutputsWithNativeTokens + ])); +} + +#[test] +fn restricted_ed25519() { + // Test from https://github.com/iotaledger/tips-draft/blob/tip50/tips/TIP-0050/tip-0050.md#bech32-strings + let address = Ed25519Address::from_public_key_bytes( + hex::decode("6f1581709bb7b1ef030d210db18e3b0ba1c776fba65d8cdaad05415142d189f8") + .unwrap() + .try_into() + .unwrap(), + ) + .unwrap(); + assert_eq!( + hex::encode(address), + "efdc112efe262b304bcf379b26c31bad029f616ee3ec4aa6345a366e4c9e43a3" + ); + // Ed25519 Address (Plain) + assert_eq!( + address.to_bech32_unchecked("iota"), + "iota1qrhacyfwlcnzkvzteumekfkrrwks98mpdm37cj4xx3drvmjvnep6xqgyzyx" + ); + + // Restricted Ed25519 Address (Every Capability Disallowed) + let mut address = RestrictedAddress::new(address).unwrap(); + assert_eq!( + address.clone().to_bech32_unchecked("iota"), + "iota19qqwlhq39mlzv2esf08n0xexcvd66q5lv9hw8mz25c695dnwfj0y8gcq3l9hek" + ); + + // Restricted Ed25519 Address (Every Capability Allowed) + address.set_allowed_capabilities(AddressCapabilities::all()); + assert_eq!( + address.clone().to_bech32_unchecked("iota"), + "iota19qqwlhq39mlzv2esf08n0xexcvd66q5lv9hw8mz25c695dnwfj0y8gcplupydhwt" + ); + + address.set_allowed_capabilities(AddressCapabilities::none()); + assert_eq!( + address.clone().to_bech32_unchecked("iota"), + "iota19qqwlhq39mlzv2esf08n0xexcvd66q5lv9hw8mz25c695dnwfj0y8gcq3l9hek" + ); + + // Restricted Ed25519 Address (Can receive Native Tokens) + address.set_allowed_capabilities([AddressCapabilityFlag::OutputsWithNativeTokens]); + assert_eq!( + address.clone().to_bech32_unchecked("iota"), + "iota19qqwlhq39mlzv2esf08n0xexcvd66q5lv9hw8mz25c695dnwfj0y8gcpqytmqxr4" + ); +} + +#[test] +fn restricted_account() { + // Test from https://github.com/iotaledger/tips-draft/blob/tip50/tips/TIP-0050/tip-0050.md#bech32-strings + let address = Address::unpack_verified( + hex::decode("08f1c011fb54df4a4e5b07462536fbacc779bf80cc45e03bc3410836587b4efc98").unwrap(), + &(), + ) + .unwrap(); + // Account Address (Plain) + assert_eq!( + address.clone().to_bech32_unchecked("iota"), + "iota1prcuqy0m2n055njmqarz2dhm4nrhn0uqe3z7qw7rgyyrvkrmfm7fsnwyxu6" + ); + + // Restricted Account Address (Every Capability Disallowed) + let mut address = RestrictedAddress::new(address).unwrap(); + assert_eq!( + address.clone().to_bech32_unchecked("iota"), + "iota19qy0rsq3ld2d7jjwtvr5vffklwkvw7dlsrxytcpmcdqssdjc0d80exqqdyjudm" + ); + + // Restricted Account Address (Every Capability Allowed) + address.set_allowed_capabilities(AddressCapabilities::all()); + assert_eq!( + address.clone().to_bech32_unchecked("iota"), + "iota19qy0rsq3ld2d7jjwtvr5vffklwkvw7dlsrxytcpmcdqssdjc0d80exqplurds6sq" + ); + + address.set_allowed_capabilities(AddressCapabilities::none()); + assert_eq!( + address.clone().to_bech32_unchecked("iota"), + "iota19qy0rsq3ld2d7jjwtvr5vffklwkvw7dlsrxytcpmcdqssdjc0d80exqqdyjudm" + ); + + // Restricted Account Address (Can receive Native Tokens) + address.set_allowed_capabilities([AddressCapabilityFlag::OutputsWithNativeTokens]); + assert_eq!( + address.clone().to_bech32_unchecked("iota"), + "iota19qy0rsq3ld2d7jjwtvr5vffklwkvw7dlsrxytcpmcdqssdjc0d80exqpqyfjata7" + ); +} + +#[test] +fn restricted_nft() { + // Test from https://github.com/iotaledger/tips-draft/blob/tip50/tips/TIP-0050/tip-0050.md#bech32-strings + let address = Address::unpack_verified( + hex::decode("10c72a65ae53d70b99a57f72637bfd1d5ea7baa2b4ba095c989b667d38558087db").unwrap(), + &(), + ) + .unwrap(); + // NFT Address (Plain) + assert_eq!( + address.clone().to_bech32_unchecked("iota"), + "iota1zrrj5edw20tshxd90aexx7lar4020w4zkjaqjhycndn86wz4szrak44cs6h" + ); + + // Restricted NFT Address (Every Capability Disallowed) + let mut address = RestrictedAddress::new(address).unwrap(); + assert_eq!( + address.clone().to_bech32_unchecked("iota"), + "iota19qgvw2n94efawzue54lhycmml5w4afa6526t5z2unzdkvlfc2kqg0kcqek0lex" + ); + + // Restricted NFT Address (Every Capability Allowed) + address.set_allowed_capabilities(AddressCapabilities::all()); + assert_eq!( + address.clone().to_bech32_unchecked("iota"), + "iota19qgvw2n94efawzue54lhycmml5w4afa6526t5z2unzdkvlfc2kqg0kcpluts738a" + ); + + address.set_allowed_capabilities(AddressCapabilities::none()); + assert_eq!( + address.clone().to_bech32_unchecked("iota"), + "iota19qgvw2n94efawzue54lhycmml5w4afa6526t5z2unzdkvlfc2kqg0kcqek0lex" + ); + + // Restricted NFT Address (Can receive Native Tokens) + address.set_allowed_capabilities([AddressCapabilityFlag::OutputsWithNativeTokens]); + assert_eq!( + address.clone().to_bech32_unchecked("iota"), + "iota19qgvw2n94efawzue54lhycmml5w4afa6526t5z2unzdkvlfc2kqg0kcpqyp0nq2r" + ); +} diff --git a/sdk/tests/types/output/account.rs b/sdk/tests/types/output/account.rs index d62f730398..ba8bc05acd 100644 --- a/sdk/tests/types/output/account.rs +++ b/sdk/tests/types/output/account.rs @@ -36,11 +36,11 @@ fn builder() { let mut builder = AccountOutput::build_with_amount(amount, account_id) .add_native_token(NativeToken::new(TokenId::from(foundry_id), 1000).unwrap()) - .add_unlock_condition(gov_address_1) - .add_unlock_condition(state_address_1) - .add_feature(sender_1) - .replace_feature(sender_2) - .replace_immutable_feature(issuer_1) + .add_unlock_condition(gov_address_1.clone()) + .add_unlock_condition(state_address_1.clone()) + .add_feature(sender_1.clone()) + .replace_feature(sender_2.clone()) + .replace_immutable_feature(issuer_1.clone()) .add_immutable_feature(issuer_2); let output = builder.clone().finish().unwrap(); @@ -57,8 +57,8 @@ fn builder() { .clear_unlock_conditions() .clear_features() .clear_immutable_features() - .replace_unlock_condition(gov_address_2) - .replace_unlock_condition(state_address_2); + .replace_unlock_condition(gov_address_2.clone()) + .replace_unlock_condition(state_address_2.clone()); let output = builder.clone().finish().unwrap(); assert_eq!(output.unlock_conditions().governor_address(), Some(&gov_address_2)); assert_eq!( @@ -76,8 +76,8 @@ fn builder() { &account_id, )) .add_unlock_condition(rand_governor_address_unlock_condition_different_from(&account_id)) - .with_features([Feature::from(metadata.clone()), sender_1.into()]) - .with_immutable_features([Feature::from(metadata.clone()), issuer_1.into()]) + .with_features([Feature::from(metadata.clone()), sender_1.clone().into()]) + .with_immutable_features([Feature::from(metadata.clone()), issuer_1.clone().into()]) .finish_with_params(ValidationParams::default().with_protocol_parameters(protocol_parameters.clone())) .unwrap(); diff --git a/sdk/tests/types/output/basic.rs b/sdk/tests/types/output/basic.rs index ae0dbea7a9..7acc124919 100644 --- a/sdk/tests/types/output/basic.rs +++ b/sdk/tests/types/output/basic.rs @@ -30,9 +30,9 @@ fn builder() { let mut builder = BasicOutput::build_with_amount(amount) .add_native_token(NativeToken::new(TokenId::from(foundry_id), 1000).unwrap()) - .add_unlock_condition(address_1) - .add_feature(sender_1) - .replace_feature(sender_2); + .add_unlock_condition(address_1.clone()) + .add_feature(sender_1.clone()) + .replace_feature(sender_2.clone()); let output = builder.clone().finish().unwrap(); assert_eq!(output.amount(), amount); @@ -42,7 +42,7 @@ fn builder() { builder = builder .clear_unlock_conditions() .clear_features() - .replace_unlock_condition(address_2); + .replace_unlock_condition(address_2.clone()); let output = builder.clone().finish().unwrap(); assert_eq!(output.unlock_conditions().address(), Some(&address_2)); assert!(output.features().is_empty()); @@ -52,7 +52,7 @@ fn builder() { let output = builder .with_minimum_storage_deposit(protocol_parameters.rent_structure()) .add_unlock_condition(rand_address_unlock_condition()) - .with_features([Feature::from(metadata.clone()), sender_1.into()]) + .with_features([Feature::from(metadata.clone()), sender_1.clone().into()]) .finish_with_params(ValidationParams::default().with_protocol_parameters(protocol_parameters.clone())) .unwrap(); diff --git a/sdk/tests/types/output/nft.rs b/sdk/tests/types/output/nft.rs index 4617325d0d..56f2e84ee5 100644 --- a/sdk/tests/types/output/nft.rs +++ b/sdk/tests/types/output/nft.rs @@ -29,10 +29,10 @@ fn builder() { let mut builder = NftOutput::build_with_amount(amount, NftId::null()) .add_native_token(NativeToken::new(TokenId::from(foundry_id), 1000).unwrap()) - .add_unlock_condition(address_1) + .add_unlock_condition(address_1.clone()) .add_feature(sender_1) - .replace_feature(sender_2) - .replace_immutable_feature(issuer_1) + .replace_feature(sender_2.clone()) + .replace_immutable_feature(issuer_1.clone()) .add_immutable_feature(issuer_2); let output = builder.clone().finish().unwrap(); @@ -45,7 +45,7 @@ fn builder() { .clear_unlock_conditions() .clear_features() .clear_immutable_features() - .replace_unlock_condition(address_2); + .replace_unlock_condition(address_2.clone()); let output = builder.clone().finish().unwrap(); assert_eq!(output.unlock_conditions().address(), Some(&address_2)); assert!(output.features().is_empty()); diff --git a/sdk/tests/types/transaction_regular_essence.rs b/sdk/tests/types/transaction_regular_essence.rs index 08eb1d02ba..2e318a5457 100644 --- a/sdk/tests/types/transaction_regular_essence.rs +++ b/sdk/tests/types/transaction_regular_essence.rs @@ -347,7 +347,7 @@ fn duplicate_output_nft() { let address = Address::from(Ed25519Address::new(bytes)); let amount = 1_000_000; let basic = BasicOutput::build_with_amount(amount) - .add_unlock_condition(AddressUnlockCondition::new(address)) + .add_unlock_condition(AddressUnlockCondition::new(address.clone())) .finish_output(protocol_parameters.token_supply()) .unwrap(); let nft_id = NftId::from(bytes); @@ -378,7 +378,7 @@ fn duplicate_output_nft_null() { let address = Address::from(Ed25519Address::new(bytes)); let amount = 1_000_000; let basic = BasicOutput::build_with_amount(amount) - .add_unlock_condition(AddressUnlockCondition::new(address)) + .add_unlock_condition(AddressUnlockCondition::new(address.clone())) .finish_output(protocol_parameters.token_supply()) .unwrap(); let nft_id = NftId::null(); @@ -406,12 +406,12 @@ fn duplicate_output_account() { let address = Address::from(Ed25519Address::new(bytes)); let amount = 1_000_000; let basic = BasicOutput::build_with_amount(amount) - .add_unlock_condition(AddressUnlockCondition::new(address)) + .add_unlock_condition(AddressUnlockCondition::new(address.clone())) .finish_output(protocol_parameters.token_supply()) .unwrap(); let account_id = AccountId::from(bytes); let account = AccountOutput::build_with_amount(1_000_000, account_id) - .add_unlock_condition(StateControllerAddressUnlockCondition::new(address)) + .add_unlock_condition(StateControllerAddressUnlockCondition::new(address.clone())) .add_unlock_condition(GovernorAddressUnlockCondition::new(address)) .finish_output(protocol_parameters.token_supply()) .unwrap(); diff --git a/sdk/tests/wallet/accounts.rs b/sdk/tests/wallet/accounts.rs index a6ee7baac5..b709ae33ca 100644 --- a/sdk/tests/wallet/accounts.rs +++ b/sdk/tests/wallet/accounts.rs @@ -148,9 +148,9 @@ async fn account_first_address_exists() -> Result<()> { let account = wallet.create_account().with_alias("Alice").finish().await?; // When the account is generated, the first public address also gets generated and added to it - assert_eq!(account.addresses().await?.len(), 1); + assert_eq!(account.addresses().await.len(), 1); // First address is a public address - assert_eq!(account.addresses().await?.first().unwrap().internal(), &false); + assert_eq!(account.addresses().await.first().unwrap().internal(), &false); tear_down(storage_path) } diff --git a/sdk/tests/wallet/address_generation.rs b/sdk/tests/wallet/address_generation.rs index 7677855da0..6eea883da3 100644 --- a/sdk/tests/wallet/address_generation.rs +++ b/sdk/tests/wallet/address_generation.rs @@ -130,7 +130,7 @@ async fn wallet_address_generation_ledger() -> Result<()> { .listen([WalletEventType::LedgerAddressGeneration], move |event| { if let WalletEvent::LedgerAddressGeneration(address) = &event.event { sender - .try_send(address.address) + .try_send(address.address.clone()) .expect("too many LedgerAddressGeneration events"); } else { panic!("expected LedgerAddressGeneration event") @@ -163,7 +163,7 @@ async fn wallet_address_generation_ledger() -> Result<()> { .recv() .await .expect("never received event") - .inner() + .into_inner() .to_bech32_unchecked("smr"), // Address generated with bip32 path: [44, 4218, 0, 0, 0]. // This address was generated with a MnemonicSecretManager and the ledger simulator mnemonic. diff --git a/sdk/tests/wallet/backup_restore.rs b/sdk/tests/wallet/backup_restore.rs index 9f9d29e8e6..2d6737e653 100644 --- a/sdk/tests/wallet/backup_restore.rs +++ b/sdk/tests/wallet/backup_restore.rs @@ -100,7 +100,7 @@ async fn backup_and_restore() -> Result<()> { // Get account let recovered_account = restore_wallet.get_account("Alice").await?; - assert_eq!(account.addresses().await?, recovered_account.addresses().await?); + assert_eq!(account.addresses().await, recovered_account.addresses().await); // secret manager is the same assert_eq!( @@ -182,7 +182,7 @@ async fn backup_and_restore_mnemonic_secret_manager() -> Result<()> { // Get account let recovered_account = restore_wallet.get_account("Alice").await?; - assert_eq!(account.addresses().await?, recovered_account.addresses().await?); + assert_eq!(account.addresses().await, recovered_account.addresses().await); // secret manager is the same assert_eq!( @@ -264,7 +264,7 @@ async fn backup_and_restore_different_coin_type() -> Result<()> { assert_eq!(new_account.details().await.coin_type(), &IOTA_COIN_TYPE); // secret manager is the same assert_eq!( - new_account.addresses().await?[0].address(), + new_account.first_address_bech32().await, "smr1qrpwecegav7eh0z363ca69laxej64rrt4e3u0rtycyuh0mam3vq3ulygj9p" ); @@ -346,8 +346,8 @@ async fn backup_and_restore_same_coin_type() -> Result<()> { // addresses are still there assert_eq!( - restored_accounts[0].addresses().await?, - account_before_backup.addresses().await? + restored_accounts[0].addresses().await, + account_before_backup.addresses().await ); // compare client options, they are not restored @@ -425,8 +425,8 @@ async fn backup_and_restore_different_coin_type_dont_ignore() -> Result<()> { // No accounts restored, because the coin type was different let restored_account = restore_wallet.get_account("Alice").await?; assert_eq!( - account.addresses().await?[0].address(), - restored_account.addresses().await?[0].address(), + account.first_address_bech32().await, + restored_account.first_address_bech32().await, ); // Restored coin type is used @@ -434,7 +434,7 @@ async fn backup_and_restore_different_coin_type_dont_ignore() -> Result<()> { assert_eq!(new_account.details().await.coin_type(), &SHIMMER_COIN_TYPE); // secret manager is restored assert_eq!( - new_account.addresses().await?[0].address(), + new_account.first_address_bech32().await, "smr1qzvjvjyqxgfx4f0m3xhn2rj24e03dwsmjz082735y3wx88v2gudu2afedhu" ); diff --git a/sdk/tests/wallet/balance.rs b/sdk/tests/wallet/balance.rs index 53f1f279e5..8c68db9c4b 100644 --- a/sdk/tests/wallet/balance.rs +++ b/sdk/tests/wallet/balance.rs @@ -103,15 +103,13 @@ async fn balance_expiration() -> Result<()> { let outputs = [BasicOutputBuilder::new_with_amount(1_000_000) // Send to account 1 with expiration to account 2, both have no amount yet .with_unlock_conditions([ - UnlockCondition::Address(AddressUnlockCondition::new( - *account_1.addresses().await?[0].address().as_ref(), - )), + UnlockCondition::Address(AddressUnlockCondition::new(account_1.first_address_bech32().await)), UnlockCondition::Expiration(ExpirationUnlockCondition::new( - *account_2.addresses().await?[0].address().as_ref(), + account_2.first_address_bech32().await, account_0.client().get_slot_index().await? + slots_until_expired, )?), ]) - .with_features([SenderFeature::new(*account_0.addresses().await?[0].address().as_ref())]) + .with_features([SenderFeature::new(account_0.first_address_bech32().await)]) .finish_output(token_supply)?]; let balance_before_tx = account_0.balance().await?; @@ -160,7 +158,7 @@ async fn balance_expiration() -> Result<()> { let outputs = [BasicOutputBuilder::new_with_amount(1_000_000) // Send to account 1 with expiration to account 2, both have no amount yet .with_unlock_conditions([AddressUnlockCondition::new( - *account_1.addresses().await?[0].address().as_ref(), + account_1.addresses().await[0].clone().into_bech32(), )]) .finish_output(token_supply)?]; let _tx = account_2.send_outputs(outputs, None).await?; @@ -182,7 +180,7 @@ async fn addresses_balance() -> Result<()> { let acc_1_addr = &account_1.generate_ed25519_addresses(1, None).await?[0]; let balance_0 = account_0 - .addresses_balance(addresses_0.iter().map(|a| a.address()).collect()) + .addresses_balance(addresses_0.iter().map(|a| a.address().clone()).collect()) .await?; let balance_0_sync = account_0.balance().await?; let to_send = balance_0.base_coin().available(); @@ -196,10 +194,10 @@ async fn addresses_balance() -> Result<()> { assert_eq!(balance_1.base_coin().available(), 0); // Send to 1 - let tx = account_0.send(to_send, acc_1_addr.address(), None).await?; + let tx = account_0.send(to_send, acc_1_addr.address().clone(), None).await?; // Balance should update without sync let balance_0 = account_0 - .addresses_balance(addresses_0.iter().map(|a| a.address()).collect()) + .addresses_balance(addresses_0.iter().map(|a| a.address().clone()).collect()) .await?; let balance_0_sync = account_0.balance().await?; assert_eq!(balance_0.base_coin().available(), 0); @@ -211,7 +209,7 @@ async fn addresses_balance() -> Result<()> { account_1.sync(None).await?; // Balance should have transferred entirely - let balance_1 = account_1.addresses_balance(vec![acc_1_addr.address()]).await?; + let balance_1 = account_1.addresses_balance(vec![acc_1_addr.address().clone()]).await?; let balance_1_sync = account_1.balance().await?; assert!(balance_1.base_coin().available() > 0); assert_eq!(balance_1, balance_1_sync); @@ -219,19 +217,23 @@ async fn addresses_balance() -> Result<()> { // Internal transfer on account 1 let acc_1_addr_2 = &account_1.generate_ed25519_addresses(1, None).await?[0]; - let tx = account_1.send(to_send / 2, acc_1_addr_2.address(), None).await?; + let tx = account_1 + .send(to_send / 2, acc_1_addr_2.address().clone(), None) + .await?; account_1 .reissue_transaction_until_included(&tx.transaction_id, None, None) .await?; let balance_1_sync = account_1.sync(None).await?; // Check the new address - let balance_1 = account_1.addresses_balance(vec![acc_1_addr_2.address()]).await?; + let balance_1 = account_1 + .addresses_balance(vec![acc_1_addr_2.address().clone()]) + .await?; assert_eq!(to_send / 2, balance_1.base_coin().available()); // Check old and new together let balance_1_total = account_1 - .addresses_balance(vec![acc_1_addr.address(), acc_1_addr_2.address()]) + .addresses_balance(vec![acc_1_addr.address().clone(), acc_1_addr_2.address().clone()]) .await?; assert_eq!(balance_1_total, balance_1_sync); diff --git a/sdk/tests/wallet/bech32_hrp_validation.rs b/sdk/tests/wallet/bech32_hrp_validation.rs index a5e43fdc47..21bc97a5e4 100644 --- a/sdk/tests/wallet/bech32_hrp_validation.rs +++ b/sdk/tests/wallet/bech32_hrp_validation.rs @@ -23,7 +23,7 @@ async fn bech32_hrp_send_amount() -> Result<()> { .send_with_params( [SendParams::new( 1_000_000, - Bech32Address::try_new("wronghrp", account.addresses().await?[0].address())?, + Bech32Address::try_new("wronghrp", account.first_address_bech32().await)?, )?], None, ) @@ -58,9 +58,10 @@ async fn bech32_hrp_prepare_output() -> Result<()> { let error = account .prepare_output( OutputParams { - recipient_address: account.addresses().await?[0] - .address() - .as_ref() + recipient_address: account.addresses().await[0] + .clone() + .into_bech32() + .into_inner() .to_bech32_unchecked("wronghrp"), amount: 1_000_000, assets: None, diff --git a/sdk/tests/wallet/burn_outputs.rs b/sdk/tests/wallet/burn_outputs.rs index 2d364aada0..a165d8ed9f 100644 --- a/sdk/tests/wallet/burn_outputs.rs +++ b/sdk/tests/wallet/burn_outputs.rs @@ -23,7 +23,7 @@ async fn mint_and_burn_nft() -> Result<()> { let account = &create_accounts_with_funds(&wallet, 1).await?[0]; let nft_options = [MintNftParams::new() - .with_address(*account.addresses().await?[0].address()) + .with_address(account.first_address_bech32().await) .with_metadata(b"some nft metadata".to_vec()) .with_immutable_metadata(b"some immutable nft metadata".to_vec())]; @@ -69,11 +69,11 @@ async fn mint_and_burn_expired_nft() -> Result<()> { let outputs = [NftOutputBuilder::new_with_amount(amount, NftId::null()) .with_unlock_conditions([ UnlockCondition::Address(AddressUnlockCondition::new( - *account_0.addresses().await?[0].address().as_ref(), + account_0.addresses().await[0].clone().into_bech32().into_inner(), )), // immediately expired to account_1 UnlockCondition::Expiration(ExpirationUnlockCondition::new( - *account_1.addresses().await?[0].address().as_ref(), + account_1.addresses().await[0].clone().into_bech32().into_inner(), 1, )?), ]) diff --git a/sdk/tests/wallet/claim_outputs.rs b/sdk/tests/wallet/claim_outputs.rs index 706811796e..1f0205ec0a 100644 --- a/sdk/tests/wallet/claim_outputs.rs +++ b/sdk/tests/wallet/claim_outputs.rs @@ -29,8 +29,8 @@ async fn claim_2_basic_micro_outputs() -> Result<()> { let tx = accounts[1] .send_with_params( [ - SendParams::new(micro_amount, *accounts[0].addresses().await?[0].address())?, - SendParams::new(micro_amount, *accounts[0].addresses().await?[0].address())?, + SendParams::new(micro_amount, accounts[0].first_address_bech32().await)?, + SendParams::new(micro_amount, accounts[0].first_address_bech32().await)?, ], TransactionOptions { allow_micro_amount: true, @@ -79,8 +79,8 @@ async fn claim_1_of_2_basic_outputs() -> Result<()> { let tx = accounts[1] .send_with_params( [ - SendParams::new(amount, *accounts[0].addresses().await?[0].address())?, - SendParams::new(0, *accounts[0].addresses().await?[0].address())?, + SendParams::new(amount, accounts[0].first_address_bech32().await)?, + SendParams::new(0, accounts[0].first_address_bech32().await)?, ], TransactionOptions { allow_micro_amount: true, @@ -132,11 +132,9 @@ async fn claim_2_basic_outputs_no_outputs_in_claim_account() -> Result<()> { let expiration_slot = account_0.client().get_slot_index().await? + 86400; let output = BasicOutputBuilder::new_with_minimum_storage_deposit(rent_structure) - .add_unlock_condition(AddressUnlockCondition::new( - *account_1.addresses().await?[0].address().as_ref(), - )) + .add_unlock_condition(AddressUnlockCondition::new(account_1.first_address_bech32().await)) .add_unlock_condition(ExpirationUnlockCondition::new( - *account_0.addresses().await?[0].address().as_ref(), + account_0.first_address_bech32().await, expiration_slot, )?) .finish_output(token_supply)?; @@ -226,11 +224,11 @@ async fn claim_2_native_tokens() -> Result<()> { .send_native_tokens( [ SendNativeTokensParams::new( - *accounts[0].addresses().await?[0].address(), + accounts[0].first_address_bech32().await, [(create_tx_0.token_id, native_token_amount)], )?, SendNativeTokensParams::new( - *accounts[0].addresses().await?[0].address(), + accounts[0].first_address_bech32().await, [(create_tx_1.token_id, native_token_amount)], )?, ], @@ -329,21 +327,17 @@ async fn claim_2_native_tokens_no_outputs_in_claim_account() -> Result<()> { .send_outputs( [ BasicOutputBuilder::new_with_minimum_storage_deposit(rent_structure) - .add_unlock_condition(AddressUnlockCondition::new( - *account_1.addresses().await?[0].address().as_ref(), - )) + .add_unlock_condition(AddressUnlockCondition::new(account_1.first_address_bech32().await)) .add_unlock_condition(ExpirationUnlockCondition::new( - *account_0.addresses().await?[0].address().as_ref(), + account_0.first_address_bech32().await, account_0.client().get_slot_index().await? + 5000, )?) .add_native_token(NativeToken::new(create_tx_0.token_id, native_token_amount)?) .finish_output(token_supply)?, BasicOutputBuilder::new_with_minimum_storage_deposit(rent_structure) - .add_unlock_condition(AddressUnlockCondition::new( - *account_1.addresses().await?[0].address().as_ref(), - )) + .add_unlock_condition(AddressUnlockCondition::new(account_1.first_address_bech32().await)) .add_unlock_condition(ExpirationUnlockCondition::new( - *account_0.addresses().await?[0].address().as_ref(), + account_0.first_address_bech32().await, account_0.client().get_slot_index().await? + 5000, )?) .add_native_token(NativeToken::new(create_tx_1.token_id, native_token_amount)?) @@ -401,22 +395,18 @@ async fn claim_2_nft_outputs() -> Result<()> { // address of the owner of the NFT NftOutputBuilder::new_with_amount(1_000_000, NftId::null()) .with_unlock_conditions([ - UnlockCondition::Address(AddressUnlockCondition::new( - *accounts[0].addresses().await?[0].address().as_ref(), - )), + UnlockCondition::Address(AddressUnlockCondition::new(accounts[0].first_address_bech32().await)), UnlockCondition::Expiration(ExpirationUnlockCondition::new( - *accounts[1].addresses().await?[0].address().as_ref(), + accounts[1].first_address_bech32().await, accounts[1].client().get_slot_index().await? + 5000, )?), ]) .finish_output(token_supply)?, NftOutputBuilder::new_with_amount(1_000_000, NftId::null()) .with_unlock_conditions([ - UnlockCondition::Address(AddressUnlockCondition::new( - *accounts[0].addresses().await?[0].address().as_ref(), - )), + UnlockCondition::Address(AddressUnlockCondition::new(accounts[0].first_address_bech32().await)), UnlockCondition::Expiration(ExpirationUnlockCondition::new( - *accounts[1].addresses().await?[0].address().as_ref(), + accounts[1].first_address_bech32().await, accounts[1].client().get_slot_index().await? + 5000, )?), ]) @@ -462,22 +452,18 @@ async fn claim_2_nft_outputs_no_outputs_in_claim_account() -> Result<()> { // address of the owner of the NFT NftOutputBuilder::new_with_amount(1_000_000, NftId::null()) .with_unlock_conditions([ - UnlockCondition::Address(AddressUnlockCondition::new( - *account_1.addresses().await?[0].address().as_ref(), - )), + UnlockCondition::Address(AddressUnlockCondition::new(account_1.first_address_bech32().await)), UnlockCondition::Expiration(ExpirationUnlockCondition::new( - *account_0.addresses().await?[0].address().as_ref(), + account_0.first_address_bech32().await, account_0.client().get_slot_index().await? + 5000, )?), ]) .finish_output(token_supply)?, NftOutputBuilder::new_with_amount(1_000_000, NftId::null()) .with_unlock_conditions([ - UnlockCondition::Address(AddressUnlockCondition::new( - *account_1.addresses().await?[0].address().as_ref(), - )), + UnlockCondition::Address(AddressUnlockCondition::new(account_1.first_address_bech32().await)), UnlockCondition::Expiration(ExpirationUnlockCondition::new( - *account_0.addresses().await?[0].address().as_ref(), + account_0.first_address_bech32().await, account_0.client().get_slot_index().await? + 5000, )?), ]) @@ -521,10 +507,7 @@ async fn claim_basic_micro_output_error() -> Result<()> { let micro_amount = 1; let tx = account_0 .send_with_params( - [SendParams::new( - micro_amount, - *account_1.addresses().await?[0].address(), - )?], + [SendParams::new(micro_amount, account_1.first_address_bech32().await)?], TransactionOptions { allow_micro_amount: true, ..Default::default() diff --git a/sdk/tests/wallet/common/mod.rs b/sdk/tests/wallet/common/mod.rs index 394e99b900..732a0abb41 100644 --- a/sdk/tests/wallet/common/mod.rs +++ b/sdk/tests/wallet/common/mod.rs @@ -74,7 +74,7 @@ pub(crate) async fn create_accounts_with_funds(wallet: &Wallet, amount: usize) - let mut new_accounts = Vec::new(); 'accounts: for _ in 0..amount { let account = wallet.create_account().finish().await?; - request_funds_from_faucet(FAUCET_URL, account.addresses().await?[0].address()).await?; + request_funds_from_faucet(FAUCET_URL, &account.first_address_bech32().await).await?; // Continue only after funds are received for _ in 0..30 { diff --git a/sdk/tests/wallet/consolidation.rs b/sdk/tests/wallet/consolidation.rs index c11fc9d92c..c0e94cf516 100644 --- a/sdk/tests/wallet/consolidation.rs +++ b/sdk/tests/wallet/consolidation.rs @@ -20,7 +20,7 @@ async fn consolidation() -> Result<()> { let amount = 1_000_000; let tx = account_0 .send_with_params( - vec![SendParams::new(amount, *account_1.addresses().await?[0].address())?; 10], + vec![SendParams::new(amount, account_1.first_address_bech32().await)?; 10], None, ) .await?; diff --git a/sdk/tests/wallet/core.rs b/sdk/tests/wallet/core.rs index 6067df3c39..d25b65467b 100644 --- a/sdk/tests/wallet/core.rs +++ b/sdk/tests/wallet/core.rs @@ -133,7 +133,7 @@ async fn shimmer_coin_type() -> Result<()> { // Creating a new account with providing a coin type will use the Shimmer coin type with shimmer testnet bech32 hrp assert_eq!( - Bech32Address::try_new("smr", account.addresses().await?[0].address())?.to_string(), + Bech32Address::try_new("smr", account.first_address_bech32().await)?.to_string(), // Address generated with bip32 path: [44, 4219, 0, 0, 0] "smr1qq724zgvdujt3jdcd3xzsuqq7wl9pwq3dvsa5zvx49rj9tme8cat65xq7jz" ); @@ -165,7 +165,7 @@ async fn iota_coin_type() -> Result<()> { // Creating a new account with providing a coin type will use the iota coin type with shimmer testnet bech32 hrp assert_eq!( - Bech32Address::try_new("smr", account.addresses().await?[0].address())?.to_string(), + Bech32Address::try_new("smr", account.first_address_bech32().await)?.to_string(), // Address generated with bip32 path: [44, 4218, 0, 0, 0] "smr1qrpwecegav7eh0z363ca69laxej64rrt4e3u0rtycyuh0mam3vq3ulygj9p" ); diff --git a/sdk/tests/wallet/output_preparation.rs b/sdk/tests/wallet/output_preparation.rs index 37e36ba2af..d1852d7ec5 100644 --- a/sdk/tests/wallet/output_preparation.rs +++ b/sdk/tests/wallet/output_preparation.rs @@ -34,7 +34,7 @@ async fn output_preparation() -> Result<()> { let output = account .prepare_output( OutputParams { - recipient_address, + recipient_address: recipient_address.clone(), amount: 500, assets: None, features: None, @@ -53,7 +53,7 @@ async fn output_preparation() -> Result<()> { let output = account .prepare_output( OutputParams { - recipient_address, + recipient_address: recipient_address.clone(), amount: 500000, assets: None, features: None, @@ -74,7 +74,7 @@ async fn output_preparation() -> Result<()> { let output = account .prepare_output( OutputParams { - recipient_address, + recipient_address: recipient_address.clone(), amount: 500000, assets: Some(Assets { native_tokens: Some(vec![native_token]), @@ -95,7 +95,7 @@ async fn output_preparation() -> Result<()> { let output = account .prepare_output( OutputParams { - recipient_address, + recipient_address: recipient_address.clone(), amount: 300000, assets: None, features: Some(Features { @@ -120,7 +120,7 @@ async fn output_preparation() -> Result<()> { let output = account .prepare_output( OutputParams { - recipient_address, + recipient_address: recipient_address.clone(), amount: 1, assets: None, features: Some(Features { @@ -148,7 +148,7 @@ async fn output_preparation() -> Result<()> { let output = account .prepare_output( OutputParams { - recipient_address, + recipient_address: recipient_address.clone(), amount: 12000, assets: None, features: Some(Features { @@ -173,7 +173,7 @@ async fn output_preparation() -> Result<()> { let output = account .prepare_output( OutputParams { - recipient_address, + recipient_address: recipient_address.clone(), amount: 1, assets: None, features: Some(Features { @@ -202,7 +202,7 @@ async fn output_preparation() -> Result<()> { let error = account .prepare_output( OutputParams { - recipient_address, + recipient_address: recipient_address.clone(), amount: 500000, assets: Some(Assets { native_tokens: None, @@ -226,7 +226,7 @@ async fn output_preparation() -> Result<()> { if let Ok(output) = account .prepare_output( OutputParams { - recipient_address, + recipient_address: recipient_address.clone(), amount: 500000, assets: Some(Assets { native_tokens: None, @@ -252,7 +252,7 @@ async fn output_preparation() -> Result<()> { Bech32Address::try_from_str("rms1qq724zgvdujt3jdcd3xzsuqq7wl9pwq3dvsa5zvx49rj9tme8cat6qptyfm")?; // Roundtrip to get the correct bech32 HRP let issuer_and_sender_address = issuer_and_sender_address_bech32 - .inner() + .into_inner() .to_bech32(account.client().get_bech32_hrp().await?); let expected_address = issuer_and_sender_address.inner(); @@ -260,7 +260,7 @@ async fn output_preparation() -> Result<()> { let output = account .prepare_output( OutputParams { - recipient_address, + recipient_address: recipient_address.clone(), amount: 500000, assets: Some(Assets { native_tokens: Some(vec![native_token]), @@ -270,7 +270,7 @@ async fn output_preparation() -> Result<()> { metadata: None, tag: None, issuer: None, - sender: Some(issuer_and_sender_address), + sender: Some(issuer_and_sender_address.clone()), }), unlocks: None, storage_deposit: None, @@ -290,13 +290,13 @@ async fn output_preparation() -> Result<()> { let error = account .prepare_output( OutputParams { - recipient_address, + recipient_address: recipient_address.clone(), amount: 500000, assets: None, features: Some(Features { metadata: None, tag: None, - issuer: Some(issuer_and_sender_address), + issuer: Some(issuer_and_sender_address.clone()), sender: None, }), unlocks: None, @@ -315,7 +315,7 @@ async fn output_preparation() -> Result<()> { let output = account .prepare_output( OutputParams { - recipient_address, + recipient_address: recipient_address.clone(), amount: 500000, assets: Some(Assets { native_tokens: None, @@ -326,8 +326,8 @@ async fn output_preparation() -> Result<()> { features: Some(Features { metadata: None, tag: None, - issuer: Some(issuer_and_sender_address), - sender: Some(issuer_and_sender_address), + issuer: Some(issuer_and_sender_address.clone()), + sender: Some(issuer_and_sender_address.clone()), }), unlocks: Some(Unlocks { expiration_slot_index: Some(SlotIndex::from(1)), @@ -359,7 +359,7 @@ async fn output_preparation() -> Result<()> { let output = account .prepare_output( OutputParams { - recipient_address, + recipient_address: recipient_address.clone(), amount: 500, assets: Some(Assets { native_tokens: None, @@ -390,7 +390,7 @@ async fn output_preparation() -> Result<()> { let output = account .prepare_output( OutputParams { - recipient_address, + recipient_address: recipient_address.clone(), amount: 42600, assets: None, features: Some(Features { @@ -443,7 +443,7 @@ async fn output_preparation_sdr() -> Result<()> { let output = account .prepare_output( OutputParams { - recipient_address, + recipient_address: recipient_address.clone(), amount: 8001, assets: None, features: None, @@ -464,7 +464,7 @@ async fn output_preparation_sdr() -> Result<()> { let output = account .prepare_output( OutputParams { - recipient_address, + recipient_address: recipient_address.clone(), amount: 42599, assets: None, features: None, @@ -486,7 +486,7 @@ async fn output_preparation_sdr() -> Result<()> { let output = account .prepare_output( OutputParams { - recipient_address, + recipient_address: recipient_address.clone(), amount: 42599, assets: None, features: None, @@ -511,7 +511,7 @@ async fn output_preparation_sdr() -> Result<()> { let output = account .prepare_output( OutputParams { - recipient_address, + recipient_address: recipient_address.clone(), amount: 42599, assets: None, features: None, @@ -542,15 +542,15 @@ async fn prepare_nft_output_features_update() -> Result<()> { let wallet = make_wallet(storage_path, None, None).await?; let accounts = &create_accounts_with_funds(&wallet, 1).await?; - let addresses = accounts[0].addresses().await?; + let addresses = accounts[0].addresses().await; let address = addresses[0].address(); let nft_options = [MintNftParams::new() - .with_address(*address) - .with_sender(*address) + .with_address(address.clone()) + .with_sender(address.clone()) .with_metadata(b"some nft metadata".to_vec()) .with_tag(b"some nft tag".to_vec()) - .with_issuer(*address) + .with_issuer(address.clone()) .with_immutable_metadata(b"some immutable nft metadata".to_vec())]; let transaction = accounts[0].mint_nfts(nft_options, None).await.unwrap(); @@ -563,7 +563,7 @@ async fn prepare_nft_output_features_update() -> Result<()> { let nft = accounts[0] .prepare_output( OutputParams { - recipient_address: *address, + recipient_address: address.clone(), amount: 1_000_000, assets: Some(Assets { native_tokens: None, @@ -585,7 +585,10 @@ async fn prepare_nft_output_features_update() -> Result<()> { .clone(); assert_eq!(nft.amount(), 1_000_000); - assert_eq!(nft.address(), accounts[0].addresses().await?[0].address().as_ref()); + assert_eq!( + nft.address(), + accounts[0].addresses().await[0].clone().into_bech32().as_ref() + ); assert!(nft.features().sender().is_none()); assert!(nft.features().tag().is_none()); assert_eq!(nft.features().metadata().unwrap().data(), &[42]); @@ -595,7 +598,7 @@ async fn prepare_nft_output_features_update() -> Result<()> { ); assert_eq!( nft.immutable_features().issuer().unwrap().address(), - accounts[0].addresses().await?[0].address().as_ref() + accounts[0].addresses().await[0].clone().into_bech32().as_ref() ); tear_down(storage_path) @@ -610,7 +613,7 @@ async fn prepare_output_remainder_dust() -> Result<()> { let wallet = make_wallet(storage_path, None, None).await?; let accounts = &create_accounts_with_funds(&wallet, 2).await?; let account = &accounts[0]; - let addresses = &accounts[1].addresses().await?; + let addresses = &accounts[1].addresses().await; let address = addresses[0].address(); let rent_structure = account.client().get_rent_structure().await?; @@ -624,7 +627,7 @@ async fn prepare_output_remainder_dust() -> Result<()> { let output = account .prepare_output( OutputParams { - recipient_address: *address, + recipient_address: address.clone(), amount: balance.base_coin().available() - 63900, assets: None, features: None, @@ -644,7 +647,7 @@ async fn prepare_output_remainder_dust() -> Result<()> { let output = account .prepare_output( OutputParams { - recipient_address: *address, + recipient_address: address.clone(), amount: minimum_required_storage_deposit - 1, // Leave less than min. deposit assets: None, features: None, @@ -668,7 +671,7 @@ async fn prepare_output_remainder_dust() -> Result<()> { let result = account .prepare_output( OutputParams { - recipient_address: *address, + recipient_address: address.clone(), amount: minimum_required_storage_deposit - 1, // Leave less than min. deposit assets: None, features: None, @@ -688,7 +691,7 @@ async fn prepare_output_remainder_dust() -> Result<()> { let output = account .prepare_output( OutputParams { - recipient_address: *address, + recipient_address: address.clone(), amount: 100, // leave more behind than min. deposit assets: None, features: None, @@ -712,7 +715,7 @@ async fn prepare_output_remainder_dust() -> Result<()> { let output = account .prepare_output( OutputParams { - recipient_address: *address, + recipient_address: address.clone(), amount: 100, // leave more behind than min. deposit assets: None, features: None, @@ -749,14 +752,17 @@ async fn prepare_output_only_single_nft() -> Result<()> { let account_0 = &create_accounts_with_funds(&wallet, 1).await?[0]; // Create second account without funds, so it only gets the NFT let account_1 = wallet.create_account().finish().await?; - let addresses = &account_0.addresses().await?; + let addresses = &account_0.addresses().await; let account_0_address = addresses[0].address(); - let addresses = &account_1.addresses().await?; + let addresses = &account_1.addresses().await; let account_1_address = addresses[0].address(); // Send NFT to second account let tx = account_0 - .mint_nfts([MintNftParams::new().try_with_address(account_1_address)?], None) + .mint_nfts( + [MintNftParams::new().try_with_address(account_1_address.clone())?], + None, + ) .await?; account_0 .reissue_transaction_until_included(&tx.transaction_id, None, None) @@ -771,7 +777,7 @@ async fn prepare_output_only_single_nft() -> Result<()> { let output = account_1 .prepare_output( OutputParams { - recipient_address: *account_0_address, + recipient_address: account_0_address.clone(), amount: nft_data.output.amount(), assets: Some(Assets { native_tokens: None, @@ -809,15 +815,15 @@ async fn prepare_existing_nft_output_gift() -> Result<()> { let wallet = make_wallet(storage_path, None, None).await?; let accounts = &create_accounts_with_funds(&wallet, 1).await?; - let addresses = accounts[0].addresses().await?; + let addresses = accounts[0].addresses().await; let address = addresses[0].address(); let nft_options = [MintNftParams::new() - .with_address(*address) - .with_sender(*address) + .with_address(address.clone()) + .with_sender(address.clone()) .with_metadata(b"some nft metadata".to_vec()) .with_tag(b"some nft tag".to_vec()) - .with_issuer(*address) + .with_issuer(address.clone()) .with_immutable_metadata(b"some immutable nft metadata".to_vec())]; let transaction = accounts[0].mint_nfts(nft_options, None).await.unwrap(); @@ -830,7 +836,7 @@ async fn prepare_existing_nft_output_gift() -> Result<()> { let nft = accounts[0] .prepare_output( OutputParams { - recipient_address: *address, + recipient_address: address.clone(), amount: 0, assets: Some(Assets { native_tokens: None, @@ -854,7 +860,10 @@ async fn prepare_existing_nft_output_gift() -> Result<()> { assert_eq!(nft.amount(), minimum_storage_deposit); assert_eq!(nft.amount(), 52300); - assert_eq!(nft.address(), accounts[0].addresses().await?[0].address().as_ref()); + assert_eq!( + nft.address(), + accounts[0].addresses().await[0].clone().into_bech32().as_ref() + ); assert!(nft.features().is_empty()); assert_eq!( nft.immutable_features().metadata().unwrap().data(), @@ -862,7 +871,7 @@ async fn prepare_existing_nft_output_gift() -> Result<()> { ); assert_eq!( nft.immutable_features().issuer().unwrap().address(), - accounts[0].addresses().await?[0].address().as_ref() + accounts[0].addresses().await[0].clone().into_bech32().as_ref() ); tear_down(storage_path) diff --git a/sdk/tests/wallet/syncing.rs b/sdk/tests/wallet/syncing.rs index 529e5479f8..306d24d1d6 100644 --- a/sdk/tests/wallet/syncing.rs +++ b/sdk/tests/wallet/syncing.rs @@ -56,19 +56,19 @@ async fn sync_only_most_basic_outputs() -> Result<()> { let account_0 = &create_accounts_with_funds(&wallet, 1).await?[0]; let account_1 = wallet.create_account().finish().await?; - let account_1_address = *account_1.addresses().await?[0].address().as_ref(); + let account_1_address = account_1.first_address_bech32().await; let token_supply = account_0.client().get_token_supply().await?; // Only one basic output without further unlock conditions let outputs = [ BasicOutputBuilder::new_with_amount(1_000_000) - .with_unlock_conditions([AddressUnlockCondition::new(account_1_address)]) + .with_unlock_conditions([AddressUnlockCondition::new(account_1_address.clone())]) .finish_output(token_supply)?, BasicOutputBuilder::new_with_amount(1_000_000) .with_unlock_conditions([ - UnlockCondition::Address(AddressUnlockCondition::new(account_1_address)), + UnlockCondition::Address(AddressUnlockCondition::new(account_1_address.clone())), UnlockCondition::Expiration(ExpirationUnlockCondition::new( - account_1_address, + account_1_address.clone(), // Already expired account_0.client().get_slot_index().await? - 5000, )?), @@ -76,9 +76,9 @@ async fn sync_only_most_basic_outputs() -> Result<()> { .finish_output(token_supply)?, BasicOutputBuilder::new_with_amount(1_000_000) .with_unlock_conditions([ - UnlockCondition::Address(AddressUnlockCondition::new(account_1_address)), + UnlockCondition::Address(AddressUnlockCondition::new(account_1_address.clone())), UnlockCondition::Expiration(ExpirationUnlockCondition::new( - account_1_address, + account_1_address.clone(), // Not expired account_0.client().get_slot_index().await? + 5000, )?), @@ -86,30 +86,32 @@ async fn sync_only_most_basic_outputs() -> Result<()> { .finish_output(token_supply)?, BasicOutputBuilder::new_with_amount(1_000_000) .with_unlock_conditions([ - UnlockCondition::Address(AddressUnlockCondition::new(account_1_address)), + UnlockCondition::Address(AddressUnlockCondition::new(account_1_address.clone())), UnlockCondition::StorageDepositReturn(StorageDepositReturnUnlockCondition::new( - account_1_address, + account_1_address.clone(), 1_000_000, token_supply, )?), ]) .finish_output(token_supply)?, NftOutputBuilder::new_with_amount(1_000_000, NftId::null()) - .with_unlock_conditions([AddressUnlockCondition::new(account_1_address)]) + .with_unlock_conditions([AddressUnlockCondition::new(account_1_address.clone())]) .finish_output(token_supply)?, NftOutputBuilder::new_with_amount(1_000_000, NftId::null()) .with_unlock_conditions([ - UnlockCondition::Address(AddressUnlockCondition::new(account_1_address)), + UnlockCondition::Address(AddressUnlockCondition::new(account_1_address.clone())), UnlockCondition::Expiration(ExpirationUnlockCondition::new( - account_1_address, + account_1_address.clone(), account_0.client().get_slot_index().await? + 5000, )?), ]) .finish_output(token_supply)?, AccountOutputBuilder::new_with_amount(1_000_000, AccountId::null()) .with_unlock_conditions([ - UnlockCondition::StateControllerAddress(StateControllerAddressUnlockCondition::new(account_1_address)), - UnlockCondition::GovernorAddress(GovernorAddressUnlockCondition::new(account_1_address)), + UnlockCondition::StateControllerAddress(StateControllerAddressUnlockCondition::new( + account_1_address.clone(), + )), + UnlockCondition::GovernorAddress(GovernorAddressUnlockCondition::new(account_1_address.clone())), ]) .finish_output(token_supply)?, ]; @@ -135,14 +137,14 @@ async fn sync_only_most_basic_outputs() -> Result<()> { assert!(output_data.output.is_basic()); assert_eq!(output_data.output.unlock_conditions().unwrap().len(), 1); assert_eq!( - *output_data + output_data .output .unlock_conditions() .unwrap() .address() .unwrap() .address(), - account_1_address + account_1_address.as_ref() ); }); @@ -160,13 +162,13 @@ async fn sync_incoming_transactions() -> Result<()> { let account_0 = &create_accounts_with_funds(&wallet, 1).await?[0]; let account_1 = wallet.create_account().finish().await?; - let account_1_address = *account_1.addresses().await?[0].address().as_ref(); + let account_1_address = account_1.first_address_bech32().await; let token_supply = account_0.client().get_token_supply().await?; let outputs = [ BasicOutputBuilder::new_with_amount(750_000) - .with_unlock_conditions([AddressUnlockCondition::new(account_1_address)]) + .with_unlock_conditions([AddressUnlockCondition::new(account_1_address.clone())]) .finish_output(token_supply)?, BasicOutputBuilder::new_with_amount(250_000) .with_unlock_conditions([AddressUnlockCondition::new(account_1_address)]) @@ -211,7 +213,7 @@ async fn background_syncing() -> Result<()> { iota_sdk::client::request_funds_from_faucet( crate::wallet::common::FAUCET_URL, - account.addresses().await?[0].address(), + &account.first_address_bech32().await, ) .await?; diff --git a/sdk/tests/wallet/transactions.rs b/sdk/tests/wallet/transactions.rs index ce6f2dc3a8..4741a1049c 100644 --- a/sdk/tests/wallet/transactions.rs +++ b/sdk/tests/wallet/transactions.rs @@ -18,10 +18,7 @@ async fn send_amount() -> Result<()> { let amount = 1_000_000; let tx = account_0 - .send_with_params( - [SendParams::new(amount, *account_1.addresses().await?[0].address())?], - None, - ) + .send_with_params([SendParams::new(amount, account_1.first_address_bech32().await)?], None) .await?; account_0 @@ -51,7 +48,7 @@ async fn send_amount_127_outputs() -> Result<()> { vec![ SendParams::new( amount, - *account_1.addresses().await?[0].address(), + account_1.first_address_bech32().await, )?; // Only 127, because we need one remainder 127 @@ -85,7 +82,7 @@ async fn send_amount_custom_input() -> Result<()> { let amount = 1_000_000; let tx = account_0 .send_with_params( - vec![SendParams::new(amount, *account_1.addresses().await?[0].address())?; 10], + vec![SendParams::new(amount, account_1.first_address_bech32().await)?; 10], None, ) .await?; @@ -101,7 +98,7 @@ async fn send_amount_custom_input() -> Result<()> { let custom_input = &account_1.unspent_outputs(None).await?[5]; let tx = account_1 .send_with_params( - [SendParams::new(amount, *account_0.addresses().await?[0].address())?], + [SendParams::new(amount, account_0.first_address_bech32().await)?], Some(TransactionOptions { custom_inputs: Some(vec![custom_input.output_id]), ..Default::default() @@ -125,7 +122,7 @@ async fn send_nft() -> Result<()> { let accounts = &create_accounts_with_funds(&wallet, 2).await?; let nft_options = [MintNftParams::new() - .with_address(*accounts[0].addresses().await?[0].address()) + .with_address(accounts[0].first_address_bech32().await) .with_metadata(b"some nft metadata".to_vec()) .with_immutable_metadata(b"some immutable nft metadata".to_vec())]; @@ -138,10 +135,7 @@ async fn send_nft() -> Result<()> { // Send to account 1 let transaction = accounts[0] .send_nft( - [SendNftParams::new( - *accounts[1].addresses().await?[0].address(), - nft_id, - )?], + [SendNftParams::new(accounts[1].first_address_bech32().await, nft_id)?], None, ) .await @@ -171,7 +165,7 @@ async fn send_with_note() -> Result<()> { let amount = 1_000_000; let tx = account_0 .send_with_params( - [SendParams::new(amount, *account_1.addresses().await?[0].address())?], + [SendParams::new(amount, account_1.first_address_bech32().await)?], Some(TransactionOptions { note: Some(String::from("send_with_note")), ..Default::default() @@ -207,7 +201,7 @@ async fn conflicting_transaction() -> Result<()> { .send_with_params( [SendParams::new( 1_000_000, - *wallet_0_account.addresses().await?[0].address(), + wallet_0_account.first_address_bech32().await, )?], None, ) @@ -222,7 +216,7 @@ async fn conflicting_transaction() -> Result<()> { // Something in the transaction must be different than in the first one, otherwise it will be the same // one 2_000_000, - *wallet_0_account.addresses().await?[0].address(), + wallet_0_account.first_address_bech32().await, )?], None, ) @@ -289,10 +283,7 @@ async fn prepare_transaction_ledger() -> Result<()> { .await; let tx = account_0 - .send_with_params( - [SendParams::new(amount, *account_1.addresses().await?[0].address())?], - None, - ) + .send_with_params([SendParams::new(amount, account_1.first_address_bech32().await)?], None) .await?; let data = receiver.recv().await.expect("never recieved event");