@0block.io/sdk
v0.6.0
Published
Client-side transaction builder for the 0Block DEX router. Builds unsigned v0 swap transactions entirely offline — tenant-wrapped or direct — with shared address lookup tables, gateway tips, and on-chain fee-event decoding for accounting.
Maintainers
Readme
@0block.io/sdk
The SDK for 0Block's two products — independent, composable:
| Product | What it does | Needs the other? | |---|---|---| | DEX Smart Router | Build swap transactions on-chain-routed across 60+ venues — through your tenant wrapper (your fee applies) or directly (0Block fee only) | No — submit through any RPC | | Landing Service | Land any fully-signed Solana transaction: tip-gated submission forwarded to staked infrastructure and current/next leader TPUs | No — accepts any transaction |
Used together they form a complete trade pipeline: build with the router SDK, sign externally, land through the service.
Router highlights:
- Zero network calls at trade time. Market context, lookup tables, and a blockhash are resolved once server-side and streamed; the client assembles the transaction synchronously.
- One signature. The end user is the only required signer, in both modes.
- Fees are on-chain, not inputs. Tenant and platform commissions are read and enforced by the programs; the SDK never accepts a fee parameter.
- SOL-native UX, program-managed. In tenant mode the wrapper wraps/unwraps and cleans up token accounts on-chain — clients never touch wSOL.
- External-signer native. Returns the exact message bytes to sign, for
wallets, signing services, or HSMs — no
Keypairever touches the SDK.
Installation
npm install @0block.io/sdkRequires Node ≥ 20 server-side; browser builds work with any modern bundler.
Modes
The mode is fixed at initialization:
import { createContext } from "@0block.io/sdk";
// Tenant mode — swaps route through your tenant wrapper program.
// Your tenant fee (set on-chain) and the 0Block fee apply.
const ctx = createContext({
tenant: {
wrapperProgramId: "<your tenant wrapper program id>",
tenantFeeVault: "<your tenant fee vault>",
},
});
// Direct mode — swaps invoke the 0Block router directly.
// Only the 0Block platform fee applies.
const ctx = createContext();Quick start
1. Backend — resolve market context once, stream it
import { connectionFetcher, resolvePumpfunMarketContext } from "@0block.io/sdk";
const fetcher = connectionFetcher(connection);
const market = await resolvePumpfunMarketContext(fetcher, { mint });
// `market` is plain strings — push it to clients over your existing
// stream together with quotes and a FINALIZED blockhash.2. Backend ops — shared lookup table, once per venue set
import {
buildLookupTableAddresses,
buildInitLookupTableInstructions,
} from "@0block.io/sdk";
const addresses = buildLookupTableAddresses({ context: ctx, market });
const { tableAddress, transactions } = buildInitLookupTableInstructions({
authority: opsKey,
recentSlot,
addresses,
});
// Send each batch as one transaction, in order. Wait ~1 slot before first use.3. Client — preset → unsigned transaction → your signer
import { applyPreset, buildPumpfunSwapTransaction } from "@0block.io/sdk";
const { minReturn, tip, computeBudget } = applyPreset(
{ slippagePct: 20, prioritySol: 0.001, bribeSol: 0.001 },
{ expectedOut: quote.expectedOut, tipAccount: streamed.tipAccount }
);
const built = buildPumpfunSwapTransaction({
context: ctx,
market,
user: wallet.address,
side: "buy",
amountIn: 10_000_000n, // 0.01 SOL
minReturn,
recentBlockhash: streamed.finalizedBlockhash,
lookupTables: streamed.lookupTables,
tip,
computeBudget,
});
// built.messageBytes → sign with the user's key (exactly one signature)
// built.orderId → correlate the fill via SwapResultEvent
// built.estimatedSize → wire size, pre-checked against the 1232-byte limitAttach the signature and get wire bytes with serializeWithSignatures(built, [sig]),
then submit through the 0Block gateway.
4. Indexer — fee accounting from confirmed transactions
import { decodeSwapResultEvents } from "@0block.io/sdk";
const events = decodeSwapResultEvents(tx.meta.logMessages);
// [{ orderId, status, commissions: { feeMint, totalFeeAmount,
// items: [{ kind: "platform" | "tenant", bps, amount, tenantProgram }] } }]Landing Service (standalone)
Land any transaction — router-built or otherwise. Three rules: include the tip (a plain transfer to an allowlisted account, kept as a static message key, never behind a lookup table), use a finalized blockhash, and confirm by polling (no websocket).
import { createLandingClient, withLandingTip } from "@0block.io/sdk";
// Any instructions at all — no router required:
const ixs = withLandingTip(myInstructions, payer, {
account: tipAccount, // allowlisted tip account
lamports: 1_000_000n, // ≥ the service minimum
});
// ...compile with a FINALIZED blockhash, sign...
const landing = createLandingClient({
url: "https://<landing-endpoint>",
apiKey: process.env.ZEROBLOCK_API_KEY,
});
const { signature, slot } = await landing.submitAndConfirm(wireBytes, readConnection, {
lastValidBlockHeight,
});Router-built transactions already carry the tip when you pass tip to
buildPumpfunSwapTransaction — hand their signed wire bytes straight to
landing.sendTransaction.
API overview
| Runs | Function | Purpose |
|---|---|---|
| anywhere | createContext | Resolve tenant vs direct mode from init params |
| backend | resolvePumpfunMarketContext | Chain-derived accounts for a mint, as streamable strings |
| backend | buildLookupTableAddresses / buildInitLookupTableInstructions | Create the shared static-accounts table |
| client | applyPreset | Slippage % / priority SOL / bribe SOL → build params |
| client | buildPumpfunSwapTransaction | Complete unsigned v0 transaction (offline) |
| client | serializeWithSignatures | Attach external signatures → wire bytes |
| indexer | decodeSwapResultEvents | SwapResultEvent → normalized commission breakdown |
Lower-level building blocks (buildPumpfunSwapInstruction, PDA derivations,
wSOL wrap/unwrap helpers, tip and compute-budget builders, vendored IDLs) are
exported as well.
Operational notes
- Order ids are server-authoritative in tenant mode. The wrapper enforces
an exactly-once replay gate keyed by
(payer, order_id): a duplicate order id fails at account creation and the whole swap reverts. MintorderIdon your backend (non-zero u128, one per business order — reuse it across retries) and pass it tobuildPumpfunSwapTransaction; the SDK refuses to build tenant swaps without one. Direct mode has no marker; a random id is generated when omitted. - Tenant mode prerequisites. Your wrapper must be the build in which the
wrapper_configPDA signs the router CPI, and the router'sTenantConfig.tenant_authoritymust equal that PDA (deriveWrapperConfig(wrapperProgramId)). Otherwise swaps failUnauthorizedTenant. - Direct mode prerequisite. The router requires a
TenantConfigaccount in thetenant_cfgslot even on direct calls (ignored for fees). The router operator provisions a standing "direct" config; override its key withdirectTenantConfigKeyif needed. - Tips must stay static message keys. The gateway verifies the tip transfer on raw bytes without resolving lookup tables. The builder masks tip accounts out of table copies defensively — never add tip accounts to an ALT.
- Use a FINALIZED blockhash. The gateway forwards to a different staked
node; a
confirmedhash may be unknown there. - Native SOL handling. Buys wrap SOL → wSOL in-transaction (idempotent ATA
- transfer + SyncNative); sells close the wSOL ATA back to SOL. Opt out with
wrapSol/unwrapWsolif your users manage wSOL themselves.
- transfer + SyncNative); sells close the wSOL ATA back to SOL. Opt out with
- Venue coverage. v1 ships the pump.fun bonding-curve venue (buy/sell, single hop). Additional venues follow the same shape — one remaining-accounts builder per venue.
Development
npm install
npm test # compiles, then runs the offline smoke test (no network, no keys)The vendored IDLs under src/idl/ are generated from the on-chain programs'
Anchor builds. Regenerate them after any program change — account ordering and
discriminators must match on-chain reality.
License
UNLICENSED — all rights reserved. Contact 0block.io for usage terms.
