PART 1 · PROTOCOL SPEC
DEPLOYMENT
| PROGRAM | CbVRMsNRwX7rBrCRjaEbqaVCQiQcNqc2Af6edtGFaD6g ↗ |
| CLUSTER | solana-devnet |
| RPC | https://api.devnet.solana.com |
| UPGRADE AUTH | 5dVpzr3cU3nCFypi5RGBshq429dDF1AvSXrFKqZxzYnk |
| AUDIT | none — devnet only — do not ape |
ACTORS
- USER — owns source tokens, wants dest tokens, sets minimum out + recipient.
- SOLVER — registered with x25519 pubkey + bond, decrypts targeted intents and fulfills.
- KEEPER — anyone can call
reclaim_intentafter deadline.
FLOW
- Solver registers: bond moves to PDA, x25519 pubkey published.
- User encodes the intent body (136 bytes) and seals it via NaCl box. Wire:
nonce(24) || ciphertext. - User calls
submit_intentwith cleartext (source_mint, amount_in, deadline_slot), sealed blob, ephemeral pk, SHA-256 commit. - Solver subscribes to its address, decrypts locally, verifies revealed body matches on-chain cleartext.
- Solver calls
fulfill_intent. Program re-hashes, compares to commit, releases escrow. - If deadline passes:
reclaim_intent. Vault refund + bond slash to user.
COMMIT HASH
commit_hash = sha256( dest_mint ‖ // 32 bytes min_amount_out ‖ // u64 little-endian recipient ‖ // 32 bytes nonce // 16 bytes )
Bound at submit time. Solver cannot lie about destination, minimum, or recipient without producing a colliding hash.
GUARANTEES
- OPACITY — mempool sees only source mint, amount-in, deadline, target solver, ciphertext.
- COMMIT-HONESTY — solver cannot rewrite intent terms post-submission.
- ATOMIC SETTLEMENT — both legs in one transaction or neither.
- LIVENESS — if no fulfillment, user reclaims and slashes after deadline.
NON-GOALS
- not a launchpad. not a dex. not a wallet.
- not a router — solvers source liquidity wherever they want.
- not a privacy mixer — only intent contents are hidden, not balances.
- v0 is single-solver-per-intent; multi-solver auctions are a v1 layer.
PART 2 · INTEGRATION
INSTALL
# clone the monorepo + the committed IDL lives at idl/bullet.json
git clone https://github.com/bullet-army/bullet.git
cd bullet
npm installUSER SIDE
import { AnchorProvider, Program } from '@coral-xyz/anchor';
import idl from './idl/bullet.json';
const program = new Program(idl, provider);
await program.methods
.submitIntent(nonce, amountIn, deadlineSlot, commitHash, encryptedBlob, ephemeralPk)
.accounts({
config, solver, intent,
sourceMint, userSource,
vaultAuthority, vault,
user: user.publicKey,
tokenProgram: TOKEN_PROGRAM_ID,
systemProgram: SystemProgram.programId,
rent: SYSVAR_RENT_PUBKEY,
})
.signers([user])
.rpc();The destination, minimum out, and recipient are sealed. They never appear in plaintext on-chain.
SOLVER SIDE
// subscribe + decrypt + fulfill
const filter = { memcmp: { offset: /* solver pubkey offset */, bytes } };
await program.methods
.fulfillIntent(nonce, destMint, minAmountOut, recipient, actualAmountOut)
.accounts({
config, solver, intent, destMint,
vaultAuthority, vault,
solverSource, solverDest, recipientDest,
solverAuthority: solver.publicKey,
tokenProgram: TOKEN_PROGRAM_ID,
})
.signers([solver])
.rpc();RECLAIM
await program.methods
.reclaimIntent()
.accounts({
config, solver, intent,
vaultAuthority, vault,
userSource,
user: user.publicKey,
tokenProgram: TOKEN_PROGRAM_ID,
})
.signers([user])
.rpc();Callable after deadline_slot. Refunds vault, slashes solver bond to user, marks solver inactive.
PROGRAM ADDRESSES
| PROGRAM | CbVRMsNRwX7rBrCRjaEbqaVCQiQcNqc2Af6edtGFaD6g |
| CLUSTER | solana-devnet |
| SEEDS · CONFIG | [“config”] |
| SEEDS · SOLVER | [“solver”, authority] |
| SEEDS · INTENT | [“intent”, user, nonce(16)] |
| SEEDS · VAULT | [“vault”, intent] |
| SEEDS · VAULT AUTH | [“vault_auth”, intent] |