FIELD MANUAL

protocol spec
+ integration guide.

Everything you need to understand Bullet and talk to the deployed program directly. No gatekeeper SDK — just the IDL in the repo and an anchor client.

PART 1 · PROTOCOL SPEC

DEPLOYMENT

PROGRAMCbVRMsNRwX7rBrCRjaEbqaVCQiQcNqc2Af6edtGFaD6g
CLUSTERsolana-devnet
RPChttps://api.devnet.solana.com
UPGRADE AUTH5dVpzr3cU3nCFypi5RGBshq429dDF1AvSXrFKqZxzYnk
AUDITnone — 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_intent after deadline.

FLOW

  1. Solver registers: bond moves to PDA, x25519 pubkey published.
  2. User encodes the intent body (136 bytes) and seals it via NaCl box. Wire: nonce(24) || ciphertext.
  3. User calls submit_intent with cleartext (source_mint, amount_in, deadline_slot), sealed blob, ephemeral pk, SHA-256 commit.
  4. Solver subscribes to its address, decrypts locally, verifies revealed body matches on-chain cleartext.
  5. Solver calls fulfill_intent. Program re-hashes, compares to commit, releases escrow.
  6. 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 install

USER 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

PROGRAMCbVRMsNRwX7rBrCRjaEbqaVCQiQcNqc2Af6edtGFaD6g
CLUSTERsolana-devnet
SEEDS · CONFIG[“config”]
SEEDS · SOLVER[“solver”, authority]
SEEDS · INTENT[“intent”, user, nonce(16)]
SEEDS · VAULT[“vault”, intent]
SEEDS · VAULT AUTH[“vault_auth”, intent]