Create a Batch of Lockup Linear Streams
In this guide, we will show you how to programmatically batch create linear streams via the Sablier's Batch Lockup contract.
This guide assumes that you have already gone through the Protocol Concepts section.
This guide interacts with the Periphery contract.
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;
Now, import the relevant symbols from @sablier/v2-core
and @sablier/v2-periphery
:
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { ud60x18 } from "@prb/math/src/UD60x18.sol";
import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol";
import { Broker, LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol";
import { ISablierV2BatchLockup } from "@sablier/v2-periphery/src/interfaces/ISablierV2BatchLockup.sol";
import { BatchLockup } from "@sablier/v2-periphery/src/types/DataTypes.sol";
Create a contract called BatchLLStreamCreator
, and declare a constant DAI
of type IERC20
, a constant
LOCKUP_LINEAR
of type ISablierV2LockupLinear
, and a constant BATCH_LOCKUP
of type ISablierV2BatchLockup
:
contract BatchLLStreamCreator {
IERC20 public constant DAI = IERC20(0x68194a729C2450ad26072b3D33ADaCbcef39D574);
ISablierV2LockupLinear public constant LOCKUP_LINEAR =
ISablierV2LockupLinear(0x3E435560fd0a03ddF70694b35b673C25c65aBB6C);
ISablierV2BatchLockup public constant BATCH_LOCKUP = ISablierV2BatchLockup(0x04A9c14b7a000640419aD5515Db4eF4172C00E31);
}
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 Mainnet. If you need to work with a different chain, the Sablier addresses can be obtained from the Deployment Addresses page.
Batch create functions
There are two batch create functions for the Lockup Linear contract:
Which one you choose depends upon your use case. In this guide, we will use createWithDurationsLL
.
Function definition
Define a function called batchCreateStreams
that takes a parameter perStreamAmount
and returns an array of ids for
the created streams:
function batchCreateStreams(uint128 perStreamAmount) public returns (uint256[] memory streamIds) {
// ...
}
Batch size
Next, declare a batch size, which is needed to calculate the transfer amount:
// Create a batch of two streams
uint256 batchSize = 2;
// Calculate the combined amount of DAI assets to transfer to this contract
uint256 transferAmount = perStreamAmount * batchSize;
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 also approve the Batch
contract to pull the assets 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), transferAmount);
// Approve the Batch contract to spend DAI
DAI.approve(address(BATCH_LOCKUP), transferAmount);
For more guidance on how to approve and transfer ERC-20 assets, see this article on the Ethereum website.
Stream Parameters
Given that we declared a batchSize
of two, we need to define two
BatchLockup.CreateWithDurationsLL
structs:
// Declare the first stream in the batch
BatchLockup.CreateWithDurationsLL memory stream0;
stream0.sender = address(0xABCD); // The sender to stream the assets, he will be able to cancel the stream
stream0.recipient = address(0xCAFE); // The recipient of the streamed assets
stream0.totalAmount = perStreamAmount; // The total amount of each stream, inclusive of all fees
stream0.cancelable = true; // Whether the stream will be cancelable or not
stream0.transferable = false; // Whether the recipient can transfer the NFT or not
stream0.durations = LockupLinear.Durations({
cliff: 4 weeks, // Assets will be unlocked only after 4 weeks
total: 52 weeks // Setting a total duration of ~1 year
});
stream0.broker = Broker(address(0), ud60x18(0)); // Optional parameter left undefined
To add some variety, we will change the parameters of the second stream:
// Declare the second stream in the batch
BatchLockup.CreateWithDurationsLL memory stream1;
stream1.sender = address(0xABCD); // The sender to stream the assets, he will be able to cancel the stream
stream1.recipient = address(0xBEEF); // The recipient of the streamed assets
stream1.totalAmount = perStreamAmount; // The total amount of each stream, inclusive of all fees
stream1.cancelable = false; // Whether the stream will be cancelable or not
stream1.transferable = false; // Whether the recipient can transfer the NFT or not
stream1.durations = LockupLinear.Durations({
cliff: 1 weeks, // Assets will be unlocked only after 1 week
total: 26 weeks // Setting a total duration of ~6 months
});
stream1.broker = Broker(address(0), ud60x18(0)); // Optional parameter left undefined
Once both structs are declared, the batch array has to be filled:
// Fill the batch param
BatchLockup.CreateWithDurationsLL[] memory batch = new BatchLockup.CreateWithDurationsLL[](batchSize);
batch[0] = stream0;
batch[1] = stream1;
Invoke the batch create function
With all parameters set, we can now call the createWithDurationsLL
function, and assign the ids of the newly created
streams to the array:
streamIds = BATCH_LOCKUP.createWithDurationsLL(LOCKUP_LINEAR, DAI, batch);
The complete Batch Lockup Linear stream creator contract
Below you can see the complete functioning code: a contract that batch creates Lockup Linear streams using Sablier's
Batch
that start at block.timestamp
. You can access the code on GitHub through this
link.
loading...