diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index aca18cab..345fba54 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -32,6 +32,11 @@ jobs: - name: Check types run: pnpm run check:types + - name: Build + run: pnpm run build + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + e2e: name: E2E Tests runs-on: ubuntu-latest diff --git a/src/pages/guide/payments/sponsor-user-fees.mdx b/src/pages/guide/payments/sponsor-user-fees.mdx index cae2f47e..c3c56618 100644 --- a/src/pages/guide/payments/sponsor-user-fees.mdx +++ b/src/pages/guide/payments/sponsor-user-fees.mdx @@ -156,6 +156,47 @@ const { receipt } = await client.token.transferSync({ - **Balance checks**: Network verifies fee payer has sufficient balance - **Signature validation**: Both signatures must be valid +### Signing Hash Behavior + +When building fee-sponsored transactions, the sender must indicate sponsorship **before signing**. This is because the transaction's signing hash differs based on whether it will be sponsored: + +- **Sponsored transactions**: The `fee_token` field is omitted from the sender's signing payload, and a `0x00` marker is used. This allows the fee payer to choose the fee token. +- **Non-sponsored transactions**: The `fee_token` is included in the sender's signing payload. + +The `withFeePayer` transport and `feePayer: true` parameter handle this automatically. If you're building transactions manually with a local account as fee payer, the Viem SDK handles this for you: + +```ts twoslash +// @noErrors +import { createClient, http, parseUnits } from 'viem' +import { privateKeyToAccount } from 'viem/accounts' +import { tempoModerato } from 'viem/chains' + +const client = createClient({ + chain: tempoModerato, + transport: http(), +}) + +const senderAccount = privateKeyToAccount('0x...') +const feePayerAccount = privateKeyToAccount('0x...') + +// When feePayer is an Account object, Viem: +// 1. Has the sender sign the transaction (with feeToken) +// 2. Recovers the sender address from their signature +// 3. Has the fee payer sign a separate hash (includes sender + feeToken) +// 4. Serializes the dual-signed transaction +const { receipt } = await client.token.transferSync({ + account: senderAccount, + amount: parseUnits('10.5', 6), + to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb', + token: '0x20c0000000000000000000000000000000000000', + feePayer: feePayerAccount, // [!code hl] +}) +``` + +:::info +When using `feePayer: true` with the `withFeePayer` transport, the SDK deletes the `feeToken` from the transaction before signing, then sends it to the fee payer service which adds their signature. +::: + ## Learning Resources diff --git a/vocs.config.ts b/vocs.config.ts index 900c8ed2..18b76687 100644 --- a/vocs.config.ts +++ b/vocs.config.ts @@ -9,7 +9,8 @@ const baseUrl = (() => { export default defineConfig({ changelog: Changelog.github({ prereleases: true, repo: 'tempoxyz/tempo' }), - checkDeadlinks: true, + // TODO: Set back to true once tempoxyz/tempo#tip-1011 dead link is fixed + checkDeadlinks: 'warn', title: 'Tempo', titleTemplate: '%s ⋅ Tempo',