rig-onchain-kit Agent: Solana Balance → Jupiter Swap (dry-run) → SignerContext Isolation
Built by Murtaza Ali Imtiaz · Technology Lead · Polar Bear (🍨) · July 2019 – Present
A production-grade Rust implementation of the rig-onchain-kit on-chain agent pattern, powered
by Rig (ARC). Demonstrates Solana devnet balance queries, Jupiter V6 dry-run
swap quotes, and task-local SignerContext signer isolation via tokio::task_local! - the
correct async-safe implementation of the rig-onchain-kit security boundary pattern.
ANTHROPIC_API_KEY is only required for --mode full without --no-agent. Every other
invocation - including --mode full --no-agent - works without it.
| Invocation | API key needed? |
|---|---|
--mode full |
✅ Yes - constructs the rig-core agent |
--mode full --no-agent |
❌ No - runs balance → quote → signer directly |
--mode balance |
❌ No |
--mode quote |
❌ No |
--mode signer |
❌ No |
cargo test |
❌ No - live provider tests are #[ignore]-gated |
cargo run --example * |
❌ No |
Pass --no-agent alongside --mode full to bypass the rig-core agent call entirely. The
pipeline still runs all three on-chain subsystems in sequence (balance → quote → signer) but
skips constructing the Anthropic client, so no API key is needed.
# Full pipeline output, no API key required
cargo run --release -- --mode full --no-agent --amount 0.1Useful for:
- CI / CD - smoke-test the on-chain subsystems without injecting secrets
- Local development - iterate on balance/quote/signer logic before wiring the agent
- Demos - run the complete pipeline output in a keyless environment
When the flag is absent, --mode full behaves as before: it requires ANTHROPIC_API_KEY
and returns a clear error if it is missing.
CLI Entry (main.rs)
--mode [full|balance|quote|signer] [--no-agent]
│
├─ --mode full (default) ──────────────────────────── requires API key
│ ┌──────────────────────────────────────────────┐
│ │ rig-core Agent (claude-sonnet-4-6) │
│ │ PEV loop: SolanaBalanceTool │
│ │ JupiterQuoteTool (dry-run) │
│ │ SignerIsolationTool │
│ └──────────────────────────────────────────────┘
│
└─ --mode full --no-agent ────────────────────────── no API key needed
balance → quote → signer (direct, no LLM)
│
┌────┴───────────────────────────────┐
│ onchain::signer │──── tokio::task_local! SignerContext
│ with_signer(signer, f) │ (task-scoped, not thread-scoped)
├────────────────────────────────────┤
│ onchain::balance │──── Solana devnet RPC get_balance
│ onchain::jupiter │──── Jupiter V6 /quote (read-only)
└────────────────────────────────────┘
| Layer | Technology |
|---|---|
| AI Agent Framework | rig-core (Rig / ARC) ≥ 0.37 |
| On-chain bridge | rig-onchain-kit pattern (tokio::task_local! SignerContext) |
| Async runtime | Tokio |
| Blockchain | Solana (devnet) |
| DEX aggregator | Jupiter V6 /quote (dry-run, no txns) |
| CLI | clap |
| Logging | tracing + tracing-subscriber |
| IDE | Zed (tasks, debugger, rust-analyzer LSP) |
# Prerequisites: Rust 1.93.1+ (rustup.rs)
git clone https://github.com/murtazaai/polar-bear-rig-onchain
cd polar-bear-rig-onchain
# Build - no API key needed
cargo build --release
# ── No API key needed ──────────────────────────────────────────────────────
# Full pipeline, agent skipped (balance → quote → signer direct)
cargo run --release -- --mode full --no-agent
# Solana devnet balance query
cargo run --release -- --mode balance
# Jupiter V6 dry-run swap quote
cargo run --release -- --mode quote --amount 0.1
# SignerContext task-local isolation demo (3 concurrent tasks)
cargo run --release -- --mode signer
# Standalone examples
cargo run --example balance_demo
cargo run --example signer_demo
cargo run --example jupiter_dry_run
# ── Full pipeline with rig-core agent (requires ANTHROPIC_API_KEY) ─────────
cp .env.example .env # then set ANTHROPIC_API_KEY=sk-ant-...
cargo run --release -- --mode full --wallet <DEVNET_ADDRESS> --amount 0.1# All deterministic unit tests - no API key needed
cargo test
# Per-module (all keyless, --nocapture for tracing output)
cargo test --test test_balance -- --nocapture
cargo test --test test_jupiter -- --nocapture
cargo test --test test_signer_context -- --nocapture
# Lint
cargo clippy --all-targets -- -D warnings
# Live provider tests - require ANTHROPIC_API_KEY, opt-in only
ANTHROPIC_API_KEY=sk-ant-... cargo test --test providers -- --ignored --test-threads=1 --nocaptureThe live tests in tests/providers/anthropic.rs are gated with #[ignore] and are never
included in a plain cargo test run. They must be invoked explicitly with --ignored and a
valid key in the environment.
The repository ships a fully configured .zed/ workspace with three files that wire up the
entire development workflow inside Zed.
Open the task picker with Cmd/Ctrl + Shift + P → "task: spawn" (or bind to a key). Tasks are tagged so you can filter by group in the picker.
| Tag | Tasks included |
|---|---|
build |
check, build: debug, build: release, clean |
lint |
clippy, fmt: check, doc: CI check |
fmt |
fmt, fmt: check |
docs |
doc: open, doc: open (private items), doc: CI check |
test |
test: all, test: balance, test: jupiter, test: signer context, test: providers ⚠ |
run |
run: full --no-agent, run: balance, run: quote, run: signer demo, run: full pipeline ⚠ |
example |
example: balance_demo, example: signer_demo, example: jupiter_dry_run |
live |
Tasks marked ⚠ that require ANTHROPIC_API_KEY |
Every task sets cwd = $ZED_WORKTREE_ROOT, RUST_LOG = polar_bear_rig_onchain=debug, and
RUST_BACKTRACE so tracing output is always visible. The doc: CI check task sets
RUSTDOCFLAGS = "--cfg docsrs -D warnings" to mirror the docs.rs build exactly.
Install the CodeLLDB extension via Zed → Extensions if not already present.
Every debug config calls "build_task": "build: debug" before attaching, so the binary
is always fresh. Set breakpoints in the gutter before launching.
| Config | API key? | Notes |
|---|---|---|
Debug: --mode full --no-agent |
❌ | Recommended starting point - steps through all three subsystems |
Debug: --mode balance |
❌ | Isolates the Solana RPC call |
Debug: --mode quote |
❌ | Isolates the Jupiter HTTP call and JSON deserialisation |
Debug: --mode signer |
❌ | Steps through the 3-task tokio::task_local! demo |
Debug: --mode full ⚠ |
✅ | Full PEV loop; breakpoints in src/agent/mod.rs or tools.rs |
Debug: --mode full --wallet --amount 0.5 ⚠ |
✅ | Custom wallet and amount pre-filled |
Debug: example - balance_demo |
❌ | |
Debug: example - signer_demo |
❌ | |
Debug: example - jupiter_dry_run |
❌ |
All configs set RUST_LOG = polar_bear_rig_onchain=debug and RUST_BACKTRACE = full so
structured log output appears in the Zed debug console.
Applies only to this workspace (overrides ~/.config/zed/settings.json):
- Format on save - delegates to rust-analyzer → rustfmt, so
rustfmt.toml(edition 2024, max_width 100, crate-level imports) is respected automatically - rust-analyzer check command - runs
clippy --all-targets -- -D warningson save, keeping the Zed Problems panel in sync with theclippytask - Inlay hints - type hints, parameter hints, chaining hints, closure capture hints, lifetime elision hints, reborrow hints
- Proc macros - enabled for clap
#[derive(Parser/ValueEnum)], serde#[derive], andtokio::task_local!(with a suppression for false-positive "not expanded" warnings) - Completion - autoimport, fill-arguments snippets, private-editable members
- Import granularity -
crate+StdExternalCrategrouping, matchingrustfmt.toml - Code lens - run/debug/references/implementations rendered above functions and structs
- Semantic highlighting - strings, operators, punctuation
Tokio tasks can be moved between OS threads at any await point. Using thread_local!
would cause a signer installed on thread T to be visible to a different task that later runs
on thread T - key contamination in a concurrent HFT pipeline. tokio::task_local! via
with_signer(signer, f) scopes the keypair slot to the Tokio task, giving each in-flight
trade its own isolated signing context with zero locking overhead.
Proprietary - © 2026 Murtaza Ali Imtiaz / Polar Bear (🍨)
See LICENSE-PBS for permitted use.
Licensed under:
Murtaza Ali Imtiaz · Technology Lead · Polar Bear (🍨) · (July 2019 – Present)
- GitHub: @murtazaai
- LinkedIn: linkedin.com/in/murtazai
- Portfolio: murtazai.com