Skip to content

Managing Fee Liquidity

The Fee AMM converts transaction fees between stablecoins when users pay in a different token than the validator prefers. This guide shows you how to add and remove liquidity to enable fee conversions.

Manage Fee Liquidity

demo
1
Create an account, or use an existing one.
2
Add testnet funds to your account.
3
Create & deploy a token to testnet.
4
Mint 100 pathUSD of Fee Liquidity for your token.
5
View Fee AMM pool for your token.
6
Burn 10 LP tokens from your token pool.
pnpx gitpick tempoxyz/tempo-ts/tree/main/examples/issuance

Steps

Check pool reserves

Before adding liquidity, check the current pool reserves to understand the pool state.

ManageFeeLiquidity.tsx
import { Hooks } from 'tempo.ts/wagmi'
import { formatUnits } from 'viem'
 
const userToken = '0x20c0000000000000000000000000000000000002' // BetaUSD
const validatorToken = '0x20c0000000000000000000000000000000000001' // AlphaUSD
 
function ManageFeeLiquidity() {
  const { data: pool } = Hooks.amm.usePool({ 
    userToken, 
    validatorToken, 
  }) 
 
  return (
    <div>
      <div>User token reserves: {formatUnits(pool?.reserveUserToken ?? 0n, 6)}</div>
      <div>Validator token reserves: {formatUnits(pool?.reserveValidatorToken ?? 0n, 6)}</div>
    </div>
  )
}

Add liquidity

Add validator token to the pool to receive LP tokens representing your share. The first liquidity provider to a new pool must burn 1,000 units of liquidity. This costs approximately 0.002 USD and prevents attacks on pool reserves. Learn more in the Fee AMM specification.

ManageFeeLiquidity.tsx
import { Hooks } from 'tempo.ts/wagmi'
import { formatUnits, parseUnits } from 'viem'
import { useConnection } from 'wagmi'
 
const userToken = '0x20c0000000000000000000000000000000000002' // BetaUSD
const validatorToken = '0x20c0000000000000000000000000000000000001' // AlphaUSD
 
function ManageFeeLiquidity() {
  const { address } = useConnection()
 
  const { data: pool } = Hooks.amm.usePool({
    userToken,
    validatorToken,
  })
 
  const mintLiquidity = Hooks.amm.useMintSync() 
 
  return (
    <div>
      <div>User token reserves: {formatUnits(pool?.reserveUserToken ?? 0n, 6)}</div>
      <div>Validator token reserves: {formatUnits(pool?.reserveValidatorToken ?? 0n, 6)}</div>
      <button type="button" onClick={() => { 
        if (!address) return
        mintLiquidity.mutate({ 
          userTokenAddress: userToken, 
          validatorTokenAddress: validatorToken, 
          validatorTokenAmount: parseUnits('100', 6), 
          to: address, 
          feeToken: validatorToken, 
        }) 
      }}> 
        Add Liquidity 
      </button> 
    </div>
  )
}

Check your LP balance

View your LP token balance to see your share of the pool.

ManageFeeLiquidity.tsx
import { Hooks } from 'tempo.ts/wagmi'
import { formatUnits, parseUnits } from 'viem'
import { useConnection } from 'wagmi'
 
const userToken = '0x20c0000000000000000000000000000000000002' // BetaUSD
const validatorToken = '0x20c0000000000000000000000000000000000001' // AlphaUSD
 
function ManageFeeLiquidity() {
  const { address } = useConnection()
 
  const { data: pool } = Hooks.amm.usePool({
    userToken,
    validatorToken,
  })
 
  const { data: balance } = Hooks.amm.useLiquidityBalance({ 
    address, 
    userToken, 
    validatorToken, 
  }) 
 
  const mintLiquidity = Hooks.amm.useMintSync()
 
  return (
    <div>
      <div>LP token balance: {formatUnits(balance ?? 0n, 6)}</div>  
      <div>User token reserves: {formatUnits(pool?.reserveUserToken ?? 0n, 6)}</div>
      <div>Validator token reserves: {formatUnits(pool?.reserveValidatorToken ?? 0n, 6)}</div>
      <button type="button" onClick={() => {
        if (!address) return
        mintLiquidity.mutate({
          userTokenAddress: userToken,
          validatorTokenAddress: validatorToken,
          validatorTokenAmount: parseUnits('100', 6),
          to: address,
          feeToken: validatorToken,
        })
      }}>
        Add Liquidity
      </button>
    </div>
  )
}

Remove liquidity

Burn LP tokens to withdraw your share of pool reserves plus accumulated fees.

ManageFeeLiquidity.tsx
import { Hooks } from 'tempo.ts/wagmi'
import { formatUnits, parseUnits } from 'viem'
import { useConnection } from 'wagmi'
 
const userToken = '0x20c0000000000000000000000000000000000002' // BetaUSD
const validatorToken = '0x20c0000000000000000000000000000000000001' // AlphaUSD
 
function ManageFeeLiquidity() {
  const { address } = useConnection()
 
  const { data: pool } = Hooks.amm.usePool({
    userToken,
    validatorToken,
  })
 
  const { data: balance } = Hooks.amm.useLiquidityBalance({
    address,
    userToken,
    validatorToken,
  })
 
  const mintLiquidity = Hooks.amm.useMintSync()
  const burnLiquidity = Hooks.amm.useBurnSync() 
 
  return (
    <div>
      <div>LP token balance: {formatUnits(balance ?? 0n, 6)}</div>
      <div>User token reserves: {formatUnits(pool?.reserveUserToken ?? 0n, 6)}</div>
      <div>Validator token reserves: {formatUnits(pool?.reserveValidatorToken ?? 0n, 6)}</div>
      <button type="button" onClick={() => {
        if (!address) return
        mintLiquidity.mutate({
          userTokenAddress: userToken,
          validatorTokenAddress: validatorToken,
          validatorTokenAmount: parseUnits('100', 6),
          to: address,
          feeToken: validatorToken,
        })
      }}>
        Add Liquidity
      </button>
      <button type="button" onClick={() => { 
        if (!address) return
        burnLiquidity.mutate({ 
          userToken, 
          validatorToken, 
          liquidity: parseUnits('10', 6), // Burn 10 LP tokens 
          to: address, 
        }) 
      }}> 
        Remove Liquidity 
      </button> 
    </div>
  )
}

Recipes

Monitor pool utilization

Track fee swap activity to understand pool utilization and revenue.

MonitorSwaps.tsx
import * as React from 'react'
import { Hooks } from 'tempo.ts/wagmi'
import { formatUnits } from 'viem'
 
const userToken = '0x20c0000000000000000000000000000000000002' // BetaUSD
const validatorToken = '0x20c0000000000000000000000000000000000001' // AlphaUSD
 
function MonitorSwaps() {
  const [swaps, setSwaps] = React.useState<any[]>([])
 
  Hooks.amm.useWatchFeeSwap({ 
    userToken, 
    validatorToken, 
    onLogs(logs) { 
      for (const log of logs) { 
        setSwaps((prev) => [...prev, { 
          amountIn: formatUnits(log.args.amountIn, 6), 
          amountOut: formatUnits(log.args.amountOut, 6), 
          revenue: formatUnits(log.args.amountIn * 30n / 10000n, 6), 
        }]) 
      } 
    }, 
  }) 
 
  return (
    <div>
      {swaps.map((swap, i) => (
        <div key={i}>
          Swap: {swap.amountIn}{swap.amountOut} (LP revenue: {swap.revenue})
        </div>
      ))}
    </div>
  )
}

Rebalance pools

You can rebalance pools by swapping validator tokens for accumulated user tokens at a fixed rate. Rebalancing restores validator token reserves and enables continued fee conversions. Learn more here.

RebalancePool.tsx
import { Hooks } from 'tempo.ts/wagmi'
import { formatUnits, parseUnits } from 'viem'
import { useConnection } from 'wagmi'
 
const userToken = '0x20c0000000000000000000000000000000000002' // BetaUSD
const validatorToken = '0x20c0000000000000000000000000000000000001' // AlphaUSD
 
function RebalancePool() {
  const { address } = useConnection()
 
  const { data: pool } = Hooks.amm.usePool({
    userToken,
    validatorToken,
  })
 
  const rebalance = Hooks.amm.useRebalanceSwapSync() 
 
  return (
    <div>
      <div>User token reserves: {formatUnits(pool?.reserveUserToken ?? 0n, 6)}</div>
      <div>Validator token reserves: {formatUnits(pool?.reserveValidatorToken ?? 0n, 6)}</div>
      <button type="button" onClick={() => { 
        if (!address || !pool) return
        // Swap validator token for user token at 0.9985 rate 
        rebalance.mutate({ 
          userToken, 
          validatorToken, 
          amountOut: pool.reserveUserToken, // Amount of user token to receive 
          to: address, 
        }) 
      }}> 
        Rebalance 
      </button> 
    </div>
  )
}

Best Practices

Monitor pool reserves

Regularly check pool reserves to ensure sufficient liquidity for fee conversions. Low reserves can prevent transactions from being processed.

Add liquidity when:

  • Transaction rates increase for a given userToken
  • Reserve levels drop below expected daily volume
  • Multiple validators begin preferring the same token

Maintain adequate reserves

As an issuer, keep sufficient validator token reserves to handle expected transaction volume. Consider your anticipated fee conversion volume when determining reserve levels.

For new token pairs, provide the entire initial amount in the validator token. The pool naturally accumulates user tokens as fees are paid.

Deploy liquidity strategically

Focus liquidity on pools with:

  • High transaction volume and frequent fee conversions
  • New stablecoins that need initial bootstrapping
  • Validator tokens preferred by multiple validators

Learning Resources