WAVS Prediction Market Demo
This project implements a prediction market that is resolved by an AVS oracle. There is a frontend to interact with the demo.
Core (Docker, Compose, Make, JQ, Node v21+, Foundry)
- Linux:
sudo apt update && sudo apt install build-essential
If prompted, remove containerd with sudo apt remove containerd.io
.
- MacOS:
brew install --cask docker
- Linux:
sudo apt -y install docker.io
- Windows WSL: docker desktop wsl &
sudo chmod 666 /var/run/docker.sock
- Docker Documentation
- MacOS: Already installed with Docker installer
sudo apt remove docker-compose-plugin
may be required if you get adpkg
error - Linux + Windows WSL:
sudo apt-get install docker-compose-v2
- Compose Documentation
- MacOS:
brew install make
- Linux + Windows WSL:
sudo apt -y install make
- Make Documentation
- MacOS:
brew install jq
- Linux + Windows WSL:
sudo apt -y install jq
- JQ Documentation
- Required Version: v21+
- Installation via NVM
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
nvm install --lts
curl -L https://foundry.paradigm.xyz | bash && $HOME/.foundry/bin/foundryup
Rust v1.85+
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup toolchain install stable
rustup target add wasm32-wasip2
# Remove old targets if present
rustup target remove wasm32-wasi || true
rustup target remove wasm32-wasip1 || true
# Update and add required target
rustup update stable
rustup target add wasm32-wasip2
Cargo Components
On Ubuntu LTS, if you later encounter errors like:
wkg: /lib/x86_64-linux-gnu/libm.so.6: version `GLIBC_2.38' not found (required by wkg)
wkg: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.39' not found (required by wkg)
If GLIB is out of date. Consider updating your system using:
sudo do-release-upgrade
# Install required cargo components
# https://github.com/bytecodealliance/cargo-component#installation
cargo install cargo-binstall
cargo binstall cargo-component wasm-tools warg-cli wkg --locked --no-confirm --force
# Configure default registry
# Found at: $HOME/.config/wasm-pkg/config.toml
wkg config --default-registry wa.dev
# Allow publishing to a registry
warg key new
Install the required packages to build the Solidity contracts. This project supports both submodules and npm packages.
# Install packages (npm & submodules)
make setup
# Build the contracts
forge build
# Run the solidity tests
forge test
Now build the WASI components into the compiled
output directory.
Warning
If you get: error: no registry configured for namespace "wavs"
run, wkg config --default-registry wa.dev
Warning
If you get: failed to find the 'wasm32-wasip1' target and 'rustup' is not available
brew uninstall rust
& install it from https://rustup.rs
# Remove `WASI_BUILD_DIR` to build all components.
make wasi-build
Note
If you are running on a Mac with an ARM chip, you will need to do the following:
- Set up Rosetta:
softwareupdate --install-rosetta
- Enable Rosetta (Docker Desktop: Settings -> General -> enable "Use Rosetta for x86_64/amd64 emulation on Apple Silicon")
Configure one of the following networking:
- Docker Desktop: Settings -> Resources -> Network -> 'Enable Host Networking'
brew install chipmk/tap/docker-mac-net-connect && sudo brew services start chipmk/tap/docker-mac-net-connect
Start an ethereum node (anvil), the WAVS service, and deploy eigenlayer contracts to the local network.
Set Log Level:
- Open the
.env
file. - Set the
log_level
variable for wavs to debug to ensure detailed logs are captured.
Note
To see details on how to access both traces and metrics, please check out Telemetry Documentation.
# This must remain running in your terminal. Use another terminal to run other commands.
# You can stop the services with `ctrl+c`. Some MacOS terminals require pressing it twice.
cp .env.example .env
# update the .env for either LOCAL or TESTNET
# Starts anvil + IPFS, WARG, Jaeger, and prometheus.
make start-all-local
These sections can be run on the same machine, or separate for testnet environments. Run the following steps on the deployer/aggregator machine.
# local: create deployer & auto fund. testnet: create & iterate check balance
bash ./script/create-deployer.sh
## Deploy Eigenlayer from Deployer
COMMAND=deploy make wavs-middleware
Key Concepts:
- Trigger Contract: Any contract that emits events, then WAVS monitors. When a relevant event occurs, WAVS triggers the execution of your WebAssembly component.
- Submission Contract: This contract is used by the AVS service operator to submit the results generated by the WAVS component on-chain.
WAVS_SERVICE_MANAGER_ADDRESS
is the address of the Eigenlayer service manager contract. It was deployed in the previous step. Then you deploy the trigger and submission contracts which depends on the service manager. The service manager will verify that a submission is valid (from an authorized operator) before saving it to the blockchain. The trigger contract is any arbitrary contract that emits some event that WAVS will watch for. Yes, this can be on another chain (e.g. an L2) and then the submission contract on the L1 (Ethereum for now because that is where Eigenlayer is deployed).
# Forge deploy contracts
make deploy-contracts
Deploy the compiled component with the contract information from the previous steps.
# ** Testnet Setup: https://wa.dev/account/credentials
export COMPONENT_FILENAME=prediction_market_oracle.wasm
export PKG_NAME="prediction-market-oracle"
export PKG_VERSION="0.4.0-rc.1"
source script/upload-to-wasi-registry.sh || true
# Testnet: set values (default: local if not set)
# export TRIGGER_CHAIN=holesky
# export SUBMIT_CHAIN=holesky
# Package not found with wa.dev? -- make sure it is public
export AGGREGATOR_URL=http://127.0.0.1:8001
REGISTRY=${REGISTRY} bash ./script/build_service.sh
# local
export DEPLOYER_PK=$(cat .nodes/deployer)
export WAVS_SERVICE_MANAGER_ADDRESS=$(jq -r .addresses.WavsServiceManager ./.nodes/avs_deploy.json)
# Upload service.json to IPFS
SERVICE_FILE=.docker/service.json source ./script/ipfs-upload.sh
TESTNET You can move the aggregator it to its own machine for testnet deployments, it's easiest to run this on the deployer machine first. If moved, ensure you set the env variables correctly (copy pasted from the previous steps on the other machine).
bash ./script/create-aggregator.sh 1
IPFS_GATEWAY=${IPFS_GATEWAY} bash ./infra/aggregator-1/start.sh
wget -q --header="Content-Type: application/json" --post-data="{\"uri\": \"${IPFS_URI}\"}" ${AGGREGATOR_URL}/register-service -O -
TESTNET The WAVS service should be run in its own machine (creation, start, and opt-in). If moved, make sure you set the env variables correctly (copy pasted from the previous steps on the other machine).
bash ./script/create-operator.sh 1
IPFS_GATEWAY=${IPFS_GATEWAY} bash ./infra/wavs-1/start.sh
# Deploy the service JSON to WAVS so it now watches and submits.
# 'opt in' for WAVS to watch (this is before we register to Eigenlayer)
WAVS_ENDPOINT=http://127.0.0.1:8000 SERVICE_URL=${IPFS_URI} IPFS_GATEWAY=${IPFS_GATEWAY} make deploy-service
Making test mnemonic: cast wallet new-mnemonic --json | jq -r .mnemonic
Each service gets their own key path (hd_path). The first service starts at 1 and increments from there. Get the service ID
source ./script/avs-signing-key.sh
# TESTNET: set WAVS_SERVICE_MANAGER_ADDRESS
COMMAND="register ${OPERATOR_PRIVATE_KEY} ${AVS_SIGNING_ADDRESS} 0.001ether" make wavs-middleware
# Verify registration
COMMAND="list_operator" PAST_BLOCKS=500 make wavs-middleware
Buy YES outcome tokens in the prediction market governed by the oracle AVS, which is going to resolve whether or not the price of Bitcoin is over $1.
make buy-yes
Notice in the logs that you start with 1e18 collateral tokens, and then purchase 1e18 YES shares for 525090975565627651 (~5.25e17) collateral tokens, leaving 474909024434372349 (~4.75e17) collateral tokens remaining.
Run the AVS service to resolve the market.
make resolve-market
# Wait for the component to execute
echo "waiting 3 seconds for the component to execute..."
sleep 3
Redeem the YES outcome tokens for collateral tokens.
make redeem
Notice in the logs that you redeem 1e18 outcome (YES) shares for 1e18 collateral tokens, ending up with 1474909024434372349 (~1.47e18) collateral tokens. This is more than you started with since you earned a profit from the market by betting on the correct outcome.
A frontend application is included for interacting with the prediction market system.
- Connect your Ethereum wallet
- Submit predictions to existing markets by buying YES/NO outcome tokens
- View market history and probabilities with interactive charts
- Display payout distribution for resolved markets
- Display past markets and their results
- Admin/debug interface to trigger the AVS oracle to resolve markets
The frontend must be started after the backend is running and the contracts are deployed, since the environment variables are set by the rune2e.sh script and need to be available to the frontend.
# Install frontend dependencies
cd frontend
npm install
# And start the server
npm run dev
# Frontend will be available at http://localhost:3000
To test the prediction market, follow these steps:
- Go to the admin page and use the faucet to get fee tokens
- Go to the markets page and click the active market
- Buy YES outcome tokens
- Go to the admin page and trigger the oracle to resolve the market as YES
- Back on the market page, redeem your YES outcome tokens for collateral tokens now that the market has been resolved
To spin up a sandboxed instance of Claude Code in a Docker container that only has access to this project's files, run the following command:
npm run claude-code
# or with no restrictions (--dangerously-skip-permissions)
npm run claude-code:unrestricted