Encrypt-evaluated guardrails. Ika-cosigned execution. Zero plaintext policy on-chain.
Demo video · Pull request #1 — Ika × Encrypt integration · Author
- The problem
- What Warden is
- How it works (end-to-end)
- Why Encrypt and Ika are both load-bearing
- On-chain surface
- Live devnet artefacts
- Repository layout
- Running it yourself
- Test evidence
- Highlights from PR #1
- Roadmap
- License
You allocate capital to an autonomous AI agent — a market-making bot, a treasury rebalancer, a yield router. You want hard guardrails: max 3 % per trade, max 2 % daily drawdown, no more than five concurrent positions. You also want execution to span Bitcoin, Ethereum, and Solana without bridges or custodians.
Three things make this hard today.
- Policy is alpha. Publishing the guardrails on a public chain destroys their value — the moment the limits are visible, every other agent front-runs them.
- Position state is private. The agent does not want to publish its position book either. So neither side can give the other the inputs needed to verify compliance the obvious way.
- Authority must be multi-chain. The agent does not live on one chain; the policy does. Bridging the verdict is a custody hop and a new trust assumption.
Warden solves all three on real, deployed infrastructure.
warden-core is an Anchor program on Solana devnet at
Htrj84e45UCgFTfn7GfDoHZHRRiPC8Lr74PD3mKdtBFq.
It is the only legal path between an autonomous agent and its multi-chain dWallet. Every action the agent wants to sign must:
- Pass a homomorphically-evaluated compliance graph on the Encrypt network (so neither the policy nor the action ever leaves ciphertext on-chain), and
- Receive a co-signature from the agent's Ika dWallet via a Warden-controlled CPI authority (so the boolean from step 1 is enforceable across every chain Ika supports).
Both steps are mandatory. There is no bypass instruction.
PRINCIPAL AGENT
| |
| 1. create_agent + bind_dwallet |
v |
+------------------+ |
| warden-core | |
| (this program) | <-------- 2. submit_proposal --+
+------------------+
| |
| CPI | (encrypted inputs: trade-size, daily-loss,
v | open-positions vs encrypted guardrails)
+-----------------+
| Encrypt | 3. execute_graph(check_compliance) ---> EBool ciphertext
| REFHE network |
+-----------------+
|
| 4. request_decryption (digest pinned in proposal state)
v
network publishes plaintext into decryption_request account
|
| 5. reveal_and_authorize
v
+------------------+
| warden-core |
+------------------+
|
| CPI (only if EBool == true)
v
+-----------------+ 6. approve_message(result_commitment)
| Ika dWallet | ----------------------------------------> agent broadcasts
| 2PC-MPC | on BTC / ETH / SOL / ...
+-----------------+
Step by step:
- Bind. The principal calls
create_agentandbind_dwallet. The dWallet's signing authority is held by the Warden PDA[b"__ika_cpi_authority"]— only this program can request an Ika co-signature for that agent. - Propose. Off-chain, the agent encrypts six
EUint64values via Encrypt's gRPC (CreateInput): the proposed trade size, the rolling daily loss, the open-position count, plus the principal's three encrypted guardrail thresholds. Then it callssubmit_proposal, which CPIsEncrypt::execute_graph. Encrypt's network homomorphically evaluatescheck_complianceand writes a verifiedEBoolciphertext into the output account. - Reveal. Anyone can call
request_compliance_decryption. Warden CPIsEncrypt::request_decryptionand pins the ciphertext digest into proposal state so a later reveal cannot be substituted by a malicious executor. - Authorize. Once the network publishes the plaintext,
reveal_and_authorizereadsread_decrypted_verified::<Bool>(req_data, digest). Iftrue, Warden CPIsIka::approve_messageto co-sign the action'sresult_commitment(a Pedersen commitment to the plaintext action). Iffalse, the proposal is permanently markedRejected.
The principal never sees the agent's positions. The agent never sees the principal's guardrails. The signing key is held jointly between user and Ika network — neither can move funds alone.
| Layer | What we use | Why it can't be removed |
|---|---|---|
| Encrypt | #[encrypt_fn] check_compliance graph + execute_graph + request_decryption + read_decrypted_verified |
The policy is the alpha. Without homomorphic evaluation, either the principal publishes the rules (dead product) or runs an off-chain oracle (custodial). Encrypt is the only way to get programmable, private, on-chain policy in normal Rust. |
| Ika | DWalletContext::approve_message with the dWallet authority delegated to Warden's PDA |
The verdict has to translate into a signature on every chain the agent operates on. Without a 2PC-MPC dWallet, the agent either holds its own keys (no enforcement) or each chain needs a bespoke threshold-signing network. Ika is the only practical multi-chain primitive. |
Pull either out and Warden has no product.
The compliance graph itself is just plain Rust, lifted into FHE by #[encrypt_fn]:
#[encrypt_fn]
fn check_compliance(
trade_size_bps: EUint64, daily_loss_bps: EUint64, open_positions: EUint64,
max_trade_bps: EUint64, loss_limit_bps: EUint64, max_open_pos: EUint64,
) -> EBool {
let size_ok = trade_size_bps.is_less_or_equal(&max_trade_bps);
let loss_ok = daily_loss_bps.is_less_or_equal(&loss_limit_bps);
let positions_ok = open_positions.is_less_than(&max_open_pos);
size_ok.and(&loss_ok).and(&positions_ok)
}warden-core exposes five instructions:
| Instruction | Purpose | CPI |
|---|---|---|
create_agent |
Create the Agent PDA owned by the principal. |
— |
bind_dwallet |
Persist the Ika dWallet pubkey on the agent. | — |
submit_proposal |
Run check_compliance over six encrypted inputs. |
Encrypt::execute_graph |
request_compliance_decryption |
Ask the Encrypt network to decrypt the EBool result; pin digest in state. | Encrypt::request_decryption |
reveal_and_authorize |
Verify digest, read bool, and co-sign on success. | Ika::approve_message |
State is two PDA-anchored accounts: Agent (principal authority, dWallet binding, counters) and Proposal (status, ciphertext pubkey, digest, commitment, timestamps).
CPI authority PDAs:
[b"__encrypt_cpi_authority"]under warden-core's program ID[b"__ika_cpi_authority"]under warden-core's program ID
| Resource | Value |
|---|---|
warden-core program |
Htrj84e45UCgFTfn7GfDoHZHRRiPC8Lr74PD3mKdtBFq |
| Encrypt program | 4ebfzWdKnrnGseuQpezXdG8yCdHqwQ1SSBHD3bWArND8 |
| Ika dWallet program | 87W54kGYFQ1rgWqMeu4XTPHWXWmXSQCcjm8vCTfiq1oY |
| Encrypt gRPC endpoint | pre-alpha-dev-1.encrypt.ika-network.net:443 |
| Solana RPC | https://api.devnet.solana.com |
| BPF binary size | 248 KB |
programs/
├── warden-core/ ← the deployed program
│ ├── src/lib.rs ← 5 instructions: create_agent, bind_dwallet,
│ │ submit_proposal, request_compliance_decryption,
│ │ reveal_and_authorize
│ ├── tests/smoke.rs ← LiteSVM smoke tests
│ ├── tests/cpi_integration.rs← CPI tests against the real Encrypt + Ika BPF binaries
│ └── target/deploy/warden_core.so
├── warden-policy/ ← v1 design archive (pre-SDK stubs)
├── warden-fhe-state/ ← v1 design archive
└── warden-settlement/ ← v1 design archive
external-sdks/
├── encrypt/ ← github.com/dwallet-labs/encrypt-pre-alpha (vendored as path dep)
└── ika/ ← github.com/dwallet-labs/ika-pre-alpha (vendored as path dep)
scripts/devnet/
├── keygen.ts ← mint / load a devnet payer
├── deploy.sh ← legacy shell deploy
├── deploy.ts ← BPFLoaderUpgradeable deploy with bincode-correct Write
├── upgrade.ts ← in-place upgrade (saves rent vs re-deploy)
├── close-buffer.ts ← reclaim SOL from orphaned BPFLoader buffers
└── e2e-demo.ts ← full Encrypt + Ika flow against devnet
app/ ← Next.js dashboard reading warden-core PDAs live
packages/ ← shared TS types and helpers
The warden-policy, warden-fhe-state, and warden-settlement directories are kept as a reference for the original architectural split, written against stub crates before the real Encrypt and Ika SDKs were available. The active program is programs/warden-core.
Prerequisites: Solana CLI ≥ 1.18, Rust toolchain with cargo-build-sbf, Node ≥ 18.
# 1. Install JS deps (tsx is used to run the devnet scripts on plain Node)
npm install
# 2. Build the BPF binary against the real encrypt-anchor / ika-dwallet-anchor
cargo-build-sbf --manifest-path programs/warden-core/Cargo.toml
# 3. Run the local proof of life: 7/7 LiteSVM tests against the real Encrypt + Ika BPF
cargo test --manifest-path programs/warden-core/Cargo.toml
# 4. Mint or load a devnet payer (~/.config/solana/id.json by default).
# Devnet airdrop is captcha-gated from cloud egress; if it stalls, fund the
# wallet manually at https://faucet.solana.com.
npm run devnet:keygen
# 5. Deploy or upgrade warden-core
npm run devnet:deploy # first time
# npm run devnet:upgrade # subsequent times — much cheaper
# 6. Run the full end-to-end demo against real Encrypt + real Ika on devnet
export WARDEN_PROGRAM_ID=$(cat scripts/devnet/.warden-program-id)
npm run devnet:e2eThe e2e-demo.ts script walks all six steps end-to-end and prints a per-step trace ([1/7] Setup … → [7/7] reveal_and_authorize …).
cargo test --manifest-path programs/warden-core/Cargo.toml — 7/7 passing:
- 3 smoke tests on
warden_core.soalone —create_agent,bind_dwallet, and rejection ofbind_dwalletfrom a non-authority. - 3 CPI integration tests that load
encrypt_program.soandika_dwallet_program.sofromexternal-sdks/*/bin/alongside our program in LiteSVM. They confirm:- Both Encrypt and Ika binaries deploy successfully.
Encrypt::initializeruns against the real binary.submit_proposalreaches the Encrypt program with valid CPI account ordering and discriminator (rejected at ciphertext validation, as expected without a populated ciphertext registry).
- 1
declare_id!round-trip test.
Wired and asserted error codes: DwalletAlreadyBound, WrongDwallet, ProposalNotPending, ProposalNotDecrypting, DecryptionVerificationFailed.
PR #1 — Warden — Ika dWallet × Encrypt FHE compliance layer lands the Ika + Encrypt integration in 20 atomic, reviewable commits. Notable items:
- BPFLoader bincode fix.
deploy.tsnow usesu64length prefixes forVec<u8>per bincode spec — fixesinvalid instruction dataonWrite. - Programdata rent optimisation.
max_data_lenis tightened to actual program size, saving ~1 SOL of programdata rent on every deploy. - In-place
upgrade.tsviaUpgradeableLoaderInstruction::Upgrade(tag 3) — buffer rent spills back to payer. close-buffer.tsreclaims SOL from orphaned BPFLoader buffers via tag 5.- Real Encrypt and Ika program IDs.
e2e-demo.tsnow points at the actual deployed devnet binaries — previous defaults were doc placeholders. - Idempotent deposit creation. Encrypt deposit PDA is reused across runs.
- Correct chain enum + FHE type id.
chain: SOLANA = 0(was 1, which isINVALID_ARGUMENT);FHE_UINT64 = 4(was 5, which isUint128and triggersConstraintRaw). - Output-ciphertext signer-escalation workaround.
e2e-demo.tspre-creates the EBool output via gRPCCreateInputsoEncrypt::execute_graphupdates an existing account rather than failing to callsystem::create_accountfrom a stripped-signer "remaining" account. - Upstream SDK patch.
external-sdks/encrypt/.../anchor/src/lib.rsnow forwardsis_signer = truefor thedecryption_requestaccount inEncryptContext::request_decryption— fixes the same signer-escalation class of bug for the decryption flow.
Full commit list and rationale: see the PR.
- Multi-policy agents. Today the guardrail set is a single
EUint64triple. Next: arbitrary user-defined#[encrypt_fn]graphs with structured input schemas registered atbind_dwallettime. - Off-chain executor. A reference TypeScript executor that watches
ProposalSubmittedevents, drives the Encrypt network for decryption, and submitsreveal_and_authorizeautomatically. - Cross-chain settlement adapters. EVM and Bitcoin executors that take an Ika-cosigned
result_commitmentand broadcast a corresponding tx on the target chain, with proof of inclusion fed back to Solana. - SDK PRs upstream. Land the
is_signerfixes inencrypt-anchorand propose a typed signer-list API forexecute_graphso callers can opt new ciphertexts in cleanly.
Apache-2.0 for everything in this repo authored by us. The Encrypt and Ika SDKs vendored under external-sdks/ retain their original BSD-3-Clause-Clear license.