Skip to main content

Import

import { useFeeOptions } from '@0xsequence/connect'
import { zeroAddress } from 'viem'

Usage

import { useFeeOptions } from '@0xsequence/connect'
import { useEffect, useState } from 'react'

function App() {
  // Use the hook with default balance checking
  // This returns the wallet balance for each fee option
  const [
    pendingFeeOptionConfirmation,
    confirmPendingFeeOption,
    rejectPendingFeeOption
  ] = useFeeOptions()

  // Or skip balance checking if needed:
  // const [pendingFeeOptionConfirmation, confirmPendingFeeOption, rejectPendingFeeOption] =
  //   useFeeOptions({ skipFeeBalanceCheck: true })

  const [selectedFeeOptionTokenAddress, setSelectedFeeOptionTokenAddress] = useState<string>()

  // Initialize with first option when fee options become available
  useEffect(() => {
    if (pendingFeeOptionConfirmation) {
      console.log('Pending fee options: ', pendingFeeOptionConfirmation.options)

      if (pendingFeeOptionConfirmation.options.length > 0) {
        const firstOption = pendingFeeOptionConfirmation.options[0]
        setSelectedFeeOptionTokenAddress(firstOption.token.contractAddress || '')
      }
    }
  }, [pendingFeeOptionConfirmation])

  const handleConfirmFee = (tokenAddress: string) => {
    if (pendingFeeOptionConfirmation) {
      confirmPendingFeeOption(pendingFeeOptionConfirmation.id, tokenAddress)
    }
  }

  const handleRejectFee = () => {
    if (pendingFeeOptionConfirmation) {
      rejectPendingFeeOption(pendingFeeOptionConfirmation.id)
    }
  }

  if (pendingFeeOptionConfirmation) {
    return (
      <div>
        <h2>Select Fee Payment Token</h2>
        <div>
          {pendingFeeOptionConfirmation.options.map((option) => (
            <div key={option.token.contractAddress || 'native'}>
              <input
                type="radio"
                name="feeOption"
                checked={selectedFeeOptionTokenAddress === (option.token.contractAddress || zeroAddress)}
                onChange={() => setSelectedFeeOptionTokenAddress(option.token.contractAddress || zeroAddress)}
              />
              <label>
                {option.token.symbol} - {option.token.contractAddress || 'Native Token'}
                {'balanceFormatted' in option &&
                  ` (Balance: ${option.balanceFormatted} ${option.token.symbol})`}
                {'hasEnoughBalanceForFee' in option && !option.hasEnoughBalanceForFee &&
                  ' (Insufficient Balance)'}
              </label>
            </div>
          ))}
        </div>
        <div>
          <button
            onClick={() => handleConfirmFee(selectedFeeOptionTokenAddress || zeroAddress)}
            disabled={!selectedFeeOptionTokenAddress}
          >
            Confirm
          </button>
          <button onClick={handleRejectFee}>Cancel</button>
        </div>
      </div>
    )
  }

  return <div>No pending fee confirmation</div>
}

Parameters

interface FeeOptionsConfig {
  skipFeeBalanceCheck?: boolean
}
ParameterTypeDefaultDescription
skipFeeBalanceCheckbooleanfalseWhen true, skips wallet balance verification for fee options.

Return Type

type UseFeeOptionsReturnType = [
  pendingFeeOptionConfirmation: FeeOptionConfirmation | undefined,
  confirmPendingFeeOption: (id: string, feeTokenAddress: string) => void,
  rejectPendingFeeOption: (id: string) => void
]

FeeOptionConfirmation Type

type FeeOptionConfirmation = {
  id: string                                       // Unique identifier for the fee confirmation
  options: Relayer.FeeOption[] | FeeOptionExtended[] // Available fee options with balance information
  chainId: number                                  // Chain ID where the transaction will be executed
}

FeeOptionExtended Type

When skipFeeBalanceCheck is false (the default), fee options are extended with balance data:
type FeeOptionExtended = Relayer.FeeOption & {
  balance: string                  // Raw balance string
  balanceFormatted: string         // Formatted balance with proper decimals
  hasEnoughBalanceForFee: boolean  // Indicates if wallet has enough balance
}
This hook provides shared state management across multiple hook instances, ensuring consistent behavior when multiple components use the hook simultaneously.