> ## Documentation Index
> Fetch the complete documentation index at: https://gora.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Raffle App

> Build and deploy a cross-chain raffle with an off-chain Gora drawer and Base, Solana, and Algorand on-chain treasuries.

# 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.

```text theme={null}
participant registers on a chain
        ↓
operator runs the next raffle round
        ↓
Gora executes the off-chain drawer
        ↓
drawer returns winner + attestation hash inputs
        ↓
operator settles on the winner's chain
        ↓
winner-chain treasury pays 5% to the winner
```

## What you are building

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

| Part           | File or folder                      | Purpose                                                                                   |
| -------------- | ----------------------------------- | ----------------------------------------------------------------------------------------- |
| Off-chain app  | `src/index.ts`                      | Deterministically chooses the winner from round number and participant registry.          |
| Manifest       | `gora.app.json`                     | Declares app id, chains, actions, contracts, and iOS display metadata.                    |
| HTML UI        | `ui/index.html`                     | Renders the mobile app screen in a WebView and calls `window.Gora`.                       |
| App interface  | `gora.app.json` -> `interface`      | Declares generic views and actions so the bridge/mobile app do not hardcode raffle logic. |
| Policy         | `policy.json`                       | Restricts allowed chains and actions.                                                     |
| Base contract  | `contracts/base/GoraBaseRaffle.sol` | Stores Base participants and pays ETH from the contract treasury.                         |
| Solana program | `contracts/solana/program`          | Stores Solana state and pays SOL from the treasury account.                               |
| Algorand app   | `contracts/algorand`                | Uses app opt-in as registration and pays ALGO from the app account.                       |
| Demo CLI       | `scripts/raffle.mjs`                | Deploys, registers, funds, draws, and settles for local demos.                            |

## HTML UI

The mobile wallet does not render raffle-specific Swift UI. It loads the app's HTML from:

```bash theme={null}
curl https://gora-bridge-dev.ngrok.app/apps/raffle_app/ui
```

The Raffle HTML uses the injected WebView bridge:

```js theme={null}
await window.Gora.context()
await window.Gora.view("participants")
await window.Gora.view("next_round")
await window.Gora.action("register", { chain: "base" })
await window.Gora.action("run_round", { chain: "base" })
```

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:

| View           | What it returns               | How it is evaluated                                                                                             |
| -------------- | ----------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `participants` | participants grouped by chain | Base uses `participantCount()` + `participants(index)`. Algorand uses app opt-ins from the dev wallet registry. |
| `next_round`   | the next round number         | Base, Solana, and Algorand expose per-chain round counters; the bridge reduces them with `max`.                 |

The important actions are:

| Action          | Role        | What it declares                                                                    |
| --------------- | ----------- | ----------------------------------------------------------------------------------- |
| `register`      | participant | EVM `register()`, Solana `register` instruction, Algorand app opt-in.               |
| `run_round`     | operator    | Build off-chain input from `participants` + `next_round`, invoke Gora, then settle. |
| `settle_winner` | operator    | Map app output fields into the winner-chain settlement transaction.                 |

The Dev Bridge evaluates these declarations through generic endpoints:

```bash theme={null}
curl https://gora-bridge-dev.ngrok.app/apps/raffle_app/views/participants
curl https://gora-bridge-dev.ngrok.app/apps/raffle_app/views/next_round
```

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:

```bash theme={null}
cargo run -p protocol-core --bin gora -- devnet up \
  --chains base,solana,algorand \
  --mobile-signing true
```

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:

```text theme={null}
Gora API:     https://gora-dev.ngrok.app
Dev bridge:   https://gora-bridge-dev.ngrok.app
Base RPC:     https://base-dev.ngrok.app
Solana RPC:   https://solana-dev.ngrok.app
Algorand RPC: https://algorand-dev.ngrok.app
```

Check the bridge:

```bash theme={null}
curl https://gora-bridge-dev.ngrok.app/health
```

The health response should include:

```text theme={null}
GET /apps
GET /apps/:id/ui
GET /mobile/signing-requests
GET /chains/algorand/transactions/params
POST /chains/algorand/transactions
```

## Create the app workspace

For the reference demo, copy or open the existing example:

```bash theme={null}
cd examples/apps/raffle-app
npm install
npm run setup:wallets
```

`setup:wallets` writes:

```text theme={null}
test-wallets/wallets.json
test-wallets/secrets.json
```

`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:

```text theme={null}
.gora/contracts/<chain>/latest.json
```

The raffle script also writes a legacy copy to:

```text theme={null}
.gora/contracts/<chain>/raffle.json
```

### Base

```bash theme={null}
npm run deploy:base
```

### Algorand

```bash theme={null}
npm run deploy:algorand
```

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

```bash theme={null}
npm run fund -- algorand 100
```

### Solana

Build and deploy the Solana program where the Solana toolchain is installed:

```bash theme={null}
cargo build-sbf --manifest-path contracts/solana/program/Cargo.toml
PROGRAM_KEYPAIR=contracts/solana/program/target/deploy/gora_solana_raffle-keypair.json
SO_FILE=contracts/solana/program/target/deploy/gora_solana_raffle.so
solana program deploy "$SO_FILE" --program-id "$PROGRAM_KEYPAIR" --url http://127.0.0.1:8899
export SOLANA_PROGRAM_ID=$(solana address -k "$PROGRAM_KEYPAIR")
npm run deploy:solana
```

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:

```bash theme={null}
export SOLANA_RPC_URL=http://127.0.0.1:8899
export SOLANA_PUBLIC_RPC_URL=https://solana-dev.ngrok.app
```

Fund the Solana treasury before drawing a Solana winner:

```bash theme={null}
npm run fund -- solana 25
```

## Deploy the off-chain Gora app

Point the CLI at Devnet and deploy:

```bash theme={null}
gora config set node.url https://gora-dev.ngrok.app
gora build
gora validate
gora deploy
```

Confirm the app is visible:

```bash theme={null}
gora inspect app raffle_app
curl https://gora-bridge-dev.ngrok.app/apps
```

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:

```bash theme={null}
npm run register -- base 0xC224C62A6AEaafc76D3beE9ccf6bAcBF73a75F11
npm run register -- solana <SOLANA_PARTICIPANT_ADDRESS>
npm run register -- algorand <ALGORAND_PARTICIPANT_ADDRESS>
```

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.

```bash theme={null}
npm run fund -- base 1
npm run fund -- solana 25
npm run fund -- algorand 100
```

For Algorand, fund the app address from the deployment receipt:

```bash theme={null}
cat .gora/contracts/algorand/latest.json
```

Use the `app_address` value if you want to fund manually from the mobile wallet.

## Draw and settle

Run one round:

```bash theme={null}
npm run draw
```

Run later rounds:

```bash theme={null}
npm run draw -- --round 2
npm run draw -- --round 3
```

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:

```bash theme={null}
lsof -nP -iTCP:8787 -sTCP:LISTEN
kill <PID>
```

### 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:

```bash theme={null}
curl https://gora-bridge-dev.ngrok.app/health
curl https://gora-bridge-dev.ngrok.app/balances/algorand/<ADDRESS>
```

### Algorand settle says app does not exist

The mobile app or bridge is using an old Algorand app id. Refresh Apps and verify:

```bash theme={null}
cat .gora/contracts/algorand/latest.json
```

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:

```bash theme={null}
npm run fund -- algorand 100
```

### 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:

```text theme={null}
target/devnet-chains/logs/raffle-app-solana-wallet-funding.log
```

## 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.
