Expand description
§Data Anchor Client
This crate is a Rust client which handles interactions with the Nitro Blober on-chain program and with the Data Anchor indexer service in an optimized way.
§Usage
All you need to do to get started with the client is add it to your project dependencies:
cargo add data-anchor-client
§Connecting
To start uploading and reading data, pass the configs into the client like the following:
let data_anchor_client = DataAnchorClient::builder()
.payer(payer)
.program_id(program_id)
.indexer_from_url(&indexer_url)
.await?
.build_with_config(config)
.await?;
- The
payer
is aArc<Keypair>
- a solana keypair you want to use with the client - The
program_id
is the address of the blober on-chain program you want to interact with - The
indexer_url
is an optional parameter to provide if you are using our indexer service - The
config
is asolana_cli_config::Config
object used to determine RPC details with which to send the transactions
§Builder options
The builder exposes a few additional helpers. A common pattern is to use an indexer together with the Helius fee estimator:
let client = DataAnchorClient::builder()
.payer(payer)
.program_id(program_id)
.indexer_from_url("https://indexer.example.com", None)
.await?
.with_helius_fee_estimate()
.build_with_config(Config::default())
.await?;
The with_helius_fee_estimate
flag enables querying the Helius API for a better
priority fee estimate when sending transactions.
§Uploading data
Uploading data once you have a blober client is as simple as:
let transaction_outcomes = data_anchor_client.upload_blob(data, fee, blober_id, timeout).await?;
- The
data
is a slice of bytes (&[u8]
) to upload - The
fee
is a fee strategy for how much you want to send as the priority fee - The
blober_id
is the blober PDA (namespace) you want to upload to - The
timeout
is an optional parameter which specifies how long to wait before discarding a started data upload
The transaction outcomes is a vector of
TransactionOutcome
enum structs which contain the success state (successfull, failed or unknown) and in case of success the transaction signature and slot at which the transaction landed.
§Estimating fees
Before uploading a blob you can estimate the expected cost using estimate_fees
:
let priority = Priority::default();
let expected_fees = data_anchor_client
.estimate_fees(data.len(), blober_pubkey, priority)
.await?;
println!("Estimated lamports: {}", expected_fees.total_fee());
§Querying data
To later retrieve the data that was uploaded, you can either do it from the ledger directly:
let blob = data_anchor_client
.get_ledger_blobs_from_signatures(blober, signatures)
.await?;
Where the signatures
are the list of signatures you got by sending the upload request.
let blobs = data_anchor_client.get_ledger_blobs(slot, blober, lookback_slots).await?;
Where the slot
is the slot at which the upload was finalized and lookback_slots
is an optional parameter to limit how many slots before the slot
to fetch in the past.
Or from the indexer service with:
let blobs = data_anchor_client.get_blobs(slot, blober).await?;
And getting the indexer proofs (these prove that the indexer is sending you valid data):
let proof = data_anchor_client.get_slot_proof(slot, blober).await?;
§Complete workflow
The following snippet demonstrates creating a blober, uploading data and fetching it back from the ledger:
use std::{sync::Arc, time::Duration};
use data_anchor_client::{DataAnchorClient, FeeStrategy};
use solana_cli_config::Config;
use solana_keypair::Keypair;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let payer = Arc::new(Keypair::new());
let client = DataAnchorClient::builder()
.payer(payer.clone())
.program_id(data_anchor_blober::id())
.build_with_config(Config::default())
.await?;
let ns = "example";
client.initialize_blober(FeeStrategy::default(), ns, None).await?;
let blob = b"hello world";
let outcomes = client
.upload_blob(blob, FeeStrategy::default(), ns, Some(Duration::from_secs(10)))
.await?;
let sigs = outcomes.iter().map(|o| o.signature).collect::<Vec<_>>();
let recovered = client
.get_ledger_blobs_from_signatures(ns.into(), sigs)
.await?;
assert_eq!(blob.to_vec(), recovered);
Ok(())
}
For more details, check out the docs.
§API reference
The following table contains small examples for the public methods provided by
DataAnchorClient
. Every function is asynchronous and returns a
DataAnchorClientResult
.
§Blober management
let ns = "example";
client.initialize_blober(FeeStrategy::default(), ns, None).await?;
client.close_blober(FeeStrategy::default(), ns, None).await?;
§Upload helpers
let blob_pubkey = Pubkey::new_unique();
client.upload_blob(data, FeeStrategy::default(), ns, None).await?;
client.discard_blob(FeeStrategy::default(), blob_pubkey, ns, None).await?;
client.estimate_fees(data.len(), blob_pubkey, Priority::default()).await?;
§Ledger queries
client.get_ledger_blobs_from_signatures(ns.into(), signatures).await?;
client.get_ledger_blobs(slot, ns.into(), None).await?;
client.get_blob_messages(slot, ns.into()).await?;
§Indexer queries
client.get_blobs(slot, ns.into()).await?;
client.get_blobs_by_blober(ns.into(), None).await?;
client.get_blobs_by_payer(payer_pubkey, network_name.clone(), None).await?;
client.get_blobs_by_network(network_name.clone(), time_range).await?;
client.get_blobs_by_namespace_for_payer(ns.into(), Some(payer_pubkey), time_range).await?;
client.get_proof(slot, ns.into()).await?;
client.get_proof_for_blob(blob_pubkey).await?;
§Builder helpers
let client = DataAnchorClient::builder()
.payer(payer)
.program_id(program_id)
.indexer_from_url(indexer_url, None)
.await?
.with_helius_fee_estimate()
.build_with_config(Config::default())
.await?;
Structs§
- Batch
Client - A client that wraps an
RpcClient
and uses it to submit batches of transactions. - Data
Anchor Client - Failed
Transaction - A transaction that resulted in an error.
- Fee
- The expected fees for a blob upload, broken down by source.
- Lamports
- The smallest fraction of the native Solana token, SOL. 1 lamport = 0.000000001 SOL.
- Micro
Lamports - 10^-6 lamports, only used for prioritization fee calculations.
- Successful
Transaction - A transaction that was successfully confirmed by the network at the desired commitment level.
- Unknown
Transaction - A transaction that either was not submitted to the network, or it was submitted but not confirmed.
Enums§
- Blober
Identifier - Identifier for a blober, which can be either a combination of payer and namespace or just a pubkey.
- Chain
Error - An error that can occur when uploading a blob to a blober account.
- Data
Anchor Client Error - Errors that can occur when interacting with the Blober client.
- FeeStrategy
- The strategy to use for calculating the fees for transactions.
- Indexer
Error - Indexer
Url - Default Indexer API URL assignments.
- Outcome
Error - Transaction outcomes were not successfull.
- Priority
- The percentile of recent prioritization fees to use as the compute unit price for a transaction.
- Proof
Error - Transaction
Outcome - The final outcome of a transaction after the [
BatchClient
] is done, either successfully or due to reaching the timeout. - Transaction
Type - Transaction types which can be performed by the
data_anchor_blober::blober
program.
Type Aliases§
- Data
Anchor Client Result - Result returned when interacting with the Blober client.