DOCUMENTATION
BlindRail SDK reference
program HzGxztZEtQkkNyXDKejtUu4M9RtXa1TPoiGhiLnP9zBK · same on mainnet & devnet
Getting started
Introduction
blindrail-sdk is a TypeScript client for the BlindRail treasury program on Solana. Open on-chain SOL treasuries, accept payments, pay out to any address, add SPL / Token-2022 token vaults, airdrop SOL or tokens, and stake the protocol token for fee discounts — from a backend or the browser. Every instruction is input-validated, the IDL is bundled, and transactions confirm over HTTP (so a flaky RPC websocket can't leave a transaction hanging). SOL amounts are in lamports (1 SOL = 1,000,000,000); token amounts are in the mint's base units.Installation
Install from npm. Peer deps
@solana/web3.js and @coral-xyz/anchor are pulled in automatically; the SDK re-exports the handles you need.npm install blindrail-sdk
import { TreasuryClient, Connection, Keypair, PublicKey, BN } from "blindrail-sdk";Initialize the client
From a keypair (backend/scripts) or an Anchor provider (browser wallet). The signing wallet is the treasury admin for management calls, or a delegated operator for payouts.
// Backend
const connection = new Connection("https://your-rpc-endpoint", "confirmed");
const client = TreasuryClient.fromKeypair(connection, keypair);
// Browser (wallet adapter)
import { AnchorProvider } from "@coral-xyz/anchor";
const provider = new AnchorProvider(connection, wallet, { commitment: "confirmed" });
const client = new TreasuryClient(provider);The treasury address
A treasury is an account at a Program Derived Address (PDA). That address — one base58 string — is the only identifier you need; pass it to every call.
createTreasury returns it; store it and reuse it. You never handle seeds, the numeric id, or the vault — the SDK derives those.Fees & the rent reserve
- SOL payout fee — basis points taken from each SOL payout; deducted from the amount (recipient gets
amount − fee). Read viafetchProtocol().feeBps. - Creation fee — flat SOL charged when opening a treasury (read via
fetchCreationFee()). - Token-vault fees — adding a mint to a treasury costs a flat SOL fee, and each token payout costs a flat SOL fee; the recipient always receives the full token amount. Read both via
fetchTokenFees(). - Airdrop fee — a flat 0.001 SOL per recipient, paid straight from the sender (no treasury needed).
- Staking discount — staking the protocol token (DRAIL) reduces the SOL payout & token-payout fees by 25–100%. The SDK applies it automatically when you have an active stake; see
stake. - Rent reserve — the vault must stay rent-exempt (~0.00089 SOL), so the max single payout is
vaultBalance − 0.00089 SOL. UsecloseTreasuryto recover 100%.
Use with AI agents
Agent skill
BlindRail ships an official agent skill — a compact spec that teaches AI coding tools how to use
blindrail-sdk correctly (lamports, the treasury-address-is-the-PDA rule, the { signature, treasury, id } return shape, HTTP-polled confirmation, the operator security model). One command drops it into your project. Works natively as a skill in Claude, and as a rules/context file for every other major assistant.# Claude Code / Claude Agent SDK (native skill → .claude/skills/blindrail/)
npx blindrail-sdk add-skill
# pick a tool, or install for all of them at once
npx blindrail-sdk add-skill cursor # → .cursor/rules/blindrail.md
npx blindrail-sdk add-skill windsurf # → .windsurf/rules/blindrail.md
npx blindrail-sdk add-skill copilot # → .github/copilot-instructions.md
npx blindrail-sdk add-skill codex # → AGENTS.md (Codex & AGENTS.md-aware agents)
npx blindrail-sdk add-skill allSupported tools
- Claude Code / Claude Agent SDK — installed as a native skill at
.claude/skills/blindrail/. - Cursor — project rule at
.cursor/rules/blindrail.md. - Windsurf — rule at
.windsurf/rules/blindrail.md. - GitHub Copilot — repo instructions at
.github/copilot-instructions.md. - OpenAI Codex & other AGENTS.md-aware agents —
AGENTS.mdat the repo root.
Treasury
createTreasury
createTreasury(name?, opts?): Promise<{ signature, treasury, id }>
Open a new treasury (charges the creation fee). Returns the treasury address.
PARAMETERS
RETURNS
{ signature, treasury: PublicKey, id: BN } — store treasury.const { signature, treasury, id } = await client.createTreasury("Season Rewards");deposit
deposit(treasury, amount): Promise<string>
Fund the treasury vault with SOL. Anyone can deposit.
PARAMETERS
RETURNS
Transaction signature.
await client.deposit(treasury, 1_000_000_000); // 1 SOLcloseTreasury
closeTreasury(treasury): Promise<string>
Drain the entire vault back to the admin and close the treasury. Admin only.
PARAMETERS
RETURNS
Transaction signature.
await client.closeTreasury(treasury);fetchTreasury
fetchTreasury(treasury): Promise<TreasuryAccount>
Read a treasury's on-chain state.
PARAMETERS
RETURNS
{ admin, creator, name, operators, operatorCount, totalDeposited, totalPaidOut, paused, pendingAdmin }const t = await client.fetchTreasury(treasury);
console.log(t.name, t.totalDeposited.toString());Payments
pay
pay(treasury, amount, reference?): Promise<{ signature, receipt }>
Customer payment into a treasury with an order/invoice
reference. The full amount lands in the treasury (no inbound fee); returns a receipt and emits a PaymentReceived event for reconciliation.PARAMETERS
RETURNS
{ signature, receipt: { treasury, payer, amount, reference, newTotalDeposited, timestamp } }const { receipt } = await client.pay(treasury, 50_000_000, 100423);Payouts
payout
payout(treasury, recipient, amount): Promise<string>
Pay SOL from the vault to any address. The protocol fee is deducted from
amount (recipient receives amount − fee). Callable by the admin or a delegated operator.PARAMETERS
RETURNS
Transaction signature.
await client.payout(treasury, recipient, 50_000_000); // 0.05 SOL grossbatchPayout
batchPayout(treasury, items): Promise<string>
Up to 10 payouts in a single transaction (
MAX_BATCH_PAYOUTS = 10) — the Solana transaction-size limit. Admin or operator. For larger lists (e.g. a CSV of hundreds of recipients), split them into chunks of 10 and send one transaction per chunk, as shown below. The dashboard's CSV / Excel upload does exactly this, with a confirmation step and progress.PARAMETERS
RETURNS
Transaction signature.
// single batch (≤ 10)
await client.batchPayout(treasury, [
{ recipient: addrA, amount: 50_000_000 },
{ recipient: addrB, amount: 30_000_000 },
]);
// larger list — chunk into groups of 10
const all = [/* { recipient, amount } … hundreds */];
for (let i = 0; i < all.length; i += 10) {
await client.batchPayout(treasury, all.slice(i, i + 10));
}Token vaults
createTokenVault
createTokenVault(treasury, mint, tokenProgram?): Promise<{ signature, vault }>
Add an SPL / Token-2022 mint to a treasury — opens a vault (an ATA owned by the treasury PDA) for that mint. Charges the flat add-mint fee in SOL. A treasury can hold many mints; deposits afterwards are plain token transfers into the returned
vault. Admin only.PARAMETERS
RETURNS
{ signature, vault: PublicKey } — the treasury's token vault address.const info = await client.fetchMintInfo(mint);
const { vault } = await client.createTokenVault(treasury, mint, info.tokenProgram);payoutToken
payoutToken(treasury, mint, recipient, amount, tokenProgram?): Promise<string>
Pay tokens from a treasury's vault. The recipient receives the full token amount; the fee is charged separately in SOL (the configurable token-payout fee, reduced by any staking discount).
amount is in base units. Admin or operator.PARAMETERS
RETURNS
Transaction signature.
await client.payoutToken(treasury, mint, recipient, 250_000_000n); // 250 tokens (9 dp)closeTokenVault
closeTokenVault(treasury, mint, adminTokenAccount, tokenProgram?): Promise<string>
Sweep a treasury's token vault to the admin's token account and close it, reclaiming the rent. Admin only.
PARAMETERS
RETURNS
Transaction signature.
await client.closeTokenVault(treasury, mint, adminAta);fetchTokenFees
fetchTokenFees(): Promise<{ addMintFee, payoutFee } | null>
The current token-vault fees in lamports — the SOL charged to add a mint and per token payout.
RETURNS
{ addMintFee: BN, payoutFee: BN } | nullconst f = await client.fetchTokenFees();
console.log(f?.addMintFee.toString(), f?.payoutFee.toString());fetchMintInfo
fetchMintInfo(mint): Promise<MintInfo | null>
Read a mint's decimals and owning token program (SPL Token vs Token-2022) — pass the program through to the token calls.
PARAMETERS
RETURNS
{ mint, decimals, tokenProgram } | nullconst info = await client.fetchMintInfo(mint);Airdrops
airdropSol
airdropSol(items): Promise<string>
Send SOL to many wallets in one transaction — no treasury needed, straight from the signer. Flat 0.001 SOL fee per recipient. Up to 10 per transaction (
MAX_AIRDROP_RECIPIENTS = 10); chunk larger lists like batchPayout.PARAMETERS
RETURNS
Transaction signature.
await client.airdropSol([
{ recipient: addrA, amount: 10_000_000 },
{ recipient: addrB, amount: 10_000_000 },
]);airdropToken
airdropToken(mint, items, tokenProgram?): Promise<string>
Send tokens to many wallets in one transaction, from the signer's token account. Each recipient must already hold a token account for the mint. Flat 0.001 SOL fee per recipient; up to 10 per transaction.
PARAMETERS
RETURNS
Transaction signature.
await client.airdropToken(mint, [
{ recipient: addrA, amount: 100_000_000n },
{ recipient: addrB, amount: 100_000_000n },
]);Staking
stake
stake(mint, amount, tokenProgram?): Promise<string>
Stake (or top up) the protocol token (DRAIL) to earn a fee discount: 500k → 25%, 1M → 50%, 2.5M → 75%, 5M → 100% off the SOL payout & token-payout fees. Staking (or any top-up) resets a 30-day lock. The discount applies automatically to your payouts while staked.
amount is in base units.PARAMETERS
RETURNS
Transaction signature.
await client.stake(drailMint, 500_000_000_000n); // 500k DRAIL (9 dp)unstake
unstake(mint, amount, tokenProgram?): Promise<string>
Withdraw staked tokens after the 30-day lock has elapsed. Partial withdrawals are allowed; the discount continues to apply to whatever remains staked.
PARAMETERS
RETURNS
Transaction signature.
await client.unstake(drailMint, 500_000_000_000n);fetchStake
fetchStake(owner?): Promise<{ amount, unlockAt } | null>
The staked amount and unlock timestamp for a wallet (defaults to the signer), or null if it hasn't staked.
PARAMETERS
RETURNS
{ amount: BN, unlockAt: BN } | nullconst s = await client.fetchStake();
if (s) console.log(s.amount.toString(), new Date(s.unlockAt.toNumber() * 1000));fetchStakingConfig
fetchStakingConfig(): Promise<StakingConfig | null>
The staking config — the protocol-token mint and the four tier thresholds — or null if staking isn't live yet.
RETURNS
{ mint, tier1, tier2, tier3, tier4, totalStaked } | nullconst cfg = await client.fetchStakingConfig();Operators
addOperator
addOperator(treasury, operator): Promise<string>
Authorize a delegated operator key that can sign payouts (max 5). Admin only — keep the admin key offline; let your server hold the operator key.
PARAMETERS
RETURNS
Transaction signature.
await client.addOperator(treasury, operatorPublicKey);removeOperator
removeOperator(treasury, operator): Promise<string>
Revoke an operator. Admin only.
PARAMETERS
RETURNS
Transaction signature.
await client.removeOperator(treasury, operatorPublicKey);Management
setPaused
setPaused(treasury, paused): Promise<string>
Pause or resume payouts. Deposits and payments still work while paused. Admin only.
PARAMETERS
RETURNS
Transaction signature.
await client.setPaused(treasury, true);nominateAdmin
nominateAdmin(treasury, newAdmin): Promise<string>
Step 1 of a two-step admin transfer. The admin does not change until the nominee calls acceptAdmin, so a wrong address can't lock you out. Admin only.
PARAMETERS
RETURNS
Transaction signature.
await client.nominateAdmin(treasury, newAdminPubkey);acceptAdmin
acceptAdmin(treasury): Promise<string>
Step 2 — the nominated wallet (the current signer) accepts and becomes the admin.
PARAMETERS
RETURNS
Transaction signature.
// signed by the nominated wallet's client
await newAdminClient.acceptAdmin(treasury);Reads
fetchProtocol
fetchProtocol(): Promise<ProtocolConfig | null>
Read the protocol config — useful for the current payout fee.
RETURNS
{ feeBps, totalTreasuries, totalFeesCollected }const p = await client.fetchProtocol();
console.log(p.feeBps); // e.g. 100 = 1%fetchCreationFee
fetchCreationFee(): Promise<BN | null>
Current treasury-creation fee in lamports (null if not configured).
RETURNS
BN | null
const fee = await client.fetchCreationFee();