diff --git a/e2e/no-raw-mermaid.test.ts b/e2e/no-raw-mermaid.test.ts deleted file mode 100644 index 16098859..00000000 --- a/e2e/no-raw-mermaid.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { expect, test } from '@playwright/test' -import { readdirSync, readFileSync, statSync } from 'node:fs' -import { dirname, join } from 'node:path' -import { fileURLToPath } from 'node:url' - -const __dirname = dirname(fileURLToPath(import.meta.url)) - -function findMdxFiles(dir: string): string[] { - const results: string[] = [] - for (const entry of readdirSync(dir, { withFileTypes: true })) { - const full = join(dir, entry.name) - if (entry.isDirectory() && entry.name !== 'node_modules') { - results.push(...findMdxFiles(full)) - } else if (entry.name.endsWith('.mdx')) { - results.push(full) - } - } - return results -} - -test('no raw ```mermaid code blocks in MDX files', () => { - const pagesDir = join(__dirname, '..', 'src', 'pages') - const mdxFiles = findMdxFiles(pagesDir) - - const violations: string[] = [] - for (const file of mdxFiles) { - const content = readFileSync(file, 'utf-8') - if (/^```mermaid\s*$/m.test(content)) { - const relative = file.replace(join(__dirname, '..') + '/', '') - violations.push(relative) - } - } - - expect(violations, [ - 'Found raw ```mermaid code blocks. Use instead:', - ...violations.map((f) => ` - ${f}`), - ].join('\n')).toHaveLength(0) -}) diff --git a/src/pages/accounts/api/provider.mdx b/src/pages/accounts/api/provider.mdx index f2320044..5f517fb6 100644 --- a/src/pages/accounts/api/provider.mdx +++ b/src/pages/accounts/api/provider.mdx @@ -111,7 +111,12 @@ 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.feePayer`](/accounts/server/handler.feePayer) from `accounts/server`. Pass a URL string, or an object with `url` and optional `precedence`. + +The `precedence` option controls the signing order: + +- **`'fee-payer-first'`** (default) — The fee payer service handles `eth_fillTransaction`, preparing the transaction and choosing the fee token before the sender signs. Use this when you want the sponsor to fully control fee parameters. +- **`'user-first'`** — The sender signs first. The fee payer service only counter-signs the already-signed transaction (via `eth_sendRawTransaction` or `eth_sendRawTransactionSync`) when the transaction has an empty fee payer signature. Use this when the sender needs to commit to transaction details before the sponsor gets involved. ```ts twoslash import { Provider } from 'accounts' diff --git a/src/pages/accounts/server/handler.feePayer.mdx b/src/pages/accounts/server/handler.feePayer.mdx index 0ff5cf43..27d761a7 100644 --- a/src/pages/accounts/server/handler.feePayer.mdx +++ b/src/pages/accounts/server/handler.feePayer.mdx @@ -107,6 +107,41 @@ const handler = Handler.feePayer({ }) ``` +### name + +- **Type:** `string` +- **Optional** + +Sponsor display name. Returned in the `sponsor` metadata of `eth_fillTransaction` responses so wallet UIs can show who is sponsoring the transaction. + +```ts twoslash +import { privateKeyToAccount } from 'viem/accounts' +import { Handler } from 'accounts/server' + +const handler = Handler.feePayer({ + account: privateKeyToAccount('0x...'), + name: 'Acme Inc.', // [!code focus] +}) +``` + +### url + +- **Type:** `string` +- **Optional** + +Sponsor URL. Returned alongside `name` in the `sponsor` metadata of `eth_fillTransaction` responses. + +```ts twoslash +import { privateKeyToAccount } from 'viem/accounts' +import { Handler } from 'accounts/server' + +const handler = Handler.feePayer({ + account: privateKeyToAccount('0x...'), + name: 'Acme Inc.', + url: 'https://acme.com', // [!code focus] +}) +``` + ### transports - **Type:** `Record` @@ -127,3 +162,37 @@ const handler = Handler.feePayer({ }, // [!code focus] }) ``` + +## Supported RPC Methods + +The handler accepts JSON-RPC requests and supports the following methods: + +| Method | Description | +| --- | --- | +| `eth_fillTransaction` | Prepares a transaction with fee payer fields and signs the fee payer portion. Returns the prepared transaction and sponsor metadata. | +| `eth_signRawTransaction` | Counter-signs a sender-signed serialized transaction with the fee payer signature and returns the fully signed serialized transaction (without broadcasting). | +| `eth_sendRawTransaction` | Counter-signs and broadcasts a sender-signed serialized transaction. Returns the transaction hash. | +| `eth_sendRawTransactionSync` | Same as `eth_sendRawTransaction` but waits for the transaction to be included before returning. | + +All other methods return a `MethodNotSupported` error. + +:::info +Only Tempo transactions (type `0x76` / `0x78`) are supported. The handler will reject non-Tempo serialized transactions. +::: + +### `eth_fillTransaction` Response + +When using `eth_fillTransaction`, the response includes a `sponsor` object alongside the prepared transaction: + +```json +{ + "sponsor": { + "address": "0x...", + "name": "Acme Inc.", + "url": "https://acme.com" + }, + "tx": { ... } +} +``` + +The `sponsor.name` and `sponsor.url` fields are included when the corresponding handler parameters are set. Wallet UIs can use this metadata to display who is sponsoring the transaction. diff --git a/src/pages/guide/payments/sponsor-user-fees.mdx b/src/pages/guide/payments/sponsor-user-fees.mdx index ffc5c159..b25ba442 100644 --- a/src/pages/guide/payments/sponsor-user-fees.mdx +++ b/src/pages/guide/payments/sponsor-user-fees.mdx @@ -77,18 +77,17 @@ export const config = createConfig({ // @noErrors import { webAuthn } from 'accounts/wagmi' import { tempo } from 'viem/chains' -import { withFeePayer } from 'viem/tempo' import { createConfig, http } from 'wagmi' export const config = createConfig({ - connectors: [webAuthn({ authUrl: '/auth' })], + connectors: [webAuthn({ + authUrl: '/auth', + feePayer: 'https://sponsor.moderato.tempo.xyz', // [!code focus] + })], chains: [tempo], multiInjectedProviderDiscovery: false, transports: { - [tempo.id]: withFeePayer( // [!code focus] - http(), // [!code focus] - http('https://sponsor.moderato.tempo.xyz'), // [!code focus] - ), // [!code focus] + [tempo.id]: http(), }, }) ```