@dignetwork/chia-query-wasm
v0.5.2
Published
coinset.org REST access for the browser and Node (the DIG Network chia-query wasm build)
Maintainers
Readme
chia-query
Rust crate for querying the Chia blockchain. Maintains a pool of decentralized peer connections as the primary data source and falls back to the coinset.org HTTP API when peers fail.
All hash parameters and return values use 0x-prefixed hex strings. All methods are async and return Result<T, ChiaQueryError>.
Installation
[dependencies]
chia-query = "0.1"
tokio = { version = "1", features = ["full"] }Initialization
use chia_query::{ChiaQuery, ChiaQueryConfig, NetworkType};
use std::time::Duration;
// Default: mainnet, 5 peers, coinset.org fallback enabled
let client = ChiaQuery::new(ChiaQueryConfig::default()).await?;
// Custom configuration
let client = ChiaQuery::new(ChiaQueryConfig {
network: NetworkType::Testnet11,
max_peers: 3,
coinset_base_url: "https://api.coinset.org".into(),
coinset_fallback_enabled: true,
cert_path: "/path/to/wallet_node.crt".into(),
key_path: "/path/to/wallet_node.key".into(),
peer_connect_timeout: Duration::from_secs(8),
peer_request_timeout: Duration::from_secs(30),
coinset_request_timeout: Duration::from_secs(30),
}).await?;TLS certificates auto-generate if the files don't exist. Set TRUSTED_FULLNODE env var to an IP address to prioritize a specific peer. Localhost (127.0.0.1) is always tried before DNS discovery.
Types
Coin
pub struct Coin {
pub parent_coin_info: String, // 0x-prefixed hex, 32 bytes
pub puzzle_hash: String, // 0x-prefixed hex, 32 bytes
pub amount: u64, // mojos
}CoinRecord
pub struct CoinRecord {
pub coin: Coin,
pub confirmed_block_index: u32,
pub spent_block_index: u32, // 0 if unspent
pub spent: bool,
pub coinbase: bool,
pub timestamp: u64, // unix seconds
}CoinSpend
pub struct CoinSpend {
pub coin: Coin,
pub puzzle_reveal: String, // 0x-prefixed hex, serialized CLVM
pub solution: String, // 0x-prefixed hex, serialized CLVM
}SpendBundle
pub struct SpendBundle {
pub coin_spends: Vec<CoinSpend>,
pub aggregated_signature: String, // 0x-prefixed hex, 96 bytes BLS sig
}BlockRecord
pub struct BlockRecord {
pub header_hash: String,
pub height: u32,
pub weight: u64,
pub prev_hash: String,
pub total_iters: u64,
pub signage_point_index: u8,
pub farmer_puzzle_hash: String,
pub pool_puzzle_hash: String,
pub timestamp: Option<u64>,
pub fees: Option<u64>,
pub extra: serde_json::Value, // additional fields from API
}Other types
FullBlock--serde_json::Value(deeply nested block structure)FeeEstimate-- estimates, target_times, current_fee_rate, mempool_size, etc.NetworkInfo-- network_name, network_prefix, genesis_challengeBlockchainState-- peak, sync state, difficulty, mempool statsTxStatus-- status string, success boolAdditionsAndRemovals-- additions:Vec<CoinRecord>, removals:Vec<CoinRecord>Condition-- opcode:serde_json::Value(hex string), vars:Vec<String>CoinSpendWithConditions-- coin_spend + parsed conditionsMempoolItem--serde_json::Value
API Reference
Coin queries
get_coin_record_by_name
Look up a single coin by its ID.
let record = client.get_coin_record_by_name("0xabcd...").await?;
// record.coin.amount, record.spent, record.confirmed_block_indexget_coin_records_by_puzzle_hash
Find all coins matching a puzzle hash.
let records = client.get_coin_records_by_puzzle_hash(
"0xabcd...", // puzzle_hash
Some(1000000), // start_height (optional)
Some(2000000), // end_height (optional)
false, // include_spent_coins
).await?;get_coin_records_by_puzzle_hashes
Same as above but batched for multiple puzzle hashes.
let hashes = vec!["0xaaa...".into(), "0xbbb...".into()];
let records = client.get_coin_records_by_puzzle_hashes(
&hashes, None, None, true,
).await?;get_coin_records_by_names
Look up multiple coins by their IDs.
let names = vec!["0xaaa...".into(), "0xbbb...".into()];
let records = client.get_coin_records_by_names(
&names, None, None, true,
).await?;get_coin_records_by_parent_ids
Find child coins of given parent coin IDs.
let parents = vec!["0xaaa...".into()];
let records = client.get_coin_records_by_parent_ids(
&parents, None, None, true,
).await?;get_coin_records_by_hint / get_coin_records_by_hints
Find coins by hint (used in CAT and NFT protocols).
let records = client.get_coin_records_by_hint(
"0xabcd...", None, None, false,
).await?;get_memos_by_coin_name
Get memos associated with a coin. Returns serde_json::Value.
let memos = client.get_memos_by_coin_name("0xabcd...").await?;Puzzle and solution
get_puzzle_and_solution
Get the puzzle reveal and solution for a spent coin. Height is optional -- if omitted, the spent height is auto-resolved from the peer.
let spend = client.get_puzzle_and_solution(
"0xabcd...", // coin_id
Some(500000), // height (optional)
).await?;
// spend.puzzle_reveal, spend.solutionget_puzzle_and_solution_with_conditions
Same as above but also runs the puzzle against the solution to extract parsed CLVM conditions.
let result = client.get_puzzle_and_solution_with_conditions(
"0xabcd...", Some(500000),
).await?;
for cond in &result.conditions {
// cond.opcode = "0x33" (CREATE_COIN), cond.vars = ["0x..puzzle_hash", "0x..amount"]
}Transaction broadcast
push_tx
Broadcast a spend bundle to the network.
let status = client.push_tx(&SpendBundle {
coin_spends: vec![CoinSpend {
coin: Coin {
parent_coin_info: "0x...".into(),
puzzle_hash: "0x...".into(),
amount: 1000000000000,
},
puzzle_reveal: "0xff...".into(),
solution: "0xff...".into(),
}],
aggregated_signature: "0xc0...".into(),
}).await?;
// status.status = "SUCCESS" | "PENDING" | "FAILED"
// status.success = true/falseBlock queries
get_block / get_block_by_height
Get a full block. Returns serde_json::Value.
let block = client.get_block("0x...header_hash").await?;
let block = client.get_block_by_height(1000000).await?;
// block["reward_chain_block"]["height"].as_u64()get_block_record / get_block_record_by_height
Get a block record (lighter than full block).
let record = client.get_block_record_by_height(1000000).await?;
// record.height, record.weight, record.timestamp, record.farmer_puzzle_hashget_block_records
Get a range of block records. Range is [start, end).
let records = client.get_block_records(1000000, 1000010).await?;get_blocks
Get a range of full blocks. Range is [start, end).
let blocks = client.get_blocks(
1000000, // start
1000005, // end
false, // exclude_header_hash
false, // exclude_reorged
).await?;get_additions_and_removals
Get all coins created (additions) and spent (removals) in a block.
let ar = client.get_additions_and_removals("0x...header_hash").await?;
// ar.additions: Vec<CoinRecord> -- coins created
// ar.removals: Vec<CoinRecord> -- coins spentget_block_spends
Get all coin spends in a block with puzzle_reveal and solution.
let spends = client.get_block_spends("0x...header_hash").await?;
for spend in &spends {
// spend.coin, spend.puzzle_reveal, spend.solution
}get_block_spends_with_conditions
Same as above but also extracts parsed CLVM conditions per spend.
let spends = client.get_block_spends_with_conditions("0x...header_hash").await?;
for s in &spends {
// s.coin_spend.puzzle_reveal, s.conditions
}get_block_count_metrics
let metrics = client.get_block_count_metrics().await?;
// metrics.compact_blocks, metrics.uncompact_blocks, metrics.hint_countget_unfinished_block_headers
let headers = client.get_unfinished_block_headers().await?;Fee estimation
get_fee_estimate
Estimate fees for target confirmation times (in seconds).
let estimate = client.get_fee_estimate(
None, // spend_bundle (optional)
Some(&[60, 120, 300]), // target_times in seconds
None, // spend_count (optional)
).await?;
// estimate.estimates = [0.0, 0.0, 0.0] -- fee rate per target time
// estimate.current_fee_rate, estimate.mempool_sizeNetwork and state
get_network_info
let info = client.get_network_info().await?;
// info.network_name = "mainnet"
// info.network_prefix = "xch"
// info.genesis_challenge = "0xccd5bb71..."get_aggsig_additional_data
let data = client.get_aggsig_additional_data().await?;
// "0xccd5bb71..." (same as genesis_challenge on mainnet)get_blockchain_state
let state = client.get_blockchain_state().await?;
// state.peak.unwrap().height
// state.sync.unwrap().synced
// state.difficulty, state.mempool_sizeget_network_space
let space = client.get_network_space(
"0x...newer_header_hash",
"0x...older_header_hash",
).await?;Mempool
get_all_mempool_items
let items = client.get_all_mempool_items().await?;
// HashMap<String, serde_json::Value> keyed by transaction IDget_all_mempool_tx_ids
let tx_ids = client.get_all_mempool_tx_ids().await?;get_mempool_item_by_tx_id
let item = client.get_mempool_item_by_tx_id("0x...").await?;get_mempool_items_by_coin_name
let items = client.get_mempool_items_by_coin_name("0x...", Some(true)).await?;Convenience
wait_for_confirmation
Poll until a coin is confirmed on-chain.
use std::time::Duration;
let record = client.wait_for_confirmation(
"0xabcd...", // coin_id
Duration::from_secs(5), // poll interval
Duration::from_secs(300), // timeout
).await?;
// Returns CoinRecord once confirmed_block_index > 0
// Errors with timeout if not confirmed within the deadlineRouting behavior
Every request tries decentralized peers first (two attempts on different peers), then falls back to coinset.org. Peer connections are maintained in a pool of up to max_peers (default 5). Failed peers are ejected and replaced in the background.
| Source | Endpoints | |---|---| | Peer first, coinset fallback | All coin queries, puzzle_and_solution, fee_estimate, push_tx, block_record_by_height, block_records, additions_and_removals, block_spends, blocks | | Constants (no network call) | get_network_info, get_aggsig_additional_data | | Coinset only | get_block_count_metrics, get_block_record (by hash), get_unfinished_block_headers, get_memos_by_coin_name, get_network_space, all mempool endpoints |
Error handling
use chia_query::ChiaQueryError;
match client.get_coin_record_by_name("0x...").await {
Ok(record) => { /* use record */ }
Err(ChiaQueryError::PeerRejection(msg)) => { /* peer rejected */ }
Err(ChiaQueryError::PeerConnection(msg)) => { /* connection/timeout */ }
Err(ChiaQueryError::CoinsetApiError(msg)) => { /* coinset returned error */ }
Err(ChiaQueryError::CoinsetHttp(e)) => { /* HTTP transport error */ }
Err(ChiaQueryError::AllSourcesFailed { .. }) => { /* both peer and coinset failed */ }
Err(ChiaQueryError::InvalidRequest(msg)) => { /* bad input */ }
Err(ChiaQueryError::TlsError(msg)) => { /* certificate issue */ }
Err(ChiaQueryError::PeerDiscoveryFailed) => { /* no peers found */ }
Err(ChiaQueryError::UnsupportedWithoutCoinset(ep)) => { /* coinset disabled, endpoint needs it */ }
}License
MIT
