@sfpro/sdk

Architecture

Understanding how the Superfluid SDK is structured

SDK Structure

The Superfluid SDK is organized into three main import paths, each serving a specific purpose.

Important: The SDK primarily exposes Forwarder contracts (CFA Forwarder, GDA Forwarder) which are the recommended way to interact with Superfluid. These contracts handle the complexity of encoding calls and routing them through the Host contract.

1. ABIs (@sfpro/sdk/abi)

Raw contract ABIs with enhanced type information:

import { 
  superTokenAbi,
  cfaForwarderAbi,
  gdaForwarderAbi,
  cfaForwarderAddress
} from "@sfpro/sdk/abi"

Use ABIs when:

  • Working directly with viem
  • Building custom abstractions
  • Need maximum flexibility

2. Actions (@sfpro/sdk/action)

Core functions for non-React environments:

import { 
  readSuperToken,
  writeSuperToken,
  readCfaForwarder,
  writeCfaForwarder
} from "@sfpro/sdk/action"

Use actions when:

  • Building server-side applications
  • Working in non-React frameworks
  • Need wagmi's conveniences without React

3. Hooks (@sfpro/sdk/hook)

React hooks for seamless integration:

import { 
  useReadSuperToken,
  useWriteSuperToken,
  useReadCfaForwarder,
  useWriteCfaForwarder
} from "@sfpro/sdk/hook"

Use hooks when:

  • Building React applications
  • Need reactive data updates
  • Want built-in loading/error states

Import Path Organization

Each import path has sub-paths for different contract categories:

Remember: All SDK exports use singular names (action not actions, hook not hooks, util not utils). This consistent naming makes imports predictable!

Root Imports (Main)

Default imports contain the most commonly used contracts:

  • Super Token operations
  • CFA Forwarder (streaming) - Recommended for flow management
  • GDA Forwarder (distributions) - Recommended for pool management

These forwarders are the simplest and safest way to interact with Superfluid.

Core Imports

import { hostAbi } from "@sfpro/sdk/abi/core"
import { readHost } from "@sfpro/sdk/action/core"
import { useReadHost } from "@sfpro/sdk/hook/core"

Protocol-level contracts for advanced use cases:

  • Host (core protocol)
  • CFA/GDA/IDA (agreement contracts - use forwarders instead when possible)
  • TOGA (liquidations)

Note: Direct interaction with agreement contracts (CFA, GDA, IDA) requires encoding calls through the Host. Use the forwarders instead for a simpler interface.

Automation Imports

import { vestingSchedulerAbi } from "@sfpro/sdk/abi/automation"
import { readVestingScheduler } from "@sfpro/sdk/action/automation"
import { useReadVestingScheduler } from "@sfpro/sdk/hook/automation"

Automation contracts for scheduled operations:

  • AutoWrap (automatic token wrapping)
  • VestingScheduler (token vesting)
  • FlowScheduler (scheduled streams)

Why Forwarders?

Superfluid's architecture requires all agreement operations to go through the Host contract. This means:

  1. Direct calls don't work: You cannot call CFA/GDA/IDA contracts directly
  2. Complex encoding: Operations must be encoded and sent via Host.callAgreement()
  3. Changed msg.sender: The Host becomes the msg.sender for operations

Forwarders solve these complexities by:

  • Handling all encoding automatically
  • Being trusted by the protocol (can use forwardBatchCall)
  • Providing a simple, direct interface
  • Maintaining the correct msg.sender context

Contract Naming Convention

Contract names are transformed for consistency:

  • CFAv1ForwardercfaForwarder
  • GeneralDistributionAgreementV1gda
  • ISuperTokensuperToken

Contract Interaction Patterns

// Simple and direct - handles all complexity
import { cfaForwarderAbi, cfaForwarderAddress } from "@sfpro/sdk/abi"
import { createWalletClient, http } from "viem"
import { mainnet } from "viem/chains"
// @noErrors
const walletClient = {} as any
// ---cut---

// Direct call to forwarder - recommended!
await walletClient.writeContract({
  address: cfaForwarderAddress[mainnet.id],
  abi: cfaForwarderAbi,
  functionName: "setFlowrate",
  args: ["0x...", "0x...", 1000n]
})

Advanced Pattern: Direct Host Interaction

// Complex and error-prone - avoid unless necessary
import { hostAbi } from "@sfpro/sdk/abi/core"
// @noErrors
const encodedCall = "0x..." // Complex encoding required
const walletClient = {} as any
// ---cut---

// Must encode agreement call data first
await walletClient.writeContract({
  address: "0x...", // Host address
  abi: hostAbi,
  functionName: "callAgreement",
  args: ["0x...", encodedCall, "0x"] // Agreement, encoded data, context
})

Best Practices

  1. Always use forwarders when possible:

    • CFA Forwarder for flows
    • GDA Forwarder for distributions
    • Only use Host/agreements for advanced cases
  2. Choose the right import path:

    • ABIs for direct viem usage
    • Actions for server/non-React
    • Hooks for React apps
  3. Use specific imports:

    // Good
    import { cfaForwarderAbi } from "@sfpro/sdk/abi"
    
    // Avoid
    import * as sdk from "@sfpro/sdk"
  4. Leverage type safety:

    • Let TypeScript guide your implementation
    • Use autocomplete for function names
    • Trust the type errors

Next Steps