Create, Update, and Delete Flows
This guide covers various methods for managing flows directly on the Superfluid protocol. It includes creating, updating, and deleting flows, on chain, from another smart contracts. This guide does NOT cover:
- How to create a flow by an operator on behalf of another account. For that, please refer to the ACL and User Data guide.
- How to manage flows with user data. For that, please refer to the ACL and User Data guide.
- How to create flows by interacting with the Money Streaming Forwarder contract from client applications. For that please refer to the SDK section.
Prerequisites
Before proceeding, ensure you have:
- Familiarity with Solidity.
- Basic understanding of Superfluid and its functionalities.
- Access to a development environment for deploying or interacting with Smart Contracts.
- Importing the SuperTokenV1Library in your smart contract. For more details, refer to the Using the SuperTokenV1Library.
What is a flow?
In Superfluid terminology, a flow is a continuous stream of tokens from one account to another. It is a fundamental concept in the Superfluid protocol, enabling real-time, continuous payments between accounts.
This is a small technicality which is not necessarily important to understand. However, in Superfluid, a "Flow" is a more general term than a "Stream". A Stream is a non-zero flow, while a zero flow is not considered a Stream.
Create a flow
To create a flow, you need to call createFlow
by specifying the token, sender, receiver, and flow rate.
Function: createFlow
This function creates a flow without user data.
/**
* @dev Create flow without userData
* @param token The token used in flow
* @param receiver The receiver of the flow
* @param flowRate The desired flowRate
*/
function createFlow(ISuperToken token, address receiver, int96 flowRate)
internal returns (bool)
The CFAv1Forwarder contract has a function called setFlowrate
which is used to create or update flows.
This function does not exist in the SuperTokenV1Library for a reason.
In short, the main contributors to the Superfluid Protocol believe that the best practice of building on-chain contracts is to not abstract away the creation and update of flows through a wrapper. The developer using the library should be aware of the difference between creating and updating flows, and should call the appropriate function accordingly.
Update a flow
For an existing flow, you can update the flow rate through the updateFlow
function.
Function: updateFlow
/**
* @dev Update flow without userData
* @param token The token used in flow
* @param receiver The receiver of the flow
* @param flowRate The desired flowRate
*/
function updateFlow(ISuperToken token, address receiver, int96 flowRate)
internal returns (bool)
Deleting a flow
To delete an eisting, you need to call deleteFlow
and specify the token, sender, and receiver.
Function: deleteFlow
/**
* @dev Delete flow without userData
* @param token The token used in flow
* @param sender The sender of the flow
* @param receiver The receiver of the flow
*/
function deleteFlow(ISuperToken token, address sender, address receiver)
internal returns (bool)
No, you cannot update or delete a non-existent flow. If a flow does not exist, the function will revert.
For a full list of functions related to creating, updating, and deleting flows, refer to the SuperTokenV1Library technical reference.
Example: Creating, Updating, and Deleting Flows
For this example, we'll use the FlowSender
contract described in the Quickstart as our example to demonstrate how to write a contract with creates, updates, deletes and reads flow data.
Click here to show FlowSender
contract
//SPDX-License-Identifier: Unlicensed
pragma solidity ^0.8.14;
import { ISuperfluid, ISuperToken } from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol";
import { SuperTokenV1Library } from "@superfluid-finance/ethereum-contracts/contracts/apps/SuperTokenV1Library.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
// For deployment on Mumbai Testnet
interface IFakeDAI is IERC20 {
function mint(address account, uint256 amount) external;
}
contract FlowSender {
using SuperTokenV1Library for ISuperToken;
mapping (address => bool) public accountList;
ISuperToken public daix;
// fDAIx address on Polygon Mumbai = 0x5D8B4C2554aeB7e86F387B4d6c00Ac33499Ed01f
constructor(ISuperToken _daix) {
daix = _daix;
}
/// @dev Mints 10,000 fDAI to this contract and wraps it all into fDAIx
function gainDaiX() external {
// Get address of fDAI by getting underlying token address from DAIx token
IFakeDAI fdai = IFakeDAI( daix.getUnderlyingToken() );
// Mint 10,000 fDAI
fdai.mint(address(this), 10000e18);
// Approve fDAIx contract to spend fDAI
fdai.approve(address(daix), 20000e18);
// Wrap the fDAI into fDAIx
daix.upgrade(10000e18);
}
/// @dev creates a stream from this contract to desired receiver at desired rate
function createStream(int96 flowRate, address receiver) external {
// Create stream
daix.createFlow(receiver, flowRate);
}
/// @dev updates a stream from this contract to desired receiver to desired rate
function updateStream(int96 flowRate, address receiver) external {
// Update stream
daix.updateFlow(receiver, flowRate);
}
/// @dev deletes a stream from this contract to desired receiver
function deleteStream(address receiver) external {
// Delete stream
daix.deleteFlow(address(this), receiver);
}
/// @dev get flow rate between this contract to certain receiver
function readFlowRate(address receiver) external view returns (int96 flowRate) {
// Get flow rate
return daix.getFlowRate(address(this), receiver);
}
}
This contract has a few functions:
- gainDaiX: Mints and wraps fDAI into fDAIx (Superfluid's wrapped token).
- createStream: Initiates a new money stream to a specified receiver.
- updateStream: Updates an existing money stream's flow rate.
- deleteStream: Terminates an existing money stream.
- readFlowRate: Reads the current flow rate of a stream.
SuperTokenV1Library
As you can see, the FlowSender
contract uses the SuperTokenV1Library
to interact with the Superfluid protocol.
Instead of passing the token address to the functions, the contract uses using SuperTokenV1Library for ISuperToken;
to access the functions directly.
You can learn more about how to use the SuperTokenV1Library
in the Using the SuperTokenV1Library guide.