Relay.create
Overview
Create a Tempo relay with a parsed RPC handler and a Fetch endpoint. Both methods share the same ordered plugins. Requests that plugins do not handle are forwarded to a Viem client.
Choose a chain-configured client for one chain or getClient for multiple
chains. Exactly one is required. A relay with no plugins forwards requests without
adding multisig coordination or fee sponsorship.
Recipes
Serve One Chain
import { createClient, http } from 'viem'
import { tempo } from 'viem/chains'
import { Relay } from 'viem/tempo'
const relay = Relay.create({
client: createClient({ chain: tempo, transport: http() }),
})
export default { fetch: relay.fetch }Resolve Multiple Chains
import { createClientResolver, http } from 'viem'
import { tempo, tempoModerato } from 'viem/chains'
import { Relay } from 'viem/tempo'
const resolver = createClientResolver({
chains: [tempo, tempoModerato],
transport: () => http(),
})
const relay = Relay.create({ getClient: resolver.getClient })
const chainId = await relay.request(
{ method: 'eth_chainId' },
{ chainId: tempo.id },
)A plugin may infer the chain from a signed payload or stored operation. Ordinary
calls such as eth_blockNumber need an explicit chain when using getClient.
The relay never selects the resolver's first chain as a default.
Relay.create
Creates handlers sharing one plugin pipeline.
Usage
import { createClient, Relay } from 'viem/tempo'
const client = createClient()
const relay = Relay.create({ client })Parameters
options.client
- Type: Chain-configured Viem
Client
Supplies the default chain. Explicit and inferred chain IDs must match
client.chain.id. Cannot be combined with getClient.
const relay = Relay.create({ client }) options.getClient
- Type:
({ chainId }) => Client
Resolves a client only when forwarding requires one. Accepts
createClientResolver().getClient directly and preserves
its supported chain IDs. Cannot be combined with client.
const relay = Relay.create({ getClient: resolver.getClient }) options.plugins
- Type:
readonly Relay.Plugin[] - Default:
[]
Requests enter plugins in array order. Plugins are composed once per relay.
const relay = Relay.create({
client,
plugins: [Relay.multisig({ store })],
})options.resolveTokens
- Type:
(chainId: number) => readonly Address[] | Promise<readonly Address[]> - Default: Bundled Tempo token addresses for the selected chain.
Supplies fee-token candidates shared by fee selection and sponsorship. Results are memoized within each request and chain. Implement cross-request caching inside the resolver when fetching a remote token list.
const relay = Relay.create({
client,
resolveTokens: () => [Addresses.pathUsd],
plugins: [Relay.feeToken()],
})options.timeout
- Type:
number - Default:
10_000
Deadline in milliseconds for a plugin-handled fill, including token resolution, validation, signing, and enrichment. Expiry aborts downstream requests and rejects the fill.
Custom callbacks must observe cancellation themselves to stop their own work. Raw transaction submission and multisig polling use their existing timeouts.
The timeout must be a positive integer no greater than 2_147_483_647 milliseconds. Exceeding the deadline rejects the fill with RPC error code -32005. Transport retries run within the same deadline.
import { createClient, Relay } from 'viem/tempo'
const relay = Relay.create({
client: createClient(),
plugins: [Relay.feeToken(), Relay.simulate()],
timeout: 5_000,
})Return Value
Relay.create.ReturnType
An object with request and fetch methods. Neither method requires this binding.
Errors
| Error | Description |
|---|---|
RpcResponse.InvalidParamsError | Both or neither client options were supplied, or the single client has no valid configured chain. |
relay.request
Handles { method, params? } and returns Promise<unknown> containing the RPC
result. The optional second argument preserves Viem request options, including
cancellation, retries, and deduplication, and adds an optional numeric chainId.
await relay.request({ method: 'eth_blockNumber' }, { chainId: 4217 })Invalid, conflicting, or missing required chain IDs reject with
RpcResponse.InvalidParamsError. Resolver and downstream errors propagate.
Locally handled requests need not resolve a client.
relay.fetch
Handles a standard Request and returns Promise<Response>. The optional second
argument accepts the same request options as relay.request. The incoming
request's abort signal is used unless an explicit signal is supplied.
export default {
fetch(request: Request) {
return relay.fetch(request, { chainId: 4217 })
},
}HTTP Behavior
| Request | Response |
|---|---|
POST with Content-Type: application/json | JSON-RPC response with HTTP 200. |
| Valid batch | Array of responses, omitting notifications. |
| Notification or notification-only batch | Empty HTTP 204 response after execution. |
| Malformed JSON | JSON-RPC parse error, code -32700. |
| Invalid request or empty batch | JSON-RPC invalid-request error, code -32600. |
| Named parameters | JSON-RPC invalid-params error; Tempo RPC uses positional parameters. |
| Method other than POST | HTTP 405 with Allow: POST. |
| Unsupported content type | HTTP 415. |
RPC errors preserve their code, message, and data. Unexpected exceptions become JSON-RPC internal errors without exposing implementation details. A batch item that cannot be serialized fails individually.
Applications supply routing, authentication, CORS, and persistent storage. Read Run a Relay for deployment examples.
Migration
Replace Multisig.handleRequest with the multisig plugin. The low-level composer
retains an existing downstream callback:
-import { Multisig } from 'viem/tempo'
+import { Relay } from 'viem/tempo'
-const request = Multisig.handleRequest(next, { store })
+const request = Relay.handleRequest(next, {
+ plugins: [Relay.multisig({ store })],
+})For a service backed by a Viem client, use Relay.create({ client, plugins }) to
obtain both parsed RPC and Fetch handlers.