Skip to main content

Pull Vesting Data

This guide explains how you can pull vesting data from Sablier Lockup streams. This data can be useful for a variety of use cases, including but not limited to staking contracts, blockchain explorers, and data dashboards:

  1. Staking: Staking of Sablier streams require access to the amount of tokens that are held by the stream. You do not want to distribute rewards for tokens that have been withdrawn by users.
  2. Explorers (e.g., Etherscan, CoinGecko): One major feature of blockchain explorers is to show accurate circulating supplies. When tokens are vested through Sablier, you may want to exclude the amount of unvested tokens from the circulating supply. This is helpful to accurately calculate the market cap, which depends upon the amount of liquid tokens.
  3. Data Dashboards (e.g., Tokenomist, Nansen, Dune): Investors and traders use data dashboards to make informed trading decisions. When Sablier is used, you may want to show the amount of liquid (or vested) tokens and the amount of illiquid (or unvested) tokens. This is helpful to understand the token distribution and the team's commitment to the long-term success of the project.
note

Note that 'streamed amount' is synonymous with 'vested amount'.

Frontend Sandbox

The examples in this guide are written in Solidity, but you may want to interact with the Sablier Lockup contract from your frontend application. A good starting point for this is the Sablier Sandbox.

For a comprehensive list of all the functions available in Sablier Lockup, visit the References section of this website.

Actions

Cancel on first withdraw

To automatically cancel streams as soon as the user withdraws their tokens, you can use one of two methods: onchain or offchain.

The onchain method is to track the withdrawn amount and check if its value is greater than 0:

if (lockup.getWithdrawnAmount(streamId) > 0) {
lockup.cancel(streamId);
}

Offchain, you can monitor the WithdrawFromLockupStream events. As soon as a withdrawal event is detected, you can send a transaction to cancel the stream.

Calculating Amounts

Amount in stream

This is the amount of tokens held by the stream. It is the sum of locked tokens and vested tokens that have not been withdrawn. This value is particularly useful for applications like staking. The following formula can be used for both cancelable and non-cancelable streams:

uint256 amountInStream = sablierLockup.getDepositedAmount(streamId)
- sablierLockup.getWithdrawnAmount(streamId)
- sablierLockup.getRefundedAmount(streamId);

For non-cancelable stream, a more efficient way to calculate the amount in stream is:

uint256 amountInStream = sablierLockup.getDepositedAmount(streamId)
- sablierLockup.getWithdrawnAmount(streamId);

Locked amount

This is the amount of tokens that are locked in the stream and are effectively illiquid. This is particularly relevant when calculating the circulating supply of a token.

uint256 lockedAmount = lockup.getDepositedAmount(streamId)
- lockup.streamedAmountOf(streamId)
- sablierLockup.getRefundedAmount(streamId);

For non-cancelable stream, a more efficient way to calculate locked amount is:

uint256 lockedAmount = lockup.getDepositedAmount(streamId) - lockup.streamedAmountOf(streamId);

While calculating the circulating supply, you may want to subtract the locked amount from your calculations.

Unlocked amount

As opposed to the locked amount, the unlocked amount refers to tokens that are no longer locked and are effectively liquid.

uint256 unlockedAmount = lockup.streamedAmountOf(streamId)
+ sablierLockup.getRefundedAmount(streamId);

For non-cancelable stream, a more efficient way to calculate unlocked amount is:

uint256 unlockedAmount = lockup.streamedAmountOf(streamId);

Vested amount not withdrawn

If you are building an application that requires access to amount of tokens that have been vested but not yet withdrawn, you can use the following formula:

uint256 vestedAmount = lockup.streamedAmountOf(streamId) - lockup.getWithdrawnAmount(streamId);

This may be useful for use cases in which you want to reward 'diamond hands', i.e., users who have not withdrawn their share of airdrops.

Unlock Dates

This section is useful if you are building a data dashboard and want to index the dates when tokens will be unlocked in Sablier.

To obtain the time at which a stream will be fully unlocked, you can use the following function:

uint256 unlockTime = lockup.getEndTime(streamId);

Obtaining the earlier unlock times depends on the type of stream. Let's go through each stream type:

Linear streams

For Lockup Linear streams, make a request to lockup.getTimestamps(streamId) and then calculate cliff time and end time as timestamps.cliff and timestamps.end respectively.

Dynamic streams

For Lockup Dynamic streams, you may be particularly interested in the unlock amount and time of the current segment.

LockupDynamic.Segment[] memory segments = lockup.getSegments(streamId);

// Loop over the segments to find the next unlock time.
for (uint i; i < segments.length; ++i) {
if (segments[i].timestamp > block.timestamp) {
nextUnlockAmount = segments[i].amount;
nextUnlockTime = segments[i].timestamp;
break;
}
}

Tranched streams

For Lockup Tranched streams, you may be particularly interested in the unlock amount and time of the current tranche.

LockupTranched.Tranche[] memory tranches = lockup.getTranches(streamId);

// Loop over the tranches to find the next unlock time.
for (uint i; i < tranches.length; ++i) {
if (tranches[i].timestamp > block.timestamp) {
nextUnlockAmount = tranches[i].amount;
nextUnlockTime = tranches[i].timestamp;
break;
}
}

We hope you have found this guide helpful. If you have a use case that is not covered here, please reach out to us on Discord, and we will assist you.