Create a Lockup Tranched Stream
Lockup Tranched are streams with discrete unlocks. In this guide, we will show you how to create a Lockup Tranched stream using Solidity.
This guide assumes that you have already gone through the Protocol Concepts section.
The code in this guide is not production-ready, and is implemented in a simplistic manner for the purpose of learning.
Set up a contract
Declare the Solidity version used to compile the contract:
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.8.22;
Import the relevant symbols from @sablier/lockup:
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { ISablierLockup } from "@sablier/lockup/src/interfaces/ISablierLockup.sol";
import { Lockup } from "@sablier/lockup/src/types/Lockup.sol";
import { LockupTranched } from "@sablier/lockup/src/types/LockupTranched.sol";
Create a contract called LockupTranchedStreamCreator, and declare a constant DAI of type IERC20 and a constant
LOCKUP of type ISablierLockup:
contract LockupTranchedStreamCreator {
IERC20 public constant DAI = IERC20(0x68194a729C2450ad26072b3D33ADaCbcef39D574);
ISablierLockup public constant LOCKUP = ISablierLockup(0xcF8ce57fa442ba50aCbC57147a62aD03873FfA73);
}
In the code above, the contract addresses are hard-coded for demonstration purposes. However, in production, you would likely use input parameters to allow flexibility in changing the addresses.
Also, these addresses are deployed on Ethereum Sepolia. If you need to work with a different chain, Lockup addresses can be obtained from the Lockup Deployments page.
There are two create functions in the Lockup contract that can be used to create Tranched streams:
createWithDurationsLT: takes duration and calculates the tranche timestamps based on the provided durations.createWithTimestampsLT: takes UNIX timestamps for tranches.
Which one you choose depends upon your use case. In this guide, we will use createWithDurationsLT.
Function definition
Define a function called createStream which takes two parameters, amount0 and amount1, and which returns the id of
the created stream:
function createStream(uint128 amount0, uint128 amount1) public returns (uint256 streamId) {
// ...
}
Next, sum up the amount0 and amount1 parameters to get the deposit amount of the stream, which will be needed in
many of the steps below:
uint128 depositAmount = amount0 + amount1;
ERC-20 steps
To create a stream, the caller must approve the creator contract to pull the tokens from the calling address's account. Then, we have to approve the Lockup contract to pull the tokens that the creator contract will be in possession of after they are transferred from the calling address (you):
// Transfer the provided amount of DAI tokens to this contract
DAI.transferFrom(msg.sender, address(this), depositAmount);
// Approve the Sablier contract to spend DAI
DAI.approve(address(LOCKUP), depositAmount);
For more guidance on how to approve and transfer ERC-20 tokens, see this article on the Ethereum website.
Parameters
The struct associated with createWithDurationsLT are
Lockup.CreateWithDurations (a shared struct
across all the lockup streams) and
LockupTranched.TrancheWithDuration.
Lockup.CreateWithDurations memory params;
LockupTranched.TrancheWithDuration[] memory tranches = new LockupTranched.TrancheWithDuration[](2);
Let's review each parameter in detail.
Sender
The address streaming the tokens, with the ability to cancel the stream:
params.sender = msg.sender;
Recipient
The address receiving the tokens:
params.recipient = address(0xCAFE);
Deposit amount
The deposit amount of ERC-20 tokens to be paid, denoted in units of the token's decimals.
params.depositAmount = depositAmount;
Token
The contract address of the ERC-20 token used for streaming. In this example, we will stream DAI:
params.token = DAI;
Cancelable
Boolean that indicates whether the stream will be cancelable or not.
params.cancelable = true;
Transferable
Boolean that indicates whether the stream will be transferable or not.
params.transferable = true;
Tranches With Duration
Tranches are what the protocol uses to compose the discrete unlocks. For a full exposition of tranches, see the Tranches guide.
Each tranche is characterized by a specific amount and timestamp. Because we are using createWithDurationsLT in this
example, these tranches are supplied to the function in the form of an array containing
LockupTranched.TrancheWithDuration
structs.
Let's define two dummy tranches:
tranches[0] = LockupTranched.TrancheWithDuration({ amount: amount0, duration: uint40(4 weeks) });
tranches[1] = (LockupTranched.TrancheWithDuration({ amount: amount1, duration: uint40(6 weeks) }));
In this example, the first tranche (amount0) will unlock at the end of the 4 weeks after the stream was created and
the second tranche (amount1) will unlock after further 6 weeks. Thus, the deposit amount will be unlocked in 10 weeks.
Invoke the create function
With all parameters set, we can now call the createWithDurationsLT function, and assign the ID of the newly created
stream to a variable:
streamId = LOCKUP.createWithDurationsLT(params, tranches);
Full code
Below you can see the full code. You can also access the code on GitHub through this link.
loading...