We'll break down the schema into primary and secondary entities.
|Contract, Action, Stream, Asset, Segment
|Batch, Batcher, Watcher
The subgraph is designed to track multiple deployments. Therefore, at any given time the indexer may listen for updates
on many instances of
SablierV2LockupDynamic contracts .
alias will be attributed to every contract, such that contracts (and later streams) will be identifiable
through both a long form and a short form identifier. See the Stream for details.
Events emitted by the Sablier Lockup contracts will:
- Be used to mutate the data stored in the individual
- 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:
To keep all actions under the same umbrella, some details will be stored under general purpose attributes like
addressB which based on the type of action can be resolved to context-specific
values. Am example can be found
the Cancel event.
Inside the contracts, streams will be assigned a unique
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.
|A self-explanatory structure built using the following structure:
|A short version of the
id where the contract is aliased:
Both examples from the table above translate to: a stream on Polygon (chain id
137), within the Lockup Linear
contract at address
0xAB..12, with the tokenId
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).
To provide a simple visual structure, while also accounting for future stream types (backwards compatibility) we use the following abbreviations as aliases:
- Lockup Linear V2.0 contracts become
- Lockup Linear V2.1 contracts become
- Lockup Dynamic V2.0 contracts become
- Lockup Dynamic V2.1 contracts become
Within the larger Sablier V2 ecosystem, the number of relevant entities participating in a stream (and the dynamics
between them) has grown past the immutable
recipient (as in V1). 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 assets. The
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 assets as part of the refund.
When the stream is created, they provide the assets 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
The proxender (relevant only for V2.0, deprecated with V2.1+)
Sablier V2.0 involved users deploying a "PRBProxy" contract through which they interacted with the Sablier contracts.
With Sablier V2.1+ this is not the case any more as streams are now designed to be managed without these forwarder
contracts. Therefore, you don't need to worry about
proxy if you're not looking to support old
versions of the protocol.
[In V2.0] While not mandatory for the core functionality, Sablier (through its periphery) made use of PRBProxy. The official client interfaces provided support for functionality exposed through both EOAs and this proxy integration, at the same time.
For streams created within this "extended" ecosystem, a few attributes and entities will change meaning as such:
stream.proxiedflag will turn
stream.senderaddress will resolve to a
proxycontract address, owned by the
stream.proxenderaddress (usually an EOA) will control this
senderproxy and instruct it to perform actions on its behalf (like a special account)
|Examples of supported flows
0xF the funder (EOA), funds a stream on behalf of
0xA, the sender (EOA), towards
0xB the recipient (EOA)
0xA the sender (EOA), creates a stream towards
0xB the recipient (EOA)
0xA the proxender (EOA), through their proxy
0xA1 (proxy contract), creates a stream towards
0xB the recipient
While other combinations are possible, the most likely ones (as supported by the official interfaces) will be similar to example #3.
For Sablier V2.1 and above, you should only rely on the
Tokens (ERC20) streamed through the protocol will be defined through an
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.
The custom emission curve used by LockupDynamic 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.
Batch and Batcher
The v2-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
batchCreateWithDeltas 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.
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.