This guide walks you through building or deploying the Predinex Soroban contract locally, wiring the web frontend to it, and running the full test suite — all from a clean checkout.
| Tool | Version | Install |
|---|---|---|
| Rust + Cargo | stable (1.74+) | https://rustup.rs |
wasm32-unknown-unknown target |
— | rustup target add wasm32-unknown-unknown |
| Stellar CLI | 21+ | https://developers.stellar.org/docs/build/smart-contracts/getting-started/setup |
| Node.js | 18+ | https://nodejs.org |
| npm | 8+ | bundled with Node.js |
| Supported wallet flow | current | See wallet and network support for the active wallet entry point and unsupported combinations. |
Verify everything is in order with the bootstrap script:
./scripts/bootstrap.shcd contracts/predinex
# Run the full test suite (no network required)
cargo test
# Build the release WASM
stellar contract buildThe compiled artifact lands at:
contracts/predinex/target/wasm32-unknown-unknown/release/predinex.wasm
Tip:
stellar contract buildwrapscargo build --release --target wasm32-unknown-unknownand applies the release profile fromCargo.toml(opt-level = "z",codegen-units = 1).
Skip this section if you only need to run the web app against the existing testnet contract. Jump to section 3 and use the contract address already in
.env.example.
# Generate a new keypair and save it as "deployer"
stellar keys generate deployer --network testnet
# Fund it from Friendbot
stellar keys fund deployer --network testnetstellar contract optimize \
--wasm target/wasm32-unknown-unknown/release/predinex.wasmOutput: predinex.optimized.wasm
stellar contract deploy \
--wasm predinex.optimized.wasm \
--network testnet \
--source deployerThe CLI prints the new contract ID — copy it. You will need it in the next step.
Contract deployed successfully with ID: C<your-contract-id>
initialize requires a token address (the Stellar Asset Contract for XLM on testnet) and a treasury recipient address.
# XLM native asset SAC on Stellar testnet
XLM_TOKEN=CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC
# Your deployer public key acts as treasury recipient for local development
TREASURY=$(stellar keys address deployer)
stellar contract invoke \
--id <your-contract-id> \
--network testnet \
--source deployer \
-- initialize \
--token $XLM_TOKEN \
--treasury_recipient $TREASURYstellar contract invoke \
--id <your-contract-id> \
--network testnet \
-- get_pool_countExpected output: 0 (no pools yet).
cd web
# Copy the example env file
cp .env.example .env.localOpen .env.local and set the minimum required values:
# Required
NEXT_PUBLIC_NETWORK=testnet
# Set to your deployed contract ID from step 2c,
# or leave as-is to use the shared testnet deployment
NEXT_PUBLIC_CONTRACT_ADDRESS=<your-contract-id>
NEXT_PUBLIC_CONTRACT_NAME=predinex
# Optional — only needed for WalletConnect UI features
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=YOUR_PROJECT_IDHow the app resolves the contract:
web/app/lib/runtime-config.tsreadsNEXT_PUBLIC_NETWORKto select the network and derives the API base URL fromweb/app/lib/network-config.ts. Contract reads flow throughweb/app/lib/adapters/predinex-read-api.ts.
| Variable | Required | Default | Description |
|---|---|---|---|
NEXT_PUBLIC_NETWORK |
Yes | — | testnet or mainnet |
NEXT_PUBLIC_CONTRACT_ADDRESS |
Yes | — | Deployed contract ID (C…) |
NEXT_PUBLIC_CONTRACT_NAME |
No | predinex-pool |
Contract name suffix |
NEXT_PUBLIC_APP_URL |
No | https://predinex.app |
Used for WalletConnect metadata |
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID |
No | — | WalletConnect Cloud project ID |
NEXT_PUBLIC_STACKS_API_URL |
No | Hiro testnet URL | Override the legacy helper API endpoint |
DEBUG |
No | false |
Enable verbose client-side logging |
cd web
npm install # skip if you ran bootstrap.sh
npm run devOpen http://localhost:3000.
Smoke-check the contract wiring:
- Connect using the supported wallet flow documented in WALLET_NETWORK_SUPPORT.md and set the app to the matching supported network.
- Navigate to Markets — the page fetches live pool data via
predinexReadApi.getMarkets(). - Open browser DevTools → Network. Confirm a request to the configured helper API endpoint returns 200.
- If
DEBUG=trueis set in.env.local, check the console for the resolved runtime config log.
Verify the full round-trip by creating a pool from the CLI and confirming it appears in the UI:
stellar contract invoke \
--id <your-contract-id> \
--network testnet \
--source deployer \
-- create_pool \
--creator $(stellar keys address deployer) \
--title "Will BTC hit $100k?" \
--description "Closes at end of month" \
--outcome_a "Yes" \
--outcome_b "No" \
--duration 86400Refresh the Markets page — the new pool should appear within one polling cycle (~30 s).
cd contracts/predinex
cargo testAll tests run against soroban-sdk's in-process environment — no network required.
cd web
npm run testcd web
npm run lint
npm run build# Contracts
cd contracts/predinex
cargo fmt --check
cargo clippy -- -D warnings
cargo test
# Web
cd ../../web
npm run lint
npm run test
npm run build| Symptom | Likely cause | Fix |
|---|---|---|
Missing required config: NEXT_PUBLIC_NETWORK |
.env.local not created or missing the variable |
cp .env.example .env.local and set NEXT_PUBLIC_NETWORK=testnet |
| Markets page shows no pools | Wrong contract ID in env | Double-check NEXT_PUBLIC_CONTRACT_ADDRESS matches the deployed contract |
Already initialized panic on deploy |
Contract was already initialized | This is expected on re-deploy; only call initialize once per contract ID |
wasm32-unknown-unknown target missing |
Rust target not installed | rustup target add wasm32-unknown-unknown |
| Wallet shows "wrong network" | Wallet connected to the wrong supported network | Switch the app to the matching supported network and reconnect |
stellar: command not found |
Stellar CLI not installed | Follow the CLI setup guide |
⚠️ Mainnet deployments are automated via the CI/CD pipeline. Only run this manually if the pipeline is unavailable.
The pipeline in .github/workflows/mainnet-deploy.yml triggers on any tag matching v<major>.<minor>.<patch>:
# Create and push a release tag
git tag -a v1.0.0 -m "Release v1.0.0"
git push origin v1.0.0Pipeline stages:
- Safety checks —
cargo fmt --check,cargo clippy,cargo test, WASM build, deployer balance check (≥ 20 XLM required) - Deploy (requires manual approval in GitHub → Environments →
mainnet) — deploys WASM, initializes contract, saves deployment record todeployments/ - Smoke tests —
get_pool_count,create_pool,get_pool - Notify — posts status to Slack / Discord
Required GitHub secrets:
| Secret | Description |
|---|---|
MAINNET_DEPLOYER_SECRET |
Stellar secret key of the deploy account |
MAINNET_XLM_SAC |
XLM Stellar Asset Contract address on mainnet |
MAINNET_TREASURY_ADDRESS |
Treasury recipient address |
VALIDATION_CLOUD_KEY |
Validation Cloud RPC API key |
SLACK_WEBHOOK_URL |
(optional) Slack incoming webhook |
DISCORD_WEBHOOK_URL |
(optional) Discord webhook |
After a successful deploy, the contract ID is recorded in deployments/v<version>.json and committed to main.
If a bad deployment needs to be reverted, redeploy an older verified WASM:
export MAINNET_DEPLOYER_SECRET=S...
export MAINNET_XLM_SAC=C...
export MAINNET_TREASURY_ADDRESS=G...
./scripts/rollback.sh v1.0.1 # version to roll back TOThe script:
- Loads
deployments/v1.0.1.jsonand verifies the WASM SHA-256 hash - Asks for typed confirmation before touching mainnet
- Redeploys the verified WASM as a new contract ID
- Initializes the new contract
- Prints the steps to update
NEXT_PUBLIC_CONTRACT_ADDRESS
Soroban contracts are immutable — rollback deploys a new contract ID. Update your env vars and redeploy the web app after running the rollback.