datalayer-driver 6.0.0

Native Chia DataLayer Driver for storing and retrieving data in Chia blockchain
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
//! # DataLayer Driver
//!
//! Native Chia DataLayer Driver for storing and retrieving data in Chia blockchain.
//!
//! This crate provides Rust APIs for interacting with Chia's DataLayer,
//! including minting, updating, and syncing data stores.
//!
//! ## Features
//!
//! - Mint new data stores
//! - Update store metadata and ownership
//! - Sync stores from the blockchain
//! - Oracle spend functionality
//! - Server coin management
//! - Fee management utilities

// `WalletError` is this crate's single error type, so nearly every fallible function returns it by
// value. chia-wallet-sdk 0.36 grew `ClientError` to ~136 bytes, which pushes `WalletError` past
// clippy's 128-byte `result_large_err` threshold — an upstream size change, not a defect in these
// signatures. Boxing the variant would shrink it, but that reshapes the crate's PUBLIC error enum
// and every construction and match site with it, which is a deliberate refactor rather than part of
// a dependency move. Tracked as a follow-up; suppressed here so the size of an upstream struct does
// not silently become a reason to stop building.
#![allow(clippy::result_large_err)]

// Re-export core types from dependencies
pub use chia_bls::{master_to_wallet_unhardened, PublicKey, SecretKey, Signature};
pub use chia_protocol::{Bytes, Bytes32, Coin, CoinSpend, CoinState, Program, SpendBundle};
pub use chia_puzzle_types::{EveProof, LineageProof, Proof};
pub use chia_wallet_sdk::client::Peer;
pub use chia_wallet_sdk::driver::{
    Datastore, DatastoreInfo, DatastoreMetadata, DelegatedPuzzle, P2ParentCoin,
};
pub use chia_wallet_sdk::utils::Address;

// Re-export async_api and constants modules at the top level for convenience
pub use async_api::{connect_peer, connect_random, create_tls_connector, NetworkType};
pub use constants::{get_mainnet_genesis_challenge, get_testnet11_genesis_challenge};

// Internal modules
mod dig_coin;
mod dig_collateral_coin;
mod error;
pub mod types;
pub mod wallet;
pub mod xch_server_coin;

// Re-export types from internal modules
pub use types::{
    BlsPair, SimulatorPuzzle, SuccessResponse, UnspentCoinStates, UnspentCoinsResponse,
};
pub use wallet::{
    create_simple_did, generate_did_proof, generate_did_proof_from_chain,
    generate_did_proof_manual, get_fee_estimate, get_header_hash, get_store_creation_height,
    get_unspent_coin_states, is_coin_spent, look_up_possible_launchers, mint_nft,
    spend_xch_server_coins, subscribe_to_coin_states, sync_store, sync_store_using_launcher_id,
    unsubscribe_from_coin_states, verify_signature, DataStoreInnerSpend, PossibleLaunchersResponse,
    SyncStoreResponse, TargetNetwork,
};
pub use xch_server_coin::{morph_launcher_id, XchServerCoin};
pub use {dig_coin::DigCoin, dig_collateral_coin::DigCollateralCoin};

use hex_literal::hex;

// Type aliases for convenience
pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;

// Helper functions for common conversions
use chia_puzzle_types::{standard::StandardArgs, DeriveSynthetic};
// Helper functions for common conversions
use xch_server_coin::NewXchServerCoin;

pub const DIG_MIN_HEIGHT: u32 = 5777842;
pub const DIG_MIN_HEIGHT_HEADER_HASH: Bytes32 = Bytes32::new(hex!(
    "b29a4daac2434fd17a36e15ba1aac5d65012d4a66f99bed0bf2b5342e92e562c"
));

/// Converts a master public key to a wallet synthetic key.
pub fn master_public_key_to_wallet_synthetic_key(public_key: &PublicKey) -> PublicKey {
    master_to_wallet_unhardened(public_key, 0).derive_synthetic()
}

/// Converts a master public key to the first puzzle hash.
pub fn master_public_key_to_first_puzzle_hash(public_key: &PublicKey) -> Bytes32 {
    let wallet_pk = master_to_wallet_unhardened(public_key, 0).derive_synthetic();
    StandardArgs::curry_tree_hash(wallet_pk).into()
}

/// Converts a master secret key to a wallet synthetic secret key.
pub fn master_secret_key_to_wallet_synthetic_secret_key(secret_key: &SecretKey) -> SecretKey {
    master_to_wallet_unhardened(secret_key, 0).derive_synthetic()
}

/// Converts a secret key to its corresponding public key.
pub fn secret_key_to_public_key(secret_key: &SecretKey) -> PublicKey {
    secret_key.public_key()
}

/// Converts a synthetic key to its corresponding standard puzzle hash.
pub fn synthetic_key_to_puzzle_hash(synthetic_key: &PublicKey) -> Bytes32 {
    StandardArgs::curry_tree_hash(*synthetic_key).into()
}

/// Creates an admin delegated puzzle for a given key.
pub fn admin_delegated_puzzle_from_key(synthetic_key: &PublicKey) -> DelegatedPuzzle {
    DelegatedPuzzle::Admin(StandardArgs::curry_tree_hash(*synthetic_key))
}

/// Creates a writer delegated puzzle from a given key.
pub fn writer_delegated_puzzle_from_key(synthetic_key: &PublicKey) -> DelegatedPuzzle {
    DelegatedPuzzle::Writer(StandardArgs::curry_tree_hash(*synthetic_key))
}

/// Creates an oracle delegated puzzle.
pub fn oracle_delegated_puzzle(oracle_puzzle_hash: Bytes32, oracle_fee: u64) -> DelegatedPuzzle {
    DelegatedPuzzle::Oracle(oracle_puzzle_hash, oracle_fee)
}

/// Gets the coin ID for a given coin.
pub fn get_coin_id(coin: &Coin) -> Bytes32 {
    coin.coin_id()
}

/// Converts a puzzle hash to an address by encoding it using bech32m.
pub fn puzzle_hash_to_address(puzzle_hash: Bytes32, prefix: &str) -> Result<String> {
    use chia_wallet_sdk::utils::Address;
    Ok(Address::new(puzzle_hash, prefix.to_string()).encode()?)
}

/// Converts an address to a puzzle hash using bech32m.
pub fn address_to_puzzle_hash(address: &str) -> Result<Bytes32> {
    use chia_wallet_sdk::utils::Address;
    Ok(Address::decode(address)?.puzzle_hash)
}

/// Converts hex-encoded spend bundle to coin spends.
pub fn hex_spend_bundle_to_coin_spends(hex: &str) -> Result<Vec<CoinSpend>> {
    use chia_traits::Streamable;
    let bytes = hex::decode(hex)?;
    let spend_bundle = SpendBundle::from_bytes(&bytes)?;
    Ok(spend_bundle.coin_spends)
}

/// Converts a spend bundle to hex encoding.
pub fn spend_bundle_to_hex(spend_bundle: &SpendBundle) -> Result<String> {
    use chia_traits::Streamable;
    let bytes = spend_bundle.to_bytes()?;
    Ok(hex::encode(bytes))
}

/// Adds an offset to a launcher id to make it deterministically unique from the original.
pub fn morph_launcher_id_wrapper(launcher_id: Bytes32, offset: u64) -> Bytes32 {
    xch_server_coin::morph_launcher_id(launcher_id, &offset.into())
}

/// Output for send_xch function
#[derive(Debug, Clone)]
pub struct Output {
    pub puzzle_hash: Bytes32,
    pub amount: u64,
    pub memos: Vec<Bytes>,
}

/// Sends XCH to a given set of puzzle hashes (Rust API version).
pub fn send_xch(
    synthetic_key: &PublicKey,
    selected_coins: &[Coin],
    outputs: &[Output],
    fee: u64,
) -> Result<Vec<CoinSpend>> {
    let outputs: Vec<(Bytes32, u64, Vec<Bytes>)> = outputs
        .iter()
        .map(|output| (output.puzzle_hash, output.amount, output.memos.clone()))
        .collect();

    Ok(wallet::send_xch(
        *synthetic_key,
        selected_coins,
        &outputs,
        fee,
    )?)
}

/// Selects coins using the knapsack algorithm (Rust API version).
pub fn select_coins(all_coins: &[Coin], total_amount: u64) -> Result<Vec<Coin>> {
    Ok(wallet::select_coins(all_coins.to_vec(), total_amount)?)
}

/// Adds a fee to any transaction (Rust API version).
pub fn add_fee(
    spender_synthetic_key: &PublicKey,
    selected_coins: &[Coin],
    assert_coin_ids: &[Bytes32],
    fee: u64,
) -> Result<Vec<CoinSpend>> {
    Ok(wallet::add_fee(
        *spender_synthetic_key,
        selected_coins.to_vec(),
        assert_coin_ids.to_vec(),
        fee,
    )?)
}

/// Signs coin spends using a list of keys (Rust API version).
pub fn sign_coin_spends(
    coin_spends: &[CoinSpend],
    private_keys: &[SecretKey],
    for_testnet: bool,
) -> Result<Signature> {
    Ok(wallet::sign_coin_spends(
        coin_spends.to_vec(),
        private_keys.to_vec(),
        if for_testnet {
            wallet::TargetNetwork::Testnet11
        } else {
            wallet::TargetNetwork::Mainnet
        },
    )?)
}

/// Signs a message using the provided private key (Rust API version).
pub fn sign_message(message: &[u8], private_key: &SecretKey) -> Result<Signature> {
    Ok(wallet::sign_message(message.into(), private_key.clone())?)
}

/// Verifies a signed message using the provided public key (Rust API version).
pub fn verify_signed_message(
    signature: &Signature,
    public_key: &PublicKey,
    message: &[u8],
) -> Result<bool> {
    Ok(wallet::verify_signature(
        message.into(),
        *public_key,
        signature.clone(),
    )?)
}

/// Calculates the total cost of coin spends (Rust API version).
pub fn get_cost(coin_spends: &[CoinSpend]) -> Result<u64> {
    Ok(wallet::get_cost(coin_spends.to_vec())?)
}

/// Mints a new datastore (Rust API version).
#[allow(clippy::too_many_arguments)]
pub fn mint_store(
    minter_synthetic_key: PublicKey,
    selected_coins: Vec<Coin>,
    root_hash: Bytes32,
    label: Option<String>,
    description: Option<String>,
    bytes: Option<u64>,
    size_proof: Option<String>,
    owner_puzzle_hash: Bytes32,
    delegated_puzzles: Vec<DelegatedPuzzle>,
    fee: u64,
) -> Result<SuccessResponse> {
    Ok(wallet::mint_store(
        minter_synthetic_key,
        selected_coins,
        root_hash,
        label,
        description,
        bytes,
        size_proof,
        owner_puzzle_hash,
        delegated_puzzles,
        fee,
    )?)
}

/// Spends a store in oracle mode (Rust API version).
pub fn oracle_spend(
    spender_synthetic_key: PublicKey,
    selected_coins: Vec<Coin>,
    store: Datastore,
    fee: u64,
) -> Result<SuccessResponse> {
    Ok(wallet::oracle_spend(
        spender_synthetic_key,
        selected_coins,
        store,
        fee,
    )?)
}

/// Updates the metadata of a store (Rust API version).
#[allow(clippy::too_many_arguments)]
pub fn update_store_metadata(
    store: Datastore,
    new_root_hash: Bytes32,
    new_label: Option<String>,
    new_description: Option<String>,
    new_bytes: Option<u64>,
    new_size_proof: Option<String>,
    inner_spend_info: DataStoreInnerSpend,
) -> Result<SuccessResponse> {
    Ok(wallet::update_store_metadata(
        store,
        new_root_hash,
        new_label,
        new_description,
        new_bytes,
        new_size_proof,
        inner_spend_info,
    )?)
}

/// Updates the ownership of a store (Rust API version).
pub fn update_store_ownership(
    store: Datastore,
    new_owner_puzzle_hash: Bytes32,
    new_delegated_puzzles: Vec<DelegatedPuzzle>,
    inner_spend_info: wallet::DataStoreInnerSpend,
) -> Result<SuccessResponse> {
    Ok(wallet::update_store_ownership(
        store,
        new_owner_puzzle_hash,
        new_delegated_puzzles,
        inner_spend_info,
    )?)
}

/// Melts a store (Rust API version).
pub fn melt_store(store: Datastore, owner_pk: PublicKey) -> Result<Vec<CoinSpend>> {
    Ok(wallet::melt_store(store, owner_pk)?)
}

/// Creates a server coin (Rust API version).
pub fn create_server_coin(
    synthetic_key: PublicKey,
    selected_coins: Vec<Coin>,
    hint: Bytes32,
    uris: Vec<String>,
    amount: u64,
    fee: u64,
) -> Result<NewXchServerCoin> {
    Ok(wallet::create_server_coin(
        synthetic_key,
        selected_coins,
        hint,
        uris,
        amount,
        fee,
    )?)
}

/// Async functions for blockchain interaction (Rust API versions)
pub mod async_api {
    use super::*;
    use futures_util::stream::{FuturesUnordered, StreamExt};
    use rand::seq::SliceRandom;
    use std::net::SocketAddr;
    use tokio::net::lookup_host;
    use tokio::time::{timeout, Duration};

    // DNS introducers and default ports for connecting to random peers.
    const MAINNET_DNS_INTRODUCERS: &[&str] = &[
        "dns-introducer.chia.net",
        "chia.ctrlaltdel.ch",
        "seeder.dexie.space",
        "chia.hoffmang.com",
    ];
    const TESTNET11_DNS_INTRODUCERS: &[&str] = &["dns-introducer-testnet11.chia.net"];
    const MAINNET_DEFAULT_PORT: u16 = 8444;
    const TESTNET11_DEFAULT_PORT: u16 = 58444;

    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
    pub enum NetworkType {
        Mainnet,
        Testnet11,
    }

    /// Connects to a random peer on the specified network (Rust API version).
    ///
    /// The function performs DNS lookups using the network's introducers, picks a random
    /// address from the returned list, and attempts to establish a connection. It will
    /// try every resolved address until a connection succeeds.
    pub async fn connect_random(
        network: NetworkType,
        cert_path: &str,
        key_path: &str,
    ) -> Result<Peer> {
        // Load TLS certificate
        let cert = chia_wallet_sdk::client::load_ssl_cert(cert_path, key_path)?;
        let tls = chia_wallet_sdk::client::create_native_tls_connector(&cert)?;

        // Introducers and default port per network
        let (introducers, default_port) = match network {
            NetworkType::Mainnet => (MAINNET_DNS_INTRODUCERS, MAINNET_DEFAULT_PORT),
            NetworkType::Testnet11 => (TESTNET11_DNS_INTRODUCERS, TESTNET11_DEFAULT_PORT),
        };

        // Resolve all introducers to socket addresses
        let mut addrs = Vec::new();
        for introducer in introducers {
            if let Ok(iter) = lookup_host((*introducer, default_port)).await {
                addrs.extend(iter);
            }
        }

        if addrs.is_empty() {
            return Err("Failed to resolve any peer addresses from introducers".into());
        }

        // Shuffle for randomness so every call has different order
        {
            let mut rng = rand::thread_rng();
            addrs.shuffle(&mut rng);
        }

        // Try to connect in concurrent batches with timeout logic
        const BATCH_SIZE: usize = 10;
        const CONNECT_TIMEOUT: Duration = Duration::from_secs(8);

        for chunk in addrs.chunks(BATCH_SIZE) {
            let mut futures = FuturesUnordered::new();
            for addr in chunk {
                let addr = *addr;
                let network_str = match network {
                    NetworkType::Mainnet => "mainnet",
                    NetworkType::Testnet11 => "testnet11",
                };
                let tls_clone = tls.clone();

                // Spawn connection attempt with timeout
                futures.push(async move {
                    timeout(
                        CONNECT_TIMEOUT,
                        chia_wallet_sdk::client::connect_peer(
                            network_str.to_string(),
                            tls_clone,
                            addr,
                            chia_wallet_sdk::client::PeerOptions::default(),
                        ),
                    )
                    .await
                });
            }

            while let Some(result) = futures.next().await {
                match result {
                    Ok(Ok((peer, _receiver))) => {
                        // Successfully connected, return the peer
                        return Ok(peer);
                    }
                    _ => {
                        // Either timed out or failed; continue with others
                    }
                }
            }
        }

        Err("Unable to connect to any discovered peer".into())
    }

    /// Creates a TLS connector for Chia peer connections (Rust API version).
    pub fn create_tls_connector(
        cert_path: &str,
        key_path: &str,
    ) -> Result<chia_wallet_sdk::client::Connector> {
        let cert = chia_wallet_sdk::client::load_ssl_cert(cert_path, key_path)?;
        Ok(chia_wallet_sdk::client::create_native_tls_connector(&cert)?)
    }

    /// Connects to a specific peer address (Rust API version).
    pub async fn connect_peer(
        network: NetworkType,
        tls_connector: chia_wallet_sdk::client::Connector,
        address: SocketAddr,
    ) -> Result<Peer> {
        let network_str = match network {
            NetworkType::Mainnet => "mainnet",
            NetworkType::Testnet11 => "testnet11",
        };

        let (peer, _receiver) = chia_wallet_sdk::client::connect_peer(
            network_str.to_string(),
            tls_connector,
            address,
            chia_wallet_sdk::client::PeerOptions::default(),
        )
        .await?;

        Ok(peer)
    }

    /// Mints a new NFT using a DID string (Rust API version).
    #[allow(clippy::too_many_arguments)]
    pub async fn mint_nft(
        peer: &Peer,
        synthetic_key: PublicKey,
        selected_coins: Vec<Coin>,
        did_string: &str,
        recipient_puzzle_hash: Bytes32,
        metadata: chia_puzzle_types::nft::NftMetadata,
        royalty_puzzle_hash: Option<Bytes32>,
        royalty_basis_points: u16,
        fee: u64,
        for_testnet: Option<bool>,
    ) -> Result<Vec<CoinSpend>> {
        let network = if for_testnet.unwrap_or(false) {
            wallet::TargetNetwork::Testnet11
        } else {
            wallet::TargetNetwork::Mainnet
        };

        Ok(wallet::mint_nft(
            peer,
            synthetic_key,
            selected_coins,
            did_string,
            recipient_puzzle_hash,
            metadata,
            royalty_puzzle_hash,
            royalty_basis_points,
            fee,
            network,
        )
        .await?)
    }

    /// Generates a DID proof automatically (Rust API version).
    pub async fn generate_did_proof(
        peer: &Peer,
        did_coin: Coin,
        for_testnet: bool,
    ) -> Result<(Proof, Coin)> {
        let network = if for_testnet {
            wallet::TargetNetwork::Testnet11
        } else {
            wallet::TargetNetwork::Mainnet
        };

        Ok(wallet::generate_did_proof(peer, did_coin, network).await?)
    }

    /// Creates a simple DID (Rust API version).
    pub fn create_simple_did(
        synthetic_key: PublicKey,
        selected_coins: Vec<Coin>,
        fee: u64,
    ) -> Result<(Vec<CoinSpend>, Coin)> {
        Ok(wallet::create_simple_did(
            synthetic_key,
            selected_coins,
            fee,
        )?)
    }

    /// Synchronizes a datastore (Rust API version).
    pub async fn sync_store(
        peer: &Peer,
        store: &Datastore,
        last_height: Option<u32>,
        last_header_hash: Bytes32,
        with_history: bool,
    ) -> Result<SyncStoreResponse> {
        Ok(wallet::sync_store(peer, store, last_height, last_header_hash, with_history).await?)
    }

    /// Synchronizes a store using its launcher ID (Rust API version).
    pub async fn sync_store_from_launcher_id(
        peer: &Peer,
        launcher_id: Bytes32,
        last_height: Option<u32>,
        last_header_hash: Bytes32,
        with_history: bool,
    ) -> Result<SyncStoreResponse> {
        Ok(wallet::sync_store_using_launcher_id(
            peer,
            launcher_id,
            last_height,
            last_header_hash,
            with_history,
        )
        .await?)
    }

    /// Gets all unspent coins hinted by the provided hint.
    pub async fn get_unspent_coins_by_hints(
        peer: &Peer,
        hint: Bytes32,
        network: NetworkType,
    ) -> Result<UnspentCoinStates> {
        Ok(wallet::get_unspent_coin_states_by_hint(peer, hint, network).await?)
    }

    /// Gets all unspent coins for a puzzle hash (Rust API version).
    pub async fn get_all_unspent_coins(
        peer: &Peer,
        puzzle_hash: Bytes32,
        previous_height: Option<u32>,
        previous_header_hash: Bytes32,
    ) -> Result<UnspentCoinStates> {
        Ok(wallet::get_unspent_coin_states(
            peer,
            puzzle_hash,
            previous_height,
            previous_header_hash,
            false,
        )
        .await?)
    }

    /// Checks if a coin is spent on-chain (Rust API version).
    pub async fn is_coin_spent(
        peer: &Peer,
        coin_id: Bytes32,
        last_height: Option<u32>,
        header_hash: Bytes32,
    ) -> Result<bool> {
        Ok(wallet::is_coin_spent(peer, coin_id, last_height, header_hash).await?)
    }

    /// Gets the header hash at a specific height (Rust API version).
    pub async fn get_header_hash(peer: &Peer, height: u32) -> Result<Bytes32> {
        Ok(wallet::get_header_hash(peer, height).await?)
    }

    /// Gets fee estimate for target time (Rust API version).
    pub async fn get_fee_estimate(peer: &Peer, target_time_seconds: u64) -> Result<u64> {
        Ok(wallet::get_fee_estimate(peer, target_time_seconds).await?)
    }

    /// Broadcasts a spend bundle (Rust API version).
    pub async fn broadcast_spend_bundle(
        peer: &Peer,
        spend_bundle: SpendBundle,
    ) -> Result<chia_protocol::TransactionAck> {
        Ok(wallet::broadcast_spend_bundle(peer, spend_bundle).await?)
    }
}

/// Constants for different networks
pub mod constants {
    use chia_wallet_sdk::types::{MAINNET_CONSTANTS, TESTNET11_CONSTANTS};

    /// Returns the mainnet genesis challenge.
    pub fn get_mainnet_genesis_challenge() -> chia_protocol::Bytes32 {
        MAINNET_CONSTANTS.genesis_challenge
    }

    /// Returns the testnet11 genesis challenge.
    pub fn get_testnet11_genesis_challenge() -> chia_protocol::Bytes32 {
        TESTNET11_CONSTANTS.genesis_challenge
    }
}

/// Example usage of the Rust API
#[cfg(test)]
mod examples {
    use super::*;

    #[test]
    fn example_key_operations() {
        // Example: Generate keys and addresses
        let secret_key = SecretKey::from_bytes(&[1u8; 32]).unwrap();
        let public_key = secret_key_to_public_key(&secret_key);
        let _synthetic_key = master_public_key_to_wallet_synthetic_key(&public_key);
        let puzzle_hash = master_public_key_to_first_puzzle_hash(&public_key);

        // Convert to address
        let address = puzzle_hash_to_address(puzzle_hash, "xch").unwrap();
        println!("Address: {}", address);

        // Convert back
        let decoded_hash = address_to_puzzle_hash(&address).unwrap();
        assert_eq!(puzzle_hash, decoded_hash);
    }

    #[tokio::test]
    async fn example_nft_minting() {
        // Example of complete NFT minting workflow using Rust API

        /*
        // 1. Connect to a random peer
        let peer = connect_random(
            NetworkType::Mainnet,
            "~/.chia/mainnet/config/ssl/wallet/wallet_node.crt",
            "~/.chia/mainnet/config/ssl/wallet/wallet_node.key"
        ).await.unwrap();

        // 2. Set up your wallet keys (from mnemonic or existing keys)
        let master_secret_key = SecretKey::from_bytes([1u8; 32]).unwrap(); // Your actual key
        let master_public_key = secret_key_to_public_key(&master_secret_key);
        let synthetic_key = master_public_key_to_wallet_synthetic_key(&master_public_key);
        let puzzle_hash = master_public_key_to_first_puzzle_hash(&master_public_key);

        // 3. Get unspent coins for the transaction
        let unspent_coins = async_api::get_all_unspent_coins(
            &peer,
            puzzle_hash,
            None,
            get_mainnet_genesis_challenge(),
        ).await.unwrap();

        // 4. Select coins for the transaction
        let fee = 1_000_000; // 1 million mojos
        let selected_coins = select_coins(&unspent_coins.coin_states.iter().map(|cs| cs.coin).collect::<Vec<_>>(), fee + 1).unwrap();

        // 5. Create NFT metadata
        let metadata = chia_puzzle_types::nft::NftMetadata {
            data_uris: vec!["https://example.com/nft.png".to_string()],
            metadata_uris: vec!["https://example.com/metadata.json".to_string()],
            ..Default::default()
        };

        // 6. Mint the NFT
        let nft_spends = async_api::mint_nft(
            &peer,
            synthetic_key,
            selected_coins,
            "did:chia:1s8j4pquxfu5mhlldzu357qfqkwa9r35mdx5a0p0ehn76dr4ut4tqs0n6kv",
            puzzle_hash, // Send NFT to yourself
            metadata,
            None, // No royalty address
            300,  // 3% royalty
            fee,
            None, // Defaults to mainnet
        ).await.unwrap();

        // 7. Sign the transaction
        let signature = sign_coin_spends(
            &nft_spends,
            &[master_secret_key_to_wallet_synthetic_secret_key(&master_secret_key)],
            false, // mainnet
        ).unwrap();

        // 8. Create and broadcast spend bundle
        let spend_bundle = SpendBundle::new(nft_spends, signature);
        let result = async_api::broadcast_spend_bundle(&peer, spend_bundle).await.unwrap();

        println!("NFT minting transaction broadcast: {:?}", result);
        */
    }
}