use near_kit::*;
#[cfg(feature = "sandbox")]
use near_kit::sandbox::{OwnedSandbox, SandboxConfig};
#[cfg(feature = "sandbox")]
async fn sequential_example() -> Result<(), Error> {
println!("Starting local sandbox...\n");
let sandbox: OwnedSandbox = SandboxConfig::fresh().await;
let root_near = sandbox.client();
let root_account = root_near.account_id().unwrap().to_string();
let num_keys = 3;
let keypairs: Vec<KeyPair> = (0..num_keys).map(|_| KeyPair::random()).collect();
let bot_account = format!("bot-{}.{}", std::process::id(), root_account);
println!("Creating bot account: {bot_account}");
root_near
.transaction(&bot_account)
.create_account()
.transfer(NearToken::near(50))
.add_full_access_key(keypairs[0].public_key.clone())
.send()
.await?;
let bot_near = Near::custom(sandbox.rpc_url())
.signer(InMemorySigner::from_secret_key(
bot_account.parse()?,
keypairs[0].secret_key.clone(),
))
.build();
keypairs[1..]
.iter()
.fold(bot_near.transaction(&bot_account), |tx, kp| {
tx.add_full_access_key(kp.public_key.clone())
})
.send()
.await?;
let recipient = format!("recipient.{root_account}");
root_near
.transaction(&recipient)
.create_account()
.transfer(NearToken::millinear(100))
.send()
.await?;
let secret_keys: Vec<SecretKey> = keypairs.into_iter().map(|kp| kp.secret_key).collect();
let rotating = RotatingSigner::new(&bot_account, secret_keys)?;
println!(
"Splitting {} keys into per-key signers...",
rotating.key_count()
);
let per_key_signers = rotating.into_per_key_signers();
let txs_per_key = 5;
println!("Sending {txs_per_key} sequential txs per key ({num_keys} keys in parallel)...\n");
let start = std::time::Instant::now();
let rpc_url = sandbox.rpc_url().to_string();
let handles: Vec<_> = per_key_signers
.into_iter()
.enumerate()
.map(|(key_idx, signer)| {
let rpc_url = rpc_url.clone();
let recipient = recipient.clone();
tokio::spawn(async move {
let near = Near::custom(&rpc_url).signer(signer).build();
for tx_idx in 0..txs_per_key {
near.transfer(&recipient, NearToken::millinear(1))
.send()
.wait_until(TxExecutionStatus::Included)
.await
.unwrap();
println!(" key[{key_idx}] tx {tx_idx} included");
}
})
})
.collect();
for h in handles {
h.await.unwrap();
}
let duration = start.elapsed();
let total = num_keys * txs_per_key;
println!("\n=== Results ===");
println!("Total txs: {total}");
println!("Duration: {:.2?}", duration);
println!(
"Throughput: {:.1} tx/s",
total as f64 / duration.as_secs_f64()
);
let balance = bot_near.balance(&recipient).await?;
println!("\nRecipient balance: {}", balance.available);
println!("\nDone.");
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Error> {
println!("Sequential Per-Key Sends Example\n");
println!(
"Demonstrates per-key sequential transaction execution using into_per_key_signers().\n"
);
#[cfg(feature = "sandbox")]
{
sequential_example().await?;
}
#[cfg(not(feature = "sandbox"))]
{
println!("This example requires the `sandbox` feature.");
println!("Run with: cargo run --example sequential_sends --features sandbox");
}
Ok(())
}