Skip to main content

Structure

We'll break down the schema into primary and secondary entities.

TypeEntities
PrimaryContract, Action, Stream, Asset, Segment, Tranche
SecondaryBatch, Batcher, Watcher

Contract

The subgraph is designed to track multiple deployments. Therefore, at any given time the indexer may listen for updates on many instances of LockupLinear or ...Dynamic / Tranched / Merged contracts .

A unique alias will be attributed to every contract such that streams become identifiable through either a long form or a short form identifier (e.g. LK-137-1). See the Stream section for details.


Action

Events emitted by the Sablier Lockup contracts will:

  1. Be used to mutate the data stored in the individual Stream entities
  2. Be stored as historical logs (list of Action) to show the evolution of a related stream

Based on the schema defined ActionCategory, the following actions will be tracked by the subgraph:

ActionContract Events
ApprovalApproval
ApprovalForAllApprovalForAll
CreateCreateLockupLinearStream, CreateLockupDynamicStream
CancelCancelLockupStream
RenounceRenounceLockupStream
TransferTransfer
WithdrawWithdrawFromLockupStream

To keep all actions under the same umbrella, some details will be stored under general purpose attributes like amountA, amountB, addressA, addressB which based on the type of action can be resolved to context-specific values. Am example can be found here for the Cancel event.


Stream

Identifying

Inside the contracts, streams will be assigned a unique tokenId (or streamId). While this makes it easy to identify items at the contract level, we need to consider the following for both subgraphs and client interfaces:

  • items should be uniquely recognizable across multiple contract instances
  • items should be uniquely identifiable across multiple chains
  • items should be identifiable with short, easy to digest names

To address these observations, the subgraph uses two related identifiers for a Stream.

TypeDescriptionExample
Stream.idA self-explanatory structure built using the following structure: contractAddress-chainId-tokenId0xAB..12-137-21
Stream.aliasA short version of the id where the contract is aliased: contractAlias-chainId-tokenIdLK-137-21

Both examples from the table above translate to: a stream on Polygon (chain id 137), within the Lockup Merged contract at address 0xAB..12, with the tokenId 21.

note

The aliases defined in the subgraph will be used by client apps to resolve data about a stream. Make sure to keep them in sync, avoid conflicts and regard them as immutable (once decided, never change them).

Aliases

important

Following the merger of LockupLinear, LockupDynamic, and LockupTranched into a single contract, the alias for all streaming shapes was standardized as LK.

To provide a simple visual structure, while also accounting for future stream curves (backwards compatibility) we use the following abbreviations as aliases:

  • Lockup v2.0 contracts become LK, e.g LK-137-1
  • Lockup v1.0 Linear contracts become LL, e.g. LL-137-1
  • Lockup v1.1 Linear contracts become LL2, e.g. LL2-137-1
  • Lockup v1.2 Linear contracts become LL3, e.g. LL3-137-1
  • Lockup v1.0 Dynamic contracts become LD, e.g. LD-137-1
  • Lockup v1.1 Dynamic contracts become LD2, e.g. LD2-137-1
  • Lockup v1.2 Dynamic contracts become LD3, e.g. LD3-137-1
  • Lockup v1.2 Tranched contracts become LT, e.g. LT3-137-1

More on contract iterations and past versions here.

Relevant parties

Within the larger Sablier ecosystem, the number of relevant entities participating in a stream (and the dynamics between them) has grown past the immutable sender and recipient. Therefore, we identify the following parties involved in a stream.

The recipient (gets paid*)

As funds are being streamed, they will slowly become eligible to withdraw and spend unlocked tokens. The recipient is defined at the start of the stream but can change as a result of a transfer.

On transfer, the old recipient moves the NFT (the stream itself) to another address, which becomes the new recipient. Rights to withdraw and claim future streamed funds are naturally transferred to this new address.

The sender (will pay*)

They are an immutable party, defined at the start of the stream. Based on the configuration chosen for the stream, they will be entitled to later cancel the stream, renounce it (disable cancelability) or withdraw on behalf of the recipient.

In case of a cancelation, the sender will receive any unstreamed tokens as part of the refund.

The funder

When the stream is created, they provide the tokens to be gradually streamed to the recipient. Usually, they are the same entity as the sender. However, there may be cases when someone wishes to create a stream on another user's behalf, while also marking them as the sender. In that case, this initial address will be accounted for as the stream's funder.


Asset

Tokens (ERC20) streamed through the protocol will be defined through an Asset entity.

info

As a development caveat, some ERC20 contracts are designed to store details (e.g. name, symbol) as bytes32 and not string. Prior to deploying a subgraph, make sure you take into account these details as part of any Asset entity implementation. For examples, see the asset "helper" files inside this subgraph's repository code.


Segment

The custom emission curve used by Lockup Dynamic streams will be defined as a sequence of segments. This entity will store data regarding those segments, which will be later used to reconstruct the shape of the curve client side.


Tranche

The custom emission curve used by Lockup Tranched streams will be defined as a sequence of tranches. This entity will store data regarding those tranches, which will be later used to reconstruct the shape of the curve client side.

tip

Tranches can also be represented as a set of two segments (one horizontal, one vertical) so client apps may benefit from artificially creating segments from tranches.


Batch and Batcher

The lockup-periphery, while not explicitly tracked by the subgraph will offer some extra functionality to proxy-sourced streams. One of these functionalities will be batch stream creation (or stream grouping). Using methods like createWithDurations or createWithTimestamps a sender will be able to create multiple streams at once - considered part of the same batch.

To identify these relationships between stream items, the Batch entity will group items created in the same transaction, by finding events emitted with the same tx hash. The Batcher will then assign a user-specific unique index to every group.


Watcher

The Watcher (one for the entire subgraph) will provide specific utilities to the entire system, like global stream identifiers (a numeric id unique to a stream across all contract instances) and global action identifiers.