Skip to main content

Off-Chain

Introduction

The GDAv1Forwarder contract is a crucial component of the Superfluid protocol, enabling users to easily interact with Instant and Streaming Distributions (formerly known as General Distribution Agreement - GDA) through Super Tokens. This guide will walk you through the most important functions of the GDAv1Forwarder.sol contract and show you how to use them with ethers.js.

Click here to show GDAv1Forwarder contract

// SPDX-License-Identifier: AGPLv3
pragma solidity 0.8.19;

import {
ISuperfluid,
ISuperfluidToken,
} from "../interfaces/superfluid/ISuperfluid.sol";
import { ISuperfluidPool } from "../agreements/gdav1/SuperfluidPool.sol";
import {
IGeneralDistributionAgreementV1,
PoolConfig,
} from "../interfaces/agreements/gdav1/IGeneralDistributionAgreementV1.sol";
import { ForwarderBase } from "./ForwarderBase.sol";

/\*\*

- @title GDAv1Forwarder
- @author Superfluid
- The GDAv1Forwarder contract provides an easy to use interface to
- GeneralDistributionAgreementV1 specific functionality of Super Tokens.
- Instances of this contract can operate on the protocol only if configured as "trusted forwarder"
- by protocol governance.
\*/
contract GDAv1Forwarder is ForwarderBase {
IGeneralDistributionAgreementV1 internal immutable \_gda;

// is tied to a specific instance of host and agreement contracts at deploy time
constructor(ISuperfluid host) ForwarderBase(host) {
_gda = IGeneralDistributionAgreementV1(
address(
_host.getAgreementClass(keccak256("org.superfluid-finance.agreements.GeneralDistributionAgreement.v1"))
)
);
}

/**
* @dev Creates a new Superfluid Pool.
* @param token The Super Token address.
* @param admin The pool admin address.
* @param config The pool configuration (see PoolConfig in IGeneralDistributionAgreementV1.sol)
* @return success A boolean value indicating whether the pool was created successfully.
* @return pool The address of the deployed Superfluid Pool
*/
function createPool(ISuperfluidToken token, address admin, PoolConfig memory config)
external
returns (bool success, ISuperfluidPool pool)
{
pool = _gda.createPool(token, admin, config);
success = true;
}

/**
* @dev Updates the units of a pool member.
* @param pool The Superfluid Pool to update.
* @param memberAddress The address of the member to update.
* @param newUnits The new units of the member.
* @param userData User-specific data.
*/
function updateMemberUnits(ISuperfluidPool pool, address memberAddress, uint128 newUnits, bytes memory userData)
external
returns (bool success)
{
bytes memory callData = abi.encodeCall(_gda.updateMemberUnits, (pool, memberAddress, newUnits, new bytes(0)));

return _forwardBatchCall(address(_gda), callData, userData);
}

/**
* @dev Claims all tokens from the pool.
* @param pool The Superfluid Pool to claim from.
* @param memberAddress The address of the member to claim for.
* @param userData User-specific data.
*/
function claimAll(ISuperfluidPool pool, address memberAddress, bytes memory userData)
external
returns (bool success)
{
bytes memory callData = abi.encodeCall(_gda.claimAll, (pool, memberAddress, new bytes(0)));

return _forwardBatchCall(address(_gda), callData, userData);
}

/**
* @dev Connects a pool member to `pool`.
* @param pool The Superfluid Pool to connect.
* @param userData User-specific data.
* @return A boolean value indicating whether the connection was successful.
*/
function connectPool(ISuperfluidPool pool, bytes memory userData) external returns (bool) {
bytes memory callData = abi.encodeCall(_gda.connectPool, (pool, new bytes(0)));

return _forwardBatchCall(address(_gda), callData, userData);
}

/**
* @dev Disconnects a pool member from `pool`.
* @param pool The Superfluid Pool to disconnect.
* @param userData User-specific data.
* @return A boolean value indicating whether the disconnection was successful.
*/
function disconnectPool(ISuperfluidPool pool, bytes memory userData) external returns (bool) {
bytes memory callData = abi.encodeCall(_gda.disconnectPool, (pool, new bytes(0)));

return _forwardBatchCall(address(_gda), callData, userData);
}

/**
* @dev Tries to distribute `requestedAmount` amount of `token` from `from` to `pool`.
* @param token The Super Token address.
* @param from The address from which to distribute tokens.
* @param pool The Superfluid Pool address.
* @param requestedAmount The amount of tokens to distribute.
* @param userData User-specific data.
* @return A boolean value indicating whether the distribution was successful.
*/
function distribute(
ISuperfluidToken token,
address from,
ISuperfluidPool pool,
uint256 requestedAmount,
bytes memory userData
) external returns (bool) {
bytes memory callData = abi.encodeCall(_gda.distribute, (token, from, pool, requestedAmount, new bytes(0)));

return _forwardBatchCall(address(_gda), callData, userData);
}

/**
* @dev Tries to distribute flow at `requestedFlowRate` of `token` from `from` to `pool`.
* @param token The Super Token address.
* @param from The address from which to distribute tokens.
* @param pool The Superfluid Pool address.
* @param requestedFlowRate The flow rate of tokens to distribute.
* @param userData User-specific data.
* @return A boolean value indicating whether the distribution was successful.
*/
function distributeFlow(
ISuperfluidToken token,
address from,
ISuperfluidPool pool,
int96 requestedFlowRate,
bytes memory userData
) external returns (bool) {
bytes memory callData =
abi.encodeCall(_gda.distributeFlow, (token, from, pool, requestedFlowRate, new bytes(0)));

return _forwardBatchCall(address(_gda), callData, userData);
}

/**
* @dev Checks if the specified account is a pool.
* @param token The Super Token address.
* @param account The account address to check.
* @return A boolean value indicating whether the account is a pool.
*/
function isPool(ISuperfluidToken token, address account) external view virtual returns (bool) {
return _gda.isPool(token, account);
}

/**
* @dev Gets the GDA net flow rate for the specified account.
* @param token The Super Token address.
* @param account The account address.
* @return The gda net flow rate for the account.
*/
function getNetFlow(ISuperfluidToken token, address account) external view returns (int96) {
return _gda.getNetFlow(token, account);
}

/**
* @dev Gets the flow rate of tokens between the specified accounts.
* @param token The Super Token address.
* @param from The sender address.
* @param to The receiver address (the pool address).
* @return The flow distribution flow rate
*/
function getFlowDistributionFlowRate(ISuperfluidToken token, address from, ISuperfluidPool to)
external
view
returns (int96)
{
return _gda.getFlowRate(token, from, to);
}

/**
* @dev Gets the pool adjustment flow rate for the specified pool.
* @param pool The pool address.
* @return The pool adjustment flow rate.
*/
function getPoolAdjustmentFlowRate(address pool) external view virtual returns (int96) {
return _gda.getPoolAdjustmentFlowRate(pool);
}

/**
* @dev Estimates the actual flow rate for flow distribution to the specified pool.
* @param token The Super Token address.
* @param from The sender address.
* @param to The pool address.
* @param requestedFlowRate The requested flow rate.
* @return actualFlowRate
* @return totalDistributionFlowRate
*/
function estimateFlowDistributionActualFlowRate(
ISuperfluidToken token,
address from,
ISuperfluidPool to,
int96 requestedFlowRate
) external view returns (int96 actualFlowRate, int96 totalDistributionFlowRate) {
return _gda.estimateFlowDistributionActualFlowRate(token, from, to, requestedFlowRate);
}

/**
* @dev Estimates the actual amount for distribution to the specified pool.
* @param token The Super Token address.
* @param from The sender address.
* @param to The pool address.
* @param requestedAmount The requested amount.
* @return actualAmount The actual amount for distribution.
*/
function estimateDistributionActualAmount(
ISuperfluidToken token,
address from,
ISuperfluidPool to,
uint256 requestedAmount
) external view returns (uint256 actualAmount) {
return _gda.estimateDistributionActualAmount(token, from, to, requestedAmount);
}

/**
* @dev Checks if the specified member is connected to the pool.
* @param pool The Superfluid Pool address.
* @param member The member address.
* @return A boolean value indicating whether the member is connected to the pool.
*/
function isMemberConnected(ISuperfluidPool pool, address member) external view returns (bool) {
return _gda.isMemberConnected(pool, member);
}

/**
* @dev Gets the pool adjustment flow information for the specified pool.
* @param pool The pool address.
* @return The pool admin, pool ID, and pool adjustment flow rate.
*/
function getPoolAdjustmentFlowInfo(ISuperfluidPool pool) external view virtual returns (address, bytes32, int96) {
return _gda.getPoolAdjustmentFlowInfo(pool);
}

}

Setup and Connection

To interact with the GDAv1Forwarder contract, you'll first need to set up your project environment.

Installation

Install the necessary packages for either ethers.js or wagmi, depending on your preference.

npm install ethers

Connecting to the Contract

After installing the necessary packages, connect to the Ethereum blockchain using a provider and sign transactions with a signer.

import { ethers } from "ethers";

// Connect to an Ethereum node
const provider = new ethers.providers.JsonRpcProvider("YOUR_RPC_URL");

// Connect to the CFAv1Forwarder contract
const contractAddress = "YOUR_CONTRACT_ADDRESS";
const abi = "YOUR_CONTRACT_ABI";
const cfaForwarder = new ethers.Contract(contractAddress, abi, provider);
Where to find contract addresses?

You can find the addresses of the deployed GDAv1Forwarder contracts on the Console with all the addresses of all the public Superfluid contracts.

Where to find the contract ABI?

You can find the ABI of the GDAv1Forwarder contracts below:

Click here to show GDAv1Forwarder ABI

[
{
"inputs": [
{
"internalType": "contract ISuperfluid",
"name": "host",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [
{
"internalType": "contract ISuperfluidPool",
"name": "pool",
"type": "address"
},
{ "internalType": "address", "name": "memberAddress", "type": "address" },
{ "internalType": "bytes", "name": "userData", "type": "bytes" }
],
"name": "claimAll",
"outputs": [{ "internalType": "bool", "name": "success", "type": "bool" }],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract ISuperfluidPool",
"name": "pool",
"type": "address"
},
{ "internalType": "bytes", "name": "userData", "type": "bytes" }
],
"name": "connectPool",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract ISuperfluidToken",
"name": "token",
"type": "address"
},
{ "internalType": "address", "name": "admin", "type": "address" },
{
"components": [
{
"internalType": "bool",
"name": "transferabilityForUnitsOwner",
"type": "bool"
},
{
"internalType": "bool",
"name": "distributionFromAnyAddress",
"type": "bool"
}
],
"internalType": "struct PoolConfig",
"name": "config",
"type": "tuple"
}
],
"name": "createPool",
"outputs": [
{ "internalType": "bool", "name": "success", "type": "bool" },
{
"internalType": "contract ISuperfluidPool",
"name": "pool",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract ISuperfluidPool",
"name": "pool",
"type": "address"
},
{ "internalType": "bytes", "name": "userData", "type": "bytes" }
],
"name": "disconnectPool",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract ISuperfluidToken",
"name": "token",
"type": "address"
},
{ "internalType": "address", "name": "from", "type": "address" },
{
"internalType": "contract ISuperfluidPool",
"name": "pool",
"type": "address"
},
{
"internalType": "uint256",
"name": "requestedAmount",
"type": "uint256"
},
{ "internalType": "bytes", "name": "userData", "type": "bytes" }
],
"name": "distribute",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract ISuperfluidToken",
"name": "token",
"type": "address"
},
{ "internalType": "address", "name": "from", "type": "address" },
{
"internalType": "contract ISuperfluidPool",
"name": "pool",
"type": "address"
},
{ "internalType": "int96", "name": "requestedFlowRate", "type": "int96" },
{ "internalType": "bytes", "name": "userData", "type": "bytes" }
],
"name": "distributeFlow",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract ISuperfluidToken",
"name": "token",
"type": "address"
},
{ "internalType": "address", "name": "from", "type": "address" },
{
"internalType": "contract ISuperfluidPool",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "requestedAmount",
"type": "uint256"
}
],
"name": "estimateDistributionActualAmount",
"outputs": [
{ "internalType": "uint256", "name": "actualAmount", "type": "uint256" }
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract ISuperfluidToken",
"name": "token",
"type": "address"
},
{ "internalType": "address", "name": "from", "type": "address" },
{
"internalType": "contract ISuperfluidPool",
"name": "to",
"type": "address"
},
{ "internalType": "int96", "name": "requestedFlowRate", "type": "int96" }
],
"name": "estimateFlowDistributionActualFlowRate",
"outputs": [
{ "internalType": "int96", "name": "actualFlowRate", "type": "int96" },
{
"internalType": "int96",
"name": "totalDistributionFlowRate",
"type": "int96"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract ISuperfluidToken",
"name": "token",
"type": "address"
},
{ "internalType": "address", "name": "from", "type": "address" },
{
"internalType": "contract ISuperfluidPool",
"name": "to",
"type": "address"
}
],
"name": "getFlowDistributionFlowRate",
"outputs": [{ "internalType": "int96", "name": "", "type": "int96" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract ISuperfluidToken",
"name": "token",
"type": "address"
},
{ "internalType": "address", "name": "account", "type": "address" }
],
"name": "getNetFlow",
"outputs": [{ "internalType": "int96", "name": "", "type": "int96" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract ISuperfluidPool",
"name": "pool",
"type": "address"
}
],
"name": "getPoolAdjustmentFlowInfo",
"outputs": [
{ "internalType": "address", "name": "", "type": "address" },
{ "internalType": "bytes32", "name": "", "type": "bytes32" },
{ "internalType": "int96", "name": "", "type": "int96" }
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "pool", "type": "address" }
],
"name": "getPoolAdjustmentFlowRate",
"outputs": [{ "internalType": "int96", "name": "", "type": "int96" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract ISuperfluidPool",
"name": "pool",
"type": "address"
},
{ "internalType": "address", "name": "member", "type": "address" }
],
"name": "isMemberConnected",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract ISuperfluidToken",
"name": "token",
"type": "address"
},
{ "internalType": "address", "name": "account", "type": "address" }
],
"name": "isPool",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract ISuperfluidPool",
"name": "pool",
"type": "address"
},
{ "internalType": "address", "name": "memberAddress", "type": "address" },
{ "internalType": "uint128", "name": "newUnits", "type": "uint128" },
{ "internalType": "bytes", "name": "userData", "type": "bytes" }
],
"name": "updateMemberUnits",
"outputs": [{ "internalType": "bool", "name": "success", "type": "bool" }],
"stateMutability": "nonpayable",
"type": "function"
}
]

Key Functions

1. createPool

Creates a new Superfluid Pool.

  • Parameters:
    • token: Super Token address (ISuperfluidToken).
    • admin: Pool admin address.
    • config: Pool configuration (PoolConfig).
async function createPool(token, admin, config) {
const tx = await gdaForwarder.createPool(token, admin, config);
const receipt = await tx.wait();
return receipt.events.find((event) => event.event === "PoolCreated").args
.pool;
}

2. updateMemberUnits

Updates the units of a pool member.

  • Parameters:
    • pool: Superfluid Pool address (ISuperfluidPool).
    • memberAddress: Address of the member.
    • newUnits: New units of the member.
    • userData: User-specific data.
async function updateMemberUnits(pool, memberAddress, newUnits, userData) {
const tx = await gdaForwarder.updateMemberUnits(pool, memberAddress, newUnits, userData);
await tx.wait();
}

3. claimAll

Claims all tokens from the pool.

  • Parameters:
    • pool: Superfluid Pool address (ISuperfluidPool).
    • memberAddress: Address of the member.
    • userData: User-specific data.
async function claimAll(pool, memberAddress, userData) {
const tx = await gdaForwarder.claimAll(pool, memberAddress, userData);
await tx.wait();
}

4. connectPool

Connects a pool member to a pool.

  • Parameters:
    • pool: Superfluid Pool address (ISuperfluidPool).
    • userData: User-specific data.
async function connectPool(pool, userData) {
const tx = await gdaForwarder.connectPool(pool, userData);
await tx.wait();
}

5. disconnectPool

Disconnects a pool member from a pool.

  • Parameters:
    • pool: Superfluid Pool address (ISuperfluidPool).
    • userData: User-specific data.
async function disconnectPool(pool, userData) {
const tx = await gdaForwarder.disconnectPool(pool, userData);
await tx.wait();
}

This guide provides an overview of how to interact with the GDAv1Forwarder contract using ethers.js or Wagmi. For more information, check out the GDAv1Forwarder Technical Reference