A TypeScript client for the ESPO RPC API. Every call returns a BoxedResponse (via the bxrs package) so that success and failure states are explicit and exhaustively typed. If you have used Rust’s Result, you will feel right at home.
npm install espo-ts
# or
yarn add espo-ts
# or
pnpm add espo-tsimport { Espo } from "espo-ts";
const client = new Espo("https://api.alkanode.com/rpc");
const holders = await client.getHolders("2:0", { limit: 5 });
if (holders.isErr()) {
console.error("Failed:", holders.errorType, holders.message);
process.exit(1);
}
console.log("First holder", holders.data.items.at(0));This SDK intentionally reuses bxrs primitives so you can chain, unwrap, or retry the same way you would with a Rust Result. If you have not used bxrs before, read their primitives (mirrored below) and the official docs.
import {
BoxedResponse,
Err,
Ok,
isErr,
retryOnBoxedError,
consumeOrThrow,
} from "bxrs";bxrs gives you:
Ok(data)/Err(message?, errorType?)factories- Gradual transforms with
.map(),.mapErr(),.andThen() - Guards (
isErr,isOk) and helpers (consumeOrThrow,consumeOrNull,retryOnBoxedError) - Long-form docs covering motivation, core types, and best practices
const espo = new Espo("https://api.alkanode.com/rpc");
const tip = await espo.getTipHeight();
const holders = await espo.getHolders("2:68479", { limit: 10, page: 1 });
const balances = await espo.getAddressBalances(
"bc1pzysd2zc06t2j8wv3tqpsc2sj49kseerqmfn5g4zzz5nq23a4g36qwq24rw",
{ includeOutpoints: true }
);
const outpoints = await espo.getAddressOutpoints(
"bc1pzysd2zc06t2j8wv3tqpsc2sj49kseerqmfn5g4zzz5nq23a4g36qwq24rw"
);
const keys = await espo.getKeys("2:68479", { tryDecodeUtf8: true, limit: 5 });All returned payloads have already been normalized:
- Amount strings are converted to
number(8-decimal precision) orbigint - Optional curse-case parameters accept both snake and camel variants
value_u128fields fromgetKeysbecomebigint | null
const pool = "2:69744";
const base = "2:68479";
const quote = "2:0";
const pools = await espo.getPools({ limit: 5 });
const trades = await espo.getTrades(pool, { limit: 10, direction: "desc" });
const path = await espo.findBestSwapPath(base, quote, {
mode: "exact_in",
amountIn: 1_000_000n,
});
if (path.isOk()) {
console.log("Amount out", path.data.amount_out.toString());
console.log("Hops", path.data.hops);
}Every AMM response preserves full precision: pool reserves, swap amounts, and profits arrive as bigints, and swap hops are pre-normalized for convenience.
const holders = await espo.getHolders("2:0");
const response = holders
.map((payload) => payload.items.slice(0, 3))
.mapErr((err) => ({
errorType: "EspoClientError",
message: `failed at ${new Date().toISOString()}: ${err.message}`,
}));
if (response.isErr()) {
console.error(response.errorType, response.message);
return;
}
console.table(response.data);If you prefer to throw on failure:
import { consumeOrThrow } from "bxrs";
const retry = retryOnBoxedError({ timeoutMs: 10_000 });
const bestSwap = await retry(() => espo.getBestMevSwap("2:68479"));
const payload = consumeOrThrow(bestSwap);
console.log(payload.amount_out.toString());This repo includes npm run test which hits the public RPC using the provided address/outpoint/pool. Review test.ts for a real-world example of calling every method in sequence.