Submit Transactions
How to submit transaction
Astralane provides a multitude of options for your needs, here are some of RPC methods we offer for Transaction sending.
sendTransaction
This RPC call is fully compatible with all Solana libraries , making it a simple drop-in replacement for your existing workflows. It routes through our partner SWQoS clients, including Jito and Paladin (higher min tip), ensuring optimized transaction processing and maximum reliability.
Just insert the URL where you would place a RPC URL and send your txns how you send normally. The only change is to add an instruction to tip.
JSON-RPC params format should be :
"params" : [ // params is an array
<encoded_transaction>,
<Transaction Configuration>,
<mevProtect true/false>
]
Encoded Transaction
String
A base64 encoded transaction.
Transaction Configuration
JSON Object
Its recommended to put
- encoding
as base64
- skipPreflight
as true
MeV Protect
JSON Object
Optional, setting it true will enable mev protect, Default is false.
Example JSON :
{
"id": 1,
"jsonrpc": "2.0",
"method": "sendTransaction",
"params": [
"base64_encoded_txn1",
{
"encoding": "base64",
"skipPreflight": true
},
{ "mevProtect": true }
]
}
const TIP: Pubkey = pubkey!("astra4uejePWneqNaJKuFFA8oonqCE1sqF6b45kDMZm"); // Use tip wallet depending on region of access
const MIN_TIP_AMOUNT: u64 = 10000; // added for spam prevention
fn send_tx_tipped(
ixs: &mut Vec<Instruction>,
signer: &Keypair,
rpc_client: &RpcClient, // https://api.mainnet-beta.solana.com
astralane_txn_sender: &RpcClient // Astralane RPC
) {
let tip_ix = system_instruction::transfer(&signer.pubkey(), &TIP, MIN_TIP_AMOUNT);
ixs.push(tip_ix);
let blockhash = rpc_client.get_latest_blockhash().unwrap();
let tx = Transaction::new_signed_with_payer(ixs, Some(&signer.pubkey()), &[signer], blockhash);
let encoded_tx = base64::prelude::BASE64_STANDARD.encode(&bincode::serialize(tx).unwrap());
let response = client
.post(url)
.header("api_key", "xxx")
.json(&json! ({
"jsonrpc": "2.0",
"id": 1,
"method": "sendTransaction",
"params": [
encoded_tx,
{
"encoding": "base64",
"skipPreflight": true,
},
{ "mevProtect": true }// Mev protect enable, Optional, Default is false
]
}))
.send()
.await;
}
}
sendBundle
If your operation depends on atomic txn execution then use our sendBundle endpoint to send atomically executing transactions.
Send up to 4 transactions in a single atomic bundle. These transactions are executed sequentially , ensuring precision and reliability. If any transaction fails, the entire bundle is reverted—guaranteeing consistency and eliminating partial failures.
revertProtection
Boolean
Optional, If a bundle only has 1 txn with this parameter as false
, then it will also sent via sentTransaction
pipeline. Default is false
const TIP: Pubkey = pubkey!("astra4uejePWneqNaJKuFFA8oonqCE1sqF6b45kDMZm"); // Use tip wallet depending on region of access
const MIN_TIP_AMOUNT: u64 = 10000; // added for spam prevention
async fn send_bundle(
ixs: &mut Vec<Instruction>,
signer: &Keypair,
client: reqwest::Client,
blockhash: Hash,
url: String,
) {
let tip_ix = system_instruction::transfer(&signer.pubkey(), &TIP, MIN_TIP_AMOUNT);
ixs.push(tip_ix);
let tx = Transaction::new_signed_with_payer(ixs, Some(&signer.pubkey()), &[signer], blockhash);
let encoded_tx = base64::prelude::BASE64_STANDARD.encode(&bincode::serialize(tx).unwrap());
let response = client
.post(url)
.header("api_key", "xxx")
.json(&json! ({
"jsonrpc": "2.0",
"id": 1,
"method": "sendBundle",
"params": [[encoded_tx]],
}))
.send()
.await;
}
Request:
{
"id": 1,
"jsonrpc": "2.0",
"method": "sendBundle",
"params": [
[
"base64_encoded_txn1",
"base64_encoded_txn2",
"base64_encoded_txn3"
],
{
"encoding": "base64",
"mevProtect": true,
"revertProtection": false
}
]
}
Response:
List of signatures for your transaction
{
"jsonrpc": "2.0",
"id": 1,
"result": [
"37Dxw2nJYw3T8JVqenPQMf39VJ9CNZYCyQm67b6nRj6fa6UjQ1UuLqFvh3wJ2G7LcMuZn4oq5kDt2A2CEXfi8D8"
]
}
sendIdeal
Perfect for snipers! Due to the segregation in validators as JITO validators and normal ones, traders are often conflicted between spending more on jito tips vs more in priority fees. Durable nonces offer a way to mitigate this issue.
Our sendIdeal RPC method accepts two transactions:
One with a high priority fee + min tip
Another with a high tip + lower priority fee
We route them through our advanced SWQoS and bundling pipelines. Using durable nonces, once one transaction lands, the other is automatically canceled—ensuring optimal efficiency and cost savings. if you don't want to manage the durable nonce accounts on your own we also provide a managed service for this, We create a nonce account for each api-key you use and you can query them using our getNonce rpc call. Follow the below integration steps for best utilization of this feature:
Step 1 : Generate Nonce instruction
use solana_sdk::hash::Hash;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::{EncodableKey, Keypair, Signer};
async fn get_nonce(
client: reqwest::Client,
url: String,
auth_key: String,
) {
let response = client
.post(url)
.header("api_key", "xxx")
.json(&json! ({
"jsonrpc": "2.0",
"id": 1,
"method": "getNonce",
"params": [api_key], // provided during onboarding
}))
.send()
.await;
let result = response["result"].clone();
let nonce = result["nonce"].as_str().unwrap();
let nonce_account = Pubkey::from_str(result["nonceAccount"].as_str().unwrap()).unwrap();
let nonce_authority =
Pubkey::from_str(result["nonceAuthority"].as_str().unwrap()).unwrap();
let nonce_as_hash = Hash::from_str(nonce).unwrap();
}
To use this nonce, just include an advance nonce instruction as the first instruction in your transactions.
Step 2 : Generate your txn and Partial Sign it before submission
const TIP: Pubkey = pubkey!("astra4uejePWneqNaJKuFFA8oonqCE1sqF6b45kDMZm"); // Use tip wallet depending on region of access
const MIN_TIP_AMOUNT: u64 = 10000; // added for spam prevention
async fn send_ideal(
signer: &Keypair,
client: reqwest::Client,
nonce: Hash,
instructions: Vec<Instruction>,
nonce_authority: &Pubkey,
nonce_account: &Pubkey,
) {
// add advance nonce instruction
let advance_nonce = solana_sdk::system_instruction::advance_nonce_account(nonce_account, nonce_authority);
let low_tip_high_fee_ixs = vec![
advance_nonce.clone(),
solana_sdk::compute_budget::ComputeBudgetInstruction::set_compute_unit_price(
10 * MICRO_LAMPORTS_PER_LAMPORTS,
),
// add your instructions here
solana_sdk::system_instruction::transfer(&signer.pubkey(), &TIP, MIN_TIP_AMOUNT),
];
let high_tip_low_fee_ixs = vec![
advance_nonce,
solana_sdk::compute_budget::ComputeBudgetInstruction::set_compute_unit_price(
100,
),
// add your instructions here
solana_sdk::system_instruction::transfer(&signer.pubkey(), &TIP, 100 * MIN_TIP_AMOUNT),
];
//add high transaction priority fee and min tip
let mut low_tip_high_fee_tx = Transaction::new_with_payer(&low_tip_high_fee_ixs, Some(&signer.pubkey()));
low_tip_high_fee_tx.partial_sign(&[&signer], nonce);
let mut high_tip_low_fee_tx = Transaction::new_with_payer(&high_tip_low_fee_ixs, Some(&signer.pubkey()));
high_tip_low_fee_tx.partial_sign(&[&signer], nonce);
let low_tip_high_fee_tx_encoded = base64::prelude::BASE64_STANDARD.encode(&bincode::serialize(low_tip_high_fee_tx).unwrap());
let high_tip_low_fee_tx_encoded = base64::prelude::BASE64_STANDARD.encode(&bincode::serialize(high_tip_low_fee_tx).unwrap());
let response = client
.post(url)
.json(&json! ({
"jsonrpc": "2.0",
"id": 1,
"method": "sendIdeal",
"params": [[low_tip_high_fee_tx_encoded, high_tip_low_fee_tx_encoded]],
}))
.send()
.await;
}
Request:
{
"id": 1,
"jsonrpc": "2.0",
"method": "sendIdeal",
"params": [[
"transction_with_large_tip_low_priority_fee",
"transaction_with_large_priority_fee_low_tip"
]]
}
Response:
{
"jsonrpc": "2.0",
"id": 1,
"result": [
"<signature A>",
"<signature B>"
]
}
The latency performance for your transactions will depend on current network dynamics. Kindly reach out to us on telegram for recommendations on ideal setup for your operation
sendPaladin (Beta)
Paladin is a custom TPU port implementation which provides a more efficient way to send your transactions directly to the leaders. The paladin client is currently running on 10% of the Solana network (as per 10th March 2025). Read more
Step 1 : Tracking Paladin leaders
Since paladin validators are not present for all slots, we provided a paladin leader tracker endpoint which can be used for knowing which slots we have a paladin leader. Here are some general guidelines for tracker integration to dynamically send txns based on leader info:
It's mandatory to use the gRPC for tracking the current leaders as normal RPCs are too slow to use effectively here. If you currently don't have access to gRPC, we would recommend you to put config
enableFallback
as true for best performance.
Fetch all Palidator public keys for current epoch
⚔️ GET http://paladin.astralane.io/api/palidators
[
"Ss...Z77",
"ACv...mi",
"7Z...Z84",
]
Or you can refer to `https://api.paladin.one/validators`
Get next Palidator leader slots
⚔️ GET http://paladin.astralane.io/api/next_palidator
{
"pubkey": "Csd...def",
"leader_slot": 42424242,
"context_slot": 42424242
}
Get next leader Palidator on or after given slot
⚔️ GET http://paladin.astralane.io/api/next_palidator/{slot}
{
"pubkey": "Csd...def",
"leader_slot": 42424242,
"context_slot": 42424242
}
NOTE: Sometimes certain malicious operators might mimick themselves as using paladin, so to prevent getting exploited on these scenarios we recommend you implement proactive blocklists for malicious operators. Reach out to us on our discord, if you are confused on how to achieve this. There is a very small minimum limit of 10 lamports/cu, on txns being sent to paladin validators.
Step 2: Crafting paladin transaction
Use the sendPaladin
method, to send the transactions during these slots. Please make sure to respect the min tip requirements and min priority fee requirements while sending txns on this endpoint.
All transactions sent to this endpoint, go through a small auction and the ordering will based on:
Priority fees
Any tiebreakers will be decided by tip to Astralane tip addresses
revertProtection
Boolean
Mandatory, A failed transaction will be dropped rather than landed as failed when enabled. Default is false
enableFallback
Boolean
Mandatory, when set as true
: if the slot leader is not paladin OR if your tx is not able to win during the auction during paladin leader slot => they will be sent via our sendTransaction pipeline. When set as false, it will throw error like Invalid Request: transaction received at slot xxx which is not a paladin slot, fallback not enabled
and will be silently dropped.
{
"id": 1,
"jsonrpc": "2.0",
"method": "sendPaladin",
"params": [
"<base 64 tx>",
{
"revertProtection": false,
"enableFallback" : true
}
]
}
{
"jsonrpc": "2.0",
"id": 1,
"result": [
"<signature A>",
]
}
Need Help?
Join our Discord for technical guides, integration support, and live updates.
Last updated