Treasure Hunt is a two-player strategy game built on Aztec. Each player hides treasures on an 8x8 board and races to find the opponent's first.
What makes it interesting is not the board itself, but the information model:
- some actions are public
- some actions are private
- some private actions are intentionally indistinguishable from each other
That lets the game create real uncertainty during play. Your opponent can know that something changed without knowing what changed.
- Original demo video: YouTube
- Live app: aztec-treasure-hunt.vercel.app
Most onchain games that want privacy end up in one of two places:
- Everything is public.
- They use commit-reveal.
Commit-reveal can hide a fixed initial state, but it does not handle dynamic private strategy well. As soon as players need to secretly move pieces, add hidden elements mid-match, or consume private inventory over time, the model starts leaking information or becomes impractical.
Aztec gives the game something much stronger:
- private treasure positions
- private trap positions
- private power inventory
- private state transitions during the match
That is what enables mechanics like:
- Golden Shovel: move one of your treasures without revealing where it went
- Trap: place a hidden trap during the game
- Private inventory: consume powers without exposing your remaining count to the opponent
Each player:
- hides 3 treasures
- receives a limited set of powers
- wins by finding 2 of the opponent's 3 treasures first
| Power | Quantity | Effect |
|---|---|---|
| Radar | 3 | Scans a 3x3 area and returns how many treasures are inside |
| Compass | 2 | Returns the distance to the nearest treasure |
| Golden Shovel | 1 | Moves one of your treasures |
| Trap | 2 | If the opponent digs there, they lose their next turn |
- Public: turn order, dig results, radar usage, compass usage
- Private: treasure positions, trap positions, shovel moves, trap placement, private power state
The interesting part is that from the outside, some private actions can look identical. The opponent may know you used a hidden action, but not whether you moved a treasure or planted a trap.
The project now supports:
- local network development
- testnet deployment
- Azguard
- embedded wallets
- Aztec Accelerator with embedded wallets
- Sponsored FPC for app-sponsored embedded wallet fees
Current next step:
- mainnet deployment
| Network | Embedded Wallet | Azguard | Fee model |
|---|---|---|---|
| Local | Yes | Yes | Sponsored FPC |
| Devnet | Yes | Yes | Sponsored FPC |
| Testnet | Yes | Yes | Project-sponsored FPC |
| Mainnet | Planned | Planned | Project-sponsored FPC |
.
├── contracts/ # Noir contracts, deployment scripts, tests
└── client/ # React/Vite frontend
- Node.js 22+
- npm 10+ recommended
- Aztec
4.2.0-aztecnr-rc.2
Install Aztec:
bash -i <(curl -s https://install.aztec.network)
PATH="$HOME/.aztec/bin:$PATH" aztec-up install 4.2.0-aztecnr-rc.2cd contracts
npm install
cd ../client
npm installcd contracts
npm run aztec:startWait until the node is listening on http://localhost:8080.
To stop it later:
cd contracts
npm run aztec:stopIn another terminal:
cd contracts
npm run compile
npm run codegen
npm run deployLocal deploy does all of this:
- deploys the game account
- deploys
TreasureHunt - copies artifacts into
client/src/artifacts - writes
client/.env.local
In a third terminal:
cd client
npm run devOpen http://localhost:3001.
From contracts/:
npm test
npm run test:js
npm run test:nrFrom client/:
npm run typecheck
npm run buildIf you changed contract code:
cd contracts
npm run compile
npm run codegenThen deploy from contracts/:
# Local
npm run deploy
# Devnet
npm run deploy::devnet
# Testnet / Mainnet
L1_PRIVATE_KEY=0x<your-ethereum-private-key> npm run deploy::testnet
L1_PRIVATE_KEY=0x<your-ethereum-private-key> npm run deploy::mainnet| Network | Command | Fee model | Generated frontend env |
|---|---|---|---|
| Local | npm run deploy |
Sponsored FPC | client/.env.local |
| Devnet | npm run deploy::devnet |
Sponsored FPC | client/.env.devnet |
| Testnet | L1_PRIVATE_KEY=0x... npm run deploy::testnet |
Project-sponsored FPC | client/.env.testnet |
| Mainnet | L1_PRIVATE_KEY=0x... npm run deploy::mainnet |
Project-sponsored FPC | client/.env.mainnet |
For testnet and mainnet, the deploy script:
- creates and deploys the admin account
- deploys a
SponsoredFPC - funds that sponsor from L1
- writes the frontend env with:
VITE_CONTRACT_ADDRESSVITE_DEPLOYER_ADDRESSVITE_ADMIN_ADDRESSVITE_DEPLOYMENT_SALTVITE_AZTEC_NODE_URLVITE_SPONSORED_FPC_ADDRESSVITE_SPONSORED_FPC_SALT
That means embedded wallets in the frontend can transact without asking end users to bridge their own Fee Juice.
The deploy script writes one env file per network. Vite gives .env.local priority during local development, so the simplest way to switch networks is:
cd client
cp .env.testnet .env.local
npm run devYou can do the same for .env.devnet or .env.mainnet.
If you are switching away from a previous network, replace .env.local or remove it first.
Testnet and mainnet use an app-sponsored fee payer. When its Fee Juice balance runs low, refill it from contracts/:
L1_PRIVATE_KEY=0x<your-ethereum-private-key> npm run refill-sponsored-fpc::testnetMainnet:
L1_PRIVATE_KEY=0x<your-ethereum-private-key> npm run refill-sponsored-fpc::mainnetOptional env vars:
SPONSORED_FPC_REFILL_AMOUNT=1000000000000000000000
SPONSORED_FPC_BOOTSTRAP_AMOUNT=50000000000000000000- The app is currently designed for desktop browsers.
- Embedded wallets and Azguard are both supported.
- Audio, settings, accelerator controls, and wallet state live entirely in the client.
- The frontend expects generated artifacts under
client/src/artifacts.
Redeploy locally and clear the PXE store:
cd contracts
npm run clear-store
npm run deploymacOS ships with Bash 3.2, but the Aztec CLI expects a newer Bash.
brew install bashIf needed:
sudo sh -c 'echo /opt/homebrew/bin/bash >> /etc/shells'
chsh -s /opt/homebrew/bin/bashThe repo wrappers also help avoid direct CLI issues:
cd contracts
npm run aztec:start
npm run compile
npm run codegen
npm run deploy![]() Caravana Studio |
![]() @dub_zn |
![]() @dpinoness |



