Skip to main content

Raffle app

The raffle app is the reference build for a Gora app with both off-chain and on-chain components. The off-chain app picks one winner from registered participants across Base, Solana, and Algorand. The winner is paid on the same chain they registered on. If the winner is on Algorand, the Algorand app treasury pays ALGO. If the winner is on Solana, the Solana treasury pays SOL. If the winner is on Base, the Base contract pays ETH.

What you are building

The reference app lives in examples/apps/raffle-app.

HTML UI

The mobile wallet does not render raffle-specific Swift UI. It loads the app’s HTML from:
The Raffle HTML uses the injected WebView bridge:
The HTML owns layout and display. Native iOS only provides wallet context, bridge requests, and the signer sheet.

App interface

The Raffle app declares its app-specific surface in gora.app.json under interface. The important views are: The important actions are: The Dev Bridge evaluates these declarations through generic endpoints:
This is the main rule for new examples: app-specific UI belongs in HTML, and app-specific chain reads/action shapes belong in the app interface, not in bridge or native mobile code. For mobile signing, the interface declares the full transaction shape. Base actions declare selectors and ABI argument paths. Solana actions declare ordered accounts, signer/writable flags, and instruction data. Algorand actions declare on_complete, app args, foreign accounts, and the minimum fee needed for inner transactions.

Prerequisites

You need:
  • a checkout of gora-v2
  • Node and npm
  • Rust/Cargo for the Gora CLI
  • the VM Devnet running Base, Solana, Algorand, Gora API, Dev Bridge, and ngrok
  • the iOS app only if you want to test the mobile flow after the CLI flow works
Start Devnet on the VM:
By default the Devnet script seeds and deploys the raffle-app example. To point the same flow at a different example later, set GORA_DEV_EXAMPLE_APP=<folder-name> or GORA_DEV_EXAMPLE_APP_DIR=/absolute/path/to/app. Expected public endpoints:
Check the bridge:
The health response should include:

Create the app workspace

For the reference demo, copy or open the existing example:
setup:wallets writes:
wallets.json contains public dev addresses. secrets.json contains dev-only private keys and mnemonics. Do not use these outside local/dev demos. For a remote VM demo, the VM only needs wallets.json to pre-fund generated Solana and Algorand accounts. Your local machine needs secrets.json if you want to import those wallets into the iOS simulator.

Deploy the chain components

The raffle has one chain component per chain. Each deploy writes a generic receipt to:
The raffle script also writes a legacy copy to:

Base

Algorand

The Algorand app account is the raffle treasury. Fund that app account before drawing an Algorand winner:

Solana

Build and deploy the Solana program where the Solana toolchain is installed:
If you are running these scripts on the VM, keep transaction submissions on the local RPC and publish the ngrok URL in the contract receipt:
Fund the Solana treasury before drawing a Solana winner:

Deploy the off-chain Gora app

Point the CLI at Devnet and deploy:
Confirm the app is visible:
The app entry should include iOS metadata, chain RPCs, owner wallets, and contract records for the deployed chains.

Register participants

Register one or more participants per chain:
Base registration calls register(). Solana registration sends the raffle register instruction. Algorand registration is an application opt-in. If an Algorand address cannot register twice, that is expected; opt-in is the registration.

Fund treasuries

Each chain has its own treasury. The winner is paid by the treasury on the winner’s chain.
For Algorand, fund the app address from the deployment receipt:
Use the app_address value if you want to fund manually from the mobile wallet.

Draw and settle

Run one round:
Run later rounds:
The output includes:
  • winner chain
  • winner address
  • registry hash
  • attestation hash
  • settlement transaction
There is no separate claim step. Settlement pays the winner in the same transaction.

Test from iOS

Use the iOS app after the CLI path works.
  1. Open the app and set the bridge URL to https://gora-bridge-dev.ngrok.app.
  2. Import the operator wallet for the chain you want to operate from.
  3. Import participant wallets in other simulators.
  4. Pull to refresh balances.
  5. Open Apps.
  6. Open Raffle.
  7. As participants, register on Base, Solana, or Algorand.
  8. As operator, run the next round.
The iOS app should show the active chains and the current participants. The operator wallet is detected from owner_wallets in the app manifest.

Troubleshooting

Apps fail with cannot reach gora node

The bridge is running but points at a stale Gora API port. Restart Devnet or kill the old bridge process on port 8787, then start Devnet again:

Algorand balance does not update

Pull to refresh in the iOS wallet. The app should query the bridge dynamically. If the balance is still stale, check the bridge Algorand RPC URL and token:

Algorand settle says app does not exist

The mobile app or bridge is using an old Algorand app id. Refresh Apps and verify:
Redeploy the Gora app after redeploying the Algorand app so the manifest has the current app id.

Algorand settle says would result negative

The Algorand app account does not have enough ALGO to pay the 5% payout and fees. Fund the app treasury:

Algorand settle says unavailable Account

The winner address must be included in the Algorand transaction’s foreign account list. This is handled by the current iOS and raffle script code. Rebuild the iOS app if you see this from mobile.

Second round fails with assert failed

The round submitted to the chain is stale. Rebuild the iOS app and refresh Apps so it queries the live next round from the bridge before invoking the off-chain app.

Solana airdrops hang

The Devnet script funds Solana accounts through JSON-RPC. If funding is slow, restart the Solana local validator and rerun Devnet. The funding log is:

Demo checklist

Before livestreaming:
  1. Start Devnet cleanly.
  2. Confirm bridge health.
  3. Deploy Base and Algorand components.
  4. Deploy Solana only if the VM toolchain is ready.
  5. Fund all treasuries.
  6. Register at least one participant per chain.
  7. Run one CLI draw.
  8. Rebuild iOS.
  9. Repeat the same flow from mobile.
If you are building a new example, keep this rule: app-specific state readers and transaction builders should move into manifest-declared schemas or reusable bridge/mobile primitives, not into Gora core.