SDK Reference
TypeScript and Rust SDK documentation for interacting with the AXIO block engine and staking protocol.
SDK Reference
AXIO provides two TypeScript SDKs: the Searcher SDK for bundle submission and MEV operations, and the Staking SDK for interacting with the rewards API and staking protocol.
Searcher SDK (@axio/searcher-sdk)
The Searcher SDK wraps the AXIO gRPC API for high-performance bundle submission, encrypted bundles, streaming, and reputation management.
Installation
npm install @axio/searcher-sdkAxioClient
Create a client instance with your API key and engine endpoint:
import { AxioClient } from "@axio/searcher-sdk";
const client = new AxioClient({
apiKey: process.env.AXIO_API_KEY!,
endpoint: "grpc://engine.axio.fortiblox.com:7440",
});| Parameter | Type | Description |
|---|---|---|
apiKey | string | Your searcher API key |
endpoint | string | gRPC endpoint URL |
submitBundle
Submit a bundle of transactions for inclusion in the next available slot.
const result = await client.submitBundle({
transactions: [serializedTx1, serializedTx2],
tip: 500_000, // lamports
mevType: "arbitrage",
targetSlot: 149_900_001, // optional — defaults to next available
});
console.log(result.bundleId); // "d500d0bf-a73f-4a1b-a0ae-824d14e3ae45"
console.log(result.status); // "accepted"Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
transactions | Buffer[] | Yes | Serialized transactions (base64-encoded) |
tip | number | Yes | Tip amount in lamports |
mevType | string | Yes | "sandwich" | "arbitrage" | "liquidation" | "jit" | "unknown" |
targetSlot | number | No | Target slot for inclusion |
Returns: { bundleId: string; status: "accepted" | "rejected" }
submitEncryptedBundle
Submit an encrypted bundle using X25519 key exchange with ChaCha20-Poly1305 symmetric encryption. The bundle payload is encrypted client-side and only decrypted inside the engine's trusted execution context.
const result = await client.submitEncryptedBundle({
transactions: [serializedTx1, serializedTx2],
tip: 750_000,
mevType: "sandwich",
targetSlot: 149_900_050,
});
console.log(result.bundleId); // "a8f1c902-..."
console.log(result.status); // "accepted"The method accepts the same parameters as submitBundle. Encryption is handled transparently by the SDK using the engine's public key (fetched on first call and cached).
Returns: { bundleId: string; status: "accepted" | "rejected" }
createBundleStream
Open a bidirectional gRPC stream for high-frequency bundle submission. The stream allows you to send bundles continuously and receive confirmations as they are processed.
const stream = client.createBundleStream();
// Send bundles
stream.send({
transactions: [serializedTx],
tip: 600_000,
mevType: "arbitrage",
});
// Listen for confirmations
stream.on("data", (confirmation) => {
console.log(confirmation.bundleId); // "d500d0bf-..."
console.log(confirmation.status); // "accepted" | "included" | "dropped" | "expired"
console.log(confirmation.slot); // 149900001
console.log(confirmation.signatures); // ["5K8rE..."]
});
stream.on("error", (err) => {
console.error("Stream error:", err.message);
});
stream.on("end", () => {
console.log("Stream closed");
});
// Close when done
stream.end();Stream events:
| Event | Payload | Description |
|---|---|---|
data | BundleConfirmation | Bundle status update |
error | Error | Stream-level error |
end | — | Stream closed by server |
getBundleStatus
Check the current status of a submitted bundle.
const status = await client.getBundleStatus("d500d0bf-a73f-4a1b-a0ae-824d14e3ae45");
console.log(status.bundleId); // "d500d0bf-..."
console.log(status.status); // "included"
console.log(status.slot); // 149900001
console.log(status.signatures); // ["5K8rE...", "3mPq1..."]Parameters:
| Field | Type | Description |
|---|---|---|
bundleId | string | The bundle ID returned from submission |
Returns: { bundleId: string; status: string; slot?: number; signatures: string[] }
getTipFloor
Fetch the current dynamic tip floor. Bundles with tips below this value are rejected.
const tipFloor = await client.getTipFloor();
console.log(tipFloor); // 250000 (lamports)Returns: number — current tip floor in lamports.
getReputation
Retrieve your searcher reputation score and metrics.
const rep = await client.getReputation();
console.log(rep.score); // 87.4
console.log(rep.tier); // "gold"
console.log(rep.landingRate); // 0.947
console.log(rep.simSuccess); // 0.982Returns:
| Field | Type | Description |
|---|---|---|
score | number | Reputation score (0-100) |
tier | string | "bronze" | "silver" | "gold" | "platinum" |
landingRate | number | Fraction of bundles that landed on-chain |
simSuccess | number | Fraction of bundles that passed simulation |
Staking SDK (@axio/staking-sdk)
The Staking SDK provides access to the AXIO rewards API, APY data, exchange rates, MEV distribution, and yield estimation.
Installation
npm install @axio/staking-sdkAxioStakingClient
import { AxioStakingClient } from "@axio/staking-sdk";
const staking = new AxioStakingClient({
baseUrl: process.env.NEXT_PUBLIC_API_URL || "http://localhost:7435",
rpcUrl: process.env.NEXT_PUBLIC_RPC_URL!,
});| Parameter | Type | Description |
|---|---|---|
baseUrl | string | Rewards API base URL |
rpcUrl | string | X1/Tachyon RPC endpoint |
getOverview
Fetch aggregate rewards metrics for a given time period.
const overview = await staking.getOverview("24h");
console.log(overview.total_tips_earned); // 12400000000 (lamports)
console.log(overview.bundles_landed); // 1842
console.log(overview.avg_tip); // 6731
console.log(overview.landing_rate); // 94.7Parameters:
| Field | Type | Description |
|---|---|---|
period | string | "1h" | "24h" | "7d" | "30d" |
Returns: RewardsOverview
getAPY
Retrieve the current APY breakdown including base staking and MEV yield.
const apy = await staking.getAPY();
console.log(apy.current_apy); // 12.4
console.log(apy.base_apy); // 7.2
console.log(apy.mev_apy); // 5.2Returns: APYData
getExchangeRate
Get the current aXNT-to-SOL exchange rate and recent changes.
const rate = await staking.getExchangeRate();
console.log(rate.rate); // 1.096
console.log(rate.rate_change_24h); // 0.12Returns: ExchangeRateData
getReserveData
Fetch cumulative reserve injection data.
const reserve = await staking.getReserveData();
console.log(reserve.cumulative_injected_sol); // 847.3
console.log(reserve.last_injection); // "2026-04-20T18:30:00Z"Returns: ReserveData
getMEVDistribution
Get the MEV revenue breakdown by type for a given period.
const distribution = await staking.getMEVDistribution("24h");
for (const entry of distribution) {
console.log(entry.mev_type); // "Sandwich", "Arbitrage", etc.
console.log(entry.bundles); // 312
console.log(entry.tips); // 5800000000
}Parameters:
| Field | Type | Description |
|---|---|---|
period | string | "1h" | "24h" | "7d" | "30d" |
Returns: MEVDistEntry[]
estimateYieldWithInjection
Estimate projected yield for a given stake amount, factoring in the current APY and reserve injection schedule.
const estimate = await staking.estimateYieldWithInjection(100_000_000_000); // 100 SOL in lamports
console.log(estimate.daily); // 0.034 SOL
console.log(estimate.monthly); // 1.033 SOL
console.log(estimate.yearly); // 12.4 SOL
console.log(estimate.current_apy); // 12.4Parameters:
| Field | Type | Description |
|---|---|---|
stakeLamports | number | Amount to stake in lamports |
Returns: YieldEstimate
getValidators
Retrieve the list of validators in the AXIO network with their MEV performance scores.
const validators = await staking.getValidators();
for (const v of validators) {
console.log(v.address); // "7Np41oe..."
console.log(v.name); // "FortiBlox #1"
console.log(v.stake); // 245000
console.log(v.apy); // 13.2
console.log(v.commission); // 5
console.log(v.mevScore); // 98
}Returns: Validator[]
TypeScript Types
All types are exported from their respective SDK packages.
RewardsOverview
interface RewardsOverview {
total_tips_earned: number; // total tips in lamports
bundles_landed: number; // number of bundles included on-chain
avg_tip: number; // average tip per bundle in lamports
landing_rate: number; // percentage of bundles that landed (0-100)
}APYData
interface APYData {
current_apy: number; // current blended APY (base + MEV)
base_apy: number; // base staking APY from inflation rewards
mev_apy: number; // additional APY from MEV revenue sharing
}ExchangeRateData
interface ExchangeRateData {
rate: number; // current aXNT per SOL exchange rate
rate_change_24h: number; // percentage change over 24 hours
}ReserveData
interface ReserveData {
cumulative_injected_sol: number; // total SOL injected into the reserve
last_injection: string; // ISO 8601 timestamp of last injection
}MEVDistEntry
interface MEVDistEntry {
mev_type: string; // "Sandwich" | "Arbitrage" | "Liquidation" | "Other"
bundles: number; // number of bundles for this type
tips: number; // total tips in lamports for this type
}YieldEstimate
interface YieldEstimate {
daily: number; // estimated daily yield in SOL
monthly: number; // estimated monthly yield in SOL
yearly: number; // estimated yearly yield in SOL
current_apy: number; // APY used for the estimate
}Configuration
Both SDKs are configured via environment variables and constructor options.
Environment Variables
| Variable | Description | Default |
|---|---|---|
NEXT_PUBLIC_API_URL | Rewards API base URL | http://localhost:7435 |
NEXT_PUBLIC_RPC_URL | X1/Tachyon RPC endpoint | — |
AXIO_API_KEY | Searcher API key for gRPC authentication | — |
Example .env
NEXT_PUBLIC_API_URL=https://axio.fortiblox.com/api/v1
NEXT_PUBLIC_RPC_URL=https://rpc.x1.fortiblox.com
AXIO_API_KEY=sk_live_abc123def456Error Handling
Both SDKs throw typed errors that should be caught and handled appropriately.
Searcher SDK Errors
import { AxioClient, AxioError } from "@axio/searcher-sdk";
const client = new AxioClient({
apiKey: process.env.AXIO_API_KEY!,
endpoint: "grpc://engine.axio.fortiblox.com:7440",
});
try {
const result = await client.submitBundle({
transactions: [serializedTx],
tip: 500_000,
mevType: "arbitrage",
});
console.log("Bundle accepted:", result.bundleId);
} catch (err) {
if (err instanceof AxioError) {
switch (err.code) {
case "TIP_BELOW_FLOOR":
console.error("Tip too low. Current floor:", err.details.tipFloor);
break;
case "SIMULATION_FAILED":
console.error("Simulation failed:", err.details.logs);
break;
case "RATE_LIMITED":
console.error("Rate limited. Retry after:", err.details.retryAfterMs, "ms");
break;
case "AUTH_FAILED":
console.error("Invalid API key");
break;
default:
console.error("AXIO error:", err.code, err.message);
}
} else {
console.error("Unexpected error:", err);
}
}Staking SDK Errors
import { AxioStakingClient } from "@axio/staking-sdk";
const staking = new AxioStakingClient({
baseUrl: process.env.NEXT_PUBLIC_API_URL || "http://localhost:7435",
rpcUrl: process.env.NEXT_PUBLIC_RPC_URL!,
});
try {
const apy = await staking.getAPY();
console.log("Current APY:", apy.current_apy);
} catch (err) {
if (err instanceof Error) {
if (err.message.includes("ECONNREFUSED")) {
console.error("Rewards API is not reachable. Check NEXT_PUBLIC_API_URL.");
} else if (err.message.includes("404")) {
console.error("Endpoint not found. Verify API version.");
} else {
console.error("Staking SDK error:", err.message);
}
}
}Stream Error Handling
const stream = client.createBundleStream();
stream.on("error", (err) => {
if (err.code === 14) {
// gRPC UNAVAILABLE — server is down or unreachable
console.error("Connection lost. Reconnecting...");
reconnect();
} else if (err.code === 8) {
// gRPC RESOURCE_EXHAUSTED — rate limited
console.error("Rate limited on stream. Backing off...");
backoff();
} else {
console.error("Stream error:", err.code, err.message);
}
});
function reconnect() {
setTimeout(() => {
const newStream = client.createBundleStream();
// Re-attach handlers...
}, 2000);
}
function backoff() {
setTimeout(() => {
// Resume sending at reduced rate
}, 5000);
}Retry Pattern
For production systems, wrap calls in a retry helper with exponential backoff:
async function withRetry<T>(
fn: () => Promise<T>,
maxRetries = 3,
baseDelayMs = 1000
): Promise<T> {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (err: any) {
if (attempt === maxRetries) throw err;
if (err.code === "RATE_LIMITED" || err.message?.includes("429")) {
const delay = baseDelayMs * Math.pow(2, attempt);
await new Promise((r) => setTimeout(r, delay));
} else {
throw err; // Don't retry non-retryable errors
}
}
}
throw new Error("Unreachable");
}
// Usage
const result = await withRetry(() =>
client.submitBundle({
transactions: [serializedTx],
tip: 500_000,
mevType: "arbitrage",
})
);