Skip to main content

Create a Batch of Lockup Linear Streams

In this guide, we will show you how you can use Solidity to batch create linear streams via the Lockup's Batch Lockup contract.

This guide assumes that you have already gone through the Protocol Concepts section.

note

This guide interacts with the Periphery contract.

caution

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 Sepolia. If you need to work with a different chain, Lockup 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 tokens, 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);

Full code

Below you can see the complete functioning code: a contract that batch creates Lockup Linear streams using Lockup's Batch that start at block.timestamp. You can access the code on GitHub through this link.

Batch Lockup Linear stream creator
loading...