# AI Pack: symbiotic-flight-delays-example

- Description: Example dApp: Symbiotic Flight Delays.
- Remote: https://github.com/symbioticfi/symbiotic-flight-delays
- Remote ref: HEAD
- Commit: 2714761506420771e532c168e1836c59d258fbab
- Generated (UTC): 2026-06-02T10:22:21.999Z
- Repomix: 1.14.1
- Preset: contract
- Ignore patterns: .gitignore, .dockerignore, .prettierignore, .npmignore, .nvmrc, .editorconfig, .gitattributes, **/.github/**, **/.husky/**, **/.vscode/**, **/.idea/**, **/.DS_Store, node_modules/, dist/, build/, .turbo/, .next/, out/, cache/, broadcast/, bin/, coverage*, *.test, target/, docs/, abis/, bindings/, test/**, !test/integration/**, script/utils/**, package-lock.json, pnpm-lock.yaml, yarn.lock, bun.lockb, .pre-commit-config.yaml
- Flags: none

---

This file is a merged representation of a subset of the codebase, containing files not matching ignore patterns, combined into a single document by Repomix.

# File Summary

## Purpose

This file contains a packed representation of a subset of the repository's contents that is considered the most important context.
It is designed to be easily consumable by AI systems for analysis, code review,
or other automated processes.

## File Format

The content is organized as follows:

1. This summary section
2. Repository information
3. Directory structure
4. Repository files (if enabled)
5. Multiple file entries, each consisting of:
   a. A header with the file path (## File: path/to/file)
   b. The full contents of the file in a code block

## Usage Guidelines

- This file should be treated as read-only. Any changes should be made to the
  original repository files, not this packed version.
- When processing this file, use the file path to distinguish
  between different files in the repository.
- Be aware that this file may contain sensitive information. Handle it with
  the same level of security as you would the original repository.

## Notes

- Some files may have been excluded based on .gitignore rules and Repomix's configuration
- Binary files are not included in this packed representation. Please refer to the Repository Structure section for a complete list of file paths, including binary files
- Files matching these patterns are excluded: .gitignore, .dockerignore, .prettierignore, .npmignore, .nvmrc, .editorconfig, .gitattributes, **/.github/**, **/.husky/**, **/.vscode/**, **/.idea/**, **/.DS_Store, node_modules/, dist/, build/, .turbo/, .next/, out/, cache/, broadcast/, bin/, coverage*, *.test, target/, docs/, abis/, bindings/, test/**, !test/integration/**, script/utils/**, package-lock.json, pnpm-lock.yaml, yarn.lock, bun.lockb, .pre-commit-config.yaml
- Files matching patterns in .gitignore are excluded
- Files matching default ignore patterns are excluded
- Files are sorted by Git change count (files with more changes are at the bottom)

# Directory Structure

```
network-scripts/
  deploy.sh
  deployer.Dockerfile
  flight-node-start.sh
  genesis-generator.sh
  sidecar-start.sh
off-chain/
  abis/
    FlightDelays.abi.json
  cmd/
    benchmark/
      main.go
    flights-api/
      main.go
    node/
      main.go
  internal/
    contracts/
      flightDelays.go
    flights/
      store_test.go
      store.go
      types.go
    utils/
      util.go
  .dockerignore
  Dockerfile
  go.mod
script/
  mocks/
    MockERC20.sol
    SymbioticRewardsConstantsHelper.sol
  my-relay-deploy.toml
  MyRelayDeploy.sol
snapshots/
  gas.txt
  sizes.txt
src/
  symbiotic/
    Driver.sol
    KeyRegistry.sol
    Settlement.sol
    VotingPowers.sol
  FlightDelays.sol
ui/
  src/
    abi/
      ERC20.json
      FlightDelays.json
      Rewards.json
      Slasher.json
      Vault.json
    api/
      flights.ts
    components/
      BuyerPage.tsx
      ProviderPage.tsx
    constants/
      airlines.ts
    utils/
      format.ts
      hash.ts
    App.tsx
    config.ts
    main.tsx
    styles.css
    types.ts
    wallet.tsx
  .dockerignore
  .env.docker
  .env.example
  Dockerfile
  index.html
  package.json
  tsconfig.json
  tsconfig.tsbuildinfo
  vite.config.ts
.gitmodules
.prettierrc
CONTRIBUTING.md
foundry.lock
foundry.toml
generate_network.sh
package.json
pnpm-workspace.yaml
README.md
remappings.txt
```

# Files

## File: network-scripts/deploy.sh

```bash
#!/bin/sh
set -e

ANVIL_RPC_URL=${ANVIL_RPC_URL:-http://anvil:8545}
MULTICALL_ADDRESS=0x05f32b3cc3888453ff71b01135b34ff8e41263f2
MULTICALL_BALANCE_HEX=0xde0b6b3a7640000 # 1 ether

echo "Waiting for anvil to be ready..."
until cast client --rpc-url "$ANVIL_RPC_URL" > /dev/null 2>&1; do sleep 1; done


echo "Deploying Multicall3 contracts..."
cast rpc --rpc-url "$ANVIL_RPC_URL" anvil_setBalance "$MULTICALL_ADDRESS" "$MULTICALL_BALANCE_HEX"
cast rpc --rpc-url "$ANVIL_RPC_URL" anvil_setNonce "$MULTICALL_ADDRESS" 0x0
cast publish 0xf90f538085174876e800830f42408080b90f00608060405234801561001057600080fd5b50610ee0806100206000396000f3fe6080604052600436106100f35760003560e01c80634d2301cc1161008a578063a8b0574e11610059578063a8b0574e1461025a578063bce38bd714610275578063c3077fa914610288578063ee82ac5e1461029b57600080fd5b80634d2301cc146101ec57806372425d9d1461022157806382ad56cb1461023457806386d516e81461024757600080fd5b80633408e470116100c65780633408e47014610191578063399542e9146101a45780633e64a696146101c657806342cbb15c146101d957600080fd5b80630f28c97d146100f8578063174dea711461011a578063252dba421461013a57806327e86d6e1461015b575b600080fd5b34801561010457600080fd5b50425b6040519081526020015b60405180910390f35b61012d610128366004610a85565b6102ba565b6040516101119190610bbe565b61014d610148366004610a85565b6104ef565b604051610111929190610bd8565b34801561016757600080fd5b50437fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0140610107565b34801561019d57600080fd5b5046610107565b6101b76101b2366004610c60565b610690565b60405161011193929190610cba565b3480156101d257600080fd5b5048610107565b3480156101e557600080fd5b5043610107565b3480156101f857600080fd5b50610107610207366004610ce2565b73ffffffffffffffffffffffffffffffffffffffff163190565b34801561022d57600080fd5b5044610107565b61012d610242366004610a85565b6106ab565b34801561025357600080fd5b5045610107565b34801561026657600080fd5b50604051418152602001610111565b61012d610283366004610c60565b61085a565b6101b7610296366004610a85565b610a1a565b3480156102a757600080fd5b506101076102b6366004610d18565b4090565b60606000828067ffffffffffffffff8111156102d8576102d8610d31565b60405190808252806020026020018201604052801561031e57816020015b6040805180820190915260008152606060208201528152602001906001900390816102f65790505b5092503660005b8281101561047757600085828151811061034157610341610d60565b6020026020010151905087878381811061035d5761035d610d60565b905060200281019061036f9190610d8f565b6040810135958601959093506103886020850185610ce2565b73ffffffffffffffffffffffffffffffffffffffff16816103ac6060870187610dcd565b6040516103ba929190610e32565b60006040518083038185875af1925050503d80600081146103f7576040519150601f19603f3d011682016040523d82523d6000602084013e6103fc565b606091505b50602080850191909152901515808452908501351761046d577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260846000fd5b5050600101610325565b508234146104e6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4d756c746963616c6c333a2076616c7565206d69736d6174636800000000000060448201526064015b60405180910390fd5b50505092915050565b436060828067ffffffffffffffff81111561050c5761050c610d31565b60405190808252806020026020018201604052801561053f57816020015b606081526020019060019003908161052a5790505b5091503660005b8281101561068657600087878381811061056257610562610d60565b90506020028101906105749190610e42565b92506105836020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff166105a66020850185610dcd565b6040516105b4929190610e32565b6000604051808303816000865af19150503d80600081146105f1576040519150601f19603f3d011682016040523d82523d6000602084013e6105f6565b606091505b5086848151811061060957610609610d60565b602090810291909101015290508061067d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064016104dd565b50600101610546565b5050509250929050565b43804060606106a086868661085a565b905093509350939050565b6060818067ffffffffffffffff8111156106c7576106c7610d31565b60405190808252806020026020018201604052801561070d57816020015b6040805180820190915260008152606060208201528152602001906001900390816106e55790505b5091503660005b828110156104e657600084828151811061073057610730610d60565b6020026020010151905086868381811061074c5761074c610d60565b905060200281019061075e9190610e76565b925061076d6020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff166107906040850185610dcd565b60405161079e929190610e32565b6000604051808303816000865af19150503d80600081146107db576040519150601f19603f3d011682016040523d82523d6000602084013e6107e0565b606091505b506020808401919091529015158083529084013517610851577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260646000fd5b50600101610714565b6060818067ffffffffffffffff81111561087657610876610d31565b6040519080825280602002602001820160405280156108bc57816020015b6040805180820190915260008152606060208201528152602001906001900390816108945790505b5091503660005b82811015610a105760008482815181106108df576108df610d60565b602002602001015190508686838181106108fb576108fb610d60565b905060200281019061090d9190610e42565b925061091c6020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff1661093f6020850185610dcd565b60405161094d929190610e32565b6000604051808303816000865af19150503d806000811461098a576040519150601f19603f3d011682016040523d82523d6000602084013e61098f565b606091505b506020830152151581528715610a07578051610a07576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064016104dd565b506001016108c3565b5050509392505050565b6000806060610a2b60018686610690565b919790965090945092505050565b60008083601f840112610a4b57600080fd5b50813567ffffffffffffffff811115610a6357600080fd5b6020830191508360208260051b8501011115610a7e57600080fd5b9250929050565b60008060208385031215610a9857600080fd5b823567ffffffffffffffff811115610aaf57600080fd5b610abb85828601610a39565b90969095509350505050565b6000815180845260005b81811015610aed57602081850181015186830182015201610ad1565b81811115610aff576000602083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b600082825180855260208086019550808260051b84010181860160005b84811015610bb1578583037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001895281518051151584528401516040858501819052610b9d81860183610ac7565b9a86019a9450505090830190600101610b4f565b5090979650505050505050565b602081526000610bd16020830184610b32565b9392505050565b600060408201848352602060408185015281855180845260608601915060608160051b870101935082870160005b82811015610c52577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0888703018452610c40868351610ac7565b95509284019290840190600101610c06565b509398975050505050505050565b600080600060408486031215610c7557600080fd5b83358015158114610c8557600080fd5b9250602084013567ffffffffffffffff811115610ca157600080fd5b610cad86828701610a39565b9497909650939450505050565b838152826020820152606060408201526000610cd96060830184610b32565b95945050505050565b600060208284031215610cf457600080fd5b813573ffffffffffffffffffffffffffffffffffffffff81168114610bd157600080fd5b600060208284031215610d2a57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81833603018112610dc357600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112610e0257600080fd5b83018035915067ffffffffffffffff821115610e1d57600080fd5b602001915036819003821315610a7e57600080fd5b8183823760009101908152919050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1833603018112610dc357600080fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa1833603018112610dc357600080fdfea2646970667358221220bb2b5c71a328032f97c676ae39a1ec2148d3e5d6f73d95e9b17910152d61f16264736f6c634300080c00331ca0edce47092c0f398cebf3ffc267f05c8e7076e3b89445e0fe50f6332273d4569ba01b0b9d000e19b24c5869b0fc3b22b0d6fa47cd63316875cbbd577d76e6fde086 --rpc-url "$ANVIL_RPC_URL"

echo "Deploying contracts..."
./node_modules/@symbioticfi/relay-contracts/script/relay-deploy.sh script/MyRelayDeploy.sol script/my-relay-deploy.toml --broadcast --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 -vvvvv

echo 'Waiting for deployment completion...'
until [ -f /deploy-data/deployment-completed.json ]; do sleep 2; done

echo "Setting interval mining..."
cast rpc --rpc-url "$ANVIL_RPC_URL" evm_setIntervalMining 1

echo "Mine a single block to finalize the deployment..."
cast rpc --rpc-url "$ANVIL_RPC_URL" evm_mine

echo "Deployment completed successfully!"

# Create deployment completion marker
echo "$(date): Deployment completed successfully" > /deploy-data/deployment-complete.marker
echo "Deployment completion marker created"
```

## File: network-scripts/deployer.Dockerfile

```dockerfile
FROM ghcr.io/foundry-rs/foundry:v1.4.4

USER root

# Install Python (needed by relay deployment helper scripts) together with tomli fallback.
RUN set -eux; \
    if command -v apt-get >/dev/null 2>&1; then \
        apt-get update; \
        DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
            python3 \
            python3-venv \
            python3-pip; \
        rm -rf /var/lib/apt/lists/*; \
    elif command -v apk >/dev/null 2>&1; then \
        apk add --no-cache \
            python3 \
            py3-virtualenv \
            py3-pip; \
    else \
        echo "Unable to determine package manager for Python installation." >&2; \
        exit 1; \
    fi; \
    python3 -m pip install --no-cache-dir tomli; \
    python3 --version

USER foundry
```

## File: network-scripts/flight-node-start.sh

```bash
#!/bin/sh
FLIGHT_DELAYS_ADDRESS=0xA4b0f5eb09891c1538494c4989Eea0203b1153b1

exec /app/flight-node --relay-api-url "$1" --evm-rpc-url http://anvil:8545 --flight-delays-address "$FLIGHT_DELAYS_ADDRESS" --flights-api-url http://flights-api:8085 --private-key "$2" --log-level info
```

## File: network-scripts/genesis-generator.sh

```bash
#!/bin/sh

echo 'Waiting for deployment completion...'
until [ -f /deploy-data/deployment-complete.marker ]; do sleep 2; done

DRIVER_ADDRESS=0x43C27243F96591892976FFf886511807B65a33d5

MAX_RETRIES=50
RETRY_DELAY=2
attempt=1

while [ $attempt -le $MAX_RETRIES ]; do
    echo "Attempt $attempt of $MAX_RETRIES: Generating network genesis..."

    if /app/relay_utils network \
            --chains http://anvil:8545 \
            --driver.address "$DRIVER_ADDRESS" \
            --driver.chainid 31337 \
          generate-genesis \
            --commit \
            --secret-keys 31337:0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80; then
        echo 'Genesis generation completed successfully!'

        # Create genesis completion marker
        echo "$(date): Genesis generation completed successfully" > /deploy-data/genesis-complete.marker
        echo "Genesis completion marker created"

        echo "Waiting few seconds before exiting..."
        sleep 5

        exit 0
    else
        echo "Genesis generation failed on attempt $attempt"
        if [ $attempt -lt $MAX_RETRIES ]; then
            echo "Waiting $RETRY_DELAY seconds before retry..."
            sleep $RETRY_DELAY
        else
            echo "All $MAX_RETRIES attempts failed. Exiting with error."
            exit 1
        fi
        attempt=$((attempt + 1))
    fi
done
```

## File: network-scripts/sidecar-start.sh

```bash
#!/bin/sh

DRIVER_ADDRESS=0x43C27243F96591892976FFf886511807B65a33d5

cat > /tmp/sidecar.yaml << EOFCONFIG
# Logging
log:
  level: "debug"
  mode: "pretty"

# API Server Configuration
api:
  listen: ":8080"

# Metrics Configuration
metrics:
  pprof: true

# Driver Contract
driver:
  chain-id: 31337
  address: "$DRIVER_ADDRESS"

# P2P Configuration
p2p:
  listen: "/ip4/0.0.0.0/tcp/8880"
  bootnodes:
    - /dns4/relay-sidecar-1/tcp/8880/p2p/16Uiu2HAmFUiPYAJ7bE88Q8d7Kznrw5ifrje2e5QFyt7uFPk2G3iR
  dht-mode: "server"
  mdns: true

# EVM Configuration
evm:
  chains:
    - "http://anvil:8545"
  max-calls: 30
EOFCONFIG


exec /app/relay_sidecar --config /tmp/sidecar.yaml --secret-keys "$1" --storage-dir "$2"
```

## File: off-chain/abis/FlightDelays.abi.json

```json
[
    {
        "type": "constructor",
        "inputs": [
            {
                "name": "vaultConfigurator",
                "type": "address",
                "internalType": "address"
            },
            {
                "name": "operatorVaultOptInService",
                "type": "address",
                "internalType": "address"
            },
            {
                "name": "operatorNetworkOptInService",
                "type": "address",
                "internalType": "address"
            },
            {
                "name": "defaultStakerRewardsFactory",
                "type": "address",
                "internalType": "address"
            },
            {
                "name": "operatorRegistry",
                "type": "address",
                "internalType": "address"
            }
        ],
        "stateMutability": "nonpayable"
    },
    {
        "type": "function",
        "name": "DEFAULT_STAKER_REWARDS_FACTORY",
        "inputs": [],
        "outputs": [
            {
                "name": "",
                "type": "address",
                "internalType": "address"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "NETWORK",
        "inputs": [],
        "outputs": [
            {
                "name": "",
                "type": "address",
                "internalType": "address"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "OPERATOR_NETWORK_OPT_IN_SERVICE",
        "inputs": [],
        "outputs": [
            {
                "name": "",
                "type": "address",
                "internalType": "address"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "OPERATOR_VAULT_OPT_IN_SERVICE",
        "inputs": [],
        "outputs": [
            {
                "name": "",
                "type": "address",
                "internalType": "address"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "SUBNETWORK",
        "inputs": [],
        "outputs": [
            {
                "name": "",
                "type": "bytes32",
                "internalType": "bytes32"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "SUBNETWORK_IDENTIFIER",
        "inputs": [],
        "outputs": [
            {
                "name": "",
                "type": "uint96",
                "internalType": "uint96"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "VAULT_CONFIGURATOR",
        "inputs": [],
        "outputs": [
            {
                "name": "",
                "type": "address",
                "internalType": "address"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "airlines",
        "inputs": [
            {
                "name": "airlineId",
                "type": "bytes32",
                "internalType": "bytes32"
            }
        ],
        "outputs": [
            {
                "name": "vault",
                "type": "address",
                "internalType": "address"
            },
            {
                "name": "rewards",
                "type": "address",
                "internalType": "address"
            },
            {
                "name": "covered",
                "type": "uint256",
                "internalType": "uint256"
            },
            {
                "name": "lastFlightId",
                "type": "bytes32",
                "internalType": "bytes32"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "buyInsurance",
        "inputs": [
            {
                "name": "airlineId",
                "type": "bytes32",
                "internalType": "bytes32"
            },
            {
                "name": "flightId",
                "type": "bytes32",
                "internalType": "bytes32"
            }
        ],
        "outputs": [],
        "stateMutability": "nonpayable"
    },
    {
        "type": "function",
        "name": "claimInsurance",
        "inputs": [
            {
                "name": "airlineId",
                "type": "bytes32",
                "internalType": "bytes32"
            },
            {
                "name": "flightId",
                "type": "bytes32",
                "internalType": "bytes32"
            }
        ],
        "outputs": [],
        "stateMutability": "nonpayable"
    },
    {
        "type": "function",
        "name": "collateral",
        "inputs": [],
        "outputs": [
            {
                "name": "",
                "type": "address",
                "internalType": "address"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "createFlight",
        "inputs": [
            {
                "name": "airlineId",
                "type": "bytes32",
                "internalType": "bytes32"
            },
            {
                "name": "flightId",
                "type": "bytes32",
                "internalType": "bytes32"
            },
            {
                "name": "scheduledTimestamp",
                "type": "uint48",
                "internalType": "uint48"
            },
            {
                "name": "epoch",
                "type": "uint48",
                "internalType": "uint48"
            },
            {
                "name": "proof",
                "type": "bytes",
                "internalType": "bytes"
            }
        ],
        "outputs": [],
        "stateMutability": "nonpayable"
    },
    {
        "type": "function",
        "name": "delayFlight",
        "inputs": [
            {
                "name": "airlineId",
                "type": "bytes32",
                "internalType": "bytes32"
            },
            {
                "name": "flightId",
                "type": "bytes32",
                "internalType": "bytes32"
            },
            {
                "name": "epoch",
                "type": "uint48",
                "internalType": "uint48"
            },
            {
                "name": "proof",
                "type": "bytes",
                "internalType": "bytes"
            }
        ],
        "outputs": [],
        "stateMutability": "nonpayable"
    },
    {
        "type": "function",
        "name": "delayWindow",
        "inputs": [],
        "outputs": [
            {
                "name": "",
                "type": "uint48",
                "internalType": "uint48"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "departFlight",
        "inputs": [
            {
                "name": "airlineId",
                "type": "bytes32",
                "internalType": "bytes32"
            },
            {
                "name": "flightId",
                "type": "bytes32",
                "internalType": "bytes32"
            },
            {
                "name": "epoch",
                "type": "uint48",
                "internalType": "uint48"
            },
            {
                "name": "proof",
                "type": "bytes",
                "internalType": "bytes"
            }
        ],
        "outputs": [],
        "stateMutability": "nonpayable"
    },
    {
        "type": "function",
        "name": "flights",
        "inputs": [
            {
                "name": "airlineId",
                "type": "bytes32",
                "internalType": "bytes32"
            },
            {
                "name": "flightId",
                "type": "bytes32",
                "internalType": "bytes32"
            }
        ],
        "outputs": [
            {
                "name": "timestamp",
                "type": "uint48",
                "internalType": "uint48"
            },
            {
                "name": "status",
                "type": "uint8",
                "internalType": "enum FlightDelays.FlightStatus"
            },
            {
                "name": "policiesSold",
                "type": "uint128",
                "internalType": "uint128"
            },
            {
                "name": "previousFlightId",
                "type": "bytes32",
                "internalType": "bytes32"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "initialize",
        "inputs": [
            {
                "name": "initParams",
                "type": "tuple",
                "internalType": "struct FlightDelays.InitParams",
                "components": [
                    {
                        "name": "votingPowers",
                        "type": "address",
                        "internalType": "address"
                    },
                    {
                        "name": "settlement",
                        "type": "address",
                        "internalType": "address"
                    },
                    {
                        "name": "collateral",
                        "type": "address",
                        "internalType": "address"
                    },
                    {
                        "name": "vaultEpochDuration",
                        "type": "uint48",
                        "internalType": "uint48"
                    },
                    {
                        "name": "messageExpiry",
                        "type": "uint32",
                        "internalType": "uint32"
                    },
                    {
                        "name": "policyWindow",
                        "type": "uint48",
                        "internalType": "uint48"
                    },
                    {
                        "name": "delayWindow",
                        "type": "uint48",
                        "internalType": "uint48"
                    },
                    {
                        "name": "policyPremium",
                        "type": "uint256",
                        "internalType": "uint256"
                    },
                    {
                        "name": "policyPayout",
                        "type": "uint256",
                        "internalType": "uint256"
                    }
                ]
            }
        ],
        "outputs": [],
        "stateMutability": "nonpayable"
    },
    {
        "type": "function",
        "name": "messageExpiry",
        "inputs": [],
        "outputs": [
            {
                "name": "",
                "type": "uint32",
                "internalType": "uint32"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "policies",
        "inputs": [
            {
                "name": "airlineId",
                "type": "bytes32",
                "internalType": "bytes32"
            },
            {
                "name": "flightId",
                "type": "bytes32",
                "internalType": "bytes32"
            },
            {
                "name": "buyer",
                "type": "address",
                "internalType": "address"
            }
        ],
        "outputs": [
            {
                "name": "policyStatus",
                "type": "uint8",
                "internalType": "enum FlightDelays.PolicyStatus"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "policyPayout",
        "inputs": [],
        "outputs": [
            {
                "name": "",
                "type": "uint256",
                "internalType": "uint256"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "policyPremium",
        "inputs": [],
        "outputs": [
            {
                "name": "",
                "type": "uint256",
                "internalType": "uint256"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "policyWindow",
        "inputs": [],
        "outputs": [
            {
                "name": "",
                "type": "uint48",
                "internalType": "uint48"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "settlement",
        "inputs": [],
        "outputs": [
            {
                "name": "",
                "type": "address",
                "internalType": "address"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "staticDelegateCall",
        "inputs": [
            {
                "name": "target",
                "type": "address",
                "internalType": "address"
            },
            {
                "name": "data",
                "type": "bytes",
                "internalType": "bytes"
            }
        ],
        "outputs": [],
        "stateMutability": "nonpayable"
    },
    {
        "type": "function",
        "name": "vaultEpochDuration",
        "inputs": [],
        "outputs": [
            {
                "name": "",
                "type": "uint48",
                "internalType": "uint48"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "votingPowers",
        "inputs": [],
        "outputs": [
            {
                "name": "",
                "type": "address",
                "internalType": "address"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "event",
        "name": "AirlineVaultDeployed",
        "inputs": [
            {
                "name": "airlineId",
                "type": "bytes32",
                "indexed": true,
                "internalType": "bytes32"
            },
            {
                "name": "vault",
                "type": "address",
                "indexed": false,
                "internalType": "address"
            },
            {
                "name": "rewards",
                "type": "address",
                "indexed": false,
                "internalType": "address"
            }
        ],
        "anonymous": false
    },
    {
        "type": "event",
        "name": "FlightCreated",
        "inputs": [
            {
                "name": "airlineId",
                "type": "bytes32",
                "indexed": true,
                "internalType": "bytes32"
            },
            {
                "name": "flightId",
                "type": "bytes32",
                "indexed": true,
                "internalType": "bytes32"
            },
            {
                "name": "scheduledTimestamp",
                "type": "uint48",
                "indexed": false,
                "internalType": "uint48"
            }
        ],
        "anonymous": false
    },
    {
        "type": "event",
        "name": "FlightDelayed",
        "inputs": [
            {
                "name": "airlineId",
                "type": "bytes32",
                "indexed": true,
                "internalType": "bytes32"
            },
            {
                "name": "flightId",
                "type": "bytes32",
                "indexed": true,
                "internalType": "bytes32"
            }
        ],
        "anonymous": false
    },
    {
        "type": "event",
        "name": "FlightDeparted",
        "inputs": [
            {
                "name": "airlineId",
                "type": "bytes32",
                "indexed": true,
                "internalType": "bytes32"
            },
            {
                "name": "flightId",
                "type": "bytes32",
                "indexed": true,
                "internalType": "bytes32"
            }
        ],
        "anonymous": false
    },
    {
        "type": "event",
        "name": "InitSubnetwork",
        "inputs": [
            {
                "name": "network",
                "type": "address",
                "indexed": false,
                "internalType": "address"
            },
            {
                "name": "subnetworkId",
                "type": "uint96",
                "indexed": false,
                "internalType": "uint96"
            }
        ],
        "anonymous": false
    },
    {
        "type": "event",
        "name": "Initialized",
        "inputs": [
            {
                "name": "version",
                "type": "uint64",
                "indexed": false,
                "internalType": "uint64"
            }
        ],
        "anonymous": false
    },
    {
        "type": "event",
        "name": "InsuranceClaimed",
        "inputs": [
            {
                "name": "airlineId",
                "type": "bytes32",
                "indexed": true,
                "internalType": "bytes32"
            },
            {
                "name": "flightId",
                "type": "bytes32",
                "indexed": true,
                "internalType": "bytes32"
            },
            {
                "name": "buyer",
                "type": "address",
                "indexed": true,
                "internalType": "address"
            },
            {
                "name": "payout",
                "type": "uint256",
                "indexed": false,
                "internalType": "uint256"
            }
        ],
        "anonymous": false
    },
    {
        "type": "event",
        "name": "InsurancePurchased",
        "inputs": [
            {
                "name": "airlineId",
                "type": "bytes32",
                "indexed": true,
                "internalType": "bytes32"
            },
            {
                "name": "flightId",
                "type": "bytes32",
                "indexed": true,
                "internalType": "bytes32"
            },
            {
                "name": "buyer",
                "type": "address",
                "indexed": true,
                "internalType": "address"
            },
            {
                "name": "premium",
                "type": "uint256",
                "indexed": false,
                "internalType": "uint256"
            }
        ],
        "anonymous": false
    },
    {
        "type": "error",
        "name": "BuyWindowClosed",
        "inputs": []
    },
    {
        "type": "error",
        "name": "FlightAlreadyExists",
        "inputs": []
    },
    {
        "type": "error",
        "name": "FlightNotDelayable",
        "inputs": []
    },
    {
        "type": "error",
        "name": "FlightNotDelayed",
        "inputs": []
    },
    {
        "type": "error",
        "name": "FlightNotScheduled",
        "inputs": []
    },
    {
        "type": "error",
        "name": "InsufficientCoverage",
        "inputs": []
    },
    {
        "type": "error",
        "name": "InvalidEpoch",
        "inputs": []
    },
    {
        "type": "error",
        "name": "InvalidFlight",
        "inputs": []
    },
    {
        "type": "error",
        "name": "InvalidInitialization",
        "inputs": []
    },
    {
        "type": "error",
        "name": "InvalidMessageSignature",
        "inputs": []
    },
    {
        "type": "error",
        "name": "InvalidPolicy",
        "inputs": []
    },
    {
        "type": "error",
        "name": "InvalidTimestamp",
        "inputs": []
    },
    {
        "type": "error",
        "name": "NetworkManager_InvalidNetwork",
        "inputs": []
    },
    {
        "type": "error",
        "name": "NotInitializing",
        "inputs": []
    },
    {
        "type": "error",
        "name": "PolicyAlreadyPurchased",
        "inputs": []
    },
    {
        "type": "error",
        "name": "PolicyNotFound",
        "inputs": []
    },
    {
        "type": "error",
        "name": "PreviousFlightIncomplete",
        "inputs": []
    },
    {
        "type": "error",
        "name": "SafeERC20FailedOperation",
        "inputs": [
            {
                "name": "token",
                "type": "address",
                "internalType": "address"
            }
        ]
    }
]
```

## File: off-chain/cmd/benchmark/main.go

```go
package main

import (
	"bytes"
	"context"
	"crypto/rand"
	"fmt"
	"log/slog"
	"math/big"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/samber/lo"

	"sum/internal/utils"

	"github.com/ethereum/go-ethereum/common"
	"github.com/go-errors/errors"
	v1 "github.com/symbioticfi/relay/api/client/v1"
	"golang.org/x/sync/errgroup"
)

type processes []process

type process struct {
	args          []string
	cmd           *exec.Cmd
	stdOut        *bytes.Buffer
	stdErr        *bytes.Buffer
	apiAddr       string
	relayClient   *v1.SymbioticClient
	runSeparately bool
}

const (
	operatorsCount       = 3
	numberOfSignRequests = 1000
	sizeOfMessageBytes   = 320
)

func main() {
	slog.SetLogLoggerLevel(slog.LevelDebug)

	ctx := context.Background()

	if err := run(ctx); err != nil {
		panic(err)
	}
}

func run(ctx context.Context) error {
	prs, err := runProcesses(ctx, false)
	if err != nil {
		return errors.Errorf("failed to run processes: %w", err)
	}
	defer prs.stopProcesses()

	if err := sendRequestAndWait(ctx, prs, numberOfSignRequests); err != nil {
		return errors.Errorf("failed to send request and wait: %w", err)
	}
	if errors.Is(err, context.Canceled) {
		slog.Info("Context was canceled, stopping processes")
	}

	select {
	case <-ctx.Done():
		slog.Info("Context done, stopping processes")
		break
	}

	return nil
}

func sendRequestAndWait(ctx context.Context, prs processes, nRequests int) *errors.Error {
	lastAllCommitted, err := prs[0].relayClient.GetLastAllCommitted(ctx, &v1.GetLastAllCommittedRequest{})
	if err != nil {
		return errors.Errorf("failed to get current epoch: %w", err)
	}

	lastCommittedEpoch := lo.Min(
		lo.Map(
			lo.Values(lastAllCommitted.EpochInfos),
			func(item *v1.ChainEpochInfo, _ int) uint64 {
				return item.LastCommittedEpoch
			}),
	)

	requestIDsSent := make([]string, nRequests)
	eg, egCtx := errgroup.WithContext(ctx)
	eg.SetLimit(20)
	for i := range nRequests {
		eg.Go(func() error {
			requestID, err := prs.sendMessageToAllRelays(egCtx, lastCommittedEpoch)
			if err != nil {
				return errors.Errorf("failed to send messages to all processes: %w", err)
			}
			requestIDsSent[i] = requestID
			return nil
		})
	}
	if err := eg.Wait(); err != nil {
		return errors.Errorf("failed to send messages to all processes: %w", err)
	}

	sentTime := time.Now()
	slog.Info("Sent all requesst", "nRequests", nRequests, "time", sentTime)

	timer := time.NewTimer(0)
	defer timer.Stop()

	requestIDsAggregated := make(map[string]struct{})

cycle:
	for {
		select {
		case <-timer.C:
			for i := range requestIDsSent {
				requestID := requestIDsSent[i]
				if _, ok := requestIDsAggregated[requestID]; ok {
					continue
				}

				resp, err := prs[0].relayClient.GetAggregationProof(ctx, &v1.GetAggregationProofRequest{
					RequestId: requestID,
				})
				if err != nil {
					slog.Debug("Failed to get aggregation status", "error", err)
					continue
				}

				slog.Debug("Received aggregation proof", "requestID", requestID, "proof", common.Bytes2Hex(resp.AggregationProof.MessageHash), "elapsed", time.Since(sentTime))

				requestIDsAggregated[requestID] = struct{}{}
			}
			if len(requestIDsAggregated) == nRequests {
				slog.Info("All requests aggregated", "count", len(requestIDsAggregated), "elapsed", time.Since(sentTime))
				break cycle
			}
			timer.Reset(time.Second)
		case <-ctx.Done():
			slog.Info("Context done, stopping processes")
			break cycle
		}
	}
	return nil
}

func runProcesses(ctx context.Context, runSeparately bool) (processes, error) {
	pathToExec := "bin/symbiotic_relay"
	commonArgs := []string{"--config", "sidecar.common.yaml", "--log-mode", "text"}
	firstArgs := []string{
		"--aggregator", "true",
		"--committer", "true",
	}
	secretKeyTemplate := "symb/0/15/%s,evm/1/31337/0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
	firstKey := uint64(1e18)
	firstPort := 8080
	firstData := 0

	var prs processes

	for i := 0; i < operatorsCount; i++ {
		secretKey := fmt.Sprintf(secretKeyTemplate, common.Bytes2Hex(new(big.Int).SetUint64(firstKey+uint64(i)).Bytes()))
		apiAddr := fmt.Sprintf(":%d", firstPort+i)
		args := append(commonArgs, "--secret-keys", secretKey, "--http-listen", apiAddr, "--storage-dir", fmt.Sprintf(".data/%03d", firstData+i))
		if i == 0 {
			args = append(args, firstArgs...)
		}

		pr := process{
			args:          args,
			apiAddr:       apiAddr,
			stdOut:        &bytes.Buffer{},
			stdErr:        &bytes.Buffer{},
			runSeparately: runSeparately,
		}

		if !runSeparately {
			pr.cmd = exec.Command(pathToExec, args...)
			pr.cmd.Stdout = pr.stdOut
			pr.cmd.Stderr = pr.stdErr
			err := pr.cmd.Start()
			if err != nil {
				return nil, errors.Errorf("failed to start process: %w", err)
			}
		}

		conn, err := utils.GetGRPCConnection("localhost" + apiAddr + "/api/v1")
		if err != nil {
			return nil, errors.Errorf("failed to create relay client: %w", err)
		}
		pr.relayClient = v1.NewSymbioticClient(conn)

		slog.Debug("Started process", "args", pr.args)

		prs = append(prs, pr)
	}

	if !runSeparately {
		prs.waitServerStarted(ctx)
	}

	return prs, nil
}

func (prs processes) waitServerStarted(ctx context.Context) {
	var startedCound int

	for i := range 100 {
		startedCound = 0
		for _, pr := range prs {
			if strings.Contains(pr.stdOut.String(), "All missing epochs loaded") {
				startedCound++
			}
		}
		if startedCound == len(prs) {
			break
		}
		slog.Info("Not all processes started successfully, retrying...", "attempt", i+1, "startedCount", startedCound, "totalCount", len(prs))

		time.Sleep(time.Second)
	}
	if startedCound != len(prs) {
		prs.printErrLogs()
		panic("Not all processes started successfully. Check logs for details.")
	}
	slog.InfoContext(ctx, "All processes started", "count", len(prs))
}

func (prs processes) stopProcesses() {
	for _, pr := range prs {
		if pr.runSeparately {
			continue
		}
		// Send an interrupt signal for a graceful shutdown, that is equivalent to pressing Ctrl+C.
		slog.Debug("Stopping process...", "pid", pr.cmd.Process.Pid)
		if err := pr.cmd.Process.Signal(os.Interrupt); err != nil {
			slog.Warn("Failed to send interrupt signal to process. Trying to kill.", "pid", pr.cmd.Process.Pid, "error", err)
			// If signaling fails, you can resort to killing the process forcefully.
			if killErr := pr.cmd.Process.Kill(); killErr != nil {
				slog.Error("Failed to kill process.", "pid", pr.cmd.Process.Pid, "error", killErr)
			}
		}

		if err := pr.cmd.Wait(); err != nil {
			slog.Warn("Process exited with error.", "pid", pr.cmd.Process.Pid, "error", err, "stderr", pr.stdErr.String(), "stdout", pr.stdOut.String())
		} else {
			slog.Info("Process stopped successfully.", "pid", pr.cmd.Process.Pid)
		}
	}
}

func (prs processes) sendMessageToAllRelays(ctx context.Context, epoch uint64) (string, error) {
	message := randomMessage(sizeOfMessageBytes)
	var requestID string
	for _, pr := range prs {
		rID, err := sendSignMessageRequest(ctx, pr, message, epoch)
		if err != nil {
			return "", errors.Errorf("failed to send sign message request: %w", err)
		}
		requestID = rID
	}
	return requestID, nil
}

func sendSignMessageRequest(ctx context.Context, pr process, message []byte, epoch uint64) (string, error) {
	resp, err := pr.relayClient.SignMessage(ctx, &v1.SignMessageRequest{
		KeyTag:        15,
		Message:       message,
		RequiredEpoch: &epoch,
	})
	if err != nil {
		return "", errors.Errorf("failed to send sign message request: %w", err)
	}
	slog.Debug("Sign message request sent", "message", common.Bytes2Hex(message))

	return resp.RequestId, nil
}

func randomMessage(n int) []byte {
	b := make([]byte, n)
	_, err := rand.Read(b)
	if err != nil {
		panic(fmt.Sprintf("failed to generate random bytes: %v", err))
	}
	return b
}

func (prs processes) printErrLogs() {
	for _, pr := range prs {
		if pr.stdErr.Len() > 0 {
			slog.Error("Process stderr", "pid", pr.cmd.Process.Pid, "stderr", pr.stdErr.String())
		}
		if pr.stdOut.Len() > 0 {
			slog.Info("Process stdout", "pid", pr.cmd.Process.Pid, "stdout", pr.stdOut.String())
		}
	}
}
```

## File: off-chain/cmd/flights-api/main.go

```go
package main

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"log/slog"
	"math/rand"
	"net/http"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/go-chi/chi/v5"
	"github.com/go-chi/chi/v5/middleware"
	"github.com/spf13/cobra"

	"sum/internal/flights"
)

type config struct {
	listenAddr string
}

var cfg config

var rootCmd = &cobra.Command{
	Use:           "flights-api",
	Short:         "Mock flights API backing the flight delay protocol",
	SilenceUsage:  true,
	SilenceErrors: true,
	RunE: func(cmd *cobra.Command, args []string) error {
		ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
		defer cancel()

		store := flights.NewStore(seedAirlines(), seedFlights())
		generator := newFlightGenerator(store)
		generator.start(ctx)
		srv := newFlightServer(store)

		httpServer := &http.Server{
			Addr:              cfg.listenAddr,
			Handler:           srv.routes(),
			ReadHeaderTimeout: 5 * time.Second,
			ReadTimeout:       10 * time.Second,
			WriteTimeout:      10 * time.Second,
		}

		done := make(chan struct{})
		go func() {
			<-ctx.Done()
			shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
			defer cancel()
			if err := httpServer.Shutdown(shutdownCtx); err != nil {
				slog.Error("Failed to shut down flights API", "error", err)
			}
			close(done)
		}()

		slog.Info("Flights API listening", "addr", cfg.listenAddr)
		err := httpServer.ListenAndServe()
		if err != nil && !errors.Is(err, http.ErrServerClosed) {
			return err
		}
		<-done
		return nil
	},
}

func main() {
	rootCmd.PersistentFlags().StringVar(&cfg.listenAddr, "listen", ":8085", "HTTP listen address")

	if err := rootCmd.Execute(); err != nil {
		if !errors.Is(err, context.Canceled) {
			slog.Error("flights-api exited with error", "error", err)
		}
		os.Exit(1)
	}
}

type flightServer struct {
	store *flights.Store
}

func newFlightServer(store *flights.Store) *flightServer {
	return &flightServer{store: store}
}

func (s *flightServer) routes() http.Handler {
	r := chi.NewRouter()
	r.Use(middleware.RealIP)
	r.Use(middleware.Logger)
	r.Use(middleware.Recoverer)
	r.Use(corsMiddleware)
	r.Get("/healthz", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) })
	r.Get("/airlines", s.handleListAirlines)
	r.Post("/airlines", s.handleCreateAirline)
	r.Get("/airlines/{airlineId}/flights", s.handleListFlights)
	r.Post("/airlines/{airlineId}/flights", s.handleCreateFlight)
	r.Post("/airlines/{airlineId}/flights/{flightId}/delay", s.handleUpdateStatus(flights.StatusDelayed))
	r.Post("/airlines/{airlineId}/flights/{flightId}/depart", s.handleUpdateStatus(flights.StatusDeparted))
	return r
}

func (s *flightServer) handleListAirlines(w http.ResponseWriter, r *http.Request) {
	writeJSON(w, http.StatusOK, map[string]any{"airlines": s.store.ListAirlines()})
}

type createAirlineRequest struct {
	AirlineID string `json:"airlineId"`
	Name      string `json:"name"`
	Code      string `json:"code"`
}

func (s *flightServer) handleCreateAirline(w http.ResponseWriter, r *http.Request) {
	var body createAirlineRequest
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		respondError(w, http.StatusBadRequest, "invalid JSON body")
		return
	}
	if body.AirlineID == "" || body.Name == "" {
		respondError(w, http.StatusBadRequest, "airlineId and name are required")
		return
	}
	if err := s.store.AddAirline(flights.Airline{AirlineID: body.AirlineID, Name: body.Name, Code: body.Code}); err != nil {
		status, msg := mapStoreError(err)
		respondError(w, status, msg)
		return
	}
	writeJSON(w, http.StatusCreated, map[string]any{"airline": flights.Airline{AirlineID: body.AirlineID, Name: body.Name, Code: body.Code}})
}

func (s *flightServer) handleListFlights(w http.ResponseWriter, r *http.Request) {
	airlineID := chi.URLParam(r, "airlineId")
	flightsList, err := s.store.ListFlights(airlineID)
	if err != nil {
		status, msg := mapStoreError(err)
		respondError(w, status, msg)
		return
	}
	writeJSON(w, http.StatusOK, map[string]any{"flights": flightsList})
}

type createFlightRequest struct {
	FlightID           string `json:"flightId"`
	DepartureTimestamp int64  `json:"departureTimestamp"`
}

func (s *flightServer) handleCreateFlight(w http.ResponseWriter, r *http.Request) {
	airlineID := chi.URLParam(r, "airlineId")
	var body createFlightRequest
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		respondError(w, http.StatusBadRequest, "invalid JSON body")
		return
	}
	if body.FlightID == "" || body.DepartureTimestamp <= 0 {
		respondError(w, http.StatusBadRequest, "flightId and departureTimestamp are required")
		return
	}
	flight := flights.Flight{AirlineID: airlineID, FlightID: body.FlightID, DepartureTimestamp: body.DepartureTimestamp, Status: flights.StatusScheduled}
	created, err := s.store.CreateFlight(airlineID, flight)
	if err != nil {
		status, msg := mapStoreError(err)
		respondError(w, status, msg)
		return
	}
	writeJSON(w, http.StatusCreated, map[string]any{"flight": created})
}

func (s *flightServer) handleUpdateStatus(status flights.Status) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		airlineID := chi.URLParam(r, "airlineId")
		flightID := chi.URLParam(r, "flightId")
		updated, err := s.store.UpdateStatus(airlineID, flightID, status)
		if err != nil {
			statusCode, msg := mapStoreError(err)
			respondError(w, statusCode, msg)
			return
		}
		writeJSON(w, http.StatusOK, map[string]any{"flight": updated})
	}
}

func writeJSON(w http.ResponseWriter, status int, payload any) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)
	if payload == nil {
		return
	}
	if err := json.NewEncoder(w).Encode(payload); err != nil {
		slog.Error("failed to encode response", "error", err)
	}
}

func respondError(w http.ResponseWriter, status int, message string) {
	writeJSON(w, status, map[string]any{"error": message})
}

func mapStoreError(err error) (int, string) {
	switch {
	case errors.Is(err, flights.ErrAirlineNotFound):
		return http.StatusNotFound, err.Error()
	case errors.Is(err, flights.ErrInvalidAirline), errors.Is(err, flights.ErrInvalidFlight):
		return http.StatusBadRequest, err.Error()
	case errors.Is(err, flights.ErrAirlineExists):
		return http.StatusConflict, err.Error()
	case errors.Is(err, flights.ErrFlightExists):
		return http.StatusConflict, err.Error()
	case errors.Is(err, flights.ErrFlightNotFound):
		return http.StatusNotFound, err.Error()
	case errors.Is(err, flights.ErrInvalidStatus), errors.Is(err, flights.ErrInvalidStatusTransition):
		return http.StatusBadRequest, err.Error()
	default:
		return http.StatusInternalServerError, "internal server error"
	}
}

func seedAirlines() []flights.Airline {
	return []flights.Airline{
		{AirlineID: "ALPHA", Name: "Alpha Air", Code: "AA"},
		{AirlineID: "BETA", Name: "Beta Wings", Code: "BW"},
		{AirlineID: "GAMMA", Name: "Gamma Connect", Code: "GC"},
	}
}

func seedFlights() []flights.Flight {
	now := time.Now().Unix()
	toSeconds := func(d time.Duration) int64 { return int64(d.Seconds()) }
	return []flights.Flight{
		{AirlineID: "ALPHA", FlightID: "ALPHA-001", DepartureTimestamp: now + toSeconds(1*time.Minute), Status: flights.StatusScheduled},
		{AirlineID: "ALPHA", FlightID: "ALPHA-002", DepartureTimestamp: now + toSeconds(3*time.Minute), Status: flights.StatusScheduled},
		{AirlineID: "BETA", FlightID: "BETA-451", DepartureTimestamp: now + toSeconds(2*time.Minute), Status: flights.StatusScheduled},
		{AirlineID: "GAMMA", FlightID: "GAMMA-882", DepartureTimestamp: now + toSeconds(4*time.Minute), Status: flights.StatusScheduled},
	}
}

type flightGenerator struct {
	store          *flights.Store
	rand           *rand.Rand
	counters       map[string]int
	createInterval time.Duration
	updateInterval time.Duration
	mu             sync.Mutex
}

func newFlightGenerator(store *flights.Store) *flightGenerator {
	gen := &flightGenerator{
		store:          store,
		rand:           rand.New(rand.NewSource(time.Now().UnixNano())),
		counters:       make(map[string]int),
		createInterval: 45 * time.Second,
		updateInterval: 15 * time.Second,
	}
	gen.bootstrapCounters()
	return gen
}

func (g *flightGenerator) start(ctx context.Context) {
	createTicker := time.NewTicker(g.createInterval)
	updateTicker := time.NewTicker(g.updateInterval)
	go func() {
		defer createTicker.Stop()
		defer updateTicker.Stop()
		for {
			select {
			case <-ctx.Done():
				return
			case <-createTicker.C:
				g.maybeCreateFlights()
			case <-updateTicker.C:
				g.advanceFlights()
			}
		}
	}()
}

func (g *flightGenerator) bootstrapCounters() {
	airlines := g.store.ListAirlines()
	for _, airline := range airlines {
		flightsList, err := g.store.ListFlights(airline.AirlineID)
		if err != nil {
			continue
		}
		maxNum := 0
		for _, f := range flightsList {
			if n := parseFlightNumber(f.FlightID); n > maxNum {
				maxNum = n
			}
		}
		g.counters[airline.AirlineID] = maxNum
	}
}

func (g *flightGenerator) maybeCreateFlights() {
	airlines := g.store.ListAirlines()
	now := time.Now().Unix()
	for _, airline := range airlines {
		flightsList, err := g.store.ListFlights(airline.AirlineID)
		if err != nil {
			continue
		}
		scheduled := 0
		for _, f := range flightsList {
			if f.Status == flights.StatusScheduled && f.DepartureTimestamp > now {
				scheduled++
			}
		}
		if scheduled >= 3 {
			continue
		}
		flightID := g.nextFlightID(airline.AirlineID)
		minutesAhead := time.Duration(g.rand.Intn(4)+1) * time.Minute
		departure := now + int64(minutesAhead.Seconds())
		latest := now
		for _, f := range flightsList {
			if f.DepartureTimestamp > latest {
				latest = f.DepartureTimestamp
			}
		}
		minGap := int64((1 * time.Minute).Seconds())
		if latest >= departure {
			departure = latest + minGap
		}
		_, err = g.store.CreateFlight(airline.AirlineID, flights.Flight{
			AirlineID:          airline.AirlineID,
			FlightID:           flightID,
			DepartureTimestamp: departure,
			Status:             flights.StatusScheduled,
		})
		if err != nil {
			continue
		}
		slog.Info("auto-created flight", "airline", airline.AirlineID, "flight", flightID, "departure", departure)
	}
}

func (g *flightGenerator) advanceFlights() {
	airlines := g.store.ListAirlines()
	now := time.Now().Unix()
	for _, airline := range airlines {
		flightsList, err := g.store.ListFlights(airline.AirlineID)
		if err != nil {
			continue
		}
		for _, f := range flightsList {
			switch f.Status {
			case flights.StatusScheduled:
				if now < f.DepartureTimestamp {
					continue
				}
				if g.rand.Float64() < 0.4 {
					g.updateStatus(airline.AirlineID, f.FlightID, flights.StatusDelayed)
				} else {
					g.updateStatus(airline.AirlineID, f.FlightID, flights.StatusDeparted)
				}
			case flights.StatusDelayed:
				if now >= f.DepartureTimestamp+int64((1*time.Minute).Seconds()) {
					g.updateStatus(airline.AirlineID, f.FlightID, flights.StatusDeparted)
				}
			}
		}
	}
}

func (g *flightGenerator) updateStatus(airlineID, flightID string, status flights.Status) {
	if _, err := g.store.UpdateStatus(airlineID, flightID, status); err == nil {
		slog.Info("auto-updated flight", "airline", airlineID, "flight", flightID, "status", status)
	}
}

func (g *flightGenerator) nextFlightID(airlineID string) string {
	g.mu.Lock()
	defer g.mu.Unlock()
	n := g.counters[airlineID] + 1
	g.counters[airlineID] = n
	return fmt.Sprintf("%s-%03d", strings.ToUpper(airlineID), n)
}

func parseFlightNumber(id string) int {
	idx := strings.LastIndex(id, "-")
	if idx == -1 || idx == len(id)-1 {
		return 0
	}
	n, err := strconv.Atoi(id[idx+1:])
	if err != nil {
		return 0
	}
	return n
}

func corsMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Access-Control-Allow-Origin", "*")
		w.Header().Set("Access-Control-Allow-Methods", "GET,POST,OPTIONS")
		w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
		if r.Method == http.MethodOptions {
			w.WriteHeader(http.StatusNoContent)
			return
		}
		next.ServeHTTP(w, r)
	})
}
```

## File: off-chain/cmd/node/main.go

```go
package main

import (
	"context"
	"crypto/ecdsa"
	"encoding/json"
	"errors"
	"fmt"
	"log/slog"
	"math/big"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"sort"
	"strings"
	"syscall"
	"time"

	"github.com/ethereum/go-ethereum/accounts/abi"
	"github.com/ethereum/go-ethereum/accounts/abi/bind"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/ethclient"
	"github.com/spf13/cobra"
	v1 "github.com/symbioticfi/relay/api/client/v1"

	"sum/internal/contracts"
	"sum/internal/flights"
	"sum/internal/utils"
)

const (
	keyTag    = 15
	maxUint48 = (1 << 48) - 1
)

type config struct {
	relayAPIURL       string
	evmRPCURL         string
	contractAddress   string
	flightsAPIURL     string
	privateKeyHex     string
	pollInterval      time.Duration
	proofPollInterval time.Duration
	logLevel          string
}

var cfg config

var rootCmd = &cobra.Command{
	Use:           "flight-node",
	Short:         "Flight delay oracle node",
	SilenceUsage:  true,
	SilenceErrors: true,
	RunE: func(cmd *cobra.Command, args []string) error {
		switch strings.ToLower(cfg.logLevel) {
		case "debug":
			slog.SetLogLoggerLevel(slog.LevelDebug)
		case "warn":
			slog.SetLogLoggerLevel(slog.LevelWarn)
		case "error":
			slog.SetLogLoggerLevel(slog.LevelError)
		default:
			slog.SetLogLoggerLevel(slog.LevelInfo)
		}

		ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
		defer cancel()

		conn, err := utils.GetGRPCConnection(cfg.relayAPIURL)
		if err != nil {
			return fmt.Errorf("create relay client: %w", err)
		}
		defer conn.Close()

		relayClient := v1.NewSymbioticClient(conn)

		evmClient, err := ethclient.DialContext(ctx, cfg.evmRPCURL)
		if err != nil {
			return fmt.Errorf("dial evm rpc: %w", err)
		}
		defer evmClient.Close()

		chainID, err := evmClient.ChainID(ctx)
		if err != nil {
			return fmt.Errorf("fetch chain id: %w", err)
		}

		contractAddr := common.HexToAddress(cfg.contractAddress)
		flightDelays, err := contracts.NewFlightDelays(contractAddr, evmClient)
		if err != nil {
			return fmt.Errorf("bind flight delays: %w", err)
		}

		flightsClient := newFlightsAPIClient(cfg.flightsAPIURL)

		privKey, err := crypto.HexToECDSA(strings.TrimPrefix(cfg.privateKeyHex, "0x"))
		if err != nil {
			return fmt.Errorf("parse private key: %w", err)
		}

		node := &flightNode{
			relayClient: relayClient,
			ethClient:   evmClient,
			contract:    flightDelays,
			chainID:     chainID,
			privateKey:  privKey,
			flightsAPI:  flightsClient,
			pending:     make(map[string]*pendingAction),
		}

		if err := node.syncFlights(ctx); err != nil {
			slog.Warn("initial sync failed", "error", err)
		}

		pollTicker := time.NewTicker(cfg.pollInterval)
		defer pollTicker.Stop()
		proofTicker := time.NewTicker(cfg.proofPollInterval)
		defer proofTicker.Stop()

		for {
			select {
			case <-pollTicker.C:
				if err := node.syncFlights(ctx); err != nil {
					slog.Warn("sync flights failed", "error", err)
				}
			case <-proofTicker.C:
				if err := node.fetchProofs(ctx); err != nil {
					slog.Warn("fetch proofs failed", "error", err)
				}
				if err := node.submitReadyActions(ctx); err != nil {
					slog.Warn("submit actions failed", "error", err)
				}
			case <-ctx.Done():
				slog.Info("shutting down flight node")
				return nil
			}
		}
	},
}

func main() {
	rootCmd.PersistentFlags().StringVar(&cfg.relayAPIURL, "relay-api-url", "", "Relay API URL")
	rootCmd.PersistentFlags().StringVar(&cfg.evmRPCURL, "evm-rpc-url", "", "Execution client RPC URL")
	rootCmd.PersistentFlags().StringVar(&cfg.contractAddress, "flight-delays-address", "", "FlightDelays contract address")
	rootCmd.PersistentFlags().StringVar(&cfg.flightsAPIURL, "flights-api-url", "", "Mock flights API URL")
	rootCmd.PersistentFlags().StringVar(&cfg.privateKeyHex, "private-key", "", "Flight oracle ECDSA private key")
	rootCmd.PersistentFlags().DurationVar(&cfg.pollInterval, "poll-interval", 5*time.Second, "Polling interval for flights API")
	rootCmd.PersistentFlags().DurationVar(&cfg.proofPollInterval, "proof-poll-interval", 3*time.Second, "Polling interval for settlement proofs")
	rootCmd.PersistentFlags().StringVar(&cfg.logLevel, "log-level", "info", "Log level (debug,info,warn,error)")

	_ = rootCmd.MarkPersistentFlagRequired("relay-api-url")
	_ = rootCmd.MarkPersistentFlagRequired("evm-rpc-url")
	_ = rootCmd.MarkPersistentFlagRequired("flight-delays-address")
	_ = rootCmd.MarkPersistentFlagRequired("flights-api-url")
	_ = rootCmd.MarkPersistentFlagRequired("private-key")

	if err := rootCmd.Execute(); err != nil {
		slog.Error("node failed", "error", err)
		os.Exit(1)
	}
}

var (
	bytes32Type     = mustABIType("bytes32")
	uint48Type      = mustABIType("uint48")
	outerArgs       = abi.Arguments{{Type: bytes32Type}}
	createInnerArgs = abi.Arguments{{Type: bytes32Type}, {Type: bytes32Type}, {Type: bytes32Type}, {Type: uint48Type}, {Type: bytes32Type}}
	statusInnerArgs = abi.Arguments{{Type: bytes32Type}, {Type: bytes32Type}, {Type: bytes32Type}}
	createTypehash  = crypto.Keccak256Hash([]byte("Create(bytes32 airlineId,bytes32 flightId,uint48 departure,bytes32 previousFlightId)"))
	delayTypehash   = crypto.Keccak256Hash([]byte("Delay(bytes32 airlineId,bytes32 flightId)"))
	departTypehash  = crypto.Keccak256Hash([]byte("Depart(bytes32 airlineId,bytes32 flightId)"))
)

func mustABIType(name string) abi.Type {
	t, err := abi.NewType(name, "", nil)
	if err != nil {
		panic(err)
	}
	return t
}

type actionType string

type flightStatus uint8

const (
	actionCreate actionType = "CREATE"
	actionDelay  actionType = "DELAY"
	actionDepart actionType = "DEPART"

	statusNone      flightStatus = 0
	statusScheduled flightStatus = 1
	statusDelayed   flightStatus = 2
	statusDeparted  flightStatus = 3
)

type pendingAction struct {
	Key                string
	Airline            flights.Airline
	Flight             flights.Flight
	AirlineHash        common.Hash
	FlightHash         common.Hash
	PreviousFlightHash common.Hash
	Type               actionType
	Epoch              uint64
	RequestID          string
	Proof              []byte
	Submitted          bool
	TargetStatus       flightStatus
	TxHash             common.Hash
	CreatedAt          time.Time
}

type flightNode struct {
	relayClient *v1.SymbioticClient
	ethClient   *ethclient.Client
	contract    *contracts.FlightDelays
	chainID     *big.Int
	privateKey  *ecdsa.PrivateKey
	flightsAPI  *flightsAPIClient

	pending map[string]*pendingAction
}

func (n *flightNode) syncFlights(ctx context.Context) error {
	airlines, err := n.flightsAPI.ListAirlines(ctx)
	if err != nil {
		return fmt.Errorf("list airlines: %w", err)
	}
	for _, airline := range airlines {
		flightsForAirline, err := n.flightsAPI.ListFlights(ctx, airline.AirlineID)
		if err != nil {
			slog.Warn("list flights failed", "airline", airline.AirlineID, "error", err)
			continue
		}
		sort.SliceStable(flightsForAirline, func(i, j int) bool {
			if flightsForAirline[i].DepartureTimestamp == flightsForAirline[j].DepartureTimestamp {
				return flightsForAirline[i].FlightID < flightsForAirline[j].FlightID
			}
			return flightsForAirline[i].DepartureTimestamp < flightsForAirline[j].DepartureTimestamp
		})
		if err := n.evaluateAirlineFlights(ctx, airline, flightsForAirline); err != nil {
			slog.Warn("evaluate airline failed", "airline", airline.AirlineID, "error", err)
		}
	}
	return nil
}

func (n *flightNode) evaluateAirlineFlights(ctx context.Context, airline flights.Airline, flightsForAirline []flights.Flight) error {
	airlineHash := hashIdentifier(airline.AirlineID)
	prevMap := make(map[string]common.Hash, len(flightsForAirline))
	var prev common.Hash
	for _, flight := range flightsForAirline {
		prevMap[flight.FlightID] = prev
		prev = hashIdentifier(flight.FlightID)
	}

	for _, flight := range flightsForAirline {
		prevHash := prevMap[flight.FlightID]
		if err := n.evaluateFlight(ctx, airline, flight, airlineHash, prevHash); err != nil {
			slog.Warn("evaluate flight failed", "airline", airline.AirlineID, "flight", flight.FlightID, "error", err)
		}
	}
	return nil
}

func (n *flightNode) evaluateFlight(ctx context.Context, airline flights.Airline, flight flights.Flight, airlineHash common.Hash, previousFlightHash common.Hash) error {
	flightHash := hashIdentifier(flight.FlightID)

	info, err := n.contract.Flights(&bind.CallOpts{Context: ctx}, airlineHash, flightHash)
	if err != nil {
		return fmt.Errorf("read flight state: %w", err)
	}

	onChainStatus := flightStatus(info.Status)
	n.clearSatisfiedPending(airlineHash, flightHash, onChainStatus)

	nextAction, ok := determineAction(flight.Status, onChainStatus)
	if !ok {
		return nil
	}

	key := actionKey(airlineHash, flightHash, nextAction)
	if _, exists := n.pending[key]; exists {
		return nil
	}

	if err := n.enqueueAction(ctx, key, nextAction, airline, flight, airlineHash, flightHash, previousFlightHash); err != nil {
		return err
	}
	return nil
}

func determineAction(apiStatus flights.Status, onChain flightStatus) (actionType, bool) {
	switch onChain {
	case statusNone:
		if apiStatus == flights.StatusScheduled || apiStatus == flights.StatusDelayed || apiStatus == flights.StatusDeparted {
			return actionCreate, true
		}
	case statusScheduled:
		switch apiStatus {
		case flights.StatusDelayed:
			return actionDelay, true
		case flights.StatusDeparted:
			return actionDepart, true
		}
	}
	return "", false
}

func (n *flightNode) enqueueAction(ctx context.Context, key string, action actionType, airline flights.Airline, flight flights.Flight, airlineHash, flightHash, previousFlightHash common.Hash) error {
	if action == actionCreate && flight.DepartureTimestamp <= 0 {
		return fmt.Errorf("flight %s has invalid departure timestamp", flight.FlightID)
	}
	payload, err := buildMessagePayload(action, airlineHash, flightHash, previousFlightHash, uint64(flight.DepartureTimestamp))
	if err != nil {
		return err
	}

	epoch, requestID, err := n.requestSignature(ctx, payload)
	if err != nil {
		return fmt.Errorf("sign message: %w", err)
	}

	pending := &pendingAction{
		Key:                key,
		Airline:            airline,
		Flight:             flight,
		AirlineHash:        airlineHash,
		FlightHash:         flightHash,
		PreviousFlightHash: previousFlightHash,
		Type:               action,
		Epoch:              epoch,
		RequestID:          requestID,
		TargetStatus:       targetStatusFor(action),
		CreatedAt:          time.Now(),
	}
	n.pending[key] = pending

	slog.Info("scheduled flight action", "airline", airline.AirlineID, "flight", flight.FlightID, "action", string(action), "epoch", epoch)
	return nil
}

func (n *flightNode) requestSignature(ctx context.Context, payload []byte) (uint64, string, error) {
	epochInfos, err := n.relayClient.GetLastAllCommitted(ctx, &v1.GetLastAllCommittedRequest{})
	if err != nil {
		return 0, "", fmt.Errorf("last committed: %w", err)
	}
	var suggestedEpoch uint64
	for _, info := range epochInfos.EpochInfos {
		last := info.GetLastCommittedEpoch()
		if suggestedEpoch == 0 || last < suggestedEpoch {
			suggestedEpoch = last
		}
	}

	resp, err := n.relayClient.SignMessage(ctx, &v1.SignMessageRequest{
		KeyTag:        keyTag,
		Message:       payload,
		RequiredEpoch: &suggestedEpoch,
	})
	if err != nil {
		return 0, "", err
	}
	return resp.Epoch, resp.RequestId, nil
}

func (n *flightNode) fetchProofs(ctx context.Context) error {
	for _, action := range n.pending {
		if action.Proof != nil {
			continue
		}
		resp, err := n.relayClient.GetAggregationProof(ctx, &v1.GetAggregationProofRequest{RequestId: action.RequestID})
		if err != nil {
			continue
		}
		if resp.GetAggregationProof() == nil {
			continue
		}
		action.Proof = resp.AggregationProof.Proof
		slog.Info("aggregation proof ready", "airline", action.Airline.AirlineID, "flight", action.Flight.FlightID, "action", string(action.Type))
	}
	return nil
}

func (n *flightNode) submitReadyActions(ctx context.Context) error {
	for key, action := range n.pending {
		if action.Proof == nil || action.Submitted {
			continue
		}
		if action.Type == actionCreate {
			ready, err := n.canSubmitCreate(ctx, action)
			if err != nil {
				slog.Warn("check create readiness failed", "airline", action.Airline.AirlineID, "flight", action.Flight.FlightID, "error", err)
				continue
			}
			if !ready {
				continue
			}
		}
		if err := n.submitAction(ctx, action); err != nil {
			slog.Warn("submit action failed", "airline", action.Airline.AirlineID, "flight", action.Flight.FlightID, "action", string(action.Type), "error", err)
			continue
		}
		n.pending[key] = action
	}
	return nil
}

func (n *flightNode) submitAction(ctx context.Context, action *pendingAction) error {
	txOpts, err := bind.NewKeyedTransactorWithChainID(n.privateKey, n.chainID)
	if err != nil {
		return err
	}
	txOpts.Context = ctx

	epoch := big.NewInt(int64(action.Epoch))
	var txHash common.Hash

	switch action.Type {
	case actionCreate:
		scheduled := big.NewInt(action.Flight.DepartureTimestamp)
		prev := [32]byte(action.PreviousFlightHash)
		tx, err := n.contract.CreateFlight(txOpts, action.AirlineHash, action.FlightHash, scheduled, prev, epoch, action.Proof)
		if err != nil {
			return err
		}
		txHash = tx.Hash()
	case actionDelay:
		tx, err := n.contract.DelayFlight(txOpts, action.AirlineHash, action.FlightHash, epoch, action.Proof)
		if err != nil {
			return err
		}
		txHash = tx.Hash()
	case actionDepart:
		tx, err := n.contract.DepartFlight(txOpts, action.AirlineHash, action.FlightHash, epoch, action.Proof)
		if err != nil {
			return err
		}
		txHash = tx.Hash()
	default:
		return fmt.Errorf("unknown action %s", action.Type)
	}

	action.Submitted = true
	action.TxHash = txHash
	slog.Info("submitted flight action", "airline", action.Airline.AirlineID, "flight", action.Flight.FlightID, "action", string(action.Type), "tx", txHash.Hex())
	return nil
}

func (n *flightNode) canSubmitCreate(ctx context.Context, action *pendingAction) (bool, error) {
	airlineState, err := n.contract.Airlines(&bind.CallOpts{Context: ctx}, action.AirlineHash)
	if err != nil {
		return false, fmt.Errorf("read airline state: %w", err)
	}
	current := common.BytesToHash(airlineState.LastFlightId[:])
	return current == action.PreviousFlightHash, nil
}

func (n *flightNode) clearSatisfiedPending(airlineHash, flightHash common.Hash, status flightStatus) {
	for key, action := range n.pending {
		if action.AirlineHash == airlineHash && action.FlightHash == flightHash && action.TargetStatus == status {
			slog.Info("action confirmed on-chain", "airline", action.Airline.AirlineID, "flight", action.Flight.FlightID, "action", string(action.Type))
			delete(n.pending, key)
		}
	}
}

func targetStatusFor(action actionType) flightStatus {
	switch action {
	case actionCreate:
		return statusScheduled
	case actionDelay:
		return statusDelayed
	case actionDepart:
		return statusDeparted
	default:
		return statusNone
	}
}

func buildMessagePayload(action actionType, airlineHash, flightHash, previousFlightHash common.Hash, departure uint64) ([]byte, error) {
	var inner []byte
	var err error
	switch action {
	case actionCreate:
		if departure == 0 || departure > maxUint48 {
			return nil, fmt.Errorf("invalid departure timestamp %d", departure)
		}
		inner, err = createInnerArgs.Pack(createTypehash, airlineHash, flightHash, big.NewInt(int64(departure)), previousFlightHash)
	case actionDelay:
		inner, err = statusInnerArgs.Pack(delayTypehash, airlineHash, flightHash)
	case actionDepart:
		inner, err = statusInnerArgs.Pack(departTypehash, airlineHash, flightHash)
	default:
		return nil, errors.New("unsupported action")
	}
	if err != nil {
		return nil, err
	}
	return inner, nil
}

func hashIdentifier(id string) common.Hash {
	normalized := strings.ToUpper(strings.TrimSpace(id))
	return crypto.Keccak256Hash([]byte(normalized))
}

func actionKey(airlineHash, flightHash common.Hash, action actionType) string {
	return fmt.Sprintf("%s|%s|%s", airlineHash.Hex(), flightHash.Hex(), action)
}

type flightsAPIClient struct {
	baseURL    string
	httpClient *http.Client
}

func newFlightsAPIClient(baseURL string) *flightsAPIClient {
	return &flightsAPIClient{
		baseURL:    strings.TrimSuffix(baseURL, "/"),
		httpClient: &http.Client{Timeout: 5 * time.Second},
	}
}

func (c *flightsAPIClient) ListAirlines(ctx context.Context) ([]flights.Airline, error) {
	req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.baseURL+"/airlines", nil)
	if err != nil {
		return nil, err
	}
	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode >= 300 {
		return nil, fmt.Errorf("airlines request status %d", resp.StatusCode)
	}
	var body struct {
		Airlines []flights.Airline `json:"airlines"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
		return nil, err
	}
	return body.Airlines, nil
}

func (c *flightsAPIClient) ListFlights(ctx context.Context, airlineID string) ([]flights.Flight, error) {
	endpoint := fmt.Sprintf("%s/airlines/%s/flights", c.baseURL, url.PathEscape(airlineID))
	req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
	if err != nil {
		return nil, err
	}
	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode >= 300 {
		return nil, fmt.Errorf("flights request status %d", resp.StatusCode)
	}
	var body struct {
		Flights []flights.Flight `json:"flights"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
		return nil, err
	}
	return body.Flights, nil
}
```

## File: off-chain/internal/contracts/flightDelays.go

```go
// Code generated - DO NOT EDIT.
// This file is a generated binding and any manual changes will be lost.

package contracts

import (
	"errors"
	"math/big"
	"strings"

	ethereum "github.com/ethereum/go-ethereum"
	"github.com/ethereum/go-ethereum/accounts/abi"
	"github.com/ethereum/go-ethereum/accounts/abi/bind"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/event"
)

// Reference imports to suppress errors if they are not otherwise used.
var (
	_ = errors.New
	_ = big.NewInt
	_ = strings.NewReader
	_ = ethereum.NotFound
	_ = bind.Bind
	_ = common.Big1
	_ = types.BloomLookup
	_ = event.NewSubscription
	_ = abi.ConvertType
)

// FlightDelaysInitParams is an auto generated low-level Go binding around an user-defined struct.
type FlightDelaysInitParams struct {
	VotingPowers       common.Address
	Settlement         common.Address
	Collateral         common.Address
	VaultEpochDuration *big.Int
	MessageExpiry      uint32
	PolicyWindow       *big.Int
	DelayWindow        *big.Int
	PolicyPremium      *big.Int
	PolicyPayout       *big.Int
}

// FlightDelaysMetaData contains all meta data concerning the FlightDelays contract.
var FlightDelaysMetaData = &bind.MetaData{
	ABI: "[{\"type\":\"constructor\",\"inputs\":[{\"name\":\"vaultConfigurator\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"operatorVaultOptInService\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"operatorNetworkOptInService\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"defaultStakerRewardsFactory\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"operatorRegistry\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"DEFAULT_STAKER_REWARDS_FACTORY\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"NETWORK\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"OPERATOR_NETWORK_OPT_IN_SERVICE\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"OPERATOR_VAULT_OPT_IN_SERVICE\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"SUBNETWORK\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"SUBNETWORK_IDENTIFIER\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint96\",\"internalType\":\"uint96\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"VAULT_CONFIGURATOR\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"airlines\",\"inputs\":[{\"name\":\"airlineId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[{\"name\":\"vault\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"rewards\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"covered\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"lastFlightId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"buyInsurance\",\"inputs\":[{\"name\":\"airlineId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"flightId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"claimInsurance\",\"inputs\":[{\"name\":\"airlineId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"flightId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"collateral\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"createFlight\",\"inputs\":[{\"name\":\"airlineId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"flightId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"scheduledTimestamp\",\"type\":\"uint48\",\"internalType\":\"uint48\"},{\"name\":\"previousFlightId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"epoch\",\"type\":\"uint48\",\"internalType\":\"uint48\"},{\"name\":\"proof\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"delayFlight\",\"inputs\":[{\"name\":\"airlineId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"flightId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"epoch\",\"type\":\"uint48\",\"internalType\":\"uint48\"},{\"name\":\"proof\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"delayWindow\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint48\",\"internalType\":\"uint48\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"departFlight\",\"inputs\":[{\"name\":\"airlineId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"flightId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"epoch\",\"type\":\"uint48\",\"internalType\":\"uint48\"},{\"name\":\"proof\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"flights\",\"inputs\":[{\"name\":\"airlineId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"flightId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[{\"name\":\"timestamp\",\"type\":\"uint48\",\"internalType\":\"uint48\"},{\"name\":\"status\",\"type\":\"uint8\",\"internalType\":\"enumFlightDelays.FlightStatus\"},{\"name\":\"policiesSold\",\"type\":\"uint128\",\"internalType\":\"uint128\"},{\"name\":\"previousFlightId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"initialize\",\"inputs\":[{\"name\":\"initParams\",\"type\":\"tuple\",\"internalType\":\"structFlightDelays.InitParams\",\"components\":[{\"name\":\"votingPowers\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"settlement\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"collateral\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"vaultEpochDuration\",\"type\":\"uint48\",\"internalType\":\"uint48\"},{\"name\":\"messageExpiry\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"policyWindow\",\"type\":\"uint48\",\"internalType\":\"uint48\"},{\"name\":\"delayWindow\",\"type\":\"uint48\",\"internalType\":\"uint48\"},{\"name\":\"policyPremium\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"policyPayout\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"messageExpiry\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"policies\",\"inputs\":[{\"name\":\"airlineId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"flightId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"buyer\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"policyStatus\",\"type\":\"uint8\",\"internalType\":\"enumFlightDelays.PolicyStatus\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"policyPayout\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"policyPremium\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"policyWindow\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint48\",\"internalType\":\"uint48\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"settlement\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"staticDelegateCall\",\"inputs\":[{\"name\":\"target\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"vaultEpochDuration\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint48\",\"internalType\":\"uint48\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"votingPowers\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"event\",\"name\":\"AirlineVaultDeployed\",\"inputs\":[{\"name\":\"airlineId\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"vault\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"},{\"name\":\"rewards\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"FlightCreated\",\"inputs\":[{\"name\":\"airlineId\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"flightId\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"scheduledTimestamp\",\"type\":\"uint48\",\"indexed\":false,\"internalType\":\"uint48\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"FlightDelayed\",\"inputs\":[{\"name\":\"airlineId\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"flightId\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"FlightDeparted\",\"inputs\":[{\"name\":\"airlineId\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"flightId\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"InitSubnetwork\",\"inputs\":[{\"name\":\"network\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"},{\"name\":\"subnetworkId\",\"type\":\"uint96\",\"indexed\":false,\"internalType\":\"uint96\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Initialized\",\"inputs\":[{\"name\":\"version\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"InsuranceClaimed\",\"inputs\":[{\"name\":\"airlineId\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"flightId\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"buyer\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"payout\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"InsurancePurchased\",\"inputs\":[{\"name\":\"airlineId\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"flightId\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"buyer\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"premium\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"error\",\"name\":\"BuyWindowClosed\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"FlightAlreadyExists\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"FlightNotDelayable\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"FlightNotDelayed\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"FlightNotScheduled\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InsufficientCoverage\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidEpoch\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidFlight\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidInitialization\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidMessageSignature\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidPolicy\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidTimestamp\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"NetworkManager_InvalidNetwork\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"NotInitializing\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"PolicyAlreadyPurchased\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"PolicyNotFound\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"PreviousFlightIncomplete\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"SafeERC20FailedOperation\",\"inputs\":[{\"name\":\"token\",\"type\":\"address\",\"internalType\":\"address\"}]}]",
}

// FlightDelaysABI is the input ABI used to generate the binding from.
// Deprecated: Use FlightDelaysMetaData.ABI instead.
var FlightDelaysABI = FlightDelaysMetaData.ABI

// FlightDelays is an auto generated Go binding around an Ethereum contract.
type FlightDelays struct {
	FlightDelaysCaller     // Read-only binding to the contract
	FlightDelaysTransactor // Write-only binding to the contract
	FlightDelaysFilterer   // Log filterer for contract events
}

// FlightDelaysCaller is an auto generated read-only Go binding around an Ethereum contract.
type FlightDelaysCaller struct {
	contract *bind.BoundContract // Generic contract wrapper for the low level calls
}

// FlightDelaysTransactor is an auto generated write-only Go binding around an Ethereum contract.
type FlightDelaysTransactor struct {
	contract *bind.BoundContract // Generic contract wrapper for the low level calls
}

// FlightDelaysFilterer is an auto generated log filtering Go binding around an Ethereum contract events.
type FlightDelaysFilterer struct {
	contract *bind.BoundContract // Generic contract wrapper for the low level calls
}

// FlightDelaysSession is an auto generated Go binding around an Ethereum contract,
// with pre-set call and transact options.
type FlightDelaysSession struct {
	Contract     *FlightDelays     // Generic contract binding to set the session for
	CallOpts     bind.CallOpts     // Call options to use throughout this session
	TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
}

// FlightDelaysCallerSession is an auto generated read-only Go binding around an Ethereum contract,
// with pre-set call options.
type FlightDelaysCallerSession struct {
	Contract *FlightDelaysCaller // Generic contract caller binding to set the session for
	CallOpts bind.CallOpts       // Call options to use throughout this session
}

// FlightDelaysTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
// with pre-set transact options.
type FlightDelaysTransactorSession struct {
	Contract     *FlightDelaysTransactor // Generic contract transactor binding to set the session for
	TransactOpts bind.TransactOpts       // Transaction auth options to use throughout this session
}

// FlightDelaysRaw is an auto generated low-level Go binding around an Ethereum contract.
type FlightDelaysRaw struct {
	Contract *FlightDelays // Generic contract binding to access the raw methods on
}

// FlightDelaysCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
type FlightDelaysCallerRaw struct {
	Contract *FlightDelaysCaller // Generic read-only contract binding to access the raw methods on
}

// FlightDelaysTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
type FlightDelaysTransactorRaw struct {
	Contract *FlightDelaysTransactor // Generic write-only contract binding to access the raw methods on
}

// NewFlightDelays creates a new instance of FlightDelays, bound to a specific deployed contract.
func NewFlightDelays(address common.Address, backend bind.ContractBackend) (*FlightDelays, error) {
	contract, err := bindFlightDelays(address, backend, backend, backend)
	if err != nil {
		return nil, err
	}
	return &FlightDelays{FlightDelaysCaller: FlightDelaysCaller{contract: contract}, FlightDelaysTransactor: FlightDelaysTransactor{contract: contract}, FlightDelaysFilterer: FlightDelaysFilterer{contract: contract}}, nil
}

// NewFlightDelaysCaller creates a new read-only instance of FlightDelays, bound to a specific deployed contract.
func NewFlightDelaysCaller(address common.Address, caller bind.ContractCaller) (*FlightDelaysCaller, error) {
	contract, err := bindFlightDelays(address, caller, nil, nil)
	if err != nil {
		return nil, err
	}
	return &FlightDelaysCaller{contract: contract}, nil
}

// NewFlightDelaysTransactor creates a new write-only instance of FlightDelays, bound to a specific deployed contract.
func NewFlightDelaysTransactor(address common.Address, transactor bind.ContractTransactor) (*FlightDelaysTransactor, error) {
	contract, err := bindFlightDelays(address, nil, transactor, nil)
	if err != nil {
		return nil, err
	}
	return &FlightDelaysTransactor{contract: contract}, nil
}

// NewFlightDelaysFilterer creates a new log filterer instance of FlightDelays, bound to a specific deployed contract.
func NewFlightDelaysFilterer(address common.Address, filterer bind.ContractFilterer) (*FlightDelaysFilterer, error) {
	contract, err := bindFlightDelays(address, nil, nil, filterer)
	if err != nil {
		return nil, err
	}
	return &FlightDelaysFilterer{contract: contract}, nil
}

// bindFlightDelays binds a generic wrapper to an already deployed contract.
func bindFlightDelays(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
	parsed, err := FlightDelaysMetaData.GetAbi()
	if err != nil {
		return nil, err
	}
	return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil
}

// Call invokes the (constant) contract method with params as input values and
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named
// returns.
func (_FlightDelays *FlightDelaysRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
	return _FlightDelays.Contract.FlightDelaysCaller.contract.Call(opts, result, method, params...)
}

// Transfer initiates a plain transaction to move funds to the contract, calling
// its default method if one is available.
func (_FlightDelays *FlightDelaysRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
	return _FlightDelays.Contract.FlightDelaysTransactor.contract.Transfer(opts)
}

// Transact invokes the (paid) contract method with params as input values.
func (_FlightDelays *FlightDelaysRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
	return _FlightDelays.Contract.FlightDelaysTransactor.contract.Transact(opts, method, params...)
}

// Call invokes the (constant) contract method with params as input values and
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named
// returns.
func (_FlightDelays *FlightDelaysCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
	return _FlightDelays.Contract.contract.Call(opts, result, method, params...)
}

// Transfer initiates a plain transaction to move funds to the contract, calling
// its default method if one is available.
func (_FlightDelays *FlightDelaysTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
	return _FlightDelays.Contract.contract.Transfer(opts)
}

// Transact invokes the (paid) contract method with params as input values.
func (_FlightDelays *FlightDelaysTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
	return _FlightDelays.Contract.contract.Transact(opts, method, params...)
}

// DEFAULTSTAKERREWARDSFACTORY is a free data retrieval call binding the contract method 0x360d7094.
//
// Solidity: function DEFAULT_STAKER_REWARDS_FACTORY() view returns(address)
func (_FlightDelays *FlightDelaysCaller) DEFAULTSTAKERREWARDSFACTORY(opts *bind.CallOpts) (common.Address, error) {
	var out []interface{}
	err := _FlightDelays.contract.Call(opts, &out, "DEFAULT_STAKER_REWARDS_FACTORY")

	if err != nil {
		return *new(common.Address), err
	}

	out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address)

	return out0, err

}

// DEFAULTSTAKERREWARDSFACTORY is a free data retrieval call binding the contract method 0x360d7094.
//
// Solidity: function DEFAULT_STAKER_REWARDS_FACTORY() view returns(address)
func (_FlightDelays *FlightDelaysSession) DEFAULTSTAKERREWARDSFACTORY() (common.Address, error) {
	return _FlightDelays.Contract.DEFAULTSTAKERREWARDSFACTORY(&_FlightDelays.CallOpts)
}

// DEFAULTSTAKERREWARDSFACTORY is a free data retrieval call binding the contract method 0x360d7094.
//
// Solidity: function DEFAULT_STAKER_REWARDS_FACTORY() view returns(address)
func (_FlightDelays *FlightDelaysCallerSession) DEFAULTSTAKERREWARDSFACTORY() (common.Address, error) {
	return _FlightDelays.Contract.DEFAULTSTAKERREWARDSFACTORY(&_FlightDelays.CallOpts)
}

// NETWORK is a free data retrieval call binding the contract method 0x8759e6d1.
//
// Solidity: function NETWORK() view returns(address)
func (_FlightDelays *FlightDelaysCaller) NETWORK(opts *bind.CallOpts) (common.Address, error) {
	var out []interface{}
	err := _FlightDelays.contract.Call(opts, &out, "NETWORK")

	if err != nil {
		return *new(common.Address), err
	}

	out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address)

	return out0, err

}

// NETWORK is a free data retrieval call binding the contract method 0x8759e6d1.
//
// Solidity: function NETWORK() view returns(address)
func (_FlightDelays *FlightDelaysSession) NETWORK() (common.Address, error) {
	return _FlightDelays.Contract.NETWORK(&_FlightDelays.CallOpts)
}

// NETWORK is a free data retrieval call binding the contract method 0x8759e6d1.
//
// Solidity: function NETWORK() view returns(address)
func (_FlightDelays *FlightDelaysCallerSession) NETWORK() (common.Address, error) {
	return _FlightDelays.Contract.NETWORK(&_FlightDelays.CallOpts)
}

// OPERATORNETWORKOPTINSERVICE is a free data retrieval call binding the contract method 0x1a80e500.
//
// Solidity: function OPERATOR_NETWORK_OPT_IN_SERVICE() view returns(address)
func (_FlightDelays *FlightDelaysCaller) OPERATORNETWORKOPTINSERVICE(opts *bind.CallOpts) (common.Address, error) {
	var out []interface{}
	err := _FlightDelays.contract.Call(opts, &out, "OPERATOR_NETWORK_OPT_IN_SERVICE")

	if err != nil {
		return *new(common.Address), err
	}

	out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address)

	return out0, err

}

// OPERATORNETWORKOPTINSERVICE is a free data retrieval call binding the contract method 0x1a80e500.
//
// Solidity: function OPERATOR_NETWORK_OPT_IN_SERVICE() view returns(address)
func (_FlightDelays *FlightDelaysSession) OPERATORNETWORKOPTINSERVICE() (common.Address, error) {
	return _FlightDelays.Contract.OPERATORNETWORKOPTINSERVICE(&_FlightDelays.CallOpts)
}

// OPERATORNETWORKOPTINSERVICE is a free data retrieval call binding the contract method 0x1a80e500.
//
// Solidity: function OPERATOR_NETWORK_OPT_IN_SERVICE() view returns(address)
func (_FlightDelays *FlightDelaysCallerSession) OPERATORNETWORKOPTINSERVICE() (common.Address, error) {
	return _FlightDelays.Contract.OPERATORNETWORKOPTINSERVICE(&_FlightDelays.CallOpts)
}

// OPERATORVAULTOPTINSERVICE is a free data retrieval call binding the contract method 0x128e5d82.
//
// Solidity: function OPERATOR_VAULT_OPT_IN_SERVICE() view returns(address)
func (_FlightDelays *FlightDelaysCaller) OPERATORVAULTOPTINSERVICE(opts *bind.CallOpts) (common.Address, error) {
	var out []interface{}
	err := _FlightDelays.contract.Call(opts, &out, "OPERATOR_VAULT_OPT_IN_SERVICE")

	if err != nil {
		return *new(common.Address), err
	}

	out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address)

	return out0, err

}

// OPERATORVAULTOPTINSERVICE is a free data retrieval call binding the contract method 0x128e5d82.
//
// Solidity: function OPERATOR_VAULT_OPT_IN_SERVICE() view returns(address)
func (_FlightDelays *FlightDelaysSession) OPERATORVAULTOPTINSERVICE() (common.Address, error) {
	return _FlightDelays.Contract.OPERATORVAULTOPTINSERVICE(&_FlightDelays.CallOpts)
}

// OPERATORVAULTOPTINSERVICE is a free data retrieval call binding the contract method 0x128e5d82.
//
// Solidity: function OPERATOR_VAULT_OPT_IN_SERVICE() view returns(address)
func (_FlightDelays *FlightDelaysCallerSession) OPERATORVAULTOPTINSERVICE() (common.Address, error) {
	return _FlightDelays.Contract.OPERATORVAULTOPTINSERVICE(&_FlightDelays.CallOpts)
}

// SUBNETWORK is a free data retrieval call binding the contract method 0x773e6b54.
//
// Solidity: function SUBNETWORK() view returns(bytes32)
func (_FlightDelays *FlightDelaysCaller) SUBNETWORK(opts *bind.CallOpts) ([32]byte, error) {
	var out []interface{}
	err := _FlightDelays.contract.Call(opts, &out, "SUBNETWORK")

	if err != nil {
		return *new([32]byte), err
	}

	out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte)

	return out0, err

}

// SUBNETWORK is a free data retrieval call binding the contract method 0x773e6b54.
//
// Solidity: function SUBNETWORK() view returns(bytes32)
func (_FlightDelays *FlightDelaysSession) SUBNETWORK() ([32]byte, error) {
	return _FlightDelays.Contract.SUBNETWORK(&_FlightDelays.CallOpts)
}

// SUBNETWORK is a free data retrieval call binding the contract method 0x773e6b54.
//
// Solidity: function SUBNETWORK() view returns(bytes32)
func (_FlightDelays *FlightDelaysCallerSession) SUBNETWORK() ([32]byte, error) {
	return _FlightDelays.Contract.SUBNETWORK(&_FlightDelays.CallOpts)
}

// SUBNETWORKIDENTIFIER is a free data retrieval call binding the contract method 0xabacb807.
//
// Solidity: function SUBNETWORK_IDENTIFIER() view returns(uint96)
func (_FlightDelays *FlightDelaysCaller) SUBNETWORKIDENTIFIER(opts *bind.CallOpts) (*big.Int, error) {
	var out []interface{}
	err := _FlightDelays.contract.Call(opts, &out, "SUBNETWORK_IDENTIFIER")

	if err != nil {
		return *new(*big.Int), err
	}

	out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)

	return out0, err

}

// SUBNETWORKIDENTIFIER is a free data retrieval call binding the contract method 0xabacb807.
//
// Solidity: function SUBNETWORK_IDENTIFIER() view returns(uint96)
func (_FlightDelays *FlightDelaysSession) SUBNETWORKIDENTIFIER() (*big.Int, error) {
	return _FlightDelays.Contract.SUBNETWORKIDENTIFIER(&_FlightDelays.CallOpts)
}

// SUBNETWORKIDENTIFIER is a free data retrieval call binding the contract method 0xabacb807.
//
// Solidity: function SUBNETWORK_IDENTIFIER() view returns(uint96)
func (_FlightDelays *FlightDelaysCallerSession) SUBNETWORKIDENTIFIER() (*big.Int, error) {
	return _FlightDelays.Contract.SUBNETWORKIDENTIFIER(&_FlightDelays.CallOpts)
}

// VAULTCONFIGURATOR is a free data retrieval call binding the contract method 0xb25bc0c0.
//
// Solidity: function VAULT_CONFIGURATOR() view returns(address)
func (_FlightDelays *FlightDelaysCaller) VAULTCONFIGURATOR(opts *bind.CallOpts) (common.Address, error) {
	var out []interface{}
	err := _FlightDelays.contract.Call(opts, &out, "VAULT_CONFIGURATOR")

	if err != nil {
		return *new(common.Address), err
	}

	out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address)

	return out0, err

}

// VAULTCONFIGURATOR is a free data retrieval call binding the contract method 0xb25bc0c0.
//
// Solidity: function VAULT_CONFIGURATOR() view returns(address)
func (_FlightDelays *FlightDelaysSession) VAULTCONFIGURATOR() (common.Address, error) {
	return _FlightDelays.Contract.VAULTCONFIGURATOR(&_FlightDelays.CallOpts)
}

// VAULTCONFIGURATOR is a free data retrieval call binding the contract method 0xb25bc0c0.
//
// Solidity: function VAULT_CONFIGURATOR() view returns(address)
func (_FlightDelays *FlightDelaysCallerSession) VAULTCONFIGURATOR() (common.Address, error) {
	return _FlightDelays.Contract.VAULTCONFIGURATOR(&_FlightDelays.CallOpts)
}

// Airlines is a free data retrieval call binding the contract method 0x116a272b.
//
// Solidity: function airlines(bytes32 airlineId) view returns(address vault, address rewards, uint256 covered, bytes32 lastFlightId)
func (_FlightDelays *FlightDelaysCaller) Airlines(opts *bind.CallOpts, airlineId [32]byte) (struct {
	Vault        common.Address
	Rewards      common.Address
	Covered      *big.Int
	LastFlightId [32]byte
}, error) {
	var out []interface{}
	err := _FlightDelays.contract.Call(opts, &out, "airlines", airlineId)

	outstruct := new(struct {
		Vault        common.Address
		Rewards      common.Address
		Covered      *big.Int
		LastFlightId [32]byte
	})
	if err != nil {
		return *outstruct, err
	}

	outstruct.Vault = *abi.ConvertType(out[0], new(common.Address)).(*common.Address)
	outstruct.Rewards = *abi.ConvertType(out[1], new(common.Address)).(*common.Address)
	outstruct.Covered = *abi.ConvertType(out[2], new(*big.Int)).(**big.Int)
	outstruct.LastFlightId = *abi.ConvertType(out[3], new([32]byte)).(*[32]byte)

	return *outstruct, err

}

// Airlines is a free data retrieval call binding the contract method 0x116a272b.
//
// Solidity: function airlines(bytes32 airlineId) view returns(address vault, address rewards, uint256 covered, bytes32 lastFlightId)
func (_FlightDelays *FlightDelaysSession) Airlines(airlineId [32]byte) (struct {
	Vault        common.Address
	Rewards      common.Address
	Covered      *big.Int
	LastFlightId [32]byte
}, error) {
	return _FlightDelays.Contract.Airlines(&_FlightDelays.CallOpts, airlineId)
}

// Airlines is a free data retrieval call binding the contract method 0x116a272b.
//
// Solidity: function airlines(bytes32 airlineId) view returns(address vault, address rewards, uint256 covered, bytes32 lastFlightId)
func (_FlightDelays *FlightDelaysCallerSession) Airlines(airlineId [32]byte) (struct {
	Vault        common.Address
	Rewards      common.Address
	Covered      *big.Int
	LastFlightId [32]byte
}, error) {
	return _FlightDelays.Contract.Airlines(&_FlightDelays.CallOpts, airlineId)
}

// Collateral is a free data retrieval call binding the contract method 0xd8dfeb45.
//
// Solidity: function collateral() view returns(address)
func (_FlightDelays *FlightDelaysCaller) Collateral(opts *bind.CallOpts) (common.Address, error) {
	var out []interface{}
	err := _FlightDelays.contract.Call(opts, &out, "collateral")

	if err != nil {
		return *new(common.Address), err
	}

	out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address)

	return out0, err

}

// Collateral is a free data retrieval call binding the contract method 0xd8dfeb45.
//
// Solidity: function collateral() view returns(address)
func (_FlightDelays *FlightDelaysSession) Collateral() (common.Address, error) {
	return _FlightDelays.Contract.Collateral(&_FlightDelays.CallOpts)
}

// Collateral is a free data retrieval call binding the contract method 0xd8dfeb45.
//
// Solidity: function collateral() view returns(address)
func (_FlightDelays *FlightDelaysCallerSession) Collateral() (common.Address, error) {
	return _FlightDelays.Contract.Collateral(&_FlightDelays.CallOpts)
}

// DelayWindow is a free data retrieval call binding the contract method 0x9e4f4b76.
//
// Solidity: function delayWindow() view returns(uint48)
func (_FlightDelays *FlightDelaysCaller) DelayWindow(opts *bind.CallOpts) (*big.Int, error) {
	var out []interface{}
	err := _FlightDelays.contract.Call(opts, &out, "delayWindow")

	if err != nil {
		return *new(*big.Int), err
	}

	out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)

	return out0, err

}

// DelayWindow is a free data retrieval call binding the contract method 0x9e4f4b76.
//
// Solidity: function delayWindow() view returns(uint48)
func (_FlightDelays *FlightDelaysSession) DelayWindow() (*big.Int, error) {
	return _FlightDelays.Contract.DelayWindow(&_FlightDelays.CallOpts)
}

// DelayWindow is a free data retrieval call binding the contract method 0x9e4f4b76.
//
// Solidity: function delayWindow() view returns(uint48)
func (_FlightDelays *FlightDelaysCallerSession) DelayWindow() (*big.Int, error) {
	return _FlightDelays.Contract.DelayWindow(&_FlightDelays.CallOpts)
}

// Flights is a free data retrieval call binding the contract method 0x45e8026d.
//
// Solidity: function flights(bytes32 airlineId, bytes32 flightId) view returns(uint48 timestamp, uint8 status, uint128 policiesSold, bytes32 previousFlightId)
func (_FlightDelays *FlightDelaysCaller) Flights(opts *bind.CallOpts, airlineId [32]byte, flightId [32]byte) (struct {
	Timestamp        *big.Int
	Status           uint8
	PoliciesSold     *big.Int
	PreviousFlightId [32]byte
}, error) {
	var out []interface{}
	err := _FlightDelays.contract.Call(opts, &out, "flights", airlineId, flightId)

	outstruct := new(struct {
		Timestamp        *big.Int
		Status           uint8
		PoliciesSold     *big.Int
		PreviousFlightId [32]byte
	})
	if err != nil {
		return *outstruct, err
	}

	outstruct.Timestamp = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)
	outstruct.Status = *abi.ConvertType(out[1], new(uint8)).(*uint8)
	outstruct.PoliciesSold = *abi.ConvertType(out[2], new(*big.Int)).(**big.Int)
	outstruct.PreviousFlightId = *abi.ConvertType(out[3], new([32]byte)).(*[32]byte)

	return *outstruct, err

}

// Flights is a free data retrieval call binding the contract method 0x45e8026d.
//
// Solidity: function flights(bytes32 airlineId, bytes32 flightId) view returns(uint48 timestamp, uint8 status, uint128 policiesSold, bytes32 previousFlightId)
func (_FlightDelays *FlightDelaysSession) Flights(airlineId [32]byte, flightId [32]byte) (struct {
	Timestamp        *big.Int
	Status           uint8
	PoliciesSold     *big.Int
	PreviousFlightId [32]byte
}, error) {
	return _FlightDelays.Contract.Flights(&_FlightDelays.CallOpts, airlineId, flightId)
}

// Flights is a free data retrieval call binding the contract method 0x45e8026d.
//
// Solidity: function flights(bytes32 airlineId, bytes32 flightId) view returns(uint48 timestamp, uint8 status, uint128 policiesSold, bytes32 previousFlightId)
func (_FlightDelays *FlightDelaysCallerSession) Flights(airlineId [32]byte, flightId [32]byte) (struct {
	Timestamp        *big.Int
	Status           uint8
	PoliciesSold     *big.Int
	PreviousFlightId [32]byte
}, error) {
	return _FlightDelays.Contract.Flights(&_FlightDelays.CallOpts, airlineId, flightId)
}

// MessageExpiry is a free data retrieval call binding the contract method 0x5c96f9b5.
//
// Solidity: function messageExpiry() view returns(uint32)
func (_FlightDelays *FlightDelaysCaller) MessageExpiry(opts *bind.CallOpts) (uint32, error) {
	var out []interface{}
	err := _FlightDelays.contract.Call(opts, &out, "messageExpiry")

	if err != nil {
		return *new(uint32), err
	}

	out0 := *abi.ConvertType(out[0], new(uint32)).(*uint32)

	return out0, err

}

// MessageExpiry is a free data retrieval call binding the contract method 0x5c96f9b5.
//
// Solidity: function messageExpiry() view returns(uint32)
func (_FlightDelays *FlightDelaysSession) MessageExpiry() (uint32, error) {
	return _FlightDelays.Contract.MessageExpiry(&_FlightDelays.CallOpts)
}

// MessageExpiry is a free data retrieval call binding the contract method 0x5c96f9b5.
//
// Solidity: function messageExpiry() view returns(uint32)
func (_FlightDelays *FlightDelaysCallerSession) MessageExpiry() (uint32, error) {
	return _FlightDelays.Contract.MessageExpiry(&_FlightDelays.CallOpts)
}

// Policies is a free data retrieval call binding the contract method 0x73714bcd.
//
// Solidity: function policies(bytes32 airlineId, bytes32 flightId, address buyer) view returns(uint8 policyStatus)
func (_FlightDelays *FlightDelaysCaller) Policies(opts *bind.CallOpts, airlineId [32]byte, flightId [32]byte, buyer common.Address) (uint8, error) {
	var out []interface{}
	err := _FlightDelays.contract.Call(opts, &out, "policies", airlineId, flightId, buyer)

	if err != nil {
		return *new(uint8), err
	}

	out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8)

	return out0, err

}

// Policies is a free data retrieval call binding the contract method 0x73714bcd.
//
// Solidity: function policies(bytes32 airlineId, bytes32 flightId, address buyer) view returns(uint8 policyStatus)
func (_FlightDelays *FlightDelaysSession) Policies(airlineId [32]byte, flightId [32]byte, buyer common.Address) (uint8, error) {
	return _FlightDelays.Contract.Policies(&_FlightDelays.CallOpts, airlineId, flightId, buyer)
}

// Policies is a free data retrieval call binding the contract method 0x73714bcd.
//
// Solidity: function policies(bytes32 airlineId, bytes32 flightId, address buyer) view returns(uint8 policyStatus)
func (_FlightDelays *FlightDelaysCallerSession) Policies(airlineId [32]byte, flightId [32]byte, buyer common.Address) (uint8, error) {
	return _FlightDelays.Contract.Policies(&_FlightDelays.CallOpts, airlineId, flightId, buyer)
}

// PolicyPayout is a free data retrieval call binding the contract method 0x365ca41e.
//
// Solidity: function policyPayout() view returns(uint256)
func (_FlightDelays *FlightDelaysCaller) PolicyPayout(opts *bind.CallOpts) (*big.Int, error) {
	var out []interface{}
	err := _FlightDelays.contract.Call(opts, &out, "policyPayout")

	if err != nil {
		return *new(*big.Int), err
	}

	out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)

	return out0, err

}

// PolicyPayout is a free data retrieval call binding the contract method 0x365ca41e.
//
// Solidity: function policyPayout() view returns(uint256)
func (_FlightDelays *FlightDelaysSession) PolicyPayout() (*big.Int, error) {
	return _FlightDelays.Contract.PolicyPayout(&_FlightDelays.CallOpts)
}

// PolicyPayout is a free data retrieval call binding the contract method 0x365ca41e.
//
// Solidity: function policyPayout() view returns(uint256)
func (_FlightDelays *FlightDelaysCallerSession) PolicyPayout() (*big.Int, error) {
	return _FlightDelays.Contract.PolicyPayout(&_FlightDelays.CallOpts)
}

// PolicyPremium is a free data retrieval call binding the contract method 0x22b6fefe.
//
// Solidity: function policyPremium() view returns(uint256)
func (_FlightDelays *FlightDelaysCaller) PolicyPremium(opts *bind.CallOpts) (*big.Int, error) {
	var out []interface{}
	err := _FlightDelays.contract.Call(opts, &out, "policyPremium")

	if err != nil {
		return *new(*big.Int), err
	}

	out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)

	return out0, err

}

// PolicyPremium is a free data retrieval call binding the contract method 0x22b6fefe.
//
// Solidity: function policyPremium() view returns(uint256)
func (_FlightDelays *FlightDelaysSession) PolicyPremium() (*big.Int, error) {
	return _FlightDelays.Contract.PolicyPremium(&_FlightDelays.CallOpts)
}

// PolicyPremium is a free data retrieval call binding the contract method 0x22b6fefe.
//
// Solidity: function policyPremium() view returns(uint256)
func (_FlightDelays *FlightDelaysCallerSession) PolicyPremium() (*big.Int, error) {
	return _FlightDelays.Contract.PolicyPremium(&_FlightDelays.CallOpts)
}

// PolicyWindow is a free data retrieval call binding the contract method 0x0da1f7ca.
//
// Solidity: function policyWindow() view returns(uint48)
func (_FlightDelays *FlightDelaysCaller) PolicyWindow(opts *bind.CallOpts) (*big.Int, error) {
	var out []interface{}
	err := _FlightDelays.contract.Call(opts, &out, "policyWindow")

	if err != nil {
		return *new(*big.Int), err
	}

	out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)

	return out0, err

}

// PolicyWindow is a free data retrieval call binding the contract method 0x0da1f7ca.
//
// Solidity: function policyWindow() view returns(uint48)
func (_FlightDelays *FlightDelaysSession) PolicyWindow() (*big.Int, error) {
	return _FlightDelays.Contract.PolicyWindow(&_FlightDelays.CallOpts)
}

// PolicyWindow is a free data retrieval call binding the contract method 0x0da1f7ca.
//
// Solidity: function policyWindow() view returns(uint48)
func (_FlightDelays *FlightDelaysCallerSession) PolicyWindow() (*big.Int, error) {
	return _FlightDelays.Contract.PolicyWindow(&_FlightDelays.CallOpts)
}

// Settlement is a free data retrieval call binding the contract method 0x51160630.
//
// Solidity: function settlement() view returns(address)
func (_FlightDelays *FlightDelaysCaller) Settlement(opts *bind.CallOpts) (common.Address, error) {
	var out []interface{}
	err := _FlightDelays.contract.Call(opts, &out, "settlement")

	if err != nil {
		return *new(common.Address), err
	}

	out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address)

	return out0, err

}

// Settlement is a free data retrieval call binding the contract method 0x51160630.
//
// Solidity: function settlement() view returns(address)
func (_FlightDelays *FlightDelaysSession) Settlement() (common.Address, error) {
	return _FlightDelays.Contract.Settlement(&_FlightDelays.CallOpts)
}

// Settlement is a free data retrieval call binding the contract method 0x51160630.
//
// Solidity: function settlement() view returns(address)
func (_FlightDelays *FlightDelaysCallerSession) Settlement() (common.Address, error) {
	return _FlightDelays.Contract.Settlement(&_FlightDelays.CallOpts)
}

// VaultEpochDuration is a free data retrieval call binding the contract method 0xee1b2207.
//
// Solidity: function vaultEpochDuration() view returns(uint48)
func (_FlightDelays *FlightDelaysCaller) VaultEpochDuration(opts *bind.CallOpts) (*big.Int, error) {
	var out []interface{}
	err := _FlightDelays.contract.Call(opts, &out, "vaultEpochDuration")

	if err != nil {
		return *new(*big.Int), err
	}

	out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)

	return out0, err

}

// VaultEpochDuration is a free data retrieval call binding the contract method 0xee1b2207.
//
// Solidity: function vaultEpochDuration() view returns(uint48)
func (_FlightDelays *FlightDelaysSession) VaultEpochDuration() (*big.Int, error) {
	return _FlightDelays.Contract.VaultEpochDuration(&_FlightDelays.CallOpts)
}

// VaultEpochDuration is a free data retrieval call binding the contract method 0xee1b2207.
//
// Solidity: function vaultEpochDuration() view returns(uint48)
func (_FlightDelays *FlightDelaysCallerSession) VaultEpochDuration() (*big.Int, error) {
	return _FlightDelays.Contract.VaultEpochDuration(&_FlightDelays.CallOpts)
}

// VotingPowers is a free data retrieval call binding the contract method 0xc00f50eb.
//
// Solidity: function votingPowers() view returns(address)
func (_FlightDelays *FlightDelaysCaller) VotingPowers(opts *bind.CallOpts) (common.Address, error) {
	var out []interface{}
	err := _FlightDelays.contract.Call(opts, &out, "votingPowers")

	if err != nil {
		return *new(common.Address), err
	}

	out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address)

	return out0, err

}

// VotingPowers is a free data retrieval call binding the contract method 0xc00f50eb.
//
// Solidity: function votingPowers() view returns(address)
func (_FlightDelays *FlightDelaysSession) VotingPowers() (common.Address, error) {
	return _FlightDelays.Contract.VotingPowers(&_FlightDelays.CallOpts)
}

// VotingPowers is a free data retrieval call binding the contract method 0xc00f50eb.
//
// Solidity: function votingPowers() view returns(address)
func (_FlightDelays *FlightDelaysCallerSession) VotingPowers() (common.Address, error) {
	return _FlightDelays.Contract.VotingPowers(&_FlightDelays.CallOpts)
}

// BuyInsurance is a paid mutator transaction binding the contract method 0xb8f37ab2.
//
// Solidity: function buyInsurance(bytes32 airlineId, bytes32 flightId) returns()
func (_FlightDelays *FlightDelaysTransactor) BuyInsurance(opts *bind.TransactOpts, airlineId [32]byte, flightId [32]byte) (*types.Transaction, error) {
	return _FlightDelays.contract.Transact(opts, "buyInsurance", airlineId, flightId)
}

// BuyInsurance is a paid mutator transaction binding the contract method 0xb8f37ab2.
//
// Solidity: function buyInsurance(bytes32 airlineId, bytes32 flightId) returns()
func (_FlightDelays *FlightDelaysSession) BuyInsurance(airlineId [32]byte, flightId [32]byte) (*types.Transaction, error) {
	return _FlightDelays.Contract.BuyInsurance(&_FlightDelays.TransactOpts, airlineId, flightId)
}

// BuyInsurance is a paid mutator transaction binding the contract method 0xb8f37ab2.
//
// Solidity: function buyInsurance(bytes32 airlineId, bytes32 flightId) returns()
func (_FlightDelays *FlightDelaysTransactorSession) BuyInsurance(airlineId [32]byte, flightId [32]byte) (*types.Transaction, error) {
	return _FlightDelays.Contract.BuyInsurance(&_FlightDelays.TransactOpts, airlineId, flightId)
}

// ClaimInsurance is a paid mutator transaction binding the contract method 0x147dbcf1.
//
// Solidity: function claimInsurance(bytes32 airlineId, bytes32 flightId) returns()
func (_FlightDelays *FlightDelaysTransactor) ClaimInsurance(opts *bind.TransactOpts, airlineId [32]byte, flightId [32]byte) (*types.Transaction, error) {
	return _FlightDelays.contract.Transact(opts, "claimInsurance", airlineId, flightId)
}

// ClaimInsurance is a paid mutator transaction binding the contract method 0x147dbcf1.
//
// Solidity: function claimInsurance(bytes32 airlineId, bytes32 flightId) returns()
func (_FlightDelays *FlightDelaysSession) ClaimInsurance(airlineId [32]byte, flightId [32]byte) (*types.Transaction, error) {
	return _FlightDelays.Contract.ClaimInsurance(&_FlightDelays.TransactOpts, airlineId, flightId)
}

// ClaimInsurance is a paid mutator transaction binding the contract method 0x147dbcf1.
//
// Solidity: function claimInsurance(bytes32 airlineId, bytes32 flightId) returns()
func (_FlightDelays *FlightDelaysTransactorSession) ClaimInsurance(airlineId [32]byte, flightId [32]byte) (*types.Transaction, error) {
	return _FlightDelays.Contract.ClaimInsurance(&_FlightDelays.TransactOpts, airlineId, flightId)
}

// CreateFlight is a paid mutator transaction binding the contract method 0xd97650b0.
//
// Solidity: function createFlight(bytes32 airlineId, bytes32 flightId, uint48 scheduledTimestamp, bytes32 previousFlightId, uint48 epoch, bytes proof) returns()
func (_FlightDelays *FlightDelaysTransactor) CreateFlight(opts *bind.TransactOpts, airlineId [32]byte, flightId [32]byte, scheduledTimestamp *big.Int, previousFlightId [32]byte, epoch *big.Int, proof []byte) (*types.Transaction, error) {
	return _FlightDelays.contract.Transact(opts, "createFlight", airlineId, flightId, scheduledTimestamp, previousFlightId, epoch, proof)
}

// CreateFlight is a paid mutator transaction binding the contract method 0xd97650b0.
//
// Solidity: function createFlight(bytes32 airlineId, bytes32 flightId, uint48 scheduledTimestamp, bytes32 previousFlightId, uint48 epoch, bytes proof) returns()
func (_FlightDelays *FlightDelaysSession) CreateFlight(airlineId [32]byte, flightId [32]byte, scheduledTimestamp *big.Int, previousFlightId [32]byte, epoch *big.Int, proof []byte) (*types.Transaction, error) {
	return _FlightDelays.Contract.CreateFlight(&_FlightDelays.TransactOpts, airlineId, flightId, scheduledTimestamp, previousFlightId, epoch, proof)
}

// CreateFlight is a paid mutator transaction binding the contract method 0xd97650b0.
//
// Solidity: function createFlight(bytes32 airlineId, bytes32 flightId, uint48 scheduledTimestamp, bytes32 previousFlightId, uint48 epoch, bytes proof) returns()
func (_FlightDelays *FlightDelaysTransactorSession) CreateFlight(airlineId [32]byte, flightId [32]byte, scheduledTimestamp *big.Int, previousFlightId [32]byte, epoch *big.Int, proof []byte) (*types.Transaction, error) {
	return _FlightDelays.Contract.CreateFlight(&_FlightDelays.TransactOpts, airlineId, flightId, scheduledTimestamp, previousFlightId, epoch, proof)
}

// DelayFlight is a paid mutator transaction binding the contract method 0x43bfd533.
//
// Solidity: function delayFlight(bytes32 airlineId, bytes32 flightId, uint48 epoch, bytes proof) returns()
func (_FlightDelays *FlightDelaysTransactor) DelayFlight(opts *bind.TransactOpts, airlineId [32]byte, flightId [32]byte, epoch *big.Int, proof []byte) (*types.Transaction, error) {
	return _FlightDelays.contract.Transact(opts, "delayFlight", airlineId, flightId, epoch, proof)
}

// DelayFlight is a paid mutator transaction binding the contract method 0x43bfd533.
//
// Solidity: function delayFlight(bytes32 airlineId, bytes32 flightId, uint48 epoch, bytes proof) returns()
func (_FlightDelays *FlightDelaysSession) DelayFlight(airlineId [32]byte, flightId [32]byte, epoch *big.Int, proof []byte) (*types.Transaction, error) {
	return _FlightDelays.Contract.DelayFlight(&_FlightDelays.TransactOpts, airlineId, flightId, epoch, proof)
}

// DelayFlight is a paid mutator transaction binding the contract method 0x43bfd533.
//
// Solidity: function delayFlight(bytes32 airlineId, bytes32 flightId, uint48 epoch, bytes proof) returns()
func (_FlightDelays *FlightDelaysTransactorSession) DelayFlight(airlineId [32]byte, flightId [32]byte, epoch *big.Int, proof []byte) (*types.Transaction, error) {
	return _FlightDelays.Contract.DelayFlight(&_FlightDelays.TransactOpts, airlineId, flightId, epoch, proof)
}

// DepartFlight is a paid mutator transaction binding the contract method 0x13f8a494.
//
// Solidity: function departFlight(bytes32 airlineId, bytes32 flightId, uint48 epoch, bytes proof) returns()
func (_FlightDelays *FlightDelaysTransactor) DepartFlight(opts *bind.TransactOpts, airlineId [32]byte, flightId [32]byte, epoch *big.Int, proof []byte) (*types.Transaction, error) {
	return _FlightDelays.contract.Transact(opts, "departFlight", airlineId, flightId, epoch, proof)
}

// DepartFlight is a paid mutator transaction binding the contract method 0x13f8a494.
//
// Solidity: function departFlight(bytes32 airlineId, bytes32 flightId, uint48 epoch, bytes proof) returns()
func (_FlightDelays *FlightDelaysSession) DepartFlight(airlineId [32]byte, flightId [32]byte, epoch *big.Int, proof []byte) (*types.Transaction, error) {
	return _FlightDelays.Contract.DepartFlight(&_FlightDelays.TransactOpts, airlineId, flightId, epoch, proof)
}

// DepartFlight is a paid mutator transaction binding the contract method 0x13f8a494.
//
// Solidity: function departFlight(bytes32 airlineId, bytes32 flightId, uint48 epoch, bytes proof) returns()
func (_FlightDelays *FlightDelaysTransactorSession) DepartFlight(airlineId [32]byte, flightId [32]byte, epoch *big.Int, proof []byte) (*types.Transaction, error) {
	return _FlightDelays.Contract.DepartFlight(&_FlightDelays.TransactOpts, airlineId, flightId, epoch, proof)
}

// Initialize is a paid mutator transaction binding the contract method 0x08251708.
//
// Solidity: function initialize((address,address,address,uint48,uint32,uint48,uint48,uint256,uint256) initParams) returns()
func (_FlightDelays *FlightDelaysTransactor) Initialize(opts *bind.TransactOpts, initParams FlightDelaysInitParams) (*types.Transaction, error) {
	return _FlightDelays.contract.Transact(opts, "initialize", initParams)
}

// Initialize is a paid mutator transaction binding the contract method 0x08251708.
//
// Solidity: function initialize((address,address,address,uint48,uint32,uint48,uint48,uint256,uint256) initParams) returns()
func (_FlightDelays *FlightDelaysSession) Initialize(initParams FlightDelaysInitParams) (*types.Transaction, error) {
	return _FlightDelays.Contract.Initialize(&_FlightDelays.TransactOpts, initParams)
}

// Initialize is a paid mutator transaction binding the contract method 0x08251708.
//
// Solidity: function initialize((address,address,address,uint48,uint32,uint48,uint48,uint256,uint256) initParams) returns()
func (_FlightDelays *FlightDelaysTransactorSession) Initialize(initParams FlightDelaysInitParams) (*types.Transaction, error) {
	return _FlightDelays.Contract.Initialize(&_FlightDelays.TransactOpts, initParams)
}

// StaticDelegateCall is a paid mutator transaction binding the contract method 0x9f86fd85.
//
// Solidity: function staticDelegateCall(address target, bytes data) returns()
func (_FlightDelays *FlightDelaysTransactor) StaticDelegateCall(opts *bind.TransactOpts, target common.Address, data []byte) (*types.Transaction, error) {
	return _FlightDelays.contract.Transact(opts, "staticDelegateCall", target, data)
}

// StaticDelegateCall is a paid mutator transaction binding the contract method 0x9f86fd85.
//
// Solidity: function staticDelegateCall(address target, bytes data) returns()
func (_FlightDelays *FlightDelaysSession) StaticDelegateCall(target common.Address, data []byte) (*types.Transaction, error) {
	return _FlightDelays.Contract.StaticDelegateCall(&_FlightDelays.TransactOpts, target, data)
}

// StaticDelegateCall is a paid mutator transaction binding the contract method 0x9f86fd85.
//
// Solidity: function staticDelegateCall(address target, bytes data) returns()
func (_FlightDelays *FlightDelaysTransactorSession) StaticDelegateCall(target common.Address, data []byte) (*types.Transaction, error) {
	return _FlightDelays.Contract.StaticDelegateCall(&_FlightDelays.TransactOpts, target, data)
}

// FlightDelaysAirlineVaultDeployedIterator is returned from FilterAirlineVaultDeployed and is used to iterate over the raw logs and unpacked data for AirlineVaultDeployed events raised by the FlightDelays contract.
type FlightDelaysAirlineVaultDeployedIterator struct {
	Event *FlightDelaysAirlineVaultDeployed // Event containing the contract specifics and raw log

	contract *bind.BoundContract // Generic contract to use for unpacking event data
	event    string              // Event name to use for unpacking event data

	logs chan types.Log        // Log channel receiving the found contract events
	sub  ethereum.Subscription // Subscription for errors, completion and termination
	done bool                  // Whether the subscription completed delivering logs
	fail error                 // Occurred error to stop iteration
}

// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *FlightDelaysAirlineVaultDeployedIterator) Next() bool {
	// If the iterator failed, stop iterating
	if it.fail != nil {
		return false
	}
	// If the iterator completed, deliver directly whatever's available
	if it.done {
		select {
		case log := <-it.logs:
			it.Event = new(FlightDelaysAirlineVaultDeployed)
			if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
				it.fail = err
				return false
			}
			it.Event.Raw = log
			return true

		default:
			return false
		}
	}
	// Iterator still in progress, wait for either a data or an error event
	select {
	case log := <-it.logs:
		it.Event = new(FlightDelaysAirlineVaultDeployed)
		if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
			it.fail = err
			return false
		}
		it.Event.Raw = log
		return true

	case err := <-it.sub.Err():
		it.done = true
		it.fail = err
		return it.Next()
	}
}

// Error returns any retrieval or parsing error occurred during filtering.
func (it *FlightDelaysAirlineVaultDeployedIterator) Error() error {
	return it.fail
}

// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *FlightDelaysAirlineVaultDeployedIterator) Close() error {
	it.sub.Unsubscribe()
	return nil
}

// FlightDelaysAirlineVaultDeployed represents a AirlineVaultDeployed event raised by the FlightDelays contract.
type FlightDelaysAirlineVaultDeployed struct {
	AirlineId [32]byte
	Vault     common.Address
	Rewards   common.Address
	Raw       types.Log // Blockchain specific contextual infos
}

// FilterAirlineVaultDeployed is a free log retrieval operation binding the contract event 0x6cb291016784959f890661d07225a645792785a83baeb2592251dcc9e4e7bf34.
//
// Solidity: event AirlineVaultDeployed(bytes32 indexed airlineId, address vault, address rewards)
func (_FlightDelays *FlightDelaysFilterer) FilterAirlineVaultDeployed(opts *bind.FilterOpts, airlineId [][32]byte) (*FlightDelaysAirlineVaultDeployedIterator, error) {

	var airlineIdRule []interface{}
	for _, airlineIdItem := range airlineId {
		airlineIdRule = append(airlineIdRule, airlineIdItem)
	}

	logs, sub, err := _FlightDelays.contract.FilterLogs(opts, "AirlineVaultDeployed", airlineIdRule)
	if err != nil {
		return nil, err
	}
	return &FlightDelaysAirlineVaultDeployedIterator{contract: _FlightDelays.contract, event: "AirlineVaultDeployed", logs: logs, sub: sub}, nil
}

// WatchAirlineVaultDeployed is a free log subscription operation binding the contract event 0x6cb291016784959f890661d07225a645792785a83baeb2592251dcc9e4e7bf34.
//
// Solidity: event AirlineVaultDeployed(bytes32 indexed airlineId, address vault, address rewards)
func (_FlightDelays *FlightDelaysFilterer) WatchAirlineVaultDeployed(opts *bind.WatchOpts, sink chan<- *FlightDelaysAirlineVaultDeployed, airlineId [][32]byte) (event.Subscription, error) {

	var airlineIdRule []interface{}
	for _, airlineIdItem := range airlineId {
		airlineIdRule = append(airlineIdRule, airlineIdItem)
	}

	logs, sub, err := _FlightDelays.contract.WatchLogs(opts, "AirlineVaultDeployed", airlineIdRule)
	if err != nil {
		return nil, err
	}
	return event.NewSubscription(func(quit <-chan struct{}) error {
		defer sub.Unsubscribe()
		for {
			select {
			case log := <-logs:
				// New log arrived, parse the event and forward to the user
				event := new(FlightDelaysAirlineVaultDeployed)
				if err := _FlightDelays.contract.UnpackLog(event, "AirlineVaultDeployed", log); err != nil {
					return err
				}
				event.Raw = log

				select {
				case sink <- event:
				case err := <-sub.Err():
					return err
				case <-quit:
					return nil
				}
			case err := <-sub.Err():
				return err
			case <-quit:
				return nil
			}
		}
	}), nil
}

// ParseAirlineVaultDeployed is a log parse operation binding the contract event 0x6cb291016784959f890661d07225a645792785a83baeb2592251dcc9e4e7bf34.
//
// Solidity: event AirlineVaultDeployed(bytes32 indexed airlineId, address vault, address rewards)
func (_FlightDelays *FlightDelaysFilterer) ParseAirlineVaultDeployed(log types.Log) (*FlightDelaysAirlineVaultDeployed, error) {
	event := new(FlightDelaysAirlineVaultDeployed)
	if err := _FlightDelays.contract.UnpackLog(event, "AirlineVaultDeployed", log); err != nil {
		return nil, err
	}
	event.Raw = log
	return event, nil
}

// FlightDelaysFlightCreatedIterator is returned from FilterFlightCreated and is used to iterate over the raw logs and unpacked data for FlightCreated events raised by the FlightDelays contract.
type FlightDelaysFlightCreatedIterator struct {
	Event *FlightDelaysFlightCreated // Event containing the contract specifics and raw log

	contract *bind.BoundContract // Generic contract to use for unpacking event data
	event    string              // Event name to use for unpacking event data

	logs chan types.Log        // Log channel receiving the found contract events
	sub  ethereum.Subscription // Subscription for errors, completion and termination
	done bool                  // Whether the subscription completed delivering logs
	fail error                 // Occurred error to stop iteration
}

// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *FlightDelaysFlightCreatedIterator) Next() bool {
	// If the iterator failed, stop iterating
	if it.fail != nil {
		return false
	}
	// If the iterator completed, deliver directly whatever's available
	if it.done {
		select {
		case log := <-it.logs:
			it.Event = new(FlightDelaysFlightCreated)
			if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
				it.fail = err
				return false
			}
			it.Event.Raw = log
			return true

		default:
			return false
		}
	}
	// Iterator still in progress, wait for either a data or an error event
	select {
	case log := <-it.logs:
		it.Event = new(FlightDelaysFlightCreated)
		if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
			it.fail = err
			return false
		}
		it.Event.Raw = log
		return true

	case err := <-it.sub.Err():
		it.done = true
		it.fail = err
		return it.Next()
	}
}

// Error returns any retrieval or parsing error occurred during filtering.
func (it *FlightDelaysFlightCreatedIterator) Error() error {
	return it.fail
}

// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *FlightDelaysFlightCreatedIterator) Close() error {
	it.sub.Unsubscribe()
	return nil
}

// FlightDelaysFlightCreated represents a FlightCreated event raised by the FlightDelays contract.
type FlightDelaysFlightCreated struct {
	AirlineId          [32]byte
	FlightId           [32]byte
	ScheduledTimestamp *big.Int
	Raw                types.Log // Blockchain specific contextual infos
}

// FilterFlightCreated is a free log retrieval operation binding the contract event 0x1e735cf33b66ed3dc00862d938ae4f8f7dbbda9493652fc483c3607ce23c0070.
//
// Solidity: event FlightCreated(bytes32 indexed airlineId, bytes32 indexed flightId, uint48 scheduledTimestamp)
func (_FlightDelays *FlightDelaysFilterer) FilterFlightCreated(opts *bind.FilterOpts, airlineId [][32]byte, flightId [][32]byte) (*FlightDelaysFlightCreatedIterator, error) {

	var airlineIdRule []interface{}
	for _, airlineIdItem := range airlineId {
		airlineIdRule = append(airlineIdRule, airlineIdItem)
	}
	var flightIdRule []interface{}
	for _, flightIdItem := range flightId {
		flightIdRule = append(flightIdRule, flightIdItem)
	}

	logs, sub, err := _FlightDelays.contract.FilterLogs(opts, "FlightCreated", airlineIdRule, flightIdRule)
	if err != nil {
		return nil, err
	}
	return &FlightDelaysFlightCreatedIterator{contract: _FlightDelays.contract, event: "FlightCreated", logs: logs, sub: sub}, nil
}

// WatchFlightCreated is a free log subscription operation binding the contract event 0x1e735cf33b66ed3dc00862d938ae4f8f7dbbda9493652fc483c3607ce23c0070.
//
// Solidity: event FlightCreated(bytes32 indexed airlineId, bytes32 indexed flightId, uint48 scheduledTimestamp)
func (_FlightDelays *FlightDelaysFilterer) WatchFlightCreated(opts *bind.WatchOpts, sink chan<- *FlightDelaysFlightCreated, airlineId [][32]byte, flightId [][32]byte) (event.Subscription, error) {

	var airlineIdRule []interface{}
	for _, airlineIdItem := range airlineId {
		airlineIdRule = append(airlineIdRule, airlineIdItem)
	}
	var flightIdRule []interface{}
	for _, flightIdItem := range flightId {
		flightIdRule = append(flightIdRule, flightIdItem)
	}

	logs, sub, err := _FlightDelays.contract.WatchLogs(opts, "FlightCreated", airlineIdRule, flightIdRule)
	if err != nil {
		return nil, err
	}
	return event.NewSubscription(func(quit <-chan struct{}) error {
		defer sub.Unsubscribe()
		for {
			select {
			case log := <-logs:
				// New log arrived, parse the event and forward to the user
				event := new(FlightDelaysFlightCreated)
				if err := _FlightDelays.contract.UnpackLog(event, "FlightCreated", log); err != nil {
					return err
				}
				event.Raw = log

				select {
				case sink <- event:
				case err := <-sub.Err():
					return err
				case <-quit:
					return nil
				}
			case err := <-sub.Err():
				return err
			case <-quit:
				return nil
			}
		}
	}), nil
}

// ParseFlightCreated is a log parse operation binding the contract event 0x1e735cf33b66ed3dc00862d938ae4f8f7dbbda9493652fc483c3607ce23c0070.
//
// Solidity: event FlightCreated(bytes32 indexed airlineId, bytes32 indexed flightId, uint48 scheduledTimestamp)
func (_FlightDelays *FlightDelaysFilterer) ParseFlightCreated(log types.Log) (*FlightDelaysFlightCreated, error) {
	event := new(FlightDelaysFlightCreated)
	if err := _FlightDelays.contract.UnpackLog(event, "FlightCreated", log); err != nil {
		return nil, err
	}
	event.Raw = log
	return event, nil
}

// FlightDelaysFlightDelayedIterator is returned from FilterFlightDelayed and is used to iterate over the raw logs and unpacked data for FlightDelayed events raised by the FlightDelays contract.
type FlightDelaysFlightDelayedIterator struct {
	Event *FlightDelaysFlightDelayed // Event containing the contract specifics and raw log

	contract *bind.BoundContract // Generic contract to use for unpacking event data
	event    string              // Event name to use for unpacking event data

	logs chan types.Log        // Log channel receiving the found contract events
	sub  ethereum.Subscription // Subscription for errors, completion and termination
	done bool                  // Whether the subscription completed delivering logs
	fail error                 // Occurred error to stop iteration
}

// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *FlightDelaysFlightDelayedIterator) Next() bool {
	// If the iterator failed, stop iterating
	if it.fail != nil {
		return false
	}
	// If the iterator completed, deliver directly whatever's available
	if it.done {
		select {
		case log := <-it.logs:
			it.Event = new(FlightDelaysFlightDelayed)
			if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
				it.fail = err
				return false
			}
			it.Event.Raw = log
			return true

		default:
			return false
		}
	}
	// Iterator still in progress, wait for either a data or an error event
	select {
	case log := <-it.logs:
		it.Event = new(FlightDelaysFlightDelayed)
		if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
			it.fail = err
			return false
		}
		it.Event.Raw = log
		return true

	case err := <-it.sub.Err():
		it.done = true
		it.fail = err
		return it.Next()
	}
}

// Error returns any retrieval or parsing error occurred during filtering.
func (it *FlightDelaysFlightDelayedIterator) Error() error {
	return it.fail
}

// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *FlightDelaysFlightDelayedIterator) Close() error {
	it.sub.Unsubscribe()
	return nil
}

// FlightDelaysFlightDelayed represents a FlightDelayed event raised by the FlightDelays contract.
type FlightDelaysFlightDelayed struct {
	AirlineId [32]byte
	FlightId  [32]byte
	Raw       types.Log // Blockchain specific contextual infos
}

// FilterFlightDelayed is a free log retrieval operation binding the contract event 0x8e8076a6235494b484b7cd7000b0bb95229118813ebf3437799a65912956d478.
//
// Solidity: event FlightDelayed(bytes32 indexed airlineId, bytes32 indexed flightId)
func (_FlightDelays *FlightDelaysFilterer) FilterFlightDelayed(opts *bind.FilterOpts, airlineId [][32]byte, flightId [][32]byte) (*FlightDelaysFlightDelayedIterator, error) {

	var airlineIdRule []interface{}
	for _, airlineIdItem := range airlineId {
		airlineIdRule = append(airlineIdRule, airlineIdItem)
	}
	var flightIdRule []interface{}
	for _, flightIdItem := range flightId {
		flightIdRule = append(flightIdRule, flightIdItem)
	}

	logs, sub, err := _FlightDelays.contract.FilterLogs(opts, "FlightDelayed", airlineIdRule, flightIdRule)
	if err != nil {
		return nil, err
	}
	return &FlightDelaysFlightDelayedIterator{contract: _FlightDelays.contract, event: "FlightDelayed", logs: logs, sub: sub}, nil
}

// WatchFlightDelayed is a free log subscription operation binding the contract event 0x8e8076a6235494b484b7cd7000b0bb95229118813ebf3437799a65912956d478.
//
// Solidity: event FlightDelayed(bytes32 indexed airlineId, bytes32 indexed flightId)
func (_FlightDelays *FlightDelaysFilterer) WatchFlightDelayed(opts *bind.WatchOpts, sink chan<- *FlightDelaysFlightDelayed, airlineId [][32]byte, flightId [][32]byte) (event.Subscription, error) {

	var airlineIdRule []interface{}
	for _, airlineIdItem := range airlineId {
		airlineIdRule = append(airlineIdRule, airlineIdItem)
	}
	var flightIdRule []interface{}
	for _, flightIdItem := range flightId {
		flightIdRule = append(flightIdRule, flightIdItem)
	}

	logs, sub, err := _FlightDelays.contract.WatchLogs(opts, "FlightDelayed", airlineIdRule, flightIdRule)
	if err != nil {
		return nil, err
	}
	return event.NewSubscription(func(quit <-chan struct{}) error {
		defer sub.Unsubscribe()
		for {
			select {
			case log := <-logs:
				// New log arrived, parse the event and forward to the user
				event := new(FlightDelaysFlightDelayed)
				if err := _FlightDelays.contract.UnpackLog(event, "FlightDelayed", log); err != nil {
					return err
				}
				event.Raw = log

				select {
				case sink <- event:
				case err := <-sub.Err():
					return err
				case <-quit:
					return nil
				}
			case err := <-sub.Err():
				return err
			case <-quit:
				return nil
			}
		}
	}), nil
}

// ParseFlightDelayed is a log parse operation binding the contract event 0x8e8076a6235494b484b7cd7000b0bb95229118813ebf3437799a65912956d478.
//
// Solidity: event FlightDelayed(bytes32 indexed airlineId, bytes32 indexed flightId)
func (_FlightDelays *FlightDelaysFilterer) ParseFlightDelayed(log types.Log) (*FlightDelaysFlightDelayed, error) {
	event := new(FlightDelaysFlightDelayed)
	if err := _FlightDelays.contract.UnpackLog(event, "FlightDelayed", log); err != nil {
		return nil, err
	}
	event.Raw = log
	return event, nil
}

// FlightDelaysFlightDepartedIterator is returned from FilterFlightDeparted and is used to iterate over the raw logs and unpacked data for FlightDeparted events raised by the FlightDelays contract.
type FlightDelaysFlightDepartedIterator struct {
	Event *FlightDelaysFlightDeparted // Event containing the contract specifics and raw log

	contract *bind.BoundContract // Generic contract to use for unpacking event data
	event    string              // Event name to use for unpacking event data

	logs chan types.Log        // Log channel receiving the found contract events
	sub  ethereum.Subscription // Subscription for errors, completion and termination
	done bool                  // Whether the subscription completed delivering logs
	fail error                 // Occurred error to stop iteration
}

// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *FlightDelaysFlightDepartedIterator) Next() bool {
	// If the iterator failed, stop iterating
	if it.fail != nil {
		return false
	}
	// If the iterator completed, deliver directly whatever's available
	if it.done {
		select {
		case log := <-it.logs:
			it.Event = new(FlightDelaysFlightDeparted)
			if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
				it.fail = err
				return false
			}
			it.Event.Raw = log
			return true

		default:
			return false
		}
	}
	// Iterator still in progress, wait for either a data or an error event
	select {
	case log := <-it.logs:
		it.Event = new(FlightDelaysFlightDeparted)
		if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
			it.fail = err
			return false
		}
		it.Event.Raw = log
		return true

	case err := <-it.sub.Err():
		it.done = true
		it.fail = err
		return it.Next()
	}
}

// Error returns any retrieval or parsing error occurred during filtering.
func (it *FlightDelaysFlightDepartedIterator) Error() error {
	return it.fail
}

// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *FlightDelaysFlightDepartedIterator) Close() error {
	it.sub.Unsubscribe()
	return nil
}

// FlightDelaysFlightDeparted represents a FlightDeparted event raised by the FlightDelays contract.
type FlightDelaysFlightDeparted struct {
	AirlineId [32]byte
	FlightId  [32]byte
	Raw       types.Log // Blockchain specific contextual infos
}

// FilterFlightDeparted is a free log retrieval operation binding the contract event 0x2894e01f1028238685425dd73fa9ec9fea4ea16dfd68e0fc5ff8985cd6e1a183.
//
// Solidity: event FlightDeparted(bytes32 indexed airlineId, bytes32 indexed flightId)
func (_FlightDelays *FlightDelaysFilterer) FilterFlightDeparted(opts *bind.FilterOpts, airlineId [][32]byte, flightId [][32]byte) (*FlightDelaysFlightDepartedIterator, error) {

	var airlineIdRule []interface{}
	for _, airlineIdItem := range airlineId {
		airlineIdRule = append(airlineIdRule, airlineIdItem)
	}
	var flightIdRule []interface{}
	for _, flightIdItem := range flightId {
		flightIdRule = append(flightIdRule, flightIdItem)
	}

	logs, sub, err := _FlightDelays.contract.FilterLogs(opts, "FlightDeparted", airlineIdRule, flightIdRule)
	if err != nil {
		return nil, err
	}
	return &FlightDelaysFlightDepartedIterator{contract: _FlightDelays.contract, event: "FlightDeparted", logs: logs, sub: sub}, nil
}

// WatchFlightDeparted is a free log subscription operation binding the contract event 0x2894e01f1028238685425dd73fa9ec9fea4ea16dfd68e0fc5ff8985cd6e1a183.
//
// Solidity: event FlightDeparted(bytes32 indexed airlineId, bytes32 indexed flightId)
func (_FlightDelays *FlightDelaysFilterer) WatchFlightDeparted(opts *bind.WatchOpts, sink chan<- *FlightDelaysFlightDeparted, airlineId [][32]byte, flightId [][32]byte) (event.Subscription, error) {

	var airlineIdRule []interface{}
	for _, airlineIdItem := range airlineId {
		airlineIdRule = append(airlineIdRule, airlineIdItem)
	}
	var flightIdRule []interface{}
	for _, flightIdItem := range flightId {
		flightIdRule = append(flightIdRule, flightIdItem)
	}

	logs, sub, err := _FlightDelays.contract.WatchLogs(opts, "FlightDeparted", airlineIdRule, flightIdRule)
	if err != nil {
		return nil, err
	}
	return event.NewSubscription(func(quit <-chan struct{}) error {
		defer sub.Unsubscribe()
		for {
			select {
			case log := <-logs:
				// New log arrived, parse the event and forward to the user
				event := new(FlightDelaysFlightDeparted)
				if err := _FlightDelays.contract.UnpackLog(event, "FlightDeparted", log); err != nil {
					return err
				}
				event.Raw = log

				select {
				case sink <- event:
				case err := <-sub.Err():
					return err
				case <-quit:
					return nil
				}
			case err := <-sub.Err():
				return err
			case <-quit:
				return nil
			}
		}
	}), nil
}

// ParseFlightDeparted is a log parse operation binding the contract event 0x2894e01f1028238685425dd73fa9ec9fea4ea16dfd68e0fc5ff8985cd6e1a183.
//
// Solidity: event FlightDeparted(bytes32 indexed airlineId, bytes32 indexed flightId)
func (_FlightDelays *FlightDelaysFilterer) ParseFlightDeparted(log types.Log) (*FlightDelaysFlightDeparted, error) {
	event := new(FlightDelaysFlightDeparted)
	if err := _FlightDelays.contract.UnpackLog(event, "FlightDeparted", log); err != nil {
		return nil, err
	}
	event.Raw = log
	return event, nil
}

// FlightDelaysInitSubnetworkIterator is returned from FilterInitSubnetwork and is used to iterate over the raw logs and unpacked data for InitSubnetwork events raised by the FlightDelays contract.
type FlightDelaysInitSubnetworkIterator struct {
	Event *FlightDelaysInitSubnetwork // Event containing the contract specifics and raw log

	contract *bind.BoundContract // Generic contract to use for unpacking event data
	event    string              // Event name to use for unpacking event data

	logs chan types.Log        // Log channel receiving the found contract events
	sub  ethereum.Subscription // Subscription for errors, completion and termination
	done bool                  // Whether the subscription completed delivering logs
	fail error                 // Occurred error to stop iteration
}

// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *FlightDelaysInitSubnetworkIterator) Next() bool {
	// If the iterator failed, stop iterating
	if it.fail != nil {
		return false
	}
	// If the iterator completed, deliver directly whatever's available
	if it.done {
		select {
		case log := <-it.logs:
			it.Event = new(FlightDelaysInitSubnetwork)
			if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
				it.fail = err
				return false
			}
			it.Event.Raw = log
			return true

		default:
			return false
		}
	}
	// Iterator still in progress, wait for either a data or an error event
	select {
	case log := <-it.logs:
		it.Event = new(FlightDelaysInitSubnetwork)
		if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
			it.fail = err
			return false
		}
		it.Event.Raw = log
		return true

	case err := <-it.sub.Err():
		it.done = true
		it.fail = err
		return it.Next()
	}
}

// Error returns any retrieval or parsing error occurred during filtering.
func (it *FlightDelaysInitSubnetworkIterator) Error() error {
	return it.fail
}

// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *FlightDelaysInitSubnetworkIterator) Close() error {
	it.sub.Unsubscribe()
	return nil
}

// FlightDelaysInitSubnetwork represents a InitSubnetwork event raised by the FlightDelays contract.
type FlightDelaysInitSubnetwork struct {
	Network      common.Address
	SubnetworkId *big.Int
	Raw          types.Log // Blockchain specific contextual infos
}

// FilterInitSubnetwork is a free log retrieval operation binding the contract event 0x469c2e982e7d76d34cf5d1e72abee29749bb9971942c180e9023cea09f5f8e83.
//
// Solidity: event InitSubnetwork(address network, uint96 subnetworkId)
func (_FlightDelays *FlightDelaysFilterer) FilterInitSubnetwork(opts *bind.FilterOpts) (*FlightDelaysInitSubnetworkIterator, error) {

	logs, sub, err := _FlightDelays.contract.FilterLogs(opts, "InitSubnetwork")
	if err != nil {
		return nil, err
	}
	return &FlightDelaysInitSubnetworkIterator{contract: _FlightDelays.contract, event: "InitSubnetwork", logs: logs, sub: sub}, nil
}

// WatchInitSubnetwork is a free log subscription operation binding the contract event 0x469c2e982e7d76d34cf5d1e72abee29749bb9971942c180e9023cea09f5f8e83.
//
// Solidity: event InitSubnetwork(address network, uint96 subnetworkId)
func (_FlightDelays *FlightDelaysFilterer) WatchInitSubnetwork(opts *bind.WatchOpts, sink chan<- *FlightDelaysInitSubnetwork) (event.Subscription, error) {

	logs, sub, err := _FlightDelays.contract.WatchLogs(opts, "InitSubnetwork")
	if err != nil {
		return nil, err
	}
	return event.NewSubscription(func(quit <-chan struct{}) error {
		defer sub.Unsubscribe()
		for {
			select {
			case log := <-logs:
				// New log arrived, parse the event and forward to the user
				event := new(FlightDelaysInitSubnetwork)
				if err := _FlightDelays.contract.UnpackLog(event, "InitSubnetwork", log); err != nil {
					return err
				}
				event.Raw = log

				select {
				case sink <- event:
				case err := <-sub.Err():
					return err
				case <-quit:
					return nil
				}
			case err := <-sub.Err():
				return err
			case <-quit:
				return nil
			}
		}
	}), nil
}

// ParseInitSubnetwork is a log parse operation binding the contract event 0x469c2e982e7d76d34cf5d1e72abee29749bb9971942c180e9023cea09f5f8e83.
//
// Solidity: event InitSubnetwork(address network, uint96 subnetworkId)
func (_FlightDelays *FlightDelaysFilterer) ParseInitSubnetwork(log types.Log) (*FlightDelaysInitSubnetwork, error) {
	event := new(FlightDelaysInitSubnetwork)
	if err := _FlightDelays.contract.UnpackLog(event, "InitSubnetwork", log); err != nil {
		return nil, err
	}
	event.Raw = log
	return event, nil
}

// FlightDelaysInitializedIterator is returned from FilterInitialized and is used to iterate over the raw logs and unpacked data for Initialized events raised by the FlightDelays contract.
type FlightDelaysInitializedIterator struct {
	Event *FlightDelaysInitialized // Event containing the contract specifics and raw log

	contract *bind.BoundContract // Generic contract to use for unpacking event data
	event    string              // Event name to use for unpacking event data

	logs chan types.Log        // Log channel receiving the found contract events
	sub  ethereum.Subscription // Subscription for errors, completion and termination
	done bool                  // Whether the subscription completed delivering logs
	fail error                 // Occurred error to stop iteration
}

// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *FlightDelaysInitializedIterator) Next() bool {
	// If the iterator failed, stop iterating
	if it.fail != nil {
		return false
	}
	// If the iterator completed, deliver directly whatever's available
	if it.done {
		select {
		case log := <-it.logs:
			it.Event = new(FlightDelaysInitialized)
			if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
				it.fail = err
				return false
			}
			it.Event.Raw = log
			return true

		default:
			return false
		}
	}
	// Iterator still in progress, wait for either a data or an error event
	select {
	case log := <-it.logs:
		it.Event = new(FlightDelaysInitialized)
		if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
			it.fail = err
			return false
		}
		it.Event.Raw = log
		return true

	case err := <-it.sub.Err():
		it.done = true
		it.fail = err
		return it.Next()
	}
}

// Error returns any retrieval or parsing error occurred during filtering.
func (it *FlightDelaysInitializedIterator) Error() error {
	return it.fail
}

// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *FlightDelaysInitializedIterator) Close() error {
	it.sub.Unsubscribe()
	return nil
}

// FlightDelaysInitialized represents a Initialized event raised by the FlightDelays contract.
type FlightDelaysInitialized struct {
	Version uint64
	Raw     types.Log // Blockchain specific contextual infos
}

// FilterInitialized is a free log retrieval operation binding the contract event 0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2.
//
// Solidity: event Initialized(uint64 version)
func (_FlightDelays *FlightDelaysFilterer) FilterInitialized(opts *bind.FilterOpts) (*FlightDelaysInitializedIterator, error) {

	logs, sub, err := _FlightDelays.contract.FilterLogs(opts, "Initialized")
	if err != nil {
		return nil, err
	}
	return &FlightDelaysInitializedIterator{contract: _FlightDelays.contract, event: "Initialized", logs: logs, sub: sub}, nil
}

// WatchInitialized is a free log subscription operation binding the contract event 0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2.
//
// Solidity: event Initialized(uint64 version)
func (_FlightDelays *FlightDelaysFilterer) WatchInitialized(opts *bind.WatchOpts, sink chan<- *FlightDelaysInitialized) (event.Subscription, error) {

	logs, sub, err := _FlightDelays.contract.WatchLogs(opts, "Initialized")
	if err != nil {
		return nil, err
	}
	return event.NewSubscription(func(quit <-chan struct{}) error {
		defer sub.Unsubscribe()
		for {
			select {
			case log := <-logs:
				// New log arrived, parse the event and forward to the user
				event := new(FlightDelaysInitialized)
				if err := _FlightDelays.contract.UnpackLog(event, "Initialized", log); err != nil {
					return err
				}
				event.Raw = log

				select {
				case sink <- event:
				case err := <-sub.Err():
					return err
				case <-quit:
					return nil
				}
			case err := <-sub.Err():
				return err
			case <-quit:
				return nil
			}
		}
	}), nil
}

// ParseInitialized is a log parse operation binding the contract event 0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2.
//
// Solidity: event Initialized(uint64 version)
func (_FlightDelays *FlightDelaysFilterer) ParseInitialized(log types.Log) (*FlightDelaysInitialized, error) {
	event := new(FlightDelaysInitialized)
	if err := _FlightDelays.contract.UnpackLog(event, "Initialized", log); err != nil {
		return nil, err
	}
	event.Raw = log
	return event, nil
}

// FlightDelaysInsuranceClaimedIterator is returned from FilterInsuranceClaimed and is used to iterate over the raw logs and unpacked data for InsuranceClaimed events raised by the FlightDelays contract.
type FlightDelaysInsuranceClaimedIterator struct {
	Event *FlightDelaysInsuranceClaimed // Event containing the contract specifics and raw log

	contract *bind.BoundContract // Generic contract to use for unpacking event data
	event    string              // Event name to use for unpacking event data

	logs chan types.Log        // Log channel receiving the found contract events
	sub  ethereum.Subscription // Subscription for errors, completion and termination
	done bool                  // Whether the subscription completed delivering logs
	fail error                 // Occurred error to stop iteration
}

// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *FlightDelaysInsuranceClaimedIterator) Next() bool {
	// If the iterator failed, stop iterating
	if it.fail != nil {
		return false
	}
	// If the iterator completed, deliver directly whatever's available
	if it.done {
		select {
		case log := <-it.logs:
			it.Event = new(FlightDelaysInsuranceClaimed)
			if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
				it.fail = err
				return false
			}
			it.Event.Raw = log
			return true

		default:
			return false
		}
	}
	// Iterator still in progress, wait for either a data or an error event
	select {
	case log := <-it.logs:
		it.Event = new(FlightDelaysInsuranceClaimed)
		if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
			it.fail = err
			return false
		}
		it.Event.Raw = log
		return true

	case err := <-it.sub.Err():
		it.done = true
		it.fail = err
		return it.Next()
	}
}

// Error returns any retrieval or parsing error occurred during filtering.
func (it *FlightDelaysInsuranceClaimedIterator) Error() error {
	return it.fail
}

// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *FlightDelaysInsuranceClaimedIterator) Close() error {
	it.sub.Unsubscribe()
	return nil
}

// FlightDelaysInsuranceClaimed represents a InsuranceClaimed event raised by the FlightDelays contract.
type FlightDelaysInsuranceClaimed struct {
	AirlineId [32]byte
	FlightId  [32]byte
	Buyer     common.Address
	Payout    *big.Int
	Raw       types.Log // Blockchain specific contextual infos
}

// FilterInsuranceClaimed is a free log retrieval operation binding the contract event 0x57e4f7b973a879494a4ada5b47e55405e342f437cf7280d04008aac8dde6a588.
//
// Solidity: event InsuranceClaimed(bytes32 indexed airlineId, bytes32 indexed flightId, address indexed buyer, uint256 payout)
func (_FlightDelays *FlightDelaysFilterer) FilterInsuranceClaimed(opts *bind.FilterOpts, airlineId [][32]byte, flightId [][32]byte, buyer []common.Address) (*FlightDelaysInsuranceClaimedIterator, error) {

	var airlineIdRule []interface{}
	for _, airlineIdItem := range airlineId {
		airlineIdRule = append(airlineIdRule, airlineIdItem)
	}
	var flightIdRule []interface{}
	for _, flightIdItem := range flightId {
		flightIdRule = append(flightIdRule, flightIdItem)
	}
	var buyerRule []interface{}
	for _, buyerItem := range buyer {
		buyerRule = append(buyerRule, buyerItem)
	}

	logs, sub, err := _FlightDelays.contract.FilterLogs(opts, "InsuranceClaimed", airlineIdRule, flightIdRule, buyerRule)
	if err != nil {
		return nil, err
	}
	return &FlightDelaysInsuranceClaimedIterator{contract: _FlightDelays.contract, event: "InsuranceClaimed", logs: logs, sub: sub}, nil
}

// WatchInsuranceClaimed is a free log subscription operation binding the contract event 0x57e4f7b973a879494a4ada5b47e55405e342f437cf7280d04008aac8dde6a588.
//
// Solidity: event InsuranceClaimed(bytes32 indexed airlineId, bytes32 indexed flightId, address indexed buyer, uint256 payout)
func (_FlightDelays *FlightDelaysFilterer) WatchInsuranceClaimed(opts *bind.WatchOpts, sink chan<- *FlightDelaysInsuranceClaimed, airlineId [][32]byte, flightId [][32]byte, buyer []common.Address) (event.Subscription, error) {

	var airlineIdRule []interface{}
	for _, airlineIdItem := range airlineId {
		airlineIdRule = append(airlineIdRule, airlineIdItem)
	}
	var flightIdRule []interface{}
	for _, flightIdItem := range flightId {
		flightIdRule = append(flightIdRule, flightIdItem)
	}
	var buyerRule []interface{}
	for _, buyerItem := range buyer {
		buyerRule = append(buyerRule, buyerItem)
	}

	logs, sub, err := _FlightDelays.contract.WatchLogs(opts, "InsuranceClaimed", airlineIdRule, flightIdRule, buyerRule)
	if err != nil {
		return nil, err
	}
	return event.NewSubscription(func(quit <-chan struct{}) error {
		defer sub.Unsubscribe()
		for {
			select {
			case log := <-logs:
				// New log arrived, parse the event and forward to the user
				event := new(FlightDelaysInsuranceClaimed)
				if err := _FlightDelays.contract.UnpackLog(event, "InsuranceClaimed", log); err != nil {
					return err
				}
				event.Raw = log

				select {
				case sink <- event:
				case err := <-sub.Err():
					return err
				case <-quit:
					return nil
				}
			case err := <-sub.Err():
				return err
			case <-quit:
				return nil
			}
		}
	}), nil
}

// ParseInsuranceClaimed is a log parse operation binding the contract event 0x57e4f7b973a879494a4ada5b47e55405e342f437cf7280d04008aac8dde6a588.
//
// Solidity: event InsuranceClaimed(bytes32 indexed airlineId, bytes32 indexed flightId, address indexed buyer, uint256 payout)
func (_FlightDelays *FlightDelaysFilterer) ParseInsuranceClaimed(log types.Log) (*FlightDelaysInsuranceClaimed, error) {
	event := new(FlightDelaysInsuranceClaimed)
	if err := _FlightDelays.contract.UnpackLog(event, "InsuranceClaimed", log); err != nil {
		return nil, err
	}
	event.Raw = log
	return event, nil
}

// FlightDelaysInsurancePurchasedIterator is returned from FilterInsurancePurchased and is used to iterate over the raw logs and unpacked data for InsurancePurchased events raised by the FlightDelays contract.
type FlightDelaysInsurancePurchasedIterator struct {
	Event *FlightDelaysInsurancePurchased // Event containing the contract specifics and raw log

	contract *bind.BoundContract // Generic contract to use for unpacking event data
	event    string              // Event name to use for unpacking event data

	logs chan types.Log        // Log channel receiving the found contract events
	sub  ethereum.Subscription // Subscription for errors, completion and termination
	done bool                  // Whether the subscription completed delivering logs
	fail error                 // Occurred error to stop iteration
}

// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *FlightDelaysInsurancePurchasedIterator) Next() bool {
	// If the iterator failed, stop iterating
	if it.fail != nil {
		return false
	}
	// If the iterator completed, deliver directly whatever's available
	if it.done {
		select {
		case log := <-it.logs:
			it.Event = new(FlightDelaysInsurancePurchased)
			if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
				it.fail = err
				return false
			}
			it.Event.Raw = log
			return true

		default:
			return false
		}
	}
	// Iterator still in progress, wait for either a data or an error event
	select {
	case log := <-it.logs:
		it.Event = new(FlightDelaysInsurancePurchased)
		if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
			it.fail = err
			return false
		}
		it.Event.Raw = log
		return true

	case err := <-it.sub.Err():
		it.done = true
		it.fail = err
		return it.Next()
	}
}

// Error returns any retrieval or parsing error occurred during filtering.
func (it *FlightDelaysInsurancePurchasedIterator) Error() error {
	return it.fail
}

// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *FlightDelaysInsurancePurchasedIterator) Close() error {
	it.sub.Unsubscribe()
	return nil
}

// FlightDelaysInsurancePurchased represents a InsurancePurchased event raised by the FlightDelays contract.
type FlightDelaysInsurancePurchased struct {
	AirlineId [32]byte
	FlightId  [32]byte
	Buyer     common.Address
	Premium   *big.Int
	Raw       types.Log // Blockchain specific contextual infos
}

// FilterInsurancePurchased is a free log retrieval operation binding the contract event 0xb5741046414a47afe0df7618cfbff25d005a117a6e9aa4dde31c6c8ffb6ab4d5.
//
// Solidity: event InsurancePurchased(bytes32 indexed airlineId, bytes32 indexed flightId, address indexed buyer, uint256 premium)
func (_FlightDelays *FlightDelaysFilterer) FilterInsurancePurchased(opts *bind.FilterOpts, airlineId [][32]byte, flightId [][32]byte, buyer []common.Address) (*FlightDelaysInsurancePurchasedIterator, error) {

	var airlineIdRule []interface{}
	for _, airlineIdItem := range airlineId {
		airlineIdRule = append(airlineIdRule, airlineIdItem)
	}
	var flightIdRule []interface{}
	for _, flightIdItem := range flightId {
		flightIdRule = append(flightIdRule, flightIdItem)
	}
	var buyerRule []interface{}
	for _, buyerItem := range buyer {
		buyerRule = append(buyerRule, buyerItem)
	}

	logs, sub, err := _FlightDelays.contract.FilterLogs(opts, "InsurancePurchased", airlineIdRule, flightIdRule, buyerRule)
	if err != nil {
		return nil, err
	}
	return &FlightDelaysInsurancePurchasedIterator{contract: _FlightDelays.contract, event: "InsurancePurchased", logs: logs, sub: sub}, nil
}

// WatchInsurancePurchased is a free log subscription operation binding the contract event 0xb5741046414a47afe0df7618cfbff25d005a117a6e9aa4dde31c6c8ffb6ab4d5.
//
// Solidity: event InsurancePurchased(bytes32 indexed airlineId, bytes32 indexed flightId, address indexed buyer, uint256 premium)
func (_FlightDelays *FlightDelaysFilterer) WatchInsurancePurchased(opts *bind.WatchOpts, sink chan<- *FlightDelaysInsurancePurchased, airlineId [][32]byte, flightId [][32]byte, buyer []common.Address) (event.Subscription, error) {

	var airlineIdRule []interface{}
	for _, airlineIdItem := range airlineId {
		airlineIdRule = append(airlineIdRule, airlineIdItem)
	}
	var flightIdRule []interface{}
	for _, flightIdItem := range flightId {
		flightIdRule = append(flightIdRule, flightIdItem)
	}
	var buyerRule []interface{}
	for _, buyerItem := range buyer {
		buyerRule = append(buyerRule, buyerItem)
	}

	logs, sub, err := _FlightDelays.contract.WatchLogs(opts, "InsurancePurchased", airlineIdRule, flightIdRule, buyerRule)
	if err != nil {
		return nil, err
	}
	return event.NewSubscription(func(quit <-chan struct{}) error {
		defer sub.Unsubscribe()
		for {
			select {
			case log := <-logs:
				// New log arrived, parse the event and forward to the user
				event := new(FlightDelaysInsurancePurchased)
				if err := _FlightDelays.contract.UnpackLog(event, "InsurancePurchased", log); err != nil {
					return err
				}
				event.Raw = log

				select {
				case sink <- event:
				case err := <-sub.Err():
					return err
				case <-quit:
					return nil
				}
			case err := <-sub.Err():
				return err
			case <-quit:
				return nil
			}
		}
	}), nil
}

// ParseInsurancePurchased is a log parse operation binding the contract event 0xb5741046414a47afe0df7618cfbff25d005a117a6e9aa4dde31c6c8ffb6ab4d5.
//
// Solidity: event InsurancePurchased(bytes32 indexed airlineId, bytes32 indexed flightId, address indexed buyer, uint256 premium)
func (_FlightDelays *FlightDelaysFilterer) ParseInsurancePurchased(log types.Log) (*FlightDelaysInsurancePurchased, error) {
	event := new(FlightDelaysInsurancePurchased)
	if err := _FlightDelays.contract.UnpackLog(event, "InsurancePurchased", log); err != nil {
		return nil, err
	}
	event.Raw = log
	return event, nil
}
```

## File: off-chain/internal/flights/store_test.go

```go
package flights

import (
	"testing"
	"time"
)

func TestUpdateStatusAllowsDelayedToDeparted(t *testing.T) {
	store := NewStore(
		[]Airline{{AirlineID: "ALPHA", Name: "Alpha Air"}},
		[]Flight{{AirlineID: "ALPHA", FlightID: "ALPHA-1", DepartureTimestamp: time.Now().Unix(), Status: StatusScheduled}},
	)

	_, err := store.UpdateStatus("ALPHA", "ALPHA-1", StatusDelayed)
	if err != nil {
		t.Fatalf("expected delayed update to succeed, got %v", err)
	}

	updated, err := store.UpdateStatus("ALPHA", "ALPHA-1", StatusDeparted)
	if err != nil {
		t.Fatalf("expected delayed->departed transition to succeed, got %v", err)
	}
	if updated.Status != StatusDeparted {
		t.Fatalf("expected status %s, got %s", StatusDeparted, updated.Status)
	}
}

func TestUpdateStatusRejectsInvalidTransitions(t *testing.T) {
	now := time.Now().Unix()
	store := NewStore(
		[]Airline{{AirlineID: "BETA", Name: "Beta Wings"}},
		[]Flight{{AirlineID: "BETA", FlightID: "BETA-1", DepartureTimestamp: now, Status: StatusDeparted}},
	)

	if _, err := store.UpdateStatus("BETA", "BETA-1", StatusDelayed); err == nil {
		t.Fatalf("expected depart->delayed transition to fail")
	}
}
```

## File: off-chain/internal/flights/store.go

```go
package flights

import (
	"sort"
	"strings"
	"sync"
	"time"
)

// Store keeps airlines and flights in memory for the mock API.
type Store struct {
	mu       sync.RWMutex
	airlines map[string]Airline
	flights  map[string]map[string]*Flight // airlineID -> flightID -> Flight
}

// NewStore creates an in-memory store seeded with the provided airlines and flights.
func NewStore(initialAirlines []Airline, initialFlights []Flight) *Store {
	s := &Store{
		airlines: make(map[string]Airline),
		flights:  make(map[string]map[string]*Flight),
	}
	for _, airline := range initialAirlines {
		_ = s.AddAirline(airline)
	}
	s.mu.Lock()
	for _, flight := range initialFlights {
		if flight.Status == "" {
			flight.Status = StatusScheduled
		}
		_ = s.createFlightLocked(flight.AirlineID, flight)
	}
	s.mu.Unlock()
	return s
}

// ListAirlines returns airlines sorted alphabetically by code then name.
func (s *Store) ListAirlines() []Airline {
	s.mu.RLock()
	defer s.mu.RUnlock()
	items := make([]Airline, 0, len(s.airlines))
	for _, airline := range s.airlines {
		items = append(items, airline)
	}
	sort.Slice(items, func(i, j int) bool {
		if strings.EqualFold(items[i].Code, items[j].Code) {
			return items[i].Name < items[j].Name
		}
		return items[i].Code < items[j].Code
	})
	return items
}

// AddAirline registers a new airline.
func (s *Store) AddAirline(airline Airline) error {
	if strings.TrimSpace(airline.AirlineID) == "" {
		return ErrInvalidAirline
	}
	s.mu.Lock()
	defer s.mu.Unlock()
	if _, ok := s.airlines[airline.AirlineID]; ok {
		return ErrAirlineExists
	}
	s.airlines[airline.AirlineID] = airline
	return nil
}

// ListFlights returns flights for the given airline sorted by departure.
func (s *Store) ListFlights(airlineID string) ([]Flight, error) {
	s.mu.RLock()
	defer s.mu.RUnlock()
	if _, ok := s.airlines[airlineID]; !ok {
		return nil, ErrAirlineNotFound
	}
	flightMap := s.flights[airlineID]
	items := make([]Flight, 0, len(flightMap))
	for _, flight := range flightMap {
		items = append(items, *flight)
	}
	sort.Slice(items, func(i, j int) bool {
		if items[i].DepartureTimestamp == items[j].DepartureTimestamp {
			return items[i].FlightID < items[j].FlightID
		}
		return items[i].DepartureTimestamp < items[j].DepartureTimestamp
	})
	return items, nil
}

// GetFlight returns a specific flight copy.
func (s *Store) GetFlight(airlineID, flightID string) (Flight, error) {
	s.mu.RLock()
	defer s.mu.RUnlock()
	flightMap, ok := s.flights[airlineID]
	if !ok {
		return Flight{}, ErrAirlineNotFound
	}
	flight, ok := flightMap[flightID]
	if !ok {
		return Flight{}, ErrFlightNotFound
	}
	return *flight, nil
}

// CreateFlight registers a new flight for an airline.
func (s *Store) CreateFlight(airlineID string, flight Flight) (Flight, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	if err := s.createFlightLocked(airlineID, flight); err != nil {
		return Flight{}, err
	}
	return *s.flights[airlineID][flight.FlightID], nil
}

func (s *Store) createFlightLocked(airlineID string, flight Flight) error {
	if flight.AirlineID == "" {
		flight.AirlineID = airlineID
	}
	if flight.Status == "" {
		flight.Status = StatusScheduled
	}
	if strings.TrimSpace(flight.FlightID) == "" {
		return ErrInvalidFlight
	}
	if !validStatus(flight.Status) {
		return ErrInvalidStatus
	}
	if _, ok := s.airlines[airlineID]; !ok {
		return ErrAirlineNotFound
	}
	if s.flights[airlineID] == nil {
		s.flights[airlineID] = make(map[string]*Flight)
	}
	if _, exists := s.flights[airlineID][flight.FlightID]; exists {
		return ErrFlightExists
	}
	flight.UpdatedAt = time.Now().Unix()
	copy := flight
	s.flights[airlineID][flight.FlightID] = &copy
	return nil
}

// UpdateStatus updates the status for a flight with basic validation.
func (s *Store) UpdateStatus(airlineID, flightID string, status Status) (Flight, error) {
	if !validStatus(status) {
		return Flight{}, ErrInvalidStatus
	}
	s.mu.Lock()
	defer s.mu.Unlock()
	flightMap, ok := s.flights[airlineID]
	if !ok {
		return Flight{}, ErrAirlineNotFound
	}
	flight, ok := flightMap[flightID]
	if !ok {
		return Flight{}, ErrFlightNotFound
	}
	if !isValidTransition(flight.Status, status) {
		return Flight{}, ErrInvalidStatusTransition
	}
	flight.Status = status
	flight.UpdatedAt = time.Now().Unix()
	return *flight, nil
}

func isValidTransition(from, to Status) bool {
	switch from {
	case StatusScheduled:
		return to == StatusDelayed || to == StatusDeparted || to == StatusScheduled
	case StatusDelayed:
		return to == StatusDelayed || to == StatusDeparted
	case StatusDeparted:
		return to == StatusDeparted
	default:
		return false
	}
}
```

## File: off-chain/internal/flights/types.go

```go
package flights

import "errors"

// Status describes the lifecycle state of a flight tracked by the mock API.
type Status string

const (
	StatusScheduled Status = "SCHEDULED"
	StatusDelayed   Status = "DELAYED"
	StatusDeparted  Status = "DEPARTED"
)

// Airline represents a carrier that can have flights scheduled on-chain.
type Airline struct {
	AirlineID string `json:"airlineId"`
	Name      string `json:"name"`
	Code      string `json:"code"`
}

// Flight is a single tracked flight instance owned by an airline.
type Flight struct {
	AirlineID          string `json:"airlineId"`
	FlightID           string `json:"flightId"`
	DepartureTimestamp int64  `json:"departureTimestamp"`
	Status             Status `json:"status"`
	UpdatedAt          int64  `json:"updatedAt"`
}

var (
	ErrAirlineExists           = errors.New("airline already exists")
	ErrAirlineNotFound         = errors.New("airline not found")
	ErrInvalidAirline          = errors.New("invalid airline id")
	ErrFlightExists            = errors.New("flight already exists")
	ErrFlightNotFound          = errors.New("flight not found")
	ErrInvalidFlight           = errors.New("invalid flight id")
	ErrInvalidStatusTransition = errors.New("invalid flight status transition")
	ErrInvalidStatus           = errors.New("invalid flight status")
)

// validStatus reports whether the provided status is recognised by the API.
func validStatus(status Status) bool {
	switch status {
	case StatusScheduled, StatusDelayed, StatusDeparted:
		return true
	default:
		return false
	}
}
```

## File: off-chain/internal/utils/util.go

```go
package utils

import (
	"time"

	grpcmiddleware "github.com/grpc-ecosystem/go-grpc-middleware"
	"github.com/grpc-ecosystem/go-grpc-middleware/retry"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

func GetGRPCConnection(address string) (*grpc.ClientConn, error) {
	retryOpts := []grpc_retry.CallOption{
		grpc_retry.WithMax(3),
		grpc_retry.WithBackoff(grpc_retry.BackoffLinear(time.Second)),
	}
	unaryInterceptors := []grpc.UnaryClientInterceptor{grpc_retry.UnaryClientInterceptor(retryOpts...)}
	opts := []grpc.DialOption{
		grpc.WithStreamInterceptor(grpc_retry.StreamClientInterceptor(retryOpts...)),
		grpc.WithUnaryInterceptor(grpcmiddleware.ChainUnaryClient(unaryInterceptors...)),
		grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(100*1024*1024), grpc.MaxCallSendMsgSize(100*1024*1024)),
	}

	opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))

	return grpc.NewClient(address, opts...)
}
```

## File: off-chain/.dockerignore

```
# Git files
.git
.gitignore

# Docker files
Dockerfile
.dockerignore

# Documentation
README.md
*.md

# IDE files
.vscode/
.idea/
*.swp
*.swo

# OS files
.DS_Store
Thumbs.db

# Logs
*.log

# Temporary files
*.tmp
*.temp

# Test files
*_test.go
test/

# Build artifacts
bin/
dist/

# Coverage files
*.out
coverage.txt

# Go specific
vendor/
```

## File: off-chain/Dockerfile

```
# Multi-stage build for the off-chain Go application
# Build stage
FROM golang:1.24.5-alpine AS builder

# Install build dependencies
RUN apk add --no-cache git ca-certificates tzdata

# Set working directory
WORKDIR /app

# Copy go mod files
COPY go.mod go.sum ./

# Download dependencies
RUN go mod download

# Copy source code
COPY . .

# Build the binaries with optimizations for production
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
    -ldflags="-w -s" \
    -o /app/flight-node \
    ./cmd/node

RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
    -ldflags="-w -s" \
    -o /app/flights-api \
    ./cmd/flights-api

# Runtime stage
FROM alpine:3.19

# Install runtime dependencies
RUN apk add --no-cache ca-certificates tzdata

# Set working directory
WORKDIR /app

# Copy binaries from builder stage
COPY --from=builder /app/flight-node .
COPY --from=builder /app/flights-api .

# Expose port if needed (adjust based on your application)
# EXPOSE 8080

# Set the entrypoint
ENTRYPOINT ["/app/flight-node"]

# Default command (can be overridden)
CMD ["--help"]
```

## File: off-chain/go.mod

```
module sum

go 1.24.5

require (
	github.com/ethereum/go-ethereum v1.16.3
	github.com/go-chi/chi/v5 v5.2.3
	github.com/go-errors/errors v1.5.1
	github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
	github.com/samber/lo v1.51.0
	github.com/spf13/cobra v1.10.1
	github.com/symbioticfi/relay v0.2.1-0.20250929084906-8a36673e5ad5
	golang.org/x/sync v0.17.0
	google.golang.org/grpc v1.75.1
)

require (
	github.com/Microsoft/go-winio v0.6.2 // indirect
	github.com/bits-and-blooms/bitset v1.24.0 // indirect
	github.com/consensys/gnark-crypto v0.19.0 // indirect
	github.com/crate-crypto/go-eth-kzg v1.4.0 // indirect
	github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect
	github.com/deckarep/golang-set/v2 v2.8.0 // indirect
	github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
	github.com/ethereum/c-kzg-4844/v2 v2.1.1 // indirect
	github.com/ethereum/go-verkle v0.2.2 // indirect
	github.com/fsnotify/fsnotify v1.9.0 // indirect
	github.com/go-ole/go-ole v1.3.0 // indirect
	github.com/google/uuid v1.6.0 // indirect
	github.com/gorilla/websocket v1.5.3 // indirect
	github.com/holiman/uint256 v1.3.2 // indirect
	github.com/inconshreveable/mousetrap v1.1.0 // indirect
	github.com/rogpeppe/go-internal v1.13.1 // indirect
	github.com/shirou/gopsutil v3.21.11+incompatible // indirect
	github.com/spf13/pflag v1.0.10 // indirect
	github.com/supranational/blst v0.3.15 // indirect
	github.com/tklauser/go-sysconf v0.3.15 // indirect
	github.com/tklauser/numcpus v0.10.0 // indirect
	github.com/yusufpapurcu/wmi v1.2.4 // indirect
	golang.org/x/crypto v0.42.0 // indirect
	golang.org/x/net v0.44.0 // indirect
	golang.org/x/sys v0.36.0 // indirect
	golang.org/x/text v0.29.0 // indirect
	google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 // indirect
	google.golang.org/protobuf v1.36.9 // indirect
)
```

## File: script/mocks/MockERC20.sol

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MockERC20 is ERC20 {
    constructor(string memory name, string memory symbol) ERC20(name, symbol) {
        _mint(msg.sender, 1_000_000_000_000_000_000_000_000);
    }

    function mint(address to, uint256 amount) public {
        _mint(to, amount);
    }
}
```

## File: script/mocks/SymbioticRewardsConstantsHelper.sol

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import {SymbioticRewardsConstants} from "@symbioticfi/rewards/test/integration/SymbioticRewardsConstants.sol";

contract SymbioticRewardsConstantsHelper {
    function defaultStakerRewardsFactory() public returns (address) {
        return address(SymbioticRewardsConstants.defaultStakerRewardsFactory());
    }
}
```

## File: script/my-relay-deploy.toml

```toml
[31337]
endpoint_url = "http://anvil:8545"

[1234567890]
endpoint_url = ""
keyRegistry = 31337
votingPowerProvider = [31337]
settlement = [31337]
valSetDriver = 31337
```

## File: script/MyRelayDeploy.sol

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import {Vm, VmSafe} from "forge-std/Vm.sol";

import {VotingPowers} from "../src/symbiotic/VotingPowers.sol";
import {FlightDelays} from "../src/FlightDelays.sol";
import {KeyRegistry} from "../src/symbiotic/KeyRegistry.sol";
import {Driver} from "../src/symbiotic/Driver.sol";
import {Settlement} from "../src/symbiotic/Settlement.sol";
import {MockERC20} from "./mocks/MockERC20.sol";
import {BN254G2} from "./utils/BN254G2.sol";
import {SymbioticRewardsConstantsHelper} from "./mocks/SymbioticRewardsConstantsHelper.sol";

import {RelayDeploy} from "@symbioticfi/relay-contracts/script/RelayDeploy.sol";

import {BN254} from "@symbioticfi/relay-contracts/src/libraries/utils/BN254.sol";
import {KeyTags} from "@symbioticfi/relay-contracts/src/libraries/utils/KeyTags.sol";
import {KeyEcdsaSecp256k1} from "@symbioticfi/relay-contracts/src/libraries/keys/KeyEcdsaSecp256k1.sol";
import {KeyBlsBn254} from "@symbioticfi/relay-contracts/src/libraries/keys/KeyBlsBn254.sol";
import {
    SigVerifierBlsBn254Simple
} from "@symbioticfi/relay-contracts/src/modules/settlement/sig-verifiers/SigVerifierBlsBn254Simple.sol";
import {
    SigVerifierBlsBn254ZK
} from "@symbioticfi/relay-contracts/src/modules/settlement/sig-verifiers/SigVerifierBlsBn254ZK.sol";
import {
    IVotingPowerProvider
} from "@symbioticfi/relay-contracts/src/interfaces/modules/voting-power/IVotingPowerProvider.sol";
import {
    IOpNetVaultAutoDeploy
} from "@symbioticfi/relay-contracts/src/interfaces/modules/voting-power/extensions/IOpNetVaultAutoDeploy.sol";
import {INetworkManager} from "@symbioticfi/relay-contracts/src/interfaces/modules/base/INetworkManager.sol";
import {IOzEIP712} from "@symbioticfi/relay-contracts/src/interfaces/modules/base/IOzEIP712.sol";
import {IOzOwnable} from "@symbioticfi/relay-contracts/src/interfaces/modules/common/permissions/IOzOwnable.sol";
import {
    IKeyRegistry,
    KEY_TYPE_BLS_BN254,
    KEY_TYPE_ECDSA_SECP256K1
} from "@symbioticfi/relay-contracts/src/interfaces/modules/key-registry/IKeyRegistry.sol";
import {
    IBaseSlashing
} from "@symbioticfi/relay-contracts/src/interfaces/modules/voting-power/extensions/IBaseSlashing.sol";
import {
    IBaseRewards
} from "@symbioticfi/relay-contracts/src/interfaces/modules/voting-power/extensions/IBaseRewards.sol";
import {IValSetDriver} from "@symbioticfi/relay-contracts/src/interfaces/modules/valset-driver/IValSetDriver.sol";
import {IEpochManager} from "@symbioticfi/relay-contracts/src/interfaces/modules/valset-driver/IEpochManager.sol";
import {ISettlement} from "@symbioticfi/relay-contracts/src/interfaces/modules/settlement/ISettlement.sol";

import {Network, INetwork} from "@symbioticfi/network/src/Network.sol";

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IVault} from "@symbioticfi/core/src/interfaces/vault/IVault.sol";
import {INetworkMiddlewareService} from "@symbioticfi/core/src/interfaces/service/INetworkMiddlewareService.sol";
import {Logs} from "@symbioticfi/core/script/utils/Logs.sol";
import {SymbioticCoreConstants} from "@symbioticfi/core/test/integration/SymbioticCoreConstants.sol";

contract MyRelayDeploy is RelayDeploy {
    using KeyTags for uint8;
    using KeyBlsBn254 for BN254.G1Point;
    using KeyEcdsaSecp256k1 for address;
    using KeyEcdsaSecp256k1 for KeyEcdsaSecp256k1.KEY_ECDSA_SECP256K1;
    using KeyEcdsaSecp256k1 for bytes;
    using BN254 for BN254.G1Point;
    using KeyBlsBn254 for KeyBlsBn254.KEY_BLS_BN254;

    bytes32 internal constant KEY_OWNERSHIP_TYPEHASH = keccak256("KeyOwnership(address operator,bytes key)");

    // Configurable constants
    uint48 internal immutable EPOCH_DURATION = uint48(vm.envOr("EPOCH_TIME", uint256(60)));
    uint48 internal constant SLASHING_WINDOW = 1 days; // 1 day
    uint208 internal constant MAX_VALIDATORS_COUNT = 1000; // 1000 validators
    uint256 internal constant MAX_VOTING_POWER = 2 ** 247; // no max limit
    uint256 internal constant MIN_INCLUSION_VOTING_POWER = 0; // include anyone
    uint248 internal constant QUORUM_THRESHOLD = (uint248(1e18) * 2) / 3 + 1; // 2/3 + 1
    uint8 internal constant REQUIRED_KEY_TAG = 15; // 15 is the default key tag (BLS-BN254/15)
    uint256 internal constant OPERATOR_STAKE_AMOUNT = 100_000;
    uint8 internal constant REQUIRED_KEY_TAG_ECDSA = 16; // 16 is the default key tag for ecdsa keys (ECDSA-SECP256K1/0)
    uint8 internal constant REQUIRED_KEY_TAG_SECONDARY_BLS = 11;
    uint256 internal immutable OPERATOR_COUNT = vm.envOr("OPERATOR_COUNT", uint256(4));
    uint8 internal immutable VERIFICATION_TYPE = uint8(vm.envOr("VERIFICATION_TYPE", uint256(1)));
    uint208 internal immutable NUM_AGGREGATORS = uint208(vm.envOr("NUM_AGGREGATORS", uint256(1)));
    uint208 internal immutable NUM_COMMITTERS = uint208(vm.envOr("NUM_COMMITTERS", uint256(1)));

    // CREATE3 salts
    bytes11 public constant NETWORK_SALT = bytes11("Network");
    bytes11 public constant KEY_REGISTRY_SALT = bytes11("KeyRegistry");
    bytes11 public constant VOTING_POWER_PROVIDER_SALT = bytes11("VPProvider");
    bytes11 public constant SETTLEMENT_SALT = bytes11("Settlement");
    bytes11 public constant VALSET_DRIVER_SALT = bytes11("VSDriver");
    bytes11 public constant FLIGHT_DELAYS_SALT = bytes11("FlightDelay");

    constructor() RelayDeploy("./temp-network/my-relay-deploy.toml") {}

    function getDeployerAddress() internal returns (address deployer) {
        (,, deployer) = vm.readCallers();
    }

    function getStakingToken() internal withoutBroadcast returns (address) {
        if (config.get("staking_token").data.length == 0) {
            vm.broadcast();
            config.set("staking_token", address(new MockERC20("StakingToken", "STK")));
        }
        return config.get("staking_token").toAddress();
    }

    function getInsuranceToken() internal withoutBroadcast returns (address) {
        if (config.get("insurance_token").data.length == 0) {
            vm.broadcast();
            config.set("insurance_token", address(new MockERC20("MockUSD Coin", "mUSDC")));
        }
        return config.get("insurance_token").toAddress();
    }

    function getDefaultStakerRewardsFactory() internal withoutBroadcast returns (address) {
        SymbioticRewardsConstantsHelper helper = new SymbioticRewardsConstantsHelper();
        try helper.defaultStakerRewardsFactory() returns (address factory) {
            return factory;
        } catch {
            if (config.get("default_staker_rewards_factory").data.length == 0) {
                SymbioticCoreConstants.Core memory core = getCore();
                vm.broadcast();
                address implementation = deployCode(
                    "node_modules/@symbioticfi/rewards/out/DefaultStakerRewards.sol/DefaultStakerRewards.json",
                    abi.encode(address(core.vaultFactory), address(core.networkMiddlewareService))
                );
                vm.broadcast();
                address factory = deployCode(
                    "node_modules/@symbioticfi/rewards/out/DefaultStakerRewardsFactory.sol/DefaultStakerRewardsFactory.json",
                    abi.encode(implementation)
                );
                config.set("default_staker_rewards_factory", factory);
            }
            return config.get("default_staker_rewards_factory").toAddress();
        }
    }

    function getNetwork() internal withoutBroadcast returns (address) {
        if (config.get("network").data.length == 0) {
            address[] memory proposersAndExecutors = new address[](1);
            proposersAndExecutors[0] = getDeployerAddress();
            SymbioticCoreConstants.Core memory core = getCore();
            vm.broadcast();
            address networkImpl =
                address(new Network(address(core.networkRegistry), address(core.networkMiddlewareService)));
            config.set(
                "network",
                _deployContract(
                    NETWORK_SALT,
                    networkImpl,
                    abi.encodeCall(
                        INetwork.initialize,
                        (INetwork.NetworkInitParams({
                                globalMinDelay: 0,
                                delayParams: new INetwork.DelayParams[](0),
                                proposers: proposersAndExecutors,
                                executors: proposersAndExecutors,
                                name: "Example Network",
                                metadataURI: "https://example.network",
                                defaultAdminRoleHolder: getDeployerAddress(),
                                nameUpdateRoleHolder: getDeployerAddress(),
                                metadataURIUpdateRoleHolder: getDeployerAddress()
                            }))
                    ),
                    getDeployerAddress(),
                    false
                )
            );
        }
        return config.get("network").toAddress();
    }

    function _keyRegistryParams() internal override returns (address implementation, bytes memory initData) {
        vm.broadcast();
        implementation = address(new KeyRegistry());

        initData = abi.encodeCall(
            KeyRegistry.initialize,
            (IKeyRegistry.KeyRegistryInitParams({
                    ozEip712InitParams: IOzEIP712.OzEIP712InitParams({name: "KeyRegistry", version: "1"})
                }))
        );
    }

    function _votingPowerProviderParams() internal override returns (address implementation, bytes memory initData) {
        vm.startBroadcast();
        implementation = address(
            new VotingPowers(
                address(getCore().operatorRegistry),
                address(getCore().vaultFactory),
                address(getCore().vaultConfigurator)
            )
        );
        vm.stopBroadcast();

        initData = abi.encodeCall(
            VotingPowers.initialize,
            (
                IVotingPowerProvider.VotingPowerProviderInitParams({
                    networkManagerInitParams: INetworkManager.NetworkManagerInitParams({
                        network: getNetwork(), subnetworkId: 0
                    }),
                    ozEip712InitParams: IOzEIP712.OzEIP712InitParams({name: "VotingPowers", version: "1"}),
                    requireSlasher: true,
                    minVaultEpochDuration: SLASHING_WINDOW,
                    token: getStakingToken()
                }),
                IOpNetVaultAutoDeploy.OpNetVaultAutoDeployInitParams({
                    isAutoDeployEnabled: true,
                    config: IOpNetVaultAutoDeploy.AutoDeployConfig({
                        epochDuration: SLASHING_WINDOW,
                        collateral: getStakingToken(),
                        burner: address(0),
                        withSlasher: true,
                        isBurnerHook: false
                    }),
                    isSetMaxNetworkLimitHookEnabled: true
                }),
                IOzOwnable.OzOwnableInitParams({owner: getDeployerAddress()}),
                IBaseRewards.BaseRewardsInitParams({rewarder: getDeployerAddress()}),
                IBaseSlashing.BaseSlashingInitParams({slasher: address(0)})
            )
        );
    }

    function _settlementParams() internal override returns (address implementation, bytes memory initData) {
        vm.startBroadcast();
        implementation = address(new Settlement());

        address verifier;
        if (VERIFICATION_TYPE == 0) {
            address[] memory verifiers = new address[](3);
            verifiers[0] = deployCode("out/Verifier_10.sol/Verifier.json");
            verifiers[1] = deployCode("out/Verifier_100.sol/Verifier.json");
            verifiers[2] = deployCode("out/Verifier_1000.sol/Verifier.json");
            uint256[] memory maxValidators = new uint256[](verifiers.length);
            maxValidators[0] = 10;
            maxValidators[1] = 100;
            maxValidators[2] = 1000;
            verifier = address(new SigVerifierBlsBn254ZK(verifiers, maxValidators));
        } else if (VERIFICATION_TYPE == 1) {
            verifier = address(new SigVerifierBlsBn254Simple());
        } else {
            revert("Invalid verification type");
        }
        vm.stopBroadcast();
        initData = abi.encodeCall(
            Settlement.initialize,
            (
                ISettlement.SettlementInitParams({
                    networkManagerInitParams: INetworkManager.NetworkManagerInitParams({
                        network: getNetwork(), subnetworkId: 0
                    }),
                    ozEip712InitParams: IOzEIP712.OzEIP712InitParams({name: "Settlement", version: "1"}),
                    sigVerifier: verifier
                }),
                getDeployerAddress()
            )
        );
    }

    function _valSetDriverParams() internal override returns (address implementation, bytes memory initData) {
        vm.broadcast();
        implementation = address(new Driver());

        IValSetDriver.QuorumThreshold[] memory quorumThresholds = new IValSetDriver.QuorumThreshold[](3);
        quorumThresholds[0] =
            IValSetDriver.QuorumThreshold({keyTag: REQUIRED_KEY_TAG, quorumThreshold: QUORUM_THRESHOLD});
        quorumThresholds[1] =
            IValSetDriver.QuorumThreshold({keyTag: REQUIRED_KEY_TAG_ECDSA, quorumThreshold: QUORUM_THRESHOLD});
        quorumThresholds[2] =
            IValSetDriver.QuorumThreshold({keyTag: REQUIRED_KEY_TAG_SECONDARY_BLS, quorumThreshold: QUORUM_THRESHOLD});
        uint8[] memory requiredKeyTags = new uint8[](3);
        requiredKeyTags[0] = REQUIRED_KEY_TAG;
        requiredKeyTags[1] = REQUIRED_KEY_TAG_ECDSA;
        requiredKeyTags[2] = REQUIRED_KEY_TAG_SECONDARY_BLS;
        initData = abi.encodeCall(
            Driver.initialize,
            (
                IValSetDriver.ValSetDriverInitParams({
                    networkManagerInitParams: INetworkManager.NetworkManagerInitParams({
                        network: getNetwork(), subnetworkId: 0
                    }),
                    epochManagerInitParams: IEpochManager.EpochManagerInitParams({
                        epochDuration: EPOCH_DURATION, epochDurationTimestamp: 0
                    }),
                    numAggregators: NUM_AGGREGATORS,
                    numCommitters: NUM_COMMITTERS,
                    votingPowerProviders: getVotingPowerProviders(),
                    keysProvider: getKeyRegistry(),
                    settlements: getSettlements(),
                    maxVotingPower: MAX_VOTING_POWER,
                    minInclusionVotingPower: MIN_INCLUSION_VOTING_POWER,
                    maxValidatorsCount: MAX_VALIDATORS_COUNT,
                    requiredKeyTags: requiredKeyTags,
                    quorumThresholds: quorumThresholds,
                    requiredHeaderKeyTag: REQUIRED_KEY_TAG,
                    verificationType: VERIFICATION_TYPE
                }),
                getDeployerAddress()
            )
        );
    }

    function runDeployKeyRegistry() public override {
        deployKeyRegistry({proxyOwner: getDeployerAddress(), isDeployerGuarded: false, salt: KEY_REGISTRY_SALT});

        fundOperators();
        for (uint256 i; i < OPERATOR_COUNT; ++i) {
            configureOperatorKeys(i);
        }
    }

    function runDeployVotingPowerProvider() public override {
        address votingPowerProvider = deployVotingPowerProvider({
            proxyOwner: getDeployerAddress(), isDeployerGuarded: false, salt: VOTING_POWER_PROVIDER_SALT
        });
        address network = getNetwork();
        SymbioticCoreConstants.Core memory core = getCore();
        vm.startBroadcast(getDeployerAddress());
        Network(payable(network))
            .schedule(
                address(core.networkMiddlewareService),
                0,
                abi.encodeWithSelector(INetworkMiddlewareService.setMiddleware.selector, votingPowerProvider),
                bytes32(0),
                bytes32(0),
                0
            );
        Network(payable(network))
            .execute(
                address(core.networkMiddlewareService),
                0,
                abi.encodeWithSelector(INetworkMiddlewareService.setMiddleware.selector, votingPowerProvider),
                bytes32(0),
                bytes32(0)
            );
        vm.stopBroadcast();

        fundOperators();
        for (uint256 i; i < OPERATOR_COUNT; ++i) {
            registerOperator(i, OPERATOR_STAKE_AMOUNT);
        }
        printOperatorsInfo();
    }

    function runDeploySettlement() public override {
        deploySettlement({proxyOwner: getDeployerAddress(), isDeployerGuarded: false, salt: SETTLEMENT_SALT});

        if (config.get("voting_power_provider").data.length != 0) {
            FlightDelays.InitParams memory initParams = FlightDelays.InitParams({
                votingPowers: getVotingPowerProvider(),
                settlement: getSettlement(),
                collateral: getInsuranceToken(),
                vaultEpochDuration: uint48(3 days),
                messageExpiry: uint32(12_000),
                policyWindow: uint48(2 minutes),
                delayWindow: uint48(30 seconds),
                policyPremium: 5 ether,
                policyPayout: 50 ether
            });

            vm.startBroadcast();
            address flightDelays = deployCreate3AndInit(
                bytes32(FLIGHT_DELAYS_SALT),
                abi.encodePacked(
                    type(FlightDelays).creationCode,
                    abi.encode(
                        address(getCore().vaultConfigurator),
                        address(getCore().operatorVaultOptInService),
                        address(getCore().operatorNetworkOptInService),
                        getDefaultStakerRewardsFactory(),
                        address(getCore().operatorRegistry)
                    )
                ),
                abi.encodeCall(FlightDelays.initialize, (initParams))
            );
            vm.stopBroadcast();

            vm.startBroadcast(VotingPowers(getVotingPowerProvider()).owner());
            VotingPowers(getVotingPowerProvider()).setSlasher(flightDelays);
            VotingPowers(getVotingPowerProvider()).setRewarder(flightDelays);
            VotingPowers(getVotingPowerProvider()).setFlightDelays(flightDelays);
            vm.stopBroadcast();

            config.set("flight_delays", flightDelays);
        }

        fundOperators();
    }

    function runDeployValSetDriver() public override {
        deployValSetDriver({proxyOwner: getDeployerAddress(), isDeployerGuarded: false, salt: VALSET_DRIVER_SALT});
        vm.writeJson("", "temp-network/deploy-data/deployment-completed.json");
    }

    function configureOperatorKeys(uint256 index) public {
        Vm.Wallet memory operator = getOperator(index);
        (BN254.G1Point memory g1Key, BN254.G2Point memory g2Key) = getBLSKeys(operator.privateKey);
        KeyRegistry keyRegistry = KeyRegistry(getKeyRegistry().addr);

        vm.startBroadcast(operator.privateKey);
        bytes memory keyBytes = KeyBlsBn254.wrap(g1Key).toBytes();
        bytes32 messageHash = keyRegistry.hashTypedDataV4(
            keccak256(abi.encode(KEY_OWNERSHIP_TYPEHASH, operator.addr, keccak256(keyBytes)))
        );
        BN254.G1Point memory messageG1 = BN254.hashToG1(messageHash);
        BN254.G1Point memory sigG1 = messageG1.scalar_mul(operator.privateKey);
        keyRegistry.setKey(KEY_TYPE_BLS_BN254.getKeyTag(15), keyBytes, abi.encode(sigG1), abi.encode(g2Key));

        // Register BLS-BN254 key with tag 11, not related to header key tag
        uint256 secondaryBLSKey = operator.privateKey + 10_000;
        (g1Key, g2Key) = getBLSKeys(secondaryBLSKey);
        keyBytes = KeyBlsBn254.wrap(g1Key).toBytes();
        messageHash = keyRegistry.hashTypedDataV4(
            keccak256(abi.encode(KEY_OWNERSHIP_TYPEHASH, operator.addr, keccak256(keyBytes)))
        );
        messageG1 = BN254.hashToG1(messageHash);
        sigG1 = messageG1.scalar_mul(secondaryBLSKey);

        keyRegistry.setKey(KEY_TYPE_BLS_BN254.getKeyTag(11), keyBytes, abi.encode(sigG1), abi.encode(g2Key));

        keyBytes = KeyEcdsaSecp256k1.wrap(operator.addr).toBytes();
        messageHash = keyRegistry.hashTypedDataV4(
            keccak256(abi.encode(KEY_OWNERSHIP_TYPEHASH, operator.addr, keccak256(keyBytes)))
        );
        (uint8 v, bytes32 r, bytes32 s) = vm.sign(operator.privateKey, messageHash);
        bytes memory signature = abi.encodePacked(r, s, v);

        // Register ECDSA key
        keyRegistry.setKey(KEY_TYPE_ECDSA_SECP256K1.getKeyTag(0), keyBytes, signature, new bytes(0));
        vm.stopBroadcast();
    }

    function registerOperator(uint256 index, uint256 stakeAmount) public {
        Vm.Wallet memory operator = getOperator(index);
        IERC20 stakingToken = IERC20(getStakingToken());
        VotingPowers votingPowers = VotingPowers(getVotingPowerProvider());

        vm.broadcast();
        stakingToken.transfer(operator.addr, stakeAmount);

        vm.startBroadcast(operator.privateKey);
        getCore().operatorRegistry.registerOperator();
        getCore().operatorNetworkOptInService.optIn(address(getNetwork()));
        votingPowers.registerOperator();
        IVault vault = IVault(votingPowers.getAutoDeployedVault(operator.addr));
        getCore().operatorVaultOptInService.optIn(address(vault));

        stakingToken.approve(address(vault), stakeAmount);
        vault.deposit(address(stakingToken), stakeAmount);
        vm.stopBroadcast();
    }

    function fundOperators() public {
        for (uint256 i; i < OPERATOR_COUNT; ++i) {
            Vm.Wallet memory operator = getOperator(i);
            vm.broadcast();
            payable(operator.addr).transfer(1 ether);
        }
    }

    function getOperator(uint256 index) public returns (VmSafe.Wallet memory operator) {
        // deterministic operator private key
        operator = vm.createWallet(1e18 + index);
        vm.rememberKey(operator.privateKey);
        return operator;
    }

    function getBLSKeys(uint256 privateKey) public returns (BN254.G1Point memory, BN254.G2Point memory) {
        BN254.G1Point memory G1Key = BN254.generatorG1().scalar_mul(privateKey);
        BN254.G2Point memory G2 = BN254.generatorG2();
        (uint256 x1, uint256 x2, uint256 y1, uint256 y2) =
            BN254G2.ECTwistMul(privateKey, G2.X[1], G2.X[0], G2.Y[1], G2.Y[0]);
        return (G1Key, BN254.G2Point([x2, x1], [y2, y1]));
    }

    function printOperatorsInfo() public {
        VotingPowers votingPowers = VotingPowers(getVotingPowerProvider());
        address[] memory operators = votingPowers.getOperators();
        VotingPowers.OperatorVotingPower[] memory operatorVPs = votingPowers.getVotingPowers(new bytes[](0));

        string memory logMessage =
            string.concat("Operators total: ", vm.toString(operators.length), "\n", "Operators:\n");

        for (uint256 i; i < operatorVPs.length; ++i) {
            uint256 totalVotingPower;
            logMessage =
                string.concat(logMessage, "   Address: ", vm.toString(operatorVPs[i].operator), "\n", "   Vaults:\n");
            for (uint256 j; j < operatorVPs[i].vaults.length; ++j) {
                logMessage = string.concat(
                    logMessage,
                    "       Address: ",
                    vm.toString(operatorVPs[i].vaults[j].vault),
                    "\n",
                    "       Voting power: ",
                    vm.toString(operatorVPs[i].vaults[j].value),
                    "\n"
                );
                totalVotingPower += operatorVPs[i].vaults[j].value;
            }
            logMessage = string.concat(logMessage, "   Total voting power: ", vm.toString(totalVotingPower), "\n");
        }

        Logs.log(logMessage);
    }
}
```

## File: snapshots/gas.txt

```
No files changed, compilation skipped

Ran 10 tests for test/FlightDelays.t.sol:FlightDelaysTest
[PASS] testBuyInsuranceAndRecordPolicy() (gas: 1193983)
[PASS] testCannotBuyAfterWindowCloses() (gas: 1069590)
[PASS] testCannotBuyBeforePolicyWindowOpens() (gas: 1069193)
[PASS] testCannotBuyInsuranceWithoutFlight() (gas: 32987)
[PASS] testCannotBuyOnceFlightDelayed() (gas: 1111461)
[PASS] testCompleteFlightDistributesRewards() (gas: 1507642)
[PASS] testCreateFlightRequiresSequentialOrdering() (gas: 944838)
[PASS] testDelayFlightAndClaim() (gas: 1436930)
[PASS] testDelayRequiresEarlierFlightsProcessed() (gas: 1276496)
[PASS] testFlightsMustBeCreatedWithNonDecreasingTimestamp() (gas: 1042914)
Suite result: ok. 10 passed; 0 failed; 0 skipped; finished in 4.51ms (9.57ms CPU time)

╭-----------------------------------------------+-----------------+-------+--------+-------+---------╮
| script/mocks/MockERC20.sol:MockERC20 Contract |                 |       |        |       |         |
+====================================================================================================+
| Deployment Cost                               | Deployment Size |       |        |       |         |
|-----------------------------------------------+-----------------+-------+--------+-------+---------|
| 557307                                        | 3103            |       |        |       |         |
|-----------------------------------------------+-----------------+-------+--------+-------+---------|
|                                               |                 |       |        |       |         |
|-----------------------------------------------+-----------------+-------+--------+-------+---------|
| Function Name                                 | Min             | Avg   | Median | Max   | # Calls |
|-----------------------------------------------+-----------------+-------+--------+-------+---------|
| approve                                       | 46678           | 46678 | 46678  | 46678 | 10      |
|-----------------------------------------------+-----------------+-------+--------+-------+---------|
| balanceOf                                     | 2559            | 2559  | 2559   | 2559  | 2       |
|-----------------------------------------------+-----------------+-------+--------+-------+---------|
| mint                                          | 34227           | 34227 | 34227  | 34227 | 7       |
|-----------------------------------------------+-----------------+-------+--------+-------+---------|
| transfer                                      | 51625           | 51625 | 51625  | 51625 | 7       |
╰-----------------------------------------------+-----------------+-------+--------+-------+---------╯

╭--------------------------------------------+-----------------+--------+--------+--------+---------╮
| src/FlightDelays.sol:FlightDelays Contract |                 |        |        |        |         |
+===================================================================================================+
| Deployment Cost                            | Deployment Size |        |        |        |         |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| 0                                          | 11085           |        |        |        |         |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
|                                            |                 |        |        |        |         |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| Function Name                              | Min             | Avg    | Median | Max    | # Calls |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| airlines                                   | 8959            | 8959   | 8959   | 8959   | 1       |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| buyInsurance                               | 24759           | 72448  | 27489  | 134327 | 7       |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| claimInsurance                             | 52798           | 52798  | 52798  | 52798  | 1       |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| createFlight                               | 40183           | 596529 | 893204 | 893204 | 14      |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| delayFlight                                | 42441           | 73528  | 50150  | 151373 | 4       |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| delayWindow                                | 2407            | 2407   | 2407   | 2407   | 1       |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| departFlight                               | 51356           | 202741 | 202741 | 354126 | 2       |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| flights                                    | 4959            | 4959   | 4959   | 4959   | 2       |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| initialize                                 | 238659          | 238659 | 238659 | 238659 | 10      |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| policies                                   | 2809            | 2809   | 2809   | 2809   | 2       |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| policyWindow                               | 2364            | 2364   | 2364   | 2364   | 1       |
╰--------------------------------------------+-----------------+--------+--------+--------+---------╯

╭------------------------------------------------------+-----------------+------+--------+------+---------╮
| test/mock/FlightDelaysMocks.sol:MockRewards Contract |                 |      |        |      |         |
+=========================================================================================================+
| Deployment Cost                                      | Deployment Size |      |        |      |         |
|------------------------------------------------------+-----------------+------+--------+------+---------|
| 0                                                    | 1188            |      |        |      |         |
|------------------------------------------------------+-----------------+------+--------+------+---------|
|                                                      |                 |      |        |      |         |
|------------------------------------------------------+-----------------+------+--------+------+---------|
| Function Name                                        | Min             | Avg  | Median | Max  | # Calls |
|------------------------------------------------------+-----------------+------+--------+------+---------|
| lastAmount                                           | 2316            | 2316 | 2316   | 2316 | 1       |
|------------------------------------------------------+-----------------+------+--------+------+---------|
| lastSnapshot                                         | 2395            | 2395 | 2395   | 2395 | 1       |
|------------------------------------------------------+-----------------+------+--------+------+---------|
| lastToken                                            | 2325            | 2325 | 2325   | 2325 | 1       |
╰------------------------------------------------------+-----------------+------+--------+------+---------╯

╭-------------------------------------------------------------+-----------------+------+--------+------+---------╮
| test/mock/FlightDelaysMocks.sol:MockRewardsFactory Contract |                 |      |        |      |         |
+================================================================================================================+
| Deployment Cost                                             | Deployment Size |      |        |      |         |
|-------------------------------------------------------------+-----------------+------+--------+------+---------|
| 372083                                                      | 1505            |      |        |      |         |
|-------------------------------------------------------------+-----------------+------+--------+------+---------|
|                                                             |                 |      |        |      |         |
|-------------------------------------------------------------+-----------------+------+--------+------+---------|
| Function Name                                               | Min             | Avg  | Median | Max  | # Calls |
|-------------------------------------------------------------+-----------------+------+--------+------+---------|
| lastRewards                                                 | 2323            | 2323 | 2323   | 2323 | 1       |
╰-------------------------------------------------------------+-----------------+------+--------+------+---------╯

╭------------------------------------------------------+-----------------+------+--------+------+---------╮
| test/mock/FlightDelaysMocks.sol:MockSlasher Contract |                 |      |        |      |         |
+=========================================================================================================+
| Deployment Cost                                      | Deployment Size |      |        |      |         |
|------------------------------------------------------+-----------------+------+--------+------+---------|
| 0                                                    | 1019            |      |        |      |         |
|------------------------------------------------------+-----------------+------+--------+------+---------|
|                                                      |                 |      |        |      |         |
|------------------------------------------------------+-----------------+------+--------+------+---------|
| Function Name                                        | Min             | Avg  | Median | Max  | # Calls |
|------------------------------------------------------+-----------------+------+--------+------+---------|
| slashableStake                                       | 3369            | 3369 | 3369   | 3369 | 3       |
╰------------------------------------------------------+-----------------+------+--------+------+---------╯

╭----------------------------------------------------+-----------------+-------+--------+-------+---------╮
| test/mock/FlightDelaysMocks.sol:MockVault Contract |                 |       |        |       |         |
+=========================================================================================================+
| Deployment Cost                                    | Deployment Size |       |        |       |         |
|----------------------------------------------------+-----------------+-------+--------+-------+---------|
| 0                                                  | 1005            |       |        |       |         |
|----------------------------------------------------+-----------------+-------+--------+-------+---------|
|                                                    |                 |       |        |       |         |
|----------------------------------------------------+-----------------+-------+--------+-------+---------|
| Function Name                                      | Min             | Avg   | Median | Max   | # Calls |
|----------------------------------------------------+-----------------+-------+--------+-------+---------|
| activeStake                                        | 2312            | 2312  | 2312   | 2312  | 3       |
|----------------------------------------------------+-----------------+-------+--------+-------+---------|
| setActiveStake                                     | 26530           | 26530 | 26530  | 26530 | 7       |
|----------------------------------------------------+-----------------+-------+--------+-------+---------|
| slasher                                            | 2325            | 2325  | 2325   | 2325  | 3       |
╰----------------------------------------------------+-----------------+-------+--------+-------+---------╯

╭----------------------------------------------------------------+-----------------+------+--------+------+---------╮
| test/mock/FlightDelaysMocks.sol:MockVaultConfigurator Contract |                 |      |        |      |         |
+===================================================================================================================+
| Deployment Cost                                                | Deployment Size |      |        |      |         |
|----------------------------------------------------------------+-----------------+------+--------+------+---------|
| 714096                                                         | 3227            |      |        |      |         |
|----------------------------------------------------------------+-----------------+------+--------+------+---------|
|                                                                |                 |      |        |      |         |
|----------------------------------------------------------------+-----------------+------+--------+------+---------|
| Function Name                                                  | Min             | Avg  | Median | Max  | # Calls |
|----------------------------------------------------------------+-----------------+------+--------+------+---------|
| lastVault                                                      | 2302            | 2302 | 2302   | 2302 | 7       |
╰----------------------------------------------------------------+-----------------+------+--------+------+---------╯

╭-----------------------------------------------------------+-----------------+-------+--------+-------+---------╮
| test/mock/FlightDelaysMocks.sol:VotingPowersMock Contract |                 |       |        |       |         |
+================================================================================================================+
| Deployment Cost                                           | Deployment Size |       |        |       |         |
|-----------------------------------------------------------+-----------------+-------+--------+-------+---------|
| 739953                                                    | 3350            |       |        |       |         |
|-----------------------------------------------------------+-----------------+-------+--------+-------+---------|
|                                                           |                 |       |        |       |         |
|-----------------------------------------------------------+-----------------+-------+--------+-------+---------|
| Function Name                                             | Min             | Avg   | Median | Max   | # Calls |
|-----------------------------------------------------------+-----------------+-------+--------+-------+---------|
| NETWORK                                                   | 334             | 334   | 334    | 334   | 10      |
|-----------------------------------------------------------+-----------------+-------+--------+-------+---------|
| SUBNETWORK_IDENTIFIER                                     | 271             | 271   | 271    | 271   | 10      |
|-----------------------------------------------------------+-----------------+-------+--------+-------+---------|
| setFlightDelays                                           | 44000           | 44000 | 44000  | 44000 | 10      |
╰-----------------------------------------------------------+-----------------+-------+--------+-------+---------╯

╭------------------------------------------------------+-----------------+------+--------+------+---------╮
| test/mock/SettlementMock.sol:SettlementMock Contract |                 |      |        |      |         |
+=========================================================================================================+
| Deployment Cost                                      | Deployment Size |      |        |      |         |
|------------------------------------------------------+-----------------+------+--------+------+---------|
| 226020                                               | 827             |      |        |      |         |
|------------------------------------------------------+-----------------+------+--------+------+---------|
|                                                      |                 |      |        |      |         |
|------------------------------------------------------+-----------------+------+--------+------+---------|
| Function Name                                        | Min             | Avg  | Median | Max  | # Calls |
|------------------------------------------------------+-----------------+------+--------+------+---------|
| getCaptureTimestampFromValSetHeaderAt                | 372             | 372  | 372    | 372  | 20      |
|------------------------------------------------------+-----------------+------+--------+------+---------|
| getQuorumThresholdFromValSetHeaderAt                 | 308             | 308  | 308    | 308  | 20      |
|------------------------------------------------------+-----------------+------+--------+------+---------|
| getRequiredKeyTagFromValSetHeaderAt                  | 438             | 438  | 438    | 438  | 20      |
|------------------------------------------------------+-----------------+------+--------+------+---------|
| verifyQuorumSigAt                                    | 1249            | 1249 | 1249   | 1249 | 20      |
╰------------------------------------------------------+-----------------+------+--------+------+---------╯


Ran 1 test suite in 8.51ms (4.51ms CPU time): 10 tests passed, 0 failed, 0 skipped (10 total tests)
```

## File: snapshots/sizes.txt

```
No files changed, compilation skipped

╭--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------╮
| Contract                                                                                         | Runtime Size (B) | Initcode Size (B) | Runtime Margin (B) | Initcode Margin (B) |
+====================================================================================================================================================================================+
| Address                                                                                          | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| BN254                                                                                            | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| BN254G2                                                                                          | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| BaseRewardsLogic                                                                                 | 1,606            | 1,658             | 22,970             | 47,494              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| BaseSlashingLogic                                                                                | 4,387            | 4,439             | 20,189             | 44,713              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| Bytes                                                                                            | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| Checkpoints (node_modules/@openzeppelin/contracts/utils/structs/Checkpoints.sol)                 | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| Checkpoints (node_modules/@symbioticfi/core/src/contracts/libraries/Checkpoints.sol)             | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| Checkpoints (node_modules/@symbioticfi/relay-contracts/src/libraries/structs/Checkpoints.sol)    | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| Create2                                                                                          | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| CreateXWrapper                                                                                   | 2,764            | 2,792             | 21,812             | 46,360              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| Driver                                                                                           | 17,519           | 17,547            | 7,057              | 31,605              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| ECDSA                                                                                            | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| ERC1967Proxy                                                                                     | 122              | 934               | 24,454             | 48,218              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| ERC1967Utils                                                                                     | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| ERC4626Math                                                                                      | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| Errors                                                                                           | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| ExtraDataStorageHelper                                                                           | 123              | 173               | 24,453             | 48,979              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| FeeOnTransferToken                                                                               | 1,785            | 3,197             | 22,791             | 45,955              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| FlightDelays                                                                                     | 10,617           | 10,925            | 13,959             | 38,227              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| InputNormalizer                                                                                  | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| KeyBlsBn254                                                                                      | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| KeyEcdsaSecp256k1                                                                                | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| KeyRegistry (node_modules/@symbioticfi/relay-contracts/src/modules/key-registry/KeyRegistry.sol) | 14,998           | 15,026            | 9,578              | 34,126              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| KeyRegistry (src/symbiotic/KeyRegistry.sol)                                                      | 15,267           | 15,295            | 9,309              | 33,857              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| KeyTags                                                                                          | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| Logs                                                                                             | 4,290            | 4,342             | 20,286             | 44,810              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| Math                                                                                             | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| MessageHashUtils                                                                                 | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| MockERC20                                                                                        | 1,821            | 2,911             | 22,755             | 46,241              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| MockRewards                                                                                      | 1,030            | 1,156             | 23,546             | 47,996              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| MockRewardsFactory                                                                               | 1,477            | 1,505             | 23,099             | 47,647              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| MockSlasher                                                                                      | 849              | 987               | 23,727             | 48,165              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| MockVault                                                                                        | 747              | 941               | 23,829             | 48,211              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| MockVaultConfigurator                                                                            | 3,064            | 3,195             | 21,512             | 45,957              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| Network                                                                                          | 12,358           | 12,550            | 12,218             | 36,602              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| OpNetVaultAutoDeployLogic                                                                        | 5,693            | 5,745             | 18,883             | 43,407              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| OperatorRegistryMock                                                                             | 57               | 83                | 24,519             | 49,069              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| OptInServiceMock                                                                                 | 199              | 225               | 24,377             | 48,927              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| Panic                                                                                            | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| PersistentSet                                                                                    | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| ProxyAdmin                                                                                       | 977              | 1,213             | 23,599             | 47,939              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| SafeCast                                                                                         | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| SafeERC20                                                                                        | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| Settlement (src/symbiotic/Settlement.sol)                                                        | 11,798           | 11,826            | 12,778             | 37,326              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| SettlementMock                                                                                   | 799              | 827               | 23,777             | 48,325              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| SigBlsBn254                                                                                      | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| SigEcdsaSecp256k1                                                                                | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| SigVerifierBlsBn254Simple                                                                        | 4,946            | 4,974             | 19,630             | 44,178              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| SigVerifierBlsBn254ZK                                                                            | 2,694            | 3,774             | 21,882             | 45,378              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| SignatureChecker                                                                                 | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| SignedMath                                                                                       | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| Simulation                                                                                       | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| StorageSlot                                                                                      | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| Strings                                                                                          | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| Subnetwork                                                                                       | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| SymbioticCoreBytecode                                                                            | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| SymbioticCoreConstants                                                                           | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| SymbioticRewardsConstants                                                                        | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| SymbioticRewardsConstantsHelper                                                                  | 348              | 376               | 24,228             | 48,776              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| SymbioticUtils                                                                                   | 80               | 109               | 24,496             | 49,043              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| TimelockControllerUpgradeable                                                                    | 7,690            | 7,718             | 16,886             | 41,434              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| Token                                                                                            | 1,723            | 3,065             | 22,853             | 46,087              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| TransparentUpgradeableProxy                                                                      | 1,073            | 3,445             | 23,503             | 45,707              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| VotingPowerProviderLogic                                                                         | 15,628           | 15,680            | 8,948              | 33,472              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| VotingPowers                                                                                     | 17,135           | 17,343            | 7,441              | 31,809              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| VotingPowersMock                                                                                 | 3,065            | 3,286             | 21,511             | 45,866              |
╰--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------╯
```

## File: src/symbiotic/Driver.sol

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import {OzAccessControl} from "@symbioticfi/relay-contracts/src/modules/common/permissions/OzAccessControl.sol";
import {ValSetDriver} from "@symbioticfi/relay-contracts/src/modules/valset-driver/ValSetDriver.sol";

contract Driver is ValSetDriver, OzAccessControl {
    function initialize(ValSetDriverInitParams memory valSetDriverInitParams, address defaultAdmin)
        public
        virtual
        initializer
    {
        __ValSetDriver_init(valSetDriverInitParams);
        __OzAccessControl_init();

        _grantRole(DEFAULT_ADMIN_ROLE, defaultAdmin);
    }
}
```

## File: src/symbiotic/KeyRegistry.sol

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import {
    KeyRegistry as SymbioticKeyRegistry
} from "@symbioticfi/relay-contracts/src/modules/key-registry/KeyRegistry.sol";

contract KeyRegistry is SymbioticKeyRegistry {
    function initialize(KeyRegistryInitParams memory keyRegistryInitParams) public virtual initializer {
        __KeyRegistry_init(keyRegistryInitParams);
    }
}
```

## File: src/symbiotic/Settlement.sol

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import {OzAccessControl} from "@symbioticfi/relay-contracts/src/modules/common/permissions/OzAccessControl.sol";
import {Settlement as SymbioticSettlement} from "@symbioticfi/relay-contracts/src/modules/settlement/Settlement.sol";

contract Settlement is SymbioticSettlement, OzAccessControl {
    function initialize(SettlementInitParams memory settlementInitParams, address defaultAdmin)
        public
        virtual
        initializer
    {
        __Settlement_init(settlementInitParams);
        __OzAccessControl_init();

        _grantRole(DEFAULT_ADMIN_ROLE, defaultAdmin);
    }
}
```

## File: src/symbiotic/VotingPowers.sol

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import {BaseRewards} from "@symbioticfi/relay-contracts/src/modules/voting-power/extensions/BaseRewards.sol";
import {BaseSlashing} from "@symbioticfi/relay-contracts/src/modules/voting-power/extensions/BaseSlashing.sol";
import {
    EqualStakeVPCalc
} from "@symbioticfi/relay-contracts/src/modules/voting-power/common/voting-power-calc/EqualStakeVPCalc.sol";
import {
    IBaseRewards
} from "@symbioticfi/relay-contracts/src/interfaces/modules/voting-power/extensions/IBaseRewards.sol";
import {
    IBaseSlashing
} from "@symbioticfi/relay-contracts/src/interfaces/modules/voting-power/extensions/IBaseSlashing.sol";
import {
    IOpNetVaultAutoDeploy
} from "@symbioticfi/relay-contracts/src/interfaces/modules/voting-power/extensions/IOpNetVaultAutoDeploy.sol";
import {IOzOwnable} from "@symbioticfi/relay-contracts/src/interfaces/modules/common/permissions/IOzOwnable.sol";
import {ISetMaxNetworkLimitHook} from "@symbioticfi/network/src/interfaces/ISetMaxNetworkLimitHook.sol";
import {IVault} from "@symbioticfi/core/src/interfaces/vault/IVault.sol";
import {
    IVotingPowerProvider
} from "@symbioticfi/relay-contracts/src/interfaces/modules/voting-power/IVotingPowerProvider.sol";
import {
    OpNetVaultAutoDeploy
} from "@symbioticfi/relay-contracts/src/modules/voting-power/extensions/OpNetVaultAutoDeploy.sol";
import {OzOwnable} from "@symbioticfi/relay-contracts/src/modules/common/permissions/OzOwnable.sol";
import {VotingPowerProvider} from "@symbioticfi/relay-contracts/src/modules/voting-power/VotingPowerProvider.sol";

contract VotingPowers is
    VotingPowerProvider,
    OzOwnable,
    EqualStakeVPCalc,
    OpNetVaultAutoDeploy,
    BaseSlashing,
    BaseRewards
{
    error NotFlightDelays();

    address public flightDelays;

    constructor(address operatorRegistry, address vaultFactory, address vaultConfigurator)
        VotingPowerProvider(operatorRegistry, vaultFactory)
        OpNetVaultAutoDeploy(vaultConfigurator)
    {}

    function initialize(
        IVotingPowerProvider.VotingPowerProviderInitParams memory votingPowerProviderInitParams,
        IOpNetVaultAutoDeploy.OpNetVaultAutoDeployInitParams memory opNetVaultAutoDeployInitParams,
        IOzOwnable.OzOwnableInitParams memory ozOwnableInitParams,
        IBaseRewards.BaseRewardsInitParams memory baseRewardsInitParams,
        IBaseSlashing.BaseSlashingInitParams memory baseSlashingInitParams
    ) public virtual initializer {
        __VotingPowerProvider_init(votingPowerProviderInitParams);
        __OpNetVaultAutoDeploy_init(opNetVaultAutoDeployInitParams);
        __OzOwnable_init(ozOwnableInitParams);
        __EqualStakeVPCalc_init();
        __BaseRewards_init(baseRewardsInitParams);
        __BaseSlashing_init(baseSlashingInitParams);
    }

    function _registerOperatorImpl(address operator) internal override(OpNetVaultAutoDeploy, VotingPowerProvider) {
        super._registerOperatorImpl(operator);
    }

    function _unregisterOperatorVaultImpl(address operator, address vault)
        internal
        override(OpNetVaultAutoDeploy, VotingPowerProvider)
    {
        super._unregisterOperatorVaultImpl(operator, vault);
    }

    /// @dev To simplify protocol management, FlightDelays contract need to deploy new Vaults for airlines automatically.
    /// Due to Network's contract implementation allowing to set max network limit only to its middleware, it is done via VotingPowers contract,
    /// because it already contains extensions for rewards and slashing so a good choice to be a "middleware".
    function setMaxNetworkLimit(address vault) public {
        if (flightDelays != msg.sender) {
            revert NotFlightDelays();
        }
        ISetMaxNetworkLimitHook(NETWORK())
            .setMaxNetworkLimit(IVault(vault).delegator(), SUBNETWORK_IDENTIFIER(), type(uint256).max);
    }

    function setFlightDelays(address flightDelays_) public checkPermission {
        flightDelays = flightDelays_;
    }
}
```

## File: src/FlightDelays.sol

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import {VotingPowers} from "./symbiotic/VotingPowers.sol";

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {
    TimelockControllerUpgradeable
} from "@openzeppelin/contracts-upgradeable/governance/TimelockControllerUpgradeable.sol";

import {IBaseDelegator} from "@symbioticfi/core/src/interfaces/delegator/IBaseDelegator.sol";
import {
    IBaseRewards
} from "@symbioticfi/relay-contracts/src/interfaces/modules/voting-power/extensions/IBaseRewards.sol";
import {IBaseSlasher} from "@symbioticfi/core/src/interfaces/slasher/IBaseSlasher.sol";
import {
    IBaseSlashing
} from "@symbioticfi/relay-contracts/src/interfaces/modules/voting-power/extensions/IBaseSlashing.sol";
import {
    IDefaultStakerRewardsFactory
} from "@symbioticfi/rewards/src/interfaces/defaultStakerRewards/IDefaultStakerRewardsFactory.sol";
import {
    IDefaultStakerRewards
} from "@symbioticfi/rewards/src/interfaces/defaultStakerRewards/IDefaultStakerRewards.sol";
import {INetworkManager} from "@symbioticfi/relay-contracts/src/interfaces/modules/base/INetworkManager.sol";
import {
    IOperatorNetworkSpecificDelegator
} from "@symbioticfi/core/src/interfaces/delegator/IOperatorNetworkSpecificDelegator.sol";
import {IOperatorRegistry} from "@symbioticfi/core/src/interfaces/IOperatorRegistry.sol";
import {IOptInService} from "@symbioticfi/core/src/interfaces/service/IOptInService.sol";
import {ISettlement} from "@symbioticfi/relay-contracts/src/interfaces/modules/settlement/ISettlement.sol";
import {ISlasher} from "@symbioticfi/core/src/interfaces/slasher/ISlasher.sol";
import {IVaultConfigurator} from "@symbioticfi/core/src/interfaces/IVaultConfigurator.sol";
import {IVault} from "@symbioticfi/core/src/interfaces/vault/IVault.sol";
import {
    IVotingPowerProvider
} from "@symbioticfi/relay-contracts/src/interfaces/modules/voting-power/IVotingPowerProvider.sol";
import {NetworkManager} from "@symbioticfi/relay-contracts/src/modules/base/NetworkManager.sol";
import {Subnetwork} from "@symbioticfi/core/src/contracts/libraries/Subnetwork.sol";

contract FlightDelays is NetworkManager {
    using SafeERC20 for IERC20;
    using Subnetwork for bytes32;

    error BuyWindowClosed();
    error FlightNotScheduled();
    error FlightNotDelayable();
    error FlightNotDelayed();
    error FlightAlreadyExists();
    error PreviousFlightIncomplete();
    error InvalidFlight();
    error InvalidTimestamp();
    error InvalidPreviousFlight();
    error InvalidMessageSignature();
    error InvalidEpoch();
    error PolicyAlreadyPurchased();
    error PolicyNotFound();
    error SlashFailed();
    error InsufficientCoverage();
    error InvalidPolicy();

    enum FlightStatus {
        NONE,
        SCHEDULED,
        DELAYED,
        DEPARTED
    }

    enum PolicyStatus {
        NONE,
        PURCHASED,
        CLAIMED
    }

    struct Flight {
        uint48 timestamp;
        FlightStatus status;
        uint128 policiesSold;
        bytes32 previousFlightId;
    }

    struct Airline {
        address vault;
        address rewards;
        uint256 covered;
        bytes32 lastFlightId;
    }

    struct InitParams {
        address votingPowers;
        address settlement;
        address collateral;
        uint48 vaultEpochDuration;
        uint32 messageExpiry;
        uint48 policyWindow;
        uint48 delayWindow;
        uint256 policyPremium;
        uint256 policyPayout;
    }

    event AirlineVaultDeployed(bytes32 indexed airlineId, address vault, address rewards);
    event FlightCreated(bytes32 indexed airlineId, bytes32 indexed flightId, uint48 scheduledTimestamp);
    event FlightDelayed(bytes32 indexed airlineId, bytes32 indexed flightId);
    event FlightDeparted(bytes32 indexed airlineId, bytes32 indexed flightId);
    event InsurancePurchased(
        bytes32 indexed airlineId, bytes32 indexed flightId, address indexed buyer, uint256 premium
    );
    event InsuranceClaimed(bytes32 indexed airlineId, bytes32 indexed flightId, address indexed buyer, uint256 payout);

    bytes32 internal constant CREATE_MESSAGE_TYPEHASH =
        keccak256("Create(bytes32 airlineId,bytes32 flightId,uint48 departure,bytes32 previousFlightId)");
    bytes32 internal constant DELAY_MESSAGE_TYPEHASH = keccak256("Delay(bytes32 airlineId,bytes32 flightId)");
    bytes32 internal constant DEPART_MESSAGE_TYPEHASH = keccak256("Depart(bytes32 airlineId,bytes32 flightId)");

    address public immutable VAULT_CONFIGURATOR;
    address public immutable DEFAULT_STAKER_REWARDS_FACTORY;
    address public immutable OPERATOR_VAULT_OPT_IN_SERVICE;
    address public immutable OPERATOR_NETWORK_OPT_IN_SERVICE;
    address public immutable OPERATOR_REGISTRY;

    address public votingPowers;
    address public settlement;
    address public collateral;
    uint48 public policyWindow;
    uint48 public delayWindow;
    uint48 public vaultEpochDuration;
    uint32 public messageExpiry;
    uint256 public policyPremium;
    uint256 public policyPayout;

    mapping(bytes32 airlineId => Airline airline) public airlines;
    mapping(bytes32 airlineId => mapping(bytes32 flightId => Flight flight)) public flights;
    mapping(bytes32 airlineId => mapping(bytes32 flightId => mapping(address buyer => PolicyStatus policyStatus)))
        public policies;

    constructor(
        address vaultConfigurator,
        address operatorVaultOptInService,
        address operatorNetworkOptInService,
        address defaultStakerRewardsFactory,
        address operatorRegistry
    ) {
        VAULT_CONFIGURATOR = vaultConfigurator;
        OPERATOR_VAULT_OPT_IN_SERVICE = operatorVaultOptInService;
        OPERATOR_NETWORK_OPT_IN_SERVICE = operatorNetworkOptInService;
        DEFAULT_STAKER_REWARDS_FACTORY = defaultStakerRewardsFactory;
        OPERATOR_REGISTRY = operatorRegistry;
    }

    function initialize(InitParams calldata initParams) external initializer {
        if (initParams.policyPremium == 0 || initParams.policyPayout < initParams.policyPremium) {
            revert InvalidPolicy();
        }

        settlement = initParams.settlement;
        collateral = initParams.collateral;
        vaultEpochDuration = initParams.vaultEpochDuration;
        messageExpiry = initParams.messageExpiry;
        policyWindow = initParams.policyWindow;
        delayWindow = initParams.delayWindow;
        policyPremium = initParams.policyPremium;
        policyPayout = initParams.policyPayout;
        votingPowers = initParams.votingPowers;

        __NetworkManager_init(
            INetworkManager.NetworkManagerInitParams({
                network: INetworkManager(votingPowers).NETWORK(),
                subnetworkId: INetworkManager(votingPowers).SUBNETWORK_IDENTIFIER()
            })
        );

        IOperatorRegistry(OPERATOR_REGISTRY).registerOperator();
        IOptInService(OPERATOR_NETWORK_OPT_IN_SERVICE).optIn(NETWORK());
    }

    function createFlight(
        bytes32 airlineId,
        bytes32 flightId,
        uint48 scheduledTimestamp,
        bytes32 previousFlightId,
        uint48 epoch,
        bytes calldata proof
    ) external {
        if (airlineId == bytes32(0) || flightId == bytes32(0)) {
            revert InvalidFlight();
        }
        _verifyFlightMessage(
            abi.encode(
                keccak256(
                    abi.encode(CREATE_MESSAGE_TYPEHASH, airlineId, flightId, scheduledTimestamp, previousFlightId)
                )
            ),
            epoch,
            proof
        );

        Airline storage airline = airlines[airlineId];
        if (airline.vault == address(0)) {
            _deployAirline(airlineId);
        }
        if (previousFlightId != airline.lastFlightId) {
            revert InvalidPreviousFlight();
        }

        Flight storage flight = flights[airlineId][flightId];
        if (flight.status != FlightStatus.NONE) {
            revert FlightAlreadyExists();
        }

        if (previousFlightId != bytes32(0) && scheduledTimestamp < flights[airlineId][previousFlightId].timestamp) {
            revert InvalidTimestamp();
        }

        flight.timestamp = scheduledTimestamp;
        flight.status = FlightStatus.SCHEDULED;
        flight.previousFlightId = previousFlightId;
        airline.lastFlightId = flightId;

        emit FlightCreated(airlineId, flightId, scheduledTimestamp);
    }

    function delayFlight(bytes32 airlineId, bytes32 flightId, uint48 epoch, bytes calldata proof) external {
        Flight storage flight = flights[airlineId][flightId];
        if (flight.status != FlightStatus.SCHEDULED) {
            revert FlightNotDelayable();
        }

        _verifyFlightMessage(
            abi.encode(keccak256(abi.encode(DELAY_MESSAGE_TYPEHASH, airlineId, flightId))), epoch, proof
        );

        bytes32 previousFlightId = flight.previousFlightId;
        if (
            previousFlightId != bytes32(0)
                && (flights[airlineId][previousFlightId].status != FlightStatus.DEPARTED
                    && flights[airlineId][previousFlightId].status != FlightStatus.DELAYED)
        ) {
            revert PreviousFlightIncomplete();
        }

        flight.status = FlightStatus.DELAYED;

        Airline storage airline = airlines[airlineId];
        uint256 coverage = flight.policiesSold * policyPayout;

        if (coverage > 0) {
            (bool success,) = IBaseSlashing(votingPowers)
                .slashVault(flight.timestamp - policyWindow, airline.vault, address(this), coverage, new bytes(0));
            if (!success) {
                revert SlashFailed();
            }
        }

        airline.covered -= coverage;

        emit FlightDelayed(airlineId, flightId);
    }

    function departFlight(bytes32 airlineId, bytes32 flightId, uint48 epoch, bytes calldata proof) external {
        Flight storage flight = flights[airlineId][flightId];
        if (flight.status != FlightStatus.SCHEDULED) {
            revert FlightNotScheduled();
        }

        _verifyFlightMessage(
            abi.encode(keccak256(abi.encode(DEPART_MESSAGE_TYPEHASH, airlineId, flightId))), epoch, proof
        );

        bytes32 previousFlightId = flight.previousFlightId;
        if (
            previousFlightId != bytes32(0)
                && (flights[airlineId][previousFlightId].status != FlightStatus.DEPARTED
                    && flights[airlineId][previousFlightId].status != FlightStatus.DELAYED)
        ) {
            revert PreviousFlightIncomplete();
        }

        flight.status = FlightStatus.DEPARTED;

        Airline storage airline = airlines[airlineId];
        airline.covered -= flight.policiesSold * policyPayout;

        uint256 rewardsAmount = flight.policiesSold * policyPremium;
        if (rewardsAmount > 0) {
            IERC20(collateral).safeTransfer(votingPowers, rewardsAmount);
            IBaseRewards(votingPowers)
                .distributeStakerRewards(
                    airline.rewards,
                    collateral,
                    rewardsAmount,
                    abi.encode(flight.timestamp - policyWindow, 10_000, new bytes(0), new bytes(0))
                );
        }

        emit FlightDeparted(airlineId, flightId);
    }

    function buyInsurance(bytes32 airlineId, bytes32 flightId) external {
        Flight storage flight = flights[airlineId][flightId];
        if (flight.status != FlightStatus.SCHEDULED) {
            revert FlightNotScheduled();
        }

        uint48 captureTimestamp = flight.timestamp - policyWindow;
        if (block.timestamp <= captureTimestamp || block.timestamp > flight.timestamp - delayWindow) {
            revert BuyWindowClosed();
        }

        PolicyStatus policyStatus = policies[airlineId][flightId][msg.sender];
        if (policyStatus != PolicyStatus.NONE) {
            revert PolicyAlreadyPurchased();
        }

        Airline storage airline = airlines[airlineId];
        uint256 newCovered = airline.covered + policyPayout;
        if (
            newCovered
                > IBaseSlasher(IVault(airline.vault).slasher())
                    .slashableStake(SUBNETWORK(), address(this), captureTimestamp, new bytes(0))
        ) {
            revert InsufficientCoverage();
        }

        policies[airlineId][flightId][msg.sender] = PolicyStatus.PURCHASED;
        ++flight.policiesSold;
        airline.covered = newCovered;

        IERC20(collateral).safeTransferFrom(msg.sender, address(this), policyPremium);

        emit InsurancePurchased(airlineId, flightId, msg.sender, policyPremium);
    }

    function claimInsurance(bytes32 airlineId, bytes32 flightId) external {
        Flight storage flight = flights[airlineId][flightId];
        if (flight.status != FlightStatus.DELAYED) {
            revert FlightNotDelayed();
        }

        PolicyStatus policyStatus = policies[airlineId][flightId][msg.sender];
        if (policyStatus != PolicyStatus.PURCHASED) {
            revert PolicyNotFound();
        }

        policies[airlineId][flightId][msg.sender] = PolicyStatus.CLAIMED;

        IERC20(collateral).safeTransfer(msg.sender, policyPayout);

        emit InsuranceClaimed(airlineId, flightId, msg.sender, policyPayout);
    }

    function _deployAirline(bytes32 airlineId) internal {
        Airline storage airline = airlines[airlineId];
        if (airline.vault != address(0)) {
            return;
        }

        (address vault,,) = IVaultConfigurator(VAULT_CONFIGURATOR)
            .create(
                IVaultConfigurator.InitParams({
                    version: 1,
                    owner: address(this),
                    vaultParams: abi.encode(
                        IVault.InitParams({
                            collateral: collateral,
                            burner: address(this),
                            epochDuration: vaultEpochDuration,
                            depositWhitelist: false,
                            isDepositLimit: false,
                            depositLimit: 0,
                            defaultAdminRoleHolder: address(0),
                            depositWhitelistSetRoleHolder: address(0),
                            depositorWhitelistRoleHolder: address(0),
                            isDepositLimitSetRoleHolder: address(0),
                            depositLimitSetRoleHolder: address(0)
                        })
                    ),
                    delegatorIndex: uint64(IVotingPowerProvider.DelegatorType.OPERATOR_NETWORK_SPECIFIC),
                    delegatorParams: abi.encode(
                        IOperatorNetworkSpecificDelegator.InitParams({
                            baseParams: IBaseDelegator.BaseParams({
                                defaultAdminRoleHolder: address(this),
                                hook: address(0),
                                hookSetRoleHolder: address(this)
                            }),
                            network: NETWORK(),
                            operator: address(this)
                        })
                    ),
                    withSlasher: true,
                    slasherIndex: uint64(IVotingPowerProvider.SlasherType.INSTANT),
                    slasherParams: abi.encode(
                        ISlasher.InitParams({baseParams: IBaseSlasher.BaseParams({isBurnerHook: false})})
                    )
                })
            );

        IOptInService(OPERATOR_VAULT_OPT_IN_SERVICE).optIn(vault);

        VotingPowers(votingPowers).setMaxNetworkLimit(vault);

        address rewards = IDefaultStakerRewardsFactory(DEFAULT_STAKER_REWARDS_FACTORY)
            .create(
                IDefaultStakerRewards.InitParams({
                    vault: vault,
                    adminFee: 0,
                    defaultAdminRoleHolder: address(0),
                    adminFeeClaimRoleHolder: address(0),
                    adminFeeSetRoleHolder: address(0)
                })
            );

        airline.vault = vault;
        airline.rewards = rewards;

        emit AirlineVaultDeployed(airlineId, vault, rewards);
    }

    function _verifyFlightMessage(bytes memory payload, uint48 epoch, bytes calldata proof) internal view {
        uint48 nextCaptureTimestamp = ISettlement(settlement).getCaptureTimestampFromValSetHeaderAt(epoch + 1);
        if (nextCaptureTimestamp > 0 && block.timestamp >= nextCaptureTimestamp + messageExpiry) {
            revert InvalidEpoch();
        }
        if (!ISettlement(settlement)
                .verifyQuorumSigAt(
                    payload,
                    ISettlement(settlement).getRequiredKeyTagFromValSetHeaderAt(epoch),
                    ISettlement(settlement).getQuorumThresholdFromValSetHeaderAt(epoch),
                    proof,
                    epoch,
                    new bytes(0)
                )) {
            revert InvalidMessageSignature();
        }
    }
}
```

## File: ui/src/abi/ERC20.json

```json
[
    {
        "type": "function",
        "name": "name",
        "stateMutability": "view",
        "inputs": [],
        "outputs": [{ "name": "", "type": "string" }]
    },
    {
        "type": "function",
        "name": "symbol",
        "stateMutability": "view",
        "inputs": [],
        "outputs": [{ "name": "", "type": "string" }]
    },
    {
        "type": "function",
        "name": "decimals",
        "stateMutability": "view",
        "inputs": [],
        "outputs": [{ "name": "", "type": "uint8" }]
    },
    {
        "type": "function",
        "name": "balanceOf",
        "stateMutability": "view",
        "inputs": [{ "name": "account", "type": "address" }],
        "outputs": [{ "name": "", "type": "uint256" }]
    },
    {
        "type": "function",
        "name": "allowance",
        "stateMutability": "view",
        "inputs": [
            { "name": "owner", "type": "address" },
            { "name": "spender", "type": "address" }
        ],
        "outputs": [{ "name": "", "type": "uint256" }]
    },
    {
        "type": "function",
        "name": "approve",
        "stateMutability": "nonpayable",
        "inputs": [
            { "name": "spender", "type": "address" },
            { "name": "amount", "type": "uint256" }
        ],
        "outputs": [{ "name": "", "type": "bool" }]
    }
]
```

## File: ui/src/abi/FlightDelays.json

```json
[
    {
        "type": "constructor",
        "inputs": [
            {
                "name": "vaultConfigurator",
                "type": "address",
                "internalType": "address"
            },
            {
                "name": "operatorVaultOptInService",
                "type": "address",
                "internalType": "address"
            },
            {
                "name": "operatorNetworkOptInService",
                "type": "address",
                "internalType": "address"
            },
            {
                "name": "defaultStakerRewardsFactory",
                "type": "address",
                "internalType": "address"
            },
            {
                "name": "operatorRegistry",
                "type": "address",
                "internalType": "address"
            }
        ],
        "stateMutability": "nonpayable"
    },
    {
        "type": "function",
        "name": "DEFAULT_STAKER_REWARDS_FACTORY",
        "inputs": [],
        "outputs": [
            {
                "name": "",
                "type": "address",
                "internalType": "address"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "NETWORK",
        "inputs": [],
        "outputs": [
            {
                "name": "",
                "type": "address",
                "internalType": "address"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "OPERATOR_NETWORK_OPT_IN_SERVICE",
        "inputs": [],
        "outputs": [
            {
                "name": "",
                "type": "address",
                "internalType": "address"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "OPERATOR_VAULT_OPT_IN_SERVICE",
        "inputs": [],
        "outputs": [
            {
                "name": "",
                "type": "address",
                "internalType": "address"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "SUBNETWORK",
        "inputs": [],
        "outputs": [
            {
                "name": "",
                "type": "bytes32",
                "internalType": "bytes32"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "SUBNETWORK_IDENTIFIER",
        "inputs": [],
        "outputs": [
            {
                "name": "",
                "type": "uint96",
                "internalType": "uint96"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "VAULT_CONFIGURATOR",
        "inputs": [],
        "outputs": [
            {
                "name": "",
                "type": "address",
                "internalType": "address"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "airlines",
        "inputs": [
            {
                "name": "airlineId",
                "type": "bytes32",
                "internalType": "bytes32"
            }
        ],
        "outputs": [
            {
                "name": "vault",
                "type": "address",
                "internalType": "address"
            },
            {
                "name": "rewards",
                "type": "address",
                "internalType": "address"
            },
            {
                "name": "covered",
                "type": "uint256",
                "internalType": "uint256"
            },
            {
                "name": "lastFlightId",
                "type": "bytes32",
                "internalType": "bytes32"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "buyInsurance",
        "inputs": [
            {
                "name": "airlineId",
                "type": "bytes32",
                "internalType": "bytes32"
            },
            {
                "name": "flightId",
                "type": "bytes32",
                "internalType": "bytes32"
            }
        ],
        "outputs": [],
        "stateMutability": "nonpayable"
    },
    {
        "type": "function",
        "name": "claimInsurance",
        "inputs": [
            {
                "name": "airlineId",
                "type": "bytes32",
                "internalType": "bytes32"
            },
            {
                "name": "flightId",
                "type": "bytes32",
                "internalType": "bytes32"
            }
        ],
        "outputs": [],
        "stateMutability": "nonpayable"
    },
    {
        "type": "function",
        "name": "collateral",
        "inputs": [],
        "outputs": [
            {
                "name": "",
                "type": "address",
                "internalType": "address"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "createFlight",
        "inputs": [
            {
                "name": "airlineId",
                "type": "bytes32",
                "internalType": "bytes32"
            },
            {
                "name": "flightId",
                "type": "bytes32",
                "internalType": "bytes32"
            },
            {
                "name": "scheduledTimestamp",
                "type": "uint48",
                "internalType": "uint48"
            },
            {
                "name": "epoch",
                "type": "uint48",
                "internalType": "uint48"
            },
            {
                "name": "proof",
                "type": "bytes",
                "internalType": "bytes"
            }
        ],
        "outputs": [],
        "stateMutability": "nonpayable"
    },
    {
        "type": "function",
        "name": "delayFlight",
        "inputs": [
            {
                "name": "airlineId",
                "type": "bytes32",
                "internalType": "bytes32"
            },
            {
                "name": "flightId",
                "type": "bytes32",
                "internalType": "bytes32"
            },
            {
                "name": "epoch",
                "type": "uint48",
                "internalType": "uint48"
            },
            {
                "name": "proof",
                "type": "bytes",
                "internalType": "bytes"
            }
        ],
        "outputs": [],
        "stateMutability": "nonpayable"
    },
    {
        "type": "function",
        "name": "delayWindow",
        "inputs": [],
        "outputs": [
            {
                "name": "",
                "type": "uint48",
                "internalType": "uint48"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "departFlight",
        "inputs": [
            {
                "name": "airlineId",
                "type": "bytes32",
                "internalType": "bytes32"
            },
            {
                "name": "flightId",
                "type": "bytes32",
                "internalType": "bytes32"
            },
            {
                "name": "epoch",
                "type": "uint48",
                "internalType": "uint48"
            },
            {
                "name": "proof",
                "type": "bytes",
                "internalType": "bytes"
            }
        ],
        "outputs": [],
        "stateMutability": "nonpayable"
    },
    {
        "type": "function",
        "name": "flights",
        "inputs": [
            {
                "name": "airlineId",
                "type": "bytes32",
                "internalType": "bytes32"
            },
            {
                "name": "flightId",
                "type": "bytes32",
                "internalType": "bytes32"
            }
        ],
        "outputs": [
            {
                "name": "timestamp",
                "type": "uint48",
                "internalType": "uint48"
            },
            {
                "name": "status",
                "type": "uint8",
                "internalType": "enum FlightDelays.FlightStatus"
            },
            {
                "name": "policiesSold",
                "type": "uint128",
                "internalType": "uint128"
            },
            {
                "name": "previousFlightId",
                "type": "bytes32",
                "internalType": "bytes32"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "initialize",
        "inputs": [
            {
                "name": "initParams",
                "type": "tuple",
                "internalType": "struct FlightDelays.InitParams",
                "components": [
                    {
                        "name": "votingPowers",
                        "type": "address",
                        "internalType": "address"
                    },
                    {
                        "name": "settlement",
                        "type": "address",
                        "internalType": "address"
                    },
                    {
                        "name": "collateral",
                        "type": "address",
                        "internalType": "address"
                    },
                    {
                        "name": "vaultEpochDuration",
                        "type": "uint48",
                        "internalType": "uint48"
                    },
                    {
                        "name": "messageExpiry",
                        "type": "uint32",
                        "internalType": "uint32"
                    },
                    {
                        "name": "policyWindow",
                        "type": "uint48",
                        "internalType": "uint48"
                    },
                    {
                        "name": "delayWindow",
                        "type": "uint48",
                        "internalType": "uint48"
                    },
                    {
                        "name": "policyPremium",
                        "type": "uint256",
                        "internalType": "uint256"
                    },
                    {
                        "name": "policyPayout",
                        "type": "uint256",
                        "internalType": "uint256"
                    }
                ]
            }
        ],
        "outputs": [],
        "stateMutability": "nonpayable"
    },
    {
        "type": "function",
        "name": "messageExpiry",
        "inputs": [],
        "outputs": [
            {
                "name": "",
                "type": "uint32",
                "internalType": "uint32"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "policies",
        "inputs": [
            {
                "name": "airlineId",
                "type": "bytes32",
                "internalType": "bytes32"
            },
            {
                "name": "flightId",
                "type": "bytes32",
                "internalType": "bytes32"
            },
            {
                "name": "buyer",
                "type": "address",
                "internalType": "address"
            }
        ],
        "outputs": [
            {
                "name": "policyStatus",
                "type": "uint8",
                "internalType": "enum FlightDelays.PolicyStatus"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "policyPayout",
        "inputs": [],
        "outputs": [
            {
                "name": "",
                "type": "uint256",
                "internalType": "uint256"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "policyPremium",
        "inputs": [],
        "outputs": [
            {
                "name": "",
                "type": "uint256",
                "internalType": "uint256"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "policyWindow",
        "inputs": [],
        "outputs": [
            {
                "name": "",
                "type": "uint48",
                "internalType": "uint48"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "settlement",
        "inputs": [],
        "outputs": [
            {
                "name": "",
                "type": "address",
                "internalType": "address"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "staticDelegateCall",
        "inputs": [
            {
                "name": "target",
                "type": "address",
                "internalType": "address"
            },
            {
                "name": "data",
                "type": "bytes",
                "internalType": "bytes"
            }
        ],
        "outputs": [],
        "stateMutability": "nonpayable"
    },
    {
        "type": "function",
        "name": "vaultEpochDuration",
        "inputs": [],
        "outputs": [
            {
                "name": "",
                "type": "uint48",
                "internalType": "uint48"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "function",
        "name": "votingPowers",
        "inputs": [],
        "outputs": [
            {
                "name": "",
                "type": "address",
                "internalType": "address"
            }
        ],
        "stateMutability": "view"
    },
    {
        "type": "event",
        "name": "AirlineVaultDeployed",
        "inputs": [
            {
                "name": "airlineId",
                "type": "bytes32",
                "indexed": true,
                "internalType": "bytes32"
            },
            {
                "name": "vault",
                "type": "address",
                "indexed": false,
                "internalType": "address"
            },
            {
                "name": "rewards",
                "type": "address",
                "indexed": false,
                "internalType": "address"
            }
        ],
        "anonymous": false
    },
    {
        "type": "event",
        "name": "FlightCreated",
        "inputs": [
            {
                "name": "airlineId",
                "type": "bytes32",
                "indexed": true,
                "internalType": "bytes32"
            },
            {
                "name": "flightId",
                "type": "bytes32",
                "indexed": true,
                "internalType": "bytes32"
            },
            {
                "name": "scheduledTimestamp",
                "type": "uint48",
                "indexed": false,
                "internalType": "uint48"
            }
        ],
        "anonymous": false
    },
    {
        "type": "event",
        "name": "FlightDelayed",
        "inputs": [
            {
                "name": "airlineId",
                "type": "bytes32",
                "indexed": true,
                "internalType": "bytes32"
            },
            {
                "name": "flightId",
                "type": "bytes32",
                "indexed": true,
                "internalType": "bytes32"
            }
        ],
        "anonymous": false
    },
    {
        "type": "event",
        "name": "FlightDeparted",
        "inputs": [
            {
                "name": "airlineId",
                "type": "bytes32",
                "indexed": true,
                "internalType": "bytes32"
            },
            {
                "name": "flightId",
                "type": "bytes32",
                "indexed": true,
                "internalType": "bytes32"
            }
        ],
        "anonymous": false
    },
    {
        "type": "event",
        "name": "InitSubnetwork",
        "inputs": [
            {
                "name": "network",
                "type": "address",
                "indexed": false,
                "internalType": "address"
            },
            {
                "name": "subnetworkId",
                "type": "uint96",
                "indexed": false,
                "internalType": "uint96"
            }
        ],
        "anonymous": false
    },
    {
        "type": "event",
        "name": "Initialized",
        "inputs": [
            {
                "name": "version",
                "type": "uint64",
                "indexed": false,
                "internalType": "uint64"
            }
        ],
        "anonymous": false
    },
    {
        "type": "event",
        "name": "InsuranceClaimed",
        "inputs": [
            {
                "name": "airlineId",
                "type": "bytes32",
                "indexed": true,
                "internalType": "bytes32"
            },
            {
                "name": "flightId",
                "type": "bytes32",
                "indexed": true,
                "internalType": "bytes32"
            },
            {
                "name": "buyer",
                "type": "address",
                "indexed": true,
                "internalType": "address"
            },
            {
                "name": "payout",
                "type": "uint256",
                "indexed": false,
                "internalType": "uint256"
            }
        ],
        "anonymous": false
    },
    {
        "type": "event",
        "name": "InsurancePurchased",
        "inputs": [
            {
                "name": "airlineId",
                "type": "bytes32",
                "indexed": true,
                "internalType": "bytes32"
            },
            {
                "name": "flightId",
                "type": "bytes32",
                "indexed": true,
                "internalType": "bytes32"
            },
            {
                "name": "buyer",
                "type": "address",
                "indexed": true,
                "internalType": "address"
            },
            {
                "name": "premium",
                "type": "uint256",
                "indexed": false,
                "internalType": "uint256"
            }
        ],
        "anonymous": false
    },
    {
        "type": "error",
        "name": "BuyWindowClosed",
        "inputs": []
    },
    {
        "type": "error",
        "name": "FlightAlreadyExists",
        "inputs": []
    },
    {
        "type": "error",
        "name": "FlightNotDelayable",
        "inputs": []
    },
    {
        "type": "error",
        "name": "FlightNotDelayed",
        "inputs": []
    },
    {
        "type": "error",
        "name": "FlightNotScheduled",
        "inputs": []
    },
    {
        "type": "error",
        "name": "InsufficientCoverage",
        "inputs": []
    },
    {
        "type": "error",
        "name": "InvalidEpoch",
        "inputs": []
    },
    {
        "type": "error",
        "name": "InvalidFlight",
        "inputs": []
    },
    {
        "type": "error",
        "name": "InvalidInitialization",
        "inputs": []
    },
    {
        "type": "error",
        "name": "InvalidMessageSignature",
        "inputs": []
    },
    {
        "type": "error",
        "name": "InvalidPolicy",
        "inputs": []
    },
    {
        "type": "error",
        "name": "InvalidTimestamp",
        "inputs": []
    },
    {
        "type": "error",
        "name": "NetworkManager_InvalidNetwork",
        "inputs": []
    },
    {
        "type": "error",
        "name": "NotInitializing",
        "inputs": []
    },
    {
        "type": "error",
        "name": "PolicyAlreadyPurchased",
        "inputs": []
    },
    {
        "type": "error",
        "name": "PolicyNotFound",
        "inputs": []
    },
    {
        "type": "error",
        "name": "PreviousFlightIncomplete",
        "inputs": []
    },
    {
        "type": "error",
        "name": "SafeERC20FailedOperation",
        "inputs": [
            {
                "name": "token",
                "type": "address",
                "internalType": "address"
            }
        ]
    }
]
```

## File: ui/src/abi/Rewards.json

```json
[
    {
        "type": "function",
        "name": "claimable",
        "stateMutability": "view",
        "inputs": [
            { "name": "token", "type": "address" },
            { "name": "account", "type": "address" },
            { "name": "data", "type": "bytes" }
        ],
        "outputs": [{ "name": "", "type": "uint256" }]
    },
    {
        "type": "function",
        "name": "claimRewards",
        "stateMutability": "nonpayable",
        "inputs": [
            { "name": "recipient", "type": "address" },
            { "name": "token", "type": "address" },
            { "name": "data", "type": "bytes" }
        ],
        "outputs": []
    }
]
```

## File: ui/src/abi/Slasher.json

```json
[
    {
        "type": "function",
        "name": "slashableStake",
        "stateMutability": "view",
        "inputs": [
            { "name": "network", "type": "bytes32" },
            { "name": "operator", "type": "address" },
            { "name": "captureTimestamp", "type": "uint48" },
            { "name": "hints", "type": "bytes" }
        ],
        "outputs": [{ "name": "", "type": "uint256" }]
    }
]
```

## File: ui/src/abi/Vault.json

```json
[
    {
        "type": "function",
        "name": "deposit",
        "stateMutability": "nonpayable",
        "inputs": [
            { "name": "onBehalfOf", "type": "address" },
            { "name": "amount", "type": "uint256" }
        ],
        "outputs": [
            { "name": "depositedAmount", "type": "uint256" },
            { "name": "mintedShares", "type": "uint256" }
        ]
    },
    {
        "type": "function",
        "name": "withdraw",
        "stateMutability": "nonpayable",
        "inputs": [
            { "name": "claimer", "type": "address" },
            { "name": "amount", "type": "uint256" }
        ],
        "outputs": [
            { "name": "burnedShares", "type": "uint256" },
            { "name": "mintedShares", "type": "uint256" }
        ]
    },
    {
        "type": "function",
        "name": "activeBalanceOf",
        "stateMutability": "view",
        "inputs": [{ "name": "account", "type": "address" }],
        "outputs": [{ "name": "", "type": "uint256" }]
    },
    {
        "type": "function",
        "name": "slasher",
        "stateMutability": "view",
        "inputs": [],
        "outputs": [{ "name": "", "type": "address" }]
    }
]
```

## File: ui/src/api/flights.ts

```typescript
import type { AirlineDTO, AirlineWithFlights, FlightDTO } from "../types";

async function getJSON<T>(url: string): Promise<T> {
    const resp = await fetch(url);
    if (!resp.ok) {
        throw new Error(`Request failed: ${resp.status}`);
    }
    return resp.json();
}

function normalizeBase(url: string) {
    return url.replace(/\/+$/, "");
}

export async function fetchAirlinesWithFlights(baseUrl: string): Promise<AirlineWithFlights[]> {
    const normalized = normalizeBase(baseUrl);
    const airlinesResp = await getJSON<{ airlines: AirlineDTO[] }>(`${normalized}/airlines`);
    const airlines = airlinesResp.airlines ?? [];

    const results = await Promise.all(
        airlines.map(async (airline) => {
            const flightsResp = await getJSON<{ flights: FlightDTO[] }>(
                `${normalized}/airlines/${encodeURIComponent(airline.airlineId)}/flights`,
            );
            return { ...airline, flights: flightsResp.flights ?? [] };
        }),
    );
    return results;
}
```

## File: ui/src/components/BuyerPage.tsx

```typescript
import type { ReactNode } from "react";

import { airlineImages } from "../constants/airlines";
import type { ChainFlightData, FlattenedFlight, PolicyMap, ProtocolInfo } from "../types";
import { formatTimestamp } from "../utils/format";
import { flightKey } from "../utils/hash";

export function BuyerPage({
  flightsLoading,
  flattenedFlights,
  chainFlights,
  policies,
  protocol,
  isConnected,
  allowanceEnough,
  handleApprove,
  handleBuy,
  handleClaim,
  now,
}: BuyerPageProps) {
  return (
    <section className="panel">
      <h2>Available Flights</h2>
      {flightsLoading ? (
        <p>Loading flights...</p>
      ) : (
        <table>
          <thead>
            <tr>
              <th>Airline</th>
              <th>Flight</th>
              <th>Departure</th>
              <th>API Status</th>
              <th>On-chain</th>
              <th>Policies</th>
              <th>Action</th>
            </tr>
          </thead>
          <tbody>
            {flattenedFlights.map(({ airline, flight }) => {
              const key = flightKey(airline.airlineId, flight.flightId);
              const chainData = chainFlights[key];
              const policyStatus = policies[key] ?? 0;
              const hasPolicy = policyStatus === 1 || policyStatus === 2;
              const chainStatus = chainData?.status ?? (flight.departureTimestamp > now ? 1 : 0);
              const onChainStatus = statusLabel(chainStatus);
              const buyWindow = getBuyWindowInfo(chainData, protocol, now, flight.departureTimestamp);
              const departureTs = chainData ? Number(chainData.timestamp ?? 0n) : flight.departureTimestamp;
              const departureCountdown = Math.max(0, departureTs - now);
              const buyDisabled = !isConnected || !allowanceEnough || buyWindow?.state !== "open" || hasPolicy;

              let actionNode: ReactNode = null;
              let timerLabel: string | null = null;
              let timerClass = "after";

              if (chainStatus === 2) {
                if (policyStatus === 2) {
                  actionNode = <button disabled>Claimed</button>;
                } else if (policyStatus === 1) {
                  actionNode = (
                    <button onClick={() => handleClaim(airline.airlineId, flight.flightId)} disabled={!isConnected}>
                      Claim
                    </button>
                  );
                } else {
                  actionNode = <>Delayed</>;
                }
                timerLabel = "Flight delayed";
              } else if (chainStatus === 3) {
                actionNode = <>Departed</>;
              } else {
                if (!protocol) {
                  actionNode = <button disabled>Buy</button>;
                  timerLabel = `Departs in ${formatCountdown(departureCountdown)}`;
                } else if (buyWindow) {
                  switch (buyWindow.state) {
                    case "before":
                      actionNode = <button disabled>Buy</button>;
                      timerLabel = `Opens in ${formatCountdown(buyWindow.seconds)}`;
                      timerClass = "before";
                      break;
                    case "open":
                      if (hasPolicy) {
                        actionNode = <button disabled>Purchased</button>;
                        timerLabel = `Closes in ${formatCountdown(buyWindow.seconds)}`;
                        timerClass = "open";
                      } else {
                        actionNode = (
                          <button onClick={() => handleBuy(airline.airlineId, flight.flightId)} disabled={buyDisabled}>
                            Buy
                          </button>
                        );
                        timerLabel = `Closes in ${formatCountdown(buyWindow.seconds)}`;
                        timerClass = "open";
                      }
                      break;
                    case "after":
                      actionNode = <button disabled>Buy</button>;
                      timerLabel = `Departs in ${formatCountdown(buyWindow.seconds)}`;
                      timerClass = "after";
                      break;
                  }
                } else {
                  actionNode = <button disabled>Buy</button>;
                  timerLabel = `Departs in ${formatCountdown(departureCountdown)}`;
                }
              }
              return (
                <tr key={key}>
                  <td>
                    <div className="airline-cell">
                      <img
                        src={airlineImages[airline.airlineId.toUpperCase()] ?? "/logo.png"}
                        alt={airline.name}
                        className="airline-thumb"
                      />
                      <span>{airline.name}</span>
                    </div>
                  </td>
                  <td>{flight.flightId}</td>
                  <td>{formatTimestamp(flight.departureTimestamp)}</td>
                  <td>{flight.status}</td>
                  <td>{onChainStatus}</td>
                  <td>{policyStatusLabel(policyStatus)}</td>
                  <td className="actions">
                    {actionNode}
                    {timerLabel && <span className={`buy-window ${timerClass}`}>{timerLabel}</span>}
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
      )}
      {isConnected && protocol && !allowanceEnough && (
        <div className="notice">
          <p>Approve the FlightDelays contract to spend your collateral before buying insurance.</p>
          <button onClick={handleApprove}>Approve {protocol.collateralSymbol}</button>
        </div>
      )}
    </section>
  );
}

function statusLabel(status: number) {
  switch (status) {
    case 1:
      return "Scheduled";
    case 2:
      return "Delayed";
    case 3:
      return "Departed";
    default:
      return "Not created";
  }
}

function policyStatusLabel(status: number) {
  switch (status) {
    case 1:
      return "Purchased";
    case 2:
      return "Claimed";
    default:
      return "None";
  }
}

function getBuyWindowInfo(
  chainData: { timestamp: bigint; status: number } | undefined,
  protocol: ProtocolInfo | null,
  now: number,
  fallbackDeparture?: number,
) {
  if (!protocol) return null;
  const status = chainData?.status ?? (fallbackDeparture ? 1 : 0);
  if (status !== 1) return null;
  const ts = chainData ? Number(chainData.timestamp ?? 0n) : fallbackDeparture ?? 0;
  if (!ts) return null;
  const openAt = ts - Number(protocol.policyWindow ?? 0n);
  const closeAt = ts - Number(protocol.delayWindow ?? 0n);
  if (closeAt <= openAt) return null;
  if (now < openAt) {
    return { state: "before" as const, seconds: openAt - now };
  }
  if (now >= openAt && now <= closeAt) {
    return { state: "open" as const, seconds: closeAt - now };
  }
  if (now < ts) {
    return { state: "after" as const, seconds: ts - now };
  }
  return { state: "after" as const, seconds: 0 };
}

function formatCountdown(seconds: number) {
  const clamped = Math.max(0, Math.floor(seconds));
  const minutes = Math.floor(clamped / 60);
  const secs = clamped % 60;
  if (minutes <= 0) {
    return `${secs}s`;
  }
  return `${minutes}m ${secs.toString().padStart(2, "0")}s`;
}

interface BuyerPageProps {
  flightsLoading: boolean;
  flattenedFlights: FlattenedFlight[];
  chainFlights: ChainFlightData;
  policies: PolicyMap;
  protocol: ProtocolInfo | null;
  isConnected: boolean;
  allowanceEnough: boolean;
  handleApprove: () => Promise<void>;
  handleBuy: (airlineId: string, flightId: string) => Promise<void>;
  handleClaim: (airlineId: string, flightId: string) => Promise<void>;
  now: number;
}
```

## File: ui/src/components/ProviderPage.tsx

```typescript
import type { Dispatch, SetStateAction } from "react";
import type { Address } from "viem";
import { parseUnits } from "viem";

import { airlineImages } from "../constants/airlines";
import type { AirlineWithFlights, ProtocolInfo } from "../types";
import { formatAmount } from "../utils/format";

export function ProviderPage({
  flights,
  isConnected,
  protocol,
  airlinesOnChain,
  vaultAllowances,
  vaultBalances,
  rewardEstimates,
  depositInputs,
  withdrawInputs,
  maxRewardsInputs,
  setDepositInputs,
  setWithdrawInputs,
  setMaxRewardsInputs,
  handleDeposit,
  handleWithdraw,
  handleApproveVault,
  handleClaimRewards,
}: ProviderPageProps) {
  return (
    <section className="panel">
      <h2>Provider Tools</h2>
      {!isConnected ? (
        <p>Connect your wallet to manage vault liquidity and rewards.</p>
      ) : flights.length === 0 ? (
        <p>No airlines loaded yet.</p>
      ) : (
        flights.map((airline) => {
          const vaultInfo = airlinesOnChain.get(airline.airlineId);
          const balance = vaultBalances.get(airline.airlineId) ?? 0n;
          const claimable = rewardEstimates.get(airline.airlineId) ?? 0n;
          const depositValue = depositInputs[airline.airlineId] ?? "";
          const withdrawValue = withdrawInputs[airline.airlineId] ?? "";
          const maxRewardsValue = maxRewardsInputs[airline.airlineId] ?? "";
          const decimals = protocol?.collateralDecimals ?? 18;
          const vaultAllowance = vaultAllowances.get(airline.airlineId) ?? 0n;
          const desiredDeposit = (() => {
            try {
              return depositValue ? parseUnits(depositValue, decimals) : 0n;
            } catch {
              return 0n;
            }
          })();
          const needsVaultApproval = desiredDeposit > 0n && vaultAllowance < desiredDeposit;
          return (
            <div key={airline.airlineId} className="airline-card">
              <div className="airline-card__header">
                <div className="airline-card__brand">
                  <img
                    src={airlineImages[airline.airlineId.toUpperCase()] ?? "/logo.png"}
                    alt={airline.name}
                    className="airline-thumb large"
                  />
                  <div>
                    <h3>{airline.name}</h3>
                    <p className="muted">Vault: {vaultInfo?.vault ?? "-"}</p>
                    <p className="muted">Rewards: {vaultInfo?.rewards ?? "-"}</p>
                  </div>
                </div>
                <div>
                  <div>
                    Staked: {protocol ? formatAmount(balance, protocol.collateralDecimals) : "0"}{" "}
                    {protocol?.collateralSymbol}
                  </div>
                  <div>
                    Claimable rewards: {protocol ? formatAmount(claimable, protocol.collateralDecimals) : "0"}{" "}
                    {protocol?.collateralSymbol}
                  </div>
                </div>
              </div>
              <div className="airline-card__actions">
                <div>
                  <label>Deposit amount</label>
                  <div className="form-row">
                    <input
                      type="number"
                      min="0"
                      step="0.01"
                      value={depositValue}
                      onChange={(e) => setDepositInputs((prev) => ({ ...prev, [airline.airlineId]: e.target.value }))}
                    />
                    <button onClick={() => handleDeposit(airline.airlineId)} disabled={!depositValue}>
                      Deposit
                    </button>
                  </div>
                  {protocol && (
                    <p className="muted small">
                      Allowance: {formatAmount(vaultAllowance, decimals)} {protocol.collateralSymbol}
                    </p>
                  )}
                  {needsVaultApproval && (
                    <button className="ghost-btn" onClick={() => handleApproveVault(airline.airlineId)}>
                      Approve vault spending
                    </button>
                  )}
                </div>
                <div>
                  <label>Withdraw amount</label>
                  <div className="form-row">
                    <input
                      type="number"
                      min="0"
                      step="0.01"
                      value={withdrawValue}
                      onChange={(e) => setWithdrawInputs((prev) => ({ ...prev, [airline.airlineId]: e.target.value }))}
                    />
                    <button onClick={() => handleWithdraw(airline.airlineId)} disabled={!withdrawValue}>
                      Withdraw
                    </button>
                  </div>
                </div>
                <div>
                  <label>Rewards (max batches)</label>
                  <div className="form-row">
                    <input
                      type="number"
                      min="1"
                      step="1"
                      value={maxRewardsValue}
                      onChange={(e) =>
                        setMaxRewardsInputs((prev) => ({ ...prev, [airline.airlineId]: e.target.value }))
                      }
                    />
                    <button onClick={() => handleClaimRewards(airline.airlineId)}>Claim Rewards</button>
                  </div>
                </div>
              </div>
            </div>
          );
        })
      )}
    </section>
  );
}

interface ProviderPageProps {
  flights: AirlineWithFlights[];
  isConnected: boolean;
  protocol: ProtocolInfo | null;
  airlinesOnChain: Map<string, { vault: Address; rewards: Address }>;
  vaultAllowances: Map<string, bigint>;
  vaultBalances: Map<string, bigint>;
  rewardEstimates: Map<string, bigint>;
  depositInputs: Record<string, string>;
  withdrawInputs: Record<string, string>;
  maxRewardsInputs: Record<string, string>;
  setDepositInputs: Dispatch<SetStateAction<Record<string, string>>>;
  setWithdrawInputs: Dispatch<SetStateAction<Record<string, string>>>;
  setMaxRewardsInputs: Dispatch<SetStateAction<Record<string, string>>>;
  handleDeposit: (airlineId: string) => Promise<void>;
  handleWithdraw: (airlineId: string) => Promise<void>;
  handleApproveVault: (airlineId: string) => Promise<void>;
  handleClaimRewards: (airlineId: string) => Promise<void>;
}
```

## File: ui/src/constants/airlines.ts

```typescript
export const airlineImages: Record<string, string> = {
    ALPHA: "/alpha-air.png",
    BETA: "/beta-wings.png",
    GAMMA: "/gamma-connect.png",
};
```

## File: ui/src/utils/format.ts

```typescript
import { formatUnits } from "viem";

export function formatAmount(value: bigint, decimals: number, precision = 2) {
    const formatted = Number(formatUnits(value, decimals));
    return formatted.toLocaleString(undefined, {
        minimumFractionDigits: 0,
        maximumFractionDigits: precision,
    });
}

export function formatTimestamp(timestamp: number) {
    const date = new Date(timestamp * 1000);
    return date.toLocaleString();
}
```

## File: ui/src/utils/hash.ts

```typescript
import { keccak256, stringToBytes } from "viem";

export function hashIdentifier(value: string) {
    const normalized = value.trim().toUpperCase();
    return keccak256(stringToBytes(normalized));
}

export function flightKey(airlineId: string, flightId: string) {
    return `${airlineId}::${flightId}`;
}
```

## File: ui/src/App.tsx

```typescript
import { useCallback, useEffect, useMemo, useState } from "react";
import { NavLink, Route, Routes } from "react-router-dom";
import { useAccount, usePublicClient, useWriteContract, useReadContracts } from "wagmi";
import { useQuery } from "@tanstack/react-query";
import {
  Address,
  Hex,
  BaseError,
  encodeAbiParameters,
  maxUint256,
  parseUnits,
  zeroAddress,
  createPublicClient,
  http,
} from "viem";
import type { Abi, PublicClient } from "viem";
import { useAppKit } from "@reown/appkit/library/react";
import { ToastContainer, toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";

import flightDelaysAbi from "./abi/FlightDelays.json";
import erc20Abi from "./abi/ERC20.json";
import vaultAbi from "./abi/Vault.json";
import rewardsAbi from "./abi/Rewards.json";
import { BuyerPage } from "./components/BuyerPage";
import { ProviderPage } from "./components/ProviderPage";
import { chain, flightsApiUrl, flightDelaysAddress, rpcUrl } from "./config";
import { fetchAirlinesWithFlights } from "./api/flights";
import type { AirlineWithFlights, ChainFlightData, FlattenedFlight, PolicyMap, ProtocolInfo } from "./types";
import { flightKey, hashIdentifier } from "./utils/hash";
import { formatAmount } from "./utils/format";

const flightDelaysABI = flightDelaysAbi as Abi;
const erc20ABI = erc20Abi as Abi;
const vaultABI = vaultAbi as Abi;
const rewardsABI = rewardsAbi as Abi;

const defaultRewardsToClaim = 5n;

type ToastMessages = {
  pending: string;
  success: string;
  error: string;
};

function formatWriteError(error: unknown) {
  if (error && typeof error === "object") {
    const baseError = error as Partial<BaseError> & Partial<Error>;
    if (typeof baseError.shortMessage === "string") {
      return baseError.shortMessage;
    }
    if (typeof baseError.message === "string") {
      return baseError.message;
    }
  }
  return "Check console for more details.";
}

type MaybeContractResult<T> = { status: "success"; result: T } | { status: "failure"; error: Error } | T | undefined;

function unwrapResult<T>(entry: MaybeContractResult<T>): T | undefined {
  if (entry === undefined || entry === null) return undefined;
  if (typeof entry === "object" && entry !== null && "status" in entry) {
    const typed = entry as { status?: string; result?: T };
    return typed.status === "success" ? (typed["result"] as T | undefined) : undefined;
  }
  return entry as T;
}

export default function App() {
  const { address, isConnected } = useAccount();
  const wagmiPublicClient = usePublicClient({ chainId: chain.id });
  const fallbackClient = useMemo(() => createPublicClient({ chain, transport: http(rpcUrl) }), []);
  const publicClient: PublicClient = wagmiPublicClient ?? fallbackClient;
  const { writeContract } = useWriteContract();
  const { open } = useAppKit();

  const flightsQuery = useQuery<AirlineWithFlights[]>({
    queryKey: ["flights", flightsApiUrl],
    queryFn: () => fetchAirlinesWithFlights(flightsApiUrl),
    refetchInterval: 10_000,
  });

  const flights = flightsQuery.data ?? [];
  const [now, setNow] = useState(() => Math.floor(Date.now() / 1000));

  useEffect(() => {
    const id = setInterval(() => setNow(Math.floor(Date.now() / 1000)), 1_000);
    return () => clearInterval(id);
  }, []);

  const submitWrite = useCallback(
    async (variables: Parameters<typeof writeContract>[0], messages: ToastMessages) => {
      const txPromise = new Promise<Hex>((resolve, reject) => {
        writeContract(variables, {
          onSuccess: (hash) => resolve(hash as Hex),
          onError: (error) => reject(error),
        });
      });
      toast.promise(txPromise, {
        pending: messages.pending,
        success: {
          render({ data }) {
            return `${messages.success}: ${data}`;
          },
        },
        error: {
          render({ data }) {
            return `${messages.error}: ${formatWriteError(data)}`;
          },
        },
      });
      return txPromise;
    },
    [writeContract],
  );

  const requestWalletConnection = async () => {
    if (address) {
      return true;
    }
    try {
      await open?.({ view: "Connect" });
    } catch (err) {
      console.error("wallet modal error", err);
    }
    return false;
  };

  const flattenedFlights: FlattenedFlight[] = useMemo(
    () => flights.flatMap((airline) => airline.flights.map((flight) => ({ airline, flight }))),
    [flights],
  );

  const protocolBaseContracts = useMemo(
    () => [
      { address: flightDelaysAddress, abi: flightDelaysABI, functionName: "policyPremium" } as const,
      { address: flightDelaysAddress, abi: flightDelaysABI, functionName: "policyPayout" } as const,
      { address: flightDelaysAddress, abi: flightDelaysABI, functionName: "policyWindow" } as const,
      { address: flightDelaysAddress, abi: flightDelaysABI, functionName: "delayWindow" } as const,
      { address: flightDelaysAddress, abi: flightDelaysABI, functionName: "collateral" } as const,
      { address: flightDelaysAddress, abi: flightDelaysABI, functionName: "NETWORK" } as const,
    ],
    [],
  );

  const protocolBaseReads = useReadContracts({
    contracts: protocolBaseContracts,
    allowFailure: false,
    query: {
      refetchInterval: 15_000,
    },
  });

  const collateralAddress = useMemo(() => {
    const baseResults = protocolBaseReads.data as readonly unknown[] | undefined;
    return ((baseResults?.[4] as Address | undefined) ?? zeroAddress) as Address;
  }, [protocolBaseReads.data]);

  const collateralContracts = useMemo(() => {
    if (!collateralAddress) {
      return [];
    }
    return [
      { address: collateralAddress, abi: erc20ABI, functionName: "symbol" } as const,
      { address: collateralAddress, abi: erc20ABI, functionName: "decimals" } as const,
    ];
  }, [collateralAddress]);

  const collateralReads = useReadContracts({
    contracts: collateralContracts,
    allowFailure: false,
    query: {
      enabled: collateralContracts.length > 0,
      refetchInterval: 30_000,
    },
  });

  const protocol: ProtocolInfo | null = useMemo(() => {
    const base = protocolBaseReads.data as readonly unknown[] | undefined;
    if (!base) return null;
    const policyPremium = (base[0] as bigint | undefined) ?? 0n;
    const policyPayout = (base[1] as bigint | undefined) ?? 0n;
    const policyWindow = (base[2] as bigint | undefined) ?? 0n;
    const delayWindow = (base[3] as bigint | undefined) ?? 0n;
    const collateral = (base[4] as Address | undefined) ?? zeroAddress;
    const network = (base[5] as Address | undefined) ?? zeroAddress;
    const collateralData = collateralReads.data as readonly unknown[] | undefined;
    const symbol = (collateralData?.[0] as string | undefined) ?? "TOKEN";
    const decimals = Number((collateralData?.[1] as number | bigint | undefined) ?? 18);
    return {
      policyPremium,
      policyPayout,
      policyWindow,
      delayWindow,
      collateral,
      collateralSymbol: symbol,
      collateralDecimals: decimals,
      network,
    };
  }, [protocolBaseReads.data, collateralReads.data]);

  const flightContracts = useMemo(
    () =>
      flattenedFlights.map(({ airline, flight }) => ({
        address: flightDelaysAddress,
        abi: flightDelaysABI,
        functionName: "flights",
        args: [hashIdentifier(airline.airlineId), hashIdentifier(flight.flightId)] as const,
      })),
    [flattenedFlights],
  );

  const flightReads = useReadContracts({
    contracts: flightContracts,
    allowFailure: true,
    query: {
      enabled: flightContracts.length > 0,
      refetchInterval: 6_000,
    },
  });

  const chainFlights: ChainFlightData = useMemo(() => {
    const map: ChainFlightData = {};
    const data = flightReads.data;
    if (!data) return map;
    data.forEach((resp, idx) => {
      const key = flightKey(flattenedFlights[idx].airline.airlineId, flattenedFlights[idx].flight.flightId);
      const result = unwrapResult<[bigint, number, bigint]>(resp as MaybeContractResult<[bigint, number, bigint]>);
      if (!result) return;
      const [timestamp, status, policiesSold] = result;
      map[key] = { timestamp, status: Number(status), policiesSold };
    });
    return map;
  }, [flightReads.data, flattenedFlights]);

  const policyContracts = useMemo(() => {
    if (!address) return [];
    return flattenedFlights.map(({ airline, flight }) => ({
      address: flightDelaysAddress,
      abi: flightDelaysABI,
      functionName: "policies",
      args: [hashIdentifier(airline.airlineId), hashIdentifier(flight.flightId), address] as const,
    }));
  }, [address, flattenedFlights]);

  const policyReads = useReadContracts({
    contracts: policyContracts,
    allowFailure: true,
    query: {
      enabled: policyContracts.length > 0,
      refetchInterval: 6_000,
    },
  });

  const policies: PolicyMap = useMemo(() => {
    const map: PolicyMap = {};
    const data = policyReads.data;
    if (!data) return map;
    data.forEach((resp, idx) => {
      const contract = policyContracts[idx];
      if (!contract) return;
      const key = flightKey(flattenedFlights[idx].airline.airlineId, flattenedFlights[idx].flight.flightId);
      const policyState = unwrapResult<number | bigint>(resp as MaybeContractResult<number | bigint>);
      if (policyState === undefined) return;
      map[key] = Number(policyState);
    });
    return map;
  }, [policyReads.data, policyContracts, flattenedFlights]);

  const airlineContracts = useMemo(
    () =>
      flights.map((airline) => ({
        address: flightDelaysAddress,
        abi: flightDelaysABI,
        functionName: "airlines",
        args: [hashIdentifier(airline.airlineId)] as const,
      })),
    [flights],
  );

  const airlineReads = useReadContracts({
    contracts: airlineContracts,
    allowFailure: true,
    query: {
      enabled: airlineContracts.length > 0,
      refetchInterval: 12_000,
    },
  });

  const airlinesOnChain = useMemo(() => {
    const map = new Map<string, { vault: Address; rewards: Address }>();
    const data = airlineReads.data;
    if (!data) return map;
    data.forEach((resp, idx) => {
      const tuple = unwrapResult<[Address, Address, bigint, Hex]>(
        resp as MaybeContractResult<[Address, Address, bigint, Hex]>,
      );
      if (!tuple) return;
      const [vault, rewards] = tuple;
      map.set(flights[idx].airlineId, { vault, rewards });
    });
    return map;
  }, [airlineReads.data, flights]);

  const policyAllowanceContracts = useMemo(() => {
    if (!address || !protocol) return [];
    return [
      {
        address: protocol.collateral,
        abi: erc20ABI,
        functionName: "allowance",
        args: [address, flightDelaysAddress] as const,
      },
    ];
  }, [address, protocol]);

  const policyAllowanceReads = useReadContracts({
    contracts: policyAllowanceContracts,
    allowFailure: false,
    query: {
      enabled: policyAllowanceContracts.length > 0,
      refetchInterval: 8_000,
    },
  });

  const allowanceValue = useMemo(() => {
    const data = policyAllowanceReads.data as readonly unknown[] | undefined;
    return (data?.[0] as bigint | undefined) ?? 0n;
  }, [policyAllowanceReads.data]);

  const vaultAllowanceContracts = useMemo(() => {
    if (!address || !protocol || airlinesOnChain.size === 0) return [];
    const entries: { airlineId: string; contract: any }[] = [];
    airlinesOnChain.forEach((info, airlineId) => {
      entries.push({
        airlineId,
        contract: {
          address: protocol.collateral,
          abi: erc20ABI,
          functionName: "allowance",
          args: [address, info.vault] as const,
        },
      });
    });
    return entries;
  }, [address, protocol, airlinesOnChain]);

  const vaultAllowanceReads = useReadContracts({
    contracts: vaultAllowanceContracts.map((entry) => entry.contract),
    allowFailure: true,
    query: {
      enabled: vaultAllowanceContracts.length > 0,
      refetchInterval: 12_000,
    },
  });

  const vaultAllowances = useMemo(() => {
    const map = new Map<string, bigint>();
    const data = vaultAllowanceReads.data;
    if (!data) return map;
    data.forEach((resp, idx) => {
      const airlineId = vaultAllowanceContracts[idx]?.airlineId;
      if (!airlineId) return;
      const allowance = unwrapResult<bigint>(resp as MaybeContractResult<bigint>);
      if (allowance === undefined) return;
      map.set(airlineId, allowance);
    });
    return map;
  }, [vaultAllowanceReads.data, vaultAllowanceContracts]);

  const vaultBalanceContracts = useMemo(() => {
    if (!address || airlinesOnChain.size === 0) return [];
    const entries: { airlineId: string; contract: any }[] = [];
    airlinesOnChain.forEach((info, airlineId) => {
      entries.push({
        airlineId,
        contract: {
          address: info.vault,
          abi: vaultABI,
          functionName: "activeBalanceOf",
          args: [address] as const,
        },
      });
    });
    return entries;
  }, [address, airlinesOnChain]);

  const vaultBalanceReads = useReadContracts({
    contracts: vaultBalanceContracts.map((entry) => entry.contract),
    allowFailure: true,
    query: {
      enabled: vaultBalanceContracts.length > 0,
      refetchInterval: 10_000,
    },
  });

  const vaultBalances = useMemo(() => {
    const balances = new Map<string, bigint>();
    const data = vaultBalanceReads.data;
    if (!data) return balances;
    data.forEach((resp, idx) => {
      const airlineId = vaultBalanceContracts[idx]?.airlineId;
      if (!airlineId) return;
      const balance = unwrapResult<bigint>(resp as MaybeContractResult<bigint>);
      if (balance === undefined) return;
      balances.set(airlineId, balance);
    });
    return balances;
  }, [vaultBalanceReads.data, vaultBalanceContracts]);

  const rewardContracts = useMemo(() => {
    if (!address || !protocol || airlinesOnChain.size === 0) return [];
    const entries: { airlineId: string; contract: any }[] = [];
    airlinesOnChain.forEach((info, airlineId) => {
      const encoded = encodeAbiParameters(
        [
          { name: "network", type: "address" },
          { name: "maxRewards", type: "uint256" },
          { name: "hints", type: "bytes[]" },
        ],
        [protocol.network, defaultRewardsToClaim, []],
      );
      entries.push({
        airlineId,
        contract: {
          address: info.rewards,
          abi: rewardsABI,
          functionName: "claimable",
          args: [protocol.collateral, address!, encoded] as const,
        },
      });
    });
    return entries;
  }, [address, protocol, airlinesOnChain]);

  const rewardReads = useReadContracts({
    contracts: rewardContracts.map((entry) => entry.contract),
    allowFailure: true,
    query: {
      enabled: rewardContracts.length > 0,
      refetchInterval: 15_000,
    },
  });

  const rewardEstimates = useMemo(() => {
    const map = new Map<string, bigint>();
    const data = rewardReads.data;
    if (!data) return map;
    data.forEach((resp, idx) => {
      const airlineId = rewardContracts[idx]?.airlineId;
      if (!airlineId) return;
      const rewardValue = unwrapResult<bigint>(resp as MaybeContractResult<bigint>);
      if (rewardValue === undefined) return;
      map.set(airlineId, rewardValue);
    });
    return map;
  }, [rewardReads.data, rewardContracts]);

  const [depositInputs, setDepositInputs] = useState<Record<string, string>>({});
  const [withdrawInputs, setWithdrawInputs] = useState<Record<string, string>>({});
  const [maxRewardsInputs, setMaxRewardsInputs] = useState<Record<string, string>>({});

  const handleApprove = async () => {
    if (!protocol) return;
    if (!address) {
      await requestWalletConnection();
      return;
    }
    try {
      await submitWrite(
        {
          address: protocol.collateral,
          abi: erc20ABI,
          functionName: "approve",
          args: [flightDelaysAddress, maxUint256],
        },
        {
          pending: "Submitting collateral approval...",
          success: "Collateral approval sent",
          error: "Collateral approval failed",
        },
      );
    } catch (err) {
      console.error("policy approval error", err);
    }
  };

  const handleBuy = async (airlineId: string, flightId: string) => {
    if (!address) {
      await requestWalletConnection();
      return;
    }
    try {
      await submitWrite(
        {
          address: flightDelaysAddress,
          abi: flightDelaysABI,
          functionName: "buyInsurance",
          args: [hashIdentifier(airlineId), hashIdentifier(flightId)],
        },
        {
          pending: `Submitting buy for ${flightId}...`,
          success: "Buy transaction sent",
          error: "Buy failed",
        },
      );
    } catch (err) {
      console.error("buy error", err);
    }
  };

  const handleClaim = async (airlineId: string, flightId: string) => {
    if (!address) {
      await requestWalletConnection();
      return;
    }
    try {
      await submitWrite(
        {
          address: flightDelaysAddress,
          abi: flightDelaysABI,
          functionName: "claimInsurance",
          args: [hashIdentifier(airlineId), hashIdentifier(flightId)],
        },
        {
          pending: `Submitting claim for ${flightId}...`,
          success: "Claim transaction sent",
          error: "Claim failed",
        },
      );
    } catch (err) {
      console.error("claim error", err);
    }
  };

  const handleDeposit = async (airlineId: string) => {
    if (!protocol) return;
    if (!address) {
      await requestWalletConnection();
      return;
    }
    const amount = depositInputs[airlineId];
    if (!amount) return;
    try {
      const parsed = parseUnits(amount, protocol.collateralDecimals);
      const vault = airlinesOnChain.get(airlineId)?.vault;
      if (!vault) return;
      await submitWrite(
        {
          address: vault,
          abi: vaultABI,
          functionName: "deposit",
          args: [address as Address, parsed],
        },
        {
          pending: `Depositing to ${airlineId} vault...`,
          success: "Deposit transaction sent",
          error: "Deposit failed",
        },
      );
      setDepositInputs((prev) => ({ ...prev, [airlineId]: "" }));
    } catch (err) {
      console.error("deposit error", err);
    }
  };

  const handleWithdraw = async (airlineId: string) => {
    if (!protocol) return;
    if (!address) {
      await requestWalletConnection();
      return;
    }
    const amount = withdrawInputs[airlineId];
    if (!amount) return;
    try {
      const parsed = parseUnits(amount, protocol.collateralDecimals);
      const vault = airlinesOnChain.get(airlineId)?.vault;
      if (!vault) return;
      await submitWrite(
        {
          address: vault,
          abi: vaultABI,
          functionName: "withdraw",
          args: [address as Address, parsed],
        },
        {
          pending: `Withdrawing from ${airlineId} vault...`,
          success: "Withdraw transaction sent",
          error: "Withdraw failed",
        },
      );
      setWithdrawInputs((prev) => ({ ...prev, [airlineId]: "" }));
    } catch (err) {
      console.error("withdraw error", err);
    }
  };

  const handleClaimRewards = async (airlineId: string) => {
    if (!protocol) return;
    if (!address) {
      await requestWalletConnection();
      return;
    }
    const rewardsAddr = airlinesOnChain.get(airlineId)?.rewards;
    if (!rewardsAddr) return;
    const maxRewards = maxRewardsInputs[airlineId] ? BigInt(maxRewardsInputs[airlineId]) : defaultRewardsToClaim;
    try {
      const data = encodeAbiParameters(
        [
          { name: "network", type: "address" },
          { name: "maxRewards", type: "uint256" },
          { name: "hints", type: "bytes[]" },
        ],
        [protocol.network, maxRewards, []],
      );
      await submitWrite(
        {
          address: rewardsAddr,
          abi: rewardsABI,
          functionName: "claimRewards",
          args: [address as Address, protocol.collateral, data],
        },
        {
          pending: `Claiming rewards for ${airlineId}...`,
          success: "Rewards claim transaction sent",
          error: "Rewards claim failed",
        },
      );
    } catch (err) {
      console.error("claim rewards error", err);
    }
  };

  const handleApproveVault = async (airlineId: string) => {
    if (!protocol) return;
    if (!address) {
      await requestWalletConnection();
      return;
    }
    const vault = airlinesOnChain.get(airlineId)?.vault;
    if (!vault) return;
    try {
      await submitWrite(
        {
          address: protocol.collateral,
          abi: erc20ABI,
          functionName: "approve",
          args: [vault, maxUint256],
        },
        {
          pending: `Approving ${airlineId} vault...`,
          success: "Vault approval transaction sent",
          error: "Vault approval failed",
        },
      );
    } catch (err) {
      console.error("vault approval error", err);
    }
  };

  const allowanceEnough = protocol ? allowanceValue >= protocol.policyPremium : false;

  const collateralBalanceContracts = useMemo(() => {
    if (!address || !protocol) return [];
    return [
      {
        address: protocol.collateral,
        abi: erc20ABI,
        functionName: "balanceOf",
        args: [address] as const,
      },
    ];
  }, [address, protocol]);

  const collateralBalanceReads = useReadContracts({
    contracts: collateralBalanceContracts,
    allowFailure: false,
    query: {
      enabled: collateralBalanceContracts.length > 0,
      refetchInterval: 12_000,
    },
  });

  const collateralBalance = useMemo(() => {
    const data = collateralBalanceReads.data as readonly unknown[] | undefined;
    return (data?.[0] as bigint | undefined) ?? 0n;
  }, [collateralBalanceReads.data]);

  return (
    <div className="app-shell">
      <header className="app-header">
        <div className="app-title">
          <img src="/logo.png" alt="Symbiotic Flight Delay Insurance" className="app-logo" />
          <div>
            <h1>Symbiotic Flight Delay Insurance</h1>
            <p>Buy coverage, monitor flights, and manage vault liquidity powered by Settlement signatures.</p>
          </div>
        </div>
        <WalletStatus protocol={protocol} isConnected={isConnected} collateralBalance={collateralBalance} />
      </header>
      <nav className="main-nav">
        <NavLink to="/" end>
          Buy Coverage
        </NavLink>
        <NavLink to="/providers">Provide Coverage</NavLink>
      </nav>

      <Routes>
        <Route
          path="/"
          element={
            <BuyerPage
              flightsLoading={flightsQuery.isLoading}
              flattenedFlights={flattenedFlights}
              chainFlights={chainFlights}
              policies={policies}
              protocol={protocol}
              isConnected={isConnected}
              allowanceEnough={allowanceEnough}
              handleApprove={handleApprove}
              handleBuy={handleBuy}
              handleClaim={handleClaim}
              now={now}
            />
          }
        />
        <Route
          path="/providers"
          element={
            <ProviderPage
              flights={flights}
              isConnected={isConnected}
              protocol={protocol}
              airlinesOnChain={airlinesOnChain}
              vaultAllowances={vaultAllowances}
              vaultBalances={vaultBalances}
              rewardEstimates={rewardEstimates}
              depositInputs={depositInputs}
              setDepositInputs={setDepositInputs}
              withdrawInputs={withdrawInputs}
              setWithdrawInputs={setWithdrawInputs}
              maxRewardsInputs={maxRewardsInputs}
              setMaxRewardsInputs={setMaxRewardsInputs}
              handleDeposit={handleDeposit}
              handleWithdraw={handleWithdraw}
              handleApproveVault={handleApproveVault}
              handleClaimRewards={handleClaimRewards}
            />
          }
        />
      </Routes>
      <ToastContainer position="bottom-right" newestOnTop closeOnClick pauseOnFocusLoss={false} />
    </div>
  );
}

function WalletStatus({
  protocol,
  isConnected,
  collateralBalance,
}: {
  protocol: ProtocolInfo | null;
  isConnected: boolean;
  collateralBalance: bigint;
}) {
  const symbol = protocol?.collateralSymbol ?? "TOKEN";
  const decimals = protocol?.collateralDecimals ?? 18;
  const formattedBalance = (() => {
    if (!isConnected) return "Connect wallet";
    if (!protocol) return `-- ${symbol}`;
    return `${formatAmount(collateralBalance, decimals, 4)} ${symbol}`;
  })();

  return (
    <div className="wallet-status">
      <div className="balance-chip">
        <span className="balance-chip__label">{symbol} balance</span>
        <span className="balance-chip__value">{formattedBalance}</span>
      </div>
      <appkit-button />
    </div>
  );
}
```

## File: ui/src/config.ts

```typescript
import { defineChain } from "viem";
import type { Address } from "viem";

const isBrowser = typeof window !== "undefined";
const fallbackHost = isBrowser ? window.location.hostname || "127.0.0.1" : "127.0.0.1";
const fallbackProtocol = isBrowser ? window.location.protocol : "http:";

const chainId = Number(import.meta.env.VITE_CHAIN_ID ?? 31337);
const chainName = import.meta.env.VITE_CHAIN_NAME ?? "Symbiotic Local";

const defaultRpcUrl = `${fallbackProtocol}//${fallbackHost}:8545`;
const configuredRpcUrl = import.meta.env.VITE_RPC_URL ?? defaultRpcUrl;

const defaultFlightsApiUrl = `${fallbackProtocol}//${fallbackHost}:8085`;
const configuredFlightsApiUrl = import.meta.env.VITE_FLIGHTS_API_URL ?? defaultFlightsApiUrl;

const dockerHostnames = new Set(["flights-api", "anvil"]);

function resolveUrl(raw: string, fallbackPort?: number) {
    if (!isBrowser) {
        return raw;
    }
    try {
        const parsed = new URL(raw, `${fallbackProtocol}//${fallbackHost}`);
        if (dockerHostnames.has(parsed.hostname)) {
            parsed.hostname = fallbackHost;
            if (fallbackPort && !parsed.port) {
                parsed.port = String(fallbackPort);
            }
            parsed.protocol = fallbackProtocol;
        }
        return parsed.toString();
    } catch {
        if (fallbackPort) {
            return `${fallbackProtocol}//${fallbackHost}:${fallbackPort}`;
        }
        return `${fallbackProtocol}//${fallbackHost}`;
    }
}

export const rpcUrl = resolveUrl(configuredRpcUrl, 8545);

export const flightDelaysAddress = (import.meta.env.VITE_FLIGHT_DELAYS_ADDRESS ??
    "0x0000000000000000000000000000000000000000") as Address;
export const flightsApiUrl = resolveUrl(configuredFlightsApiUrl, 8085);

export const chain = defineChain({
    id: chainId,
    name: chainName,
    nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
    rpcUrls: { default: { http: [rpcUrl] }, public: { http: [rpcUrl] } },
});
```

## File: ui/src/main.tsx

```typescript
import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import App from "./App";
import "./styles.css";
import { AppProviders } from "./wallet";

ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
  <React.StrictMode>
    <BrowserRouter>
      <AppProviders>
        <App />
      </AppProviders>
    </BrowserRouter>
  </React.StrictMode>,
);
```

## File: ui/src/styles.css

```css
:root {
    font-family:
        "Inter",
        system-ui,
        -apple-system,
        BlinkMacSystemFont,
        "Segoe UI",
        sans-serif;
    line-height: 1.5;
    font-weight: 400;
    background-color: #0b0c10;
    color: #f4f7ff;
}

body {
    margin: 0;
    min-height: 100vh;
    background: #050608;
}

a {
    color: inherit;
}

* {
    box-sizing: border-box;
}

#root {
    min-height: 100vh;
}

button {
    cursor: pointer;
}

.app-shell {
    max-width: 1100px;
    margin: 0 auto;
    padding: 32px 24px 80px;
    display: flex;
    flex-direction: column;
    gap: 24px;
}

.app-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 24px;
}

.app-title {
    display: flex;
    align-items: center;
    gap: 16px;
}

.app-logo {
    width: 56px;
    height: 56px;
    border-radius: 12px;
    object-fit: cover;
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.35);
}

.app-header h1 {
    margin: 0 0 8px;
}

.app-header p {
    margin: 0;
    color: #b3bdcc;
}

.main-nav {
    display: flex;
    gap: 16px;
    margin-bottom: 8px;
}

.main-nav a {
    text-decoration: none;
    color: #b3bdcc;
    padding: 6px 12px;
    border-radius: 999px;
    border: 1px solid transparent;
}

.main-nav a.active {
    color: #0b0c10;
    background: #ffd666;
    border-color: #ffd666;
}

.panel {
    background: rgba(255, 255, 255, 0.03);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 16px;
    padding: 20px;
}

.panel h2 {
    margin-top: 0;
}

table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.9rem;
}

th,
td {
    padding: 10px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}

.airline-cell {
    display: flex;
    align-items: center;
    gap: 10px;
}

.airline-thumb {
    width: 36px;
    height: 36px;
    border-radius: 8px;
    object-fit: cover;
    border: 1px solid rgba(255, 255, 255, 0.1);
}

.airline-thumb.large {
    width: 64px;
    height: 64px;
}

.actions button {
    padding: 6px 16px;
    border-radius: 8px;
    border: none;
    background: #5e5be5;
    color: white;
}

.actions button:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

.buy-window {
    display: block;
    font-size: 0.78rem;
    margin-top: 4px;
    color: #b3bdcc;
}

.buy-window.open {
    color: #ffd666;
}

.buy-window.before {
    color: #8bc1ff;
}

.buy-window.after {
    color: #ff8f8f;
}

.wallet-status {
    display: flex;
    gap: 12px;
    align-items: center;
}

.balance-chip {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 8px 12px;
    min-width: 170px;
    border-radius: 12px;
    border: 1px solid rgba(255, 255, 255, 0.12);
    background: rgba(255, 255, 255, 0.05);
}

.balance-chip__label {
    font-size: 0.75rem;
    color: #b3bdcc;
    letter-spacing: 0.01em;
}

.balance-chip__value {
    font-weight: 600;
    color: #f4f7ff;
}

.wallet-status button {
    border-radius: 8px;
    border: 1px solid rgba(255, 255, 255, 0.12);
    background: transparent;
    color: white;
    padding: 8px 16px;
}

.muted {
    color: #9aa5b8;
    font-size: 0.9rem;
}

.muted.small {
    font-size: 0.75rem;
}

.feedback {
    background: rgba(94, 91, 229, 0.15);
    border: 1px solid rgba(94, 91, 229, 0.4);
    border-radius: 12px;
    padding: 12px 16px;
}

.notice {
    margin-top: 16px;
    padding: 12px;
    border-radius: 10px;
    background: rgba(255, 214, 102, 0.12);
    border: 1px solid rgba(255, 214, 102, 0.4);
}

.notice button {
    margin-top: 8px;
    border: none;
    border-radius: 8px;
    background: #ffd666;
    color: #1b1d23;
    padding: 6px 14px;
}

.airline-card {
    border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: 14px;
    padding: 16px;
    margin-top: 16px;
    background: rgba(0, 0, 0, 0.25);
}

.airline-card__header {
    display: flex;
    justify-content: space-between;
    gap: 16px;
}

.airline-card__brand {
    display: flex;
    gap: 12px;
    align-items: center;
}

.airline-card__actions {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 16px;
    margin-top: 16px;
}

.form-row {
    display: flex;
    gap: 8px;
    align-items: center;
}

input[type="number"],
input[type="text"] {
    flex: 1;
    border-radius: 8px;
    border: 1px solid rgba(255, 255, 255, 0.2);
    padding: 8px;
    background: transparent;
    color: white;
}

.form-row button {
    border: none;
    border-radius: 8px;
    padding: 8px 14px;
    background: #5e5be5;
    color: white;
}

.ghost-btn {
    margin-top: 6px;
    border-radius: 8px;
    border: 1px dashed rgba(255, 255, 255, 0.4);
    background: transparent;
    color: #f4f7ff;
    padding: 6px 12px;
}

.error-text {
    color: #ff8686;
    font-size: 0.85rem;
}
```

## File: ui/src/types.ts

```typescript
export type FlightAPIStatus = "SCHEDULED" | "DELAYED" | "DEPARTED";

export interface AirlineDTO {
    airlineId: string;
    name: string;
    code: string;
}

export interface FlightDTO {
    airlineId: string;
    flightId: string;
    departureTimestamp: number;
    status: FlightAPIStatus;
}

export interface AirlineWithFlights extends AirlineDTO {
    flights: FlightDTO[];
}

export interface FlightChainState {
    timestamp: bigint;
    status: number;
    policiesSold: bigint;
}

export interface PolicyState {
    status: number;
}

export interface ProtocolInfo {
    policyPremium: bigint;
    policyPayout: bigint;
    policyWindow: bigint;
    delayWindow: bigint;
    collateral: `0x${string}`;
    collateralSymbol: string;
    collateralDecimals: number;
    network: `0x${string}`;
}

export type ChainFlightData = Record<string, FlightChainState>;

export type PolicyMap = Record<string, number>;

export type FlattenedFlight = {
    airline: AirlineWithFlights;
    flight: FlightDTO;
};
```

## File: ui/src/wallet.tsx

```typescript
import type { ReactNode } from "react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { WagmiProvider } from "wagmi";
import { createAppKit } from "@reown/appkit/react";
import { WagmiAdapter } from "@reown/appkit-adapter-wagmi";
import { chain } from "./config";

const queryClient = new QueryClient();

const projectId = (import.meta.env.VITE_APPKIT_PROJECT_ID as string | undefined) ?? "43eff4b1fac476ffee43e467ab916f34";

const appUrl = typeof window !== "undefined" ? window.location.origin : "https://symbiotic.fi";

const metadata = {
  name: "Symbiotic Flight Delays",
  description: "Purchase policies, manage vaults, and claim rewards.",
  url: appUrl,
  icons: ["https://symbiotic.fi/favicon.ico"],
};

const networks = [chain];

export const wagmiAdapter = new WagmiAdapter({
  networks: networks as any,
  projectId,
  ssr: false,
});

createAppKit({
  adapters: [wagmiAdapter],
  networks: networks as any,
  projectId,
  metadata,
  features: {
    analytics: false,
  },
  themeMode: "light",
});

export function AppProviders({ children }: { children: ReactNode }) {
  return (
    <WagmiProvider config={wagmiAdapter.wagmiConfig}>
      <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
    </WagmiProvider>
  );
}
```

## File: ui/.dockerignore

```
node_modules
.DS_Store
dist
.tmp
.git
```

## File: ui/.env.docker

```
VITE_CHAIN_ID=31337
VITE_CHAIN_NAME="Symbiotic Local"
VITE_RPC_URL=http://anvil:8545
VITE_FLIGHTS_API_URL=http://flights-api:8085
VITE_FLIGHT_DELAYS_ADDRESS=0xA4b0f5eb09891c1538494c4989Eea0203b1153b1
VITE_APPKIT_PROJECT_ID=43eff4b1fac476ffee43e467ab916f34
```

## File: ui/.env.example

```
VITE_CHAIN_ID=31337
VITE_CHAIN_NAME=Local
VITE_RPC_URL=http://127.0.0.1:8545
VITE_FLIGHT_DELAYS_ADDRESS=0xA4b0f5eb09891c1538494c4989Eea0203b1153b1
VITE_FLIGHTS_API_URL=http://127.0.0.1:8085
VITE_APPKIT_PROJECT_ID=43eff4b1fac476ffee43e467ab916f34
```

## File: ui/Dockerfile

```
FROM node:20-alpine

WORKDIR /app

COPY package.json pnpm-lock.yaml ./
RUN apk add --no-cache python3 make g++ \
    && corepack enable \
    && pnpm install --frozen-lockfile

COPY . .
RUN cp .env.docker .env && pnpm run build

EXPOSE 5173
CMD ["pnpm", "run", "preview", "--host", "0.0.0.0", "--port", "5173"]
```

## File: ui/index.html

```html
<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <link rel="icon" type="image/png" href="/logo.png" />
        <title>Flight Delay Insurance</title>
    </head>
    <body>
        <div id="root"></div>
        <script type="module" src="/src/main.tsx"></script>
    </body>
</html>
```

## File: ui/package.json

```json
{
    "name": "flight-delays-ui",
    "version": "0.1.0",
    "private": true,
    "type": "module",
    "scripts": {
        "dev": "vite",
        "build": "tsc -b && vite build",
        "preview": "vite preview"
    },
    "dependencies": {
        "@reown/appkit": "1.8.4",
        "@reown/appkit-adapter-wagmi": "1.8.4",
        "@tanstack/react-query": "5.51.23",
        "react": "18.3.1",
        "react-dom": "18.3.1",
        "react-toastify": "11.0.5",
        "react-router-dom": "6.28.1",
        "viem": "2.39.3",
        "wagmi": "2.19.5"
    },
    "devDependencies": {
        "@types/react": "18.3.8",
        "@types/react-dom": "18.3.0",
        "@vitejs/plugin-react": "4.3.3",
        "typescript": "5.7.3",
        "vite": "5.4.11"
    }
}
```

## File: ui/tsconfig.json

```json
{
    "compilerOptions": {
        "target": "ES2020",
        "useDefineForClassFields": true,
        "lib": ["ES2020", "DOM", "DOM.Iterable"],
        "module": "ESNext",
        "skipLibCheck": true,
        "moduleResolution": "Bundler",
        "allowSyntheticDefaultImports": true,
        "esModuleInterop": true,
        "resolveJsonModule": true,
        "isolatedModules": true,
        "noEmit": true,
        "jsx": "react-jsx",
        "strict": true,
        "types": ["react", "react-dom", "vite/client"]
    },
    "include": ["src"]
}
```

## File: ui/tsconfig.tsbuildinfo

```
{"root":["./src/app.tsx","./src/config.ts","./src/main.tsx","./src/types.ts","./src/wallet.tsx","./src/api/flights.ts","./src/components/buyerpage.tsx","./src/components/providerpage.tsx","./src/constants/airlines.ts","./src/utils/format.ts","./src/utils/hash.ts"],"version":"5.7.3"}
```

## File: ui/vite.config.ts

```typescript
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
    plugins: [react()],
    server: {
        port: 5173,
        host: true,
    },
});
```

## File: .gitmodules

```
[submodule "lib/forge-std"]
	path = lib/forge-std
	url = https://github.com/foundry-rs/forge-std
```

## File: .prettierrc

```
{
  "printWidth": 120,
  "tabWidth": 2,
  "useTabs": false,
  "singleQuote": false,
  "bracketSpacing": true,
  "trailingComma": "all",
  "overrides": [
    {
      "files": "*.sol",
      "options": {
        "printWidth": 120,
        "tabWidth": 4,
        "useTabs": false,
        "singleQuote": false,
        "bracketSpacing": false
      }
    },
    {
      "files": "*.json",
      "options": {
        "tabWidth": 4
      }
    }
  ]
}
```

## File: CONTRIBUTING.md

````markdown
# Contributing

- [Install](#install)
- [Pre-commit Hooks](#pre-commit-hooks)
- [Requirements for merge](#requirements-for-merge)
- [Branching](#branching)
    - [Main](#main)
    - [Audit](#audit)
- [Code Practices](#code-practices)
    - [Code Style](#code-style)
    - [Solidity Versioning](#solidity-versioning)
    - [Interfaces](#interfaces)
    - [NatSpec \& Comments](#natspec--comments)
- [Testing](#testing)
    - [Best Practices](#best-practices)
    - [IR Compilation](#ir-compilation)
    - [Gas Metering](#gas-metering)
- [Deployment](#deployment)
    - [Bytecode Hash](#bytecode-hash)
- [Dependency Management](#dependency-management)
- [Releases](#releases)

## Install

Follow these steps to set up your local environment for development:

- [Install foundry](https://book.getfoundry.sh/getting-started/installation)
- Install dependencies: `forge install`
- [Install pre-commit](https://pre-commit.com/#installation)
- Install pre commit hooks: `pre-commit install`

## Pre-commit Hooks

Follow the [installation steps](#install) to enable pre-commit hooks. To ensure consistency in our formatting `pre-commit` is used to check whether code was formatted properly and the documentation is up to date. Whenever a commit does not meet the checks implemented by pre-commit, the commit will fail and the pre-commit checks will modify the files to make the commits pass. Include these changes in your commit for the next commit attempt to succeed. On pull requests the CI checks whether all pre-commit hooks were run correctly.
This repo includes the following pre-commit hooks that are defined in the `.pre-commit-config.yaml`:

- `mixed-line-ending`: This hook ensures that all files have the same line endings (LF).
- `trailing-whitespace`: Strips trailing spaces from lines so that diffs remain clean and editors don't introduce noise.
- `end-of-file-fixer`: Ensures every file ends with a single newline and removes extra blank lines at the end of files.
- `check-merge-conflict`: Fails when Git merge conflict markers are present to avoid committing unresolved conflicts.
- `check-json`: Validates JSON files and fails fast on malformed syntax.
- `check-yaml`: Parses YAML files to verify they are syntactically valid.
- `sort-imports`: Normalises and sorts imports according to the rules mentioned in the [Code Style](#code-style) below.
- `sort-errors`: Sorts errors according to the rules mentioned in the [Code Style](#code-style) below.
- `format`: This hook uses `forge fmt` to format all Solidity files.
- `doc`: This hook uses `forge doc` to generate the Solidity documentation. Commit the generated files whenever the documentation changes.
- `prettier`: All remaining files are formatted using prettier.

## Requirements for merge

In order for a PR to be merged, it must pass the following requirements:

- All commits within the PR must be signed
- CI must pass (tests, linting, etc.)
- New features must be merged with associated tests
- Bug fixes must have a corresponding test that fails without the fix
- The PR must be approved by at least one maintainer

## Branching

This section outlines the branching strategy of this repo.

### Main

The main branch is supposed to reflect the deployed state on all networks, if not indicated otherwise inside the README. Only audited code should be merged into main. Сommits from dev branches should be merged into the main branch using a regular merge strategy. The commit messages should follow [the Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/).

### Audit

Before an audit, the code should be frozen on a branch dedicated to the audit with the naming convention `audit/<provider>`. Each fix in response to an audit finding should be developed as a separate commit. The commit message should look similar to `fix: <provider> - <issue title>`.

## Code Practices

### Code Style

The repo follows the official [Solidity Style Guide](https://docs.soliditylang.org/en/latest/style-guide.html). In addition to that, this repo also borrows the following rules from [OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/GUIDELINES.md#solidity-conventions):

- Internal or private state variables or functions should have an underscore prefix.

    ```solidity
    contract TestContract {
        uint256 private _privateVar;
        uint256 internal _internalVar;
        function _testInternal() internal { ... }
        function _testPrivate() private { ... }
    }
    ```

- Naming collisions should be avoided using a single trailing underscore.

    ```solidity
    contract TestContract {
        uint256 public foo;

        constructor(uint256 foo_) {
          foo = foo_;
        }
    }
    ```

- Interface names should have a capital I prefix.

    ```solidity
    interface IERC777 {
    ```

- Contracts not intended to be used standalone should be marked abstract, so they are required to be inherited by other contracts.

    ```solidity
    abstract contract AccessControl is ..., {
    ```

- Unchecked arithmetic blocks should contain comments explaining why overflow is guaranteed not to happen or is permissible. If the reason is immediately apparent from the line above the unchecked block, the comment may be omitted.

Also, such exceptions/additions exist:

- Functions should be grouped according to their visibility and ordered:
    1. constructor

    2. external

    3. public

    4. internal

    5. private

    6. receive function (if exists)

    7. fallback function (if exists)

- Each contract should be virtually divided into sections by using such separators:
    1. /\* CONSTANTS \*/
    2. /\* IMMUTABLES \*/
    3. /\* STATE VARIABLES \*/
    4. /\* MODIFIERS \*/
    5. /\* CONSTRUCTOR \*/
    6. /\* EXTERNAL FUNCTIONS \*/
    7. /\* PUBLIC FUNCTIONS \*/
    8. /\* INTERNAL FUNCTIONS \*/
    9. /\* PRIVATE FUNCTIONS \*/
    10. /\* RECEIVE FUNCTION \*/
    11. /\* FALLBACK FUNCTION \*/

- Each interface should be virtually divided into sections by using such separators:
    1. /\* ERRORS \*/
    2. /\* STRUCTS \*/
    3. /\* EVENTS \*/
    4. /\* FUNCTIONS \*/

- Do not use external and private visibilities in most cases.

- Events should generally be emitted immediately after the state change that they
  represent, and should be named the same as the function's name. Some exceptions may be made for gas
  efficiency if the result doesn't affect the observable ordering of events.

    ```solidity
    function _burn(address who, uint256 value) internal {
        super._burn(who, value);
        emit Burn(who, value);
    }
    ```

- Custom errors should be used whenever possible. The naming should be concise and easy to read.

- Imports should be divided into separate groups and ordered alphabetically ascending inside each group:
    1. contracts

    2. libraries

    3. interfaces

    4. external files separately

    ```solidity
    import {NetworkManager} from "../base/NetworkManager.sol";
    import {OzEIP712} from "../base/OzEIP712.sol";
    import {PermissionManager} from "../base/PermissionManager.sol";

    import {Checkpoints} from "../../libraries/structs/Checkpoints.sol";
    import {KeyTags} from "../../libraries/utils/KeyTags.sol";

    import {ISettlement} from "../interfaces/modules/settlement/ISettlement.sol";
    import {ISigVerifier} from "../interfaces/modules/settlement/sig-verifiers/ISigVerifier.sol";

    import {StaticDelegateCallable} from "@symbioticfi/core/src/contracts/common/StaticDelegateCallable.sol";
    import {Subnetwork} from "@symbioticfi/core/src/contracts/libraries/Subnetwork.sol";

    import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
    ```

- In case of comparison with `msg.sender` or `tx.origin`, these keywords should be on the right side of the inequality.

    ```solidity
    modifier onlyOwner() internal {
        if (owner != msg.sender) {
          revert NotOwner();
        }
    }
    ```

- Errors should be ordered alphabetically ascending.

    ```solidity
    error InsufficientFunds();
    error NoAccess();
    error NotOwner();
    ```

### Solidity Versioning

Contracts that are meant to be deployed should have an explicit version set in the `pragma` statement.

```solidity
pragma solidity 0.8.X;
```

Abstract contracts, libraries and interfaces should use the caret (`^`) range operator to specify the version range to ensure better compatibility.

```solidity
pragma solidity ^0.X.0;
```

Libraries and abstract contracts using functionality introduced in newer versions of Solidity can use caret range operators with higher path versions (e.g., `^0.8.24` when using transient storage opcodes). For interfaces, it should be considered to use the greater than or equal to (`>=`) range operator to ensure better compatibility with future versions of Solidity.

### Interfaces

Every contract MUST implement its corresponding interface that includes all externally callable functions, errors and events.

### NatSpec & Comments

Interfaces should be the entry point for all contracts. When exploring a contract within the repository, the interface MUST contain all relevant information to understand the functionality of the contract in the form of NatSpec comments. This includes all externally callable functions, structs, errors and events. The NatSpec documentation MUST be added to the functions, structs, errors and events within the interface. This allows a reader to understand the functionality of a function before moving on to the implementation. The implementing functions MUST point to the NatSpec documentation in the interface using `@inheritdoc`. Internal and private functions shouldn't have NatSpec documentation except for `@dev` comments, whenever more context is needed. Additional comments within a function should only be used to give more context to more complex operations; otherwise, the code should be kept readable and self-explanatory. NatSpec comments in contracts should use a triple slash (`///`) to bring less noise to the implementation, while libraries and interfaces should use `/* */` wrappers.

The comments should respect the following rules:

- For read functions: `@notice Returns <...>`
- For write functions: `@notice <What it does, starts with verb>`
- For structs: `@notice <What it is>`
- For errors: `@notice Raised when <...>`
- For events: `@notice Emitted when <...>`

Each contract/library/interface should have a title comment that should follow such a structure:

1. `@title <Name>` (e.g., `Vault`)
2. `@notice Contract/Library/Interface for <...>.` - also, other variations are possible, e.g.:
    - `@notice Interface for the Vault contract.`
    - `@notice Base contract for <...>.`
    - `@notice Library-logic for <...>.`
3. `@dev <...>` (optional)

## Testing

The following testing practices should be followed when writing unit tests for new code. All functions, lines and branches should be tested to result in 100% testing coverage. Fuzz parameters and conditions whenever possible. Extremes should be tested in dedicated edge case and corner case tests. Invariants should be tested in dedicated invariant tests.

Differential testing should be used to compare assembly implementations with implementations in Solidity or testing alternative implementations against existing Solidity or non-Solidity code using ffi.

New features must be merged with associated tests. Bug fixes should have a corresponding test that fails without the bug fix.

### Best Practices

Best practices and naming conventions should be followed as outlined in the [Foundry Book](https://getfoundry.sh/forge/tests/overview).

### IR Compilation

All contracts and tests should be compilable without IR whenever possible.

### Gas Metering

Gas for function calls should be metered using the built-in `vm.snapshotGasLastCall` function in forge. To meter across multiple calls `vm.startSnapshotGas` and `vm.stopSnapshotGas` can be used. Tests that measure gas should be annotated with `/// forge-config: default.isolate = true` and not be fuzzed to ensure that the gas snapshot is accurate and consistent for CI verification. All external functions should have a gas snapshot test, and diverging paths within a function should have appropriate gas snapshot tests.
For more information on gas metering, see the [Forge cheatcodes reference](https://getfoundry.sh/reference/cheatcodes/gas-snapshots/#snapshotgas-cheatcodes).

### Bytecode Hash

Bytecode hash should be set to `none` in the `foundry.toml` file to ensure that the bytecode is consistent.

## Dependency Management

The preferred way to manage dependencies is using [`forge install`](https://book.getfoundry.sh/forge/dependencies). This ensures that your project uses the correct versions and structure for all external libraries. Also, `npm` and `soldeer` packages should be published to increase coverage of different use-cases.

## Releases

Every deployment and change made to contracts after deployment should be accompanied by a tag and release on GitHub.
````

## File: foundry.lock

```
{
  "circuits": {
    "rev": "338c1b1faadf409d9f9fb7f64e7e2d1df4da9d16"
  },
  "lib/forge-std": {
    "tag": {
      "name": "v1.11.0",
      "rev": "8e40513d678f392f398620b3ef2b418648b33e89"
    }
  }
}
```

## File: foundry.toml

```toml
[profile.default]
evm_version = "prague"
solc = "0.8.28"
optimizer = true
optimizer_runs = 200
via_ir = false
bytecode_hash = "none"
src = "src"
out = "out"
libs = ["lib"]
fs_permissions = [{ access = "read-write", path = "./"}]
gas_reports = ["*"]
gas_limit = "18446744073709551615"
dynamic_test_linking = true
ignored_warnings_from = ["test/","script/"]
ffi = true

[rpc_endpoints]
mainnet = "${ETH_RPC_URL}"
hoodi = "${ETH_RPC_URL_HOODI}"
holesky = "${ETH_RPC_URL_HOLESKY}"
sepolia = "${ETH_RPC_URL_SEPOLIA}"

[fmt]
bracket_spacing = false
int_types = "long"
line_length = 120
multiline_func_header = "attributes_first"
number_underscore = "thousands"
quote_style = "double"
tab_width = 4
single_line_statement_blocks = "preserve"
sort_imports = false
contract_new_lines = false
override_spacing = false
hex_underscore = "preserve"
wrap_comments = false

[lint]
lint_on_build = false
exclude_lints = ["asm-keccak256","mixed-case-function","mixed-case-variable","pascal-case-struct","screaming-snake-case-const"]
ignore = ["test/**/*.sol","script/**/*.sol","src/interfaces/**/*.sol"]

additional_compiler_profiles = [
  { name = "test", via_ir = false, optimizer = false },
]

compilation_restrictions = [
  { paths = "test/**", via_ir = false, optimizer = false },
]

[profile.default.fuzz]
runs = 1000
max_test_rejects = 262144

[profile.pr.fuzz]
runs = 10000
max_test_rejects = 262144

[profile.ci.fuzz]
runs = 100000
max_test_rejects = 262144

[profile.debug]
via_ir = false
optimizer_runs = 200
fuzz.runs = 100

# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
```

## File: generate_network.sh

```bash
#!/bin/bash

# Symbiotic Network Generator
# This script generates a Docker Compose setup for a local blockchain network
# with configurable number of operators, commiters, and aggregators

set -e

# Define the image tag for the relay service, that the current flight node is compatible with
RELAY_IMAGE_TAG="0.3.0"

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'

# Default values
DEFAULT_OPERATORS=4
DEFAULT_COMMITERS=1
DEFAULT_AGGREGATORS=1
MAX_OPERATORS=999


print_status() {
    echo -e "${GREEN}[INFO]${NC} $1"
}

print_warning() {
    echo -e "${YELLOW}[WARNING]${NC} $1"
}

print_error() {
    echo -e "${RED}[ERROR]${NC} $1"
}

print_header() {
    echo -e "${BLUE}================================${NC}"
    echo -e "${BLUE}$1${NC}"
    echo -e "${BLUE}================================${NC}"
}


validate_number() {
    local num=$1
    local name=$2
    if ! [[ "$num" =~ ^[0-9]+$ ]] || [ "$num" -lt 1 ]; then
        print_error "$name must be a positive integer"
        exit 1
    fi
}

get_user_input() {
    echo
    print_header "Symbiotic Network Configuration"
    echo

    read -p "Enter number of operators (default: $DEFAULT_OPERATORS, max: $MAX_OPERATORS): " operators
    operators=${operators:-$DEFAULT_OPERATORS}
    validate_number "$operators" "Number of operators"

    read -p "Enter number of commiters (default: $DEFAULT_COMMITERS): " commiters
    commiters=${commiters:-$DEFAULT_COMMITERS}
    validate_number "$commiters" "Number of commiters"

    read -p "Enter number of aggregators (default: $DEFAULT_AGGREGATORS): " aggregators
    aggregators=${aggregators:-$DEFAULT_AGGREGATORS}
    validate_number "$aggregators" "Number of aggregators"

    # Validate that commiters + aggregators <= operators
    total_special_roles=$((commiters + aggregators))
    if [ "$total_special_roles" -gt "$operators" ]; then
        print_error "Total commiters ($commiters) + aggregators ($aggregators) cannot exceed total operators ($operators)"
        exit 1
    fi

    if [ "$operators" -gt $MAX_OPERATORS ]; then
        print_error "Maximum $MAX_OPERATORS operators supported. Requested: $operators"
        exit 1
    fi

    print_status "Configuration:"
    print_status "  Operators: $operators"
    print_status "  Committers: $commiters"
    print_status "  Aggregators: $aggregators"
    print_status "  Regular signers: $((operators - total_special_roles))"
}

# Function to generate Docker Compose file
generate_docker_compose() {
    local operators=$1
    local commiters=$2
    local aggregators=$3

    local network_dir="temp-network"

    if [ -d "$network_dir" ]; then
        print_status "Cleaning up existing $network_dir directory..."
        rm -rf "$network_dir"
    fi

    mkdir -p "$network_dir/deploy-data"
    chmod 777 "$network_dir/deploy-data"

    # Create cache and broadcast directories with proper permissions
    print_status "Creating out, cache and broadcast directories..."
    mkdir -p "$network_dir/out" "$network_dir/cache" "$network_dir/broadcast"
    chmod 777 "$network_dir/out" "$network_dir/cache" "$network_dir/broadcast"

    local deploy_config_src="script/my-relay-deploy.toml"
    local deploy_config_dst="$network_dir/my-relay-deploy.toml"
    if [ ! -f "$deploy_config_src" ]; then
        print_error "Deployment config not found at $deploy_config_src"
        exit 1
    fi
    print_status "Copying deployment config to $deploy_config_dst"
    cp "$deploy_config_src" "$deploy_config_dst"

    for i in $(seq 1 $operators); do
        local storage_dir="$network_dir/data-$(printf "%02d" $i)"
        mkdir -p "$storage_dir"
        # Make sure the directory is writable
        chmod 777 "$storage_dir"
    done

    local anvil_port=8545
    local relay_start_port=8081
    local sum_start_port=9091

    cat > "$network_dir/docker-compose.yml" << EOF
services:
  # Main Anvil local Ethereum network (Chain ID: 31337)
  anvil:
    image: ghcr.io/foundry-rs/foundry:v1.4.4
    container_name: symbiotic-anvil
    entrypoint: ["anvil"]
    command: "--port 8545 --chain-id 31337 --auto-impersonate --slots-in-an-epoch 1 --accounts 10 --balance 10000 --gas-limit 30000000 --gas-price 10000000"
    environment:
      - ANVIL_IP_ADDR=0.0.0.0
    ports:
      - "8545:8545"
    networks:
      - symbiotic-network
    healthcheck:
      test: ["CMD", "cast", "client", "--rpc-url", "http://localhost:8545"]
      interval: 2s
      timeout: 1s
      retries: 10

  # Contract deployment service for main chain
  deployer:
    build:
      context: ..
      dockerfile: network-scripts/deployer.Dockerfile
    image: symbiotic-deployer
    container_name: symbiotic-deployer
    user: "1000:1000"
    volumes:
      - ../:/app
      - ./cache:/app/cache
      - ./broadcast:/app/broadcast
      - ./out:/app/out
      - ./deploy-data:/deploy-data
      - ./my-relay-deploy.toml:/my-relay-deploy.toml
    working_dir: /app
    command: ./network-scripts/deploy.sh
    depends_on:
      anvil:
        condition: service_healthy
    networks:
      - symbiotic-network
    environment:
      - OPERATOR_COUNT=$operators
      - NUM_AGGREGATORS=$aggregators
      - NUM_COMMITTERS=$commiters

  # Genesis generation service
  genesis-generator:
    image: symbioticfi/relay:$RELAY_IMAGE_TAG
    container_name: symbiotic-genesis-generator
    volumes:
      - ../:/workspace
      - ./deploy-data:/deploy-data
    working_dir: /workspace
    command: ./network-scripts/genesis-generator.sh
    depends_on:
      deployer:
        condition: service_completed_successfully
    networks:
      - symbiotic-network

  flights-api:
    build:
      context: ../off-chain
      dockerfile: Dockerfile
    container_name: symbiotic-flights-api
    entrypoint: ["/app/flights-api"]
    command: ["--listen", ":8085"]
    ports:
      - "8085:8085"
    depends_on:
      genesis-generator:
        condition: service_completed_successfully
    networks:
      - symbiotic-network

  ui:
    build:
      context: ../ui
      dockerfile: Dockerfile
    container_name: symbiotic-ui
    ports:
      - "5173:5173"
    depends_on:
      flights-api:
        condition: service_started
    networks:
      - symbiotic-network

EOF

    local committer_count=0
    local aggregator_count=0
    local signer_count=0

    # Calculate symb private key properly
    # ECDSA secp256k1 private keys must be 32 bytes (64 hex chars) and within range [1, n-1]
    # where n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
    BASE_PRIVATE_KEY=1000000000000000000
    SWARM_KEY=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140

    for i in $(seq 1 $operators); do
        local port=$((relay_start_port + i - 1))
        local storage_dir="data-$(printf "%02d" $i)"
        local key_index=$((i - 1))

        SYMB_PRIVATE_KEY_DECIMAL=$(($BASE_PRIVATE_KEY + $key_index))
        SYMB_SECONDARY_PRIVATE_KEY_DECIMAL=$(($BASE_PRIVATE_KEY + $key_index + 10000))
        SYMB_PRIVATE_KEY_HEX=$(printf "%064x" $SYMB_PRIVATE_KEY_DECIMAL)
        SYMB_SECONDARY_PRIVATE_KEY_HEX=$(printf "%064x" $SYMB_SECONDARY_PRIVATE_KEY_DECIMAL)

        # Validate ECDSA secp256k1 private key range (must be between 1 and n-1)
        # Maximum valid key: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140
        if [ $SYMB_PRIVATE_KEY_DECIMAL -eq 0 ]; then
            echo "ERROR: Generated private key is zero (invalid for ECDSA)"
            exit 1
        fi

        cat >> "$network_dir/docker-compose.yml" << EOF

  # Relay sidecar $i ($role_name)
  relay-sidecar-$i:
    image: symbioticfi/relay:$RELAY_IMAGE_TAG
    container_name: symbiotic-relay-$i
    command:
      - /workspace/network-scripts/sidecar-start.sh
      - symb/0/15/0x$SYMB_PRIVATE_KEY_HEX,symb/0/11/0x$SYMB_SECONDARY_PRIVATE_KEY_HEX,symb/1/0/0x$SYMB_PRIVATE_KEY_HEX,evm/1/31337/0x$SYMB_PRIVATE_KEY_HEX,p2p/1/0/$SWARM_KEY,p2p/1/1/$SYMB_PRIVATE_KEY_HEX
      - /app/$storage_dir
    ports:
      - "$port:8080"
    volumes:
      - ../:/workspace
      - ./$storage_dir:/app/$storage_dir
      - ./deploy-data:/deploy-data
    depends_on:
      genesis-generator:
        condition: service_completed_successfully
    networks:
      - symbiotic-network
    restart: unless-stopped

EOF

    local relay_port=$((relay_start_port + i - 1))
    local sum_port=$((sum_start_port + i - 1))

    cat >> "$network_dir/docker-compose.yml" << EOF

  flight-node-$i:
    build:
      context: ../off-chain
      dockerfile: Dockerfile
    container_name: symbiotic-flight-node-$i
    entrypoint: ["/workspace/network-scripts/flight-node-start.sh"]
    command: ["relay-sidecar-$i:8080", "$SYMB_PRIVATE_KEY_HEX"]
    environment:
      - FLIGHTS_API_URL=http://flights-api:8085
    volumes:
      - ../:/workspace
      - ./deploy-data:/deploy-data
    ports:
      - "$sum_port:8080"
    depends_on:
      relay-sidecar-$i:
        condition: service_started
    networks:
      - symbiotic-network
    restart: unless-stopped

EOF
    done

    cat >> "$network_dir/docker-compose.yml" << EOF

networks:
  symbiotic-network:
    driver: bridge

EOF
}


# Main execution
main() {
    print_header "Symbiotic Network Generator"

    # Check if required tools are available
    if ! command -v docker &> /dev/null; then
        print_error "Docker is not installed or not in PATH"
        exit 1
    fi

    if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then
        print_error "Docker Compose is not installed or not in PATH"
        exit 1
    fi

    get_user_input


    print_status "Generating Docker Compose configuration..."
    print_status "Creating $operators new operator accounts..."
    generate_docker_compose "$operators" "$commiters" "$aggregators"

    print_header "Setup Complete!"
    echo
    print_status "Files generated in temp-network/ directory:"
    echo "  - temp-network/docker-compose.yml"
    echo "  - temp-network/data-* (storage directories)"
    echo
    print_status "To start the network, run:"
    echo "  docker compose --project-directory temp-network up -d"
    echo
    print_status "To check the status, run:"
    echo "  docker compose --project-directory temp-network ps"
    echo
    print_status "To view logs, run:"
    echo "  docker compose --project-directory temp-network logs -f"
    echo
    print_warning "Note: The first startup may take several minutes(2-4mins) as it needs to:"
    echo "  1. Download Docker images"
    echo "  2. Build the flight-node image"
    echo "  3. Deploy contracts"
    echo "  4. Generate network genesis and fund operators"
    echo
}

main "$@"
```

## File: package.json

```json
{
    "name": "@symbioticfi/symbiotic-super-sum",
    "version": "1.0.0",
    "description": "Simple task-based network built with Symbiotic Relay",
    "homepage": "https://symbiotic.fi/symbiotic-super-sum",
    "bugs": "https://github.com/symbioticfi/symbiotic-super-sum/issues",
    "license": "MIT",
    "author": "Symbiotic Team",
    "files": ["examples/**/*", "src/**/*", "script/**/*", "test/mocks/**/*"],
    "repository": {
        "type": "git",
        "url": "https://github.com/symbioticfi/symbiotic-super-sum.git"
    },
    "keywords": ["solidity", "ethereum", "smart", "contracts", "security"],
    "dependencies": {
        "@openzeppelin/contracts": "5.4.0",
        "@openzeppelin/contracts-upgradeable": "5.4.0",
        "@symbioticfi/core": "1.0.3",
        "@symbioticfi/relay-contracts": "^1.0.0-rc.1",
        "@symbioticfi/network": "1.0.0-rc.1",
        "@symbioticfi/rewards": "2.0.0"
    }
}
```

## File: pnpm-workspace.yaml

```yaml
packages:
    - .
    - ui

onlyBuiltDependencies:
    - "@reown/appkit"
    - bufferutil
    - esbuild
    - keccak
    - utf-8-validate
```

## File: README.md

````markdown
# Symbiotic Flight Delay Insurance

[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/symbioticfi/symbiotic-flight-delays)

This repository hosts a full end-to-end example of a flight-delay insurance protocol built on top of Symbiotic Settlement:

- **Smart contracts** (Foundry) manage airlines, vault deployment, premium accounting, slashing, and claims via `FlightDelays.sol` and `VotingPowers.sol`.
- **Mock flights API** (Go) exposes airlines and their flights with status transitions so the rest of the stack can run locally without external data feeds.
- **Off-chain oracle node** (Go) polls the flights API, asks Settlement to sign flight events, and submits `createFlight`, `delayFlight`, and `departFlight` transactions.
- **React/wagmi UI** allows buyers to purchase/claim policies and liquidity providers to deposit into the automatically deployed Symbiotic vaults and claim rewards.

The original super-sum example has been entirely replaced by this flight-delay flow.

## Repository layout

```
├─ src/                       # Solidity contracts
├─ test/                      # Foundry tests
├─ off-chain/cmd/flights-api  # Mock airlines/flights HTTP server
├─ off-chain/cmd/node         # Off-chain oracle node
├─ off-chain/internal         # Shared Go packages
├─ ui/                        # Vite + React application for buyers/providers
└─ network-scripts/           # Helper scripts to spin up local Symbiotic networks (unchanged)
```

## Prerequisites

- Node.js 18+
- pnpm
- Go 1.21+
- Foundry (`forge`)
- Docker

## Installation

```bash
git clone --recurse-submodules https://github.com/symbioticfi/symbiotic-flight-delays.git
cd symbiotic-flight-delays
pnpm install
cd off-chain && go mod tidy
cd ../ui && pnpm install
cd ..
```

## Quick start

1. Generate the docker assets (choose operator counts when prompted):
    ```bash
    ./generate_network.sh
    ```
2. Boot the entire network (anvil, relays, Settlement, flights API, oracle node) in one command:
    ```bash
    docker compose --project-directory temp-network up -d
    ```
3. Inspect or tail logs as needed:
    ```bash
    docker compose --project-directory temp-network ps
    docker compose --project-directory temp-network logs -f flight-node
    ```

The compose stack now includes the mock flights API and the off-chain node, so flights are mirrored on-chain automatically without any extra processes.

## UI

The UI surfaces a compact list of flights plus basic provider controls (approve collateral, buy coverage, claim after delays, inspect vault balances, deposit/withdraw, claim rewards).

Available at `http://localhost:5173`

## Services

- **anvil** – local execution + settlement chain seeded by the deployer.
- **relay-sidecar-\* containers** – operators/aggregators registered with Symbiotic Relay.
- **flights-api** – exposes airline & flight endpoints at `http://localhost:8085` (seed data plus POST routes for scheduling/delay/depart events).
- **flight-node** – polls the flights API, requests Settlement signatures, and submits `createFlight` / `delayFlight` / `departFlight` transactions.
- **symbiotic-deployer / genesis-generator** – deploy contracts and derive genesis data before the rest of the stack comes online.

Stop and clean everything with:

```bash
docker compose --project-directory temp-network down -v
rm -rf temp-network
```

## Ports

| Service        | Default Port | Notes                                                                         |
| -------------- | ------------ | ----------------------------------------------------------------------------- |
| UI (Vite dev)  | 5173         | `pnpm --filter ui dev`; `pnpm preview` serves on 4173 by default.             |
| Flights API    | 8085         | Mock airline/flight data plus POST endpoints for create/delay/depart actions. |
| Off-chain node | 8080         | Relay-sidecar instances listen on 8080 inside the compose network.            |
| Anvil (L1/L2)  | 8545         | JSON-RPC endpoint used by contracts, oracle node, and the UI.                 |

## API Endpoints

All endpoints below are served by the mock Flights API (`http://localhost:8085` unless overridden):

- `GET /healthz` – readiness probe.
- `GET /airlines` – lists all airlines and their current metadata.
- `POST /airlines` – create an airline (`{ "airlineId": "...", "name": "...", "code": "ALP" }`).
- `GET /airlines/{airlineId}/flights` – list flights for an airline.
- `POST /airlines/{airlineId}/flights` – create/schedule a new flight (`flightId`, `departureTimestamp`).
- `POST /airlines/{airlineId}/flights/{flightId}/delay` – mark a flight as delayed.
- `POST /airlines/{airlineId}/flights/{flightId}/depart` – mark a flight as departed.

## Local Deployments

http://anvil:8545:

- `ValSetDriver`: 0x43C27243F96591892976FFf886511807B65a33d5
- `FlightDelays`: 0xA4b0f5eb09891c1538494c4989Eea0203b1153b1
- `VotingPowerProvider`: 0x369c72C823A4Fc8d2A3A5C3B15082fb34A342878
- `KeyRegistry`: 0xe1557A820E1f50dC962c3392b875Fe0449eb184F
- `Settlement`: 0x882B9439598239d9626164f7578F812Ef324F5Cb
- `Network`: 0xfdc4b2cA12dD7b1463CC01D8022a49BDcf5cFa24
````

## File: remappings.txt

```
node_modules/@symbioticfi/relay-contracts:forge-std/=lib/forge-std/src/
@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/
@openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/
@symbioticfi/relay-contracts/=node_modules/@symbioticfi/relay-contracts/
@symbioticfi/network/=node_modules/@symbioticfi/network/
@symbioticfi/core/=node_modules/@symbioticfi/core/
@symbioticfi/rewards/=node_modules/@symbioticfi/rewards/
```
