# Sponsor User Fees

:::info
This guide is intended to be low-level. If you are looking for a high-level abstraction, check out
[Viem's Sponsor User Fees guide](https://viem.sh/tempo/guides/sponsor-fees).
:::

## Overview

Tempo fee sponsorship separates transaction authorization from fee payment. The sender signs the
transaction's execution fields, while a fee payer signs a distinct payload that commits to the
sender address and selected fee token.

Ox exposes both digests and assembles the dual-signed envelope. Coordinating the two signers,
applying sponsorship policy, and funding the fee payer remain application responsibilities.

## Recipes

### Create a Sponsor-Ready Envelope

Set `feePayerSignature` to `null` when calling
[`TxEnvelopeTempo.from`](/tempo/reference/TxEnvelopeTempo/from). This marker tells the sender
signing encoder that a fee payer will complete the envelope.

```ts twoslash
import { TxEnvelopeTempo } from 'ox/tempo'

// [!code focus:start]
const envelope = TxEnvelopeTempo.from({
  calls: [{ to: '0xcafebabecafebabecafebabecafebabecafebabe' }],
  chainId: 4217,
  feePayerSignature: null, // [!code hl]
  feeToken: '0x20c0000000000000000000000000000000000001',
  gas: 21_000n,
  maxFeePerGas: 2_000_000_000n,
  nonce: 0n,
  nonceKey: 1n,
})
// @log: {
// @log:   calls: [{ to: '0xcafebabecafebabecafebabecafebabecafebabe' }],
// @log:   feePayerSignature: null,
// @log:   feeToken: '0x20c0000000000000000000000000000000000001',
// @log:   type: 'tempo',
// @log:   ...
// @log: }
// [!code focus:end]
```

### Produce Both Signatures

The sender signs [`TxEnvelopeTempo.getSignPayload`](/tempo/reference/TxEnvelopeTempo/getSignPayload).
The sponsor signs
[`TxEnvelopeTempo.getFeePayerSignPayload`](/tempo/reference/TxEnvelopeTempo/getFeePayerSignPayload)
with the sender address.

```ts twoslash
import { Address, Secp256k1 } from 'ox'
import { TxEnvelopeTempo } from 'ox/tempo'

// 1. Set up the sender and fee payer keys.
const senderPrivateKey = Secp256k1.randomPrivateKey()
const sender = Address.fromPublicKey(
  Secp256k1.getPublicKey({ privateKey: senderPrivateKey }),
)
const feePayerPrivateKey = Secp256k1.randomPrivateKey()

// 2. Build a sponsor-ready envelope.
const envelope = TxEnvelopeTempo.from({
  calls: [{ to: '0xcafebabecafebabecafebabecafebabecafebabe' }],
  chainId: 4217,
  feePayerSignature: null,
  feeToken: '0x20c0000000000000000000000000000000000001',
  gas: 21_000n,
  maxFeePerGas: 2_000_000_000n,
  nonce: 0n,
  nonceKey: 1n,
})

// [!code focus:start]
// 3. Sign the sender's transaction commitment.
const senderSignature = Secp256k1.sign({
  payload: TxEnvelopeTempo.getSignPayload(envelope),
  privateKey: senderPrivateKey,
})

// 4. Sign the fee payer commitment with the sender address.
const feePayerSignature = Secp256k1.sign({
  payload: TxEnvelopeTempo.getFeePayerSignPayload(envelope, { sender }),
  privateKey: feePayerPrivateKey,
})
// @log: {
// @log:   senderSignature: { r: '0x...', s: '0x...', yParity: 0 },
// @log:   feePayerSignature: { r: '0x...', s: '0x...', yParity: 1 },
// @log: }
// [!code focus:end]
```

### Assemble and Serialize the Sponsored Envelope

Attach both signatures with [`TxEnvelopeTempo.from`](/tempo/reference/TxEnvelopeTempo/from), then
serialize the completed envelope for `eth_sendRawTransaction`.

```ts twoslash
import { Address, Secp256k1 } from 'ox'
import { TxEnvelopeTempo } from 'ox/tempo'

// 1. Set up the sender and fee payer keys.
const senderPrivateKey = Secp256k1.randomPrivateKey()
const sender = Address.fromPublicKey(
  Secp256k1.getPublicKey({ privateKey: senderPrivateKey }),
)
const feePayerPrivateKey = Secp256k1.randomPrivateKey()

// 2. Build a sponsor-ready envelope.
const envelope = TxEnvelopeTempo.from({
  calls: [{ to: '0xcafebabecafebabecafebabecafebabecafebabe' }],
  chainId: 4217,
  feePayerSignature: null,
  feeToken: '0x20c0000000000000000000000000000000000001',
  gas: 21_000n,
  maxFeePerGas: 2_000_000_000n,
  nonce: 0n,
  nonceKey: 1n,
})

// 3. Produce the sender and fee payer signatures.
const senderSignature = Secp256k1.sign({
  payload: TxEnvelopeTempo.getSignPayload(envelope),
  privateKey: senderPrivateKey,
})
const feePayerSignature = Secp256k1.sign({
  payload: TxEnvelopeTempo.getFeePayerSignPayload(envelope, { sender }),
  privateKey: feePayerPrivateKey,
})

// [!code focus:start]
// 4. Attach both signatures to the envelope.
const sponsored = TxEnvelopeTempo.from(envelope, {
  feePayerSignature,
  signature: senderSignature,
})

// 5. Serialize the completed sponsored envelope.
const serialized = TxEnvelopeTempo.serialize(sponsored)
// @log: '0x76...'
// [!code focus:end]
```

## Best Practices

### Authorize the Exact Sender

The fee payer payload includes the sender address. Recover or derive it from the sender's
signature flow, then verify it against the sponsorship request before signing.

### Let the Fee Payer Control Its Token

When the sponsorship marker is present, the sender sign payload does not commit to `feeToken`.
The fee payer payload does commit to it, which lets the sponsor select the asset it will spend.

### Keep the Fee Payer Key Isolated

Treat the fee payer as a policy-controlled signing service. Check allowed senders, calls, fee
caps, rate limits, and balances before releasing a signature.

### Coordinate Either Signing Order

Either party may sign first once the sender address and committed fields are known. The example
uses a sender-first flow, but it is a coordination choice rather than a protocol requirement. Do
not mutate fields covered by a signature after producing it.

## See More

<Cards>
  <Card icon="lucide:coins" title="Pay Fees in a Stablecoin" description="Select a TIP-20 token without fee sponsorship." to="/tempo/guides/transaction-envelopes/pay-fees-in-a-stablecoin" />

  <Card icon="lucide:square-function" title="getFeePayerSignPayload" description="Review the fee payer digest API reference." to="/tempo/reference/TxEnvelopeTempo/getFeePayerSignPayload" />

  <Card icon="lucide:book-open" title="Viem: Sponsor User Fees" description="Coordinate and submit sponsored transactions with a Viem client." to="https://viem.sh/tempo/guides/sponsor-fees" />
</Cards>
