@sfpro/sdk

How to wrap/unwrap Super Tokens

Learn how to convert between regular tokens and Super Tokens

Super Tokens are the foundation of Superfluid. Before you can stream or distribute tokens, you need to convert regular tokens into Super Tokens.

All Super Token amounts use 18 decimals internally, regardless of the underlying token's decimals.

Wrapper Super Tokens

Most Super Tokens are created by wrapping existing ERC-20 tokens.

Wrapping (Upgrade)

To wrap tokens, you first need to approve the Super Token contract to spend your underlying tokens:

import { useWriteSuperToken } from "@sfpro/sdk/hook"
import { useWriteContract } from "wagmi"
import { erc20Abi } from "viem"
import { parseUnits } from "viem"

const underlyingToken = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" // USDC
const superToken = "0x..." // USDCx address
const amount = parseUnits("100", 6) // 100 USDC

function WrapTokens() {
  // Step 1: Approve hook
  const { writeContract: approve } = useWriteContract()
  
  // Step 2: Upgrade hook
  const { writeContract: upgrade } = useWriteSuperToken({ 
    address: superToken, 
    functionName: "upgrade", 
    args: [amount] 
  }) 
  
  const handleWrap = async () => {
    // First approve
    await approve({
      address: underlyingToken,
      abi: erc20Abi,
      functionName: "approve",
      args: [superToken, amount]
    })
    
    // Then upgrade
    await upgrade?.()
  }
  
  return (
    <button onClick={handleWrap}>
      Wrap 100 USDC to USDCx
    </button>
  )
}
import { superTokenAbi } from "@sfpro/sdk/abi"
import { erc20Abi, createWalletClient, http, parseUnits } from "viem"
import { mainnet } from "viem/chains"
import { privateKeyToAccount } from "viem/accounts"

const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`)
const walletClient = createWalletClient({
  account,
  chain: mainnet,
  transport: http()
})

const underlyingToken = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" // USDC
const superToken = "0x1234567890abcdef..." // USDCx address - replace with actual address
const amount = parseUnits("100", 6) // 100 USDC (6 decimals)

async function wrapTokens() {
  // Step 1: Approve the Super Token to spend underlying tokens
  const approveHash = await walletClient.writeContract({
    address: underlyingToken,
    abi: erc20Abi,
    functionName: "approve",
    args: [superToken, amount]
  })
  
  // Step 2: Upgrade tokens to Super Tokens
  const upgradeHash = await walletClient.writeContract({ 
    address: superToken, 
    abi: superTokenAbi, 
    functionName: "upgrade", 
    args: [amount] 
  }) 
  
  return { approveHash, upgradeHash }
}
import { writeSuperToken } from "@sfpro/sdk/action"
import { erc20Abi } from "viem"
import { createConfig } from "@wagmi/core"
import { createWalletClient, http, parseUnits } from "viem"
import { mainnet } from "viem/chains"
import { privateKeyToAccount } from "viem/accounts"

const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`)
const wagmiConfig = createConfig({
  chains: [mainnet],
  client({ chain }) {
    return createWalletClient({
      account,
      chain,
      transport: http(),
    })
  },
})

const underlyingToken = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" // USDC
const superToken = "0x..." // USDCx address
const amount = parseUnits("100", 6) // 100 USDC

async function wrapTokens() {
  // Step 1: Approve (using viem directly)
  const walletClient = wagmiConfig.getClient({ chainId: mainnet.id })
  const approveHash = await walletClient.writeContract({
    address: underlyingToken,
    abi: erc20Abi,
    functionName: "approve",
    args: [superToken, amount]
  })
  
  // Step 2: Upgrade tokens
  const upgradeHash = await writeSuperToken(wagmiConfig, { 
    chainId: mainnet.id, 
    address: superToken, 
    functionName: "upgrade", 
    args: [amount] 
  }) 
  
  return { approveHash, upgradeHash }
}

Unwrapping (Downgrade)

Converting Super Tokens back to regular tokens:

import { useWriteSuperToken } from "@sfpro/sdk/hook"
import { parseEther } from "viem"

const superToken = "0x..." // USDCx address
const amount = parseEther("100") // 100 Super Tokens

function UnwrapTokens() {
  const { writeContract: downgrade } = useWriteSuperToken({ 
    address: superToken, 
    functionName: "downgrade", 
    args: [amount] 
  }) 
  
  return (
    <button onClick={() => downgrade?.()}>
      Unwrap 100 USDCx to USDC
    </button>
  )
}
import { superTokenAbi } from "@sfpro/sdk/abi"
import { createWalletClient, http, parseEther } from "viem"
import { mainnet } from "viem/chains"
import { privateKeyToAccount } from "viem/accounts"

const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`)
const walletClient = createWalletClient({
  account,
  chain: mainnet,
  transport: http()
})

const superToken = "0x1234567890abcdef..." // USDCx address - replace with actual address
const amount = parseEther("100") // 100 Super Tokens (always 18 decimals)

async function unwrapTokens() {
  const hash = await walletClient.writeContract({ 
    address: superToken, 
    abi: superTokenAbi, 
    functionName: "downgrade", 
    args: [amount] 
  }) 
  
  return hash
}
import { writeSuperToken } from "@sfpro/sdk/action"
import { createConfig } from "@wagmi/core"
import { createWalletClient, http, parseEther } from "viem"
import { mainnet } from "viem/chains"
import { privateKeyToAccount } from "viem/accounts"

const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`)
const wagmiConfig = createConfig({
  chains: [mainnet],
  client({ chain }) {
    return createWalletClient({
      account,
      chain,
      transport: http(),
    })
  },
})

const superToken = "0x..." // USDCx address
const amount = parseEther("100") // 100 Super Tokens

async function unwrapTokens() {
  const hash = await writeSuperToken(wagmiConfig, { 
    chainId: mainnet.id, 
    address: superToken, 
    functionName: "downgrade", 
    args: [amount] 
  }) 
  
  return hash
}

Native Asset Super Tokens

Native Super Tokens (like ETHx) have special functions for wrapping/unwrapping:

Wrapping Native Assets

import { useWriteSuperToken } from "@sfpro/sdk/hook"
import { parseEther } from "viem"

const ethx = "0x..." // ETHx address
const amount = parseEther("1") // 1 ETH

function WrapETH() {
  const { writeContract: upgrade } = useWriteSuperToken({ 
    address: ethx, 
    functionName: "upgradeByETH", 
    value: amount 
  }) 
  
  return (
    <button onClick={() => upgrade?.()}>
      Wrap 1 ETH to ETHx
    </button>
  )
}
import { superTokenAbi } from "@sfpro/sdk/abi"
import { createWalletClient, http, parseEther } from "viem"
import { mainnet } from "viem/chains"
import { privateKeyToAccount } from "viem/accounts"

const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`)
const walletClient = createWalletClient({
  account,
  chain: mainnet,
  transport: http()
})

const ethx = "0xabcdef1234567890..." // ETHx address - replace with actual address
const amount = parseEther("1") // 1 ETH

async function wrapETH() {
  const hash = await walletClient.writeContract({ 
    address: ethx, 
    abi: superTokenAbi, 
    functionName: "upgradeByETH", 
    value: amount // Note: value, not args!
  }) 
  
  return hash
}
import { writeSuperToken } from "@sfpro/sdk/action"
import { createConfig } from "@wagmi/core"
import { createWalletClient, http, parseEther } from "viem"
import { mainnet } from "viem/chains"
import { privateKeyToAccount } from "viem/accounts"

const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`)
const wagmiConfig = createConfig({
  chains: [mainnet],
  client({ chain }) {
    return createWalletClient({
      account,
      chain,
      transport: http(),
    })
  },
})

const ethx = "0x..." // ETHx address
const amount = parseEther("1") // 1 ETH

async function wrapETH() {
  const hash = await writeSuperToken(wagmiConfig, { 
    chainId: mainnet.id, 
    address: ethx, 
    functionName: "upgradeByETH", 
    value: amount 
  }) 
  
  return hash
}

Unwrapping Native Assets

import { useWriteSuperToken } from "@sfpro/sdk/hook"
import { parseEther } from "viem"

const ethx = "0x..." // ETHx address
const amount = parseEther("1") // 1 ETHx

function UnwrapETH() {
  const { writeContract: downgrade } = useWriteSuperToken({ 
    address: ethx, 
    functionName: "downgradeByETH", 
    args: [amount] 
  }) 
  
  return (
    <button onClick={() => downgrade?.()}>
      Unwrap 1 ETHx to ETH
    </button>
  )
}
import { superTokenAbi } from "@sfpro/sdk/abi"
import { createWalletClient, http, parseEther } from "viem"
import { mainnet } from "viem/chains"
import { privateKeyToAccount } from "viem/accounts"

const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`)
const walletClient = createWalletClient({
  account,
  chain: mainnet,
  transport: http()
})

const ethx = "0xabcdef1234567890..." // ETHx address - replace with actual address
const amount = parseEther("1") // 1 ETHx

async function unwrapETH() {
  const hash = await walletClient.writeContract({ 
    address: ethx, 
    abi: superTokenAbi, 
    functionName: "downgradeByETH", 
    args: [amount] 
  }) 
  
  return hash
}
import { writeSuperToken } from "@sfpro/sdk/action"
import { createConfig } from "@wagmi/core"
import { createWalletClient, http, parseEther } from "viem"
import { mainnet } from "viem/chains"
import { privateKeyToAccount } from "viem/accounts"

const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`)
const wagmiConfig = createConfig({
  chains: [mainnet],
  client({ chain }) {
    return createWalletClient({
      account,
      chain,
      transport: http(),
    })
  },
})

const ethx = "0x..." // ETHx address
const amount = parseEther("1") // 1 ETHx

async function unwrapETH() {
  const hash = await writeSuperToken(wagmiConfig, { 
    chainId: mainnet.id, 
    address: ethx, 
    functionName: "downgradeByETH", 
    args: [amount] 
  }) 
  
  return hash
}

Important Notes

  1. Decimals: Super Tokens always use 18 decimals internally
  2. Approvals: Required for wrapper tokens, not for native assets
  3. Gas: Leave some native tokens for gas fees
  4. Balances: Check balances before wrapping/unwrapping

Next Steps