Skip to content
This repository was archived by the owner on May 22, 2025. It is now read-only.

Commit 3e44c80

Browse files
Alex6323Thoralf-MDaughterOfMars
authored
Clean up wallet syncing code (#2108)
* const fn * sync options * move instead of clone * combinators * rm unnecessary cloning 1 * rm unnecessary cloning 2 * slices; impl Into<Option> * refactor * get rid of output_data * nits * clippy * nit * nest * nit * update python binding * update nodejs binding * update core binding * rm output_data.py * nit * suggestion Co-authored-by: Thoralf-M <[email protected]> * rename local var * fix tests and clippy * ci-doc * rm HashSet piping of foundry output ids * rm TODO * fix nodejs how-to * remove todo * undo rename * rust: cleanup * python: cleanup * nodejs: cleanup * rust: more cleanup * ... * .... * ..... --------- Co-authored-by: Thoralf-M <[email protected]> Co-authored-by: DaughterOfMars <[email protected]>
1 parent 19294a7 commit 3e44c80

File tree

16 files changed

+259
-232
lines changed

16 files changed

+259
-232
lines changed

bindings/core/src/method/wallet.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,8 @@ pub enum WalletMethod {
139139
/// Expected response: [`Transaction`](crate::Response::Transaction)
140140
#[serde(rename_all = "camelCase")]
141141
GetIncomingTransaction { transaction_id: TransactionId },
142-
/// Get the [`OutputData`](iota_sdk::wallet::types::OutputData) of an output stored in the wallet.
143-
/// Expected response: [`OutputData`](crate::Response::OutputData)
142+
/// Get the [`OutputData`](iota_sdk::wallet::types::OutputData) of an output stored
143+
/// in the wallet. Expected response: [`OutputData`](crate::Response::OutputData)
144144
#[serde(rename_all = "camelCase")]
145145
GetOutput { output_id: OutputId },
146146
// /// Expected response: [`ParticipationEvent`](crate::Response::ParticipationEvent)

bindings/nodejs/examples/how_tos/wallet/consolidate-outputs.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright 2023 IOTA Stiftung
22
// SPDX-License-Identifier: Apache-2.0
33

4-
import { CommonOutput, Utils, Wallet, initLogger } from '@iota/sdk';
4+
import { CommonOutput, Wallet, initLogger } from '@iota/sdk';
55

66
// This example uses secrets in environment variables for simplicity which should not be done in production.
77
require('dotenv').config({ path: '.env' });
@@ -46,11 +46,10 @@ async function run() {
4646
const outputs = await wallet.unspentOutputs();
4747
console.log('Outputs BEFORE consolidation:');
4848

49-
outputs.forEach(({ output, address }, i) => {
49+
outputs.forEach(({ output }, i) => {
5050
console.log(`OUTPUT #${i}`);
5151
console.log(
52-
'- address: %s\n- amount: %d\n- native token: %s',
53-
Utils.addressToBech32(address, 'rms'),
52+
'- amount: %d\n- native token: %s',
5453
output.getAmount(),
5554
output instanceof CommonOutput
5655
? (output as CommonOutput).getNativeToken() ?? []
@@ -80,11 +79,10 @@ async function run() {
8079

8180
// Outputs after consolidation
8281
console.log('Outputs AFTER consolidation:');
83-
outputs.forEach(({ output, address }, i) => {
82+
outputs.forEach(({ output }, i) => {
8483
console.log(`OUTPUT #${i}`);
8584
console.log(
86-
'- address: %s\n- amount: %d\n- native tokens: %s',
87-
Utils.addressToBech32(address, 'rms'),
85+
'- amount: %d\n- native tokens: %s',
8886
output.getAmount(),
8987
output instanceof CommonOutput
9088
? (output as CommonOutput).getNativeToken()

bindings/nodejs/lib/types/wallet/output.ts

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22
// SPDX-License-Identifier: Apache-2.0
33

44
import { Type } from 'class-transformer';
5-
import { Address, AddressDiscriminator } from '../block/address';
65
import { Output, OutputDiscriminator, OutputId } from '../block/output';
7-
import { OutputMetadataResponse } from '../models/api';
6+
import { OutputIdProof, OutputMetadataResponse } from '../models/api';
87

98
/** Output to claim */
109
export enum OutputsToClaim {
@@ -15,28 +14,23 @@ export enum OutputsToClaim {
1514
All = 'All',
1615
}
1716

18-
/** An output with metadata */
17+
/** An output with additional data */
1918
export class OutputData {
20-
/** The identifier of an Output */
21-
outputId!: OutputId;
22-
/** The metadata of the output */
23-
metadata!: OutputMetadataResponse;
24-
/** The actual Output */
19+
/** The output itself */
2520
@Type(() => Output, {
2621
discriminator: OutputDiscriminator,
2722
})
2823
output!: Output;
29-
/** Associated account address */
30-
@Type(() => Address, {
31-
discriminator: AddressDiscriminator,
32-
})
33-
address!: Address;
34-
/** Network ID */
24+
/** The metadata of the output */
25+
metadata!: OutputMetadataResponse;
26+
/** The output ID proof */
27+
OutputIdProof!: OutputIdProof;
28+
/** The corresponding output ID */
29+
outputId!: OutputId;
30+
/** The network ID the output belongs to */
3531
networkId!: string;
36-
/** Remainder */
32+
/** Whether the output represents a remainder amount */
3733
remainder!: boolean;
38-
/** BIP32 path */
39-
chain?: Segment[];
4034
}
4135

4236
/** A Segment of the BIP32 path*/

bindings/python/iota_sdk/types/output_data.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,19 @@
1313
@json
1414
@dataclass
1515
class OutputData:
16-
"""Output data.
16+
"""An output with additional data.
1717
1818
Attributes:
19-
output_id: With the output data corresponding output ID.
20-
metadata: With the output corresponding metadata.
21-
output: The output object itself.
19+
output: The output itself.
20+
metadata: The metadata of the output.
2221
output_id_proof: The output ID proof.
22+
output_id: The corresponding output ID.
2323
network_id: The network ID the output belongs to.
2424
remainder: Whether the output represents a remainder amount.
2525
"""
26-
output_id: OutputId
27-
metadata: OutputMetadata
2826
output: Output
27+
metadata: OutputMetadata
2928
output_id_proof: OutputIdProof
29+
output_id: OutputId
3030
network_id: str
3131
remainder: bool

bindings/python/iota_sdk/wallet/wallet.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616
from iota_sdk.types.client_options import ClientOptions
1717
from iota_sdk.types.filter_options import FilterOptions
1818
from iota_sdk.types.native_token import NativeToken
19+
from iota_sdk.types.output import BasicOutput, NftOutput, Output, deserialize_output
1920
from iota_sdk.types.output_data import OutputData
2021
from iota_sdk.types.output_id import OutputId
21-
from iota_sdk.types.output import BasicOutput, NftOutput, Output, deserialize_output
2222
from iota_sdk.types.output_params import OutputParams
2323
from iota_sdk.types.transaction_data import PreparedTransactionData, SignedTransactionData
2424
from iota_sdk.types.transaction_id import TransactionId

cli/src/wallet_cli/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1765,14 +1765,14 @@ pub async fn prompt_internal(
17651765
Ok(PromptResponse::Reprompt)
17661766
}
17671767

1768-
fn print_outputs(mut outputs: Vec<OutputData>, title: &str) -> Result<(), Error> {
1769-
if outputs.is_empty() {
1768+
fn print_outputs(mut outputs_data: Vec<OutputData>, title: &str) -> Result<(), Error> {
1769+
if outputs_data.is_empty() {
17701770
println_log_info!("No outputs found");
17711771
} else {
17721772
println_log_info!("{title}");
1773-
outputs.sort_unstable_by_key(|o| o.output_id);
1773+
outputs_data.sort_unstable_by_key(|o| o.output_id);
17741774

1775-
for (i, output_data) in outputs.into_iter().enumerate() {
1775+
for (i, output_data) in outputs_data.into_iter().enumerate() {
17761776
let kind_str = if output_data.output.is_implicit_account() {
17771777
"ImplicitAccount"
17781778
} else {

sdk/src/wallet/core/operations/background_syncing.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,12 @@ where
2828
/// Start the background syncing process for the wallet, default interval is 7 seconds
2929
pub async fn start_background_syncing(
3030
&self,
31-
options: Option<SyncOptions>,
31+
options: impl Into<Option<SyncOptions>> + Send,
3232
interval: Option<Duration>,
3333
) -> Result<(), WalletError> {
3434
log::debug!("[start_background_syncing]");
3535

36+
let options = options.into();
3637
let (tx_background_sync, mut rx_background_sync) = self.background_syncing_status.clone();
3738

3839
// stop existing process if running

sdk/src/wallet/operations/syncing/addresses/output_ids/account_foundry.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ impl<S: 'static + SecretManage> Wallet<S> {
5555
) -> Result<Vec<OutputId>, WalletError> {
5656
log::debug!("[SYNC] get_foundry_output_ids");
5757
// Get account outputs, so we can then get the foundry outputs with the account addresses
58-
let account_outputs_with_meta = self.get_outputs(account_output_ids.to_vec()).await?;
58+
let account_outputs_with_meta = self.get_outputs_request_unknown(account_output_ids).await?;
5959

6060
let bech32_hrp = self.client().get_bech32_hrp().await?;
6161

@@ -77,14 +77,9 @@ impl<S: 'static + SecretManage> Wallet<S> {
7777
}
7878
}
7979

80-
let mut output_ids = HashSet::new();
8180
let results: Vec<Result<OutputIdsResponse, WalletError>> = futures::future::try_join_all(tasks).await?;
81+
let responses: Vec<OutputIdsResponse> = results.into_iter().collect::<Result<Vec<_>, _>>()?;
8282

83-
for res in results {
84-
let foundry_output_ids = res?;
85-
output_ids.extend(foundry_output_ids.items);
86-
}
87-
88-
Ok(output_ids.into_iter().collect())
83+
Ok(responses.into_iter().flat_map(|res| res.items).collect())
8984
}
9085
}

sdk/src/wallet/operations/syncing/addresses/output_ids/mod.rs

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@ use crate::{
2020
},
2121
types::block::{address::Bech32Address, output::OutputId},
2222
wallet::{
23-
constants::PARALLEL_REQUESTS_AMOUNT, operations::syncing::SyncOptions,
24-
types::address::AddressWithUnspentOutputs, Wallet, WalletError,
23+
constants::PARALLEL_REQUESTS_AMOUNT,
24+
operations::syncing::SyncOptions,
25+
types::address::{AddressWithUnspentOutputIds, SpentOutputId},
26+
Wallet, WalletError,
2527
},
2628
};
2729

@@ -217,11 +219,8 @@ impl<S: 'static + SecretManage> Wallet<S> {
217219
let results = futures::future::try_join_all(tasks).await?;
218220

219221
// Get all results
220-
let mut output_ids = HashSet::new();
221-
for res in results {
222-
let found_output_ids = res?;
223-
output_ids.extend(found_output_ids);
224-
}
222+
let output_ids = results.into_iter().collect::<Result<Vec<_>, _>>()?;
223+
let output_ids: HashSet<OutputId> = HashSet::from_iter(output_ids.into_iter().flat_map(|v| v.into_iter()));
225224

226225
Ok(output_ids.into_iter().collect())
227226
}
@@ -230,20 +229,20 @@ impl<S: 'static + SecretManage> Wallet<S> {
230229
/// return spent outputs separated
231230
pub(crate) async fn get_output_ids_for_addresses(
232231
&self,
233-
addresses_with_unspent_outputs: Vec<AddressWithUnspentOutputs>,
232+
addresses: &[AddressWithUnspentOutputIds],
234233
options: &SyncOptions,
235-
) -> Result<(Vec<AddressWithUnspentOutputs>, Vec<OutputId>), WalletError> {
234+
) -> Result<(Vec<AddressWithUnspentOutputIds>, Vec<SpentOutputId>), WalletError> {
236235
log::debug!("[SYNC] start get_output_ids_for_addresses");
237236
let address_output_ids_start_time = Instant::now();
238237

239-
let mut addresses_with_outputs = Vec::new();
238+
let mut addresses_with_unspent_outputs = Vec::new();
240239
// spent outputs or account/nft/foundries that don't get synced anymore, because of other sync options
241-
let mut spent_or_not_anymore_synced_outputs = Vec::new();
240+
let mut spent_or_ignored_outputs = Vec::new();
242241

243242
// We split the addresses into chunks so we don't get timeouts if we have thousands
244-
for addresses_chunk in &mut addresses_with_unspent_outputs
243+
for addresses_chunk in addresses
245244
.chunks(PARALLEL_REQUESTS_AMOUNT)
246-
.map(|x: &[AddressWithUnspentOutputs]| x.to_vec())
245+
.map(|x: &[AddressWithUnspentOutputIds]| x.to_vec())
247246
{
248247
let results: Vec<Result<_, WalletError>>;
249248
#[cfg(target_family = "wasm")]
@@ -276,35 +275,36 @@ impl<S: 'static + SecretManage> Wallet<S> {
276275
results = futures::future::try_join_all(tasks).await?;
277276
}
278277

279-
for res in results {
280-
let (mut address, output_ids): (AddressWithUnspentOutputs, Vec<OutputId>) = res?;
278+
let addresses_with_new_unspent_output_ids = results.into_iter().collect::<Result<Vec<_>, _>>()?;
279+
280+
for (mut address, new_unspent_output_ids) in addresses_with_new_unspent_output_ids {
281281
// only return addresses with outputs
282-
if !output_ids.is_empty() {
282+
if !new_unspent_output_ids.is_empty() {
283283
// outputs we had before, but now not anymore, got spent or are account/nft/foundries that don't
284284
// get synced anymore because of other sync options
285-
for output_id in address.output_ids {
286-
if !output_ids.contains(&output_id) {
287-
spent_or_not_anymore_synced_outputs.push(output_id);
285+
for output_id in address.unspent_output_ids {
286+
if !new_unspent_output_ids.contains(&output_id) {
287+
spent_or_ignored_outputs.push(output_id);
288288
}
289289
}
290-
address.output_ids = output_ids;
291-
addresses_with_outputs.push(address);
290+
address.unspent_output_ids = new_unspent_output_ids;
291+
addresses_with_unspent_outputs.push(address);
292292
} else {
293293
// outputs we had before, but now not anymore, got spent or are account/nft/foundries that don't
294294
// get synced anymore because of other sync options
295-
spent_or_not_anymore_synced_outputs.extend(address.output_ids);
295+
spent_or_ignored_outputs.extend(address.unspent_output_ids);
296296
}
297297
}
298298
}
299299

300300
log::debug!(
301-
"[SYNC] spent or not anymore synced account/nft/foundries outputs: {:?}",
302-
spent_or_not_anymore_synced_outputs
301+
"[SYNC] spent or ignored account/nft/foundries outputs: {:?}",
302+
spent_or_ignored_outputs
303303
);
304304
log::debug!(
305305
"[SYNC] finished get_output_ids_for_addresses in {:.2?}",
306306
address_output_ids_start_time.elapsed()
307307
);
308-
Ok((addresses_with_outputs, spent_or_not_anymore_synced_outputs))
308+
Ok((addresses_with_unspent_outputs, spent_or_ignored_outputs))
309309
}
310310
}

sdk/src/wallet/operations/syncing/addresses/outputs.rs

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,47 +8,52 @@ use crate::{
88
wallet::{
99
constants::PARALLEL_REQUESTS_AMOUNT,
1010
task,
11-
types::{address::AddressWithUnspentOutputs, OutputData},
11+
types::address::{AddressWithUnspentOutputIds, AddressWithUnspentOutputs},
1212
Wallet, WalletError,
1313
},
1414
};
1515

1616
impl<S: 'static + SecretManage> Wallet<S> {
17-
/// Get outputs from addresses
17+
/// Get unspent outputs from addresses
1818
pub(crate) async fn get_outputs_from_address_output_ids(
1919
&self,
20-
addresses_with_unspent_outputs: Vec<AddressWithUnspentOutputs>,
21-
) -> Result<Vec<(AddressWithUnspentOutputs, Vec<OutputData>)>, WalletError> {
20+
addresses_with_unspent_output_ids: &[AddressWithUnspentOutputIds],
21+
) -> Result<Vec<AddressWithUnspentOutputs>, WalletError> {
2222
log::debug!("[SYNC] start get_outputs_from_address_output_ids");
2323
let address_outputs_start_time = Instant::now();
2424

25+
let network_id = self.client().get_network_id().await?;
26+
2527
let mut addresses_with_outputs = Vec::new();
2628

2729
// We split the addresses into chunks so we don't get timeouts if we have thousands
28-
for addresses_chunk in &mut addresses_with_unspent_outputs
30+
for addresses_chunk in addresses_with_unspent_output_ids
2931
.chunks(PARALLEL_REQUESTS_AMOUNT)
30-
.map(|x: &[AddressWithUnspentOutputs]| x.to_vec())
32+
.map(|x: &[AddressWithUnspentOutputIds]| x.to_vec())
3133
{
3234
let mut tasks = Vec::new();
33-
for address_with_unspent_outputs in addresses_chunk {
35+
for address_with_unspent_output_ids in addresses_chunk {
3436
let wallet = self.clone();
3537
tasks.push(async move {
3638
task::spawn(async move {
3739
let unspent_outputs_with_metadata = wallet
38-
.get_outputs(address_with_unspent_outputs.output_ids.clone())
40+
.get_outputs_request_unknown(address_with_unspent_output_ids.unspent_output_ids())
3941
.await?;
40-
let unspent_outputs_data = wallet
41-
.output_response_to_output_data(unspent_outputs_with_metadata)
42+
let unspent_outputs = wallet
43+
.output_response_to_output_data(unspent_outputs_with_metadata, network_id)
4244
.await?;
43-
Ok((address_with_unspent_outputs, unspent_outputs_data))
45+
46+
Ok(AddressWithUnspentOutputs {
47+
address_with_unspent_output_ids,
48+
unspent_outputs,
49+
})
4450
})
4551
.await
4652
});
4753
}
4854
let results: Vec<Result<_, WalletError>> = futures::future::try_join_all(tasks).await?;
49-
for res in results {
50-
addresses_with_outputs.push(res?);
51-
}
55+
let result = results.into_iter().collect::<Result<Vec<_>, _>>()?;
56+
addresses_with_outputs.extend(result.into_iter());
5257
}
5358
log::debug!(
5459
"[SYNC] finished get_outputs_from_address_output_ids in {:.2?}",

0 commit comments

Comments
 (0)