Traditional auctions on a public chain expose every bid. ShadowBid keeps individual bid amounts encrypted until the seller runs reveal: the Arcium MXE compares bids inside MPC and only the winning pubkey and winning bid amount become public. Losing bids stay sealed.
This repo is an Arcium + Anchor reference implementation plus a Next.js app for browsing listings, bidding, and (for the listing authority) setting deadlines and revealing the winner.
| Mechanism | Status |
|---|---|
| Uniform / first-price sealed bid | Implemented: encrypted state tracks highest bid + highest bidder; reveal publishes those values. |
| Vickrey (second-price) | Not implemented — that needs a second-highest ciphertext in the MPC state and different reveal logic. |
| On-chain escrow / automatic refunds | Not implemented — amounts are proofs-of-bid semantics; settlement is assumed off-chain unless you extend the program. |
If a hackathon brief mentions Vickrey, treat ShadowBid as a minimal sealed-bid building block where Arcium handles private comparison + selective decryption of the aggregate state.
- Chain sees ciphertexts — Validators index
place_bidtransactions but only see encrypted payloads and MXE ciphertext rows, not plaintext amounts suitable for sandwiching or order-flow games. - Comparison inside the MXE — The
place_bidArcis instruction decrypts bids inside the MPC context, updatesAuctionState, and writes fresh MXE ciphertexts back to Solana (encrypted_state). - Selective revelation —
reveal_winnerruns only after optional deadline rules are satisfied (set_auction_deadlinelocks an immutablebidding_ends_at; reveal requires that time to pass if set). Output is bounded: winner + winning bid lamports — not every historical bid.
The in-app About page (when you run or deploy the web app) spells out the bidder-facing flow step by step.
create_auction— Seeds the auction account, stores public listing text (title,description, optionalimage_uri), and queuesinit_auction_stateso the MXE initializes encryptedAuctionState.place_bid— Client encrypts bid amount and bidder pubkey fragments with ephemeral X25519 + the MXE key; submits ciphertexts + nonces; Arciumplace_bidcircuit merges them intoEnc<Mxe, AuctionState>.set_auction_deadline(optional, authority once) — Setsbidding_ends_atsoplace_bidrejects afterward andreveal_winneris only callable after the window closes.reveal_winner— Authority queuesreveal_winner; callback writesrevealed,winner,winning_bidcleartext on-chain.
Arcis compilation target lives in encrypted-ixs/src/blind_auction.rs:
pub struct AuctionState {
pub highest_bid: u64,
pub highest_bidder: SerializedSolanaPublicKey,
}Solana persists three 32-byte limbs ([[u8; 32]; 3]) — ciphertext material for u64 + SerializedSolanaPublicKey (lo/hi u128) under the MXE — plus a state_nonce for rotation. bid_count is public and incremented on-chain after each finalized place_bid computation.
Winner extraction from the MPC output is flattened in reveal_winner_callback (programs/shadow_bid/src/lib.rs) into winning_bid plus winner Pubkey.
Learn more about Arcis types at Arcis Types.
programs/shadow_bid— Anchor program (create_auction,place_bid,set_auction_deadline,reveal_winner, comp-def inits).encrypted-ixs— Arcis circuits (init_auction_state,place_bid,reveal_winner).web— Next.js UI (/auctions,/auctions/[pda],/about,/dashboard).scripts/init-mxe-circuits.ts— One-shot Devnet helper: creates MXE computation definitions (if missing) and uploadsbuild/*.arcis(idempotent; see env vars in the file).scripts/dev.sh— Optional local stack:arcium localnet+ Next (web→yarn dev:full). Not a substitute for public Devnet hosting.tests/shadow_bid.ts— Integration test for create → bid → finalize path.
From the repo root:
yarn install # toolchain deps + scripts
yarn check:config # optional: Arcium.toml vs bundled IDL vs CI vs web/.env.local
arcium build # Rust program + Arcis artifacts
arcium test # Anchor-style integration suiteTo refresh bundled IDL/types/circuits for the web app:
arcium build && cd web && yarn copy:artifactsRequires Node ≥ 20.18 (see repo engines / web/.nvmrc). Prefer a current Node 22 runtime when running yarn init:mxe-circuits so toolchain warnings stay minimal.
Pushes to main run GitHub Actions over web (yarn build).
Rough order:
arcium build— Generatestarget/idl,target/types,build/*.arcis.arcium deploy(Devnet target in your.env/CLI config) — Publishes the Anchor program + Arcium MXE wiring. Costs SOL (program data + rents). Only repeat when upgrading the binary.yarn init:mxe-circuits— Separate step: uploads the encrypted circuit blobs referenced by computation definitions (place_bidalone is ~2 MB raw data and can consume multiple SOL in rent across many txs on first provisioning). Safe to rerun: comp‑definit*txs skip once accounts exist.
# From repo root (after yarn install + arcium build)
export SOLANA_RPC_URL='https://devnet.helius-rpc.com/?api-key=YOUR_API_KEY_HERE'
export SOLANA_KEYPAIR_PATH="$HOME/.config/solana/id.json" # default; MXE authority must sign
# @arcium-hq/client defaulted to 500 parallel upload txs → public RPC Helius bursts 429.
# This repo lowers the default batch size to 4; reduce further if needed:
export MXE_UPLOAD_PARALLEL=2 # or 1 under heavy limits
yarn init:mxe-circuitsUse any reliable Devnet RPC (premium providers strongly recommended during uploads). Rotate API keys leaked in logs or chats.
Canonical demo / reference identifiers for this checkout (change if you redeploy elsewhere):
| Setting | Reference value |
|---|---|
NEXT_PUBLIC_SHADOW_BID_PROGRAM_ID |
J1vKHNDu4gLpMrf4Zwzca74rjJo4Lx5MvgKUU7B7UWN7 |
NEXT_PUBLIC_ARCIUM_CLUSTER_OFFSET (Devnet) |
456 (see Arcium.toml [clusters.devnet] — use 0 only for local arcium localnet). |
Mirror these in web/.env.local for local previews and Vercel → Environment Variables.
-
Complete Devnet checklist above (IDL +
build/*.arcisuploaded viayarn init:mxe-circuits). Keepweb/lib/idl/shadow_bid.jsonand bundled types in sync (arcium buildthenwebyarn copy:artifacts). -
Set environment variables (Vercel or copy from
web/.env.local.exampleintoweb/.env.local):Variable Purpose NEXT_PUBLIC_SITE_URLPublic origin ( https://…vercel.app) for absolute links.NEXT_PUBLIC_SOLANA_RPC_URLShared Devnet RPC for every visitor (same RPC ⇒ consistent cluster state). NEXT_PUBLIC_ARCIUM_CLUSTER_OFFSETMXE cluster offset — 456for public Devnet in this repo.NEXT_PUBLIC_SHADOW_BID_PROGRAM_IDMatches declare_id!/ IDL. -
cd web && yarn install && yarn build— GitHub Actions runs this on PRs/pushes touchingweb.
- No minimum bid in-circuit — Adding one requires threading a plaintext or encrypted floor into the Arcis
place_bidlogic and proving comparison. - Multiple bids per wallet — Allowed;
bid_countcounts transactions, not unique bidders. Under first-price semantics, repeat bids from the same key only replace the encrypted high-water mark if they exceed it. - No on-chain payout — The auction proves who won and the revealed amount (lamports semantics in the ciphertext); escrow and refunds are roadmap items (also noted in-app).
See package.json (repository root).