# Tempo Zone RPC access control

A Tempo Zone's RPC is an authenticated Ethereum JSON-RPC interface for reading account data and submitting transactions. Every request includes an authorization token that proves the caller controls a Tempo account. The RPC scopes account data to that account and redacts shared data, such as block headers, to protect other accounts' activity.

## Authorization Tokens

Authorization tokens are short-lived credentials (maximum 30 days, or a shorter operator-configured limit) signed by the caller's Tempo account key. Tempo accounts support multiple signature types (secp256k1, P256, WebAuthn), and accounts with Access Keys via the `AccountKeychain` precompile can use those keys to authenticate.

The signed message includes:

* `"TempoZoneRPC"` magic prefix for domain separation
* Spec version, zone ID, and chain ID for replay protection (zone ID `0` skips the zone-ID check, but the chain ID must still match)
* Issuance and expiry timestamps

Tokens are sent via the `X-Authorization-Token` HTTP header on every request. WebSocket clients supply the token when opening the connection; subscriptions remain scoped to that account.

## Method Access Control

The redacted RPC exposes an explicit method allowlist. Authenticating with a sequencer key does not bypass its privacy rules; operator methods belong on the separate operator RPC.

Available to any authenticated caller:

| Method | Access Type | Notes |
|--------|-------------|-------|
| `eth_chainId` | Allowed | Zone chain ID |
| `eth_blockNumber` | Allowed | Latest block number |
| `eth_gasPrice` | Allowed | Current gas price |
| `eth_maxPriorityFeePerGas` | Allowed | Current priority fee |
| `eth_feeHistory` | Allowed | Redacted fee history with fixed base fee, zero rewards, and zero gas-usage ratios |
| `eth_getBlockByNumber` | Allowed | Block headers **without transaction details** |
| `eth_getBlockByHash` | Allowed | Block headers **without transaction details** |
| `eth_subscribe("newHeads")` | Allowed | Redacted block headers (see [Block Responses](#block-responses)) |
| `eth_syncing` | Allowed | Sync status |
| `eth_coinbase` | Allowed | Sequencer address |
| `net_version` | Allowed | Network ID |
| `net_listening` | Allowed | Node status |
| `web3_clientVersion` | Allowed | Client version |
| `web3_sha3` | Allowed | Pure Keccak-256 hash |
| `eth_getBalance` | Scoped | Returns balance for the authenticated account only. Queries for other accounts return `0x0`. |
| `eth_getTransactionCount` | Scoped | Returns nonce for the authenticated account only. Other accounts return `0x0`. |
| `eth_call` | Scoped | Executes with `from` set to the authenticated account. [Execution-level privacy](https://tempo.xyz/developers/docs/protocol/zones/accounts) enforces `balanceOf` access control at the contract level. |
| `eth_estimateGas` | Scoped | Defaults an omitted `from` to the authenticated account; rejects a mismatch and state overrides. |
| `eth_fillTransaction` | Scoped | Prepares a transaction for the authenticated account using deterministic fee defaults. |
| `eth_getTransactionByHash` | Scoped | Returns the transaction only if the authenticated account is the sender. Returns `null` otherwise. |
| `eth_getTransactionReceipt` | Scoped | Returns the receipt only if the authenticated account is the sender. Logs are filtered (see [Event Filtering](#event-filtering)). |
| `eth_sendRawTransaction` | Scoped | Validates that the transaction sender matches the authenticated account. |
| `eth_sendRawTransactionSync` | Scoped | Same sender check; returns a filtered receipt after inclusion. |
| `eth_getLogs` | Scoped | Filtered to TIP-20 events where the authenticated account is a relevant party (see [Event Filtering](#event-filtering)). |
| `eth_getFilterLogs` | Scoped | Same filtering as `eth_getLogs`. |
| `eth_getFilterChanges` | Scoped | Same filtering. Only returns new events since last poll. |
| `eth_newFilter` | Scoped | Creates an account-owned filter; the supplied topics must include the authenticated account. |
| `eth_subscribe("logs")` | Scoped | Subscription scoped to the authenticated account. |
| `eth_newBlockFilter` | Scoped | Returns new block hashes. |
| `eth_uninstallFilter` | Scoped | Removes a previously created filter. |

**Error vs. silent response**: Methods where the user explicitly provides a mismatched parameter (`eth_sendRawTransaction` with wrong sender, `eth_call` with wrong `from`) return explicit errors, since the user already knows the address they supplied and the error leaks nothing. Methods that query *about* other accounts return silent dummy values (`0x0`, `null`, empty results) instead of errors; an error would reveal "this data exists but you can't see it."

<span id="restricted-sequencer-only" />

### Restricted and unavailable methods

`eth_getBlockByNumber` and `eth_getBlockByHash` with `full=true` return `-32005` on the redacted RPC, including for sequencer accounts.

Raw storage and code reads, access-list creation, transaction-count and transaction-by-index block methods, `eth_getProof`, pending-transaction filters, mining methods, and the `debug_*`, `admin_*`, and `txpool_*` namespaces are not exposed here. They return `-32601` (method not found). Operators use a separate RPC endpoint.

<span id="disabled" />

### Disabled subscriptions

`eth_subscribe("newPendingTransactions")`, `eth_subscribe("syncing")`, and transaction-receipt subscriptions return `-32006` (method disabled). The supported subscriptions are `newHeads` and account-scoped `logs`.

## Timing side channels

The current implementation does not enforce a fixed minimum response time. Silent `null` or zero responses prevent direct disclosure through result values, but they do not guarantee indistinguishable timings. Log filters must include the caller in an eligible indexed topic before retrieval, which limits the scope of backend queries.

## Block Responses

All block headers returned through the redacted RPC are sanitized:

* `transactions` is always an empty array.
* `logsBloom`, `stateRoot`, `transactionsRoot`, and `receiptsRoot` are zeroed.
* `gasUsed` and block size are zeroed, and `extraData` is empty. Optional blob-gas fields and the withdrawals root are also zeroed when present.
* Public identifiers such as `number` and `hash` remain available.

Transaction and receipt ordering fields are also redacted. Receipt logs include only visible events, `transactionIndex` is zero, and `cumulativeGasUsed` is replaced with that transaction’s gas usage.

## Event Filtering

Log queries are restricted to enabled zone tokens and the receive-policy guard. Filters must include the authenticated address, padded to 32 bytes, in `topics[1]` or `topics[2]` as appropriate for the event. Broad filters without an eligible caller topic return `-32602`. To find both outgoing and incoming transfers, query each indexed position separately.

Returned events are restricted to those where the authenticated account is a relevant party:

| Event | Visible if |
|-------|-----------|
| `Transfer` | `from == caller` OR `to == caller` |
| `Approval` | `owner == caller` OR `spender == caller` |
| `TransferWithMemo` | `from == caller` OR `to == caller` |
| `Mint` | `to == caller` |
| `Burn` | `from == caller` |
| `TransferBlocked` | Caller is the receiver, originator, or recovery authority in the claim receipt. Log queries require the indexed receiver topic; transaction receipts can also expose the event to the other eligible parties. |

All other event topics (system events, role events, configuration events) are filtered out. Visible `logIndex` values restart at zero for each transaction, hiding the number of preceding private events.

## Zone-Specific RPC Methods

| Method | Access | Description |
|--------|--------|-------------|
| `zone_getAuthorizationTokenInfo` | Any authenticated | Returns the authenticated account address and token expiry |
| `zone_getZoneInfo` | Any authenticated | Returns `zoneId`, `zoneTokens`, `sequencers`, `chainId`, `tempoBlockNumber`, `isAccessEnforced`, and `isGatewayOpen` |
| `zone_getEncryptionKey` | Any authenticated | Returns the portal’s active encryption key from finalized Tempo state |

`zone_getDepositStatus` is not implemented. For settlement progress, compare a deposit’s number from `DepositMade` with `ZonePortal.lastProcessedDepositNumber()` on Tempo.

## Error codes

Authentication fails at the HTTP transport layer: a missing token returns HTTP `401`, an invalid or expired token returns HTTP `403`, and an internal authentication failure returns HTTP `500`. These responses have no JSON-RPC error body. Authenticated requests can return:

| Code | Message | Meaning |
|------|---------|---------|
| `-32003` | Transaction rejected | Transaction sender does not match authenticated account |
| `-32004` | Account mismatch | The `from` field does not match the authenticated account |
| `-32005` | Sequencer only | Full-block request rejected on the redacted RPC |
| `-32006` | Method disabled | Method is not available on zones |
| `-32601` | Method not found | Method is outside the redacted RPC allowlist |
| `-32602` | Invalid params | Unsupported filter, state override, or other invalid argument |
