Rust SDK
Last updated
Last updated
The Rust SDK is available at .
Add the Sova SDK by running:
cargo add --git https://github.com/sova-network/sova-sdk-rs --branch main
CAUTION: By default, each searcher enforces a limit of 5 requests per second. If you need to have a higher limit, you can send a request to hello@sova.network.
use sova_sdk_rs::client::SovaClient;
fn main() {
// Create a client instance for testnet.
let client = SovaClient::testnet();
}
If you need authenticated access, you can sign the challenge provided by the Sova Engine using your ED25519 private key. Replace the dummy private key with your actual 32-byte ED25519 key. If you choose to skip authentication, simply do not call this method.
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create the testnet client.
let mut client = SovaClient::testnet();
// Replace with your actual 32-byte private key.
let private_key: [u8; 32] = [0; 32];
// Authenticate and obtain an access token.
let token = client
.authenticate(private_key)
.await
.expect("Failed authentication");
println!("Authenticated successfully. Token: {:?}", token);
// Proceed to subscribe for workchain messages.
// subscribe_workchain_messages(&client).await?;
// Keep the application running to receive messages.
loop {
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
}
}
Regardless of authentication, you can use the searcher service to subscribe to Mempool messages.
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create the testnet client.
let client = SovaClient::testnet();
// Obtain a SovaSearcher instance from the client.
let mut searcher = client.searcher().await?;
// Subscribe to mempool updates filtered by a list of addresses.
searcher
.subscribe_by_addresses(
vec!["address1".to_string(), "address2".to_string()],
|packet| {
println!("Mempool packet (by addresses) received: {:?}", packet);
},
)
.await?;
// ...
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create the testnet client.
let client = SovaClient::testnet();
// Obtain a SovaSearcher instance from the client.
let mut searcher = client.searcher().await?;
// Subscribe to mempool updates for a specific workchain.
searcher
.subscribe_by_workchain(0, |packet| {
println!("Mempool packet (by workchain) received: {:?}", packet);
})
.await?;
// ...
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create the testnet client.
let client = SovaClient::testnet();
// Obtain a SovaSearcher instance from the client.
let mut searcher = client.searcher().await?;
// Subscribe to mempool updates for a specific workchain and shard.
searcher
.subscribe_by_workchain_shard(0, vec![96, 0, 0, 0, 0, 0, 0, 0], |packet| {
println!("Mempool packet (by workchain shard) received: {:?}", packet);
})
.await?;
// ...
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create the testnet client.
let client = SovaClient::testnet();
// Obtain a SovaSearcher instance from the client.
let mut searcher = client.searcher().await?;
// Subscribe using external out message body opcode filtering.
searcher
.subscribe_by_external_out_msg_body_opcode(
0,
Some(vec![96, 0, 0, 0, 0, 0, 0, 0]),
42,
|packet| {
println!(
"Mempool packet (by external out opcode) received: {:?}",
packet
);
},
)
.await?;
// ...
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create the testnet client.
let client = SovaClient::testnet();
// Obtain a SovaSearcher instance from the client.
let mut searcher = client.searcher().await?;
// Subscribe using internal message body opcode filtering.
searcher
.subscribe_by_internal_msg_body_opcode(0, Some(vec![96, 0, 0, 0, 0, 0, 0, 0]), 42, |packet| {
println!("Mempool packet (by internal opcode) received: {:?}", packet);
})
.await?;
// ...
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create the testnet client.
let client = SovaClient::testnet();
// Obtain a SovaSearcher instance from the client.
let mut searcher = client.searcher().await?;
// Retrieve the current tip addresses from the network.
// The bundle must include a transfer to one of these addresses.
let tip_addresses = searcher.get_tip_addresses().await?;
// Prepare a new bundle.
// A bundle is a collection of external messages that will be applied to a block in a declared sequence.
// IMPORTANT: The bundle must include a transfer to one of the tip addresses with a non-zero amount of TON.
// No TON will be sent unless the complete bundle is applied to a block.
let mut bundle = sova_sdk_rs::searcher::Bundle {
message: vec![],
expiration_ns: None,
};
// Create an external message.
// This message should be serialized in a Bag Of Cells format.
let ext_message_data: Vec<u8> = vec![];
// Add a transfer message to the bundle.
// The transfer must send a non-zero amount of TON to one of the tip addresses.
// Here we push an ExternalMessage, assuming 'ext_message_data' represents a properly constructed external message data.
bundle.message.push(sova_sdk_rs::searcher::ExternalMessage {
data: ext_message_data,
});
// Extend the bundle with any additional external messages.
let ext_messages = vec![];
// These messages can provide additional instructions or data.
bundle.message.extend(ext_messages);
// Send the bundle using the searcher.
// If the bundle is successfully applied to a block, the TON transfer will occur.
// Otherwise, no TON is sent.
let auction_id;
match searcher.send_bundle(bundle).await {
Ok(result) => {
// On success, capture bundle identifier that can be used to track auction status of bundle.
auction_id = result.id.clone();
}
Err(e) => {
// Handle errors gracefully.
println!("Failed to send bundle: {:?}", e);
}
}
// ...
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create the testnet client.
let client = SovaClient::testnet();
// Obtain a SovaSearcher instance from the client.
let mut searcher = client.searcher().await?;
searcher.subscribe_bundle_results(move |bundle_result| {
// Print the received bundle result.
println!("Received BundleResult: {:?}", bundle_result);
// Process the bundle result based on its type.
match bundle_result.result {
// Bundle won the auction.
Some(sova_sdk_rs::searcher::bundle_result::Result::Win(win)) => {
println!("Bundle won the auction!");
println!("Auction ID: {}", win.auction_id);
println!("Estimated nanoton tip: {}", win.estimated_nanoton_tip);
},
// Bundle lost the auction.
Some(sova_sdk_rs::searcher::bundle_result::Result::Loose(loose)) => {
println!("Bundle lost the auction.");
println!("Auction ID: {}", loose.auction_id);
},
// Bundle was interrupted.
Some(sova_sdk_rs::searcher::bundle_result::Result::Drop(drop)) => {
println!("Bundle was interrupted.");
if let Some(reason) = drop.reason {
match reason {
// Bundle was partially processed.
sova_sdk_rs::searcher::bundle_result_interrupted::Reason::PartiallyProcessed(proc) => {
println!("Bundle partially processed.");
println!("Auction ID: {}", proc.auction_id);
println!("Processed message digests: {:?}", proc.digest);
},
// Bundle partially expired.
sova_sdk_rs::searcher::bundle_result_interrupted::Reason::Expired(expired) => {
println!("Bundle partially expired.");
println!("Auction ID: {}", expired.auction_id);
println!("Expired message digests: {:?}", expired.digest);
},
}
}
},
// Bundle auction failed.
Some(sova_sdk_rs::searcher::bundle_result::Result::Failure(failure)) => {
println!("Bundle auction failed.");
if let Some(reason) = failure.reason {
match reason {
// Auction failed due to an estimate error.
sova_sdk_rs::searcher::bundle_result_auction_failed::Reason::EstimateError(err) => {
println!("Auction failed due to estimate error.");
println!("Auction ID: {}", err.auction_id);
println!("Failed message digest: {:?}", err.digest);
println!("Error message: {}", err.message);
},
// Auction failed due to an internal error.
sova_sdk_rs::searcher::bundle_result_auction_failed::Reason::InternalError(err) => {
println!("Auction failed due to internal error.");
println!("Auction ID: {}", err.auction_id);
println!("Message digests: {:?}", err.digest);
println!("Error message: {}", err.message);
},
}
}
},
// Handle the case where the bundle result is not recognized.
None => {
println!("Received an unrecognized bundle result.");
},
}
}).await.expect("Failed to subscribe to bundle results");
// ...
Ok(())
}
Priority/Anti-MEV transactions