Deployment
A full deployment is live on Stacks testnet plus a static frontend on Vercel.
Live coordinates
| Network | testnet |
| Deployer / keeper-owner | ST1QN0P1MB429DHV28KT68F80621FHE4NE880CBZK |
| Web app | bachelier-frontend.vercel.app |
| Contracts | 8 published — see Contracts |
| Round 1 | +10% OTM, IV 0.55, ~1 week tenor |
The deployer wallet doubles as keeper and owner. Its mnemonic lives only in
contracts/settings/.testnet-deployer.json (gitignored, testnet-only). The
public record — addresses, txids, round 1 — is committed to
contracts/deployments/testnet-deployment.json.
Part A — contracts
One idempotent script generates a wallet, funds it from the Hiro faucet,
publishes all 8 contracts in dependency order, wires them, and verifies the
on-chain Black–Scholes reference vector equals 42848661381:
cd contracts
pnpm tsx scripts/deploy-testnet.ts
It is safe to re-run: it skips already-deployed contracts and already-applied wiring. The wiring it applies:
bcshare-token.set-vault— authorize the vault to mint/burn sharesoracle-adapter.set-max-age(u691200)— relax staleness to 8 days (demo)pyth-mock.set-price-now(<live BTC>)— seed a pricevault.start-round(u1000, u55000000)— open round 1 (+10% OTM, IV 0.55)
:::note IPv4 gotcha
On some networks Node/undici picks an unreachable IPv6 route and fetch hangs
with UND_ERR_CONNECT_TIMEOUT. Run the script with
NODE_OPTIONS="--dns-result-order=ipv4first". It's idempotent, so just re-run.
:::
Baking the deployer into the build
Vite strips process.env from the browser bundle, so the live contract address
can't flow in through the shared env() helper at build time. The fix is
configWithDeployer() in packages/shared plus a Vite-native override: set
VITE_BACHELIER_DEPLOYER=<deployer> and the production bundle compiles in the
real addresses. (The shared config also has the placeholder principal replaced
with the live deployer, committed to the repo.)
Part B — frontend (Vercel)
The frontend is a static Vite build. Because it consumes
@bachelier/shared as TypeScript source through the pnpm workspace, the build
needs the whole monorepo context — so the Vercel project's Root Directory
is web while install runs at the workspace root.
| Setting | Value |
|---|---|
| Root Directory | web |
| Framework | Vite |
| Install | pnpm install --no-frozen-lockfile |
| Build | pnpm build |
| Output | dist |
| Env | VITE_STACKS_NETWORK=testnet, VITE_BACHELIER_DEPLOYER=ST1QN0…CBZK |
VITE_API_URL is intentionally left unset → the app runs chain-direct (see
Web app). No backend is required for the demo.
:::tip Public access
New Vercel team projects get "Vercel Authentication" deployment protection by
default, which returns 401 to the public. Disable it (project settings →
Deployment Protection, or PATCH /v9/projects/{id} with ssoProtection: null)
to make the demo URL publicly reachable.
:::
Part C — services (optional)
A basic demo doesn't need the backend (round 1 is pre-opened and the oracle is fresh for 8 days). For ongoing operation — keeping rounds live, richer history, trailing APY — run the keeper + indexer + API + Postgres. See Services.
Verification checklist
bs-call-price(reference) == 42848661381— asserted by the deploy script.- Oracle returns a fresh price; round 1 is active with a future expiry.
- The web bundle contains the deployer principal (the address compiles into
assets/index-*.js). - The 60 test users funded → minted → deposited, all confirmed.