> ## Documentation Index
> Fetch the complete documentation index at: https://www.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Gasless Transactions

<Card title="Recommended: JavaScript SDK with React Hooks" icon="react" color="#4779FE">
  For new React apps, we recommend the JavaScript SDK with React Hooks (`@dynamic-labs-sdk/react-hooks`) instead of the legacy React SDK documented here. The JS SDK comes with many benefits such as a much smaller bundle size and other optimizations. Use the [React quickstart (JavaScript SDK)](/docs/javascript/reference/react-quickstart) to get started.
</Card>

Tempo supports fee sponsorship through the `awaitingFeePayer` flag. This allows third parties to pay transaction fees on behalf of users, enabling gasless experiences.

## How Fee Sponsorship Works

1. User signs a transaction with `awaitingFeePayer: true`
2. Transaction is sent to a fee payer service
3. Fee payer adds their signature and broadcasts the transaction
4. User's transaction executes without paying gas fees

## Transaction Expiry

When using fee sponsorship, you can set transaction validity windows:

| Parameter     | Type     | Description                                              |
| ------------- | -------- | -------------------------------------------------------- |
| `validAfter`  | `number` | Unix timestamp after which the transaction becomes valid |
| `validBefore` | `number` | Unix timestamp after which the transaction expires       |

## Example: Creating a Sponsored Transaction

```tsx theme={"system"}
import { FC, useState } from 'react';
import { useDynamicContext } from '@dynamic-labs/sdk-react-core';
import { isTempoWallet } from '@dynamic-labs/tempo';
import { encodeFunctionData, erc20Abi, Hex } from 'viem';

const GaslessTransactionButton: FC = () => {
  const { primaryWallet } = useDynamicContext();
  const [signedTx, setSignedTx] = useState<string | null>(null);

  const onCreateGaslessTx = async () => {
    if (!primaryWallet || !isTempoWallet(primaryWallet)) {
      throw new Error('Tempo wallet not found');
    }

    const publicClient = await primaryWallet.getPublicClient();
    const walletClient = await primaryWallet.getWalletClient();

    const fromAddress = primaryWallet.address as Hex;
    const toAddress = '0xRecipient...' as Hex;
    const tokenAddress = '0xToken...' as Hex;
    const amount = BigInt(1000000);

    // Encode transfer
    const callData = encodeFunctionData({
      abi: erc20Abi,
      functionName: 'transfer',
      args: [toAddress, amount],
    });

    // Get nonce
    const nonce = await publicClient.getTransactionCount({
      address: fromAddress,
    });

    const gasPrice = await publicClient.getGasPrice();
    const chainId = await publicClient.getChainId();

    // Set validity window (valid for next 10 minutes)
    const now = Math.floor(Date.now() / 1000);
    const validAfter = now;
    const validBefore = now + 600; // 10 minutes

    // Build transaction with fee sponsorship flag
    const tempoTx = {
      calls: [
        {
          data: callData,
          to: tokenAddress,
          value: BigInt(0),
        },
      ],
      chainId,
      gas: BigInt(300000),
      maxFeePerGas: gasPrice,
      maxPriorityFeePerGas: gasPrice / BigInt(10),
      nonce,
      // Fee sponsorship flags
      awaitingFeePayer: true,
      validAfter,
      validBefore,
    };

    // Sign the transaction (but don't broadcast)
    const signature = await walletClient.signTransaction(tempoTx);
    setSignedTx(signature);

    console.log('Signed transaction (awaiting fee payer):', signature);

    // In production, send this to your fee payer service:
    // await fetch('/api/sponsor-transaction', {
    //   method: 'POST',
    //   body: JSON.stringify({ signedTransaction: signature }),
    // });
  };

  return (
    <div>
      <button onClick={onCreateGaslessTx}>Create Gasless Transaction</button>
      {signedTx && (
        <div>
          <p>Signed transaction ready for sponsorship:</p>
          <code style={{ wordBreak: 'break-all', fontSize: '12px' }}>
            {signedTx}
          </code>
        </div>
      )}
    </div>
  );
};
```

## Fee Payer Service (Server-Side)

The fee payer service receives the signed transaction, adds its signature for the fee payment, and broadcasts:

```typescript theme={"system"}
// Example server-side fee payer (pseudocode)
async function sponsorTransaction(signedUserTx: string) {
  // 1. Validate the transaction
  const tx = decodeTransaction(signedUserTx);

  // 2. Check if we want to sponsor this transaction
  if (!shouldSponsor(tx)) {
    throw new Error('Transaction not eligible for sponsorship');
  }

  // 3. Add fee payer signature
  const feePayerWallet = getFeePayerWallet();
  const sponsoredTx = await feePayerWallet.signAsFeePayer(signedUserTx);

  // 4. Broadcast
  const hash = await publicClient.sendRawTransaction({
    serializedTransaction: sponsoredTx,
  });

  return hash;
}
```

## Best Practices

1. **Set reasonable validity windows**: Use `validAfter` and `validBefore` to prevent stale transactions from being broadcast
2. **Validate transactions server-side**: Check transaction parameters before sponsoring
3. **Rate limit**: Implement rate limiting on your fee payer service to prevent abuse
4. **Monitor spending**: Track gas expenditure to manage sponsor costs
