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
- Decimals: Super Tokens always use 18 decimals internally
- Approvals: Required for wrapper tokens, not for native assets
- Gas: Leave some native tokens for gas fees
- Balances: Check balances before wrapping/unwrapping
Next Steps
- Learn to manage CFA flows
- Create GDA pools
- Explore batch operations