diff --git a/src/components/guides/steps/payments/SponsorUserFees.tsx b/src/components/guides/steps/payments/SponsorUserFees.tsx index db8e4ec4..8c386c8e 100644 --- a/src/components/guides/steps/payments/SponsorUserFees.tsx +++ b/src/components/guides/steps/payments/SponsorUserFees.tsx @@ -44,7 +44,6 @@ export function SendRelayerSponsoredPayment(props: DemoStepProps) { to: recipient as `0x${string}`, token: alphaUsd, memo: memo ? pad(stringToHex(memo), { size: 32 }) : undefined, - feePayer: true, }) } diff --git a/src/pages/accounts/api/provider.mdx b/src/pages/accounts/api/provider.mdx index f2320044..60024c9d 100644 --- a/src/pages/accounts/api/provider.mdx +++ b/src/pages/accounts/api/provider.mdx @@ -111,7 +111,7 @@ const provider = Provider.create({ - **Type:** `string | { url: string; precedence?: 'fee-payer-first' | 'user-first' }` - **Optional** -Fee payer configuration for interacting with a service running [`Handler.feePayer`](/accounts/server/handler.feePayer) from `accounts/server`. Pass a URL string, or an object with `url` and optional `precedence` to control whether the fee payer or the user pays first. +Fee payer configuration for interacting with a service running [`Handler.relay`](/accounts/server/handler.relay) from `accounts/server`. Pass a URL string, or an object with `url` and optional `precedence` to control whether the fee payer or the user pays first. ```ts twoslash import { Provider } from 'accounts' diff --git a/src/pages/accounts/rpc/eth_fillTransaction.mdx b/src/pages/accounts/rpc/eth_fillTransaction.mdx index aa7cdbab..1fc6d4d0 100644 --- a/src/pages/accounts/rpc/eth_fillTransaction.mdx +++ b/src/pages/accounts/rpc/eth_fillTransaction.mdx @@ -242,7 +242,7 @@ result.capabilities.fee ### With Sponsorship -When the relay is configured with a [`feePayer`](/accounts/server/handler.relay#feepayer) and the request is sponsorable, the response includes `sponsored: true` and `sponsor` details. +When the relay is configured with a [`feePayer`](/accounts/server/handler.relay#feepayer), validated transactions are sponsored. The response includes `sponsored: true` and `sponsor` details. ```ts twoslash // @noErrors diff --git a/src/pages/accounts/server/handler.compose.mdx b/src/pages/accounts/server/handler.compose.mdx index 161a983f..e3d1c02c 100644 --- a/src/pages/accounts/server/handler.compose.mdx +++ b/src/pages/accounts/server/handler.compose.mdx @@ -13,8 +13,8 @@ Composes multiple handlers into a single handler, routing requests to the handle import { Handler } from 'accounts/server' const handler = Handler.compose([ - Handler.feePayer({ - account: privateKeyToAccount('0x...') + Handler.relay({ + feePayer: { account: privateKeyToAccount('0x...') }, }), Handler.webAuthn({ kv: Kv.memory(), diff --git a/src/pages/accounts/server/handler.feePayer.mdx b/src/pages/accounts/server/handler.feePayer.mdx index 4baf405b..041878a8 100644 --- a/src/pages/accounts/server/handler.feePayer.mdx +++ b/src/pages/accounts/server/handler.feePayer.mdx @@ -1,149 +1,42 @@ --- -title: Handler.feePayer -description: Server handler that sponsors transaction fees for users. +title: Handler.feePayer (Deprecated) +description: Deprecated — use Handler.relay with feePayer option instead. --- -# `Handler.feePayer` +# `Handler.feePayer` (Deprecated) -Creates a server handler that acts as a fee payer for transactions, enabling you to subsidize gas costs for users by signing transactions with a dedicated fee payer account on your backend. - -:::info -[See the guide](/guide/payments/sponsor-user-fees) +:::warning +`Handler.feePayer` has been deprecated. Use [`Handler.relay`](/accounts/server/handler.relay) with the [`feePayer`](/accounts/server/handler.relay#feepayer) option instead. ::: -## Usage - -```ts -import { privateKeyToAccount } from 'viem/accounts' -import { Handler } from 'accounts/server' - -const handler = Handler.feePayer({ - account: privateKeyToAccount('0x...'), -}) -``` - -Then plug `handler` into your server framework of choice: - -```ts -createServer(handler.listener) // Node.js -Bun.serve(handler) // Bun -Deno.serve(handler) // Deno -app.all('*', c => handler.fetch(c.request)) // Elysia -app.use(handler.listener) // Express -app.use(c => handler.fetch(c.req.raw)) // Hono -export const GET = handler.fetch // Next.js -export const POST = handler.fetch // Next.js -``` - -## Parameters - -### account - -- **Type:** `LocalAccount` -- **Required** +## Migration -The account to use as the fee payer. This account will sign all transactions and pay the gas fees. +Replace `Handler.feePayer` calls with `Handler.relay` and nest the `account` under `feePayer`: -```ts twoslash -import { privateKeyToAccount } from 'viem/accounts' -import { Handler } from 'accounts/server' - -const handler = Handler.feePayer({ - account: privateKeyToAccount('0x...'), // [!code focus] -}) +```diff +- const handler = Handler.feePayer({ +- account: privateKeyToAccount('0x...'), +- }) ++ const handler = Handler.relay({ ++ feePayer: { ++ account: privateKeyToAccount('0x...'), ++ }, ++ }) ``` -### chains - -- **Type:** `readonly [Chain, ...Chain[]]` -- **Default:** `[tempo, tempoModerato]` - -Supported chains. The handler resolves the client based on the `chainId` in the incoming transaction. - -```ts twoslash -import { privateKeyToAccount } from 'viem/accounts' -import { tempo } from 'viem/chains' -import { Handler } from 'accounts/server' - -const handler = Handler.feePayer({ - account: privateKeyToAccount('0x...'), - chains: [tempo], // [!code focus] -}) +All other options (`chains`, `transports`, `path`, `onRequest`) remain the same — `validate` moves inside `feePayer`: + +```diff +- const handler = Handler.feePayer({ +- account: privateKeyToAccount('0x...'), +- validate: (request) => request.from !== blocked, +- }) ++ const handler = Handler.relay({ ++ feePayer: { ++ account: privateKeyToAccount('0x...'), ++ validate: (request) => request.from !== blocked, ++ }, ++ }) ``` -### onRequest - -- **Type:** `(request: RpcRequest) => Promise` -- **Optional** - -Callback called before processing each request. Useful for logging, rate limiting, or custom validation. - -```ts twoslash -import { privateKeyToAccount } from 'viem/accounts' -import { Handler } from 'accounts/server' - -const handler = Handler.feePayer({ - account: privateKeyToAccount('0x...'), - onRequest: async (request) => { // [!code focus] - console.log('Processing request:', request.method) // [!code focus] - }, // [!code focus] -}) -``` - -### path - -- **Type:** `string` -- **Default:** `'/'` - -Path where the handler listens for requests. - -```ts twoslash -import { privateKeyToAccount } from 'viem/accounts' -import { Handler } from 'accounts/server' - -const handler = Handler.feePayer({ - account: privateKeyToAccount('0x...'), - path: '/fee-payer', // [!code focus] -}) -``` - -### transports - -- **Type:** `Record` -- **Default:** `http()` for each chain - -Transports keyed by chain ID. - -```ts twoslash -import { http } from 'viem' -import { privateKeyToAccount } from 'viem/accounts' -import { tempo } from 'viem/chains' -import { Handler } from 'accounts/server' - -const handler = Handler.feePayer({ - account: privateKeyToAccount('0x...'), - transports: { // [!code focus] - [tempo.id]: http('https://rpc.tempo.xyz'), // [!code focus] - }, // [!code focus] -}) -``` - - -### validate - -- **Type:** `(request: TransactionRequest) => boolean | Promise` -- **Optional** - -Validates whether to sponsor a transaction. When omitted, all transactions are sponsored. Return `false` to reject sponsorship — the handler will re-fill without `feePayer` so gas/nonce are correct for self-payment. - -```ts twoslash -import { privateKeyToAccount } from 'viem/accounts' -import { Handler } from 'accounts/server' - -const blocked = '0x...' - -const handler = Handler.feePayer({ - account: privateKeyToAccount('0x...'), - validate: (request) => request.from !== blocked, // [!code focus] -}) -``` \ No newline at end of file +See [`Handler.relay`](/accounts/server/handler.relay) for the full API reference. diff --git a/src/pages/accounts/server/index.mdx b/src/pages/accounts/server/index.mdx index ee469483..bf98df93 100644 --- a/src/pages/accounts/server/index.mdx +++ b/src/pages/accounts/server/index.mdx @@ -17,8 +17,8 @@ Handlers are compatible with any server framework that supports the: import { Handler } from 'accounts/server' import { privateKeyToAccount } from 'viem/accounts' -const handler = Handler.feePayer({ - account: privateKeyToAccount('0x...'), +const handler = Handler.relay({ + feePayer: { account: privateKeyToAccount('0x...') }, }) createServer(handler.listener) // Node.js @@ -39,10 +39,10 @@ export const POST = handler.fetch // Next.js title="Handler.compose" /> diff --git a/src/snippets/unformatted/withFeePayer.ts b/src/snippets/unformatted/withFeePayer.ts index 91299c37..108fca71 100644 --- a/src/snippets/unformatted/withFeePayer.ts +++ b/src/snippets/unformatted/withFeePayer.ts @@ -1,12 +1,12 @@ // @ts-nocheck // [!region client] import { walletActions } from 'viem' -import { withFeePayer } from 'viem/tempo' +import { withRelay } from 'viem/tempo' const _client = createClient({ account: privateKeyToAccount('0x...'), chain: tempo, - transport: withFeePayer( + transport: withRelay( // [!code hl] http(), // [!code hl] http('http://localhost:3000'), // [!code hl] @@ -16,15 +16,15 @@ const _client = createClient({ // [!endregion client] // [!region usage] -// Regular transaction +// Sponsored transaction (automatic when relay has feePayer configured) const _receipt1 = await client.sendTransactionSync({ to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb', }) -// Sponsored transaction // [!code hl] +// Opt out of sponsorship // [!code hl] const _receipt2 = await client.sendTransactionSync({ // [!code hl] - feePayer: true, // [!code hl] + feePayer: false, // [!code hl] to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb', // [!code hl] }) // [!code hl] @@ -34,9 +34,9 @@ const _receipt2 = await client.sendTransactionSync({ import { Handler } from 'accounts/server' import { privateKeyToAccount } from 'viem/accounts' -const handler = Handler.feePayer({ +const handler = Handler.relay({ // [!code hl] - account: privateKeyToAccount('0x...'), // [!code hl] + feePayer: { account: privateKeyToAccount('0x...') }, // [!code hl] }) // [!code hl] const server = createServer(handler.listener) diff --git a/src/snippets/wagmi.config.ts b/src/snippets/wagmi.config.ts index 3c7ca7e9..af6435a4 100644 --- a/src/snippets/wagmi.config.ts +++ b/src/snippets/wagmi.config.ts @@ -22,7 +22,7 @@ export const config = createConfig({ // [!region withFeePayer] import { tempoWallet } from 'accounts/wagmi' import { tempo } from 'viem/chains' -import { withFeePayer } from 'viem/tempo' +import { withRelay } from 'viem/tempo' import { createConfig, http } from 'wagmi' import { KeyManager, webAuthn } from 'wagmi/tempo' diff --git a/src/wagmi.config.ts b/src/wagmi.config.ts index c5401041..89007557 100644 --- a/src/wagmi.config.ts +++ b/src/wagmi.config.ts @@ -4,7 +4,7 @@ import { tempoWallet, webAuthn as webAuthnAccounts } from 'accounts/wagmi' import * as React from 'react' import { parseUnits } from 'viem' import { tempoDevnet, tempoLocalnet, tempoModerato } from 'viem/chains' -import { withFeePayer } from 'viem/tempo' +import { withRelay } from 'viem/tempo' import { type CreateConfigParameters, createConfig, @@ -79,7 +79,7 @@ export function getConfig(options: getConfig.Options = {}) { key: 'tempo-docs', }), transports: { - [tempoModerato.id]: withFeePayer( + [tempoModerato.id]: withRelay( fallback([ http('https://rpc.moderato.tempo.xyz'), webSocket('wss://rpc.moderato.tempo.xyz', { @@ -89,7 +89,7 @@ export function getConfig(options: getConfig.Options = {}) { http('https://sponsor.moderato.tempo.xyz'), { policy: 'sign-only' }, ), - [tempoDevnet.id]: withFeePayer( + [tempoDevnet.id]: withRelay( fallback([ http(tempoDevnet.rpcUrls.default.http[0]), webSocket(tempoDevnet.rpcUrls.default.webSocket[0], { diff --git a/vocs.config.ts b/vocs.config.ts index e6a3ca85..73ce4857 100644 --- a/vocs.config.ts +++ b/vocs.config.ts @@ -1188,7 +1188,7 @@ export default defineConfig({ }, { source: '/sdk/typescript/server/handler.feePayer', - destination: '/accounts/server/handler.feePayer', + destination: '/accounts/server/handler.relay', status: 301, }, {