OpenTela
Tutorial

Solana Settlement

Experimental

Configure and operate automated usage billing and SPL token settlement between consumers and providers.

OpenTela can automatically settle usage bills between consumers and providers using the OTELA SPL token on Solana. When a head node dispatches requests to worker nodes, both sides independently track usage metrics (tokens generated, GPU milliseconds consumed, etc.). Once usage crosses a configurable threshold, the head node reconciles the measurements, calculates the amount owed, and submits an SPL token transfer on-chain.

This document explains how to configure and operate the settlement system.

Overview

The settlement flow works as follows:

  1. A consumer sends requests through a head node to one or more provider worker nodes.
  2. Both the head node and worker node independently extract usage metrics from HTTP response headers (X-Usage-Tokens, X-Usage-GPU-Ms, etc.).
  3. Usage records accumulate locally until a threshold is reached (value or time).
  4. The head node reconciles its measurements against the worker's via CRDT-shared aggregates.
  5. If both sides agree (within a configurable tolerance), the head node looks up the provider's rate, calculates the payment, and submits an SPL token transfer to the provider's wallet.
  6. The transaction is confirmed on-chain, and a payment record is stored in the CRDT to prevent duplicate billing.
Consumer Wallet ──(OTELA tokens)──> Provider Wallet
        ↑                              ↑
        │                              │
    Head Node (Payment Processor)

    ┌───┴────┐
    │ 1. Aggregate Usage
    │ 2. Reconcile (head vs worker)
    │ 3. Lookup Provider Rate
    │ 4. Calculate Amount
    │ 5. Build SPL Transfer Tx
    │ 6. Sign with Consumer Key
    │ 7. Submit to Solana
    └──────────────────────────

Prerequisites

  • OpenTela binary installed (see Installation)
  • A Solana wallet with OTELA tokens (see Wallet & Ownership)
  • The provider (worker node) must also have a Solana wallet
  • Access to a Solana RPC endpoint (mainnet, devnet, or testnet)

Step 1: Enable billing

Edit ~/.config/opentela/cfg.yaml on the head node to enable the billing system:

billing:
  enabled: true
  value_threshold: 10000000    # trigger settlement after this many usage units
  max_interval_minutes: 60     # or after this many minutes, whichever comes first
  dispute_threshold_pct: 10    # flag if head/worker measurements differ by >10%

You can also enable billing via environment variable:

export OF_BILLING_ENABLED=true
Config keyEnv varDefaultDescription
billing.enabledOF_BILLING_ENABLEDfalseEnable the settlement system
billing.value_thresholdOF_BILLING_VALUE_THRESHOLD10000000Usage value that triggers a settlement
billing.max_interval_minutesOF_BILLING_MAX_INTERVAL_MINUTES60Maximum time between settlements
billing.dispute_threshold_pctOF_BILLING_DISPUTE_THRESHOLD_PCT10Percentage difference that flags a dispute

Step 2: Configure the OTELA token and Solana RPC

The settlement system uses the SPL token mint configured under solana.mint. The default points to the OTELA token:

solana:
  rpc: "https://api.mainnet-beta.solana.com"   # or https://api.devnet.solana.com for testing
  mint: "EsmcTrdLkFqV3mv4CjLF3AmCx132ixfFSYYRWD78cDzR"  # OTELA SPL token mint
  skip_verification: false

For testing, use the devnet endpoint. For production, use a mainnet-beta endpoint or a dedicated RPC provider.

Step 4: Set up provider rates

Providers set their own prices per service and metric. Rates are loaded from a YAML file:

# ~/.config/opentela/rates.yaml (or wherever rates.config_path points)
providers:
  - address: "ProviderSolanaAddress1"
    services:
      - name: "llm"
        metrics:
          - name: "tokens"
            price_per_1000: 1000    # 1000 base units per 1000 tokens
          - name: "gpu_ms"
            price_per_1000: 500     # 500 base units per 1000 ms of GPU time

Point the config to this file:

rates:
  default_per_1000_tokens: 1000  # fallback if provider has no explicit rate
  default_per_gpu_ms: 1          # fallback for GPU time
  config_path: "/home/user/.config/opentela/rates.yaml"

If a provider has no explicit rate configured, the system falls back to the default_per_1000_tokens or default_per_gpu_ms values.

Step 5: Fund the consumer wallet

The head node's wallet (the consumer) needs OTELA tokens to pay providers. Check the balance:

./otela wallet balance
Wallet: 5YNmS1R9nNSCDzb5a7mMJ1dwK9uHeAAF4CerBTpfXoA4
RPC:    https://api.devnet.solana.com

  SOL balance: 1.500000000 SOL
  Token (EsmcTrdLkFqV3mv4CjLF3AmCx132ixfFSYYRWD78cDzR): 50000 (50000.000000000)

The consumer wallet also needs a small amount of SOL for transaction fees (~0.000005 SOL per transfer) and potentially for creating Associated Token Accounts (~0.002 SOL per new provider).

For devnet testing, you can request SOL from the faucet:

./otela wallet airdrop --solana.rpc https://api.devnet.solana.com 2

Step 6: Start the nodes

Start the head node with billing enabled:

./otela start --mode standalone --public-addr {YOUR_IP} --seed 0

Start worker nodes as usual (see Spin Up). The billing system operates transparently — worker nodes do not need any special configuration beyond having a wallet.

Once requests flow through the network, you will see settlement logs on the head node:

INFO  settlement: payment confirmed sig=3Kf8x... amount=5000 to=ProviderAddress...

How usage tracking works

Services report usage through HTTP response headers. When a worker node proxies a request to a local service (e.g., vLLM), OpenTela reads the response headers:

X-Usage-Tokens: 1234
X-Usage-GPU-Ms: 5000

Both the head node (which receives the proxied response) and the worker node (which made the local request) independently extract and store these metrics. This dual-attestation design ensures neither side can unilaterally inflate or deflate usage.

Aggregation

Usage records accumulate in local buckets keyed by (peer_id, service, metric_name). A settlement is triggered when either condition is met:

  • The accumulated value exceeds billing.value_threshold
  • The time since the last settlement exceeds billing.max_interval_minutes

Reconciliation

Before payment, the head node's aggregate is compared with the worker node's aggregate (shared via CRDT). If the measurements differ by more than billing.dispute_threshold_pct, the record is flagged as disputed and no payment is made. Disputed records are logged for manual review.

If the difference is within tolerance, the resolved value is the average of both measurements.


Error handling

The settlement system handles common failure scenarios:

ScenarioBehavior
Insufficient OTELA balancePre-flight check fails, no transactions submitted
Provider has no token accountATA created automatically (costs ~0.002 SOL)
Transaction fails on-chainRetried up to 3 times with exponential backoff
Transaction not confirmedPolls for up to 60 seconds before timing out
Disputed usage (>threshold)Skipped, logged for review
Provider rate not foundFalls back to default platform rate

Idempotency

Payment records are stored by transaction signature in the CRDT under /billing/payments/{signature}. Before submitting a new payment, the system checks for existing records to prevent double-billing.


Testing on devnet

For end-to-end testing without real funds:

  1. Point solana.rpc to https://api.devnet.solana.com
  2. Request SOL from the faucet: ./otela wallet airdrop 2
  3. Deploy or use an existing test SPL token on devnet
  4. Update solana.mint to the test token's mint
  5. Start the cluster and send requests — settlements will appear in the logs

Full configuration reference

# Billing and settlement
billing:
  enabled: true
  value_threshold: 10000000
  max_interval_minutes: 60
  dispute_threshold_pct: 10

# Solana connection and token
solana:
  rpc: "https://api.devnet.solana.com"
  mint: "EsmcTrdLkFqV3mv4CjLF3AmCx132ixfFSYYRWD78cDzR"
  skip_verification: false

# Provider rates
rates:
  default_per_1000_tokens: 1000
  default_per_gpu_ms: 1
  config_path: "/etc/opentela/rates.yaml"

On this page