> ## Documentation Index
> Fetch the complete documentation index at: https://docs.giwa.tokscale.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# UsageReceiptV1

> The signed off-chain statement the gateway issues per request: its EIP-712 typed data, evolution rule, and four-step provability chain.

`UsageReceiptV1` is the narrow signed statement the gateway issues after it observes provider usage and determines the immutable pricing snapshot that applies to that request. It is not an x402 payment receipt, not a wallet attestation, and not an on-chain transaction. It is a gateway assertion: the operator signs "we saw this usage, at this price, for this request," and anyone holding the payload can verify the signature with no Tokscale account and no RPC call.

## Canonical typed data

The EIP-712 domain is exactly:

```text theme={null}
name:    Tokscale Gateway
version: 1
chainId: 91342
```

The primary type is exactly:

```text theme={null}
UsageReceiptV1(
  bytes16 requestId,
  bytes16 projectId,
  string provider,
  string model,
  uint64 inputTokens,
  uint64 outputTokens,
  uint64 cacheReadTokens,
  uint64 cacheWriteTokens,
  uint256 costUsd,
  bytes32 pricingSnapshotHash,
  bytes32 providerRequestIdHash,
  uint64 settledAt,
  uint16 receiptSchemaVersion
)
```

Two field rules matter for verifiers:

* `costUsd` is an unsigned integer in 1e-4 USD units: `18420` represents `$1.8420`. The gateway computes that exact value before signing. The receipt never carries a floating-point number.
* `settledAt` is Unix seconds from the immutable terminal outcome. The vector fixtures use `1700000000` only as a stable synthetic timestamp (the same value localnet pins as its genesis time; see [Network Separation](/gateway/network-separation)).

What V1 omits: payer, payment identifier, payment scheme, transaction hash, direct wallet address, prompt, completion, and the raw provider request ID. Payment provenance lives in the credit and settlement records (see [x402 Stage A](/gateway/x402-stage-a) and [x402 Stage B](/gateway/x402-stage-b)). The provider request ID appears only as a privacy-preserving `bytes32` commitment. A builder's wallet is bound outside the receipt, in the attestation leaf (see [Receipt Attestation](/gateway/receipt-attestation)).

## The issued envelope

The typed data above is what gets signed. What `GET /v1/receipts/:request_id` returns is the issued envelope, which carries four fields beyond the signed ones:

* `digest`: the EIP-712 hash of the typed data, as lowercase `bytes32`. A verifier recomputes it and rejects the envelope if it does not match.
* `signature`: 65 bytes of ECDSA. It must be canonical low-S, and a high-S or malformed value is rejected before any recovery is attempted.
* `signerVersion`: the registered key generation that issued the receipt. It is a positive `uint16`, and it is a separate field from `receiptSchemaVersion`, which is fixed at `1` for V1.
* `signerAddress`: the lowercase address the signature must recover to.

Verification is a registry lookup keyed first by `signerVersion` and then by `signerAddress`, so the issuer key can rotate without touching the frozen V1 schema. The published vector fixture's Anvil signer sits at `signerVersion` 2, which is exactly why the production registry is versioned and excludes it.

## The contract-less domain

There is intentionally no `verifyingContract` in the domain. V1 is an off-chain gateway assertion, not a contract call. A verifying contract would change the EIP-712 domain separator and therefore the digest of every retained signature, so contract verification belongs in a separately versioned `UsageReceiptV2`, never in a V1 edit.

The evolution rule is strict: field order, Solidity types, field semantics, domain name, domain version, and chain ID are fixed for V1. Any new field, changed meaning, payment binding, verifying contract, or altered decimal scale creates `UsageReceiptV2` with a new domain version and a new persisted receipt schema version. A verifier selects the exact type from the persisted version and never reinterprets a V1 signature as V2. V1's ECDSA recovery helper covers EOA signers only; a future contract-wallet signer requires an explicit ERC-1271 path.

## The V1 wire identifiers

Three strings are hashed into every receipt and every proof built from it:

* EIP-712 domain name: `Tokscale Gateway`
* Merkle leaf domain: `TOKSCALE_RECEIPT_LEAF_V1`
* Merkle node domain: `TOKSCALE_RECEIPT_NODE_V1`

Capitalization is part of the preimage. `Tokscale Gateway` is not interchangeable with any other casing of the same words, and the two merkle separators are upper-case in full. A verifier that normalizes case derives a different digest and rejects valid receipts.

These replaced an earlier set of identifiers in a deliberate hard cutover rather than a versioned migration. Because the domain name is an EIP-712 input, every receipt signed under the previous name now derives a different digest and no longer validates. Those receipts were not migrated and cannot be re-signed, and `receiptSchemaVersion` did not change, so nothing inside a receipt distinguishes the two generations: the earlier evidence was reset, not dual-verified. Any further change to these three strings is the same breaking event and must ship as a separately versioned schema instead.

## The two-layer design: why receipts are cheap

Per-request on-chain writes would scale cost linearly with traffic (1M requests per day is hundreds of dollars of gas daily even on an L2) and would put a chain write inside the request path. Tokscale splits evidence into two layers:

| Layer                   | What happens                                                                       | Cost                                          |
| ----------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------- |
| Per request (off-chain) | The gateway signs one `UsageReceiptV1`. Instantly verifiable by `ecrecover`.       | Zero gas, zero added latency                  |
| Per epoch (on-chain)    | One Merkle root over the epoch's receipts is anchored in a single EAS attestation. | One transaction per builder subject per epoch |

The signature proves issuance and integrity immediately; the epoch root proves non-retroactive tampering after anchoring. 1M or 100M requests. Same on-chain cost.

## The provability chain

Any single receipt is provable down a four-step chain:

```text theme={null}
receipt -> receiptRoot -> leaf -> Usage Root
```

1. **receipt**: the EIP-712 signed `UsageReceiptV1`, verified against the versioned signer registry.
2. **receiptRoot**: the ordered Merkle root over one builder subject's receipts in one epoch. Leaves are `keccak256(abi.encode(leafDomain, receiptDigest, subjectWallet, subjectEpoch))` under `leafDomain = keccak256("TOKSCALE_RECEIPT_LEAF_V1")`; interior nodes are `keccak256(abi.encode(nodeDomain, left, right))` under `nodeDomain = keccak256("TOKSCALE_RECEIPT_NODE_V1")`. Ordering is ascending by raw RFC 4122 UUID bytes, pairs are never sorted, and an odd terminal node is promoted unchanged.
3. **leaf**: the wallet-bound membership leaf commits the receipt digest to one builder wallet and one subject epoch, so a receipt cannot move between passports.
4. **Usage Root**: the EAS attestation carrying that `receiptRoot` on GIWA.

A holder presents the receipt payload and its Merkle path; the verifier recomputes every step client-side.

## One vector set, three languages

The published fixtures lock the field order, domain, digest, ECDSA signature, and recovered signer as fixed public test data. Three independent implementations must reproduce identical bytes:

* **Rust** (Alloy): `cargo test -p giwa-onchain --test receipt_vectors`
* **TypeScript** (viem): the web vector runner over the same JSON
* **Solidity** (forge): `ReceiptAttestationV1Test`, which recomputes the receipt digest, leaf, root, and summary commitment on-chain

The receipt vector is verified by the Rust and TypeScript runners; the attestation vector suite (which includes the receipt digest) adds Solidity as the third implementation:

```sh theme={null}
bun run receipt-vectors:verify
bun run attestation-vectors:verify
```

`receipt-vectors:verify` exits non-zero on a digest or signature mismatch between the Rust and TypeScript runners. The leaf, root, and summary-commitment checks, and the Solidity implementation, belong to `attestation-vectors:verify`.

<Warning>
  `usage-receipt-v1.json` contains a development-only Anvil private key. It is fixed public test data, not an operational secret, and must never appear in runtime configuration.
</Warning>

## Next steps

* To verify a receipt end to end yourself, see [Verifying a Receipt](/builder-passport/verify-a-receipt).
* For the EAS attestation that carries the `receiptRoot` on-chain, see [Receipt Attestation](/gateway/receipt-attestation).
* For the worker that signs receipts and the one that publishes roots, see [The Gateway and Its Workers](/gateway/service).
* For why the domain's chain ID cannot tell localnet from Sepolia, see [Network Separation](/gateway/network-separation).
