A working reference for Eliro. What each instruction does, what it verifies, and how to invoke it from a script. Read in order, or jump to the chapter you need.
Eliro is a treasury operating system for autonomous agents, built as a single Solana program. Capital will be held in a program-derived address. The reserve. Each agent is given a lane within that reserve, with its own balance, ledger, and constraint envelope.
The system replaces the server-side budget. Where most stacks ask you to trust an operator with rules and balances, Eliro stores the rules on-chain and verifies them in the same instruction that moves the funds. There is no middleware. There is no key handed to the agent.
Posture. The chain holds the capital. The program holds the rules. The agent only signs intent. Never authority.
The system is composed of four on-chain elements:
The authority is the wallet that deployed the reserve. Only the authority can create lanes, set constraints, or move the roster. The agent itself only invokes payment instructions. Authority over the reserve is never transferred to it.
When an agent asks to pay, it does not transfer directly. It invokes the reserve program with an intent: a lane, an amount, and a destination. The program runs every check in a single instruction:
If any check fails, the entire transaction reverts. Nothing moves. If all pass, the program signs the transfer from the reserve to the recipient and writes the new balance, spent count, and transaction count to the lane.
The reserve is the root account. It is created once and lives for the lifetime of the system. The PDA is derived from the authority key plus a small set of seeds, so the address is deterministic and computable off-chain.
pub fn create_reserve(
ctx: Context<CreateReserve>,
name: String,
initial_deposit: u64,
) -> Result<()> {
let reserve = &mut ctx.accounts.reserve;
reserve.authority = ctx.accounts.authority.key();
reserve.name = name;
reserve.balance = initial_deposit;
reserve.created_at = Clock::get()?.unix_timestamp;
reserve.bump = ctx.bumps.reserve;
Ok(())
}import { Eliro } from "@eliro/sdk";
const eliro = await Eliro.connect(connection, wallet);
const reserve = await eliro.deployReserve({
name: "production",
initialDeposit: 10_000n * 10n ** 6n,
});
console.log("Reserve:", reserve.address.toBase58());Each agent gets a lane. A partition within the reserve. Lanes are PDAs derived from the reserve and a label. They are deterministic; their addresses can be computed from the label alone.
A lane carries balance, spent count, transaction count, and a status flag. Authority can create, pause, resume, or close a lane at any time. A closed lane cannot be reopened.
const lane = await reserve.openLane({
agentId: "research_agent",
budget: 2_000n * 10n ** 6n, // 2,000 USDC
});
// Compute the lane address off-chain
const address = Eliro.deriveLane(reserve.address, "research_agent");Governance is the constraint envelope on a lane. It defines spending discipline as account data:
require!(amount <= rules.max_per_tx, ElError::ExceedsMaxPerTx);
let elapsed = clock.unix_timestamp - rules.day_start;
let spent = if elapsed >= 86_400 { 0 } else { rules.spent_today };
require!(
spent + amount <= rules.max_per_day,
ElError::ExceedsDailyCap
);The roster is the per-lane allowlist of recipient addresses. Each entry is a small PDA. Address plus label plus the timestamp it was added. The program checks every outbound payment against the lane's roster before signing.
const ROSTER = [
{ address: "4zMMC9...openai", label: "OpenAI · API" },
{ address: "7xKm3...claude", label: "Anthropic · Claude" },
{ address: "9pRt7...market", label: "Feed · Market data" },
];
for (const entry of ROSTER) {
await lane.addToRoster(entry);
}A compromised agent cannot exfiltrate to an unapproved address. The program will not sign the transfer. Period.
Hours bound when a lane may move funds. Define windows in UTC, with an optional day-of-week filter. Outside any defined window, transactions are rejected. The Solana clock sysvar is the time source. The agent's server clock is irrelevant.
await lane.setHours({
windows: [
{ startHour: 9, endHour: 17, days: [1, 2, 3, 4, 5] }, // Mon–Fri
],
});Replenish is the auto-refill instruction. Set a floor and a target on a lane. When the lane's balance drops below the floor, anyone can crank the instruction. But only the program can move the funds, and only up to the target.
await lane.configureReplenish({
floor: 500n * 10n ** 6n, // refill when below 500 USDC
target: 2_000n * 10n ** 6n, // refill up to 2,000 USDC
enabled: true,
});
// Permissionless crank. Anyone can call this
await reserve.crankReplenish(lane.address);Quorum is the multi-signer rule. Above a defined amount, a payment becomes a proposal account on-chain. Each required signer approves the proposal; once the threshold is met, anyone may execute. Proposals expire after a configurable TTL. Signer sets are stored on-chain and rotated by the authority.
await reserve.configureQuorum({
signers: [w1.publicKey, w2.publicKey, w3.publicKey],
threshold: 2, // 2-of-3
amountThreshold: 5_000n * 10n ** 6n,
proposalTtl: 86_400, // 24h
});
const proposal = await reserve.createProposal({
destination: vendor,
amount: 10_000n * 10n ** 6n,
});
await proposal.approve(w1);
await proposal.approve(w2); // threshold met
await proposal.execute();The TypeScript SDK provides a typed interface over the program. It handles PDA derivation, transaction construction, confirmation, and account decoding.
| Method | Action |
|---|---|
| Eliro.connect() | Bind connection + wallet. |
| eliro.deployReserve() | Create the reserve PDA. |
| reserve.openLane() | Open a per-agent lane. |
| lane.setGovernance() | Set max-per-tx + daily cap. |
| lane.addToRoster() | Add an approved recipient. |
| lane.setHours() | Configure time windows. |
| lane.configureReplenish() | Set auto-refill envelope. |
| reserve.configureQuorum() | Configure m-of-n quorum. |
| reserve.createProposal() | Open a quorum proposal. |
| proposal.approve() / execute() | Submit and finalise. |
The TypeScript SDK is in private preview. The signatures here reflect the target API. Reach out for early access.
npm install @eliro/sdk @solana/web3.js