@sfpro/sdk

How to manage a CFA flow

Create, update, and delete continuous token streams

Constant Flow Agreement (CFA) enables continuous token streams between accounts. When we talk about "streams" in Superfluid, we're referring to CFA flows.

Understanding Flowrates

Flowrates are measured in tokens per second. The SDK provides a utility to calculate flowrates:

import { calculateFlowrate } from "@sfpro/sdk/util"
import { parseEther } from "viem"

// Stream 100 tokens per month
const monthlyFlowrate = calculateFlowrate({
  amountWei: parseEther("100"),
  timeUnit: "month"
})

// Stream 1 token per day
const dailyFlowrate = calculateFlowrate({
  amountWei: parseEther("1"),
  timeUnit: "day"
})

// Stream 0.1 tokens per hour
const hourlyFlowrate = calculateFlowrate({
  amountWei: parseEther("0.1"),
  timeUnit: "hour"
})

Create a Flow

There are two ways to create a flow: setFlowrate (recommended) and createFlow.

setFlowrate is more versatile - it creates a new flow or updates an existing one:

import { useWriteCfaForwarder } from "@sfpro/sdk/hook"
import { calculateFlowrate } from "@sfpro/sdk/util"
import { parseEther } from "viem"

const superToken = "0x..." // USDCx address
const receiver = "0x..." // Recipient address
const flowrate = calculateFlowrate({
  amountWei: parseEther("100"),
  timeUnit: "month"
})

function CreateFlow() {
  const { writeContract: setFlowrate } = useWriteCfaForwarder({ 
    functionName: "setFlowrate", 
    args: [superToken, receiver, flowrate] 
  }) 
  
  return (
    <button onClick={() => setFlowrate?.()}>
      Stream 100 tokens/month to {receiver}
    </button>
  )
}
import { cfaForwarderAbi, cfaForwarderAddress } from "@sfpro/sdk/abi"
import { calculateFlowrate } from "@sfpro/sdk/util"
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 = "0x..." // USDCx address
const receiver = "0x..." // Recipient address
const flowrate = calculateFlowrate({
  amountWei: parseEther("100"),
  timeUnit: "month"
})

async function createFlow() {
  const hash = await walletClient.writeContract({ 
    address: cfaForwarderAddress[mainnet.id], 
    abi: cfaForwarderAbi, 
    functionName: "setFlowrate", 
    args: [superToken, receiver, flowrate] 
  }) 
  
  return hash
}
import { writeCfaForwarder } from "@sfpro/sdk/action"
import { calculateFlowrate } from "@sfpro/sdk/util"
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 receiver = "0x..." // Recipient address
const flowrate = calculateFlowrate({
  amountWei: parseEther("100"),
  timeUnit: "month"
})

async function createFlow() {
  const hash = await writeCfaForwarder(wagmiConfig, { 
    chainId: mainnet.id, 
    functionName: "setFlowrate", 
    args: [superToken, receiver, flowrate] 
  }) 
  
  return hash
}

Using createFlow

createFlow explicitly creates a new flow (fails if flow already exists):

import { useWriteCfaForwarder } from "@sfpro/sdk/hook"
import { calculateFlowrate } from "@sfpro/sdk/util"
import { parseEther } from "viem"
import { useAccount } from "wagmi"

const superToken = "0x..." // USDCx address
const receiver = "0x..." // Recipient address
const flowrate = calculateFlowrate({
  amountWei: parseEther("100"),
  timeUnit: "month"
})

function CreateNewFlow() {
  const { address } = useAccount()
  const { writeContract: createFlow } = useWriteCfaForwarder({ 
    functionName: "createFlow", 
    args: [superToken, address!, receiver, flowrate, "0x"] 
  }) 
  
  return (
    <button onClick={() => createFlow?.()}>
      Create new flow
    </button>
  )
}
import { cfaForwarderAbi, cfaForwarderAddress } from "@sfpro/sdk/abi"
import { calculateFlowrate } from "@sfpro/sdk/util"
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 = "0x..." // USDCx address
const receiver = "0x..." // Recipient address
const flowrate = calculateFlowrate({
  amountWei: parseEther("100"),
  timeUnit: "month"
})

async function createNewFlow() {
  const hash = await walletClient.writeContract({ 
    address: cfaForwarderAddress[mainnet.id], 
    abi: cfaForwarderAbi, 
    functionName: "createFlow", 
    args: [superToken, account.address, receiver, flowrate, "0x"] 
  }) 
  
  return hash
}
import { writeCfaForwarder } from "@sfpro/sdk/action"
import { calculateFlowrate } from "@sfpro/sdk/util"
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 receiver = "0x..." // Recipient address
const flowrate = calculateFlowrate({
  amountWei: parseEther("100"),
  timeUnit: "month"
})

async function createNewFlow() {
  const hash = await writeCfaForwarder(wagmiConfig, { 
    chainId: mainnet.id, 
    functionName: "createFlow", 
    args: [superToken, account.address, receiver, flowrate, "0x"] 
  }) 
  
  return hash
}

Update a Flow

To change the flowrate of an existing flow:

import { useWriteCfaForwarder } from "@sfpro/sdk/hook"
import { calculateFlowrate } from "@sfpro/sdk/util"
import { parseEther } from "viem"
import { useAccount } from "wagmi"

const superToken = "0x..." // USDCx address
const receiver = "0x..." // Recipient address
const newFlowrate = calculateFlowrate({
  amountWei: parseEther("200"),
  timeUnit: "month"
})

function UpdateFlow() {
  const { address } = useAccount()
  const { writeContract: updateFlow } = useWriteCfaForwarder({ 
    functionName: "updateFlow", 
    args: [superToken, address!, receiver, newFlowrate, "0x"] 
  }) 
  
  return (
    <button onClick={() => updateFlow?.()}>
      Update flow to 200 tokens/month
    </button>
  )
}
import { cfaForwarderAbi, cfaForwarderAddress } from "@sfpro/sdk/abi"
import { calculateFlowrate } from "@sfpro/sdk/util"
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 = "0x..." // USDCx address
const receiver = "0x..." // Recipient address
const newFlowrate = calculateFlowrate({
  amountWei: parseEther("200"), // Increased to 200 tokens/month
  timeUnit: "month"
})

async function updateFlow() {
  const hash = await walletClient.writeContract({ 
    address: cfaForwarderAddress[mainnet.id], 
    abi: cfaForwarderAbi, 
    functionName: "updateFlow", 
    args: [superToken, account.address, receiver, newFlowrate, "0x"] 
  }) 
  
  return hash
}
import { writeCfaForwarder } from "@sfpro/sdk/action"
import { calculateFlowrate } from "@sfpro/sdk/util"
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 receiver = "0x..." // Recipient address
const newFlowrate = calculateFlowrate({
  amountWei: parseEther("200"),
  timeUnit: "month"
})

async function updateFlow() {
  const hash = await writeCfaForwarder(wagmiConfig, { 
    chainId: mainnet.id, 
    functionName: "updateFlow", 
    args: [superToken, account.address, receiver, newFlowrate, "0x"] 
  }) 
  
  return hash
}

Delete a Flow

To stop a flow completely:

import { useWriteCfaForwarder } from "@sfpro/sdk/hook"
import { useAccount } from "wagmi"

const superToken = "0x..." // USDCx address
const receiver = "0x..." // Recipient address

function DeleteFlow() {
  const { address } = useAccount()
  const { writeContract: deleteFlow } = useWriteCfaForwarder({ 
    functionName: "deleteFlow", 
    args: [superToken, address!, receiver, "0x"] 
  }) 
  
  return (
    <button onClick={() => deleteFlow?.()}>
      Stop streaming
    </button>
  )
}
import { cfaForwarderAbi, cfaForwarderAddress } 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 = "0x..." // USDCx address
const receiver = "0x..." // Recipient address

async function deleteFlow() {
  const hash = await walletClient.writeContract({ 
    address: cfaForwarderAddress[mainnet.id], 
    abi: cfaForwarderAbi, 
    functionName: "deleteFlow", 
    args: [superToken, account.address, receiver, "0x"] 
  }) 
  
  return hash
}
import { writeCfaForwarder } 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 receiver = "0x..." // Recipient address

async function deleteFlow() {
  const hash = await writeCfaForwarder(wagmiConfig, { 
    chainId: mainnet.id, 
    functionName: "deleteFlow", 
    args: [superToken, account.address, receiver, "0x"] 
  }) 
  
  return hash
}

Reading Flow Information

Check the current flowrate between accounts:

import { useReadCfaForwarder } from "@sfpro/sdk/hook"

const superToken = "0x..." // USDCx address
const sender = "0x..." // Sender address
const receiver = "0x..." // Receiver address

function FlowInfo() {
  const { data: flowrate } = useReadCfaForwarder({ 
    functionName: "getFlowrate", 
    args: [superToken, sender, receiver] 
  }) 
  
  return (
    <div>
      Current flowrate: {flowrate?.toString()} tokens/second
    </div>
  )
}
import { cfaForwarderAbi, cfaForwarderAddress } from "@sfpro/sdk/abi"
import { createPublicClient, http } from "viem"
import { mainnet } from "viem/chains"

const client = createPublicClient({
  chain: mainnet,
  transport: http()
})

const superToken = "0x..." // USDCx address
const sender = "0x..." // Sender address
const receiver = "0x..." // Receiver address

async function getFlowInfo() {
  const flowrate = await client.readContract({ 
    address: cfaForwarderAddress[mainnet.id], 
    abi: cfaForwarderAbi, 
    functionName: "getFlowrate", 
    args: [superToken, sender, receiver] 
  }) 
  
  return flowrate
}
import { readCfaForwarder } from "@sfpro/sdk/action"
import { createConfig } from "@wagmi/core"
import { http } from "viem"
import { mainnet } from "viem/chains"

const wagmiConfig = createConfig({
  chains: [mainnet],
  transports: {
    [mainnet.id]: http()
  }
})

const superToken = "0x..." // USDCx address
const sender = "0x..." // Sender address
const receiver = "0x..." // Receiver address

async function getFlowInfo() {
  const flowrate = await readCfaForwarder(wagmiConfig, { 
    chainId: mainnet.id, 
    functionName: "getFlowrate", 
    args: [superToken, sender, receiver] 
  }) 
  
  return flowrate
}

Best Practices

  1. Use setFlowrate for most cases - it handles both create and update
  2. Check balance before creating flows to ensure sufficient funds
  3. Monitor flows to avoid liquidation
  4. Handle errors appropriately in your UI

Next Steps