---
name: tangle-router-crypto
description: Pay for Tangle Router inference with crypto instead of a platform account — deposit stablecoins once into ShieldedCredits, then make OpenAI-compatible calls authorized by EIP-712 SpendAuth signatures (one per session, not per request), metered and settled on-chain.
---

# Tangle Router without a platform account (crypto lane)

Use this when you (the agent) control an Ethereum-style private key and want to pay for
inference directly on-chain instead of asking a human for a platform key. If your owner
can approve a platform key, prefer `agent-setup.md` — it is simpler.

Status: live on Tempo testnet (chainId 42431) rehearsal. Mainnet: pending launch.
All addresses below are the Tempo rehearsal deployment.

## Concepts in one paragraph

You deposit stablecoins into the `ShieldedCredits` contract under a `commitment`
(a bytes32 you choose; `keccak256` of a random secret is fine). The contract maps
`commitment → { spendingKey, balance, nonce }`, where `spendingKey` is an address YOU
designate at funding time. To spend, you sign an EIP-712 `SpendAuthorization`
("operator X may charge up to `amount` against my commitment before `expiry`") with the
spending key and send it as the `x-payment-signature` header. The router opens a
**session** (one on-chain tx), serves many requests against the session's remaining cap,
meters every token, and settles the session on-chain once when it goes idle — charging
exactly what was served and releasing the rest. Failed requests charge zero.

## Recipe

1. Fund your EOA with the chain's gas token + the stablecoin you will deposit.
2. Choose a random 32-byte secret; `commitment = keccak256(secret)`. Keep the secret
   safe — anyone with it can withdraw the account (`withdrawCredits`).
3. `approve` the stablecoin to ShieldedCredits, then call
   `fundCredits(token, amount, commitment, spendingKeyAddress)`.
4. Read the account: `getAccount(commitment)` → `(spendingKey, token, balance,
   totalFunded, totalSpent, nonce)`. **Read `nonce` fresh before every new session.**
5. Sign the session authorization (EIP-712):
   - domain: `{ name: "ShieldedCredits", version: "1", chainId, verifyingContract: <ShieldedCredits> }`
   - type: `SpendAuthorization(bytes32 commitment,uint64 serviceId,uint8 jobIndex,uint256 amount,address operator,uint256 nonce,uint64 expiry)`
   - values: your commitment, serviceId `1`, jobIndex `0`, amount = session cap in token
     base units, operator = the router's settlement EOA (see "Operator address" below),
     nonce = the fresh account nonce, expiry = unix seconds, at most ~1h out.
6. Call the router exactly like OpenAI, with one extra header:
   ```
   POST {router}/api/chat            (or /v1/chat/completions)
   x-payment-signature: {"commitment":"0x…","serviceId":"1","jobIndex":0,"amount":"30000","operator":"0x…","nonce":"7","expiry":"1791…","signature":"0x…"}
   {"model":"<id>","messages":[…],"stream":true,"max_tokens":512,"stream_options":{"include_usage":true}}
   ```
   Reuse the SAME header for every request in the session. When the cap is exhausted the
   router answers HTTP `402` with `shielded_session_cap_exhausted`: increment nothing, re-read the
   nonce, sign a new auth, continue. The router settles the old session for you. Replaying a closed or settled session returns HTTP `401`.
7. To recover unused balance after all sessions settle: `withdrawCredits` via a ZK proof
   (see `shielded-payment-gateway` repo, sdk/shielded-sdk) — or leave it; it never expires
   on-chain.

## Rules that will bite you if skipped

- `stream: true` for anything non-trivial; reasoning models (e.g. `huihui-qwen3.8-27b-abliterated`)
  need `max_tokens >= 512` because reasoning consumes the budget — the router returns a typed
  `reasoning_budget_exhausted` error (not a charge) when the budget is all reasoning.
- The `operator` field must be the router's settlement EOA for your session's shard. On the
  Tempo rehearsal the pool is 4 EOAs assigned by `keccak256(commitment) mod 4`; the canonical
  list is published in the gateway deployment docs. (This shard dance goes away at mainnet
  launch; ask the router's `/docs` for "shielded" at that time.)
- A SpendAuth is bearer money up to `amount` until `expiry`: keep amounts small and expiries
  short. Never put one in logs or shared memory.
- A session settles ~10 min after its last request (or at expiry margin). Until then the cap
  is escrowed on-chain; after settle, `totalSpent` increases by exactly the metered cost.

## Verifying you were charged correctly

`getAccount(commitment)` before and after a settled session: `totalSpent` delta equals the
metered cost and nothing else. Every response also carries `x-generation-id` — the router's
ledger key for the request — and on the platform-billed lane the same id appears in the
billing deduct reference.

## Reference implementation

`phase1-proof.mts` in the staging soak rig built exactly this flow with `viem` (~150 lines,
no dependencies beyond viem). The production-grade path is `@tangle-network/shielded-sdk`
in `tangle-network/shielded-payment-gateway`.
