<!-- tempo-docs-context -->
> Source: [/docs/guide/payments/pay-fees-in-any-stablecoin](https://tempo.xyz/developers/docs/guide/payments/pay-fees-in-any-stablecoin.md)
> Section: [Tempo EVM](https://tempo.xyz/developers/docs/development.md). Build on Tempo EVM: wallets and keys, payments, transactions and fees, TIP-20 Tokens, contracts, exchange, network details, nodes, protocol specifications, and changelog.
> [Documentation index](https://tempo.xyz/developers/llms.txt) · [Docs skill](https://tempo.xyz/developers/SKILL.md)

> Tempo MCP: Use `search`, `find_pages`, `read_page`, and `code` at `https://mcp.tempo.xyz` for Tempo and related documentation.
> If an indexed page is missing or reflects an older site, read this site’s [documentation index](https://tempo.xyz/developers/llms.txt) and Markdown pages directly.

<span id="pay-fees-in-any-stablecoin" />

# Choose a fee token

Choose which stablecoin pays a transaction’s fees, independently of the token being transferred. A fee token must be USD-denominated, use the TIP-20 standard, and have sufficient liquidity in the native Fee AMM. Tempo has no separate gas token.

See [transaction fees](https://tempo.xyz/developers/docs/protocol/fees) for eligibility and conversion rules, or [Sponsor fees](https://tempo.xyz/developers/docs/guide/payments/sponsor-user-fees) to have your application cover the fee.

## Fee-token payment demo

Select a supported fee token and send a payment with it in this demo.

**Interactive demo: Choose a fee token**

1. Connect
2. Add funds
3. Pay with fee token

Source: [tempoxyz/examples/tree/main/examples/payments](https://github.com/tempoxyz/examples/tree/main/examples/payments)

<span id="quick-snippet" />

## Quick fee-token snippet

Pass `feeToken` when submitting a payment. Use the [Viem client](https://tempo.xyz/developers/docs/guide/payments/send-a-payment#send-from-your-backend) for backend signing or the [Wagmi configuration](https://tempo.xyz/developers/docs/guide/payments/send-a-payment#add-a-payment-action-in-the-browser) for a browser wallet. These examples use AlphaUSD and BetaUSD on Moderato; replace the example recipient with an account you control. Set `RPC_URL` or `TEMPO_RPC_URL` to `https://rpc.moderato.tempo.xyz` for the other language examples.

### Viem

```tsx twoslash
// @noErrors
import { parseUnits } from 'viem'
import { client } from './viem.config'

const alphaUsd = '0x20c0000000000000000000000000000000000001'
const betaUsd = '0x20c0000000000000000000000000000000000002'

const { receipt } = await client.token.transferSync({
  amount: parseUnits('100', 6),
  feeToken: betaUsd, // [!code ++]
  to: '0x742d35cc6634c0532925a3b844bc9e7595f0bebb',
  token: alphaUsd,
})
```

### Wagmi

```tsx twoslash
// @noErrors
import { Hooks } from 'wagmi/tempo'
import { parseUnits } from 'viem'

const alphaUsd = '0x20c0000000000000000000000000000000000001'
const betaUsd = '0x20c0000000000000000000000000000000000002'

function SendPayment() {
  const sendPayment = Hooks.token.useTransferSync()

  return (
    <button
      disabled={sendPayment.isPending}
      onClick={() => sendPayment.mutate({
        amount: parseUnits('100', 6),
        feeToken: betaUsd,
        to: '0x742d35cc6634c0532925a3b844bc9e7595f0bebb',
        token: alphaUsd,
      })}
    >
      Send payment
    </button>
  )
}
```

### Rust

:::code-group
```rust [example.rs]
use alloy::{
    primitives::{address, U256},
    providers::Provider,
    sol_types::SolCall,
};
use tempo_alloy::{
    contracts::precompiles::ITIP20, primitives::transaction::Call,
    rpc::TempoTransactionRequest,
};

mod provider;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let provider = provider::get_provider().await?;

    let alpha_usd = address!("0x20c0000000000000000000000000000000000001");
    let beta_usd = address!("0x20c0000000000000000000000000000000000002");

    let calls = vec![Call {
        to: alpha_usd.into(),
        input: ITIP20::transferCall {
            to: address!("0x742d35cc6634c0532925a3b844bc9e7595f0bebb"),
            amount: U256::from(100_000_000),
        }
        .abi_encode()
        .into(),
        value: U256::ZERO,
    }];

    let pending = provider
        .send_transaction(TempoTransactionRequest {
            calls,
            fee_token: Some(beta_usd), // [!code ++]
            ..Default::default()
        })
        .await?;

    Ok(())
}
```

```rust [provider.rs]
use alloy::providers::ProviderBuilder;
use alloy::signers::local::PrivateKeySigner;
use tempo_alloy::TempoNetwork;

pub async fn get_provider() -> Result<
    impl alloy::providers::Provider<TempoNetwork>,
    Box<dyn std::error::Error>,
> {
    let signer: PrivateKeySigner = std::env::var("PRIVATE_KEY")
        .expect("PRIVATE_KEY not set")
        .parse()?;

    let provider = ProviderBuilder::new_with_network::<TempoNetwork>()
        .wallet(signer)
        .connect(&std::env::var("RPC_URL").expect("RPC_URL not set"))
        .await?;

    Ok(provider)
}
```
:::

### Python

```python
from pytempo import TempoTransaction
from pytempo.contracts import TIP20, ALPHA_USD, BETA_USD

tx = TempoTransaction.create(
    chain_id=w3.eth.chain_id,
    gas_limit=100_000,
    max_fee_per_gas=w3.eth.gas_price * 2,
    max_priority_fee_per_gas=w3.eth.gas_price,
    nonce=w3.eth.get_transaction_count(account.address),
    fee_token=BETA_USD, # [!code ++]
    calls=(
        TIP20(ALPHA_USD).transfer(
            to="0x742d35cc6634c0532925a3b844bc9e7595f0bebb",
            amount=100_000_000,
        ),
    ),
)
```

### Go

```go
alphaUSD := common.HexToAddress("0x20c0000000000000000000000000000000000001")
betaUSD := common.HexToAddress("0x20c0000000000000000000000000000000000002")

tx := transaction.NewBuilder(big.NewInt(transaction.ChainIdModerato)).
        SetNonce(nonce).
        SetGas(100_000).
        SetMaxFeePerGas(big.NewInt(25_000_000_000)).
        SetMaxPriorityFeePerGas(big.NewInt(1_000_000_000)).
        SetFeeToken(betaUSD). // [!code ++]
        AddCall(alphaUSD, big.NewInt(0), buildTransferData(recipient, big.NewInt(100_000_000))).
        Build()
```

### Cast

```bash
$ cast send \
  0x20c0000000000000000000000000000000000001 \
  "transfer(address,uint256)" \
  0x742d35cc6634c0532925a3b844bc9e7595f0bebb \
  100000000 \
  --rpc-url $TEMPO_RPC_URL \
  --private-key $PRIVATE_KEY \
  --tempo.fee-token 0x20c0000000000000000000000000000000000002 # [!code ++]
```

### Solidity

:::info
The fee token for a given transaction cannot be set from Solidity — it is a transaction-level parameter handled by the signing SDK. However, you can configure a **default** fee token for an account using [`setUserToken`](https://tempo.xyz/developers/docs/guide/payments/pay-fees-in-any-stablecoin#set-a-default-user-fee-token), which will apply to all future transactions unless explicitly overridden at submission.
:::

## Fee-token implementation steps

### Wagmi

::::steps
### Set up Wagmi for fee tokens

Ensure that you have set up your project with Wagmi, a Tempo chain config, and a wallet connector:

* [Connection details](https://tempo.xyz/developers/docs/quickstart/connection-details)
* [TypeScript SDK](https://tempo.xyz/developers/docs/sdk/typescript)
* [Wallet integration](https://tempo.xyz/developers/docs/quickstart/wallet-developers)

<span id="add-testnet-fee-token-funds" />

### Fund the payment and fee tokens

Before you can pay fees in a token of your choice, you need to fund your account. In this guide you will be sending `AlphaUSD` (`0x20c000…0001`) and paying fees in `BetaUSD` (`0x20c000…0002`).

The built-in Tempo testnet faucet includes `AlphaUSD` and `BetaUSD` when funding.

**Interactive demo: Add Funds**

1. Add funds

:::code-group
```tsx twoslash [AddFunds.ts]
// @noErrors
import { Hooks } from 'wagmi/tempo'
import { useConnection } from 'wagmi'

function AddFunds() {
  const { address } = useConnection()
  const { mutate, isPending } = Hooks.faucet.useFundSync()

  return (
    <button
      onClick={() => { if (address) mutate({ account: address }) }}
      disabled={!address || isPending}
    >
      Add Funds
    </button>
  )
}
```

```tsx twoslash [wagmi.config.ts] filename="wagmi.config.ts"
// @noErrors
import { tempoModerato } from 'viem/chains'
import { createConfig, http } from 'wagmi'
import { tempoWallet } from 'wagmi/connectors'

export const config = createConfig({
  connectors: [tempoWallet()],
  chains: [tempoModerato],
  multiInjectedProviderDiscovery: false,
  transports: {
    [tempoModerato.id]: http(),
  },
})
```
:::

### Add custom fee-token logic

Add a fee-token selector to your payment form. The sender needs a balance in both the payment token and fee token when they differ.

**Interactive demo: Choose a fee token**

1. Add funds
2. Pay with fee token

Source: [tempoxyz/examples/tree/main/examples/payments](https://github.com/tempoxyz/examples/tree/main/examples/payments)

:::code-group
```tsx twoslash [PayWithFeeToken.tsx]
import { Hooks } from 'wagmi/tempo'
import { parseUnits } from 'viem'

const alphaUsd = '0x20c0000000000000000000000000000000000001'
const betaUsd = '0x20c0000000000000000000000000000000000002'
const thetaUsd = '0x20c0000000000000000000000000000000000003'
const ousd = '0x20c0000000000000000000006a37da5c996874be'

// @noErrors
function PayWithFeeToken() {
  const sendPayment = Hooks.token.useTransferSync()
  const metadata = Hooks.token.useGetMetadata({
    token: alphaUsd,
  })

  return (
    <form onSubmit={
      (event) => {
        event.preventDefault()
        const formData = new FormData(event.target as HTMLFormElement)

        const recipient = (formData.get('recipient') ||
          '0x742d35cc6634c0532925a3b844bc9e7595f0bebb') as `0x${string}`
        const feeToken = (formData.get('feeToken') ||
          alphaUsd) as `0x${string}`

        sendPayment.mutate({ // [!code hl]
          amount: parseUnits('100', metadata.data?.decimals ?? 6), // [!code hl]
          to: recipient, // [!code hl]
          token: alphaUsd, // [!code hl]
          feeToken: feeToken, // [!code hl]
        }) // [!code hl]
      }
    }>
      <div>
        <label htmlFor="recipient">Recipient address</label>
        <input type="text" name="recipient" placeholder="0x..." />
      </div>
      <div>
        <label htmlFor="feeToken">Fee token</label>
        <select name="feeToken">
          <option value={alphaUsd}>AlphaUSD</option>
          <option value={betaUsd}>BetaUSD</option>
          <option value={thetaUsd}>ThetaUSD</option>
          <option value={ousd}>OUSD</option>
        </select>
      </div>
      <button type="submit" disabled={sendPayment.isPending}>
        Send Payment
      </button>
    </form>
  )
}
```

```tsx twoslash [wagmi.config.ts] filename="wagmi.config.ts"
// @noErrors
import { tempoModerato } from 'viem/chains'
import { createConfig, http } from 'wagmi'
import { tempoWallet } from 'wagmi/connectors'

export const config = createConfig({
  connectors: [tempoWallet()],
  chains: [tempoModerato],
  multiInjectedProviderDiscovery: false,
  transports: {
    [tempoModerato.id]: http(),
  },
})
```
:::

### Display the fee-token receipt

Now that users can send payments with a specified fee token, you can link to the transaction receipt.

:::code-group
```tsx twoslash [PayWithFeeToken.tsx]
import { Hooks } from 'wagmi/tempo'
import { parseUnits } from 'viem'

const alphaUsd = '0x20c0000000000000000000000000000000000001'
const betaUsd = '0x20c0000000000000000000000000000000000002'
const thetaUsd = '0x20c0000000000000000000000000000000000003'
const ousd = '0x20c0000000000000000000006a37da5c996874be'

// @noErrors
function PayWithFeeToken() {
  const sendPayment = Hooks.token.useTransferSync()
  const metadata = Hooks.token.useGetMetadata({
    token: alphaUsd,
  })

  return (
    <>
      {/* ... your payment form ... */}
      {sendPayment.data && ( // [!code ++]
        <a href={`https://explore.testnet.tempo.xyz/tx/${sendPayment.data.receipt.transactionHash}`}> {/* [!code ++] */}
          View receipt {/* [!code ++] */}
        </a> {/* [!code ++] */}
      )} {/* [!code ++] */}
    </>
  )
}
```

```tsx twoslash [wagmi.config.ts] filename="wagmi.config.ts"
// @noErrors
import { tempoModerato } from 'viem/chains'
import { createConfig, http } from 'wagmi'
import { tempoWallet } from 'wagmi/connectors'

export const config = createConfig({
  connectors: [tempoWallet()],
  chains: [tempoModerato],
  multiInjectedProviderDiscovery: false,
  transports: {
    [tempoModerato.id]: http(),
  },
})
```
:::

### Next fee-token steps

Now that you have made a payment using a desired fee token, you can:

* Follow a guide on how to [sponsor user fees](https://tempo.xyz/developers/docs/guide/payments/sponsor-user-fees) to enable gasless transactions
* Learn more about [transaction fees](https://tempo.xyz/developers/docs/protocol/fees)
::::

### Viem

::::steps
### Set up a Viem client for fee tokens

First, we will set up a Viem client configured with Tempo.

```ts twoslash [viem.config.ts]
import { tempoModerato } from 'viem/chains'
import { Account, createClient } from 'viem/tempo'

const privateKey = process.env.TEMPO_PRIVATE_KEY
if (!privateKey || !/^0x[0-9a-fA-F]{64}$/.test(privateKey)) {
  throw new Error('Set TEMPO_PRIVATE_KEY to your funded test account private key')
}

export const client = createClient({
  account: Account.fromSecp256k1(privateKey as `0x${string}`),
  chain: tempoModerato,
  feeToken: '0x20c0000000000000000000000000000000000001',
})
```

:::info
For simplicity of the guide, this example uses a Private Key (Secp256k1) account instead of Passkeys (WebAuthn).
:::

<span id="add-testnet-fee-token-funds-1" />

### Fund the payment and fee tokens

Before you can pay fees in a token of your choice, you need to fund your account. In this guide you will be sending `AlphaUSD` (`0x20c000…0001`) and paying fees in `BetaUSD` (`0x20c000…0002`).

The built-in Tempo testnet faucet includes `AlphaUSD` and `BetaUSD` when funding.

**Interactive demo: Add Funds**

1. Add funds

:::code-group
```tsx twoslash [example.ts]
// @noErrors
import { client } from './viem.config'

await client.faucet.fundSync({
  account: client.account,
})
```

```tsx twoslash [viem.config.ts] filename="viem.config.ts"
// @noErrors
import { tempoModerato } from 'viem/chains'
import { Account, createClient } from 'viem/tempo'

const privateKey = process.env.TEMPO_PRIVATE_KEY
if (!privateKey || !/^0x[0-9a-fA-F]{64}$/.test(privateKey)) {
  throw new Error('Set TEMPO_PRIVATE_KEY to your funded test account private key')
}

export const client = createClient({
  account: Account.fromSecp256k1(privateKey as `0x${string}`),
  chain: tempoModerato,
  feeToken: '0x20c0000000000000000000000000000000000001',
})
```
:::

### Add custom fee-token logic

Now that you have `AlphaUSD` to send and `BetaUSD` to pay fees with, you can now add logic to send a payment with a specified fee token.

**Interactive demo: Choose a fee token**

1. Add funds
2. Pay with fee token

Source: [tempoxyz/examples/tree/main/examples/payments](https://github.com/tempoxyz/examples/tree/main/examples/payments)

:::code-group
```tsx twoslash [example.ts]
// @noErrors
import { parseUnits } from 'viem'
import { client } from './viem.config'

const alphaUsd = '0x20c0000000000000000000000000000000000001'
const betaUsd = '0x20c0000000000000000000000000000000000002'

const { receipt } = await client.token.transferSync({
  amount: parseUnits('100', 6),
  feeToken: betaUsd, // [!code hl]
  to: '0x742d35cc6634c0532925a3b844bc9e7595f0bebb',
  token: alphaUsd,
})
```

```tsx twoslash [viem.config.ts] filename="viem.config.ts"
// @noErrors
import { tempoModerato } from 'viem/chains'
import { Account, createClient } from 'viem/tempo'

const privateKey = process.env.TEMPO_PRIVATE_KEY
if (!privateKey || !/^0x[0-9a-fA-F]{64}$/.test(privateKey)) {
  throw new Error('Set TEMPO_PRIVATE_KEY to your funded test account private key')
}

export const client = createClient({
  account: Account.fromSecp256k1(privateKey as `0x${string}`),
  chain: tempoModerato,
  feeToken: '0x20c0000000000000000000000000000000000001',
})
```
:::
::::

## Set a default user fee token

You can also set a persistent default fee token for an account, so users don't need to specify `feeToken` on every transaction. Learn more about fee token preferences [here](https://tempo.xyz/developers/docs/protocol/fees/spec-fee#fee-token-preferences).

### Wagmi

```tsx twoslash
// @noErrors
// @errors: 2307
import { Hooks } from 'wagmi/tempo'

const { data: result, mutate } = Hooks.fee.useSetUserTokenSync()

// Call `mutate` in response to user action (e.g. button click, form submission)
mutate({
  token: '0x20c0000000000000000000000000000000000001',
})

if (result) console.log('Transaction hash:', result.receipt.transactionHash)
// @log: Transaction hash: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef
```

### Viem

```ts twoslash
// @noErrors
// @errors: 2307
import { client } from './viem.config'

const { receipt } = await client.fee.setUserTokenSync({
  token: '0x20c0000000000000000000000000000000000001',
})

console.log('Transaction hash:', receipt.transactionHash)

// @log: Transaction hash: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef
```

### Rust

:::code-group
```rust [example.rs]
use alloy::primitives::address;
use tempo_alloy::contracts::precompiles::IFeeManager;

mod provider;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let provider = provider::get_provider().await?;

    let fee_manager = IFeeManager::new(
        address!("0xFEEc000000000000000000000000000000000000"),
        &provider,
    );

    let receipt = fee_manager
        .setUserToken( // [!code hl]
            address!("0x20c0000000000000000000000000000000000001"), // [!code hl]
        ) // [!code hl]
        .send()
        .await?
        .get_receipt()
        .await?;

    println!("Transaction hash: {:?}", receipt.transaction_hash);

    Ok(())
}
```

```rust [provider.rs]
use alloy::providers::ProviderBuilder;
use alloy::signers::local::PrivateKeySigner;
use tempo_alloy::TempoNetwork;

pub async fn get_provider() -> Result<
    impl alloy::providers::Provider<TempoNetwork>,
    Box<dyn std::error::Error>,
> {
    let signer: PrivateKeySigner = std::env::var("PRIVATE_KEY")
        .expect("PRIVATE_KEY not set")
        .parse()?;

    let provider = ProviderBuilder::new_with_network::<TempoNetwork>()
        .wallet(signer)
        .connect(&std::env::var("RPC_URL").expect("RPC_URL not set"))
        .await?;

    Ok(provider)
}
```
:::

### Python

:::code-group
```python [example.py]
from pytempo import TempoTransaction
from pytempo.contracts import FeeManager, ALPHA_USD
from provider import w3, account

tx = TempoTransaction.create(
    chain_id=w3.eth.chain_id,
    gas_limit=100_000,
    max_fee_per_gas=w3.eth.gas_price * 2,
    max_priority_fee_per_gas=w3.eth.gas_price,
    nonce=w3.eth.get_transaction_count(account.address),
    calls=(
        FeeManager.set_user_token(ALPHA_USD), # [!code hl]
    ),
)

signed_tx = tx.sign(account.key.hex())
tx_hash = w3.eth.send_raw_transaction(signed_tx.encode())
```

```python [provider.py]
from web3 import Web3
from eth_account import Account

w3 = Web3(Web3.HTTPProvider("https://rpc.moderato.tempo.xyz"))
account = Account.from_key("0x...")
```
:::

### Go

:::code-group
```go [main.go]
package main

import (
   "context"
   "log"
   "math/big"

   "github.com/ethereum/go-ethereum/common"
   "github.com/tempoxyz/tempo-go/pkg/signer"
   "github.com/tempoxyz/tempo-go/pkg/transaction"
)

func main() {
   sgn, _ := signer.NewSigner("0x...")
   c := newClient()
   ctx := context.Background()

   nonce, _ := c.GetTransactionCount(ctx, sgn.Address().Hex())

   feeManager := common.HexToAddress("0xFEEc000000000000000000000000000000000000")
   token := common.HexToAddress("0x20c0000000000000000000000000000000000001")

   // setUserToken(address) selector: 0xe7897444
   data := make([]byte, 36) // [!code hl]
   data[0], data[1], data[2], data[3] = 0xe7, 0x89, 0x74, 0x44 // [!code hl]
   copy(data[16:36], token.Bytes()) // [!code hl]

   tx := transaction.NewBuilder(big.NewInt(transaction.ChainIdModerato)).
           SetNonce(nonce).
           SetGas(100_000).
           SetMaxFeePerGas(big.NewInt(25_000_000_000)).
           SetMaxPriorityFeePerGas(big.NewInt(1_000_000_000)).
           AddCall(feeManager, big.NewInt(0), data).
           Build()

   _ = transaction.SignTransaction(tx, sgn)
   serialized, _ := transaction.Serialize(tx, nil)
   txHash, _ := c.SendRawTransaction(ctx, serialized)

   log.Printf("Transaction hash: %s", txHash)
}
```

```go [provider.go]
package main

import (
	"os"

	"github.com/tempoxyz/tempo-go/pkg/client"
)

func newClient() *client.Client {
	return client.New(os.Getenv("TEMPO_RPC_URL"))
}
```
:::

### Cast

```bash
$ cast send \
  0xFEEc000000000000000000000000000000000000 \
  "setUserToken(address)" \
  0x20c0000000000000000000000000000000000001 \
  --rpc-url $TEMPO_RPC_URL \
  --private-key $PRIVATE_KEY # [!code hl]
```

### Solidity

```solidity
import {StdPrecompiles} from "tempo-std/StdPrecompiles.sol";

StdPrecompiles.TIP_FEE_MANAGER.setUserToken(0x20c0000000000000000000000000000000000001); // [!code hl]
```

## Fee-token learning resources

[Transaction Fees](https://tempo.xyz/developers/docs/protocol/fees): Learn more about transaction fees on Tempo
