# Sablier Lockup Documentation > Sablier Lockup is useful for token vesting and airdrops. ## Schema Source: https://docs.sablier.com/api/streams/graphql/schema # Schema ## Overview We provide auto-generated GraphQL schema documentation for both The Graph and Envio: - [The Graph schema docs](/api/streams/graphql/the-graph/overview) - [Envio schema docs](/api/streams/graphql/envio/overview) ## Query Syntax Differences | Feature | The Graph | Envio | | --- | --- | --- | | Pagination | `first` | `limit` | | Pagination | `skip` | `offset` | | Get by ID | `lockupStream(id: "...")` / `flowStream(id: "...")` | `LockupStream_by_pk(id: "...")` / `FlowStream_by_pk(id: "...")` | | Get multiple | `lockupStreams{}` / `flowStreams{}` | `LockupStream(where: {...}){}` / `FlowStream(where: {...}){}` | | Nested items | `campaigns{ id, asset: {id, symbol}}` | `Campaign{ asset_id, asset: {id, symbol}}` | ## Entities This is the raw GraphQL file that is used by both The Graph and Envio for generating the final schemas hosted on their services. The schema only contains entities: ```graphql enum FlowActionCategory { Approval ApprovalForAll Adjust Create Deposit Pause Refund Restart Transfer Void Withdraw } enum FlowStreamCategory { Flow } enum LockupActionCategory { Approval ApprovalForAll Cancel Create Renounce Transfer Withdraw } enum LockupStreamCategory { LockupDynamic LockupLinear LockupTranched } enum ShapeSource { Event Inferred } """ ERC-20 asset """ type Asset @entity(immutable: false) { """ Unique identifier: `asset-{chainId}-{address}` """ id: String! """ Address of the ERC-20 token. """ address: Bytes! """ The chain ID where the asset exists (e.g., 137 for Polygon). """ chainId: BigInt! """ Decimals of the ERC20 token. """ decimals: BigInt! """ Name of the ERC20 token. """ name: String! """ Symbol of the ERC20 token. """ symbol: String! """ Flow streams that rely on this token """ flowStreams: [FlowStream!]! @derivedFrom(field: "asset") """ Lockup streams that rely on this token """ lockupStreams: [LockupStream!]! @derivedFrom(field: "asset") } type Watcher @entity(immutable: false) { """ The chain ID. There is one watcher per subgraph. """ id: String! """ Alias for id. """ chainId: BigInt! """ Global counter for Flow actions. """ flowActionCounter: BigInt! """ Global counter for Flow streams. """ flowStreamCounter: BigInt! """ Global counter for Lockup actions. """ lockupActionCounter: BigInt! """ Global counter for Lockup streams. """ lockupStreamCounter: BigInt! } type FlowStream @entity(immutable: false) { """ Unique identifier: `{contractAddress}-{chainId}-{tokenId}` """ id: String! """ Like the id: `{contractAlias}-{chainId}-{tokenId}` """ alias: String! """ The chain ID where the stream was created (e.g., 137 for Polygon). """ chainId: BigInt! """ Unique global id as tracked by the `Watcher` entity. 🚨 This may change if new data sources are added and the chronological order of streams changes. """ subgraphId: BigInt! """ The id provided by the NFT contract. It's the ERC-721 tokenId. """ tokenId: BigInt! """ Hash of the Ethereum transaction that created this stream. """ hash: Bytes! """ Unix timestamp of the Ethereum transaction that created this stream. """ timestamp: BigInt! """ Actions triggered by this stream. """ actions: [FlowAction!]! @derivedFrom(field: "stream") """ ERC-20 token distributed via this stream. """ asset: Asset! """ ERC-20 token decimals. Stored here to avoid loading the asset entity on each stream. Note: the "Value" suffix is added because of a bug in GraphQL Code Generator. """ assetDecimalsValue: BigInt! """ The batch the stream may be part of. Note: this is available only when created within a batch create transaction. """ batch: FlowBatch! """ Category used for sorting. """ category: FlowStreamCategory! """ The address of the contract the stream originates from. """ contract: Bytes! """ Position in the batch, if available. """ position: BigInt! """ Current recipient of the stream, with permission to withdraw funds to any third-party address. Note: the recipient can change on NFT transfer. """ recipient: Bytes! """ Manager of the stream, with ability to cancel the stream. """ sender: Bytes! """ Unix timestamp for the start of the stream. """ startTime: BigInt! """ Flag indicating the transferability of the stream. This is set when the stream is created, and cannot be changed later. """ transferable: Boolean! """ Version of contract, e.g., v1.0. """ version: String! """ The sum of all withdrawn amounts. """ withdrawnAmount: BigInt! """ This is equivalent to the value returned by ERC20.balanceOf, and it changes after deposit and withdrawal. """ availableAmount: BigInt! """ The account that created the stream, which can be different from the sender. """ creator: Bytes! """ Unix timestamp indicating the time when the stream will become insolvent. """ depletionTime: BigInt! """ The sum of all deposits. """ depositedAmount: BigInt! """ The amount of debt forgiven by a void action. """ forgivenDebt: BigInt! """ Action in which the payment rate was adjusted. """ lastAdjustmentAction: FlowAction """ Unix timestamp for when the payment rate was adjusted. """ lastAdjustmentTimestamp: BigInt! """ Flag indicating if a stream is paused. """ paused: Boolean! """ Action in which the stream was paused. """ pausedAction: FlowAction """ Unix timestamp for when the stream was paused. """ pausedTime: BigInt """ Current payment rate per second, denominated in 18 decimals. """ ratePerSecond: BigInt! """ The sum of all refunds. """ refundedAmount: BigInt! """ The amount streamed up until the time of the last adjustment, denominated in 18 decimals. """ snapshotAmount: BigInt! """ Flag indicating if a stream is voided. """ voided: Boolean! """ Action in which the stream was voided. """ voidedAction: FlowAction """ Unix timestamp for when the stream was voided. """ voidedTime: BigInt } type LockupStream @entity(immutable: false) { """ Unique identifier: `{contractAddress}-{chainId}-{tokenId}` """ id: String! """ Like the id: `{contractAlias}-{chainId}-{tokenId}` """ alias: String! """ The chain ID where the stream was created (e.g., 137 for Polygon). """ chainId: BigInt! """ Unique global id as tracked by the `Watcher` entity. 🚨 This may change if new data sources are added and the chronological order of streams changes. """ subgraphId: BigInt! """ The id provided by the NFT contract. It's the ERC-721 tokenId. """ tokenId: BigInt! """ Hash of the Ethereum transaction that created this stream. """ hash: Bytes! """ Unix timestamp of the Ethereum transaction that created this stream. """ timestamp: BigInt! """ Actions triggered by this stream. """ actions: [LockupAction!]! @derivedFrom(field: "stream") """ ERC-20 token distributed via this stream. """ asset: Asset! """ ERC-20 token decimals. Stored here to avoid loading the asset entity on each stream. Note: the "Value" suffix is added because of a bug in GraphQL Code Generator. """ assetDecimalsValue: BigInt! """ The batch the stream may be part of. Note: this is available only when created within a batch create transaction. """ batch: LockupBatch! """ Category used for sorting. """ category: LockupStreamCategory! """ The address of the contract the stream originates from. """ contract: Bytes! """ Position in the batch, if available. """ position: BigInt! """ Current recipient of the stream, with permission to withdraw funds to any third-party address. Note: the recipient can change on NFT transfer. """ recipient: Bytes! """ Manager of the stream, with ability to cancel the stream. """ sender: Bytes! """ Unix timestamp for the start of the stream. """ startTime: BigInt! """ Flag indicating the transferability of the stream. This is set when the stream is created, and cannot be changed later. """ transferable: Boolean! """ Version of contract, e.g., v1.0. """ version: String! """ The sum of all withdrawn amounts. """ withdrawnAmount: BigInt! """ Action in which the stream was canceled. """ canceledAction: LockupAction """ Action in which the stream was made non-cancelable. Note: if the stream was made non-cancelable from the get-go, this is the same as the Create action. """ renounceAction: LockupAction """ Flag indicating the cancelability of the stream. """ cancelable: Boolean! """ Flag indicating if the stream was canceled. """ canceled: Boolean! """ Unix timestamp for the when the stream was canceled. """ canceledTime: BigInt """ The amount deposited when the stream was created. """ depositAmount: BigInt! """ Snapshot of the duration in seconds (the difference between end and start time). """ duration: BigInt! """ Unix timestamp for the end of the stream. """ endTime: BigInt! """ The account that funded the stream, which can be different from the sender. """ funder: Bytes! """ The amount that is still held by the stream regardless of whether if was fully vested or not. This is the difference between the deposit amount and all withdrawn amounts. """ intactAmount: BigInt! """ Flag indicating if the stream has been fully withdrawn (intactAmount is zero). """ depleted: Boolean! """ Owner of the proxy when the stream is created through a PRBProxy (https://github.com/PaulRBerg/prb-proxy) Note that proxy = stream sender, and proxender = owner of proxy """ proxender: Bytes """ Flag for streams created through a PRBProxy. """ proxied: Boolean! """ Unix timestamp for when the stream was made non-cancelable. This can coincide with the create time. """ renounceTime: BigInt """ An optional parameter to specify the shape of the distribution. Available since Lockup v2.0. """ shape: String """ The source of the shape value: Event (from contract event) or Inferred (computed by indexer). """ shapeSource: ShapeSource """ Flag for Linear streams with a cliff. """ cliff: Boolean """ The amount that will unlock at the cliff time. """ cliffAmount: BigInt """ Unix timestamp for the start of the cliff. """ cliffTime: BigInt """ The smallest step in time between two consecutive token unlocks. Applies only for LockupLinear and available since Lockup v4.0 """ granularity: BigInt """ Flag for Linear stream with an initial unlock. Available since Lockup v2.0. """ initial: Boolean """ The initial unlock amount of a Linear stream. Available since Lockup v2.0. """ initialAmount: BigInt """ Segments of a Dynamic stream. """ segments: [Segment!]! @derivedFrom(field: "stream") """ Segments of a Tranched stream. """ tranches: [Tranche!]! @derivedFrom(field: "stream") } """ A generic entity for tracking Flow protocol actions. """ type FlowAction @entity(immutable: true) { """ Unique identifier: `action-{chainId}-{txHash}-{logIndex}` """ id: String! """ Unique global id as tracked by the `Watcher` entity. """ subgraphId: BigInt! """ Block number of the Ethereum transaction. """ block: BigInt! """ The chain ID where the action was created (e.g., 137 for Polygon). """ chainId: BigInt! """ The tx.origin of the Ethereum transaction. """ from: Bytes! """ Hash of the Ethereum transaction. """ hash: Bytes! """ Unix timestamp of the Ethereum transaction. """ timestamp: BigInt! """ Category of action, e.g., Deposit. """ category: FlowActionCategory! """ Contract through which the action was triggered. """ contract: Bytes! """ The Sablier fee paid in the native token of the chain (e.g., ETH for Mainnet). See https://docs.sablier.com/concepts/fees """ fee: BigInt """ Stream linked to this action, if any. """ stream: FlowStream """ Address of 1st actor. Who this is depends upon the action type, e.g. for Create, it is the sender. """ addressA: Bytes """ Address of 2nd actor. Who this is depends upon the action type, e.g. for Transfer, it is the recipient. """ addressB: Bytes """ 1st amount. What this is depends upon the action type, e.g. for Deposit, it is the deposit amount. """ amountA: BigInt """ 2nd amount. What this is depends upon the action type, e.g. for Withdraw, it is the refund amount. """ amountB: BigInt } """ A generic entity for tracking Lockup protocol actions. """ type LockupAction @entity(immutable: true) { """ Unique identifier: `action-{chainId}-{txHash}-{logIndex}` """ id: String! """ Unique global id as tracked by the `Watcher` entity. """ subgraphId: BigInt! """ Block number of the Ethereum transaction. """ block: BigInt! """ The chain ID where the action was created (e.g., 137 for Polygon). """ chainId: BigInt! """ The tx.origin of the Ethereum transaction. """ from: Bytes! """ Hash of the Ethereum transaction. """ hash: Bytes! """ Unix timestamp of the Ethereum transaction. """ timestamp: BigInt! """ Category of action, e.g., Deposit. """ category: LockupActionCategory! """ Contract through which the action was triggered. """ contract: Bytes! """ The Sablier fee paid in the native token of the chain (e.g., ETH for Mainnet). See https://docs.sablier.com/concepts/fees """ fee: BigInt """ Stream linked to this action, if any. """ stream: LockupStream """ Address of 1st actor. Who this is depends upon the action type, e.g. for Create, it is the sender. """ addressA: Bytes """ Address of 2nd actor. Who this is depends upon the action type, e.g. for Transfer, it is the recipient. """ addressB: Bytes """ 1st amount. What this is depends upon the action type, e.g. for Deposit, it is the deposit amount. """ amountA: BigInt """ 2nd amount. What this is depends upon the action type, e.g. for Withdraw, it is the refund amount. """ amountB: BigInt } """ Creating streams in bulk is possible using the Sablier batch contracts. """ type FlowBatch @entity(immutable: true) { """ Unique identifier: `batch-{chainId}-{txHash}-{batcher}` """ id: String! """ Hash of the Ethereum transaction that created this batch. """ hash: Bytes """ Timestamp of the transaction that created this batch. """ timestamp: BigInt """ The sender address that created this batch. """ batcher: FlowBatcher """ Index of the batch based on the `batchCounter` in the `Batcher` entity. """ position: BigInt """ Number of streams part of this batch. """ size: BigInt! """ Streams part of this batch. """ streams: [FlowStream!]! @derivedFrom(field: "batch") } """ Sender address that created batches. """ type FlowBatcher @entity(immutable: false) { """ Unique identifier: `batcher-{chainId}-{sender}` """ id: String! """ Total number of batches started by this sender. """ batchCounter: BigInt! """ Batches started by this sender. """ batches: [FlowBatch!]! @derivedFrom(field: "batcher") } """ Creating streams in bulk is possible using the Sablier batch contracts. """ type LockupBatch @entity(immutable: true) { """ Unique identifier: `batch-{chainId}-{txHash}-{batcher}` """ id: String! """ Hash of the Ethereum transaction that created this batch. """ hash: Bytes """ Timestamp of the transaction that created this batch. """ timestamp: BigInt """ The sender address that created this batch. """ batcher: LockupBatcher """ Index of the batch based on the `batchCounter` in the `Batcher` entity. """ position: BigInt """ Number of streams part of this batch. """ size: BigInt! """ Streams part of this batch. """ streams: [LockupStream!]! @derivedFrom(field: "batch") } """ Sender address that created batches. """ type LockupBatcher @entity(immutable: false) { """ Unique identifier: `batcher-{chainId}-{sender}` """ id: String! """ Total number of batches started by this sender. """ batchCounter: BigInt! """ Batches started by this sender. """ batches: [LockupBatch!]! @derivedFrom(field: "batcher") } """ An address that has sent USDC to the Sablier treasury for the billing process. """ type Sponsor @entity(immutable: false) { """ Unique identifier: `{chainId}_{address}` """ id: String! """ Address of the sponsor. """ address: Bytes! """ The chain ID where the sponsorship was made (sponsorships are chain-specific). """ chainId: BigInt! """ Number of sponsorships made by this sponsor. """ sponsorshipCount: Int! """ List of individual sponsorship transfers made by this sponsor. """ sponsorships: [Sponsorship!]! @derivedFrom(field: "sponsor") """ Cumulative raw USDC amount paid by the sponsor. """ totalAmount: BigInt! """ Human-readable cumulative USDC amount paid by the sponsor (e.g., "1729.12"). """ totalAmountDisplay: String! } """ A single USDC transfer to the Sablier treasury. Immutable — one per Transfer event. """ type Sponsorship @entity(immutable: true) { """ Unique identifier: `{chainId}_{txHash}_{logIndex}` """ id: String! """ Raw USDC amount. """ amount: BigInt! """ Human-readable USDC amount (e.g., "1719.12"). """ amountDisplay: String! """ Block number of the transaction. """ block: BigInt! """ The ID of the chain where the transfer occurred (e.g., 1 for Ethereum). """ chainId: BigInt! """ Log index of the Transfer event within the transaction. """ logIndex: Int! """ Address of the transaction signer (may differ from `from` for contract wallets). """ sender: Bytes! """ The sponsor who made this transfer. """ sponsor: Sponsor! """ Unix timestamp of the transaction. """ timestamp: BigInt! """ Hash of the transaction. """ txHash: Bytes! } """ A Sablier protocol contract """ type Contract @entity(immutable: false) { """ Unique identifier: contract-{chainId}-{address} """ id: String! """ Contract address """ address: Bytes! """ Contract alias (e.g., "FL", "LK") """ alias: String! """ Contract category (e.g., "Flow", "Lockup") """ category: String! """ The chain ID where the contract exists """ chainId: BigInt! } """ Older streams are no longer indexed. See https://x.com/Sablier/status/1914326014995620114 """ type DeprecatedStream @entity(immutable: true) { """ Unique identifier: \`{contractAddress}-{chainId}-{tokenId}\` """ id: String! """ The chain ID where the stream was created (e.g., 137 for Polygon). """ chainId: BigInt! """ The address of the contract that created the stream. """ contractAddress: Bytes! """ The id provided by the NFT contract. It's the ERC-721 tokenId. """ tokenId: BigInt! """ Hash of the Ethereum transaction that created this stream. """ hash: Bytes! """ Unix timestamp of the Ethereum transaction that created this stream. """ timestamp: BigInt! } """ See https://docs.sablier.com/concepts/lockup/segments """ type Segment @entity(immutable: true) { """ Unique identifier: `segment-{streamId}-{position}` """ id: String! """ Amount distributed by this segment. """ amount: BigInt! """ Total amount distributed at `endTime`. This is the sum of this segment's amount and all previous segments' amounts. """ endAmount: BigInt! """ Unix timestamp indicating the end of the segment. """ endTime: BigInt! """ Exponent used for the streamed amount calculations. See https://github.com/sablier-labs/lockup/blob/v2.0/src/types/DataTypes.sol#L279-L288 """ exponent: BigInt! """ Position of the segment inside the array. """ position: BigInt! """ Total amount distributed by the stream at `startTime`. This is the sum of all previous segments' amounts. """ startAmount: BigInt! """ Unix timestamp indicating the start of the segment. This is also the end time of the previous segment or, if this is the first segment, it is the start time of the stream. """ startTime: BigInt! """ The stream in which this segment was created. """ stream: LockupStream! } """ See https://docs.sablier.com/concepts/lockup/tranches """ type Tranche @entity(immutable: true) { """ Unique identifier: `tranche-{streamId}-{position}` """ id: String! """ Amount distributed by this tranche. """ amount: BigInt! """ Total amount distributed at `endTime`. This is the sum of this tranche's amount and all previous tranches' amounts. """ endAmount: BigInt! """ Unix timestamp indicating the end of the tranche. """ endTime: BigInt! """ Position of the tranche inside the array. """ position: BigInt! """ Total amount distributed by the stream at `startTime`. This is the sum of all previous tranches' amounts. """ startAmount: BigInt! """ Unix timestamp indicating the start of the tranche. This is also the end time of the previous tranche or, if this is the first tranche, it is the start time of the stream. """ startTime: BigInt! """ The stream in which this tranche was created. """ stream: LockupStream! } ``` --- ## Sablier Streams Source: https://docs.sablier.com/api/streams/indexers # Sablier Streams This page documents the indexers for the Sablier Streams protocol, which covers both [Lockup](/concepts/lockup/overview) (powering the [Vesting](/apps/features/vesting) product) and [Flow](/concepts/flow/overview) (powering the [Payments](/apps/features/payments) product) in the Sablier Interface. :::info The Lockup and Flow protocols are served by a unified indexer. In the GraphQL schema, entities are prefixed by protocol: `FlowStream`, `LockupStream`, `FlowAction`, `LockupAction`, `FlowBatch`, `LockupBatch`, etc. ::: ## Envio ### Source Code [ Envio indexer for Sablier Streamsenvio/streams ](https://github.com/sablier-labs/indexers/blob/main/envio/streams) ### Endpoints Envio is a multi-chain indexer. There's a single GraphQL API endpoint that aggregates data across chains. This approach differs from other vendors like The Graph, which require a separate indexer for each chain where Sablier is available. The standard Envio endpoints require [Hasura](https://docs.envio.dev/docs/HyperIndex/navigating-hasura) GraphQL query syntax. :::tip [Migrating from The Graph?] Envio also exposes `/converter` endpoints (see the table below) that accept subgraph-compatible queries, convert them to HyperIndex (standard GraphQL), and return responses in the same format as a standard subgraph. ::: | Chain | Production URL | Converter URL | Playground URL | Explorer URL | | --- | --- | --- | --- | --- | | Abstract | [Production](https://indexer.hyperindex.xyz/53b7e25/v1/graphql) | [Converter](https://indexer.hyperindex.xyz/53b7e25/v1/graphql/converter) | [Playground](https://cloud.hasura.io/public/graphiql?endpoint=https%3A%2F%2Findexer.hyperindex.xyz%2F53b7e25%2Fv1%2Fgraphql) | [Explorer](https://envio.dev/app/sablier-labs/lockup-envio) | | Arbitrum | [Production](https://indexer.hyperindex.xyz/53b7e25/v1/graphql) | [Converter](https://indexer.hyperindex.xyz/53b7e25/v1/graphql/converter) | [Playground](https://cloud.hasura.io/public/graphiql?endpoint=https%3A%2F%2Findexer.hyperindex.xyz%2F53b7e25%2Fv1%2Fgraphql) | [Explorer](https://envio.dev/app/sablier-labs/lockup-envio) | | Avalanche | [Production](https://indexer.hyperindex.xyz/53b7e25/v1/graphql) | [Converter](https://indexer.hyperindex.xyz/53b7e25/v1/graphql/converter) | [Playground](https://cloud.hasura.io/public/graphiql?endpoint=https%3A%2F%2Findexer.hyperindex.xyz%2F53b7e25%2Fv1%2Fgraphql) | [Explorer](https://envio.dev/app/sablier-labs/lockup-envio) | | Base | [Production](https://indexer.hyperindex.xyz/53b7e25/v1/graphql) | [Converter](https://indexer.hyperindex.xyz/53b7e25/v1/graphql/converter) | [Playground](https://cloud.hasura.io/public/graphiql?endpoint=https%3A%2F%2Findexer.hyperindex.xyz%2F53b7e25%2Fv1%2Fgraphql) | [Explorer](https://envio.dev/app/sablier-labs/lockup-envio) | | Blast | [Production](https://indexer.hyperindex.xyz/53b7e25/v1/graphql) | [Converter](https://indexer.hyperindex.xyz/53b7e25/v1/graphql/converter) | [Playground](https://cloud.hasura.io/public/graphiql?endpoint=https%3A%2F%2Findexer.hyperindex.xyz%2F53b7e25%2Fv1%2Fgraphql) | [Explorer](https://envio.dev/app/sablier-labs/lockup-envio) | | Berachain | [Production](https://indexer.hyperindex.xyz/53b7e25/v1/graphql) | [Converter](https://indexer.hyperindex.xyz/53b7e25/v1/graphql/converter) | [Playground](https://cloud.hasura.io/public/graphiql?endpoint=https%3A%2F%2Findexer.hyperindex.xyz%2F53b7e25%2Fv1%2Fgraphql) | [Explorer](https://envio.dev/app/sablier-labs/lockup-envio) | | BNB Chain | [Production](https://indexer.hyperindex.xyz/53b7e25/v1/graphql) | [Converter](https://indexer.hyperindex.xyz/53b7e25/v1/graphql/converter) | [Playground](https://cloud.hasura.io/public/graphiql?endpoint=https%3A%2F%2Findexer.hyperindex.xyz%2F53b7e25%2Fv1%2Fgraphql) | [Explorer](https://envio.dev/app/sablier-labs/lockup-envio) | | Chiliz | [Production](https://indexer.hyperindex.xyz/53b7e25/v1/graphql) | [Converter](https://indexer.hyperindex.xyz/53b7e25/v1/graphql/converter) | [Playground](https://cloud.hasura.io/public/graphiql?endpoint=https%3A%2F%2Findexer.hyperindex.xyz%2F53b7e25%2Fv1%2Fgraphql) | [Explorer](https://envio.dev/app/sablier-labs/lockup-envio) | | Gnosis | [Production](https://indexer.hyperindex.xyz/53b7e25/v1/graphql) | [Converter](https://indexer.hyperindex.xyz/53b7e25/v1/graphql/converter) | [Playground](https://cloud.hasura.io/public/graphiql?endpoint=https%3A%2F%2Findexer.hyperindex.xyz%2F53b7e25%2Fv1%2Fgraphql) | [Explorer](https://envio.dev/app/sablier-labs/lockup-envio) | | HyperEVM | [Production](https://indexer.hyperindex.xyz/53b7e25/v1/graphql) | [Converter](https://indexer.hyperindex.xyz/53b7e25/v1/graphql/converter) | [Playground](https://cloud.hasura.io/public/graphiql?endpoint=https%3A%2F%2Findexer.hyperindex.xyz%2F53b7e25%2Fv1%2Fgraphql) | [Explorer](https://envio.dev/app/sablier-labs/lockup-envio) | | Linea Mainnet | [Production](https://indexer.hyperindex.xyz/53b7e25/v1/graphql) | [Converter](https://indexer.hyperindex.xyz/53b7e25/v1/graphql/converter) | [Playground](https://cloud.hasura.io/public/graphiql?endpoint=https%3A%2F%2Findexer.hyperindex.xyz%2F53b7e25%2Fv1%2Fgraphql) | [Explorer](https://envio.dev/app/sablier-labs/lockup-envio) | | Ethereum | [Production](https://indexer.hyperindex.xyz/53b7e25/v1/graphql) | [Converter](https://indexer.hyperindex.xyz/53b7e25/v1/graphql/converter) | [Playground](https://cloud.hasura.io/public/graphiql?endpoint=https%3A%2F%2Findexer.hyperindex.xyz%2F53b7e25%2Fv1%2Fgraphql) | [Explorer](https://envio.dev/app/sablier-labs/lockup-envio) | | Mode | [Production](https://indexer.hyperindex.xyz/53b7e25/v1/graphql) | [Converter](https://indexer.hyperindex.xyz/53b7e25/v1/graphql/converter) | [Playground](https://cloud.hasura.io/public/graphiql?endpoint=https%3A%2F%2Findexer.hyperindex.xyz%2F53b7e25%2Fv1%2Fgraphql) | [Explorer](https://envio.dev/app/sablier-labs/lockup-envio) | | Monad | [Production](https://indexer.hyperindex.xyz/53b7e25/v1/graphql) | [Converter](https://indexer.hyperindex.xyz/53b7e25/v1/graphql/converter) | [Playground](https://cloud.hasura.io/public/graphiql?endpoint=https%3A%2F%2Findexer.hyperindex.xyz%2F53b7e25%2Fv1%2Fgraphql) | [Explorer](https://envio.dev/app/sablier-labs/lockup-envio) | | Morph | [Production](https://indexer.hyperindex.xyz/53b7e25/v1/graphql) | [Converter](https://indexer.hyperindex.xyz/53b7e25/v1/graphql/converter) | [Playground](https://cloud.hasura.io/public/graphiql?endpoint=https%3A%2F%2Findexer.hyperindex.xyz%2F53b7e25%2Fv1%2Fgraphql) | [Explorer](https://envio.dev/app/sablier-labs/lockup-envio) | | OP Mainnet | [Production](https://indexer.hyperindex.xyz/53b7e25/v1/graphql) | [Converter](https://indexer.hyperindex.xyz/53b7e25/v1/graphql/converter) | [Playground](https://cloud.hasura.io/public/graphiql?endpoint=https%3A%2F%2Findexer.hyperindex.xyz%2F53b7e25%2Fv1%2Fgraphql) | [Explorer](https://envio.dev/app/sablier-labs/lockup-envio) | | Polygon | [Production](https://indexer.hyperindex.xyz/53b7e25/v1/graphql) | [Converter](https://indexer.hyperindex.xyz/53b7e25/v1/graphql/converter) | [Playground](https://cloud.hasura.io/public/graphiql?endpoint=https%3A%2F%2Findexer.hyperindex.xyz%2F53b7e25%2Fv1%2Fgraphql) | [Explorer](https://envio.dev/app/sablier-labs/lockup-envio) | | Sonic | [Production](https://indexer.hyperindex.xyz/53b7e25/v1/graphql) | [Converter](https://indexer.hyperindex.xyz/53b7e25/v1/graphql/converter) | [Playground](https://cloud.hasura.io/public/graphiql?endpoint=https%3A%2F%2Findexer.hyperindex.xyz%2F53b7e25%2Fv1%2Fgraphql) | [Explorer](https://envio.dev/app/sablier-labs/lockup-envio) | | Scroll | [Production](https://indexer.hyperindex.xyz/53b7e25/v1/graphql) | [Converter](https://indexer.hyperindex.xyz/53b7e25/v1/graphql/converter) | [Playground](https://cloud.hasura.io/public/graphiql?endpoint=https%3A%2F%2Findexer.hyperindex.xyz%2F53b7e25%2Fv1%2Fgraphql) | [Explorer](https://envio.dev/app/sablier-labs/lockup-envio) | | Sei Network | [Production](https://indexer.hyperindex.xyz/53b7e25/v1/graphql) | [Converter](https://indexer.hyperindex.xyz/53b7e25/v1/graphql/converter) | [Playground](https://cloud.hasura.io/public/graphiql?endpoint=https%3A%2F%2Findexer.hyperindex.xyz%2F53b7e25%2Fv1%2Fgraphql) | [Explorer](https://envio.dev/app/sablier-labs/lockup-envio) | | Sophon | [Production](https://indexer.hyperindex.xyz/53b7e25/v1/graphql) | [Converter](https://indexer.hyperindex.xyz/53b7e25/v1/graphql/converter) | [Playground](https://cloud.hasura.io/public/graphiql?endpoint=https%3A%2F%2Findexer.hyperindex.xyz%2F53b7e25%2Fv1%2Fgraphql) | [Explorer](https://envio.dev/app/sablier-labs/lockup-envio) | | Superseed | [Production](https://indexer.hyperindex.xyz/53b7e25/v1/graphql) | [Converter](https://indexer.hyperindex.xyz/53b7e25/v1/graphql/converter) | [Playground](https://cloud.hasura.io/public/graphiql?endpoint=https%3A%2F%2Findexer.hyperindex.xyz%2F53b7e25%2Fv1%2Fgraphql) | [Explorer](https://envio.dev/app/sablier-labs/lockup-envio) | | Unichain | [Production](https://indexer.hyperindex.xyz/53b7e25/v1/graphql) | [Converter](https://indexer.hyperindex.xyz/53b7e25/v1/graphql/converter) | [Playground](https://cloud.hasura.io/public/graphiql?endpoint=https%3A%2F%2Findexer.hyperindex.xyz%2F53b7e25%2Fv1%2Fgraphql) | [Explorer](https://envio.dev/app/sablier-labs/lockup-envio) | | XDC | [Production](https://indexer.hyperindex.xyz/53b7e25/v1/graphql) | [Converter](https://indexer.hyperindex.xyz/53b7e25/v1/graphql/converter) | [Playground](https://cloud.hasura.io/public/graphiql?endpoint=https%3A%2F%2Findexer.hyperindex.xyz%2F53b7e25%2Fv1%2Fgraphql) | [Explorer](https://envio.dev/app/sablier-labs/lockup-envio) | | ZKsync Era | [Production](https://indexer.hyperindex.xyz/53b7e25/v1/graphql) | [Converter](https://indexer.hyperindex.xyz/53b7e25/v1/graphql/converter) | [Playground](https://cloud.hasura.io/public/graphiql?endpoint=https%3A%2F%2Findexer.hyperindex.xyz%2F53b7e25%2Fv1%2Fgraphql) | [Explorer](https://envio.dev/app/sablier-labs/lockup-envio) | | Base Sepolia | [Production](https://indexer.hyperindex.xyz/53b7e25/v1/graphql) | [Converter](https://indexer.hyperindex.xyz/53b7e25/v1/graphql/converter) | [Playground](https://cloud.hasura.io/public/graphiql?endpoint=https%3A%2F%2Findexer.hyperindex.xyz%2F53b7e25%2Fv1%2Fgraphql) | [Explorer](https://envio.dev/app/sablier-labs/lockup-envio) | | Sepolia | [Production](https://indexer.hyperindex.xyz/53b7e25/v1/graphql) | [Converter](https://indexer.hyperindex.xyz/53b7e25/v1/graphql/converter) | [Playground](https://cloud.hasura.io/public/graphiql?endpoint=https%3A%2F%2Findexer.hyperindex.xyz%2F53b7e25%2Fv1%2Fgraphql) | [Explorer](https://envio.dev/app/sablier-labs/lockup-envio) | ## The Graph ### Source Code [ Graph indexer for Sablier Streamsgraph/streams ](https://github.com/sablier-labs/indexers/blob/main/graph/streams) ### Endpoints In the table below, you will see three URLs: - `Production URL`: requires a Studio API key for making queries. - `Testing URL`: doesn't require an API key but it's rate-limited to 3000 queries per day. Opening this URL in the browser opens a GraphiQL playground. - `Explorer URL`: The explorer URL for the indexer, if available. To learn how to create a Studio API key, check out [this guide](https://thegraph.com/docs/en/studio/managing-api-keys). The API key has to be provided via an `Authorization: Bearer ` header. Here are two examples in curl and JavaScript: **curl** ```bash curl -X POST \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"query": "{ _meta: { block { number } } }"' \ ``` **JavaScript** ```js import { GraphQLClient } from "graphql-request"; async function getBlockNumber() { const client = new GraphQLClient("", { headers: { Authorization: `Bearer `, }, }); const query = `query { _meta: { block { number } } }`; const result = await client.request(query); console.log(result); } ``` | Chain | Production URL | Testing URL | Explorer URL | | --- | --- | --- | --- | | Abstract | [Production](https://gateway.thegraph.com/api/subgraphs/id/2QjTdDFY233faXksUruMERMiDoQDdtGG5hBLC27aT1Pw) | [Testing](https://api.studio.thegraph.com/query/112500/sablier-lockup-abstract/version/latest) | [Explorer](https://thegraph.com/explorer/subgraphs/2QjTdDFY233faXksUruMERMiDoQDdtGG5hBLC27aT1Pw) | | Arbitrum | [Production](https://gateway.thegraph.com/api/subgraphs/id/yvDXXHSyv6rGPSzfpbBcbQmMFrECac3Q2zADkYsMxam) | [Testing](https://api.studio.thegraph.com/query/112500/sablier-lockup-arbitrum/version/latest) | [Explorer](https://thegraph.com/explorer/subgraphs/yvDXXHSyv6rGPSzfpbBcbQmMFrECac3Q2zADkYsMxam) | | Avalanche | [Production](https://gateway.thegraph.com/api/subgraphs/id/FTDmonvFEm1VGkzECcnDY2CPHcW5dSmHRurSjEEfTkCX) | [Testing](https://api.studio.thegraph.com/query/112500/sablier-lockup-avalanche/version/latest) | [Explorer](https://thegraph.com/explorer/subgraphs/FTDmonvFEm1VGkzECcnDY2CPHcW5dSmHRurSjEEfTkCX) | | Base | [Production](https://gateway.thegraph.com/api/subgraphs/id/778GfecD9tsyB4xNnz4wfuAyfHU6rqGr79VCPZKu3t2F) | [Testing](https://api.studio.thegraph.com/query/112500/sablier-lockup-base/version/latest) | [Explorer](https://thegraph.com/explorer/subgraphs/778GfecD9tsyB4xNnz4wfuAyfHU6rqGr79VCPZKu3t2F) | | Base Sepolia | [Production](https://gateway.thegraph.com/api/subgraphs/id/DdiYENuyh5ztSybRJnBnCZuUgESkFasjGFHZUbURpKHz) | [Testing](https://api.studio.thegraph.com/query/112500/sablier-lockup-base-sepolia/version/latest) | [Explorer](https://thegraph.com/explorer/subgraphs/DdiYENuyh5ztSybRJnBnCZuUgESkFasjGFHZUbURpKHz) | | Berachain | [Production](https://gateway.thegraph.com/api/subgraphs/id/C2r13APcUemQtVdPFm7p7T3aJkU2rH2EvdZzrQ53zi14) | [Testing](https://api.studio.thegraph.com/query/112500/sablier-lockup-berachain/version/latest) | [Explorer](https://thegraph.com/explorer/subgraphs/C2r13APcUemQtVdPFm7p7T3aJkU2rH2EvdZzrQ53zi14) | | BNB Chain | [Production](https://gateway.thegraph.com/api/subgraphs/id/A8Vc9hi7j45u7P8Uw5dg4uqYJgPo4x1rB4oZtTVaiccK) | [Testing](https://api.studio.thegraph.com/query/112500/sablier-lockup-bsc/version/latest) | [Explorer](https://thegraph.com/explorer/subgraphs/A8Vc9hi7j45u7P8Uw5dg4uqYJgPo4x1rB4oZtTVaiccK) | | Chiliz | [Production](https://gateway.thegraph.com/api/subgraphs/id/4KsXUFvsKFHH7Q8k3BPgEv2NhCJJGwG78gCPAUpncYb) | [Testing](https://api.studio.thegraph.com/query/112500/sablier-lockup-chiliz/version/latest) | [Explorer](https://thegraph.com/explorer/subgraphs/4KsXUFvsKFHH7Q8k3BPgEv2NhCJJGwG78gCPAUpncYb) | | Denergy | [Production](https://thegraph.denergychain.com/subgraphs/name/denergychain/sablier-lockup-denergychain) | N/A | [Explorer](https://thegraph.denergychain.com/subgraphs/name/denergychain/sablier-lockup-denergychain/graphql) | | Ethereum | [Production](https://gateway.thegraph.com/api/subgraphs/id/AvDAMYYHGaEwn9F9585uqq6MM5CfvRtYcb7KjK7LKPCt) | [Testing](https://api.studio.thegraph.com/query/112500/sablier-lockup-ethereum/version/latest) | [Explorer](https://thegraph.com/explorer/subgraphs/AvDAMYYHGaEwn9F9585uqq6MM5CfvRtYcb7KjK7LKPCt) | | Gnosis | [Production](https://gateway.thegraph.com/api/subgraphs/id/DtKniy1RvB19q1r2g1WLN4reMNKDacEnuAjh284rW2iK) | [Testing](https://api.studio.thegraph.com/query/112500/sablier-lockup-gnosis/version/latest) | [Explorer](https://thegraph.com/explorer/subgraphs/DtKniy1RvB19q1r2g1WLN4reMNKDacEnuAjh284rW2iK) | | Lightlink | [Production](https://graph.phoenix.lightlink.io/query/subgraphs/name/lightlink/sablier-lockup-lightlink) | N/A | [Explorer](https://graph.phoenix.lightlink.io/query/subgraphs/name/lightlink/sablier-lockup-lightlink/graphql) | | Linea Mainnet | [Production](https://gateway.thegraph.com/api/subgraphs/id/GvpecytqVzLzuwuQB3enozXoaZRFoVx8Kr7qrfMiE9bs) | [Testing](https://api.studio.thegraph.com/query/112500/sablier-lockup-linea/version/latest) | [Explorer](https://thegraph.com/explorer/subgraphs/GvpecytqVzLzuwuQB3enozXoaZRFoVx8Kr7qrfMiE9bs) | | OP Mainnet | [Production](https://gateway.thegraph.com/api/subgraphs/id/NZHzd2JNFKhHP5EWUiDxa5TaxGCFbSD4g6YnYr8JGi6) | [Testing](https://api.studio.thegraph.com/query/112500/sablier-lockup-optimism/version/latest) | [Explorer](https://thegraph.com/explorer/subgraphs/NZHzd2JNFKhHP5EWUiDxa5TaxGCFbSD4g6YnYr8JGi6) | | Polygon | [Production](https://gateway.thegraph.com/api/subgraphs/id/8fgeQMEQ8sskVeWE5nvtsVL2VpezDrAkx2d1VeiHiheu) | [Testing](https://api.studio.thegraph.com/query/112500/sablier-lockup-polygon/version/latest) | [Explorer](https://thegraph.com/explorer/subgraphs/8fgeQMEQ8sskVeWE5nvtsVL2VpezDrAkx2d1VeiHiheu) | | Scroll | [Production](https://gateway.thegraph.com/api/subgraphs/id/GycpYx8c9eRqxvEAfqnpNd1ZfXeuLzjRhnG7vvYaqEE1) | [Testing](https://api.studio.thegraph.com/query/112500/sablier-lockup-scroll/version/latest) | [Explorer](https://thegraph.com/explorer/subgraphs/GycpYx8c9eRqxvEAfqnpNd1ZfXeuLzjRhnG7vvYaqEE1) | | Sepolia | [Production](https://gateway.thegraph.com/api/subgraphs/id/5yDtFSxyRuqyjvGJyyuQhMEW3Uah7Ddy2KFSKVhy9VMa) | [Testing](https://api.studio.thegraph.com/query/112500/sablier-lockup-sepolia/version/latest) | [Explorer](https://thegraph.com/explorer/subgraphs/5yDtFSxyRuqyjvGJyyuQhMEW3Uah7Ddy2KFSKVhy9VMa) | | Sonic | [Production](https://gateway.thegraph.com/api/subgraphs/id/GnaSPX9XLkPn219CqbGFU1NgveuQk2Hh3c8WxjtesaEh) | [Testing](https://api.studio.thegraph.com/query/112500/sablier-lockup-sonic/version/latest) | [Explorer](https://thegraph.com/explorer/subgraphs/GnaSPX9XLkPn219CqbGFU1NgveuQk2Hh3c8WxjtesaEh) | | Unichain | [Production](https://gateway.thegraph.com/api/subgraphs/id/3MUG4H3gZcp9fpGLiJMTMeUFcQQ6QdT317P4wYKyns9M) | [Testing](https://api.studio.thegraph.com/query/112500/sablier-lockup-unichain/version/latest) | [Explorer](https://thegraph.com/explorer/subgraphs/3MUG4H3gZcp9fpGLiJMTMeUFcQQ6QdT317P4wYKyns9M) | | ZKsync Era | [Production](https://gateway.thegraph.com/api/subgraphs/id/7SuEYGYwZ835LjVGB85ZE8z5zmqdKgmRh8kAEeJefWQN) | [Testing](https://api.studio.thegraph.com/query/112500/sablier-lockup-zksync/version/latest) | [Explorer](https://thegraph.com/explorer/subgraphs/7SuEYGYwZ835LjVGB85ZE8z5zmqdKgmRh8kAEeJefWQN) | --- ## Vesting Source: https://docs.sablier.com/apps/features/vesting # Vesting The Sablier Interface will showcase [Lockup](/concepts/lockup/overview) streams under the Vesting tab. These are token streams with a fixed duration, predefined amount and strict distribution curve. ![Banner Vesting](/assets/images/docs-vesting-b9c77780752ca1f1966024599b2c2a27.webp) In Q4 2024 the app has undergone a use-case centric redesign. For past users, all streams created before this update will show up in the Vesting page. | [Vesting streams](https://app.sablier.com/vesting/gallery) | | --- | | ![Vesting stream profile](/screenshots/vesting-gallery.webp) | ## Features ### Flexible curves Almost any vesting schedule can be expressed as a stream. Sablier supports multiple options out of the box, including: - Linear - Cliff-Linear - Unlock in Steps - Unlock Monthly - Backweighted - Timelock - Unlock-Linear - Unlock-Dynamic - Exponential - Cliff-Exponential But programmatically, you can create any schedule you want. See the [Stream Shapes](/concepts/lockup/stream-shapes) for more details on how we design these shapes. ### Explore the dashboard Enter the Dashboard and discover a detailed overview of your incoming and outgoing streams. Take advantage of the Search functionality to explore the chain and gain more insight into how others are using Lockup for vesting. | | | --- | | ![All](/assets/images/vesting-dashboard-e5c79efba4315eb9cb571ef91e8b326f.webp) | ### Preview any stream Gain insight into any stream. Track progress using our very own stream circle. Share the unique URL with recipients or anyone really to increase transparency of your day to day token distribution. | | | --- | | ![All](/assets/images/vesting-profile-540367bd246535251c934699a0e95102.webp) | ### NFT representation Each stream in wrapped in an ERC-721 non-fungible token (NFT), making the stream recipient the owner of the NFT. Thus streams can be transferred, traded, and used as collateral in NFT lending protocols. | | | --- | | | ### Multi-chain experience Streaming, everywhere. We support 11+ EVM chains and testnets where you can stream or get paid using Sablier. | | | --- | | ![ChainList](/assets/images/general-chains-6b88571266d34394375000a804f11399.webp) | ### Create in bulk Save time by creating up to 280 streams in bulk for your employees, investors, or community members. Use the forms for a clean and straightforward UX. | | | --- | | ![Create in bulk](/assets/images/vesting-create-80ca35f0145fd0f845006b5575f20ed2.webp) | ### Create with CSV As an alternative to manually filling out the form, you can upload a CSV file with your user data. | | | --- | | ![Create with CSV](/assets/images/vesting-create-csv-2ac43351a4ad0a44b1c6e6aa0fef1ae6.webp) | ### Simulations Eager to see what your token distribution will look like? Use our simulation tool right from the stream creation forms (or later, from the stream profile). ![Simulate](/assets/images/simulation-4564c55be55624a63dc47960d3714f1a.webp) ### Detailed panels Explore each available stream in detail. Simulate future payouts, understand historical events, or simply enjoy cool representations of a monetized passage of time (NFTs 🔥). ### Granular management Manage your streams 24/7 as you see fit. The app will guide you through every supported process and help you keep an eye on your payouts. Remember, you can always be both a sender and a recipient. | | | | --- | --- | | ![Withdraw](/assets/images/cover-withdraw-7ba642af2360a98b7abfaa7c704f5fd8.webp) | ![Cancel](/assets/images/cover-cancel-35581d48504a874e6ad2367d869f0b88.webp) | ### Any ERC-20 token Thanks to our integration of Token Lists, any ERC-20 token can be distributed via Sablier Lockup. :::warning The only exception is rebasing tokens like Aave's aTokens. Tokens that dynamically rebase their balance are not supported by Sablier. ::: | | | | --- | --- | | ![Token List](/assets/images/tokenlist-home-4cadcae6572e235d8fde6e2606896bd5.webp) | ![Token List Search](/assets/images/tokenlist-search-3fa398cc7c3234a208ae0340294ba47f.webp) | ### Mobile-ready layout Token streams on the go! Yes, the Sablier app works on mobile. And yes, we support dark mode by default (light mode coming soon). ### Permissions We've mapped the most important utilities from the Lockup contracts into in-app features. Interact with streams that reference you as a key participant (e.g. sender, recipient) directly from the interfaces. | Feature | Sender | Recipient | Public | | --- | :---: | :---: | :---: | | Create Streams | ✅ | \- | \- | | Renounce Cancelability | ✅ | ❌ | \- | | Cancel | ✅ | ❌ | \- | | Transfer | ❌ | ✅ | \- | | Withdraw | ✅ | ✅ | ✅ | ### Safe multisig support Vesting is fully integrated with [Safe](https://safe.global). Start streaming from the safety and comfort of your multisig wallet. --- ## CSV Support Source: https://docs.sablier.com/apps/guides/csv-support # CSV Support The Sablier Interfaces supports CSV files for faster processing and automating large-scale operations. This feature is available for both airdrops and streams. :::warning Formatting Caveats **Dates**: All columns with the "date" type should have the following format: "YYYY-MM-DD HH:mm". **Durations**: All columns with the "duration" type should have the following format: "**x** years **y** days **z** hours". Note that each particle is optional, e.g., you can skip the days. **Timezones**: The dates and times extracted from the CSV are processed using the same timezone used by the user's browser. **Amounts**: All token amounts should be expressed in humanized form, e.g., 10 USDC should be written as `10`, not `10000000`. The Sablier app will multiply the amounts by the token's number of decimals in the processing step. ::: ## Airdrops With Sablier, you can create airdrop campaigns with up to a million recipients. To do so, you must upload a CSV file containing all recipient addresses and the airdrop amounts. Use the provided template and fill in the rows with recipient addresses and airdrop amounts. ### CSV Template For your convenience, here's a download link for the CSV template: [ Sablier - Airdrop CSV TemplateCSV template for creating airdrops on Sablier ](https://files.sablier.com/templates/airdrop-template.csv) ### Navigation To use this feature: 1. Access the [create airdrop](https://app.sablier.com/airdrops/create) page 2. Fill out the details for your airdrop campaign in the 1st step 3. Continue to the 2nd step, where you can upload the CSV | | | --- | | ![Airdrop Create](/assets/images/airdrop-create-2-a4acb86a8c7cf35670e136296754fed8.webp) | ## Streams | | | --- | | ![Create with CSV](/assets/images/create-with-csv-d8919d393e2f8db086dc7b47d7ed6bd0.webp) | Using a CSV, you can deploy up to 280 streams all at once. Start from the suggested template, and fill in the rows with addresses, amounts, and other details. ### CSV Template Here's table with all the available CSV templates. [Sablier Flow](/concepts/use-cases#sablier-flow) (the first row in the below table) is a great fit for use cases like payroll, grants, and subscriptions. The other streaming curves in the table rely on [Sablier Lockup](/concepts/use-cases#sablier-lockup) and are a better fit for vesting and airdrops. | URL | Description | | --- | --- | | [Flow](https://files.sablier.com/templates/flow-template.csv) | [Open-ended streams](/concepts/flow/overview#total-debt) that can be topped up. | | [Linear with duration](https://files.sablier.com/templates/linear-duration-template.csv) | [Linear streams](/concepts/lockup/stream-shapes#linear) with the duration timing. | | [Linear with range](https://files.sablier.com/templates/linear-range-template.csv) | [Linear streams](/concepts/lockup/stream-shapes#linear) with the range timing. | | [Cliff with duration](https://files.sablier.com/templates/2025-02/cliff-duration-template.csv) | [Cliff streams](/concepts/lockup/stream-shapes#unlock-cliff) with the duration timing. | | [Cliff with range](https://files.sablier.com/templates/2025-02/cliff-range-template.csv) | [Cliff streams](/concepts/lockup/stream-shapes#unlock-cliff) with the range timing. | | [Monthly with range](https://files.sablier.com/templates/monthly-range-template.csv) | [Unlock Each Month streams](/concepts/lockup/stream-shapes#unlock-monthly) with the range timing. | | [Stepper with duration](https://files.sablier.com/templates/2025-02/unlockSteps-duration-template.csv) | [Unlock In Steps streams](/concepts/lockup/stream-shapes#unlock-in-steps) with the duration timing. | | [Stepper with range](https://files.sablier.com/templates/2025-02/unlockSteps-range-template.csv) | [Unlock In Steps streams](/concepts/lockup/stream-shapes#unlock-in-steps) with the range timing. | | [Timelock with duration](https://files.sablier.com/templates/timelock-duration-template.csv) | [Timelock streams](/concepts/lockup/stream-shapes#timelock) with the duration timing. | | [Timelock with range](https://files.sablier.com/templates/timelock-range-template.csv) | [Timelock streams](/concepts/lockup/stream-shapes#timelock) with the range timing. | | [BackWeighted with range](https://files.sablier.com/templates/backWeighted-range-template.csv) | [BackWeighted streams](/concepts/lockup/stream-shapes) with the range timing. | | [Unlock linear with duration](https://files.sablier.com/templates/unlockLinear-duration-template.csv) | [Unlock-Linear streams](/concepts/lockup/stream-shapes#unlock-linear) with the duration timing. | | [Unlock linear with range](https://files.sablier.com/templates/unlockLinear-range-template.csv) | [Unlock-Linear streams](/concepts/lockup/stream-shapes#unlock-linear) with the range timing. | | [Unlock cliff with duration](https://files.sablier.com/templates/2025-02/unlockCliff-duration-template.csv) | [Unlock-Cliff streams](/concepts/lockup/stream-shapes#unlock-cliff) with the duration timing. | | [Unlock cliff with range](https://files.sablier.com/templates/2025-02/unlockCliff-range-template.csv) | [Unlock-Cliff streams](/concepts/lockup/stream-shapes#unlock-cliff) with the range timing. | | [Exponential with duration](https://files.sablier.com/templates/exponential-duration-template.csv) | [Exponential streams](/concepts/lockup/stream-shapes#exponential) with the duration timing. | | [Exponential with range](https://files.sablier.com/templates/exponential-range-template.csv) | [Exponential streams](/concepts/lockup/stream-shapes#exponential) with the range timing. | | [Cliff exponential with duration](https://files.sablier.com/templates/exponentialCliff-duration-template.csv) | [Cliff-Exponential streams](/concepts/lockup/stream-shapes#cliff-exponential) with the duration timing. | | [Cliff exponential with range](https://files.sablier.com/templates/exponentialCliff-range-template.csv) | [Cliff-Exponential streams](/concepts/lockup/stream-shapes#cliff-exponential) with the range timing. | ### Navigation To use this feature: 1. Access the [vesting gallery](https://app.sablier.com/vesting/gallery/) page in the Sablier app 2. Select the desired vesting shape 3. In the top right corner, you will find a button guiding you toward the CSV feature | | | --- | | | ### Column Formats To use the CSV feature, the data you provide must be formatted correctly. Below is a list with the format expected for all column types supported by Sablier. :::warning Make sure that your CSV editing software (e.g. Microsoft Excel) does not override the cell format. We suggest double-checking in the Sablier app that the dates have been parsed as expected. ::: | Column | Type | Description | Examples | | --- | --- | --- | --- | | address | String | Recipient address | `0x12...AB` | | amount | Number | Deposit amount | `100`, `42161` or any other valid amount | | duration | String | Total duration | `1 year 20 days`, `3 years 20 days 4 hours` | | start | Date | Start date in `YYYY-MM-DD HH:mm` format | `2024-02-24 16:15`, `2026-02-14 17:25` | | end | Date | End date in `YYYY-MM-DD HH:mm` format | `2024-02-24 16:15`, `2026-02-14 17:25` | | cliffDuration | String | Cliff duration | `2 years 20 days`, `3 years 20 days 4 hours` | | cliffEnd | Date | Cliff date in `YYYY-MM-DD HH:mm` format | `2024-02-24 16:15`, `2026-02-14 17:25` | | months | Number | Number of months for the unlock monthly | `5`, `12` or any other valid integer | | steps | Number | Number of steps for the unlock in steps | `5`, `12` or any other valid integer | | unlock | Number | Amount that will be initially unlocked | `100`, `42161` or any other valid amount | | initial | String | Whether the first unlock should occur at the start date or at the end of the first month | `at start` or `end of first month` | --- ## How-to Videos Source: https://docs.sablier.com/apps/guides/how-to # How-to Videos For an extensive set of video explanations please check out the [Support](/support/how-to) section. --- ## URL Schemes Source: https://docs.sablier.com/apps/guides/url-schemes # URL Schemes The Sablier Interface makes it easy for integrators to deep-link into specific streams, campaigns, or filtered searches. The portal is split into three products — **vesting**, **payments**, and **airdrops** — and each one has a profile page and a search page reachable through composable URLs. This guide documents the URL schemes you can build against. ## Stream Page | | | --- | | ![Stream profile](/assets/images/stream-share-f4d859daf33b741099adbfc7632e4423.webp) | ### Elements Every stream URL is built from one product namespace plus three identifier parts: - a **kind** namespace — `vesting` (Lockup) or `payments` (Flow), used as the URL prefix - a **source** — either an uppercase contract **alias** (e.g. `LL3`, `FL4`) or a lowercase contract **address** (e.g. `0xb10d…f95`) - a **chainId** (e.g., `1` for Ethereum, `137` for Polygon, `8453` for Base) - a **streamId** — the ERC-721 token ID assigned at stream creation See [Identifiers](/api/ids) for the full alias table covering every released Lockup and Flow contract. ### Building the URL Concatenate the three identifier parts with hyphens (`source-chainId-streamId`) and append the result to the product-namespaced base URL `app.sablier.com/{kind}/stream/`: | URL | Description | | --- | --- | | [app.sablier.com/vesting/stream/LL3-137-29](https://app.sablier.com/vesting/stream/LL3-137-29) | Lockup v1.2 Linear vesting stream #29 on Polygon | | [app.sablier.com/vesting/stream/LK3-1-12](https://app.sablier.com/vesting/stream/LK3-1-12) | Lockup v4.0 vesting stream #12 on Ethereum | | [app.sablier.com/vesting/stream/0xB10…f95-1-6](https://app.sablier.com/vesting/stream/0xB10daee1FCF62243aE27776D7a92D39dC8740f95-1-6) | Vesting stream #6 on Ethereum, addressed by contract address | | [app.sablier.com/payments/stream/FL4-8453-100](https://app.sablier.com/payments/stream/FL4-8453-100) | Flow v3.0 payment stream #100 on Base | | [app.sablier.com/payments/stream/FL2-11155111-40](https://app.sablier.com/payments/stream/FL2-11155111-40) | Flow v1.1 payment stream #40 on Ethereum Sepolia | The portal normalizes the alias to uppercase and the contract address to lowercase before resolving the stream. #### Sub-routes Append a sub-route to land on a specific tab of the stream page: | Sub-route | Effect | Example | | --- | --- | --- | | `/details` | Opens the **Details** modal view | `app.sablier.com/vesting/stream/LL3-137-29/details` | | `/history` | Opens the **History** modal view | `app.sablier.com/payments/stream/FL4-8453-100/history` | --- ## Search Streams ### Route Shape Stream search is **path-based**. The chain lives in the path for vesting, and the asset (when present) is always the last path segment: - Vesting: `app.sablier.com/vesting/search/{chain}/{token?}` - Payments: `app.sablier.com/payments/search/{token?}` `{chain}` is the lowercase chain slug — `ethereum` for chain ID `1`, otherwise the chain's slug (`polygon`, `base`, `arbitrum`, `optimism`, `sepolia`, …). `{token?}` is the optional ERC-20 contract address to filter by. To land on the dashboard with a specific tab instead of the search view, use `/vesting` or `/payments` with `t=`. ### Query Parameters These parameters apply to both vesting and payments search: | Parameter | Code | Type | Description | Example | | --- | --- | --- | --- | --- | | chainId | `c` | Number | Chain to filter by. On `/vesting/search` the chain lives in the path; on `/payments/search` it stays as a query param. | `c=137` | | sender | `s` | Address / ENS | Stream sender (creator). | `s=vitalik.eth` | | recipient | `r` | Address / ENS | Stream recipient. | `r=0x12…AB` | | ids | `i` | Comma-separated | Filter by stream IDs or aliases. | `i=LL2-1-2,LL3-1-29` | | filter | `f` | Comma-separated | Status filter (e.g., `streaming`, `paused`, `cancelled`, `settled`). | `f=streaming,cancelled` | | sortField | `so` | Enum | `default`, `value`, or `timeline`. Omitted when `default`. | `so=value` | | sortDir | `sd` | Enum | `asc` or `desc`. Omitted when `desc`. | `sd=asc` | | page | `p` | Number | 1-indexed page number. Omitted on page 1. | `p=2` | | tab | `t` | Enum | Dashboard tab: `created`, `recipient`, or `sender`. Used on `/vesting` and `/payments`, not on `/search`. | `t=recipient` | Address values for `s` and `r` are case-insensitive on input — the portal accepts checksummed (`0xB10d…F95`) and lowercase (`0xb10d…f95`) forms equally — but it lowercases them when emitting URLs of its own. ENS names are passed through as-is. ### Examples | URL | Description | Preview | | --- | --- | --- | | [vesting/search/ethereum?s=0x0aAe…72cC](https://app.sablier.com/vesting/search/ethereum?s=0x0aAeF7BbC21c627f14caD904E283e199cA2b72cC) | Vesting streams on Ethereum created by a particular sender | ![Form](/assets/images/search-with-sender-31f311469512bd73c175727d36e628d0.webp) | | [vesting/search/ethereum?i=LL2-1-2,LL3-1-29](https://app.sablier.com/vesting/search/ethereum?i=LL2-1-2,LL3-1-29) | Vesting streams matching specific IDs on Ethereum | ![Form](/assets/images/search-with-ids-27542b27ceb4e7fcea2f492ba3dbc42c.webp) | | [payments/search/0xa0b8…eB48?c=8453](https://app.sablier.com/payments/search/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48?c=8453) | Payments streams in USDC on Base | — | | [vesting?t=sender&f=streaming](https://app.sablier.com/vesting?t=sender&f=streaming) | Vesting dashboard, "sender" tab, only currently streaming | — | :::info The legacy `?t=search` query is no longer emitted by the portal. New integrations should use the path-based search routes above. ::: --- ## Airdrop Page | | | --- | | ![Airdrop Profile](/assets/images/airdrop-profile-a83c872b319d1ff5205fd6eb4c73cd04.webp) | ### Elements Every airdrop campaign is identified by two parts: - a **chainId** (e.g. `1` for Ethereum, `137` for Polygon) - a **contract address** (e.g. `0x12…AB`) :::info Airdrops do not use aliases — campaigns are referenced by their contract address. See [Identifiers](/api/ids) for details. ::: ### Building the URL Concatenate the address and chain ID with a hyphen and append to `app.sablier.com/airdrops/campaign/`. The address is lowercased. | URL | Description | | --- | --- | | [app.sablier.com/airdrops/campaign/0xe721…bbabc-11155111](https://app.sablier.com/airdrops/campaign/0xe72175dd12ac7efca6b7d12dfc913a5f661bbabc-11155111) | Campaign on Ethereum Sepolia | #### Sub-route Append `/manage` to land directly on the admin manage view (only useful to the campaign admin): `app.sablier.com/airdrops/campaign/0xe721…bbabc-11155111/manage` --- ## Search Airdrops ### Route Shape `app.sablier.com/airdrops/search/{token?}` `{token?}` is the optional ERC-20 contract address of the airdrop's token. ### Query Parameters | Parameter | Code | Type | Description | Example | | --- | --- | --- | --- | --- | | chainId | `c` | Number | Chain to filter by. | `c=1` | | admin | `m` | Address / ENS | Campaign admin (creator). | `m=vitalik.eth` | | isAlive | `o` | Boolean | `true` for ongoing campaigns, `false` for expired. | `o=true` | | name | `n` | String | Campaign name substring. | `n=Q1+Drop` | Like the stream search params, `m` is case-insensitive on input and lowercased on output; ENS is passed through as-is. ### Example ```text app.sablier.com/airdrops/search/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48?c=1&m=0x..1&o=true ``` --- ## Global Parameter: Connected Chain Any portal URL can carry a `cc` (connected chain) parameter. When present, the portal prompts the connected wallet to switch to that chain ID before rendering — useful when deep-linking into a stream or campaign that lives on a chain different from the user's current wallet network. | Parameter | Code | Type | Description | Example | | --- | --- | --- | --- | --- | | connectedChain | `cc` | Number | Chain ID the wallet should switch to before load. | `app.sablier.com/vesting/stream/LL3-137-29?cc=137` | --- ## Migrating from the Legacy Interface If you previously linked into the older interface, here is the mapping: | Legacy URL | New URL | | --- | --- | | `app.sablier.com/stream/{id}` | `app.sablier.com/vesting/stream/{id}` or `app.sablier.com/payments/stream/{id}` | | `app.sablier.com/airstream/{id}` | `app.sablier.com/airdrops/campaign/{id}` | | `app.sablier.com/?t=search&c=…&s=…` | `app.sablier.com/vesting/search/{chain}?s=…` or `app.sablier.com/payments/search?c=…&s=…` | | `app.sablier.com/airdrops/?t=search&…` | `app.sablier.com/airdrops/search?…` | Legacy URLs still resolve through redirects, but new integrations should use the product-namespaced format. Note that vesting search now expects the chain as a path slug (`ethereum`, `polygon`, `base`, …) rather than the numeric `c=` query — convert the chain ID before building the URL. --- ## Hooks Source: https://docs.sablier.com/concepts/lockup/hooks # Hooks Hooks are arbitrary third-party functions that get automatically executed by the Sablier Protocol in response to `cancel` and `withdraw` events. They are similar to callback functions in web2. :::info Important Hooks have to be allowlisted before they can be run. Currently, only the [Comptroller](/concepts/governance) has permission to do this. In the future, we may decentralize this process through governance. ::: Hooks are a powerful feature that enable Sablier streams to interact with other DeFi protocols. Let's consider an example: You own a Sablier stream that expires in two years. You are interested in taking a loan against it with the intention to pay it all back after it expires. Hooks are what enable you to do that. With the help of Hooks, we can create an ecosystem of varied use cases for Sablier streams. This can range from lending, staking, credit, and more. It is worth noting that once a hook has been allowlisted, it can never be removed. This is to ensure stronger immutability and decentralization guarantees. Once a recipient contract is allowlisted, integrators do NOT have to trust us to keep their contract on the allowlist. ## Checklist The requirements a hook contract must meet: 1. The contract is not upgradeable. 2. The contract was audited by a third-party security researcher. 3. The contract implements `supportsInterface` and returns `true` for `0xf8ee98d3`, i.e., `type(ISablierLockupRecipient).interfaceId`. 4. If it implements `onSablierLockupCancel`: 1. It returns `ISablierLockupRecipient.onSablierLockupCancel.selector`. 2. It reverts if `msg.sender` is not the Lockup contract. 3. It uses input parameters correctly: `streamId`, `sender`, `senderAmount`, `recipientAmount`. 4. Be aware that if the call reverts, the entire `cancel` execution would revert too. 5. If it implements `onSablierLockupWithdraw`: 1. It returns `ISablierLockupRecipient.onSablierLockupWithdraw.selector`. 2. It reverts if `msg.sender` is not the Lockup contract. 3. It uses input parameters correctly: `streamId`, `caller`, `to`, `amount`. 4. Be aware that if the call reverts, the entire `withdraw` execution would revert too. ## Visual representation :::note If the recipient contract is not on the Sablier allowlist, the hooks will not be executed. ::: ### Cancel hook ### Withdraw hook ## Next steps If you are interested in using Sablier hooks in your protocol, please check the [Hook guide](/guides/lockup/examples/hooks). ```mermaid sequenceDiagram actor Sender Sender ->> SablierLockup: cancel() SablierLockup -->> Recipient: onSablierLockupCancel() Recipient -->> SablierLockup: return selector SablierLockup -->> Sender: transfer unstreamed tokens break if hook reverts Recipient -->> SablierLockup: ❌ tx fail end ``` ```mermaid sequenceDiagram actor Anyone Anyone ->> SablierLockup: withdraw() SablierLockup -->> Recipient: onSablierLockupWithdraw() Recipient -->> SablierLockup: return selector SablierLockup -->> Recipient: transfer streamed tokens break if hook reverts Recipient -->> SablierLockup: ❌ tx fail end ``` --- ## Overview Source: https://docs.sablier.com/concepts/lockup/overview # Overview Lockup is a token streaming protocol that refers to the requirement that the creator of a stream must lock up a certain amount of tokens in a smart contract. A Lockup stream, therefore, is characterized by the start time, end time, amount of tokens to be streamed and a [stream shape](/concepts/lockup/stream-shapes). Let's take an example. Imagine Alice wants to stream 3000 DAI to Bob during the whole month of April. 1. Alice deposits the 3000 DAI in Lockup before Apr 1, setting the end time to May 1. 2. Bob's allocation of the DAI deposit increases every second beginning Apr 1. 3. On Apr 10, Bob will have earned approximately 1000 DAI. He can send a transaction to Lockup to withdraw the tokens. 4. If at any point during April Alice wishes to get back her tokens, she can cancel the stream and recover what has not been streamed yet. This streaming model is especially useful for use cases like vesting and airdrops. If you are looking to create an indefinite stream of tokens, please refer to our [Flow](/concepts/flow/overview) protocol. Lockup enables multiple distribution models, a feature that is discussed in the next section. --- ## Segments Source: https://docs.sablier.com/concepts/lockup/segments # Segments ## Definition A Dynamic stream is composed of multiple segments, which are separate partitions with different streaming amount and rates. The protocol uses these segments to enable custom streaming curves, which power exponential streams, cliff streams, etc. Technically, a segment is a [struct](/reference/lockup/contracts/types/library.LockupDynamic#segment) with three fields: | Field | Type | Description | | --- | --- | --- | | Amount | `uint128` | The amount of tokens to be streamed in this segment, denoted in units of the token's decimals. | | Exponent | `UD2x18` | The exponent of this segment, denoted as a fixed-point number. | | Timestamp | `uint40` | The Unix timestamp indicating this segment's end. | Each segment has its own streaming function: $$ f(x) = x^{exp} * csa $$ Therefore, the distribution function of a dynamic stream becomes: $$ f(x) = x^{exp} * csa + \Sigma(esa) $$ Where: - $x$ is the elapsed time divided by the total time in the current segment. - $exp$ is the current segment exponent. - $csa$ is the current segment amount. - $\Sigma(esa)$ is the sum of all elapsed segments' amounts. :::info Segments can be used to represent any monotonic increasing function. ::: :::caution Because x is a percentage, the payment rate is inversely proportional to the exponent. For example, if the exponent is 0.5, the rate is quadratically faster compared to the baseline when the exponent is 1. Conversely, if the exponent is 2, the rate is quadratically slower compared to baseline. ::: ## Requirements - The sum of all segment amounts must equal the deposit amount. - There is a limit to how many segments there can be in a stream as enforced by the block gas limit. - If someone creates a stream with an excessively large number of segments, the transaction would revert as it wouldn't fit within a block. - The timestamps must be sorted in ascending order. It's not possible for the $(i-1)^{th}$ timestamp to be greater than $i^{th}$ timestamp (given that we're dealing with an increasing monotonic function). ## Examples A segment can be used to represent any monotonic increasing function. A few popular examples are highlighted below: ### Constant Curve (exp = 0)") A constant segment follows the function $f(x) = c$. This is achieved with an exponent of 0. ```solidity LockupDynamic.Segment({ amount: amount, // Total amount in this segment exponent: ud2x18(0e18), // Exponent = 0 (constant) timestamp: endTime // End time of the segment }); ``` A constant curve can be used to unlock amount in tranches where the entire segment amount unlocks at the beginning or the end of the segment. ### Linear Curve (exp = 1)") A linear segment follows the function $f(x) = cx$. This is achieved with an exponent of 1. ```solidity LockupDynamic.Segment({ amount: amount, // Total amount in this segment exponent: ud2x18(1e18), // Exponent = 1 (linear) timestamp: endTime // End time of the segment }); ``` A linear curve unlocks amount linearly over time. ### Accelerating Curve (exp > 1)") A function $f(x) = cx^{exp} \mid exp > 1$ can be used to create a segment that unlocks slowly at the beginning and then quickly at the end. For example, an exponent of 2 unlocks 25% of the segment amount in the first 50% of the time and the remaining 75% in the last 50% of the time. ```solidity segments[0] = LockupDynamic.Segment({ amount: totalAmount, // Total amount in this segment exponent: ud2x18(2e18), // Exponent = 2 (Quadratic) timestamp: endTime // End time of the stream }); ``` As you may have realized, the higher the exponent, the steeper the curve gets at the end. For example, an exponent of 4 unlocks 6% of the segment amount in the first 50% of the time and the remaining 94% in the last 50% of the time. ### Decelerating Curve (exp < 1)") A function $f(x) = cx^{exp} \mid 0 < exp < 1$ can be used to create a segment that unlocks quickly at the beginning and then slowly at the end. For example, an exponent of 0.2 unlocks 80% of the segment amount in the first 50% of the time and the remaining 20% in the last 50% of the time. ```solidity segments[0] = LockupDynamic.Segment({ amount: totalAmount, // Total amount in this segment exponent: ud2x18(0.3e18), // Exponent = 0.3 timestamp: endTime // End time of the stream }); ``` --- ## Stream Statuses Source: https://docs.sablier.com/concepts/lockup/statuses # Stream Statuses A Lockup stream can have one of five distinct statuses: | Status | Description | | --- | --- | | `PENDING` | Stream created but not started; tokens are in a pending state. | | `STREAMING` | Active stream where tokens are currently being streamed. | | `SETTLED` | All tokens have been streamed; recipient is due to withdraw them. | | `CANCELED` | Canceled stream; remaining tokens await recipient's withdrawal. | | `DEPLETED` | Depleted stream; all tokens have been withdrawn and/or refunded. | ## Temperature A stream status can have one out of two "temperatures": | Temperature | Statuses | Description | | --- | --- | --- | | Warm | Pending, Streaming | The passage of time alone can change the status. | | Cold | Settled, Canceled, Depleted | The passage of time won’t affect the status anymore. | ## State transitions The following diagram illustrates the statuses and the allowed transitions between them: ## Q&A ### Q: What is a null stream? A: An id that does not reference a created stream. Trying to interact with a null stream will result in a revert. ### Q: What to do with a stream status? A: Knowing the status of a stream can inform your decision making. For example, if a stream is canceled, you know that you can't cancel it again. Or, if a stream is depleted, you know that you can't withdraw any more tokens from it. ### Q: How can a stream enter the `SETTLED` status directly? A: This is a peculiarity of the [Lockup Dynamic](/concepts/lockup/stream-shapes#lockup-dynamic) streams. Segment amounts can be zero, and the segment milestones can be set in such a way that all non-zero segments are in the past. This will cause the stream to enter the `SETTLED` status directly. ```mermaid stateDiagram-v2 direction LR state Warm { Pending Streaming } state Cold { Settled Canceled Depleted } Null --> Pending Null --> Settled Pending --> Streaming Pending --> Settled Pending --> Canceled Streaming --> Settled Streaming --> Canceled Streaming --> Depleted Settled --> Depleted Canceled --> Depleted ``` --- ## Stream Shapes Source: https://docs.sablier.com/concepts/lockup/stream-shapes # Stream Shapes The stream shapes below are examples. Not an exhaustive list. The model for each shape is not a requirement, but a recommendation - for example, the Timelock shape can also be implemented via Linear or Dynamic models, but is most efficient with the Tranched model. :::tip When creating streams programmatically, pass the corresponding **Shape ID** (shown under each shape below) as the `shape` field. The Sablier app uses this ID to render the matching curve in its [UI gallery](https://app.sablier.com/vesting/gallery/). Install the [`sablier`](https://github.com/sablier-labs/sdk) SDK to import the IDs as a typed enum (`Shape.Lockup`). ::: :::note - The code used to generate the gas benchmarks for the different stream curves can be found [here](https://github.com/sablier-labs/evm-monorepo/tree/main/misc/benchmarks). - If you are interested in learning how to programmatically create the curves shown below in Solidity, check out the [examples](https://github.com/sablier-labs/evm-monorepo/blob/main/misc/examples/lockup/) repository and the "CurvesCreator" files. ::: ## Lockup Linear ### Linear **Shape ID:** `linear` Lockup Linear streams are the simplest token distribution curve in Sablier. The streamed amount over time follows a straight line that goes up and to the right on a graph, which corresponds to the identity function $f(x) = x$: With this shape of stream, the payment rate remains constant, meaning that the same fraction of the deposit amount is streamed to the recipient every second. This provides greater predictability and is easy to understand because of how intuitive it is. Imagine a diagonal line going up and to the right – that's how simple it is. :::info The gas cost to create this shape is approximately _168,923_ on Mainnet. This may vary due to multiple factors. ::: ### Unlock Linear **Shape ID:** `linearUnlockLinear` The Unlock Linear shape combines an initial immediate release of tokens with a steady, linear payout over time. This shape is ideal for employment contracts that include a signing bonus along with a regular payout schedule. At the beginning of the stream, a fixed amount of tokens is instantly available to the recipient — this is your "signing bonus". Following this, the remaining tokens begin to stream continuously at a consistent rate until the end of the contract term — this is your "salary". Another use case is a token distribution to investors where a particular amount gets unlocked immediately followed by a linear vesting plan. :::info The gas cost to create this shape is approximately _191,182_ on Mainnet. This may vary due to multiple factors. ::: ### Cliff **Shape ID:** `cliff` It is possible to attach a "cliff" to a Lockup Linear stream, which sets a cut-off point for releasing tokens. Prior to the cliff, the recipient cannot withdraw any tokens, but the stream continues to accrue them. After the cliff, the constant payment rate per second kicks in. This feature is especially useful for vesting ERC-20 tokens as it allows you to have, for example, a 1-year cliff, and then 3 additional years of linear streaming. If the stream is for an employee, you can make it cancellable so that if the employee leaves your company during the stream, you can cancel it and recover the tokens that have not yet been streamed. :::info The gas cost to create this shape is approximately _213,708_ on Mainnet. This may vary due to multiple factors. ::: ### Unlock Cliff **Shape ID:** `linearUnlockCliff` This shape is useful for companies who want to distribute tokens to their investors using a cliff followed by linear vesting but also want to unlock some liquidity at the beginning to be able to allow investors to bootstrap AMM pools. Initially, a set amount of tokens are made available to the recipient as an upfront payment. After this initial unlock, the tokens are held during the cliff period until the moment of time set. The release resumes in a linearly post-cliff. :::info The gas cost to create this shape is approximately _214,067_ on Mainnet. This may vary due to multiple factors. ::: ## Lockup Dynamic Lockup Dynamic streams are what makes Sablier so unique, since they enable the creation of an arbitrary streaming curve, including non-linear curves. On the Sablier Interface, we support only some distribution shapes (the ones enumerated below), but the potential for innovation is limitless when you interact programmatically with the contracts. For example, one could design a logarithmic stream that emulates the $f(x) = log(x)$ function. These streams are powered by a number of user-provided [segments](/concepts/lockup/segments), which we will cover in the next article. What is important to note here is that with Lockup Dynamic, Sablier has evolved into a universal streaming engine, capable of supporting any custom streaming curve. ### Exponential **Shape ID:** `dynamicExponential` A fantastic use case for Lockup Dynamic is Exponential streams, a shape through which the recipient receives increasingly more tokens as time passes. Exponentials are a great fit if you are looking to airdrop tokens, because your community members will receive the majority of the tokens towards the end of the stream instead of receiving the tokens all at once (no streaming) or in a linear fashion (Lockup Linear). This incentivizes long-term behavior and a constructive attitude. :::info The gas cost to create this shape is approximately _219,629_ on Mainnet. This may vary due to multiple factors. ::: ### Cliff Exponential **Shape ID:** `dynamicCliffExponential` Another use case for Lockup Dynamic is a variation of the previous design: an Cliff Exponential. The stream starts with a cliff (which can be how long you want), a specific amount instantly unlocked when the cliff ends, and then the rest of the stream is exponentially streamed. This is an excellent distribution if you are a company looking to set up a token vesting plan for your employees. Your employees will have an incentive to remain with your company in the long run, as they will receive an increasingly larger number of tokens. :::info The gas cost to create this shape is approximately _274,420_ on Mainnet. This may vary due to multiple factors. ::: ## Lockup Tranched Lockup Tranched streams are, as the name says, streams that have token unlocks in tranches. Even though you can use Lockup Dynamic to create a traditional vesting structure with periodic unlocks, Lockup Tranched is specifically designed for that use case. As a result, a stream with tranches created using Lockup Tranched is more gas efficient than the same stream created using Lockup Dynamic. These streams are powered by a number of user-provided [tranches](/concepts/lockup/tranches), which is covered in the tranches article. ### Unlock in Steps **Shape ID:** `linearStepper` _(the legacy `tranchedStepper` is deprecated)_ You can use Lockup Tranched to create a traditional vesting contract with periodic unlocks. In this case, the "streaming" rate would not be by the second, but by the week, month, or year. After each period, a specific amount becomes unlocked and available for the recipient to withdraw. Past unlocks accumulate, so if the recipient doesn't withdraw them, they will be able to withdraw them later. The advantage of using Unlock in Steps instead of a normal vesting contract is that Sablier automates the entire process. No more worries about setting up vesting contracts or creating a user interface for your investors to claim their tokens. :::info The gas cost to create this shape is approximately _299,268_ on Mainnet for four unlocks. This may vary as there are multiple factors to consider. ::: ### Unlock Monthly **Shape ID:** `tranchedMonthly` Unlock Monthly is a special case of Unlock in Steps where tokens are unlocked on the same day every month, e.g. the 1st of every month. This is suited for use cases like traditional monthly salaries or ESOPs plans. Each month, on a particular day, a specific amount of tokens becomes unlocked and available for withdrawal. Like Unlock in Steps, unwithdrawn tokens will carry over to the next period, providing flexibility and control to the recipient. This shape is ideal for employers who wish to set up advanced payment schedules for their employees, offering them access to funds on a predictable, monthly basis. :::info The gas cost to create this shape is approximately _511,476_ on Mainnet for a period of exactly **one year**. This may vary as there are multiple factors to consider. ::: ### Backweighted **Shape ID:** `tranchedBackweighted` Backweighted is a type of tranched vesting curve where the tokens are vested in a backweighted way, meaning little vests early on, and large chunks vest towards the end. Example for a 4-year vesting schedule: - Year 1: 10% vests - Year 2: 20% vests - Year 3: 30% vests - Year 4: 40% vests This makes it a particularly good fit for recipients that need to be particularly incentivized on a long-term perspective, as they receive progressively more and more tokens as the stream gets closer to its end date. :::info The gas cost to create this shape is approximately _342,999_ on Mainnet for a period of exactly **four years**. This may vary as there are multiple factors to consider. ::: ### Timelock **Shape ID:** `tranchedTimelock` The Timelock shape locks all tokens for a specified period. Users cannot access the tokens until the set period elapses. Once the set period elapses, the full amount becomes accessible to the recipient. This setup is particularly advantageous for projects seeking to stabilize token pricing and minimize market volatility, encouraging investors to maintain their stake over a more extended period. :::info The gas cost to create this shape is approximately _219,700_ on Mainnet. This may vary due to multiple factors. ::: --- ## Tranches Source: https://docs.sablier.com/concepts/lockup/tranches # Tranches ## Definition Analogous to the segments in Dynamic streams, a Tranched stream is composed of multiple tranches with different amounts and durations. The protocol uses these tranches to enable traditional vesting curves with regular unlocks. Technically, a tranche is a [struct](/reference/lockup/contracts/types/library.LockupTranched#tranche) with two fields: | Field | Type | Description | | --- | --- | --- | | Amount | `uint128` | The amount of tokens to be unlocked in a tranche, denoted in units of the token's decimals. | | Timestamp | `uint40` | The Unix timestamp indicating the tranche's end. | The distribution function of a Lockup tranched stream: $$ f(x) = \Sigma(eta) $$ Where: - $\Sigma(eta)$ is the sum of all vested tranches' amounts. ## Requirements - The sum of all tranche amounts must equal the deposit amount. - The block gas limit enforces a limit to how many tranches there can be in a stream. - If someone creates a stream with an excessively large number of tranches, the transaction would revert as it wouldn't fit within a block. In such cases, make sure to simulate the transaction first. - The timestamps must be sorted in ascending order. It's not possible for the $(i-1)^{th}$ timestamp to be greater than $i^{th}$ timestamp (given that we're dealing with an increasing monotonic function). --- ## Lockup Deployments Source: https://docs.sablier.com/guides/lockup/deployments # Lockup Deployments This section contains the deployment addresses for the v4.0 release of [@sablier/lockup](https://npmjs.com/package/@sablier/lockup). A few noteworthy details about the deployments: - The addresses are final - All contracts are non-upgradeable - The source code is verified on Etherscan across all chains :::info [important] The Lockup Periphery repo has been discontinued in favor of the new [Airdrops repo](/guides/airdrops/overview). ::: ## Versions Any updates or additional features will require a new deployment of the protocol, due to its immutable nature. Came here looking for the previous Lockup deployments? Click below to see other versions. | Version | Release Date | UI Aliases | | --- | --- | --- | | [v4.0](/guides/lockup/deployments) (latest) | March 2026 | - `LK3` (Lockup) | | [v3.0](/guides/lockup/previous-deployments/v3.0) | October 2025 | - `LK2` (Lockup) | | [v2.0](/guides/lockup/previous-deployments/v2.0) | February 2025 | - `LK` (Lockup): all models have been merged into a single contract | | [v1.2](/guides/lockup/previous-deployments/v1.2) | July 2024 | - `LD3` (Lockup Dynamic) - `LL3` (Lockup Linear) - `LT3` (Lockup Tranched) | | [v1.1](/guides/lockup/previous-deployments/v1.1) | December 2023 | - `LD2` (Lockup Dynamic) - `LL2` (Lockup Linear) | | [v1.0](/guides/lockup/previous-deployments/v1.0) | July 2023 | - `LD` (Lockup Dynamic) - `LL` (Lockup Linear) | Or maybe you're looking for Legacy? [Click here](/guides/legacy/deployments). :::info Stay up to date with any new releases by [subscribing](https://x.com/Sablier/status/1821220784661995627) to the official Sablier repositories on Github. ::: ## Mainnets ### Ethereum ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x93b37Bd5B6b278373217333Ac30D7E74c85fBDCB`](https://etherscan.io/address/0x93b37Bd5B6b278373217333Ac30D7E74c85fBDCB) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | SablierBatchLockup | [`0x4f3be262D1358A82b468CF81bfc5A9cC32Cf9875`](https://etherscan.io/address/0x4f3be262D1358A82b468CF81bfc5A9cC32Cf9875) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupHelpers | [`0xC86B56250D2758f30d09B3420D9ec5b646244C7c`](https://etherscan.io/address/0xC86B56250D2758f30d09B3420D9ec5b646244C7c) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupMath | [`0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6`](https://etherscan.io/address/0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupNFTDescriptor | [`0xA9dC6878C979B5cc1d98a1803F0664ad725A1f56`](https://etherscan.io/address/0xA9dC6878C979B5cc1d98a1803F0664ad725A1f56) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | ### Abstract ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x2a8887a7Cc494e35EEB615df34026DBfaE027a5C`](https://abscan.org/address/0x2a8887a7Cc494e35EEB615df34026DBfaE027a5C) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | SablierBatchLockup | [`0x5Ed8660672aFDd2bb991b4c9097dbB2e0a6BcB6C`](https://abscan.org/address/0x5Ed8660672aFDd2bb991b4c9097dbB2e0a6BcB6C) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupHelpers | [`0xC86B56250D2758f30d09B3420D9ec5b646244C7c`](https://abscan.org/address/0xC86B56250D2758f30d09B3420D9ec5b646244C7c) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupMath | [`0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6`](https://abscan.org/address/0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupNFTDescriptor | [`0x63Ff2E370788C163D5a1909B5FCb299DB327AEF9`](https://abscan.org/address/0x63Ff2E370788C163D5a1909B5FCb299DB327AEF9) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | ### Arbitrum ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xD103611856F3c2BbAe61D9bF138078794fC09C33`](https://arbiscan.io/address/0xD103611856F3c2BbAe61D9bF138078794fC09C33) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | SablierBatchLockup | [`0x629AC39918E97ccAF5908e74dEE1bFea0489FE95`](https://arbiscan.io/address/0x629AC39918E97ccAF5908e74dEE1bFea0489FE95) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupHelpers | [`0xC86B56250D2758f30d09B3420D9ec5b646244C7c`](https://arbiscan.io/address/0xC86B56250D2758f30d09B3420D9ec5b646244C7c) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupMath | [`0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6`](https://arbiscan.io/address/0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupNFTDescriptor | [`0xd5c6a0Dd2E1822865c308850b8b3E2CcE762D061`](https://arbiscan.io/address/0xd5c6a0Dd2E1822865c308850b8b3E2CcE762D061) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | ### Avalanche ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xB891b41533776Ec20f7738c647a11506AA44b8A8`](https://snowscan.xyz/address/0xB891b41533776Ec20f7738c647a11506AA44b8A8) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | SablierBatchLockup | [`0x3813bc15b71aEB1E4ea301c4DE37b43B0cd6b3aC`](https://snowscan.xyz/address/0x3813bc15b71aEB1E4ea301c4DE37b43B0cd6b3aC) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupHelpers | [`0xC86B56250D2758f30d09B3420D9ec5b646244C7c`](https://snowscan.xyz/address/0xC86B56250D2758f30d09B3420D9ec5b646244C7c) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupMath | [`0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6`](https://snowscan.xyz/address/0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupNFTDescriptor | [`0x906A4BD5dD0EF13654eA29bFD6185d0d64A4b674`](https://snowscan.xyz/address/0x906A4BD5dD0EF13654eA29bFD6185d0d64A4b674) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | ### Base ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xc19a09A66887017F603E5dF420ed3Cb9a5c07C0A`](https://basescan.org/address/0xc19a09A66887017F603E5dF420ed3Cb9a5c07C0A) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | SablierBatchLockup | [`0xE001bae5Ef0d86362Ca258bff994a97CAE1245F5`](https://basescan.org/address/0xE001bae5Ef0d86362Ca258bff994a97CAE1245F5) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupHelpers | [`0xC86B56250D2758f30d09B3420D9ec5b646244C7c`](https://basescan.org/address/0xC86B56250D2758f30d09B3420D9ec5b646244C7c) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupMath | [`0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6`](https://basescan.org/address/0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupNFTDescriptor | [`0x87e437030b7439150605a641483de98672E26317`](https://basescan.org/address/0x87e437030b7439150605a641483de98672E26317) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | ### Berachain ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x2455c72a4aFE3b0e2B26b5EFD7F8EFFE6B828C90`](https://berascan.com/address/0x2455c72a4aFE3b0e2B26b5EFD7F8EFFE6B828C90) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | SablierBatchLockup | [`0xfC8153d02464374C96683F09FE2B24065A03A64C`](https://berascan.com/address/0xfC8153d02464374C96683F09FE2B24065A03A64C) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupHelpers | [`0xC86B56250D2758f30d09B3420D9ec5b646244C7c`](https://berascan.com/address/0xC86B56250D2758f30d09B3420D9ec5b646244C7c) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupMath | [`0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6`](https://berascan.com/address/0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupNFTDescriptor | [`0x3bbE0a21792564604B0fDc00019532Adeffa70eb`](https://berascan.com/address/0x3bbE0a21792564604B0fDc00019532Adeffa70eb) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | ### BNB Chain ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x6cd06Aaf06506bC3fF382d83023354E2B80EeD22`](https://bscscan.com/address/0x6cd06Aaf06506bC3fF382d83023354E2B80EeD22) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | SablierBatchLockup | [`0xF90ca72fa2CC584Db0Ee2F34ac2f54DAfBe7C045`](https://bscscan.com/address/0xF90ca72fa2CC584Db0Ee2F34ac2f54DAfBe7C045) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupHelpers | [`0xC86B56250D2758f30d09B3420D9ec5b646244C7c`](https://bscscan.com/address/0xC86B56250D2758f30d09B3420D9ec5b646244C7c) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupMath | [`0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6`](https://bscscan.com/address/0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupNFTDescriptor | [`0x56831a5a932793E02251126831174Ab8Bf2f7695`](https://bscscan.com/address/0x56831a5a932793E02251126831174Ab8Bf2f7695) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | ### Chiliz ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x003b2D58A97315CE9fB3888Db6BCB9770e73f398`](https://chiliscan.com/address/0x003b2D58A97315CE9fB3888Db6BCB9770e73f398) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | SablierBatchLockup | [`0xE47A0D9081035a6aB8e4f5c29B6BA396A303cA16`](https://chiliscan.com/address/0xE47A0D9081035a6aB8e4f5c29B6BA396A303cA16) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupHelpers | [`0xC86B56250D2758f30d09B3420D9ec5b646244C7c`](https://chiliscan.com/address/0xC86B56250D2758f30d09B3420D9ec5b646244C7c) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupMath | [`0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6`](https://chiliscan.com/address/0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupNFTDescriptor | [`0x8A96f827082FB349B6e268baa0a7A5584c4Ccda6`](https://chiliscan.com/address/0x8A96f827082FB349B6e268baa0a7A5584c4Ccda6) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | ### Denergy ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xB9636F3dc2Fc1B5Ad2a7323210084DBEeD7B2377`](https://explorer.denergychain.com/address/0xB9636F3dc2Fc1B5Ad2a7323210084DBEeD7B2377) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | SablierBatchLockup | [`0x29F5527C7CdBe1548dE5634D295FD7DAC3AD327D`](https://explorer.denergychain.com/address/0x29F5527C7CdBe1548dE5634D295FD7DAC3AD327D) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupHelpers | [`0x2eFEa83cAbF1c3357Ba7661a32AaE5bc74A5aFD6`](https://explorer.denergychain.com/address/0x2eFEa83cAbF1c3357Ba7661a32AaE5bc74A5aFD6) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupMath | [`0x2e5ad3dE34C0f36B3f96a7F740Db2d9b4BAd0b33`](https://explorer.denergychain.com/address/0x2e5ad3dE34C0f36B3f96a7F740Db2d9b4BAd0b33) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupNFTDescriptor | [`0x4749dB4834be9b473D586Ad4d98133dAfC678313`](https://explorer.denergychain.com/address/0x4749dB4834be9b473D586Ad4d98133dAfC678313) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | ### Gnosis ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xF24e804B0Eb4fC0eAD41dF0e392D25fb230Bbab4`](https://gnosisscan.io/address/0xF24e804B0Eb4fC0eAD41dF0e392D25fb230Bbab4) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | SablierBatchLockup | [`0x653Cbc0cC19dCb43F06a0d0909835a9d7dec33dF`](https://gnosisscan.io/address/0x653Cbc0cC19dCb43F06a0d0909835a9d7dec33dF) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupHelpers | [`0xC86B56250D2758f30d09B3420D9ec5b646244C7c`](https://gnosisscan.io/address/0xC86B56250D2758f30d09B3420D9ec5b646244C7c) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupMath | [`0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6`](https://gnosisscan.io/address/0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupNFTDescriptor | [`0x3140a6900AA2FF3186730741ad8255ee4e6d8Ff1`](https://gnosisscan.io/address/0x3140a6900AA2FF3186730741ad8255ee4e6d8Ff1) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | ### HyperEVM ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x5369E34C92EACC1cceaFFe1be01F057C68ca1b19`](https://hyperevmscan.io/address/0x5369E34C92EACC1cceaFFe1be01F057C68ca1b19) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | SablierBatchLockup | [`0xCBFaA5f5d0DB294eeb726334b16f75e4d7AfB5fe`](https://hyperevmscan.io/address/0xCBFaA5f5d0DB294eeb726334b16f75e4d7AfB5fe) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupHelpers | [`0xC86B56250D2758f30d09B3420D9ec5b646244C7c`](https://hyperevmscan.io/address/0xC86B56250D2758f30d09B3420D9ec5b646244C7c) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupMath | [`0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6`](https://hyperevmscan.io/address/0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupNFTDescriptor | [`0x7263d77e9e872f82A15e5E1a9816440D23758708`](https://hyperevmscan.io/address/0x7263d77e9e872f82A15e5E1a9816440D23758708) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | ### Lightlink ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xa39376a844dB3aA3fAaF119321b761cfE296fe19`](https://phoenix.lightlink.io/address/0xa39376a844dB3aA3fAaF119321b761cfE296fe19) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | SablierBatchLockup | [`0x2621819f84Ef7a8424e7A8Ff573b7Fe595856D41`](https://phoenix.lightlink.io/address/0x2621819f84Ef7a8424e7A8Ff573b7Fe595856D41) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupHelpers | [`0xC86B56250D2758f30d09B3420D9ec5b646244C7c`](https://phoenix.lightlink.io/address/0xC86B56250D2758f30d09B3420D9ec5b646244C7c) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupMath | [`0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6`](https://phoenix.lightlink.io/address/0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupNFTDescriptor | [`0xCFB5F90370A7884DEc59C55533782B45FA24f4d1`](https://phoenix.lightlink.io/address/0xCFB5F90370A7884DEc59C55533782B45FA24f4d1) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | ### Linea Mainnet ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xFb898e1626c9B32F89fFB0FedD145B89590d219e`](https://lineascan.build/address/0xFb898e1626c9B32F89fFB0FedD145B89590d219e) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | SablierBatchLockup | [`0xa0B12148210bD25F739bEA4824A35F071989F60a`](https://lineascan.build/address/0xa0B12148210bD25F739bEA4824A35F071989F60a) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupHelpers | [`0xC86B56250D2758f30d09B3420D9ec5b646244C7c`](https://lineascan.build/address/0xC86B56250D2758f30d09B3420D9ec5b646244C7c) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupMath | [`0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6`](https://lineascan.build/address/0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupNFTDescriptor | [`0x1514a869D29a8B22961e8F9eBa3DC64000b96BCe`](https://lineascan.build/address/0x1514a869D29a8B22961e8F9eBa3DC64000b96BCe) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | ### Mode ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x43916BAb157B56124C46dC09D45A9516489D84B7`](https://modescan.io/address/0x43916BAb157B56124C46dC09D45A9516489D84B7) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | SablierBatchLockup | [`0x0d3cd659d417c45ac6A79db594848fCd34583671`](https://modescan.io/address/0x0d3cd659d417c45ac6A79db594848fCd34583671) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupHelpers | [`0xC86B56250D2758f30d09B3420D9ec5b646244C7c`](https://modescan.io/address/0xC86B56250D2758f30d09B3420D9ec5b646244C7c) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupMath | [`0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6`](https://modescan.io/address/0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupNFTDescriptor | [`0x64e7879558b6dfE2f510bd4b9Ad196ef0371EAA8`](https://modescan.io/address/0x64e7879558b6dfE2f510bd4b9Ad196ef0371EAA8) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | ### Monad ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x82723C1ffEc9D43dE5FA80b25Da8df99AfD470ba`](https://monadscan.com/address/0x82723C1ffEc9D43dE5FA80b25Da8df99AfD470ba) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | SablierBatchLockup | [`0xB02d463F531c3eB1a92B18B9d4756e9d03AB2562`](https://monadscan.com/address/0xB02d463F531c3eB1a92B18B9d4756e9d03AB2562) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupHelpers | [`0xC86B56250D2758f30d09B3420D9ec5b646244C7c`](https://monadscan.com/address/0xC86B56250D2758f30d09B3420D9ec5b646244C7c) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupMath | [`0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6`](https://monadscan.com/address/0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupNFTDescriptor | [`0x37BA02a35861F7254fAE733E3a7cADD96D9D32A2`](https://monadscan.com/address/0x37BA02a35861F7254fAE733E3a7cADD96D9D32A2) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | ### Morph ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xA74F2Cf047A67509f332DD9B2D6D51989e546548`](https://explorer.morphl2.io/address/0xA74F2Cf047A67509f332DD9B2D6D51989e546548) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | SablierBatchLockup | [`0x9F8A023c90ab65cAf8eE4D7e1248E883e2B74A36`](https://explorer.morphl2.io/address/0x9F8A023c90ab65cAf8eE4D7e1248E883e2B74A36) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupHelpers | [`0xC86B56250D2758f30d09B3420D9ec5b646244C7c`](https://explorer.morphl2.io/address/0xC86B56250D2758f30d09B3420D9ec5b646244C7c) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupMath | [`0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6`](https://explorer.morphl2.io/address/0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupNFTDescriptor | [`0x660314f09ac3B65E216B6De288aAdc2599AF14e2`](https://explorer.morphl2.io/address/0x660314f09ac3B65E216B6De288aAdc2599AF14e2) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | ### OP Mainnet ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x945ba0D0EeAa5766d4bae5455a9817D7ae150550`](https://optimistic.etherscan.io/address/0x945ba0D0EeAa5766d4bae5455a9817D7ae150550) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | SablierBatchLockup | [`0x9A8486b4D65134197A42076559e4A8CA9917f289`](https://optimistic.etherscan.io/address/0x9A8486b4D65134197A42076559e4A8CA9917f289) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupHelpers | [`0xC86B56250D2758f30d09B3420D9ec5b646244C7c`](https://optimistic.etherscan.io/address/0xC86B56250D2758f30d09B3420D9ec5b646244C7c) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupMath | [`0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6`](https://optimistic.etherscan.io/address/0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupNFTDescriptor | [`0x41dBa1AfBB6DF91b3330dc009842327A9858Cbae`](https://optimistic.etherscan.io/address/0x41dBa1AfBB6DF91b3330dc009842327A9858Cbae) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | ### Polygon ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xCEb5253Db890347D45778FB0834fb3c0B57aFf93`](https://polygonscan.com/address/0xCEb5253Db890347D45778FB0834fb3c0B57aFf93) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | SablierBatchLockup | [`0x052c85cB2Db728d5A7709D41f648016469C09388`](https://polygonscan.com/address/0x052c85cB2Db728d5A7709D41f648016469C09388) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupHelpers | [`0xC86B56250D2758f30d09B3420D9ec5b646244C7c`](https://polygonscan.com/address/0xC86B56250D2758f30d09B3420D9ec5b646244C7c) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupMath | [`0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6`](https://polygonscan.com/address/0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupNFTDescriptor | [`0xf5e12d0bA25FCa0D738Ec57f149736B2e4C46980`](https://polygonscan.com/address/0xf5e12d0bA25FCa0D738Ec57f149736B2e4C46980) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | ### Scroll ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x9435E262A4A312d30D6C41fE055f648e91Af411e`](https://scrollscan.com/address/0x9435E262A4A312d30D6C41fE055f648e91Af411e) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | SablierBatchLockup | [`0x92CB40d8De23A4C657d03F4a926Dd684FfB74063`](https://scrollscan.com/address/0x92CB40d8De23A4C657d03F4a926Dd684FfB74063) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupHelpers | [`0xC86B56250D2758f30d09B3420D9ec5b646244C7c`](https://scrollscan.com/address/0xC86B56250D2758f30d09B3420D9ec5b646244C7c) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupMath | [`0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6`](https://scrollscan.com/address/0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupNFTDescriptor | [`0x00Ff6443E902874924dd217c1435e3be04f57431`](https://scrollscan.com/address/0x00Ff6443E902874924dd217c1435e3be04f57431) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | ### Sonic ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xa697988451F921185A8c824aD4867DC8933C4ECB`](https://sonicscan.org/address/0xa697988451F921185A8c824aD4867DC8933C4ECB) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | SablierBatchLockup | [`0x414e7871fDEbFCDd82095431CB3b5B1C9f8f4d25`](https://sonicscan.org/address/0x414e7871fDEbFCDd82095431CB3b5B1C9f8f4d25) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupHelpers | [`0xC86B56250D2758f30d09B3420D9ec5b646244C7c`](https://sonicscan.org/address/0xC86B56250D2758f30d09B3420D9ec5b646244C7c) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupMath | [`0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6`](https://sonicscan.org/address/0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupNFTDescriptor | [`0x955dC7A2170782344FA9Ac11De0C0C42C05De2Fc`](https://sonicscan.org/address/0x955dC7A2170782344FA9Ac11De0C0C42C05De2Fc) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | ### Superseed ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x3AC18F736d0E1B9bd9259Cd6C8a43539C86C16fD`](https://explorer.superseed.xyz/address/0x3AC18F736d0E1B9bd9259Cd6C8a43539C86C16fD) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | SablierBatchLockup | [`0xD68f7514Abadad1CAF00B4606f43d03109ad1373`](https://explorer.superseed.xyz/address/0xD68f7514Abadad1CAF00B4606f43d03109ad1373) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupHelpers | [`0xC86B56250D2758f30d09B3420D9ec5b646244C7c`](https://explorer.superseed.xyz/address/0xC86B56250D2758f30d09B3420D9ec5b646244C7c) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupMath | [`0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6`](https://explorer.superseed.xyz/address/0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupNFTDescriptor | [`0xa4576b58Ec760A8282D081dc94F3dc716DFc61e9`](https://explorer.superseed.xyz/address/0xa4576b58Ec760A8282D081dc94F3dc716DFc61e9) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | ### Unichain ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xE72830E2845B74aA3bA71fB6E833D7A677129793`](https://uniscan.xyz/address/0xE72830E2845B74aA3bA71fB6E833D7A677129793) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | SablierBatchLockup | [`0x838478f87d2A907DE53862c1B36b919b5e19FCEa`](https://uniscan.xyz/address/0x838478f87d2A907DE53862c1B36b919b5e19FCEa) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupHelpers | [`0xC86B56250D2758f30d09B3420D9ec5b646244C7c`](https://uniscan.xyz/address/0xC86B56250D2758f30d09B3420D9ec5b646244C7c) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupMath | [`0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6`](https://uniscan.xyz/address/0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupNFTDescriptor | [`0xa5F12D63E18a28C9BE27B6f3d91ce693320067ba`](https://uniscan.xyz/address/0xa5F12D63E18a28C9BE27B6f3d91ce693320067ba) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | ### XDC ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x16f5c4Ddc5b828F00E8f92267f3ABf60b700dB5c`](https://xdcscan.com/address/0x16f5c4Ddc5b828F00E8f92267f3ABf60b700dB5c) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | SablierBatchLockup | [`0xFE9B690173DbFDf4Ad937A0555AFb32D50A26daD`](https://xdcscan.com/address/0xFE9B690173DbFDf4Ad937A0555AFb32D50A26daD) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupHelpers | [`0xC86B56250D2758f30d09B3420D9ec5b646244C7c`](https://xdcscan.com/address/0xC86B56250D2758f30d09B3420D9ec5b646244C7c) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupMath | [`0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6`](https://xdcscan.com/address/0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupNFTDescriptor | [`0x4c1311a9d88BFb7023148aB04F7321C2E91c29bf`](https://xdcscan.com/address/0x4c1311a9d88BFb7023148aB04F7321C2E91c29bf) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | ### ZKsync Era ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xc2FDF5DCDEaa1F7c83e569D03b22eA8636073F4A`](https://explorer.zksync.io/address/0xc2FDF5DCDEaa1F7c83e569D03b22eA8636073F4A) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | SablierBatchLockup | [`0x02Dec2d93E47e2d5Fda3969E45B16BAf42ebA607`](https://explorer.zksync.io/address/0x02Dec2d93E47e2d5Fda3969E45B16BAf42ebA607) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupHelpers | [`0xC86B56250D2758f30d09B3420D9ec5b646244C7c`](https://explorer.zksync.io/address/0xC86B56250D2758f30d09B3420D9ec5b646244C7c) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupMath | [`0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6`](https://explorer.zksync.io/address/0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupNFTDescriptor | [`0x955dC7A2170782344FA9Ac11De0C0C42C05De2Fc`](https://explorer.zksync.io/address/0x955dC7A2170782344FA9Ac11De0C0C42C05De2Fc) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | ## Testnets ### Arbitrum Sepolia ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xe4E4ecC586A79aAeAb423B2C6da8F7510de811c5`](https://sepolia.arbiscan.io/address/0xe4E4ecC586A79aAeAb423B2C6da8F7510de811c5) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | SablierBatchLockup | [`0x77DA2e9939D45031A6C003A227fB13F683b288F0`](https://sepolia.arbiscan.io/address/0x77DA2e9939D45031A6C003A227fB13F683b288F0) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupHelpers | [`0xC86B56250D2758f30d09B3420D9ec5b646244C7c`](https://sepolia.arbiscan.io/address/0xC86B56250D2758f30d09B3420D9ec5b646244C7c) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupMath | [`0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6`](https://sepolia.arbiscan.io/address/0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupNFTDescriptor | [`0x8224eb5D7d76B2D7Df43b868D875E79B11500eA8`](https://sepolia.arbiscan.io/address/0x8224eb5D7d76B2D7Df43b868D875E79B11500eA8) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | ### Base Sepolia ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xA65A1ecEf02C2B9BDc604951c6A6D02335D4532e`](https://sepolia.basescan.org/address/0xA65A1ecEf02C2B9BDc604951c6A6D02335D4532e) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | SablierBatchLockup | [`0x41813cE5F8CF780ba1429A24512f90AaAEFb8144`](https://sepolia.basescan.org/address/0x41813cE5F8CF780ba1429A24512f90AaAEFb8144) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupHelpers | [`0xC86B56250D2758f30d09B3420D9ec5b646244C7c`](https://sepolia.basescan.org/address/0xC86B56250D2758f30d09B3420D9ec5b646244C7c) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupMath | [`0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6`](https://sepolia.basescan.org/address/0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupNFTDescriptor | [`0xCA2593027BA24856c292Fdcb5F987E0c25e755a4`](https://sepolia.basescan.org/address/0xCA2593027BA24856c292Fdcb5F987E0c25e755a4) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | ### BattleChain Testnet ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xbf85cD17cA59b7A2b81d3D776cE1602a7C0aF9D9`](https://explorer.testnet.battlechain.com/address/0xbf85cD17cA59b7A2b81d3D776cE1602a7C0aF9D9) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | SablierBatchLockup | [`0x85290381D6dfD4ff0d33265B32b050C1bDe9a56a`](https://explorer.testnet.battlechain.com/address/0x85290381D6dfD4ff0d33265B32b050C1bDe9a56a) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupHelpers | [`0xC86B56250D2758f30d09B3420D9ec5b646244C7c`](https://explorer.testnet.battlechain.com/address/0xC86B56250D2758f30d09B3420D9ec5b646244C7c) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupMath | [`0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6`](https://explorer.testnet.battlechain.com/address/0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupNFTDescriptor | [`0xf978034bb3CAB5fe88d23DB5Cb38D510485DaB90`](https://explorer.testnet.battlechain.com/address/0xf978034bb3CAB5fe88d23DB5Cb38D510485DaB90) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | ### OP Sepolia ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x9D68477ef056552D769752171f348B74E0050Fe8`](https://optimism-sepolia.blockscout.com/address/0x9D68477ef056552D769752171f348B74E0050Fe8) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | SablierBatchLockup | [`0xB5549e9348ce9E0D461e0f8b9F05173E052Ef312`](https://optimism-sepolia.blockscout.com/address/0xB5549e9348ce9E0D461e0f8b9F05173E052Ef312) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupHelpers | [`0xC86B56250D2758f30d09B3420D9ec5b646244C7c`](https://optimism-sepolia.blockscout.com/address/0xC86B56250D2758f30d09B3420D9ec5b646244C7c) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupMath | [`0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6`](https://optimism-sepolia.blockscout.com/address/0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupNFTDescriptor | [`0xDf6163ddD3Ebcb552Cc1379a9c65AFe68683534e`](https://optimism-sepolia.blockscout.com/address/0xDf6163ddD3Ebcb552Cc1379a9c65AFe68683534e) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | ### Sepolia ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xe61cb9153356419bdaD0A8767c059f92d221a3C4`](https://sepolia.etherscan.io/address/0xe61cb9153356419bdaD0A8767c059f92d221a3C4) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | SablierBatchLockup | [`0xd4ddc49F9D03a48293b5c8d89Cc210Af49d03D72`](https://sepolia.etherscan.io/address/0xd4ddc49F9D03a48293b5c8d89Cc210Af49d03D72) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupHelpers | [`0xC86B56250D2758f30d09B3420D9ec5b646244C7c`](https://sepolia.etherscan.io/address/0xC86B56250D2758f30d09B3420D9ec5b646244C7c) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupMath | [`0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6`](https://sepolia.etherscan.io/address/0x6c873BcE27aA6Ca803EF7013F05d1802AB6995b6) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | | LockupNFTDescriptor | [`0x955dC7A2170782344FA9Ac11De0C0C42C05De2Fc`](https://sepolia.etherscan.io/address/0x955dC7A2170782344FA9Ac11De0C0C42C05De2Fc) | [`lockup-v4.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v4.0) | --- ## Etherscan: Manual Operations Source: https://docs.sablier.com/guides/lockup/etherscan # Etherscan: Manual Operations ## Introduction Just like any other open protocol, Lockup can be interacted with directly through a blockchain explorer like Etherscan. In this guide, we will show you how to create a stream and withdraw from a stream by manually interacting with the Lockup Core contracts on Etherscan. If you're interested in interacting with Legacy contracts, please refer to this [article](https://blog.sablier.com/operating-the-sablier-v1-protocol-manually/). ## Creating a Stream ### Prerequisites Before being able to create a stream using the Lockup Core contracts you need to have granted a sufficient token allowance. See the [Allowances](#prerequisite-erc20-allowances) section below for a guide on how to do that. ### Step 1: Go to contract page Head over to our [deployments](/guides/lockup/deployments) list to pick the contract address you want to interact with. In this tutorial, we will create a Lockup stream on Ethereum Sepolia. Once you find the right contract, click on the address to access its explorer's page. Click on the "Contract" tab, and then on the "Write Contract" sub-tab. ![Etherscan 01](/assets/images/01-9c40cb2f1951fc3abcdfcfbf4d1ce2a5.webp) ![Etherscan 02](/assets/images/02-4713fd45c3a8951bac9917f08462a49b.webp) You can now connect your wallet to the interface by clicking on "Connect to Web3". ![Etherscan 03](/assets/images/03-198c876c78c88c3edbb48b4725ee3437.webp) ### Step 2: Fill in parameters We will now proceed to create our first stream. Let's go with the following parameters: - a Lockup Linear stream - and a deposit of 100 DAI - starting Jan 1, 2026 - ending Jan 1, 2027 - with cliff until Jan 24, 2026 and cliff amount of 2 DAI - no token unlock at start time - cancelable - and transferrable As the start and end date are fixed, we'll be using the [`createwithtimestampsLL`](/reference/lockup/contracts/interfaces/interface.ISablierLockupLinear#createwithtimestampsll) method. Please note that using [`createwithdurationsLL`](/reference/lockup/contracts/interfaces/interface.ISablierLockupLinear#createwithdurationsll) is also possible if you specify durations instead of the timestamps. Open the **"createwithtimestampsLL"** method, and start filling in the stream details: :::note All functions are marked as `payable`, however, its entirely optional to attach any value (in native token) to the transaction. For this example, we will not be attaching any value and therefore `payableAmount(ether)` will be 0. ::: ![Etherscan 04](/assets/images/04-a7dcb2a87f73e876fa832cbfb9c1efd8.webp) ```json { "sender": "0xb1bEF51ebCA01EB12001a639bDBbFF6eEcA12B9F", "recipient": "0xf26994E6Af0b95cCa8DFA22A0BC25E1f38a54C42", "depositAmount": 100000000000000000000, "token": "0x776b6fC2eD15D6Bb5Fc32e0c89DE68683118c62A", "cancelable": true, "transferable": true, "timestamps": [1767261600, 1798797600], "shape": "", "unlockAmounts": ["0", "2000000000000000000"], "granularity": 1, "cliffTime": 1769261600 } ``` If the Etherscan UI does not break down the inputs into separate fields (like in the screenshot above), you will have to provide it like this: ```json [ "0xb1bEF51ebCA01EB12001a639bDBbFF6eEcA12B9F", "0xf26994E6Af0b95cCa8DFA22A0BC25E1f38a54C42", 100000000000000000000, "0x776b6fC2eD15D6Bb5Fc32e0c89DE68683118c62A", false, true, [1737936000, 1769472000], "", ["0", "2000000000000000000"], 1, 1738195200 ] ``` :::tip In case of a tuple, ensure to follow the best practices: 1. Use square brackets 2. Wrap addresses in double quotes 3. Wrap large numbers in quotes Follow [this guide](https://info.etherscan.com/understanding-the-required-input-formats-for-read-write-contract-tab/) from Etherscan to learn how to correctly format input data for Write Contract tab. ::: As an example, in the screenshot below, we are providing input parameters for [`createWithTimestampsLL`](/reference/lockup/contracts/contract.SablierBatchLockup#createwithtimestampsll) function in [`SablierBatchLockup`](https://sepolia.etherscan.io/address/0x44Fd5d5854833975E5Fc80666a10cF3376C088E0#writeContract) contract. As you can see, since `batch` requires a tuple and does not break it down into separate fields, we had to use the above method. ![Etherscan 08](/assets/images/08-06813f7b67e28fb91ad633d3703e1b59.webp) ```json [ [ "0xb1bEF51ebCA01EB12001a639bDBbFF6eEcA12B9F", "0xf26994E6Af0b95cCa8DFA22A0BC25E1f38a54C42", "100000000000000000000", true, true, [1767261600, 1798797600], 1769261600, ["0", "2000000000000000000"], 1, "" ] ] ``` #### Sender If the stream is cancelable, the sender is the wallet that will have the ability to cancel and renounce the stream. But if the stream is non-cancelable, the sender cannot cancel the stream. Most users will set their own wallet address as the sender. #### Recipient The address you want to stream tokens to. The owner of this address is the stream recipient and will receive tokens on [withdraw](#withdrawing-from-a-stream). #### Deposit Amount This is the deposit amount of tokens available to be streamed, **DECIMALS INCLUDED**. If the token has 18 decimals, for example, you will need to add eighteen zeros after the amount. Let's say you want to stream 20,000 DAI like in this example, you will need to fill in `20000000000000000000000`. #### Token The token is the contract address of the ERC-20 token being streamed. You can obtain this from the [Sablier Interface](#step-1-go-to-token-page) or from [Etherscan explorer](https://sepolia.etherscan.io/token/0x776b6fc2ed15d6bb5fc32e0c89de68683118c62a). Please double check the token address is correct before continuing. #### Cancelable This field indicates whether or not you want the stream to be cancelable. This can be set to either `true` or `false`. If set to true, the stream will be cancelable. You can make a cancelable stream non-cancelable after the stream has been created, but if it's a non-cancelable stream, it cannot become cancelable post-creation. #### Transferable The `transferable` field indicates whether the NFT owner is allowed to transfer the NFT or not. This can be set to either `true` or `false`. This flag cannot be changed later. #### Timestamps The `timestamps` field contains the start time, and the end time of the stream, in this order. The values should be UNIX timestamps (represented in **seconds**). You can find a Unix timestamp converter [here](https://www.unixtimestamp.com/). #### Shape The shape field can be used to specify the shape of the stream that you want the User Interface to display. This is an optional field and can be left empty. #### Unlock Amounts The `unlockAmounts` field contains the amount of tokens that will be unlocked at the start time and at the cliff time. For this example, we do not want to unlock any amount at the start time, however, we want to unlock 2 DAI at the cliff time. #### Cliff Time If you prefer to not have a cliff, you can simply set the cliff time to 0. If, however, you want to have a cliff, fill in the timestamp for the cliff there. #### Granularity The `granularity` field defines the smallest step in time (in seconds) between two consecutive token unlocks. For example, a granularity of `1` means tokens unlock every second (standard linear behavior), while `604800` means tokens unlock weekly in discrete steps. A value of `0` is a sentinel value that is equivalent to 1 second. For most standard linear streams, set this to `1`. :::caution Inside tuples/arrays (the `[ ... ]` structures in the example) make sure that you: - Use square brackets - Wrap addresses in double quotes, i.e. `" "` ::: Once the data is filled, and after you double-checked, click on the "Write" button and confirm the transaction in your wallet. That's all! You are done. You can now head over to the [Sablier Interface](https://app.sablier.com), connect your wallet, and your stream should appear like this: ![Etherscan 05](/assets/images/05-41b5cf45dada1a773d8e0eb2e362c48b.webp) #### How about [`createWithDurationsLL`](/reference/lockup/contracts/abstracts/abstract.SablierLockupLinear#createwithdurationsll)? For the durations version, we'll replace the `timestamps` and `cliffTime` parameters with a single `durations` parameter to represent the total duration of the stream (in seconds) and the duration of the cliff (in seconds). ```json { "granularity": 1, "durations": [0, 864000] // no cliff and a total duration of 10 days } ``` | Total Duration | Cliff Duration | \[Cliff, Total\] | | --- | --- | --- | | 10 days | no cliff | `[0, 864000]` | | 10 days | 1 day | `[86400, 864000]` | ## Withdrawing from a Stream ### Prerequisites To withdraw from a stream using Etherscan, you will need to obtain the stream's ID. To obtain this without the Sablier Interface, find the transaction which created the stream on Etherscan. Here's an [example](https://sepolia.etherscan.io/tx/0x1346d7bcb82b70f20e35ed2d404b0b65593344cf54b4b402af170434799e40cf) of what it should look like. Once found, you will see the stream ID between the two brackets. Note that stream ID and "Token ID" are the same thing. :::info Anyone can withdraw on your behalf if they pay the gas fee. When a third party withdraws, the recipient is the only allowed withdrawal address. However, if you withdraw yourself, you can choose to withdraw to any other address. You can read more about this advanced feature [here](/reference/lockup/access-control#overview). ::: ![Etherscan 06](/assets/images/06-cbc232a18f5f2c26af6fe26e03b2e8c4.webp) ### Step 1: Go to contract page Head over to our [deployments](/guides/lockup/deployments) list and select the contract address you want to interact with. Once you find the right contract, click on the address to access its explorer's page. Click on the "Contract" tab, and then on the "Write Contract" sub-tab. ![Etherscan 01](/assets/images/01-9c40cb2f1951fc3abcdfcfbf4d1ce2a5.webp) ![Etherscan 02](/assets/images/02-4713fd45c3a8951bac9917f08462a49b.webp) You can now connect your wallet to the interface by clicking on "Connect to Web3". ![Etherscan 03](/assets/images/03-198c876c78c88c3edbb48b4725ee3437.webp) ### Step 2: Fill in parameters Head over to the **`withdraw`** method, and fill in the data. ![Etherscan 07](/assets/images/07-71a4eaa32841bc09c260466edf4128c1.webp) ```json { "streamId": 1, "to": "0xb1bEF51ebCA01EB12001a639bDBbFF6eEcA12B9F", "amount": 100000000000000000000 } ``` #### Stream ID The `streamId` is the value you have previously located in the transaction in which the stream was created. #### To The `to` field is there for the stream recipient's address. This will most likely be your own wallet, but you can also choose to withdraw these funds to another wallet (e.g. a separate cold wallet) if you are the stream's recipient. If you are not the stream recipient, it MUST be the address of the recipient. #### Amount This represents the amount of tokens that you want to withdraw, **DECIMALS INCLUDED**. For example, if the token you are looking to withdraw has 18 decimals, you will need to add eighteen zeros after the amount. Let's say you want to withdraw 100 DAI like in this example, you will need to put in `100000000000000000000`. Oh, and make sure that the amount has already been streamed, you cannot withdraw funds that haven't yet been streamed over to you. Once ready, click on the "Write" button, and confirm the transaction in your wallet. You are done! --- Apart from the main flows, you may be required to do some other actions, usually listed as prerequisites. ## Prerequisite: ERC20 Allowances Before interacting directly with the Lockup [contracts](/guides/lockup/deployments) to [create a stream](#creating-a-stream) you will need to manually grant proper ERC20 allowances. ### Step 1: Go to token page Pick a token you want to stream, e.g. [DAI](https://sepolia.etherscan.io/token/0x68194a729C2450ad26072b3D33ADaCbcef39D574). Using its address, visit the token page on Etherscan (in this example, we're using Ethereum Sepolia): `https://sepolia.etherscan.io/token/` :::info To get the address of an asset in the [Sablier Interface](/apps/features/overview), you can click on its name in the token list dialog or find an existing stream with that token and click on the icon inside the stream circle. ![Etherscan 09](/assets/images/09-e0763326ba0f69fd2daa16f13c3e9968.webp) ![Etherscan 10](/assets/images/10-d2f81cb0ef08d5a1aa7b1eb0437f3322.webp) ::: ### Step 2: Go to `approve` Next, look for the "Contract" tab and the "Write Contract" sub-tab. You'll see a list of methods that can be called for that token. Pick the `approve` method (e.g. [DAI's approve](https://sepolia.etherscan.io/token/0x68194a729C2450ad26072b3D33ADaCbcef39D574#writeContract#F1)). Most ERC-20 approve methods will contain two fields: 1. The spender 2. The amount :::tip Some tokens like [USDC](https://etherscan.io/token/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48?a=#writeProxyContract) or [AAVE](https://etherscan.io/token/0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9?a=#writeProxyContract) are upgradeable and use a proxy pattern. For these, you have to use the "Write as Proxy" tab. ::: ### Step 3: Send transaction For the purpose of creating a **LockupLinear** stream with Lockup, the spender will be the [SablierLockup](/guides/lockup/deployments) contract. As for the amount, you'll have to pad it with the right number of decimals. For DAI, that's 18 decimals, so a value of `100` will turn into `100 * 1e18` (100 followed by 18 zeroes). For USDC, that's 6 decimals, so a value of `100` will turn into `100 * 1e6` (100 followed by 6 zeroes). The same logic applies to the [deposit amounts](#deposit-amount) when creating the stream. ```json { "spender": "0x6b0307b4338f2963A62106028E3B074C2c0510DA", "amount": 100000000000000000000 } ``` Before clicking on the "Write" button to submit your allowance update, make sure to connect your wallet to the interface by clicking on "Connect to Web3". --- ## Create a Batch of Dynamic Streams Source: https://docs.sablier.com/guides/lockup/examples/batch-create-streams/batch-lockup-dynamic # Create a Batch of Dynamic Streams In this guide, we will show you how you can use Solidity to batch create dynamic streams via the [Batch Lockup](/reference/lockup/contracts/contract.SablierBatchLockup) contract. This guide assumes that you have already gone through the [Protocol Concepts](/concepts/streaming) section. :::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: ```solidity // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.22; ``` Now, import the relevant symbols from `@sablier/lockup`: ```solidity import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ud2x18 } from "@prb/math/src/UD2x18.sol"; import { ISablierBatchLockup } from "@sablier/lockup/src/interfaces/ISablierBatchLockup.sol"; import { ISablierLockup } from "@sablier/lockup/src/interfaces/ISablierLockup.sol"; import { BatchLockup } from "@sablier/lockup/src/types/BatchLockup.sol"; import { LockupDynamic } from "@sablier/lockup/src/types/LockupDynamic.sol"; ``` Create a contract called `BatchLDStreamCreator`, and declare a constant `DAI` of type `IERC20`, a constant `LOCKUP` of type `ISablierLockup`, and a constant `BATCH_LOCKUP` of type `ISablierBatchLockup`: ```solidity // Mainnet addresses IERC20 public constant DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); // See https://docs.sablier.com/guides/lockup/deployments for all deployments ISablierLockup public constant LOCKUP = ISablierLockup(0x93b37Bd5B6b278373217333Ac30D7E74c85fBDCB); ISablierBatchLockup public constant BATCH_LOCKUP = ISablierBatchLockup(0x4f3be262D1358A82b468CF81bfc5A9cC32Cf9875); ``` 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 Ethereum Sepolia. If you need to work with a different chain, Lockup addresses can be obtained from the [Lockup Deployments](/guides/lockup/deployments) page. ## Batch create functions There are two batch create functions for the Dynamic streams: - [`createWithDurationsLD`](/reference/lockup/contracts/contract.SablierBatchLockup#createwithdurationsld) - [`createWithTimestampsLD`](/reference/lockup/contracts/contract.SablierBatchLockup#createwithtimestampsld) Which one you choose depends upon your use case. In this guide, we will use `createWithTimestampsLD`. ## Function definition Define a function called `batchCreateStreams` that takes a parameter `perStreamAmount` and returns an array of ids for the created streams: ```solidity BatchLockup.CreateWithTimestampsLD memory stream0; stream0.sender = address(0xABCD); // The sender to stream the tokens, he will be able to cancel the stream stream0.recipient = address(0xCAFE); // The recipient of the streamed tokens stream0.depositAmount = perStreamAmount; // The deposit 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.startTime = uint40(block.timestamp); // The start time of the stream // Declare some dummy segments stream0.segments = new LockupDynamic.Segment[](2); stream0.segments[0] = LockupDynamic.Segment({ amount: uint128(perStreamAmount / 2), exponent: ud2x18(0.25e18), timestamp: uint40(block.timestamp + 1 weeks) }); stream0.segments[1] = (LockupDynamic.Segment({ amount: uint128(perStreamAmount - stream0.segments[0].amount), exponent: ud2x18(2.71e18), timestamp: uint40(block.timestamp + 24 weeks) })); ``` ## Batch size Next, declare a batch size, which is needed to calculate the transfer amount: ```solidity BatchLockup.CreateWithTimestampsLD memory stream1; stream1.sender = address(0xABCD); // The sender to stream the tokens, he will be able to cancel the stream stream1.recipient = address(0xBEEF); // The recipient of the streamed tokens stream1.depositAmount = perStreamAmount; // The deposit amount of each stream stream1.cancelable = false; // Whether the stream will be cancelable or not stream1.transferable = false; // Whether the recipient can transfer the NFT or not stream1.startTime = uint40(block.timestamp); // The start time of the stream // Declare some dummy segments stream1.segments = new LockupDynamic.Segment[](2); stream1.segments[0] = LockupDynamic.Segment({ amount: uint128(perStreamAmount / 4), exponent: ud2x18(1e18), timestamp: uint40(block.timestamp + 4 weeks) }); stream1.segments[1] = (LockupDynamic.Segment({ amount: uint128(perStreamAmount - stream1.segments[0].amount), exponent: ud2x18(3.14e18), timestamp: uint40(block.timestamp + 52 weeks) })); ``` ## 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 tokens that the creator contract will be in possession of after they are transferred from the calling address (you): ```solidity BatchLockup.CreateWithTimestampsLD[] memory batch = new BatchLockup.CreateWithTimestampsLD[](batchSize); batch[0] = stream0; batch[1] = stream1; ``` For more guidance on how to approve and transfer ERC-20 tokens, see [this article](https://ethereum.org/en/developers/docs/standards/tokens/erc-20/) on the Ethereum website. ## Stream Parameters Given that we declared a `batchSize` of two, we need to define two [BatchLockup.CreateWithTimestampsLD](/reference/lockup/contracts/types/library.BatchLockup#createwithtimestampsld) structs: ```solidity streamIds = BATCH_LOCKUP.createWithTimestampsLD(LOCKUP, DAI, batch); } } ``` To add some variety, we will change the parameters of the second stream: ```solidity // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.22; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ud2x18 } from "@prb/math/src/UD2x18.sol"; import { ISablierBatchLockup } from "@sablier/lockup/src/interfaces/ISablierBatchLockup.sol"; import { ISablierLockup } from "@sablier/lockup/src/interfaces/ISablierLockup.sol"; import { BatchLockup } from "@sablier/lockup/src/types/BatchLockup.sol"; import { LockupDynamic } from "@sablier/lockup/src/types/LockupDynamic.sol"; contract BatchLDStreamCreator { // Mainnet addresses IERC20 public constant DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); // See https://docs.sablier.com/guides/lockup/deployments for all deployments ISablierLockup public constant LOCKUP = ISablierLockup(0x93b37Bd5B6b278373217333Ac30D7E74c85fBDCB); ISablierBatchLockup public constant BATCH_LOCKUP = ISablierBatchLockup(0x4f3be262D1358A82b468CF81bfc5A9cC32Cf9875); /// @dev For this function to work, the sender must have approved this dummy contract to spend DAI. function batchCreateStreams(uint128 perStreamAmount) public returns (uint256[] memory streamIds) { // Create a batch of two streams uint256 batchSize = 2; // Calculate the combined amount of DAI tokens to transfer to this contract uint256 transferAmount = perStreamAmount * batchSize; // 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({ spender: address(BATCH_LOCKUP), value: transferAmount }); // Declare the first stream in the batch BatchLockup.CreateWithTimestampsLD memory stream0; stream0.sender = address(0xABCD); // The sender to stream the tokens, he will be able to cancel the stream stream0.recipient = address(0xCAFE); // The recipient of the streamed tokens stream0.depositAmount = perStreamAmount; // The deposit 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.startTime = uint40(block.timestamp); // The start time of the stream // Declare some dummy segments stream0.segments = new LockupDynamic.Segment[](2); stream0.segments[0] = LockupDynamic.Segment({ amount: uint128(perStreamAmount / 2), exponent: ud2x18(0.25e18), timestamp: uint40(block.timestamp + 1 weeks) }); stream0.segments[1] = (LockupDynamic.Segment({ amount: uint128(perStreamAmount - stream0.segments[0].amount), exponent: ud2x18(2.71e18), timestamp: uint40(block.timestamp + 24 weeks) })); // Declare the second stream in the batch BatchLockup.CreateWithTimestampsLD memory stream1; stream1.sender = address(0xABCD); // The sender to stream the tokens, he will be able to cancel the stream stream1.recipient = address(0xBEEF); // The recipient of the streamed tokens stream1.depositAmount = perStreamAmount; // The deposit amount of each stream stream1.cancelable = false; // Whether the stream will be cancelable or not stream1.transferable = false; // Whether the recipient can transfer the NFT or not stream1.startTime = uint40(block.timestamp); // The start time of the stream // Declare some dummy segments stream1.segments = new LockupDynamic.Segment[](2); stream1.segments[0] = LockupDynamic.Segment({ amount: uint128(perStreamAmount / 4), exponent: ud2x18(1e18), timestamp: uint40(block.timestamp + 4 weeks) }); stream1.segments[1] = (LockupDynamic.Segment({ amount: uint128(perStreamAmount - stream1.segments[0].amount), exponent: ud2x18(3.14e18), timestamp: uint40(block.timestamp + 52 weeks) })); // Fill the batch array BatchLockup.CreateWithTimestampsLD[] memory batch = new BatchLockup.CreateWithTimestampsLD[](batchSize); batch[0] = stream0; batch[1] = stream1; streamIds = BATCH_LOCKUP.createWithTimestampsLD(LOCKUP, DAI, batch); } } ``` Once both structs are declared, the batch array has to be filled: ```solidity function batchCreateStreams(uint128 perStreamAmount) public returns (uint256[] memory streamIds) { // Create a batch of two streams uint256 batchSize = 2; // Calculate the combined amount of DAI tokens to transfer to this contract uint256 transferAmount = perStreamAmount * batchSize; // 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({ spender: address(BATCH_LOCKUP), value: transferAmount }); // Declare the first stream in the batch BatchLockup.CreateWithDurationsLL memory stream0; stream0.sender = address(0xABCD); // The sender to stream the tokens, he will be able to cancel the stream stream0.recipient = address(0xCAFE); // The recipient of the streamed tokens stream0.depositAmount = perStreamAmount; // The deposit amount of each stream 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, // Tokens will start streaming continuously after 4 weeks total: 52 weeks // Setting a total duration of ~1 year }); stream0.unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, // Whether the stream will unlock a certain amount of tokens at the start time cliff: 0 // Whether the stream will unlock a certain amount of tokens at the cliff time }); // Declare the second stream in the batch BatchLockup.CreateWithDurationsLL memory stream1; stream1.sender = address(0xABCD); // The sender to stream the tokens, he will be able to cancel the stream stream1.recipient = address(0xBEEF); // The recipient of the streamed tokens stream1.depositAmount = perStreamAmount; // The deposit amount of each stream 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, // Tokens will start streaming continuously after 4 weeks total: 26 weeks // Setting a total duration of ~6 months }); stream1.unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, // Whether the stream will unlock a certain amount of tokens at the start time cliff: 0 // Whether the stream will unlock a certain amount of tokens at the start time }); // Fill the batch param BatchLockup.CreateWithDurationsLL[] memory batch = new BatchLockup.CreateWithDurationsLL[](batchSize); batch[0] = stream0; batch[1] = stream1; streamIds = BATCH_LOCKUP.createWithDurationsLL(LOCKUP, DAI, batch); } } ``` ## Invoke the batch create function With all parameters set, we can now call the `createWithTimestampsLD` function, and assign the ids of the newly created streams to the array: ```solidity // Create a batch of two streams uint256 batchSize = 2; // Calculate the combined amount of DAI tokens to transfer to this contract uint256 transferAmount = perStreamAmount * batchSize; ``` ## Full code Below you can see the full code. You can also access the code on GitHub through [this link](https://github.com/sablier-labs/evm-monorepo/blob/main/misc/examples/lockup/BatchLDStreamCreator.sol). ```solidity // 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({ spender: address(BATCH_LOCKUP), value: transferAmount }); ``` --- ## Create a Batch of Linear Streams Source: https://docs.sablier.com/guides/lockup/examples/batch-create-streams/batch-lockup-linear # Create a Batch of Linear Streams In this guide, we will show you how you can use Solidity to batch create linear streams via the [Batch Lockup](/reference/lockup/contracts/contract.SablierBatchLockup) contract. This guide assumes that you have already gone through the [Protocol Concepts](/concepts/streaming) section. :::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: ```solidity // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.22; ``` Now, import the relevant symbols from `@sablier/lockup`: ```solidity import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ISablierLockup } from "@sablier/lockup/src/interfaces/ISablierLockup.sol"; import { ISablierBatchLockup } from "@sablier/lockup/src/interfaces/ISablierBatchLockup.sol"; import { BatchLockup } from "@sablier/lockup/src/types/BatchLockup.sol"; import { LockupLinear } from "@sablier/lockup/src/types/LockupLinear.sol"; ``` Create a contract called `BatchLLStreamCreator`, and declare a constant `DAI` of type `IERC20`, a constant `LOCKUP` of type `ISablierLockup`, and a constant `BATCH_LOCKUP` of type `ISablierBatchLockup`: ```solidity // Mainnet addresses IERC20 public constant DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); // See https://docs.sablier.com/guides/lockup/deployments for all deployments ISablierLockup public constant LOCKUP = ISablierLockup(0x93b37Bd5B6b278373217333Ac30D7E74c85fBDCB); ISablierBatchLockup public constant BATCH_LOCKUP = ISablierBatchLockup(0x4f3be262D1358A82b468CF81bfc5A9cC32Cf9875); ``` 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 Ethereum Sepolia. If you need to work with a different chain, Lockup addresses can be obtained from the [Lockup Deployments](/guides/lockup/deployments) page. ## Batch create functions There are two batch create functions for the Linear streams: - [`createWithDurationsLL`](/reference/lockup/contracts/contract.SablierBatchLockup#createwithdurationsll) - [`createWithTimestampsLL`](/reference/lockup/contracts/contract.SablierBatchLockup#createwithtimestampsll) 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: ```solidity BatchLockup.CreateWithDurationsLL memory stream0; stream0.sender = address(0xABCD); // The sender to stream the tokens, he will be able to cancel the stream stream0.recipient = address(0xCAFE); // The recipient of the streamed tokens stream0.depositAmount = perStreamAmount; // The deposit amount of each stream 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, // Tokens will start streaming continuously after 4 weeks total: 52 weeks // Setting a total duration of ~1 year }); stream0.unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, // Whether the stream will unlock a certain amount of tokens at the start time cliff: 0 // Whether the stream will unlock a certain amount of tokens at the cliff time }); ``` ## Batch size Next, declare a batch size, which is needed to calculate the transfer amount: ```solidity BatchLockup.CreateWithDurationsLL memory stream1; stream1.sender = address(0xABCD); // The sender to stream the tokens, he will be able to cancel the stream stream1.recipient = address(0xBEEF); // The recipient of the streamed tokens stream1.depositAmount = perStreamAmount; // The deposit amount of each stream 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, // Tokens will start streaming continuously after 4 weeks total: 26 weeks // Setting a total duration of ~6 months }); stream1.unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, // Whether the stream will unlock a certain amount of tokens at the start time cliff: 0 // Whether the stream will unlock a certain amount of tokens at the start time }); ``` ## 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 tokens that the creator contract will be in possession of after they are transferred from the calling address (you): ```solidity BatchLockup.CreateWithDurationsLL[] memory batch = new BatchLockup.CreateWithDurationsLL[](batchSize); batch[0] = stream0; batch[1] = stream1; ``` For more guidance on how to approve and transfer ERC-20 tokens, see [this article](https://ethereum.org/en/developers/docs/standards/tokens/erc-20/) on the Ethereum website. ## Stream Parameters Given that we declared a `batchSize` of two, we need to define two [BatchLockup.CreateWithDurationsLL](/reference/lockup/contracts/types/library.BatchLockup#createwithdurationsll) structs: ```solidity streamIds = BATCH_LOCKUP.createWithDurationsLL(LOCKUP, DAI, batch); } } ``` To add some variety, we will change the parameters of the second stream: ```solidity // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.22; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ISablierLockup } from "@sablier/lockup/src/interfaces/ISablierLockup.sol"; import { ISablierBatchLockup } from "@sablier/lockup/src/interfaces/ISablierBatchLockup.sol"; import { BatchLockup } from "@sablier/lockup/src/types/BatchLockup.sol"; import { LockupLinear } from "@sablier/lockup/src/types/LockupLinear.sol"; contract BatchLLStreamCreator { // Mainnet addresses IERC20 public constant DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); // See https://docs.sablier.com/guides/lockup/deployments for all deployments ISablierLockup public constant LOCKUP = ISablierLockup(0x93b37Bd5B6b278373217333Ac30D7E74c85fBDCB); ISablierBatchLockup public constant BATCH_LOCKUP = ISablierBatchLockup(0x4f3be262D1358A82b468CF81bfc5A9cC32Cf9875); /// @dev For this function to work, the sender must have approved this dummy contract to spend DAI. function batchCreateStreams(uint128 perStreamAmount) public returns (uint256[] memory streamIds) { // Create a batch of two streams uint256 batchSize = 2; // Calculate the combined amount of DAI tokens to transfer to this contract uint256 transferAmount = perStreamAmount * batchSize; // 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({ spender: address(BATCH_LOCKUP), value: transferAmount }); // Declare the first stream in the batch BatchLockup.CreateWithDurationsLL memory stream0; stream0.sender = address(0xABCD); // The sender to stream the tokens, he will be able to cancel the stream stream0.recipient = address(0xCAFE); // The recipient of the streamed tokens stream0.depositAmount = perStreamAmount; // The deposit amount of each stream 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, // Tokens will start streaming continuously after 4 weeks total: 52 weeks // Setting a total duration of ~1 year }); stream0.unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, // Whether the stream will unlock a certain amount of tokens at the start time cliff: 0 // Whether the stream will unlock a certain amount of tokens at the cliff time }); // Declare the second stream in the batch BatchLockup.CreateWithDurationsLL memory stream1; stream1.sender = address(0xABCD); // The sender to stream the tokens, he will be able to cancel the stream stream1.recipient = address(0xBEEF); // The recipient of the streamed tokens stream1.depositAmount = perStreamAmount; // The deposit amount of each stream 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, // Tokens will start streaming continuously after 4 weeks total: 26 weeks // Setting a total duration of ~6 months }); stream1.unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, // Whether the stream will unlock a certain amount of tokens at the start time cliff: 0 // Whether the stream will unlock a certain amount of tokens at the start time }); // Fill the batch param BatchLockup.CreateWithDurationsLL[] memory batch = new BatchLockup.CreateWithDurationsLL[](batchSize); batch[0] = stream0; batch[1] = stream1; streamIds = BATCH_LOCKUP.createWithDurationsLL(LOCKUP, DAI, batch); } } ``` Once both structs are declared, the batch array has to be filled: ```solidity function batchCreateStreams(uint128 perStreamAmount) public returns (uint256[] memory streamIds) { // Create a batch of two streams uint256 batchSize = 2; // Calculate the combined amount of DAI tokens to transfer to this contract uint256 transferAmount = perStreamAmount * batchSize; // 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({ spender: address(BATCH_LOCKUP), value: transferAmount }); // Declare the first stream in the batch BatchLockup.CreateWithDurationsLL memory stream0; stream0.sender = address(0xABCD); // The sender to stream the tokens, he will be able to cancel the stream stream0.recipient = address(0xCAFE); // The recipient of the streamed tokens stream0.depositAmount = perStreamAmount; // The deposit amount of each stream 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, // Tokens will start streaming continuously after 4 weeks total: 52 weeks // Setting a total duration of ~1 year }); stream0.unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, // Whether the stream will unlock a certain amount of tokens at the start time cliff: 0 // Whether the stream will unlock a certain amount of tokens at the cliff time }); // Declare the second stream in the batch BatchLockup.CreateWithDurationsLL memory stream1; stream1.sender = address(0xABCD); // The sender to stream the tokens, he will be able to cancel the stream stream1.recipient = address(0xBEEF); // The recipient of the streamed tokens stream1.depositAmount = perStreamAmount; // The deposit amount of each stream 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, // Tokens will start streaming continuously after 4 weeks total: 26 weeks // Setting a total duration of ~6 months }); stream1.unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, // Whether the stream will unlock a certain amount of tokens at the start time cliff: 0 // Whether the stream will unlock a certain amount of tokens at the start time }); // Fill the batch param BatchLockup.CreateWithDurationsLL[] memory batch = new BatchLockup.CreateWithDurationsLL[](batchSize); batch[0] = stream0; batch[1] = stream1; streamIds = BATCH_LOCKUP.createWithDurationsLL(LOCKUP, DAI, batch); } } ``` ## 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: ```solidity // Create a batch of two streams uint256 batchSize = 2; // Calculate the combined amount of DAI tokens to transfer to this contract uint256 transferAmount = perStreamAmount * batchSize; ``` ## Full code Below you can see the full code. You can also access the code on GitHub through [this link](https://github.com/sablier-labs/evm-monorepo/blob/main/misc/examples/lockup/BatchLLStreamCreator.sol). ```solidity // 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({ spender: address(BATCH_LOCKUP), value: transferAmount }); ``` --- ## Create a Batch of Tranched Streams Source: https://docs.sablier.com/guides/lockup/examples/batch-create-streams/batch-lockup-tranched # Create a Batch of Tranched Streams In this guide, we will show you how you can use Solidity to batch create tranched streams via the [Batch Lockup](/reference/lockup/contracts/contract.SablierBatchLockup) contract. This guide assumes that you have already gone through the [Protocol Concepts](/concepts/streaming) section. :::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: ```solidity // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.22; ``` Now, import the relevant symbols from `@sablier/lockup`: ```solidity import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ISablierBatchLockup } from "@sablier/lockup/src/interfaces/ISablierBatchLockup.sol"; import { ISablierLockup } from "@sablier/lockup/src/interfaces/ISablierLockup.sol"; import { BatchLockup } from "@sablier/lockup/src/types/BatchLockup.sol"; import { LockupTranched } from "@sablier/lockup/src/types/LockupTranched.sol"; ``` Create a contract called `BatchLTStreamCreator`, and declare a constant `DAI` of type `IERC20`, a constant `LOCKUP` of type `ISablierLockup`, and a constant `BATCH_LOCKUP` of type `ISablierBatchLockup`: ```solidity // Mainnet addresses IERC20 public constant DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); // See https://docs.sablier.com/guides/lockup/deployments for all deployments ISablierLockup public constant LOCKUP = ISablierLockup(0x93b37Bd5B6b278373217333Ac30D7E74c85fBDCB); ISablierBatchLockup public constant BATCH_LOCKUP = ISablierBatchLockup(0x4f3be262D1358A82b468CF81bfc5A9cC32Cf9875); ``` 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 Ethereum Sepolia. If you need to work with a different chain, Lockup addresses can be obtained from the [Lockup Deployments](/guides/lockup/deployments) page. ## Batch create functions There are two batch create functions for the Tranched streams: - [`createWithDurationsLT`](/reference/lockup/contracts/contract.SablierBatchLockup#createwithdurationslt) - [`createWithTimestampsLT`](/reference/lockup/contracts/contract.SablierBatchLockup#createwithtimestampslt) Which one you choose depends upon your use case. In this guide, we will use `createWithTimestampsLT`. ## Function definition Define a function called `batchCreateStreams` that takes a parameter `perStreamAmount` and returns an array of ids for the created streams: ```solidity BatchLockup.CreateWithTimestampsLT memory stream0; stream0.sender = address(0xABCD); // The sender to stream the tokens, he will be able to cancel the stream stream0.recipient = address(0xCAFE); // The recipient of the streamed tokens stream0.depositAmount = perStreamAmount; // The deposit amount of each stream stream0.cancelable = true; // Whether the stream will be cancelable or not stream0.transferable = false; // Whether the recipient can transfer the NFT or not stream0.startTime = uint40(block.timestamp); // Set the start time to block timestamp // Declare some dummy tranches stream0.tranches = new LockupTranched.Tranche[](2); stream0.tranches[0] = LockupTranched.Tranche({ amount: uint128(perStreamAmount / 2), timestamp: uint40(block.timestamp + 1 weeks) }); stream0.tranches[1] = LockupTranched.Tranche({ amount: uint128(perStreamAmount - stream0.tranches[0].amount), timestamp: uint40(block.timestamp + 24 weeks) }); ``` ## Batch size Next, declare a batch size, which is needed to calculate the transfer amount: ```solidity BatchLockup.CreateWithTimestampsLT memory stream1; stream1.sender = address(0xABCD); // The sender to stream the tokens, he will be able to cancel the stream stream1.recipient = address(0xBEEF); // The recipient of the streamed tokens stream1.depositAmount = perStreamAmount; // The deposit amount of each stream stream1.cancelable = false; // Whether the stream will be cancelable or not stream1.transferable = false; // Whether the recipient can transfer the NFT or not stream1.startTime = uint40(block.timestamp); // Set the start time to block timestamp // Declare some dummy tranches stream1.tranches = new LockupTranched.Tranche[](2); stream1.tranches[0] = LockupTranched.Tranche({ amount: uint128(perStreamAmount / 4), timestamp: uint40(block.timestamp + 4 weeks) }); stream1.tranches[1] = LockupTranched.Tranche({ amount: uint128(perStreamAmount - stream1.tranches[0].amount), timestamp: uint40(block.timestamp + 24 weeks) }); ``` ## 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 tokens that the creator contract will be in possession of after they are transferred from the calling address (you): ```solidity BatchLockup.CreateWithTimestampsLT[] memory batch = new BatchLockup.CreateWithTimestampsLT[](batchSize); batch[0] = stream0; batch[1] = stream1; ``` For more guidance on how to approve and transfer ERC-20 tokens, see [this article](https://ethereum.org/en/developers/docs/standards/tokens/erc-20/) on the Ethereum website. ## Stream Parameters Given that we declared a `batchSize` of two, we need to define two [BatchLockup.CreateWithTimestampsLT](/reference/lockup/contracts/types/library.BatchLockup#createwithtimestampslt) structs: ```solidity streamIds = BATCH_LOCKUP.createWithTimestampsLT(LOCKUP, DAI, batch); } } ``` To add some variety, we will change the parameters of the second stream: ```solidity // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.22; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ISablierBatchLockup } from "@sablier/lockup/src/interfaces/ISablierBatchLockup.sol"; import { ISablierLockup } from "@sablier/lockup/src/interfaces/ISablierLockup.sol"; import { BatchLockup } from "@sablier/lockup/src/types/BatchLockup.sol"; import { LockupTranched } from "@sablier/lockup/src/types/LockupTranched.sol"; contract BatchLTStreamCreator { // Mainnet addresses IERC20 public constant DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); // See https://docs.sablier.com/guides/lockup/deployments for all deployments ISablierLockup public constant LOCKUP = ISablierLockup(0x93b37Bd5B6b278373217333Ac30D7E74c85fBDCB); ISablierBatchLockup public constant BATCH_LOCKUP = ISablierBatchLockup(0x4f3be262D1358A82b468CF81bfc5A9cC32Cf9875); /// @dev For this function to work, the sender must have approved this dummy contract to spend DAI. function batchCreateStreams(uint128 perStreamAmount) public returns (uint256[] memory streamIds) { // Create a batch of two streams uint256 batchSize = 2; // Calculate the combined amount of DAI tokens to transfer to this contract uint256 transferAmount = perStreamAmount * batchSize; // 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({ spender: address(BATCH_LOCKUP), value: transferAmount }); // Declare the first stream in the batch BatchLockup.CreateWithTimestampsLT memory stream0; stream0.sender = address(0xABCD); // The sender to stream the tokens, he will be able to cancel the stream stream0.recipient = address(0xCAFE); // The recipient of the streamed tokens stream0.depositAmount = perStreamAmount; // The deposit amount of each stream stream0.cancelable = true; // Whether the stream will be cancelable or not stream0.transferable = false; // Whether the recipient can transfer the NFT or not stream0.startTime = uint40(block.timestamp); // Set the start time to block timestamp // Declare some dummy tranches stream0.tranches = new LockupTranched.Tranche[](2); stream0.tranches[0] = LockupTranched.Tranche({ amount: uint128(perStreamAmount / 2), timestamp: uint40(block.timestamp + 1 weeks) }); stream0.tranches[1] = LockupTranched.Tranche({ amount: uint128(perStreamAmount - stream0.tranches[0].amount), timestamp: uint40(block.timestamp + 24 weeks) }); // Declare the second stream in the batch BatchLockup.CreateWithTimestampsLT memory stream1; stream1.sender = address(0xABCD); // The sender to stream the tokens, he will be able to cancel the stream stream1.recipient = address(0xBEEF); // The recipient of the streamed tokens stream1.depositAmount = perStreamAmount; // The deposit amount of each stream stream1.cancelable = false; // Whether the stream will be cancelable or not stream1.transferable = false; // Whether the recipient can transfer the NFT or not stream1.startTime = uint40(block.timestamp); // Set the start time to block timestamp // Declare some dummy tranches stream1.tranches = new LockupTranched.Tranche[](2); stream1.tranches[0] = LockupTranched.Tranche({ amount: uint128(perStreamAmount / 4), timestamp: uint40(block.timestamp + 4 weeks) }); stream1.tranches[1] = LockupTranched.Tranche({ amount: uint128(perStreamAmount - stream1.tranches[0].amount), timestamp: uint40(block.timestamp + 24 weeks) }); // Fill the batch array BatchLockup.CreateWithTimestampsLT[] memory batch = new BatchLockup.CreateWithTimestampsLT[](batchSize); batch[0] = stream0; batch[1] = stream1; streamIds = BATCH_LOCKUP.createWithTimestampsLT(LOCKUP, DAI, batch); } } ``` Once both structs are declared, the batch array has to be filled: ```solidity function batchCreateStreams(uint128 perStreamAmount) public returns (uint256[] memory streamIds) { // Create a batch of two streams uint256 batchSize = 2; // Calculate the combined amount of DAI tokens to transfer to this contract uint256 transferAmount = perStreamAmount * batchSize; // 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({ spender: address(BATCH_LOCKUP), value: transferAmount }); // Declare the first stream in the batch BatchLockup.CreateWithDurationsLL memory stream0; stream0.sender = address(0xABCD); // The sender to stream the tokens, he will be able to cancel the stream stream0.recipient = address(0xCAFE); // The recipient of the streamed tokens stream0.depositAmount = perStreamAmount; // The deposit amount of each stream 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, // Tokens will start streaming continuously after 4 weeks total: 52 weeks // Setting a total duration of ~1 year }); stream0.unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, // Whether the stream will unlock a certain amount of tokens at the start time cliff: 0 // Whether the stream will unlock a certain amount of tokens at the cliff time }); // Declare the second stream in the batch BatchLockup.CreateWithDurationsLL memory stream1; stream1.sender = address(0xABCD); // The sender to stream the tokens, he will be able to cancel the stream stream1.recipient = address(0xBEEF); // The recipient of the streamed tokens stream1.depositAmount = perStreamAmount; // The deposit amount of each stream 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, // Tokens will start streaming continuously after 4 weeks total: 26 weeks // Setting a total duration of ~6 months }); stream1.unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, // Whether the stream will unlock a certain amount of tokens at the start time cliff: 0 // Whether the stream will unlock a certain amount of tokens at the start time }); // Fill the batch param BatchLockup.CreateWithDurationsLL[] memory batch = new BatchLockup.CreateWithDurationsLL[](batchSize); batch[0] = stream0; batch[1] = stream1; streamIds = BATCH_LOCKUP.createWithDurationsLL(LOCKUP, DAI, batch); } } ``` ## Invoke the batch create function With all parameters set, we can now call the `createWithTimestampsLT` function, and assign the ids of the newly created streams to the array: ```solidity // Create a batch of two streams uint256 batchSize = 2; // Calculate the combined amount of DAI tokens to transfer to this contract uint256 transferAmount = perStreamAmount * batchSize; ``` ## Full code Below you can see the full code. You can also access the code on GitHub through [this link](https://github.com/sablier-labs/evm-monorepo/blob/main/misc/examples/lockup/BatchLTStreamCreator.sol). ```solidity // 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({ spender: address(BATCH_LOCKUP), value: transferAmount }); ``` --- ## Create a Lockup Dynamic Stream Source: https://docs.sablier.com/guides/lockup/examples/create-stream/lockup-dynamic # Create a Lockup Dynamic Stream Dynamic streams are streams with a custom streaming function. In this guide, we will show you how to create a Lockup Dynamic stream using Solidity. This guide assumes that you have already gone through the [Protocol Concepts](/concepts/streaming) section. :::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: ```solidity // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.22; ``` Import the relevant symbols from `@sablier/lockup`: ```solidity import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ud2x18 } from "@prb/math/src/UD2x18.sol"; import { ISablierLockup } from "@sablier/lockup/src/interfaces/ISablierLockup.sol"; import { Lockup } from "@sablier/lockup/src/types/Lockup.sol"; import { LockupDynamic } from "@sablier/lockup/src/types/LockupDynamic.sol"; ``` Create a contract called `LockupDynamicStreamCreator`, and declare a constant `DAI` of type `IERC20` and a constant `LOCKUP` of type `ISablierLockup`: ```solidity // Mainnet addresses IERC20 public constant DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); ISablierLockup public constant LOCKUP = ISablierLockup(0x93b37Bd5B6b278373217333Ac30D7E74c85fBDCB); ``` 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 Ethereum Sepolia. If you need to work with a different chain, Lockup addresses can be obtained from the [Lockup Deployments](/guides/lockup/deployments) page. There are two create functions in the Lockup contract that can be used to create Dynamic streams: - `createWithDurationsLD`: takes duration and calculates the segment timestamps based on the provided durations. - `createWithTimestampsLD`: takes UNIX timestamps for segments. Which one you choose depends upon your use case. In this guide, we will use `createWithTimestampsLD`. ## Function definition Define a function called `createStream` which takes two parameters, `amount0` and `amount1`, and which returns the id of the created stream: ```solidity function createStream(uint128 amount0, uint128 amount1) public returns (uint256 streamId) { // Sum the segment amounts uint128 depositAmount = amount0 + amount1; // Transfer the provided amount of DAI tokens to this contract DAI.transferFrom(msg.sender, address(this), depositAmount); // Approve the Sablier contract to spend DAI DAI.approve(address(LOCKUP), depositAmount); // Declare the params struct Lockup.CreateWithTimestamps memory params; // Declare the function parameters params.sender = msg.sender; // The sender will be able to cancel the stream params.recipient = address(0xCAFE); // The recipient of the streamed tokens params.depositAmount = depositAmount; // The deposit amount into the stream params.token = DAI; // The streaming token params.cancelable = true; // Whether the stream will be cancelable or not params.transferable = true; // Whether the stream will be transferable or not params.timestamps.start = uint40(block.timestamp + 100 seconds); params.timestamps.end = uint40(block.timestamp + 52 weeks); // Declare some dummy segments LockupDynamic.Segment[] memory segments = new LockupDynamic.Segment[](2); segments[0] = LockupDynamic.Segment({ amount: amount0, exponent: ud2x18(1e18), timestamp: uint40(block.timestamp + 4 weeks) }); segments[1] = (LockupDynamic.Segment({ amount: amount1, exponent: ud2x18(3.14e18), timestamp: uint40(block.timestamp + 52 weeks) })); // Create the LockupDynamic stream streamId = LOCKUP.createWithTimestampsLD({ params: params, segments: segments }); } } ``` Next, sum up the `amount0` and `amount1` parameters to get the deposit amount of the stream, which will be needed in many of the steps below: ```solidity uint128 depositAmount = amount0 + amount1; // Transfer the provided amount of DAI tokens to this contract DAI.transferFrom(msg.sender, address(this), depositAmount); // Approve the Sablier contract to spend DAI DAI.approve(address(LOCKUP), depositAmount); // Declare the params struct Lockup.CreateWithTimestamps memory params; // Declare the function parameters params.sender = msg.sender; // The sender will be able to cancel the stream params.recipient = address(0xCAFE); // The recipient of the streamed tokens params.depositAmount = depositAmount; // The deposit amount into the stream params.token = DAI; // The streaming token params.cancelable = true; // Whether the stream will be cancelable or not params.transferable = true; // Whether the stream will be transferable or not params.timestamps.start = uint40(block.timestamp + 100 seconds); params.timestamps.end = uint40(block.timestamp + 52 weeks); // Declare some dummy segments LockupDynamic.Segment[] memory segments = new LockupDynamic.Segment[](2); segments[0] = LockupDynamic.Segment({ amount: amount0, exponent: ud2x18(1e18), timestamp: uint40(block.timestamp + 4 weeks) }); segments[1] = (LockupDynamic.Segment({ amount: amount1, exponent: ud2x18(3.14e18), timestamp: uint40(block.timestamp + 52 weeks) })); // Create the LockupDynamic stream streamId = LOCKUP.createWithTimestampsLD({ params: params, segments: segments }); } } ``` ## 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 approve the Lockup contract to pull the tokens that the creator contract will be in possession of after they are transferred from the calling address (you): ```solidity Lockup.CreateWithTimestamps memory params; // Declare the function parameters params.sender = msg.sender; // The sender will be able to cancel the stream params.recipient = address(0xCAFE); // The recipient of the streamed tokens params.depositAmount = depositAmount; // The deposit amount into the stream params.token = DAI; // The streaming token params.cancelable = true; // Whether the stream will be cancelable or not params.transferable = true; // Whether the stream will be transferable or not params.timestamps.start = uint40(block.timestamp + 100 seconds); params.timestamps.end = uint40(block.timestamp + 52 weeks); // Declare some dummy segments LockupDynamic.Segment[] memory segments = new LockupDynamic.Segment[](2); segments[0] = LockupDynamic.Segment({ amount: amount0, exponent: ud2x18(1e18), timestamp: uint40(block.timestamp + 4 weeks) }); segments[1] = (LockupDynamic.Segment({ amount: amount1, exponent: ud2x18(3.14e18), timestamp: uint40(block.timestamp + 52 weeks) })); // Create the LockupDynamic stream streamId = LOCKUP.createWithTimestampsLD({ params: params, segments: segments }); } } ``` For more guidance on how to approve and transfer ERC-20 tokens, see [this article](https://ethereum.org/en/developers/docs/standards/tokens/erc-20/) on the Ethereum website. ## Parameters The struct associated with [`createWithTimestampsLD`](/reference/lockup/contracts/interfaces/interface.ISablierLockupDynamic#createwithtimestampsld) are [`Lockup.CreateWithTimestamps`](/reference/lockup/contracts/types/library.Lockup#createwithtimestamps) (a shared struct across all the lockup streams) and [`LockupDynamic.Segment`](/reference/lockup/contracts/types/library.LockupDynamic#segment). Let's review each parameter in detail. ### Create With Timestamps ```solidity params.timestamps.start = uint40(block.timestamp + 100 seconds); params.timestamps.end = uint40(block.timestamp + 52 weeks); ``` #### Sender The address streaming the tokens, with the ability to cancel the stream: ```solidity LockupDynamic.Segment[] memory segments = new LockupDynamic.Segment[](2); segments[0] = LockupDynamic.Segment({ amount: amount0, exponent: ud2x18(1e18), timestamp: uint40(block.timestamp + 4 weeks) }); segments[1] = (LockupDynamic.Segment({ amount: amount1, exponent: ud2x18(3.14e18), timestamp: uint40(block.timestamp + 52 weeks) })); ``` #### Recipient The address receiving the tokens: ```solidity streamId = LOCKUP.createWithTimestampsLD({ params: params, segments: segments }); } } ``` #### Deposit amount The deposit amount of ERC-20 tokens to be paid, denoted in units of the token's decimals. ```solidity // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.22; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ud2x18 } from "@prb/math/src/UD2x18.sol"; import { ISablierLockup } from "@sablier/lockup/src/interfaces/ISablierLockup.sol"; import { Lockup } from "@sablier/lockup/src/types/Lockup.sol"; import { LockupDynamic } from "@sablier/lockup/src/types/LockupDynamic.sol"; /// @notice Example of how to create a Lockup Dynamic stream. /// @dev This code is referenced in the docs: /// https://docs.sablier.com/guides/lockup/examples/create-stream/lockup-dynamic contract LockupDynamicStreamCreator { // Mainnet addresses IERC20 public constant DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); ISablierLockup public constant LOCKUP = ISablierLockup(0x93b37Bd5B6b278373217333Ac30D7E74c85fBDCB); /// @dev For this function to work, the sender must have approved this dummy contract to spend DAI. function createStream(uint128 amount0, uint128 amount1) public returns (uint256 streamId) { // Sum the segment amounts uint128 depositAmount = amount0 + amount1; // Transfer the provided amount of DAI tokens to this contract DAI.transferFrom(msg.sender, address(this), depositAmount); // Approve the Sablier contract to spend DAI DAI.approve(address(LOCKUP), depositAmount); // Declare the params struct Lockup.CreateWithTimestamps memory params; // Declare the function parameters params.sender = msg.sender; // The sender will be able to cancel the stream params.recipient = address(0xCAFE); // The recipient of the streamed tokens params.depositAmount = depositAmount; // The deposit amount into the stream params.token = DAI; // The streaming token params.cancelable = true; // Whether the stream will be cancelable or not params.transferable = true; // Whether the stream will be transferable or not params.timestamps.start = uint40(block.timestamp + 100 seconds); params.timestamps.end = uint40(block.timestamp + 52 weeks); // Declare some dummy segments LockupDynamic.Segment[] memory segments = new LockupDynamic.Segment[](2); segments[0] = LockupDynamic.Segment({ amount: amount0, exponent: ud2x18(1e18), timestamp: uint40(block.timestamp + 4 weeks) }); segments[1] = (LockupDynamic.Segment({ amount: amount1, exponent: ud2x18(3.14e18), timestamp: uint40(block.timestamp + 52 weeks) })); // Create the LockupDynamic stream streamId = LOCKUP.createWithTimestampsLD({ params: params, segments: segments }); } } ``` #### Token The contract address of the ERC-20 token used for streaming. In this example, we will stream DAI: ```solidity params.sender = msg.sender; // The sender will be able to cancel the stream params.recipient = address(0xCAFE); // The recipient of the streamed tokens params.depositAmount = depositAmount; // The deposit amount into the stream params.token = DAI; // The streaming token params.cancelable = true; // Whether the stream will be cancelable or not params.transferable = true; // Whether the stream will be transferable or not LockupLinear.UnlockAmounts memory unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, cliff: 0 }); LockupLinear.Durations memory durations = LockupLinear.Durations({ cliff: 0, // Setting a cliff of 0 total: 52 weeks // Setting a total duration of ~1 year }); // Create the LockupLinear stream using a function that sets the start time to `block.timestamp` streamId = LOCKUP.createWithDurationsLL({ params: params, unlockAmounts: unlockAmounts, granularity: 1 seconds, durations: durations }); } } ``` #### Cancelable Boolean that indicates whether the stream will be cancelable or not. ```solidity params.recipient = address(0xCAFE); // The recipient of the streamed tokens params.depositAmount = depositAmount; // The deposit amount into the stream params.token = DAI; // The streaming token params.cancelable = true; // Whether the stream will be cancelable or not params.transferable = true; // Whether the stream will be transferable or not LockupLinear.UnlockAmounts memory unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, cliff: 0 }); LockupLinear.Durations memory durations = LockupLinear.Durations({ cliff: 0, // Setting a cliff of 0 total: 52 weeks // Setting a total duration of ~1 year }); // Create the LockupLinear stream using a function that sets the start time to `block.timestamp` streamId = LOCKUP.createWithDurationsLL({ params: params, unlockAmounts: unlockAmounts, granularity: 1 seconds, durations: durations }); } } ``` #### Transferable Boolean that indicates whether the stream will be transferable or not. ```solidity params.depositAmount = depositAmount; // The deposit amount into the stream params.token = DAI; // The streaming token params.cancelable = true; // Whether the stream will be cancelable or not params.transferable = true; // Whether the stream will be transferable or not LockupLinear.UnlockAmounts memory unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, cliff: 0 }); LockupLinear.Durations memory durations = LockupLinear.Durations({ cliff: 0, // Setting a cliff of 0 total: 52 weeks // Setting a total duration of ~1 year }); // Create the LockupLinear stream using a function that sets the start time to `block.timestamp` streamId = LOCKUP.createWithDurationsLL({ params: params, unlockAmounts: unlockAmounts, granularity: 1 seconds, durations: durations }); } } ``` ### Start Time and End Time The start and end timestamps for the stream. Note that the end timestamps much match the timestamp of the last segment. ```solidity params.token = DAI; // The streaming token params.cancelable = true; // Whether the stream will be cancelable or not params.transferable = true; // Whether the stream will be transferable or not LockupLinear.UnlockAmounts memory unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, cliff: 0 }); LockupLinear.Durations memory durations = LockupLinear.Durations({ cliff: 0, // Setting a cliff of 0 total: 52 weeks // Setting a total duration of ~1 year }); // Create the LockupLinear stream using a function that sets the start time to `block.timestamp` streamId = LOCKUP.createWithDurationsLL({ params: params, unlockAmounts: unlockAmounts, granularity: 1 seconds, durations: durations }); } } ``` ### Segments Segments are what the protocol uses to compose the custom distribution curve of a Dynamic stream. For a full exposition of segments, see the [Segments](/concepts/lockup/segments) guide. The term "segment" refers to the splitting of the stream into separate partitions, with each segment characterized by a specific amount, exponent, and timestamp. These segments are supplied to the function in the form of an array containing [`LockupDynamic.Segment`](/reference/lockup/contracts/types/library.LockupDynamic#segment) structs. Let's define two dummy segments: ```solidity params.cancelable = true; // Whether the stream will be cancelable or not params.transferable = true; // Whether the stream will be transferable or not LockupLinear.UnlockAmounts memory unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, cliff: 0 }); LockupLinear.Durations memory durations = LockupLinear.Durations({ cliff: 0, // Setting a cliff of 0 total: 52 weeks // Setting a total duration of ~1 year }); // Create the LockupLinear stream using a function that sets the start time to `block.timestamp` streamId = LOCKUP.createWithDurationsLL({ params: params, unlockAmounts: unlockAmounts, granularity: 1 seconds, durations: durations }); } } ``` In this example, the first segment (`amount0`) will stream much faster than the second segment (`amount1`), because the exponents are different. As a rule of thumb: the higher the exponent, the slower the stream. :::note The segment timestamp must be in ascending order. ::: :::info The `ud2x18` function wraps a basic integer to the `UD2x18` value type, which is part of the [PRBMath](https://github.com/PaulRBerg/prb-math) library. ::: ## Invoke the create function With all parameters set, we can now call the `createWithTimestampsLD` function, and assign the id of the newly created stream to a variable: ```solidity params.transferable = true; // Whether the stream will be transferable or not LockupLinear.UnlockAmounts memory unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, cliff: 0 }); LockupLinear.Durations memory durations = LockupLinear.Durations({ cliff: 0, // Setting a cliff of 0 total: 52 weeks // Setting a total duration of ~1 year }); // Create the LockupLinear stream using a function that sets the start time to `block.timestamp` streamId = LOCKUP.createWithDurationsLL({ params: params, unlockAmounts: unlockAmounts, granularity: 1 seconds, durations: durations }); } } ``` ## Full code Below you can see the full code. You can also access the code on GitHub through [this link](https://github.com/sablier-labs/evm-monorepo/blob/main/misc/examples/lockup/LockupDynamicStreamCreator.sol). ```solidity // Transfer the provided amount of DAI tokens to this contract DAI.transferFrom(msg.sender, address(this), depositAmount); // Approve the Sablier contract to spend DAI DAI.approve(address(LOCKUP), depositAmount); ``` --- ## Create a Lockup Linear Stream Source: https://docs.sablier.com/guides/lockup/examples/create-stream/lockup-linear # Create a Lockup Linear Stream Linear streams are streams with a linear streaming function. In this guide, we will show you how to create a Lockup Linear stream using Solidity. This guide assumes that you have already gone through the [Protocol Concepts](/concepts/streaming) section. :::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: ```solidity // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.22; ``` Import the relevant symbols from `@sablier/lockup`: ```solidity import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ISablierLockup } from "@sablier/lockup/src/interfaces/ISablierLockup.sol"; import { Lockup } from "@sablier/lockup/src/types/Lockup.sol"; import { LockupLinear } from "@sablier/lockup/src/types/LockupLinear.sol"; ``` Create a contract called `LockupLinearStreamCreator`, and declare a constant `DAI` of type `IERC20` and a constant `LOCKUP` of type `ISablierLockup`: ```solidity // Mainnet addresses IERC20 public constant DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); ISablierLockup public constant LOCKUP = ISablierLockup(0x93b37Bd5B6b278373217333Ac30D7E74c85fBDCB); ``` 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 Ethereum Sepolia. If you need to work with a different chain, Lockup addresses can be obtained from the [Lockup Deployments](/guides/lockup/deployments) page. There are two create functions in the Lockup contract that can be used to create Linear streams: - `createWithDurationsLL`: takes duration and calculates the start and end timestamps based on the provided durations. - `createWithTimestampsLL`: takes start and end timestamps. Which one you choose depends upon your use case. In this guide, we will use `createWithDurationsLL`. ## Function definition Define a function called `createStream` which takes a single parameter `depositAmount`, and which returns the id of the created stream: ```solidity function createStream(uint128 depositAmount) public returns (uint256 streamId) { // Transfer the provided amount of DAI tokens to this contract DAI.transferFrom(msg.sender, address(this), depositAmount); // Approve the Sablier contract to spend DAI DAI.approve(address(LOCKUP), depositAmount); // Declare the params struct Lockup.CreateWithDurations memory params; // Declare the function parameters params.sender = msg.sender; // The sender will be able to cancel the stream params.recipient = address(0xCAFE); // The recipient of the streamed tokens params.depositAmount = depositAmount; // The deposit amount into the stream params.token = DAI; // The streaming token params.cancelable = true; // Whether the stream will be cancelable or not params.transferable = true; // Whether the stream will be transferable or not LockupLinear.UnlockAmounts memory unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, cliff: 0 }); LockupLinear.Durations memory durations = LockupLinear.Durations({ cliff: 0, // Setting a cliff of 0 total: 52 weeks // Setting a total duration of ~1 year }); // Create the LockupLinear stream using a function that sets the start time to `block.timestamp` streamId = LOCKUP.createWithDurationsLL({ params: params, unlockAmounts: unlockAmounts, granularity: 1 seconds, durations: durations }); } } ``` ## 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 approve the Lockup contract to pull the tokens that the creator contract will be in possession of after they are transferred from the calling address (you): ```solidity Lockup.CreateWithDurations memory params; // Declare the function parameters params.sender = msg.sender; // The sender will be able to cancel the stream params.recipient = address(0xCAFE); // The recipient of the streamed tokens params.depositAmount = depositAmount; // The deposit amount into the stream params.token = DAI; // The streaming token params.cancelable = true; // Whether the stream will be cancelable or not params.transferable = true; // Whether the stream will be transferable or not LockupLinear.UnlockAmounts memory unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, cliff: 0 }); LockupLinear.Durations memory durations = LockupLinear.Durations({ cliff: 0, // Setting a cliff of 0 total: 52 weeks // Setting a total duration of ~1 year }); // Create the LockupLinear stream using a function that sets the start time to `block.timestamp` streamId = LOCKUP.createWithDurationsLL({ params: params, unlockAmounts: unlockAmounts, granularity: 1 seconds, durations: durations }); } } ``` For more guidance on how to approve and transfer ERC-20 tokens, see [this article](https://ethereum.org/en/developers/docs/standards/tokens/erc-20/) on the Ethereum website. ## Parameters The parameters for [`createWithDurationsLL`](/reference/lockup/contracts/interfaces/interface.ISablierLockupLinear#createwithdurationsll) include [`Lockup.CreateWithDurations`](/reference/lockup/contracts/types/library.Lockup#createwithdurations) (a shared struct across all the lockup streams), [`LockupLinear.UnlockAmounts`](/reference/lockup/contracts/types/library.LockupLinear#unlockamounts), a `uint40 granularity`, and [`LockupLinear.Durations`](/reference/lockup/contracts/types/library.LockupLinear#durations). Let's review each parameter in detail. ### Create With Durations ```solidity LockupLinear.UnlockAmounts memory unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, cliff: 0 }); LockupLinear.Durations memory durations = LockupLinear.Durations({ cliff: 0, // Setting a cliff of 0 total: 52 weeks // Setting a total duration of ~1 year }); // Create the LockupLinear stream using a function that sets the start time to `block.timestamp` streamId = LOCKUP.createWithDurationsLL({ params: params, unlockAmounts: unlockAmounts, granularity: 1 seconds, durations: durations }); } } ``` #### Sender The address streaming the tokens, with the ability to cancel the stream: ```solidity granularity: 1 seconds, durations: durations }); } } ``` #### Recipient The address receiving the tokens: ```solidity LockupLinear.Durations memory durations = LockupLinear.Durations({ cliff: 0, // Setting a cliff of 0 total: 52 weeks // Setting a total duration of ~1 year }); ``` #### Deposit amount The deposit amount of ERC-20 tokens to be paid, denoted in units of the token's decimals. ```solidity streamId = LOCKUP.createWithDurationsLL({ params: params, unlockAmounts: unlockAmounts, granularity: 1 seconds, durations: durations }); ``` #### Token The contract address of the ERC-20 token used for streaming. In this example, we will stream DAI: ```solidity // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.22; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ISablierLockup } from "@sablier/lockup/src/interfaces/ISablierLockup.sol"; import { Lockup } from "@sablier/lockup/src/types/Lockup.sol"; import { LockupLinear } from "@sablier/lockup/src/types/LockupLinear.sol"; /// @notice Example of how to create a Lockup Linear stream. /// @dev This code is referenced in the docs: /// https://docs.sablier.com/guides/lockup/examples/create-stream/lockup-linear contract LockupLinearStreamCreator { // Mainnet addresses IERC20 public constant DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); ISablierLockup public constant LOCKUP = ISablierLockup(0x93b37Bd5B6b278373217333Ac30D7E74c85fBDCB); /// @dev For this function to work, the sender must have approved this dummy contract to spend DAI. function createStream(uint128 depositAmount) public returns (uint256 streamId) { // Transfer the provided amount of DAI tokens to this contract DAI.transferFrom(msg.sender, address(this), depositAmount); // Approve the Sablier contract to spend DAI DAI.approve(address(LOCKUP), depositAmount); // Declare the params struct Lockup.CreateWithDurations memory params; // Declare the function parameters params.sender = msg.sender; // The sender will be able to cancel the stream params.recipient = address(0xCAFE); // The recipient of the streamed tokens params.depositAmount = depositAmount; // The deposit amount into the stream params.token = DAI; // The streaming token params.cancelable = true; // Whether the stream will be cancelable or not params.transferable = true; // Whether the stream will be transferable or not LockupLinear.UnlockAmounts memory unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, cliff: 0 }); LockupLinear.Durations memory durations = LockupLinear.Durations({ cliff: 0, // Setting a cliff of 0 total: 52 weeks // Setting a total duration of ~1 year }); // Create the LockupLinear stream using a function that sets the start time to `block.timestamp` streamId = LOCKUP.createWithDurationsLL({ params: params, unlockAmounts: unlockAmounts, granularity: 1 seconds, durations: durations }); } } ``` #### Cancelable Boolean that indicates whether the stream will be cancelable or not. ```solidity params.sender = msg.sender; // The sender will be able to cancel the stream params.recipient = address(0xCAFE); // The recipient of the streamed tokens params.depositAmount = depositAmount; // The deposit amount into the stream params.token = DAI; // The streaming token params.cancelable = true; // Whether the stream will be cancelable or not params.transferable = true; // Whether the stream will be transferable or not LockupLinear.UnlockAmounts memory unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, cliff: 0 }); LockupLinear.Durations memory durations = LockupLinear.Durations({ cliff: 0, // Setting a cliff of 0 total: 52 weeks // Setting a total duration of ~1 year }); // Create the LockupLinear stream using a function that sets the start time to `block.timestamp` streamId = LOCKUP.createWithDurationsLL({ params: params, unlockAmounts: unlockAmounts, granularity: 1 seconds, durations: durations }); } } ``` #### Transferable Boolean that indicates whether the stream will be transferable or not. ```solidity params.recipient = address(0xCAFE); // The recipient of the streamed tokens params.depositAmount = depositAmount; // The deposit amount into the stream params.token = DAI; // The streaming token params.cancelable = true; // Whether the stream will be cancelable or not params.transferable = true; // Whether the stream will be transferable or not LockupLinear.UnlockAmounts memory unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, cliff: 0 }); LockupLinear.Durations memory durations = LockupLinear.Durations({ cliff: 0, // Setting a cliff of 0 total: 52 weeks // Setting a total duration of ~1 year }); // Create the LockupLinear stream using a function that sets the start time to `block.timestamp` streamId = LOCKUP.createWithDurationsLL({ params: params, unlockAmounts: unlockAmounts, granularity: 1 seconds, durations: durations }); } } ``` ### Unlock Amounts Struct containing details on unlock amounts at start time and at cliff time. ```solidity params.depositAmount = depositAmount; // The deposit amount into the stream params.token = DAI; // The streaming token params.cancelable = true; // Whether the stream will be cancelable or not params.transferable = true; // Whether the stream will be transferable or not LockupLinear.UnlockAmounts memory unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, cliff: 0 }); LockupLinear.Durations memory durations = LockupLinear.Durations({ cliff: 0, // Setting a cliff of 0 total: 52 weeks // Setting a total duration of ~1 year }); // Create the LockupLinear stream using a function that sets the start time to `block.timestamp` streamId = LOCKUP.createWithDurationsLL({ params: params, unlockAmounts: unlockAmounts, granularity: 1 seconds, durations: durations }); } } ``` ### Granularity The smallest step in time (in seconds) between two consecutive token unlocks. A value of zero is a sentinel for 1 second. For standard linear streaming behavior, use `1 seconds`. For periodic unlock patterns (e.g., weekly), use the period duration (e.g., `1 weeks`). ```solidity params.token = DAI; // The streaming token params.cancelable = true; // Whether the stream will be cancelable or not params.transferable = true; // Whether the stream will be transferable or not LockupLinear.UnlockAmounts memory unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, cliff: 0 }); LockupLinear.Durations memory durations = LockupLinear.Durations({ cliff: 0, // Setting a cliff of 0 total: 52 weeks // Setting a total duration of ~1 year }); // Create the LockupLinear stream using a function that sets the start time to `block.timestamp` streamId = LOCKUP.createWithDurationsLL({ params: params, unlockAmounts: unlockAmounts, granularity: 1 seconds, durations: durations }); } } ``` ### Durations Struct containing (i) cliff duration and (ii) total stream duration, both denoted in seconds. ```solidity params.cancelable = true; // Whether the stream will be cancelable or not params.transferable = true; // Whether the stream will be transferable or not LockupLinear.UnlockAmounts memory unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, cliff: 0 }); LockupLinear.Durations memory durations = LockupLinear.Durations({ cliff: 0, // Setting a cliff of 0 total: 52 weeks // Setting a total duration of ~1 year }); // Create the LockupLinear stream using a function that sets the start time to `block.timestamp` streamId = LOCKUP.createWithDurationsLL({ params: params, unlockAmounts: unlockAmounts, granularity: 1 seconds, durations: durations }); } } ``` ## Invoke the create function With all parameters set, we can now call the `createWithDurationsLL` function, and assign the id of the newly created stream to a variable: ```solidity params.transferable = true; // Whether the stream will be transferable or not LockupLinear.UnlockAmounts memory unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, cliff: 0 }); LockupLinear.Durations memory durations = LockupLinear.Durations({ cliff: 0, // Setting a cliff of 0 total: 52 weeks // Setting a total duration of ~1 year }); // Create the LockupLinear stream using a function that sets the start time to `block.timestamp` streamId = LOCKUP.createWithDurationsLL({ params: params, unlockAmounts: unlockAmounts, granularity: 1 seconds, durations: durations }); } } ``` ## Full code Below you can see the full code. You can also access the code on GitHub through [this link](https://github.com/sablier-labs/evm-monorepo/blob/main/misc/examples/lockup/LockupLinearStreamCreator.sol). ```solidity // Transfer the provided amount of DAI tokens to this contract DAI.transferFrom(msg.sender, address(this), depositAmount); // Approve the Sablier contract to spend DAI DAI.approve(address(LOCKUP), depositAmount); ``` --- ## Create a Lockup Tranched Stream Source: https://docs.sablier.com/guides/lockup/examples/create-stream/lockup-tranched # Create a Lockup Tranched Stream Lockup Tranched are streams with discrete unlocks. In this guide, we will show you how to create a Lockup Tranched stream using Solidity. This guide assumes that you have already gone through the [Protocol Concepts](/concepts/streaming) section. :::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: ```solidity // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.22; ``` Import the relevant symbols from `@sablier/lockup`: ```solidity import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ISablierLockup } from "@sablier/lockup/src/interfaces/ISablierLockup.sol"; import { Lockup } from "@sablier/lockup/src/types/Lockup.sol"; import { LockupTranched } from "@sablier/lockup/src/types/LockupTranched.sol"; ``` Create a contract called `LockupTranchedStreamCreator`, and declare a constant `DAI` of type `IERC20` and a constant `LOCKUP` of type `ISablierLockup`: ```solidity // Mainnet addresses IERC20 public constant DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); ISablierLockup public constant LOCKUP = ISablierLockup(0x93b37Bd5B6b278373217333Ac30D7E74c85fBDCB); ``` 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 Ethereum Sepolia. If you need to work with a different chain, Lockup addresses can be obtained from the [Lockup Deployments](/guides/lockup/deployments) page. There are two create functions in the Lockup contract that can be used to create Tranched streams: - `createWithDurationsLT`: takes duration and calculates the tranche timestamps based on the provided durations. - `createWithTimestampsLT`: takes UNIX timestamps for tranches. Which one you choose depends upon your use case. In this guide, we will use `createWithDurationsLT`. ## Function definition Define a function called `createStream` which takes two parameters, `amount0` and `amount1`, and which returns the id of the created stream: ```solidity function createStream(uint128 amount0, uint128 amount1) public returns (uint256 streamId) { // Sum the tranche amounts uint128 depositAmount = amount0 + amount1; // Transfer the provided amount of DAI tokens to this contract DAI.transferFrom(msg.sender, address(this), depositAmount); // Approve the Sablier contract to spend DAI DAI.approve(address(LOCKUP), depositAmount); // Declare the params struct Lockup.CreateWithDurations memory params; // Declare the function parameters params.sender = msg.sender; // The sender will be able to cancel the stream params.recipient = address(0xCAFE); // The recipient of the streamed tokens params.depositAmount = depositAmount; // The deposit amount into the stream params.token = DAI; // The streaming token params.cancelable = true; // Whether the stream will be cancelable or not params.transferable = true; // Whether the stream will be transferable or not // Declare some dummy tranches LockupTranched.TrancheWithDuration[] memory tranches = new LockupTranched.TrancheWithDuration[](2); tranches[0] = LockupTranched.TrancheWithDuration({ amount: amount0, duration: uint40(4 weeks) }); tranches[1] = (LockupTranched.TrancheWithDuration({ amount: amount1, duration: uint40(6 weeks) })); // Create the LockupTranched stream streamId = LOCKUP.createWithDurationsLT({ params: params, tranchesWithDuration: tranches }); } } ``` Next, sum up the `amount0` and `amount1` parameters to get the deposit amount of the stream, which will be needed in many of the steps below: ```solidity uint128 depositAmount = amount0 + amount1; // Transfer the provided amount of DAI tokens to this contract DAI.transferFrom(msg.sender, address(this), depositAmount); // Approve the Sablier contract to spend DAI DAI.approve(address(LOCKUP), depositAmount); // Declare the params struct Lockup.CreateWithDurations memory params; // Declare the function parameters params.sender = msg.sender; // The sender will be able to cancel the stream params.recipient = address(0xCAFE); // The recipient of the streamed tokens params.depositAmount = depositAmount; // The deposit amount into the stream params.token = DAI; // The streaming token params.cancelable = true; // Whether the stream will be cancelable or not params.transferable = true; // Whether the stream will be transferable or not // Declare some dummy tranches LockupTranched.TrancheWithDuration[] memory tranches = new LockupTranched.TrancheWithDuration[](2); tranches[0] = LockupTranched.TrancheWithDuration({ amount: amount0, duration: uint40(4 weeks) }); tranches[1] = (LockupTranched.TrancheWithDuration({ amount: amount1, duration: uint40(6 weeks) })); // Create the LockupTranched stream streamId = LOCKUP.createWithDurationsLT({ params: params, tranchesWithDuration: tranches }); } } ``` ## 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 approve the Lockup contract to pull the tokens that the creator contract will be in possession of after they are transferred from the calling address (you): ```solidity Lockup.CreateWithDurations memory params; // Declare the function parameters params.sender = msg.sender; // The sender will be able to cancel the stream params.recipient = address(0xCAFE); // The recipient of the streamed tokens params.depositAmount = depositAmount; // The deposit amount into the stream params.token = DAI; // The streaming token params.cancelable = true; // Whether the stream will be cancelable or not params.transferable = true; // Whether the stream will be transferable or not // Declare some dummy tranches LockupTranched.TrancheWithDuration[] memory tranches = new LockupTranched.TrancheWithDuration[](2); tranches[0] = LockupTranched.TrancheWithDuration({ amount: amount0, duration: uint40(4 weeks) }); tranches[1] = (LockupTranched.TrancheWithDuration({ amount: amount1, duration: uint40(6 weeks) })); // Create the LockupTranched stream streamId = LOCKUP.createWithDurationsLT({ params: params, tranchesWithDuration: tranches }); } } ``` For more guidance on how to approve and transfer ERC-20 tokens, see [this article](https://ethereum.org/en/developers/docs/standards/tokens/erc-20/) on the Ethereum website. ## Parameters The struct associated with [`createWithDurationsLT`](/reference/lockup/contracts/interfaces/interface.ISablierLockupTranched#createwithdurationslt) are [`Lockup.CreateWithDurations`](/reference/lockup/contracts/types/library.Lockup#createwithdurations) (a shared struct across all the lockup streams) and [`LockupTranched.TrancheWithDuration`](/reference/lockup/contracts/types/library.LockupTranched#tranchewithduration). Let's review each parameter in detail. ### Create With Durations ```solidity LockupTranched.TrancheWithDuration[] memory tranches = new LockupTranched.TrancheWithDuration[](2); tranches[0] = LockupTranched.TrancheWithDuration({ amount: amount0, duration: uint40(4 weeks) }); tranches[1] = (LockupTranched.TrancheWithDuration({ amount: amount1, duration: uint40(6 weeks) })); ``` #### Sender The address streaming the tokens, with the ability to cancel the stream: ```solidity streamId = LOCKUP.createWithDurationsLT({ params: params, tranchesWithDuration: tranches }); } } ``` #### Recipient The address receiving the tokens: ```solidity // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.22; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ISablierLockup } from "@sablier/lockup/src/interfaces/ISablierLockup.sol"; import { Lockup } from "@sablier/lockup/src/types/Lockup.sol"; import { LockupTranched } from "@sablier/lockup/src/types/LockupTranched.sol"; /// @notice Example of how to create a Lockup Tranched stream. /// @dev This code is referenced in the docs: /// https://docs.sablier.com/guides/lockup/examples/create-stream/lockup-tranched contract LockupTranchedStreamCreator { // Mainnet addresses IERC20 public constant DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); ISablierLockup public constant LOCKUP = ISablierLockup(0x93b37Bd5B6b278373217333Ac30D7E74c85fBDCB); /// @dev For this function to work, the sender must have approved this dummy contract to spend DAI. function createStream(uint128 amount0, uint128 amount1) public returns (uint256 streamId) { // Sum the tranche amounts uint128 depositAmount = amount0 + amount1; // Transfer the provided amount of DAI tokens to this contract DAI.transferFrom(msg.sender, address(this), depositAmount); // Approve the Sablier contract to spend DAI DAI.approve(address(LOCKUP), depositAmount); // Declare the params struct Lockup.CreateWithDurations memory params; // Declare the function parameters params.sender = msg.sender; // The sender will be able to cancel the stream params.recipient = address(0xCAFE); // The recipient of the streamed tokens params.depositAmount = depositAmount; // The deposit amount into the stream params.token = DAI; // The streaming token params.cancelable = true; // Whether the stream will be cancelable or not params.transferable = true; // Whether the stream will be transferable or not // Declare some dummy tranches LockupTranched.TrancheWithDuration[] memory tranches = new LockupTranched.TrancheWithDuration[](2); tranches[0] = LockupTranched.TrancheWithDuration({ amount: amount0, duration: uint40(4 weeks) }); tranches[1] = (LockupTranched.TrancheWithDuration({ amount: amount1, duration: uint40(6 weeks) })); // Create the LockupTranched stream streamId = LOCKUP.createWithDurationsLT({ params: params, tranchesWithDuration: tranches }); } } ``` #### Deposit amount The deposit amount of ERC-20 tokens to be paid, denoted in units of the token's decimals. ```solidity params.sender = msg.sender; // The sender will be able to cancel the stream params.recipient = address(0xCAFE); // The recipient of the streamed tokens params.depositAmount = depositAmount; // The deposit amount into the stream params.token = DAI; // The streaming token params.cancelable = true; // Whether the stream will be cancelable or not params.transferable = true; // Whether the stream will be transferable or not LockupLinear.UnlockAmounts memory unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, cliff: 0 }); LockupLinear.Durations memory durations = LockupLinear.Durations({ cliff: 0, // Setting a cliff of 0 total: 52 weeks // Setting a total duration of ~1 year }); // Create the LockupLinear stream using a function that sets the start time to `block.timestamp` streamId = LOCKUP.createWithDurationsLL({ params: params, unlockAmounts: unlockAmounts, granularity: 1 seconds, durations: durations }); } } ``` #### Token The contract address of the ERC-20 token used for streaming. In this example, we will stream DAI: ```solidity params.recipient = address(0xCAFE); // The recipient of the streamed tokens params.depositAmount = depositAmount; // The deposit amount into the stream params.token = DAI; // The streaming token params.cancelable = true; // Whether the stream will be cancelable or not params.transferable = true; // Whether the stream will be transferable or not LockupLinear.UnlockAmounts memory unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, cliff: 0 }); LockupLinear.Durations memory durations = LockupLinear.Durations({ cliff: 0, // Setting a cliff of 0 total: 52 weeks // Setting a total duration of ~1 year }); // Create the LockupLinear stream using a function that sets the start time to `block.timestamp` streamId = LOCKUP.createWithDurationsLL({ params: params, unlockAmounts: unlockAmounts, granularity: 1 seconds, durations: durations }); } } ``` #### Cancelable Boolean that indicates whether the stream will be cancelable or not. ```solidity params.depositAmount = depositAmount; // The deposit amount into the stream params.token = DAI; // The streaming token params.cancelable = true; // Whether the stream will be cancelable or not params.transferable = true; // Whether the stream will be transferable or not LockupLinear.UnlockAmounts memory unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, cliff: 0 }); LockupLinear.Durations memory durations = LockupLinear.Durations({ cliff: 0, // Setting a cliff of 0 total: 52 weeks // Setting a total duration of ~1 year }); // Create the LockupLinear stream using a function that sets the start time to `block.timestamp` streamId = LOCKUP.createWithDurationsLL({ params: params, unlockAmounts: unlockAmounts, granularity: 1 seconds, durations: durations }); } } ``` #### Transferable Boolean that indicates whether the stream will be transferable or not. ```solidity params.token = DAI; // The streaming token params.cancelable = true; // Whether the stream will be cancelable or not params.transferable = true; // Whether the stream will be transferable or not LockupLinear.UnlockAmounts memory unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, cliff: 0 }); LockupLinear.Durations memory durations = LockupLinear.Durations({ cliff: 0, // Setting a cliff of 0 total: 52 weeks // Setting a total duration of ~1 year }); // Create the LockupLinear stream using a function that sets the start time to `block.timestamp` streamId = LOCKUP.createWithDurationsLL({ params: params, unlockAmounts: unlockAmounts, granularity: 1 seconds, durations: durations }); } } ``` ### Tranches With Duration Tranches are what the protocol uses to compose the discrete unlocks. For a full exposition of tranches, see the [Tranches](/concepts/lockup/tranches) guide. Each tranche is characterized by a specific amount and timestamp. Because we are using `createWithDurationsLT` in this example, these tranches are supplied to the function in the form of an array containing [`LockupTranched.TrancheWithDuration`](/reference/lockup/contracts/types/library.LockupTranched#tranchewithduration) structs. Let's define two dummy tranches: ```solidity params.cancelable = true; // Whether the stream will be cancelable or not params.transferable = true; // Whether the stream will be transferable or not LockupLinear.UnlockAmounts memory unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, cliff: 0 }); LockupLinear.Durations memory durations = LockupLinear.Durations({ cliff: 0, // Setting a cliff of 0 total: 52 weeks // Setting a total duration of ~1 year }); // Create the LockupLinear stream using a function that sets the start time to `block.timestamp` streamId = LOCKUP.createWithDurationsLL({ params: params, unlockAmounts: unlockAmounts, granularity: 1 seconds, durations: durations }); } } ``` In this example, the first tranche (`amount0`) will unlock at the end of the 4 weeks after the stream was created and the second tranche (`amount1`) will unlock after further 6 weeks. Thus, the deposit amount will be unlocked in 10 weeks. ## Invoke the create function With all parameters set, we can now call the `createWithDurationsLT` function, and assign the ID of the newly created stream to a variable: ```solidity params.transferable = true; // Whether the stream will be transferable or not LockupLinear.UnlockAmounts memory unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, cliff: 0 }); LockupLinear.Durations memory durations = LockupLinear.Durations({ cliff: 0, // Setting a cliff of 0 total: 52 weeks // Setting a total duration of ~1 year }); // Create the LockupLinear stream using a function that sets the start time to `block.timestamp` streamId = LOCKUP.createWithDurationsLL({ params: params, unlockAmounts: unlockAmounts, granularity: 1 seconds, durations: durations }); } } ``` ## Full code Below you can see the full code. You can also access the code on GitHub through [this link](https://github.com/sablier-labs/evm-monorepo/blob/main/misc/examples/lockup/LockupTranchedStreamCreator.sol). ```solidity // Transfer the provided amount of DAI tokens to this contract DAI.transferFrom(msg.sender, address(this), depositAmount); // Approve the Sablier contract to spend DAI DAI.approve(address(LOCKUP), depositAmount); ``` --- ## Implement Hooks Source: https://docs.sablier.com/guides/lockup/examples/hooks # Implement Hooks Hooks provide an interface for recipient contracts to react upon cancellations and withdrawals. In order to allow your contract to be able to hook into Lockup, you must implement this interface and it must have been allowlisted by the Lockup contract's admin. :::info [`allowToHook`](/reference/lockup/contracts/interfaces/interface.ISablierLockup#allowtohook) is an irreversible operation, i.e., once a contract has been added to the allowlist, it can never be removed. This is to ensure stronger immutability and decentralization guarantees. Once a recipient contract is allowlisted, integrators should NOT have to trust us to keep their contract on the allowlist. ::: In this guide, we will explain how to implement [hooks](/concepts/lockup/hooks) in your smart contract to allow interacting with Lockup streams. ### Overview ### Requirements The recipient contract should implement the `{IERC165-supportsInterface}` method, which MUST return `true` when called with `0xf8ee98d3`, which is the interface ID for `ISablierLockupRecipient`. ```solidity function supportsInterface(bytes4 interfaceId) public pure override(IERC165) returns (bool) { return interfaceId == 0xf8ee98d3; } ``` ### Hook Functions These are the hooks that can be implemented by a recipient contract: | Hook | Arguments | Return value | Description | | --- | --- | --- | --- | | `onSablierLockupCancel` | `(streamId, sender, senderAmount, recipientAmount)` | function selector | Called when the stream is canceled by the sender. | | `onSablierLockupWithdraw` | `(streamId, caller, to, amount)` | function selector | Called when an amount is withdrawn from the stream. | The complete interface for `ISablierLockupRecipient` can be found [here](/reference/lockup/contracts/interfaces/interface.ISablierLockupRecipient). ### Sample Implementations #### Recipient ```solidity // SPDX-License-Identifier: UNLICENSED pragma solidity >=0.8.22; import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import { ISablierLockupRecipient } from "@sablier/lockup/src/interfaces/ISablierLockupRecipient.sol"; contract RecipientHooks is ISablierLockupRecipient { error CallerNotSablierContract(address caller, address sablierLockup); /// @dev The address of the lockup contract. It could be either LockupLinear, LockupDynamic or LockupTranched /// depending on which type of streams are supported in this hook. address public immutable SABLIER_LOCKUP; mapping(address account => uint256 amount) internal _balances; /// @dev Constructor will set the address of the lockup contract. constructor(address sablierLockup_) { SABLIER_LOCKUP = sablierLockup_; } // {IERC165-supportsInterface} implementation as required by `ISablierLockupRecipient` interface. function supportsInterface(bytes4 interfaceId) public pure override(IERC165) returns (bool) { return interfaceId == 0xf8ee98d3; } // This will be called by Sablier contract when a stream is canceled by the sender. function onSablierLockupCancel( uint256 streamId, address sender, uint128 senderAmount, uint128 recipientAmount ) external view returns (bytes4 selector) { // Check: the caller is the lockup contract. if (msg.sender != SABLIER_LOCKUP) { revert CallerNotSablierContract(msg.sender, SABLIER_LOCKUP); } // Unstake the user's NFT. _unstake({ nftId: streamId }); // Update data. _updateData(streamId, sender, senderAmount, recipientAmount); return ISablierLockupRecipient.onSablierLockupCancel.selector; } // This will be called by Sablier contract when withdraw is called on a stream. function onSablierLockupWithdraw( uint256 streamId, address caller, address to, uint128 amount ) external view returns (bytes4 selector) { // Check: the caller is the lockup contract. if (msg.sender != SABLIER_LOCKUP) { revert CallerNotSablierContract(msg.sender, SABLIER_LOCKUP); } // Transfer the withdrawn amount to the original user. _transfer(to, amount); // Update data. _updateData(streamId, caller, amount, 0); return ISablierLockupRecipient.onSablierLockupWithdraw.selector; } function _unstake(uint256 nftId) internal pure { } function _updateData( uint256 streamId, address sender, uint128 senderAmount, uint128 recipientAmount ) internal pure { } function _transfer(address to, uint128 amount) internal pure { } } ``` --- ## Configure Your Local Environment Source: https://docs.sablier.com/guides/lockup/examples/local-environment # Configure Your Local Environment In this guide, we will go through the steps to set up a local development environment for building onchain integrations with Lockup. We will use Foundry to install Lockup as a dependency, and run a simple test. At the end, you’ll have a development environment set up that you can use to build the rest of the examples under "Guides", or start your own integration project. ## Pre-requisites You will need the following software on your machine: - [Git](https://git-scm.com/downloads) - [Foundry](https://github.com/foundry-rs/foundry) - [Node.js](https://nodejs.org/en/download) - [Bun](https://bun.sh) In addition, familiarity with [Ethereum](https://ethereum.org/) and [Solidity](https://soliditylang.org/) is requisite. ## Set up using integration template :::tip Make sure you are using the latest version of Foundry by running `foundryup`. ::: We put together a template repository that you can use to get started quickly. This repository features a basic project structure, pre-configured Lockup imports, and a selection of sample contracts and tests. To install the template, simply execute the following commands: ```bash $ mkdir lockup-integration-template $ cd lockup-integration-template $ forge init --template sablier-labs/lockup-integration-template $ bun install ``` Then, hop to the [Run a Fork Test](#run-a-fork-test) section to complete your set up and start developing. ## Set up using Foundry template Foundry is a popular development toolkit for Ethereum projects, which we have used to build the Lockup Protocol. For the purposes of this guide, Foundry will provide us with the tooling needed to compile and test our contracts. Let's use this command to spin up a new Foundry project: ```bash $ forge init my-project $ cd my-project ``` Once the initialization completes, take a look around at what got set up: ```bash ├── foundry.toml ├── script ├── src └── test ``` The folder structure should be intuitive: - `src` is where you'll write Solidity contracts - `test` is where you'll write tests (also in Solidity) - `script` is where you'll write scripts to perform actions like deploying contracts (you guessed it, in Solidity) - `foundry.toml` is where you can configure your Foundry settings, which we will leave as is in this guide :::note You might notice that the CLI is `forge` rather than `foundry`. This is because Foundry is a toolkit, and `forge` is just one of the tools that comes with it. ::: ## Install via npm package Let's install the Lockup Node.js packages using Bun: ```bash $ bun add @sablier/lockup ``` Bun will download the Lockup contracts, along with their dependencies, and put them in the `node_modules` directory. That's it! You should now have a functional development environment to start building onchain Lockup integrations. Let's run a quick test to confirm everything is set up properly. ## Write your first contract Delete the `src/Counter.sol` and `test/Counter.t.sol` files generated by Forge, and create two new files: `src/StreamCreator.sol` and `test/StreamCreator.t.sol`. Paste the following code into `src/StreamCreator.sol`: ```solidity // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.22; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ISablierLockup } from "@sablier/lockup/src/interfaces/ISablierLockup.sol"; import { Lockup } from "@sablier/lockup/src/types/Lockup.sol"; import { LockupLinear } from "@sablier/lockup/src/types/LockupLinear.sol"; /// @title LockupStreamCreator /// @dev This contract allows users to create Sablier lockup streams using the Lockup contract. contract LockupStreamCreator { // Mainnet addresses IERC20 public constant DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); ISablierLockup public constant LOCKUP = ISablierLockup(0x93b37Bd5B6b278373217333Ac30D7E74c85fBDCB); /// @dev Before calling this function, the user must first approve this contract to spend the tokens from the user's /// address. function createLinearStream() public returns (uint256 streamId) { // Declare the total amount as 100 DAI uint128 totalAmount = 100e18; // Transfer the provided amount of DAI tokens to this contract DAI.transferFrom(msg.sender, address(this), totalAmount); // Approve the Sablier contract to spend DAI DAI.approve(address(LOCKUP), totalAmount); // Declare the params struct Lockup.CreateWithDurations memory params; // Declare the function parameters params.sender = msg.sender; // The sender will be able to cancel the stream params.recipient = address(0xCAFE); // The recipient of the streamed tokens params.depositAmount = totalAmount; // Total amount is the amount inclusive of all fees params.token = DAI; // The streaming token params.cancelable = true; // Whether the stream will be cancelable or not params.transferable = true; // Whether the stream will be transferable or not LockupLinear.UnlockAmounts memory unlockAmounts = LockupLinear.UnlockAmounts({ start: 0, cliff: 0 }); LockupLinear.Durations memory durations = LockupLinear.Durations({ cliff: 0, // Setting a cliff of 0 total: 100 days // Setting a total duration of 100 days }); // Create the Lockup stream with Linear shape, no cliff and start time as `block.timestamp` streamId = LOCKUP.createWithDurationsLL({ params: params, unlockAmounts: unlockAmounts, durations: durations, granularity: 1 seconds }); } } ``` Let's use Forge to compile this contract: ```bash $ forge build ``` If the contract was compiled correctly, you should see this message: ```bash [⠢] Compiling... [⠰] Compiling 62 files with Solc 0.8.29 [⠒] Solc 0.8.29 finished in 798.47ms Compiler run successful! ``` :::info The minimum Solidity version supported by the Lockup contracts is v0.8.22. ::: ## Run a fork test Foundry offers native support for running tests against a fork of Ethereum Mainnet, testnets and L2s, which is useful when building and testing integrations with onchain protocols like Sablier. In practice, this enables you to access all Sablier contracts deployed on Ethereum, and use them for testing your integration. As a prerequisite, you will need an RPC that supports forking. A good solution for this is [Alchemy](https://alchemy.com/), as it includes forking in its free tier plan. Once you have obtained your RPC, you can proceed to run the following test: ```solidity // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.22; import { Test } from "forge-std/src/Test.sol"; import { LockupStreamCreator } from "../src/LockupStreamCreator.sol"; contract LockupStreamCreatorTest is Test { // Test contracts LockupStreamCreator internal creator; address internal user; function setUp() public { // Fork Ethereum Mainnet at the latest block vm.createSelectFork("mainnet"); // Deploy the stream creator contract creator = new LockupStreamCreator(); // Create a test user user = payable(makeAddr("User")); vm.deal({ account: user, newBalance: 1 ether }); // Mint some DAI tokens to the test user, which will be pulled by the creator contract. Make sure its more than // `params.totalAmount`. deal({ token: address(creator.DAI()), to: user, give: 1337e18 }); // Make the test user the `msg.sender` in all following calls vm.startPrank({ msgSender: user }); // Approve the creator contract to pull DAI tokens from the test user creator.DAI().approve({ spender: address(creator), value: 1337e18 }); } function test_CreateLinearStream() public { uint256 expectedStreamId = creator.LOCKUP().nextStreamId(); uint256 actualStreamId = creator.createLinearStream(); // Check that creating linear stream works by checking the stream id assertEq(actualStreamId, expectedStreamId); } } ``` You can run the test using Forge: ```bash $ forge test ``` If the test passed, you should see a message like this: ```bash Ran 2 tests for test/LockupStreamCreator.t.sol:LockupStreamCreatorTest [PASS] test_CreateLockupStream() (gas: 246830) Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 626.58ms (500.67µs CPU time) ``` ## Next steps Congratulations! Your environment is now configured, and you are prepared to start building. Explore the guides section to discover various features available for Lockup integration. Remember to include all contracts (`.sol` files) in the `src` folder and their corresponding tests in the `test` folder. As far as Foundry is concerned, there is much more to uncover. If you want to learn more about it, check out the [Foundry Book](https://book.getfoundry.sh/), which contains numerous examples and tutorials. A deep understanding of Foundry will enable you to create more sophisticated integrations with Lockup protocol. --- ## Cancel Streams Source: https://docs.sablier.com/guides/lockup/examples/stream-management/cancel # Cancel Streams :::note This section assumes that you have already gone through the [setup](/guides/lockup/examples/stream-management/setup) part. ::: :::tip See the [Access Control](/reference/lockup/access-control) guide for an overview of who is allowed to cancel streams. ::: Canceling streams involves stopping the flow of tokens before the stream's end time and refunding the remaining funds to the sender. However, the portion that has already been streamed is NOT automatically transferred - the recipient will need to withdraw it. There are two functions that can be used to cancel streams: 1. [`cancel`](/reference/lockup/contracts/contract.SablierLockup#cancel): cancels a single stream 2. [`cancelMultiple`](/reference/lockup/contracts/contract.SablierLockup#cancelmultiple): cancels multiple streams at once To call any of these functions, you need to have created a cancelable stream. If you don't have one yet, go back to the [previous guide](/guides/lockup/examples/create-stream/lockup-linear) and create a stream. Then, you can use the `cancel` function like this: ```solidity function cancel(uint256 streamId) external { sablier.cancel(streamId); } ``` In addition to the `cancel` function, there is the `cancelMultiple` function, which allows you to cancel several streams at once: ```solidity function cancelMultiple(uint256[] calldata streamIds) external { sablier.cancelMultiple(streamIds); } ``` --- ## Renounce Streams Source: https://docs.sablier.com/guides/lockup/examples/stream-management/renounce # Renounce Streams :::note This section assumes that you have already gone through the [setup](/guides/lockup/examples/stream-management/setup) part. ::: Renouncing a stream means that the sender of the stream will no longer be able to cancel it. This is useful if the sender wants to give up control of the stream. To renounce a stream, you can use [`renounce`](/reference/lockup/contracts/contract.SablierLockup#renounce). Before invoking this function, ensure that you have an active, cancelable stream with the sender set to the `StreamManagement` contract. Once the stream is created, you can use the `renounce` function like this: ```solidity function renounce(uint256 streamId) external { sablier.renounce(streamId); } ``` --- ## Set Up Your Contract Source: https://docs.sablier.com/guides/lockup/examples/stream-management/setup # Set Up Your Contract The "Stream Management" series will guide you through how to withdraw, cancel, renounce, and transfer ownership of streams. Before diving in, please note the following: 1. We assume you are already familiar with [creating streams](/guides/lockup/examples/create-stream/lockup-linear). 2. We also assume that the stream management contract is authorized to invoke each respective function. To learn more about access control in Lockup, see the [Access Control](/reference/lockup/access-control) guide. With that said, let's begin. First, declare the Solidity version used to compile the contract: ```solidity // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.22; ``` Import the relevant symbols from `@sablier/core`: ```solidity import { ISablierLockup } from "@sablier/lockup/src/interfaces/ISablierLockup.sol"; /// @notice Examples of how to manage Sablier streams after they have been created. /// @dev This code is referenced in the docs: https://docs.sablier.com/guides/lockup/examples/stream-management/setup contract StreamManagement { ISablierLockup public immutable sablier; constructor(ISablierLockup sablier_) { sablier = sablier_; } /*////////////////////////////////////////////////////////////////////////// 02-WITHDRAW //////////////////////////////////////////////////////////////////////////*/ // This function can be called by the sender, recipient, or an approved NFT operator function withdraw(uint256 streamId) external payable { uint256 fee = sablier.calculateMinFeeWei(streamId); sablier.withdraw{ value: fee }({ streamId: streamId, to: address(0xCAFE), amount: 1337e18 }); } // This function can be called by the sender, recipient, or an approved NFT operator function withdrawMax(uint256 streamId) external payable { uint256 fee = sablier.calculateMinFeeWei(streamId); sablier.withdrawMax{ value: fee }({ streamId: streamId, to: address(0xCAFE) }); } // This function can be called by either the recipient or an approved NFT operator function withdrawMultiple(uint256[] calldata streamIds, uint128[] calldata amounts) external payable { uint256 maxFeeRequired; // The fee required to call withdraw multiple is the maximum of the fees required to withdraw each stream. for (uint256 i = 0; i < streamIds.length; i++) { uint256 feeForStreamId = sablier.calculateMinFeeWei(streamIds[i]); if (feeForStreamId > maxFeeRequired) { maxFeeRequired = feeForStreamId; } } sablier.withdrawMultiple{ value: maxFeeRequired }({ streamIds: streamIds, amounts: amounts }); } /*////////////////////////////////////////////////////////////////////////// 03-CANCEL //////////////////////////////////////////////////////////////////////////*/ // This function can be called only by the sender function cancel(uint256 streamId) external { sablier.cancel(streamId); } // This function can be called only by the sender function cancelMultiple(uint256[] calldata streamIds) external { sablier.cancelMultiple(streamIds); } /*////////////////////////////////////////////////////////////////////////// 04-RENOUNCE //////////////////////////////////////////////////////////////////////////*/ // This function can be called only by the sender function renounce(uint256 streamId) external { sablier.renounce(streamId); } /*////////////////////////////////////////////////////////////////////////// 05-TRANSFER //////////////////////////////////////////////////////////////////////////*/ // This function can be called by either the recipient or an approved NFT operator function safeTransferFrom(uint256 streamId) external { sablier.safeTransferFrom({ from: address(this), to: address(0xCAFE), tokenId: streamId }); } // This function can be called by either the recipient or an approved NFT operator function transferFrom(uint256 streamId) external { sablier.transferFrom({ from: address(this), to: address(0xCAFE), tokenId: streamId }); } // This function can be called only by the recipient function withdrawMaxAndTransfer(uint256 streamId) external payable { // Calculate the minimum fee to withdraw the amount. uint256 fee = sablier.calculateMinFeeWei(streamId); sablier.withdrawMaxAndTransfer{ value: fee }({ streamId: streamId, newRecipient: address(0xCAFE) }); } } ``` Create a contract called `StreamManagement` and declare an immutable variable `sablier` of type `ISablierLockup`: ```solidity ISablierLockup public immutable sablier; constructor(ISablierLockup sablier_) { sablier = sablier_; } /*////////////////////////////////////////////////////////////////////////// 02-WITHDRAW //////////////////////////////////////////////////////////////////////////*/ // This function can be called by the sender, recipient, or an approved NFT operator function withdraw(uint256 streamId) external payable { uint256 fee = sablier.calculateMinFeeWei(streamId); sablier.withdraw{ value: fee }({ streamId: streamId, to: address(0xCAFE), amount: 1337e18 }); } // This function can be called by the sender, recipient, or an approved NFT operator function withdrawMax(uint256 streamId) external payable { uint256 fee = sablier.calculateMinFeeWei(streamId); sablier.withdrawMax{ value: fee }({ streamId: streamId, to: address(0xCAFE) }); } // This function can be called by either the recipient or an approved NFT operator function withdrawMultiple(uint256[] calldata streamIds, uint128[] calldata amounts) external payable { uint256 maxFeeRequired; // The fee required to call withdraw multiple is the maximum of the fees required to withdraw each stream. for (uint256 i = 0; i < streamIds.length; i++) { uint256 feeForStreamId = sablier.calculateMinFeeWei(streamIds[i]); if (feeForStreamId > maxFeeRequired) { maxFeeRequired = feeForStreamId; } } sablier.withdrawMultiple{ value: maxFeeRequired }({ streamIds: streamIds, amounts: amounts }); } /*////////////////////////////////////////////////////////////////////////// 03-CANCEL //////////////////////////////////////////////////////////////////////////*/ // This function can be called only by the sender function cancel(uint256 streamId) external { sablier.cancel(streamId); } // This function can be called only by the sender function cancelMultiple(uint256[] calldata streamIds) external { sablier.cancelMultiple(streamIds); } /*////////////////////////////////////////////////////////////////////////// 04-RENOUNCE //////////////////////////////////////////////////////////////////////////*/ // This function can be called only by the sender function renounce(uint256 streamId) external { sablier.renounce(streamId); } /*////////////////////////////////////////////////////////////////////////// 05-TRANSFER //////////////////////////////////////////////////////////////////////////*/ // This function can be called by either the recipient or an approved NFT operator function safeTransferFrom(uint256 streamId) external { sablier.safeTransferFrom({ from: address(this), to: address(0xCAFE), tokenId: streamId }); } // This function can be called by either the recipient or an approved NFT operator function transferFrom(uint256 streamId) external { sablier.transferFrom({ from: address(this), to: address(0xCAFE), tokenId: streamId }); } // This function can be called only by the recipient function withdrawMaxAndTransfer(uint256 streamId) external payable { // Calculate the minimum fee to withdraw the amount. uint256 fee = sablier.calculateMinFeeWei(streamId); sablier.withdrawMaxAndTransfer{ value: fee }({ streamId: streamId, newRecipient: address(0xCAFE) }); } } ``` Just like in the create stream guides, the next step requires you to head over to the [Deployment Addresses](/guides/lockup/deployments) page and copy the address of the Lockup contract. Then, you can deploy the stream management contract: ```solidity constructor(ISablierLockup sablier_) { sablier = sablier_; } ``` You're all set! You can now move on to the next page, which will teach you how to withdraw from a stream. --- ## Transfer Ownership Source: https://docs.sablier.com/guides/lockup/examples/stream-management/transfer # Transfer Ownership :::note This section assumes that you have already gone through the [setup](/guides/lockup/examples/stream-management/setup) part. ::: :::tip See the [Access Control](/reference/lockup/access-control) guide for an overview of who is allowed to transfer ownership. ::: You may remember from the [NFT](/concepts/nft) guide that every Lockup stream is wrapped in an [ERC-721](https://eips.ethereum.org/EIPS/eip-721) non-fungible token (NFT). One of the key benefits of this design is that the recipient of the stream has the ability to transfer the NFT to a different address, effectively redirecting the streaming of tokens to that new address. To transfer ownership of a stream, it is recommended to invoke the [`withdrawMaxAndTransfer`](/reference/lockup/contracts/contract.SablierLockup#withdrawmaxandtransfer) function, which withdraws all the unclaimed funds to the current recipient prior to transferring ownership to the new recipient: ```solidity function withdrawMaxAndTransfer(uint256 streamId) external payable { // Calculate the minimum fee to withdraw the amount. uint256 fee = sablier.calculateMinFeeWei(streamId); sablier.withdrawMaxAndTransfer{ value: fee }({ streamId: streamId, newRecipient: address(0xCAFE) }); } ``` The withdrawal will be skipped if there are no unclaimed funds. If you want to transfer ownership without withdrawing the funds, you can use the `IERC721.transferFrom` function: ```solidity function transferFrom(uint256 streamId) external { sablier.transferFrom({ from: address(this), to: address(0xCAFE), tokenId: streamId }); } ``` :::caution Be careful with `transferFrom`. All remaining funds, including the already streamed portion, will enter into the possession of the new recipient. Consider using `withdrawMaxAndTransfer` instead. ::: Finally, note that in the examples above, the new recipient is hard-coded for demonstration purposes. However, in a production environment, the new recipient would definitely be an adjustable parameter provided by the user. --- ## Withdraw from Streams Source: https://docs.sablier.com/guides/lockup/examples/stream-management/withdraw # Withdraw from Streams :::note This section assumes that you have already gone through the [setup](/guides/lockup/examples/stream-management/setup) part. ::: :::tip See the [Access Control](/reference/lockup/access-control) guide for an overview of who is allowed to withdraw from streams. ::: Withdrawing from streams means claiming the tokens that have become due to the recipient, who has the option to direct the withdrawal to an alternative address of their choice. There are four withdrawal functions: 1. [`withdraw`](/reference/lockup/contracts/contract.SablierLockup#withdraw): withdraws a specific amount of tokens. 2. [`withdrawMax`](/reference/lockup/contracts/contract.SablierLockup#withdrawmax): withdraws the maximum withdrawable amount of tokens. 3. [`withdrawMaxAndTransfer`](/reference/lockup/contracts/contract.SablierLockup#withdrawmaxandtransfer): withdraws the maximum withdrawable amount and transfers the NFT. 4. [`withdrawMultiple`](/reference/lockup/contracts/contract.SablierLockup#withdrawmultiple): withdraws specific amounts of tokens from multiple streams at once. To call any of these functions, you need to have created a stream. If you don't have one yet, go back to the [previous guide](/guides/lockup/examples/create-stream/lockup-linear) and create a stream with a brief duration, assigning the `StreamManagement` contract as the recipient. Then, you can use the `withdraw` function like this: :::note The `withdraw` functions requires a fee. Make sure to send the correct amount in `msg.value`, as shown below. ::: ```solidity function withdraw(uint256 streamId) external payable { uint256 fee = sablier.calculateMinFeeWei(streamId); sablier.withdraw{ value: fee }({ streamId: streamId, to: address(0xCAFE), amount: 1337e18 }); } ``` In this example, the withdrawal address and withdrawal amount are hard-coded for demonstration purposes. However, in a production environment, these values would likely be adjustable parameters determined by the user. Alternatively, you can use [`withdrawableAmountOf`](/reference/lockup/contracts/contract.SablierLockup#withdrawableamountof) function to determine how much amount of tokens is available to withdraw. In addition to the `withdraw` function, there is the `withdrawMax` function, which you can use to withdraw the maximum withdrawable amount of tokens at the time of invocation: ```solidity function withdrawMax(uint256 streamId) external payable { uint256 fee = sablier.calculateMinFeeWei(streamId); sablier.withdrawMax{ value: fee }({ streamId: streamId, to: address(0xCAFE) }); } ``` What `withdrawMax` does is call the [`withdrawableAmountOf`](/reference/lockup/contracts/contract.SablierLockup#withdrawableamountof) function and pass its value to `withdraw`. Similar to `withdrawMax`, you can use `withdrawMaxAndTransfer` to withdraw the maximum withdrawable tokens and at the same time, transfer the NFT to another address. Lastly, there is the `withdrawMultiple` function, with which you can use to withdraw from multiple streams at once: ```solidity function withdrawMultiple(uint256[] calldata streamIds, uint128[] calldata amounts) external payable { uint256 maxFeeRequired; // The fee required to call withdraw multiple is the maximum of the fees required to withdraw each stream. for (uint256 i = 0; i < streamIds.length; i++) { uint256 feeForStreamId = sablier.calculateMinFeeWei(streamIds[i]); if (feeForStreamId > maxFeeRequired) { maxFeeRequired = feeForStreamId; } } sablier.withdrawMultiple{ value: maxFeeRequired }({ streamIds: streamIds, amounts: amounts }); } ``` --- ## Pull Vesting Data Source: https://docs.sablier.com/guides/lockup/examples/vesting-data # 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](https://github.com/sablier-labs/sandbox). For a comprehensive list of all the functions available in Sablier Lockup, visit the [References](/reference/overview) 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: ```solidity if (lockup.getWithdrawnAmount(streamId) > 0) { lockup.cancel(streamId); } ``` Offchain, you can monitor the [`WithdrawFromLockupStream`](/reference/lockup/contracts/interfaces/interface.ISablierLockup) 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: ```solidity uint256 amountInStream = sablierLockup.getDepositedAmount(streamId) - sablierLockup.getWithdrawnAmount(streamId) - sablierLockup.getRefundedAmount(streamId); ``` For a non-cancelable stream, a more efficient way to calculate the amount in stream is: ```solidity 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. ```solidity uint256 lockedAmount = lockup.getDepositedAmount(streamId) - lockup.streamedAmountOf(streamId) - sablierLockup.getRefundedAmount(streamId); ``` For a non-cancelable stream, a more efficient way to calculate locked amount is: ```solidity 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. ```solidity uint256 unlockedAmount = lockup.streamedAmountOf(streamId) + sablierLockup.getRefundedAmount(streamId); ``` For a non-cancelable stream, a more efficient way to calculate unlocked amount is: ```solidity 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: ```solidity 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: ```solidity 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 Linear streams, make requests to `lockup.getCliffTime(streamId)` and `lockup.getEndTime(streamId)` to read cliff time and end time respectively. ### Dynamic streams For Dynamic streams, you may be particularly interested in the unlock amount and time of the current segment. ```solidity 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 Tranched streams, you may be particularly interested in the unlock amount and time of the current tranche. ```solidity 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. --- ## Gas Benchmarks Source: https://docs.sablier.com/guides/lockup/gas-benchmarks # Gas Benchmarks The gas usage of the Lockup protocol is not deterministic and varies by user. Calls to third-party contracts, such as ERC-20 tokens, may use an arbitrary amount of gas. The values in the table below are rough estimations on Ethereum mainnet - you shouldn't take them for granted. The gas usage may vary depending on the network. :::note The benchmarks were generated using the code in this [GitHub repository](https://github.com/sablier-labs/evm-monorepo/blob/main/misc/benchmarks). ::: ## BatchLockup With WETH as the streaming token. | Lockup Model | Function | Batch Size | Segments/Tranches | Gas Usage | | :----------- | :----------------------- | :--------- | :---------------- | :--------- | | Linear | \`createWithDurationsLL\` | 5 | N/A | 1,050,118 | | Linear | \`createWithTimestampsLL\` | 5 | N/A | 998,961 | | Dynamic | \`createWithDurationsLD\` | 5 | 24 | 4,183,356 | | Dynamic | \`createWithTimestampsLD\` | 5 | 24 | 3,938,420 | | Tranched | \`createWithDurationsLT\` | 5 | 24 | 4,046,168 | | Tranched | \`createWithTimestampsLT\` | 5 | 24 | 3,853,698 | | Linear | \`createWithDurationsLL\` | 10 | N/A | 1,952,882 | | Linear | \`createWithTimestampsLL\` | 10 | N/A | 1,946,687 | | Dynamic | \`createWithDurationsLD\` | 10 | 24 | 8,321,775 | | Dynamic | \`createWithTimestampsLD\` | 10 | 24 | 7,826,938 | | Tranched | \`createWithDurationsLT\` | 10 | 24 | 8,039,187 | | Tranched | \`createWithTimestampsLT\` | 10 | 24 | 7,657,724 | | Linear | \`createWithDurationsLL\` | 20 | N/A | 3,856,454 | | Linear | \`createWithTimestampsLL\` | 20 | N/A | 3,844,693 | | Dynamic | \`createWithDurationsLD\` | 20 | 24 | 16,614,741 | | Dynamic | \`createWithTimestampsLD\` | 20 | 24 | 15,607,548 | | Tranched | \`createWithDurationsLT\` | 20 | 24 | 16,022,800 | | Tranched | \`createWithTimestampsLT\` | 20 | 24 | 15,269,123 | | Linear | \`createWithDurationsLL\` | 50 | N/A | 9,576,314 | | Linear | \`createWithTimestampsLL\` | 50 | N/A | 9,550,888 | | Dynamic | \`createWithDurationsLD\` | 50 | 12 | 24,460,057 | | Dynamic | \`createWithTimestampsLD\` | 50 | 12 | 23,154,421 | | Tranched | \`createWithDurationsLT\` | 50 | 12 | 23,662,837 | | Tranched | \`createWithTimestampsLT\` | 50 | 12 | 22,742,309 | ## LockupLinear streams With WETH as the streaming token. | Function | Configuration | Gas Usage | | :----------------------- | :----------------------------------------- | :-------- | | \`burn\` | N/A | 8510 | | \`cancel\` | N/A | 39,195 | | \`renounce\` | N/A | 4532 | | \`createWithDurationsLL\` | no cliff | 145,964 | | \`createWithDurationsLL\` | with cliff | 185,937 | | \`createWithTimestampsLL\` | no cliff | 145,249 | | \`createWithTimestampsLL\` | with cliff | 184,994 | | \`withdraw\` | vesting ongoing && called by recipient | 32,194 | | \`withdraw\` | vesting completed && called by recipient | 32,394 | | \`withdraw\` | vesting ongoing && called by third-party | 32,433 | | \`withdraw\` | vesting completed && called by third-party | 32,633 | ## LockupDynamic streams With WETH as the streaming token. | Function | Segments | Configuration | Gas Usage | | :----------------------- | :------- | :----------------------------------------- | :-------- | | \`burn\` | 2 | N/A | 8510 | | \`cancel\` | 2 | N/A | 50,777 | | \`renounce\` | 2 | N/A | 4532 | | \`createWithDurationsLD\` | 2 | N/A | 197,298 | | \`createWithTimestampsLD\` | 2 | N/A | 191,161 | | \`withdraw\` | 2 | vesting ongoing && called by recipient | 43,779 | | \`withdraw\` | 2 | vesting completed && called by recipient | 32,628 | | \`withdraw\` | 2 | vesting ongoing && called by third-party | 44,018 | | \`withdraw\` | 2 | vesting completed && called by third-party | 32,867 | | \`createWithDurationsLD\` | 10 | N/A | 421,226 | | \`createWithTimestampsLD\` | 10 | N/A | 399,192 | | \`withdraw\` | 10 | vesting ongoing && called by recipient | 48,903 | | \`withdraw\` | 10 | vesting completed && called by recipient | 34,816 | | \`withdraw\` | 10 | vesting ongoing && called by third-party | 49,142 | | \`withdraw\` | 10 | vesting completed && called by third-party | 35,055 | | \`createWithDurationsLD\` | 100 | N/A | 2,947,148 | | \`createWithTimestampsLD\` | 100 | N/A | 2,742,934 | | \`withdraw\` | 100 | vesting ongoing && called by recipient | 106,703 | | \`withdraw\` | 100 | vesting completed && called by recipient | 59,586 | | \`withdraw\` | 100 | vesting ongoing && called by third-party | 106,942 | | \`withdraw\` | 100 | vesting completed && called by third-party | 59,825 | ## LockupTranched streams With WETH as the streaming token. | Function | Tranches | Configuration | Gas Usage | | :----------------------- | :------- | :----------------------------------------- | :-------- | | \`burn\` | 2 | N/A | 8510 | | \`cancel\` | 2 | N/A | 39,459 | | \`renounce\` | 2 | N/A | 4532 | | \`createWithDurationsLT\` | 2 | N/A | 194,663 | | \`createWithTimestampsLT\` | 2 | N/A | 190,054 | | \`withdraw\` | 2 | vesting ongoing && called by recipient | 32,458 | | \`withdraw\` | 2 | vesting completed && called by recipient | 32,810 | | \`withdraw\` | 2 | vesting ongoing && called by third-party | 32,697 | | \`withdraw\` | 2 | vesting completed && called by third-party | 33,049 | | \`createWithDurationsLT\` | 10 | N/A | 410,233 | | \`createWithTimestampsLT\` | 10 | N/A | 392,857 | | \`withdraw\` | 10 | vesting ongoing && called by recipient | 37,460 | | \`withdraw\` | 10 | vesting completed && called by recipient | 34,636 | | \`withdraw\` | 10 | vesting ongoing && called by third-party | 37,699 | | \`withdraw\` | 10 | vesting completed && called by third-party | 34,875 | | \`createWithDurationsLT\` | 100 | N/A | 2,838,746 | | \`createWithTimestampsLT\` | 100 | N/A | 2,676,076 | | \`withdraw\` | 100 | vesting ongoing && called by recipient | 93,804 | | \`withdraw\` | 100 | vesting completed && called by recipient | 55,250 | | \`withdraw\` | 100 | vesting ongoing && called by third-party | 94,043 | | \`withdraw\` | 100 | vesting completed && called by third-party | 55,489 | --- ## Sablier Lockup Source: https://docs.sablier.com/guides/lockup/overview # Sablier Lockup Welcome to the Sablier Lockup documentation. This section contains detailed guides and technical references for the Lockup protocol, a suite of smart contracts running autonomously in the Ethereum ecosystem. These documents offer insight into the operational nuances of the contracts, providing a valuable resource for building onchain integrations. # Guides If you are new to Sablier, we recommend you start with the [Concepts](/concepts/what-is-sablier) section first. You can then setup your [local environment](/guides/lockup/examples/local-environment) and create your [first stream](/guides/lockup/examples/create-stream/lockup-linear). # Reference For a deeper dive into the protocol specifications, read through the [technical reference](/reference/lockup/diagrams). # Versioning The product uses a unified versioning system across releases and NPM packages. Prior to Lockup v1.2, we used a different versioning scheme (V2.0, V2.1, V2.2), while the NPM package used a semantic versioning scheme (e.g., v1.0.2, v1.1.2). Since Lockup v1.2, the versioning has been unified into a single system for greater consistency across protocol releases and NPM packages. # Resources - [Source Code](https://github.com/sablier-labs/evm-monorepo/blob/main/lockup) - [Integration Templates](https://github.com/sablier-labs/lockup-integration-template) - [Examples](https://github.com/sablier-labs/evm-monorepo/tree/main/misc/examples/lockup/) - [Foundry Book](https://book.getfoundry.sh/) --- ## Lockup v1.0 Source: https://docs.sablier.com/guides/lockup/previous-deployments/v1.0 # Lockup v1.0 This section contains the deployment addresses for the v1.0 release of [@sablier/v2-core@1.0.2](https://npmjs.com/package/@sablier/v2-core/v/1.0.2) and [@sablier/v2-periphery@1.0.3](https://npmjs.com/package/@sablier/v2-periphery/v/1.0.3). A few noteworthy details about the deployments: - The addresses are final - All contracts are non-upgradeable - The source code is verified on Etherscan across all chains :::info This is an outdated version of the Lockup protocol. See the latest version [here](/guides/lockup/deployments). ::: ## Mainnets ### Ethereum ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2Comptroller | [`0xC3Be6BffAeab7B297c03383B4254aa3Af2b9a5BA`](https://etherscan.io/address/0xC3Be6BffAeab7B297c03383B4254aa3Af2b9a5BA) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2LockupDynamic | [`0x39EFdC3dbB57B2388CcC4bb40aC4CB1226Bc9E44`](https://etherscan.io/address/0x39EFdC3dbB57B2388CcC4bb40aC4CB1226Bc9E44) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2LockupLinear | [`0xB10daee1FCF62243aE27776D7a92D39dC8740f95`](https://etherscan.io/address/0xB10daee1FCF62243aE27776D7a92D39dC8740f95) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2NFTDescriptor | [`0x98F2196fECc01C240d1429B624d007Ca268EEA29`](https://etherscan.io/address/0x98F2196fECc01C240d1429B624d007Ca268EEA29) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2Archive | [`0x0Be20a8242B0781B6fd4d453e90DCC1CcF7DBcc6`](https://etherscan.io/address/0x0Be20a8242B0781B6fd4d453e90DCC1CcF7DBcc6) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2ProxyPlugin | [`0x9bdebF4F9adEB99387f46e4020FBf3dDa885D2b8`](https://etherscan.io/address/0x9bdebF4F9adEB99387f46e4020FBf3dDa885D2b8) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2ProxyTarget | [`0x297b43aE44660cA7826ef92D8353324C018573Ef`](https://etherscan.io/address/0x297b43aE44660cA7826ef92D8353324C018573Ef) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2ProxyTargetApprove | [`0x638a7aC8315767cEAfc57a6f5e3559454347C3f6`](https://etherscan.io/address/0x638a7aC8315767cEAfc57a6f5e3559454347C3f6) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | ### Arbitrum ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2Comptroller | [`0x17Ec73692F0aDf7E7C554822FBEAACB4BE781762`](https://arbiscan.io/address/0x17Ec73692F0aDf7E7C554822FBEAACB4BE781762) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2LockupDynamic | [`0xA9EfBEf1A35fF80041F567391bdc9813b2D50197`](https://arbiscan.io/address/0xA9EfBEf1A35fF80041F567391bdc9813b2D50197) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2LockupLinear | [`0x197D655F3be03903fD25e7828c3534504bfe525e`](https://arbiscan.io/address/0x197D655F3be03903fD25e7828c3534504bfe525e) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2NFTDescriptor | [`0xc245d6C9608769CeF91C3858e4d2a74802B9f1bB`](https://arbiscan.io/address/0xc245d6C9608769CeF91C3858e4d2a74802B9f1bB) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2Archive | [`0xDFa4512d07AbD4eb8Be570Cd79e2e6Fe21ff15C9`](https://arbiscan.io/address/0xDFa4512d07AbD4eb8Be570Cd79e2e6Fe21ff15C9) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2ProxyPlugin | [`0x9aB73CA73c89AF0bdc69642aCeb23CC6A55A514C`](https://arbiscan.io/address/0x9aB73CA73c89AF0bdc69642aCeb23CC6A55A514C) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2ProxyTarget | [`0xB7185AcAF42C4966fFA3c81486d9ED9633aa4c13`](https://arbiscan.io/address/0xB7185AcAF42C4966fFA3c81486d9ED9633aa4c13) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2ProxyTargetApprove | [`0x90cc23dc3e12e80f27c05b8137b5f0d2b1edfa20`](https://arbiscan.io/address/0x90cc23dc3e12e80f27c05b8137b5f0d2b1edfa20) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | ### Avalanche ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2Comptroller | [`0x66F5431B0765D984f82A4fc4551b2c9ccF7eAC9C`](https://snowscan.xyz/address/0x66F5431B0765D984f82A4fc4551b2c9ccF7eAC9C) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2LockupDynamic | [`0x665d1C8337F1035cfBe13DD94bB669110b975f5F`](https://snowscan.xyz/address/0x665d1C8337F1035cfBe13DD94bB669110b975f5F) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2LockupLinear | [`0x610346E9088AFA70D6B03e96A800B3267E75cA19`](https://snowscan.xyz/address/0x610346E9088AFA70D6B03e96A800B3267E75cA19) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2NFTDescriptor | [`0xFd050AFA2e04aA0596947DaD3Ec5690162aDc77F`](https://snowscan.xyz/address/0xFd050AFA2e04aA0596947DaD3Ec5690162aDc77F) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2Archive | [`0x7b1ef644ce9a625537e9e0c3d7fef3be667e6159`](https://snowscan.xyz/address/0x7b1ef644ce9a625537e9e0c3d7fef3be667e6159) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2ProxyPlugin | [`0x17167A7e2763121e263B4331B700a1BF9113b387`](https://snowscan.xyz/address/0x17167A7e2763121e263B4331B700a1BF9113b387) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2ProxyTarget | [`0x48B4889cf5d6f8360050f9d7606505F1433120BC`](https://snowscan.xyz/address/0x48B4889cf5d6f8360050f9d7606505F1433120BC) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2ProxyTargetApprove | [`0x817fE1364A9d57d1fB951945B53942234163Ef10`](https://snowscan.xyz/address/0x817fE1364A9d57d1fB951945B53942234163Ef10) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | ### Base ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2Comptroller | [`0x7Faaedd40B1385C118cA7432952D9DC6b5CbC49e`](https://basescan.org/address/0x7Faaedd40B1385C118cA7432952D9DC6b5CbC49e) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2LockupDynamic | [`0x645B00960Dc352e699F89a81Fc845C0C645231cf`](https://basescan.org/address/0x645B00960Dc352e699F89a81Fc845C0C645231cf) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2LockupLinear | [`0x6b9a46C8377f21517E65fa3899b3A9Fab19D17f5`](https://basescan.org/address/0x6b9a46C8377f21517E65fa3899b3A9Fab19D17f5) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2NFTDescriptor | [`0xEFc2896c29F70bc23e82892Df827d4e2259028Fd`](https://basescan.org/address/0xEFc2896c29F70bc23e82892Df827d4e2259028Fd) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2Archive | [`0x1C5Ac71dd48c7ff291743e5E6e3689ba92F73cC6`](https://basescan.org/address/0x1C5Ac71dd48c7ff291743e5E6e3689ba92F73cC6) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2ProxyPlugin | [`0x50E8B9dC7F28e5cA9253759455C1077e497c4232`](https://basescan.org/address/0x50E8B9dC7F28e5cA9253759455C1077e497c4232) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2ProxyTarget | [`0x0648C80b969501c7778b6ff3ba47aBb78fEeDF39`](https://basescan.org/address/0x0648C80b969501c7778b6ff3ba47aBb78fEeDF39) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2ProxyTargetApprove | [`0xf19576Ab425753816eCbF98aca8132A0f693aEc5`](https://basescan.org/address/0xf19576Ab425753816eCbF98aca8132A0f693aEc5) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | ### BNB Chain ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2Comptroller | [`0x33511f69A784Fd958E6713aCaC7c9dCF1A5578E8`](https://bscscan.com/address/0x33511f69A784Fd958E6713aCaC7c9dCF1A5578E8) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2LockupDynamic | [`0xF2f3feF2454DcA59ECA929D2D8cD2a8669Cc6214`](https://bscscan.com/address/0xF2f3feF2454DcA59ECA929D2D8cD2a8669Cc6214) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2LockupLinear | [`0x3FE4333f62A75c2a85C8211c6AeFd1b9Bfde6e51`](https://bscscan.com/address/0x3FE4333f62A75c2a85C8211c6AeFd1b9Bfde6e51) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2NFTDescriptor | [`0x3daD1bF57edCFF979Fb68a802AC54c5AAfB78F4c`](https://bscscan.com/address/0x3daD1bF57edCFF979Fb68a802AC54c5AAfB78F4c) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2Archive | [`0xeDe48EB173A869c0b27Cb98CC56d00BC391e5887`](https://bscscan.com/address/0xeDe48EB173A869c0b27Cb98CC56d00BC391e5887) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2ProxyPlugin | [`0xC43b2d8CedB71df30F45dFd9a21eC1E50A813bD6`](https://bscscan.com/address/0xC43b2d8CedB71df30F45dFd9a21eC1E50A813bD6) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2ProxyTarget | [`0x135e78B8E17B1d189Af75FcfCC018ab2E6c7b879`](https://bscscan.com/address/0x135e78B8E17B1d189Af75FcfCC018ab2E6c7b879) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2ProxyTargetApprove | [`0xc9bf2A6bD467A813908d836c1506efE61E465761`](https://bscscan.com/address/0xc9bf2A6bD467A813908d836c1506efE61E465761) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | ### Gnosis ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2Comptroller | [`0x73962c44c0fB4cC5e4545FB91732a5c5e87F55C2`](https://gnosisscan.io/address/0x73962c44c0fB4cC5e4545FB91732a5c5e87F55C2) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2LockupDynamic | [`0xeb148E4ec13aaA65328c0BA089a278138E9E53F9`](https://gnosisscan.io/address/0xeb148E4ec13aaA65328c0BA089a278138E9E53F9) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2LockupLinear | [`0x685E92c9cA2bB23f1B596d0a7D749c0603e88585`](https://gnosisscan.io/address/0x685E92c9cA2bB23f1B596d0a7D749c0603e88585) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2NFTDescriptor | [`0x8CE9Cd651e03325Cf6D4Ce9cfa74BE79CDf6d530`](https://gnosisscan.io/address/0x8CE9Cd651e03325Cf6D4Ce9cfa74BE79CDf6d530) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2Archive | [`0xF4A6F47Da7c6b26b6Dd774671aABA48fb4bFE309`](https://gnosisscan.io/address/0xF4A6F47Da7c6b26b6Dd774671aABA48fb4bFE309) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2ProxyPlugin | [`0xc84f0e95815A576171A19EB9E0fA55a217Ab1536`](https://gnosisscan.io/address/0xc84f0e95815A576171A19EB9E0fA55a217Ab1536) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2ProxyTarget | [`0x5B144C3B9C8cfd48297Aeb59B90a024Ef3fCcE92`](https://gnosisscan.io/address/0x5B144C3B9C8cfd48297Aeb59B90a024Ef3fCcE92) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2ProxyTargetApprove | [`0x89AfE038714e547C29Fa881029DD4B5CFB008454`](https://gnosisscan.io/address/0x89AfE038714e547C29Fa881029DD4B5CFB008454) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | ### OP Mainnet ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2Comptroller | [`0x1EECb6e6EaE6a1eD1CCB4323F3a146A7C5443A10`](https://optimistic.etherscan.io/address/0x1EECb6e6EaE6a1eD1CCB4323F3a146A7C5443A10) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2LockupDynamic | [`0x6f68516c21E248cdDfaf4898e66b2b0Adee0e0d6`](https://optimistic.etherscan.io/address/0x6f68516c21E248cdDfaf4898e66b2b0Adee0e0d6) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2LockupLinear | [`0xB923aBdCA17Aed90EB5EC5E407bd37164f632bFD`](https://optimistic.etherscan.io/address/0xB923aBdCA17Aed90EB5EC5E407bd37164f632bFD) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2NFTDescriptor | [`0xe0138C596939CC0D2382046795bC163ad5755e0E`](https://optimistic.etherscan.io/address/0xe0138C596939CC0D2382046795bC163ad5755e0E) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2Archive | [`0x9A09eC6f991386718854aDDCEe68647776Befd5b`](https://optimistic.etherscan.io/address/0x9A09eC6f991386718854aDDCEe68647776Befd5b) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2ProxyPlugin | [`0x77C8516B1F327890C956bb38F93Ac2d6B24795Ea`](https://optimistic.etherscan.io/address/0x77C8516B1F327890C956bb38F93Ac2d6B24795Ea) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2ProxyTarget | [`0x194ed7D6005C8ba4084A948406545DF299ad37cD`](https://optimistic.etherscan.io/address/0x194ed7D6005C8ba4084A948406545DF299ad37cD) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2ProxyTargetApprove | [`0x8a6974c162fdc7Cb67996F7dB8bAAFb9a99566e0`](https://optimistic.etherscan.io/address/0x8a6974c162fdc7Cb67996F7dB8bAAFb9a99566e0) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | ### Polygon ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2Comptroller | [`0x9761692EDf10F5F2A69f0150e2fd50dcecf05F2E`](https://polygonscan.com/address/0x9761692EDf10F5F2A69f0150e2fd50dcecf05F2E) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2LockupDynamic | [`0x7313AdDb53f96a4f710D3b91645c62B434190725`](https://polygonscan.com/address/0x7313AdDb53f96a4f710D3b91645c62B434190725) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2LockupLinear | [`0x67422C3E36A908D5C3237e9cFfEB40bDE7060f6E`](https://polygonscan.com/address/0x67422C3E36A908D5C3237e9cFfEB40bDE7060f6E) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2NFTDescriptor | [`0xA820946EaAceB2a85aF123f706f23192c28bC6B9`](https://polygonscan.com/address/0xA820946EaAceB2a85aF123f706f23192c28bC6B9) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2Archive | [`0xA2f5B2e798e7ADd59d85d9b76645E6AC13fC4e1f`](https://polygonscan.com/address/0xA2f5B2e798e7ADd59d85d9b76645E6AC13fC4e1f) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2ProxyPlugin | [`0xBe4cad0e99865CC62787Ecf029aD9DD4815d3d2e`](https://polygonscan.com/address/0xBe4cad0e99865CC62787Ecf029aD9DD4815d3d2e) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2ProxyTarget | [`0x576743075fc5F771bbC1376c3267A6185Af9D62B`](https://polygonscan.com/address/0x576743075fc5F771bbC1376c3267A6185Af9D62B) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2ProxyTargetApprove | [`0xccA6dd77bA2cfcccEdA01A82CB309e2A17901682`](https://polygonscan.com/address/0xccA6dd77bA2cfcccEdA01A82CB309e2A17901682) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | ### Scroll ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2Comptroller | [`0x859708495E3B3c61Bbe19e6E3E1F41dE3A5C5C5b`](https://scrollscan.com/address/0x859708495E3B3c61Bbe19e6E3E1F41dE3A5C5C5b) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2LockupDynamic | [`0xde6a30D851eFD0Fc2a9C922F294801Cfd5FCB3A1`](https://scrollscan.com/address/0xde6a30D851eFD0Fc2a9C922F294801Cfd5FCB3A1) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2LockupLinear | [`0x80640ca758615ee83801EC43452feEA09a202D33`](https://scrollscan.com/address/0x80640ca758615ee83801EC43452feEA09a202D33) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2NFTDescriptor | [`0xC1fa624733203F2B7185c3724039C4D5E5234fE4`](https://scrollscan.com/address/0xC1fa624733203F2B7185c3724039C4D5E5234fE4) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2Archive | [`0x94A18AC6e4B7d97E31f1587f6a666Dc5503086c3`](https://scrollscan.com/address/0x94A18AC6e4B7d97E31f1587f6a666Dc5503086c3) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2ProxyPlugin | [`0xED1591BD6038032a74D786A452A23536b3201490`](https://scrollscan.com/address/0xED1591BD6038032a74D786A452A23536b3201490) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2ProxyTarget | [`0x91154fc80933D25793E6B4D7CE19fb51dE6794B7`](https://scrollscan.com/address/0x91154fc80933D25793E6B4D7CE19fb51dE6794B7) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2ProxyTargetApprove | [`0x71CeA9c4d15fed2E58785cE0C05165CE34313A74`](https://scrollscan.com/address/0x71CeA9c4d15fed2E58785cE0C05165CE34313A74) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | ## Testnets ### Arbitrum Sepolia ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2Comptroller | [`0xA6A0cfA3442053fbB516D55205A749Ef2D33aed9`](https://sepolia.arbiscan.io/address/0xA6A0cfA3442053fbB516D55205A749Ef2D33aed9) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2LockupDynamic | [`0x7938c18a59FaD2bA11426AcfBe8d74F0F598a4D2`](https://sepolia.arbiscan.io/address/0x7938c18a59FaD2bA11426AcfBe8d74F0F598a4D2) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2LockupLinear | [`0xa3e36b51B7A456812c92253780f4B15bad56e34c`](https://sepolia.arbiscan.io/address/0xa3e36b51B7A456812c92253780f4B15bad56e34c) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2NFTDescriptor | [`0xEe93BFf599C17C6fF8e31F2De6c3e40bd5e51312`](https://sepolia.arbiscan.io/address/0xEe93BFf599C17C6fF8e31F2De6c3e40bd5e51312) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2Archive | [`0x2C8fA48361C7D48Dc21b27a3D549402Cf8AE16B0`](https://sepolia.arbiscan.io/address/0x2C8fA48361C7D48Dc21b27a3D549402Cf8AE16B0) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2ProxyPlugin | [`0x7D310803c3824636bAff74e4f80e81ece167c440`](https://sepolia.arbiscan.io/address/0x7D310803c3824636bAff74e4f80e81ece167c440) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2ProxyTarget | [`0x396A3a169918A4C0B339ECf86C583f46D696254E`](https://sepolia.arbiscan.io/address/0x396A3a169918A4C0B339ECf86C583f46D696254E) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | ### Sepolia ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2Comptroller | [`0x2006d43E65e66C5FF20254836E63947FA8bAaD68`](https://sepolia.etherscan.io/address/0x2006d43E65e66C5FF20254836E63947FA8bAaD68) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2LockupDynamic | [`0x421e1E7a53FF360f70A2D02037Ee394FA474e035`](https://sepolia.etherscan.io/address/0x421e1E7a53FF360f70A2D02037Ee394FA474e035) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2LockupLinear | [`0xd4300c5bc0b9e27c73ebabdc747ba990b1b570db`](https://sepolia.etherscan.io/address/0xd4300c5bc0b9e27c73ebabdc747ba990b1b570db) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2NFTDescriptor | [`0x3cb51943EbcEA05B23C35c50491B3d296FF675db`](https://sepolia.etherscan.io/address/0x3cb51943EbcEA05B23C35c50491B3d296FF675db) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2Archive | [`0x83495d8DF6221f566232e1353a6e7231A86C61fF`](https://sepolia.etherscan.io/address/0x83495d8DF6221f566232e1353a6e7231A86C61fF) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2ProxyPlugin | [`0xa333c8233CfD04740E64AB4fd5447995E357561B`](https://sepolia.etherscan.io/address/0xa333c8233CfD04740E64AB4fd5447995E357561B) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2ProxyTarget | [`0x5091900B7cF803a7407FCE6333A6bAE4aA779Fd4`](https://sepolia.etherscan.io/address/0x5091900B7cF803a7407FCE6333A6bAE4aA779Fd4) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | | SablierV2ProxyTargetApprove | [`0x105E7728C5706Ad41d194EbDc7873B047352F3d2`](https://sepolia.etherscan.io/address/0x105E7728C5706Ad41d194EbDc7873B047352F3d2) | [`lockup-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.0) | --- ## Lockup v1.1 Source: https://docs.sablier.com/guides/lockup/previous-deployments/v1.1 # Lockup v1.1 This section contains the deployment addresses for the v1.1 release of [@sablier/v2-core@1.1.2](https://npmjs.com/package/@sablier/v2-core/v/1.1.2) and [@sablier/v2-periphery@1.1.1](https://npmjs.com/package/@sablier/v2-periphery/v/1.1.1). A few noteworthy details about the deployments: - The addresses are final - All contracts are non-upgradeable - The source code is verified on Etherscan across all chains :::info This is an outdated version of the Lockup protocol. See the latest version [here](/guides/lockup/deployments). ::: ## Mainnets ### Ethereum ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2Comptroller | [`0xC3Be6BffAeab7B297c03383B4254aa3Af2b9a5BA`](https://etherscan.io/address/0xC3Be6BffAeab7B297c03383B4254aa3Af2b9a5BA) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupDynamic | [`0x7CC7e125d83A581ff438608490Cc0f7bDff79127`](https://etherscan.io/address/0x7CC7e125d83A581ff438608490Cc0f7bDff79127) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupLinear | [`0xAFb979d9afAd1aD27C5eFf4E27226E3AB9e5dCC9`](https://etherscan.io/address/0xAFb979d9afAd1aD27C5eFf4E27226E3AB9e5dCC9) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2NFTDescriptor | [`0x23eD5DA55AF4286c0dE55fAcb414dEE2e317F4CB`](https://etherscan.io/address/0x23eD5DA55AF4286c0dE55fAcb414dEE2e317F4CB) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2Batch | [`0xEa07DdBBeA804E7fe66b958329F8Fa5cDA95Bd55`](https://etherscan.io/address/0xEa07DdBBeA804E7fe66b958329F8Fa5cDA95Bd55) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2MerkleStreamerFactory | [`0x1A272b596b10f02931480BC7a3617db4a8d154E3`](https://etherscan.io/address/0x1A272b596b10f02931480BC7a3617db4a8d154E3) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | ### Arbitrum ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2Comptroller | [`0x17Ec73692F0aDf7E7C554822FBEAACB4BE781762`](https://arbiscan.io/address/0x17Ec73692F0aDf7E7C554822FBEAACB4BE781762) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupDynamic | [`0xf390cE6f54e4dc7C5A5f7f8689062b7591F7111d`](https://arbiscan.io/address/0xf390cE6f54e4dc7C5A5f7f8689062b7591F7111d) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupLinear | [`0xFDD9d122B451F549f48c4942c6fa6646D849e8C1`](https://arbiscan.io/address/0xFDD9d122B451F549f48c4942c6fa6646D849e8C1) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2NFTDescriptor | [`0x2fb103fC853b2F5022a840091ab1cDf5172E7cfa`](https://arbiscan.io/address/0x2fb103fC853b2F5022a840091ab1cDf5172E7cfa) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2Batch | [`0xAFd1434296e29a0711E24014656158055F00784c`](https://arbiscan.io/address/0xAFd1434296e29a0711E24014656158055F00784c) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2MerkleStreamerFactory | [`0x237400eF5a41886a75B0e036228221Df075b3B80`](https://arbiscan.io/address/0x237400eF5a41886a75B0e036228221Df075b3B80) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | ### Avalanche ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2Comptroller | [`0x66F5431B0765D984f82A4fc4551b2c9ccF7eAC9C`](https://snowscan.xyz/address/0x66F5431B0765D984f82A4fc4551b2c9ccF7eAC9C) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupDynamic | [`0x0310Da0D8fF141166eD47548f00c96464880781F`](https://snowscan.xyz/address/0x0310Da0D8fF141166eD47548f00c96464880781F) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupLinear | [`0xB24B65E015620455bB41deAAd4e1902f1Be9805f`](https://snowscan.xyz/address/0xB24B65E015620455bB41deAAd4e1902f1Be9805f) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2NFTDescriptor | [`0xaBEdCf46c5D1d8eD8B9a487144189887695835DC`](https://snowscan.xyz/address/0xaBEdCf46c5D1d8eD8B9a487144189887695835DC) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2Batch | [`0x68f156E5fa8C23D65B33aBEbbA50e0CA3626F741`](https://snowscan.xyz/address/0x68f156E5fa8C23D65B33aBEbbA50e0CA3626F741) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2MerkleStreamerFactory | [`0x4849e797d7Aab20FCC8f807EfafDffF98A83412E`](https://snowscan.xyz/address/0x4849e797d7Aab20FCC8f807EfafDffF98A83412E) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | ### Base ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2Comptroller | [`0x7Faaedd40B1385C118cA7432952D9DC6b5CbC49e`](https://basescan.org/address/0x7Faaedd40B1385C118cA7432952D9DC6b5CbC49e) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupDynamic | [`0x461E13056a3a3265CEF4c593F01b2e960755dE91`](https://basescan.org/address/0x461E13056a3a3265CEF4c593F01b2e960755dE91) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupLinear | [`0xFCF737582d167c7D20A336532eb8BCcA8CF8e350`](https://basescan.org/address/0xFCF737582d167c7D20A336532eb8BCcA8CF8e350) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2NFTDescriptor | [`0x67e0a126b695DBA35128860cd61926B90C420Ceb`](https://basescan.org/address/0x67e0a126b695DBA35128860cd61926B90C420Ceb) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2Batch | [`0x94E596EEd73b4e3171c067f05A87AB0268cA993c`](https://basescan.org/address/0x94E596EEd73b4e3171c067f05A87AB0268cA993c) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2MerkleStreamerFactory | [`0x5545c8E7c3E1F74aDc98e518F2E8D23A002C4412`](https://basescan.org/address/0x5545c8E7c3E1F74aDc98e518F2E8D23A002C4412) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | ### Blast ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2Comptroller | [`0x2De92156000269fa2fde7544F10f01E8cBC80fFa`](https://blastscan.io/address/0x2De92156000269fa2fde7544F10f01E8cBC80fFa) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupDynamic | [`0xDf578C2c70A86945999c65961417057363530a1c`](https://blastscan.io/address/0xDf578C2c70A86945999c65961417057363530a1c) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupLinear | [`0xcb099EfC90e88690e287259410B9AE63e1658CC6`](https://blastscan.io/address/0xcb099EfC90e88690e287259410B9AE63e1658CC6) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2NFTDescriptor | [`0xCff4a803b0Bf55dD1BE38Fb96088478F3D2eeCF2`](https://blastscan.io/address/0xCff4a803b0Bf55dD1BE38Fb96088478F3D2eeCF2) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2Batch | [`0x0eDA15D606733f6CDe9DB67263E546bfcDDe9264`](https://blastscan.io/address/0x0eDA15D606733f6CDe9DB67263E546bfcDDe9264) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2MerkleStreamerFactory | [`0x92FC05e49c27884d554D98a5C01Ff0894a9DC29a`](https://blastscan.io/address/0x92FC05e49c27884d554D98a5C01Ff0894a9DC29a) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | ### BNB Chain ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2Comptroller | [`0x33511f69A784Fd958E6713aCaC7c9dCF1A5578E8`](https://bscscan.com/address/0x33511f69A784Fd958E6713aCaC7c9dCF1A5578E8) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupDynamic | [`0xf900c5E3aA95B59Cc976e6bc9c0998618729a5fa`](https://bscscan.com/address/0xf900c5E3aA95B59Cc976e6bc9c0998618729a5fa) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupLinear | [`0x14c35E126d75234a90c9fb185BF8ad3eDB6A90D2`](https://bscscan.com/address/0x14c35E126d75234a90c9fb185BF8ad3eDB6A90D2) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2NFTDescriptor | [`0xEcAfcF09c23057210cB6470eB5D0FD8Bafd1755F`](https://bscscan.com/address/0xEcAfcF09c23057210cB6470eB5D0FD8Bafd1755F) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2Batch | [`0x2E30a2ae6565Db78C06C28dE937F668597c80a1c`](https://bscscan.com/address/0x2E30a2ae6565Db78C06C28dE937F668597c80a1c) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2MerkleStreamerFactory | [`0x434D73465aAc4125d204A6637eB6C579d8D69f48`](https://bscscan.com/address/0x434D73465aAc4125d204A6637eB6C579d8D69f48) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | ### Gnosis ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2Comptroller | [`0x73962c44c0fB4cC5e4545FB91732a5c5e87F55C2`](https://gnosisscan.io/address/0x73962c44c0fB4cC5e4545FB91732a5c5e87F55C2) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupDynamic | [`0x1DF83C7682080B0f0c26a20C6C9CB8623e0Df24E`](https://gnosisscan.io/address/0x1DF83C7682080B0f0c26a20C6C9CB8623e0Df24E) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupLinear | [`0xce49854a647a1723e8Fb7CC3D190CAB29A44aB48`](https://gnosisscan.io/address/0xce49854a647a1723e8Fb7CC3D190CAB29A44aB48) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2NFTDescriptor | [`0x01dbFE22205d8B109959e2Be02d0095379309eed`](https://gnosisscan.io/address/0x01dbFE22205d8B109959e2Be02d0095379309eed) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2Batch | [`0xBd9DDbC55B85FF6Dc0b76E9EFdCd2547Ab482501`](https://gnosisscan.io/address/0xBd9DDbC55B85FF6Dc0b76E9EFdCd2547Ab482501) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2MerkleStreamerFactory | [`0x777F66477FF83aBabADf39a3F22A8CC3AEE43765`](https://gnosisscan.io/address/0x777F66477FF83aBabADf39a3F22A8CC3AEE43765) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | ### Lightlink ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2Comptroller | [`0xb568f9Bc0dcE39B9B64e843bC19DA102B5E3E939`](https://phoenix.lightlink.io/address/0xb568f9Bc0dcE39B9B64e843bC19DA102B5E3E939) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupDynamic | [`0x49d753422ff05daa291A9efa383E4f57daEAd889`](https://phoenix.lightlink.io/address/0x49d753422ff05daa291A9efa383E4f57daEAd889) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupLinear | [`0x17c4f98c40e69a6A0D5c42B11E3733f076A99E20`](https://phoenix.lightlink.io/address/0x17c4f98c40e69a6A0D5c42B11E3733f076A99E20) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2NFTDescriptor | [`0xda55fB3E53b7d205e37B6bdCe990b789255e4302`](https://phoenix.lightlink.io/address/0xda55fB3E53b7d205e37B6bdCe990b789255e4302) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2Batch | [`0x3eb9F8f80354a157315Fce64990C554434690c2f`](https://phoenix.lightlink.io/address/0x3eb9F8f80354a157315Fce64990C554434690c2f) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2MerkleStreamerFactory | [`0xdB07a1749D5Ca49909C7C4159652Fbd527c735B8`](https://phoenix.lightlink.io/address/0xdB07a1749D5Ca49909C7C4159652Fbd527c735B8) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | ### OP Mainnet ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2Comptroller | [`0x1EECb6e6EaE6a1eD1CCB4323F3a146A7C5443A10`](https://optimistic.etherscan.io/address/0x1EECb6e6EaE6a1eD1CCB4323F3a146A7C5443A10) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupDynamic | [`0xd6920c1094eABC4b71f3dC411A1566f64f4c206e`](https://optimistic.etherscan.io/address/0xd6920c1094eABC4b71f3dC411A1566f64f4c206e) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupLinear | [`0x4b45090152a5731b5bc71b5baF71E60e05B33867`](https://optimistic.etherscan.io/address/0x4b45090152a5731b5bc71b5baF71E60e05B33867) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2NFTDescriptor | [`0xF5050c04425E639C647F5ED632218b16ce96694d`](https://optimistic.etherscan.io/address/0xF5050c04425E639C647F5ED632218b16ce96694d) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2Batch | [`0x8145429538dDBdDc4099B2bAfd24DD8958fa03b8`](https://optimistic.etherscan.io/address/0x8145429538dDBdDc4099B2bAfd24DD8958fa03b8) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2MerkleStreamerFactory | [`0x044EC80FbeC40f0eE7E7b3856828170971796C19`](https://optimistic.etherscan.io/address/0x044EC80FbeC40f0eE7E7b3856828170971796C19) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | ### Polygon ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2Comptroller | [`0x9761692EDf10F5F2A69f0150e2fd50dcecf05F2E`](https://polygonscan.com/address/0x9761692EDf10F5F2A69f0150e2fd50dcecf05F2E) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupDynamic | [`0xB194c7278C627D52E440316b74C5F24FC70c1565`](https://polygonscan.com/address/0xB194c7278C627D52E440316b74C5F24FC70c1565) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupLinear | [`0x5f0e1dea4A635976ef51eC2a2ED41490d1eBa003`](https://polygonscan.com/address/0x5f0e1dea4A635976ef51eC2a2ED41490d1eBa003) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2NFTDescriptor | [`0x8683da9DF8c5c3528e8251a5764EC7DAc7264795`](https://polygonscan.com/address/0x8683da9DF8c5c3528e8251a5764EC7DAc7264795) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2Batch | [`0x5865C73789C4496665eDE1CAF018dc52ac248598`](https://polygonscan.com/address/0x5865C73789C4496665eDE1CAF018dc52ac248598) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2MerkleStreamerFactory | [`0xF4906225e783fb8977410BDBFb960caBed6C2EF4`](https://polygonscan.com/address/0xF4906225e783fb8977410BDBFb960caBed6C2EF4) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | ### Scroll ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2Comptroller | [`0x859708495E3B3c61Bbe19e6E3E1F41dE3A5C5C5b`](https://scrollscan.com/address/0x859708495E3B3c61Bbe19e6E3E1F41dE3A5C5C5b) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupDynamic | [`0xAaff2D11f9e7Cd2A9cDC674931fAC0358a165995`](https://scrollscan.com/address/0xAaff2D11f9e7Cd2A9cDC674931fAC0358a165995) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupLinear | [`0x57e14AB4DAd920548899d86B54AD47Ea27F00987`](https://scrollscan.com/address/0x57e14AB4DAd920548899d86B54AD47Ea27F00987) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2NFTDescriptor | [`0xB71440B85172332E8B768e85EdBfdb34CB457c1c`](https://scrollscan.com/address/0xB71440B85172332E8B768e85EdBfdb34CB457c1c) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2Batch | [`0xD18faa233E02d41EDFFdb64f20281dE0592FA3b5`](https://scrollscan.com/address/0xD18faa233E02d41EDFFdb64f20281dE0592FA3b5) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2MerkleStreamerFactory | [`0xb3ade5463000E6c0D376e7d7570f372eBf98BDAf`](https://scrollscan.com/address/0xb3ade5463000E6c0D376e7d7570f372eBf98BDAf) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | ### ZKsync Era ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2Comptroller | [`0xD05bdb4cF6Be7D647c5FEcC7952660bdD82cE44C`](https://explorer.zksync.io/address/0xD05bdb4cF6Be7D647c5FEcC7952660bdD82cE44C) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupDynamic | [`0xE6c7324BEA8474209103e407779Eec600c07cF3F`](https://explorer.zksync.io/address/0xE6c7324BEA8474209103e407779Eec600c07cF3F) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupLinear | [`0x2FcA69fa0a318EFDf4c15eE8F13A873347a8A8D4`](https://explorer.zksync.io/address/0x2FcA69fa0a318EFDf4c15eE8F13A873347a8A8D4) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2NFTDescriptor | [`0xf12d2B8ff4Fc0495Db9c6d16b6a03bff9a10657A`](https://explorer.zksync.io/address/0xf12d2B8ff4Fc0495Db9c6d16b6a03bff9a10657A) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2Batch | [`0x37A20Fb12DD6e0ADA47B327C0466A231dDc4504A`](https://explorer.zksync.io/address/0x37A20Fb12DD6e0ADA47B327C0466A231dDc4504A) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2MerkleStreamerFactory | [`0x46DE683D20c3575A0381fFd66C10Ab6836390140`](https://explorer.zksync.io/address/0x46DE683D20c3575A0381fFd66C10Ab6836390140) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | ## Testnets ### Arbitrum Sepolia ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2Comptroller | [`0xA6A0cfA3442053fbB516D55205A749Ef2D33aed9`](https://sepolia.arbiscan.io/address/0xA6A0cfA3442053fbB516D55205A749Ef2D33aed9) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupDynamic | [`0x8c8102b92B1f31cC304A085D490796f4DfdF7aF3`](https://sepolia.arbiscan.io/address/0x8c8102b92B1f31cC304A085D490796f4DfdF7aF3) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupLinear | [`0x483bdd560dE53DC20f72dC66ACdB622C5075de34`](https://sepolia.arbiscan.io/address/0x483bdd560dE53DC20f72dC66ACdB622C5075de34) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2NFTDescriptor | [`0x593050f0360518C3A4F11c32Eb936146e1096FD1`](https://sepolia.arbiscan.io/address/0x593050f0360518C3A4F11c32Eb936146e1096FD1) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2Batch | [`0x72D921E579aB7FC5D19CD398B6be24d626Ccb6e7`](https://sepolia.arbiscan.io/address/0x72D921E579aB7FC5D19CD398B6be24d626Ccb6e7) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2MerkleStreamerFactory | [`0xcc87b1A4de285832f226BD585bd54a2184D32105`](https://sepolia.arbiscan.io/address/0xcc87b1A4de285832f226BD585bd54a2184D32105) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | ### Base Sepolia ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2Comptroller | [`0x90b1C663314cFb55c8FF6f9a50a8D57a2D83a664`](https://sepolia.basescan.org/address/0x90b1C663314cFb55c8FF6f9a50a8D57a2D83a664) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupDynamic | [`0xF46d5fA9bFC964E8d06846c8739AEc69BC06344d`](https://sepolia.basescan.org/address/0xF46d5fA9bFC964E8d06846c8739AEc69BC06344d) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupLinear | [`0xbd7AAA2984c0a887E93c66baae222749883763d3`](https://sepolia.basescan.org/address/0xbd7AAA2984c0a887E93c66baae222749883763d3) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2NFTDescriptor | [`0xb2b4b1E69B16411AEBD30c8EA5aB395E13069160`](https://sepolia.basescan.org/address/0xb2b4b1E69B16411AEBD30c8EA5aB395E13069160) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2Batch | [`0xbD636B8EF09760aC91f6Df3c6AC5531250420200`](https://sepolia.basescan.org/address/0xbD636B8EF09760aC91f6Df3c6AC5531250420200) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2MerkleStreamerFactory | [`0xf632521bbAb0dBC2bEf169865e6c8e285AFe0a42`](https://sepolia.basescan.org/address/0xf632521bbAb0dBC2bEf169865e6c8e285AFe0a42) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | ### OP Sepolia ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2Comptroller | [`0x6587166c4F4E0b6203549463EbAB4dBeFA63fd8f`](https://optimism-sepolia.blockscout.com/address/0x6587166c4F4E0b6203549463EbAB4dBeFA63fd8f) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupDynamic | [`0xf9e4095C1dfC058B34135C5c48cae66a8D2b3Aa5`](https://optimism-sepolia.blockscout.com/address/0xf9e4095C1dfC058B34135C5c48cae66a8D2b3Aa5) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupLinear | [`0xe59D28bEF2D37E99b93E734ed1dDcFc4B9C1bf73`](https://optimism-sepolia.blockscout.com/address/0xe59D28bEF2D37E99b93E734ed1dDcFc4B9C1bf73) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2NFTDescriptor | [`0x3590f54c5d3d83BA68c17cF5C28DB89C5d1DfA10`](https://optimism-sepolia.blockscout.com/address/0x3590f54c5d3d83BA68c17cF5C28DB89C5d1DfA10) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2Batch | [`0x65D3A5b99372ef59E741EE768443dF884aB56E0b`](https://optimism-sepolia.blockscout.com/address/0x65D3A5b99372ef59E741EE768443dF884aB56E0b) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2MerkleStreamerFactory | [`0x9b6cC73522f22Ad3f2F8187e892A51b95f1A0E8a`](https://optimism-sepolia.blockscout.com/address/0x9b6cC73522f22Ad3f2F8187e892A51b95f1A0E8a) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | ### Sepolia ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2Comptroller | [`0x2006d43E65e66C5FF20254836E63947FA8bAaD68`](https://sepolia.etherscan.io/address/0x2006d43E65e66C5FF20254836E63947FA8bAaD68) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupDynamic | [`0xc9940AD8F43aAD8e8f33A4D5dbBf0a8F7FF4429A`](https://sepolia.etherscan.io/address/0xc9940AD8F43aAD8e8f33A4D5dbBf0a8F7FF4429A) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupLinear | [`0x7a43F8a888fa15e68C103E18b0439Eb1e98E4301`](https://sepolia.etherscan.io/address/0x7a43F8a888fa15e68C103E18b0439Eb1e98E4301) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2NFTDescriptor | [`0xE8fFEbA8963CD9302ffD39c704dc2c027128D36F`](https://sepolia.etherscan.io/address/0xE8fFEbA8963CD9302ffD39c704dc2c027128D36F) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2Batch | [`0xd2569DC4A58dfE85d807Dffb976dbC0a3bf0B0Fb`](https://sepolia.etherscan.io/address/0xd2569DC4A58dfE85d807Dffb976dbC0a3bf0B0Fb) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2MerkleStreamerFactory | [`0xBacC1d151A78eeD71D504f701c25E8739DC0262D`](https://sepolia.etherscan.io/address/0xBacC1d151A78eeD71D504f701c25E8739DC0262D) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | ### ZKsync Sepolia Testnet ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2Comptroller | [`0xEB4570723ae207a0473D73B3c2B255b0D5Ec9f01`](https://sepolia.explorer.zksync.io/address/0xEB4570723ae207a0473D73B3c2B255b0D5Ec9f01) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupDynamic | [`0xe101C69A6f9c071Ab79aEE0be56928565962F56d`](https://sepolia.explorer.zksync.io/address/0xe101C69A6f9c071Ab79aEE0be56928565962F56d) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2LockupLinear | [`0xdFC6F5D327dcF5DB579eC1b47fb260F93e042409`](https://sepolia.explorer.zksync.io/address/0xdFC6F5D327dcF5DB579eC1b47fb260F93e042409) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2NFTDescriptor | [`0xABF4a24519c9A3c68a354FD6d5D4429De0A0D36C`](https://sepolia.explorer.zksync.io/address/0xABF4a24519c9A3c68a354FD6d5D4429De0A0D36C) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2Batch | [`0x5F812F1332A2294149b9e1cBd216a5eED12cEbDD`](https://sepolia.explorer.zksync.io/address/0x5F812F1332A2294149b9e1cBd216a5eED12cEbDD) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | | SablierV2MerkleStreamerFactory | [`0xd9a834135c816FFd133a411a36219aAFD190fF97`](https://sepolia.explorer.zksync.io/address/0xd9a834135c816FFd133a411a36219aAFD190fF97) | [`lockup-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.1) | --- ## Lockup v1.2 Source: https://docs.sablier.com/guides/lockup/previous-deployments/v1.2 # Lockup v1.2 This section contains the deployment addresses for the v1.2 release of [@sablier/v2-core@1.2.0](https://npmjs.com/package/@sablier/v2-core/v/1.2.0) and [@sablier/v2-periphery@1.2.0](https://npmjs.com/package/@sablier/v2-periphery/v/1.2.0). A few noteworthy details about the deployments: - The addresses are final - All contracts are non-upgradeable - The source code is verified on Etherscan across all chains :::info This is an outdated version of the Lockup protocol. See the latest version [here](/guides/lockup/deployments). ::: ## Mainnets ### Ethereum ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2LockupDynamic | [`0x9DeaBf7815b42Bf4E9a03EEc35a486fF74ee7459`](https://etherscan.io/address/0x9DeaBf7815b42Bf4E9a03EEc35a486fF74ee7459) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupLinear | [`0x3962f6585946823440d274aD7C719B02b49DE51E`](https://etherscan.io/address/0x3962f6585946823440d274aD7C719B02b49DE51E) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupTranched | [`0xf86B359035208e4529686A1825F2D5BeE38c28A8`](https://etherscan.io/address/0xf86B359035208e4529686A1825F2D5BeE38c28A8) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2NFTDescriptor | [`0xAE32Ca14d85311A506Bb852D49bbfB315466bA26`](https://etherscan.io/address/0xAE32Ca14d85311A506Bb852D49bbfB315466bA26) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2BatchLockup | [`0xB5Ec9706C3Be9d22326D208f491E5DEef7C8d9f0`](https://etherscan.io/address/0xB5Ec9706C3Be9d22326D208f491E5DEef7C8d9f0) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2MerkleLockupFactory | [`0xF35aB407CF28012Ba57CAF5ee2f6d6E4420253bc`](https://etherscan.io/address/0xF35aB407CF28012Ba57CAF5ee2f6d6E4420253bc) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | ### Abstract ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2LockupDynamic | [`0xc69c06c030E825EDE13F1486078Aa9a2E2AAffaf`](https://abscan.org/address/0xc69c06c030E825EDE13F1486078Aa9a2E2AAffaf) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupLinear | [`0x7282d83E49363f373102d195F66649eBD6C57B9B`](https://abscan.org/address/0x7282d83E49363f373102d195F66649eBD6C57B9B) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupTranched | [`0x28fCAE6bda2546C93183EeC8638691B2EB184003`](https://abscan.org/address/0x28fCAE6bda2546C93183EeC8638691B2EB184003) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2NFTDescriptor | [`0xAc2E42b520364940c90Ce164412Ca9BA212d014B`](https://abscan.org/address/0xAc2E42b520364940c90Ce164412Ca9BA212d014B) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2BatchLockup | [`0x2F1eB117A87217E8bE9AA96795F69c9e380686Db`](https://abscan.org/address/0x2F1eB117A87217E8bE9AA96795F69c9e380686Db) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2MerkleLockupFactory | [`0xe2C0C3e0ff10Df4485a2dcbbdd1D002a40446164`](https://abscan.org/address/0xe2C0C3e0ff10Df4485a2dcbbdd1D002a40446164) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | ### Arbitrum ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2LockupDynamic | [`0x53F5eEB133B99C6e59108F35bCC7a116da50c5ce`](https://arbiscan.io/address/0x53F5eEB133B99C6e59108F35bCC7a116da50c5ce) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupLinear | [`0x05a323a4C936fed6D02134c5f0877215CD186b51`](https://arbiscan.io/address/0x05a323a4C936fed6D02134c5f0877215CD186b51) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupTranched | [`0x0dA2c7Aa93E7CD43e6b8D043Aab5b85CfDDf3818`](https://arbiscan.io/address/0x0dA2c7Aa93E7CD43e6b8D043Aab5b85CfDDf3818) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2NFTDescriptor | [`0xacA12cdC4DcD7063c82E69A358549ba082463608`](https://arbiscan.io/address/0xacA12cdC4DcD7063c82E69A358549ba082463608) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2BatchLockup | [`0x785Edf1e617824A78EFE76295E040B1AE06002bf`](https://arbiscan.io/address/0x785Edf1e617824A78EFE76295E040B1AE06002bf) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2MerkleLockupFactory | [`0xc9A5a0Bc2D8E217BDbdFE7486E9E72c5c3308F01`](https://arbiscan.io/address/0xc9A5a0Bc2D8E217BDbdFE7486E9E72c5c3308F01) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | ### Avalanche ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2LockupDynamic | [`0xE3826241E5EeBB3F5fEde33F9f677047674D3FBF`](https://snowscan.xyz/address/0xE3826241E5EeBB3F5fEde33F9f677047674D3FBF) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupLinear | [`0xc0bF14AfB95CA4C049BDc19E06a3531D8065F6Fd`](https://snowscan.xyz/address/0xc0bF14AfB95CA4C049BDc19E06a3531D8065F6Fd) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupTranched | [`0xfA536049652BFb5f57ba8DCFbec1B2b2Dd9803D3`](https://snowscan.xyz/address/0xfA536049652BFb5f57ba8DCFbec1B2b2Dd9803D3) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2NFTDescriptor | [`0xcF24fb2a09227d955F8e9A12f36A26cf1ac079c6`](https://snowscan.xyz/address/0xcF24fb2a09227d955F8e9A12f36A26cf1ac079c6) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2BatchLockup | [`0xaBCdF4dcDBa57a04889784A670b862540758f9E7`](https://snowscan.xyz/address/0xaBCdF4dcDBa57a04889784A670b862540758f9E7) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2MerkleLockupFactory | [`0x0430ed39EA2789AcdF27b89268117EBABc8176D1`](https://snowscan.xyz/address/0x0430ed39EA2789AcdF27b89268117EBABc8176D1) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | ### Base ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2LockupDynamic | [`0xF9E9eD67DD2Fab3b3ca024A2d66Fcf0764d36742`](https://basescan.org/address/0xF9E9eD67DD2Fab3b3ca024A2d66Fcf0764d36742) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupLinear | [`0x4CB16D4153123A74Bc724d161050959754f378D8`](https://basescan.org/address/0x4CB16D4153123A74Bc724d161050959754f378D8) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupTranched | [`0xf4937657Ed8B3f3cB379Eed47b8818eE947BEb1e`](https://basescan.org/address/0xf4937657Ed8B3f3cB379Eed47b8818eE947BEb1e) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2NFTDescriptor | [`0x0fF9d05E6331A43A906fE1440E0C9D0742F475A3`](https://basescan.org/address/0x0fF9d05E6331A43A906fE1440E0C9D0742F475A3) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2BatchLockup | [`0xc1c548F980669615772dadcBfEBC29937c29481A`](https://basescan.org/address/0xc1c548F980669615772dadcBfEBC29937c29481A) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2MerkleLockupFactory | [`0x58A51E5382318EeA6065BB7721eecdF4331c0B90`](https://basescan.org/address/0x58A51E5382318EeA6065BB7721eecdF4331c0B90) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | ### Blast ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2LockupDynamic | [`0xA705DE617673e2Fe63a4Ea0E58c26897601D32A5`](https://blastscan.io/address/0xA705DE617673e2Fe63a4Ea0E58c26897601D32A5) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupLinear | [`0x9b1468d29b4A5869f00c92517c57f8656E928B93`](https://blastscan.io/address/0x9b1468d29b4A5869f00c92517c57f8656E928B93) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupTranched | [`0x91FB72e5297e2728c10FDe73BdE74A4888A68570`](https://blastscan.io/address/0x91FB72e5297e2728c10FDe73BdE74A4888A68570) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2NFTDescriptor | [`0x5f111b49f8f8bdb4A6001701E0D330fF52D6B370`](https://blastscan.io/address/0x5f111b49f8f8bdb4A6001701E0D330fF52D6B370) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2BatchLockup | [`0xdc988d7AD6F186ea4a236f3E61A45a7851edF84E`](https://blastscan.io/address/0xdc988d7AD6F186ea4a236f3E61A45a7851edF84E) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2MerkleLockupFactory | [`0x3aBCDDa756d069Cf3c7a17410602343966EAFf27`](https://blastscan.io/address/0x3aBCDDa756d069Cf3c7a17410602343966EAFf27) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | ### BNB Chain ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2LockupDynamic | [`0xeB6d84c585bf8AEA34F05a096D6fAA3b8477D146`](https://bscscan.com/address/0xeB6d84c585bf8AEA34F05a096D6fAA3b8477D146) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupLinear | [`0x88ad3B5c62A46Df953A5d428d33D70408F53C408`](https://bscscan.com/address/0x88ad3B5c62A46Df953A5d428d33D70408F53C408) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupTranched | [`0xAb5f007b33EDDA56962A0fC428B15D544EA46591`](https://bscscan.com/address/0xAb5f007b33EDDA56962A0fC428B15D544EA46591) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2NFTDescriptor | [`0x27641f29b012d0d523EB5943011148c42c98e7F1`](https://bscscan.com/address/0x27641f29b012d0d523EB5943011148c42c98e7F1) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2BatchLockup | [`0x70998557980CB6E8E63c46810081262B6c343051`](https://bscscan.com/address/0x70998557980CB6E8E63c46810081262B6c343051) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2MerkleLockupFactory | [`0x96Aa12809CAC29Bba4944fEca1dFDC8e1704e6c1`](https://bscscan.com/address/0x96Aa12809CAC29Bba4944fEca1dFDC8e1704e6c1) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | ### Chiliz ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2LockupDynamic | [`0xCff4a803b0Bf55dD1BE38Fb96088478F3D2eeCF2`](https://chiliscan.com/address/0xCff4a803b0Bf55dD1BE38Fb96088478F3D2eeCF2) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupLinear | [`0xDf578C2c70A86945999c65961417057363530a1c`](https://chiliscan.com/address/0xDf578C2c70A86945999c65961417057363530a1c) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupTranched | [`0xcb099EfC90e88690e287259410B9AE63e1658CC6`](https://chiliscan.com/address/0xcb099EfC90e88690e287259410B9AE63e1658CC6) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2NFTDescriptor | [`0x2De92156000269fa2fde7544F10f01E8cBC80fFa`](https://chiliscan.com/address/0x2De92156000269fa2fde7544F10f01E8cBC80fFa) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2BatchLockup | [`0x0eDA15D606733f6CDe9DB67263E546bfcDDe9264`](https://chiliscan.com/address/0x0eDA15D606733f6CDe9DB67263E546bfcDDe9264) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2MerkleLockupFactory | [`0x92FC05e49c27884d554D98a5C01Ff0894a9DC29a`](https://chiliscan.com/address/0x92FC05e49c27884d554D98a5C01Ff0894a9DC29a) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | ### Core Dao ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2LockupDynamic | [`0xf0a7F2cCE911c298B5CB8106Db19EF1D00230710`](https://scan.coredao.org/address/0xf0a7F2cCE911c298B5CB8106Db19EF1D00230710) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupLinear | [`0x98Fe0d8b2c2c05d9C6a9e635f59474Aaa0000120`](https://scan.coredao.org/address/0x98Fe0d8b2c2c05d9C6a9e635f59474Aaa0000120) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupTranched | [`0x9C99EF88399bC1c1188399B39E7Cc667D78210ea`](https://scan.coredao.org/address/0x9C99EF88399bC1c1188399B39E7Cc667D78210ea) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2NFTDescriptor | [`0x64C734B2F1704822D8E69CAF230aE8d2eC18AA3e`](https://scan.coredao.org/address/0x64C734B2F1704822D8E69CAF230aE8d2eC18AA3e) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2BatchLockup | [`0xdE21BBFf718723E9069d8528d6Bb26c2971D58a7`](https://scan.coredao.org/address/0xdE21BBFf718723E9069d8528d6Bb26c2971D58a7) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2MerkleLockupFactory | [`0x074CC814a8114126c505F5eecFC82A400B39cA03`](https://scan.coredao.org/address/0x074CC814a8114126c505F5eecFC82A400B39cA03) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | ### Gnosis ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2LockupDynamic | [`0x555eb55cbc477Aebbe5652D25d0fEA04052d3971`](https://gnosisscan.io/address/0x555eb55cbc477Aebbe5652D25d0fEA04052d3971) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupLinear | [`0xf1cAeB104AB29271463259335357D57772C90758`](https://gnosisscan.io/address/0xf1cAeB104AB29271463259335357D57772C90758) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupTranched | [`0x59A4B7255A5D01247837600e7828A6F77f664b34`](https://gnosisscan.io/address/0x59A4B7255A5D01247837600e7828A6F77f664b34) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2NFTDescriptor | [`0xA0B5C851E3E9fED83f387f4D8847DA398Da4A8E2`](https://gnosisscan.io/address/0xA0B5C851E3E9fED83f387f4D8847DA398Da4A8E2) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2BatchLockup | [`0x0F324E5CB01ac98b2883c8ac4231aCA7EfD3e750`](https://gnosisscan.io/address/0x0F324E5CB01ac98b2883c8ac4231aCA7EfD3e750) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2MerkleLockupFactory | [`0x5f12318fc6cCa518A950e2Ee16063a6317C2a9Ef`](https://gnosisscan.io/address/0x5f12318fc6cCa518A950e2Ee16063a6317C2a9Ef) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | ### Lightlink ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2LockupDynamic | [`0xAa05E418Fb7851C211351C65435F1b17cbFa88Bf`](https://phoenix.lightlink.io/address/0xAa05E418Fb7851C211351C65435F1b17cbFa88Bf) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupLinear | [`0x6329591464FA6721c8E1c1271e4c6C41531Aea6b`](https://phoenix.lightlink.io/address/0x6329591464FA6721c8E1c1271e4c6C41531Aea6b) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupTranched | [`0x83403c6426E6D044bF3B84EC1C007Db211AaA140`](https://phoenix.lightlink.io/address/0x83403c6426E6D044bF3B84EC1C007Db211AaA140) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2NFTDescriptor | [`0x5881ef3c0D3eB21b1b40E13b4a69c50754bc77C7`](https://phoenix.lightlink.io/address/0x5881ef3c0D3eB21b1b40E13b4a69c50754bc77C7) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2BatchLockup | [`0x5C847244649BD74aB41f09C893aF792AD87D32aA`](https://phoenix.lightlink.io/address/0x5C847244649BD74aB41f09C893aF792AD87D32aA) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2MerkleLockupFactory | [`0x278AC15622846806BD46FBDbdB8dB8d09614173A`](https://phoenix.lightlink.io/address/0x278AC15622846806BD46FBDbdB8dB8d09614173A) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | ### Linea Mainnet ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2LockupDynamic | [`0xF2E46B249cFe09c2b3A2022dc81E0bB4bE3336F1`](https://lineascan.build/address/0xF2E46B249cFe09c2b3A2022dc81E0bB4bE3336F1) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupLinear | [`0xB5d39049510F47EE7f74c528105D225E42747d63`](https://lineascan.build/address/0xB5d39049510F47EE7f74c528105D225E42747d63) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupTranched | [`0xC46ce4B77cBc46D17A2EceB2Cc8e2EE23D96529F`](https://lineascan.build/address/0xC46ce4B77cBc46D17A2EceB2Cc8e2EE23D96529F) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2NFTDescriptor | [`0x2E72F7523cFeaed6B841aCe20060E0b203c312F5`](https://lineascan.build/address/0x2E72F7523cFeaed6B841aCe20060E0b203c312F5) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2BatchLockup | [`0x4259557F6665eCF5907c9019a30f3Cb009c20Ae7`](https://lineascan.build/address/0x4259557F6665eCF5907c9019a30f3Cb009c20Ae7) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2MerkleLockupFactory | [`0x35E9C3445A039B258Eb7112A5Eea259a825E8AC0`](https://lineascan.build/address/0x35E9C3445A039B258Eb7112A5Eea259a825E8AC0) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | ### Mode ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2LockupDynamic | [`0x704552099f5aD679294D337638B9a57Fd4726F52`](https://modescan.io/address/0x704552099f5aD679294D337638B9a57Fd4726F52) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupLinear | [`0xD8C65Bd7CB6924EF895b2eDcA03407c652f5a2C5`](https://modescan.io/address/0xD8C65Bd7CB6924EF895b2eDcA03407c652f5a2C5) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupTranched | [`0xBbfA51A10bE68714fa33281646B986dae9f52021`](https://modescan.io/address/0xBbfA51A10bE68714fa33281646B986dae9f52021) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2NFTDescriptor | [`0xA1976d4bd6572B68A677037B496D806ACC2cBdB3`](https://modescan.io/address/0xA1976d4bd6572B68A677037B496D806ACC2cBdB3) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2BatchLockup | [`0x641A10A2c9e0CeB94F406e1EF68b1E1da002662d`](https://modescan.io/address/0x641A10A2c9e0CeB94F406e1EF68b1E1da002662d) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2MerkleLockupFactory | [`0x0Fd01Dd30F96A15dE6AfAd5627d45Ef94752460a`](https://modescan.io/address/0x0Fd01Dd30F96A15dE6AfAd5627d45Ef94752460a) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | ### Morph ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2LockupDynamic | [`0x946654AB30Dd6eD10236C89f2C8B2719df653691`](https://explorer.morphl2.io/address/0x946654AB30Dd6eD10236C89f2C8B2719df653691) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupLinear | [`0xAC19F4181E58efb7094e0cb4e1BB18c79F6AAdf4`](https://explorer.morphl2.io/address/0xAC19F4181E58efb7094e0cb4e1BB18c79F6AAdf4) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupTranched | [`0x63B92F7E2f69877184C955E63B9D8Dff55e52e14`](https://explorer.morphl2.io/address/0x63B92F7E2f69877184C955E63B9D8Dff55e52e14) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2NFTDescriptor | [`0xe785101Cb228693cc3EFdCd5d637fEf6A6Ff7259`](https://explorer.morphl2.io/address/0xe785101Cb228693cc3EFdCd5d637fEf6A6Ff7259) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2BatchLockup | [`0x28D116d7e917756310986C4207eA54183fcba06A`](https://explorer.morphl2.io/address/0x28D116d7e917756310986C4207eA54183fcba06A) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2MerkleLockupFactory | [`0x5e73bb96493C10919204045fCdb639D35ad859f8`](https://explorer.morphl2.io/address/0x5e73bb96493C10919204045fCdb639D35ad859f8) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | ### OP Mainnet ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2LockupDynamic | [`0x4994325F8D4B4A36Bd643128BEb3EC3e582192C0`](https://optimistic.etherscan.io/address/0x4994325F8D4B4A36Bd643128BEb3EC3e582192C0) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupLinear | [`0x5C22471A86E9558ed9d22235dD5E0429207ccf4B`](https://optimistic.etherscan.io/address/0x5C22471A86E9558ed9d22235dD5E0429207ccf4B) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupTranched | [`0x90952912a50079bef00D5F49c975058d6573aCdC`](https://optimistic.etherscan.io/address/0x90952912a50079bef00D5F49c975058d6573aCdC) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2NFTDescriptor | [`0x1a4837b8c668b8F7BE22Ba156419b7b823Cfd05c`](https://optimistic.etherscan.io/address/0x1a4837b8c668b8F7BE22Ba156419b7b823Cfd05c) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2BatchLockup | [`0x6cd7bB0f63aFCc9F6CeDd1Bf1E3Bd4ED078CD019`](https://optimistic.etherscan.io/address/0x6cd7bB0f63aFCc9F6CeDd1Bf1E3Bd4ED078CD019) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2MerkleLockupFactory | [`0xe041629D99730b3EE4d6518097C45b4E3591992b`](https://optimistic.etherscan.io/address/0xe041629D99730b3EE4d6518097C45b4E3591992b) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | ### Polygon ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2LockupDynamic | [`0x8D4dDc187a73017a5d7Cef733841f55115B13762`](https://polygonscan.com/address/0x8D4dDc187a73017a5d7Cef733841f55115B13762) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupLinear | [`0x8D87c5eddb5644D1a714F85930Ca940166e465f0`](https://polygonscan.com/address/0x8D87c5eddb5644D1a714F85930Ca940166e465f0) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupTranched | [`0xBF67f0A1E847564D0eFAD475782236D3Fa7e9Ec2`](https://polygonscan.com/address/0xBF67f0A1E847564D0eFAD475782236D3Fa7e9Ec2) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2NFTDescriptor | [`0xf28BF9390fb57BB68386430550818D312699ED15`](https://polygonscan.com/address/0xf28BF9390fb57BB68386430550818D312699ED15) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2BatchLockup | [`0xD29EC4B9203f2d1C9Cd4Ba8c68FCFE4ECd85f6f5`](https://polygonscan.com/address/0xD29EC4B9203f2d1C9Cd4Ba8c68FCFE4ECd85f6f5) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2MerkleLockupFactory | [`0xC28872e0c1f3633EeD467907123727ac0155029D`](https://polygonscan.com/address/0xC28872e0c1f3633EeD467907123727ac0155029D) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | ### Scroll ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2LockupDynamic | [`0xAc199bFea92aa4D4C3d8A49fd463EAD99C7a6A8f`](https://scrollscan.com/address/0xAc199bFea92aa4D4C3d8A49fd463EAD99C7a6A8f) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupLinear | [`0xBc5DC6D77612E636DA32af0d85Ca3179a57330fd`](https://scrollscan.com/address/0xBc5DC6D77612E636DA32af0d85Ca3179a57330fd) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupTranched | [`0xb0f78dDc01D829d8b567821Eb193De8082b57D9D`](https://scrollscan.com/address/0xb0f78dDc01D829d8b567821Eb193De8082b57D9D) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2NFTDescriptor | [`0xA1A281BbcaED8f0A9Dcd0fe67cbC53e0993C24cb`](https://scrollscan.com/address/0xA1A281BbcaED8f0A9Dcd0fe67cbC53e0993C24cb) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2BatchLockup | [`0x4B8BF9cD3274517609e7Fe905740fa151C9aa711`](https://scrollscan.com/address/0x4B8BF9cD3274517609e7Fe905740fa151C9aa711) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2MerkleLockupFactory | [`0x344afe8ad5dBA3d55870dc398e0F53B635B2ed0d`](https://scrollscan.com/address/0x344afe8ad5dBA3d55870dc398e0F53B635B2ed0d) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | ### Superseed ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2LockupDynamic | [`0x1fA500262b352d821B4e1c933A20f2242B45383d`](https://explorer.superseed.xyz/address/0x1fA500262b352d821B4e1c933A20f2242B45383d) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupLinear | [`0x251FC799344151026d19b959B8f3667416d56B88`](https://explorer.superseed.xyz/address/0x251FC799344151026d19b959B8f3667416d56B88) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupTranched | [`0x91211E1760280d3f7dF2182ce4D1Fd6A1735C202`](https://explorer.superseed.xyz/address/0x91211E1760280d3f7dF2182ce4D1Fd6A1735C202) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2NFTDescriptor | [`0x0a6C2E6B61cf05800F9aA91494480440843d6c3c`](https://explorer.superseed.xyz/address/0x0a6C2E6B61cf05800F9aA91494480440843d6c3c) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2BatchLockup | [`0xc4DE6f667435d5Ce0150e08BcEc9722C9017e90b`](https://explorer.superseed.xyz/address/0xc4DE6f667435d5Ce0150e08BcEc9722C9017e90b) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2MerkleLockupFactory | [`0xF60bEADEfbeb98C927E13C4165BCa7D85Ba32cB2`](https://explorer.superseed.xyz/address/0xF60bEADEfbeb98C927E13C4165BCa7D85Ba32cB2) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | ### Taiko ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2LockupDynamic | [`0x238C830FA8E4ED0f0A4bc9C986BF338aEC9e38D1`](https://taikoscan.io/address/0x238C830FA8E4ED0f0A4bc9C986BF338aEC9e38D1) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupLinear | [`0x5Ec0a2e88dAd09ad940Be2639c9aDb24D186989E`](https://taikoscan.io/address/0x5Ec0a2e88dAd09ad940Be2639c9aDb24D186989E) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupTranched | [`0x6a619d35972578E8458E33B7d1e07b155A51585E`](https://taikoscan.io/address/0x6a619d35972578E8458E33B7d1e07b155A51585E) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2NFTDescriptor | [`0xBFD6048C80665792d949692CE77307e55dbb8986`](https://taikoscan.io/address/0xBFD6048C80665792d949692CE77307e55dbb8986) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2BatchLockup | [`0x65E2C9990d4CAc5E54E65c1BD625CdcC9FDd1292`](https://taikoscan.io/address/0x65E2C9990d4CAc5E54E65c1BD625CdcC9FDd1292) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2MerkleLockupFactory | [`0xd7df0b795756b60ab51a37e26f1edb7ef9e78828`](https://taikoscan.io/address/0xd7df0b795756b60ab51a37e26f1edb7ef9e78828) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | ### Tangle ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2LockupDynamic | [`0x946654AB30Dd6eD10236C89f2C8B2719df653691`](https://explorer.tangle.tools/address/0x946654AB30Dd6eD10236C89f2C8B2719df653691) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupLinear | [`0xAC19F4181E58efb7094e0cb4e1BB18c79F6AAdf4`](https://explorer.tangle.tools/address/0xAC19F4181E58efb7094e0cb4e1BB18c79F6AAdf4) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupTranched | [`0x63B92F7E2f69877184C955E63B9D8Dff55e52e14`](https://explorer.tangle.tools/address/0x63B92F7E2f69877184C955E63B9D8Dff55e52e14) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2NFTDescriptor | [`0xe785101Cb228693cc3EFdCd5d637fEf6A6Ff7259`](https://explorer.tangle.tools/address/0xe785101Cb228693cc3EFdCd5d637fEf6A6Ff7259) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2BatchLockup | [`0x28D116d7e917756310986C4207eA54183fcba06A`](https://explorer.tangle.tools/address/0x28D116d7e917756310986C4207eA54183fcba06A) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2MerkleLockupFactory | [`0x5e73bb96493C10919204045fCdb639D35ad859f8`](https://explorer.tangle.tools/address/0x5e73bb96493C10919204045fCdb639D35ad859f8) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | ### ZKsync Era ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2LockupDynamic | [`0xf03f4Bf48b108360bAf1597Fb8053Ebe0F5245dA`](https://explorer.zksync.io/address/0xf03f4Bf48b108360bAf1597Fb8053Ebe0F5245dA) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupLinear | [`0x8cB69b514E97a904743922e1adf3D1627deeeE8D`](https://explorer.zksync.io/address/0x8cB69b514E97a904743922e1adf3D1627deeeE8D) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupTranched | [`0x1fB145A47Eb9b8bf565273e137356376197b3559`](https://explorer.zksync.io/address/0x1fB145A47Eb9b8bf565273e137356376197b3559) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2NFTDescriptor | [`0x99BA0D464942e7166dEBb8BAaAF1192F8d4117eb`](https://explorer.zksync.io/address/0x99BA0D464942e7166dEBb8BAaAF1192F8d4117eb) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2BatchLockup | [`0xAE1A55205A0499d6BBb0Cf0f1210641957e9cb7e`](https://explorer.zksync.io/address/0xAE1A55205A0499d6BBb0Cf0f1210641957e9cb7e) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2MerkleLockupFactory | [`0x8a84fCF962163A7E98Bf0daFD918973c846fa5C8`](https://explorer.zksync.io/address/0x8a84fCF962163A7E98Bf0daFD918973c846fa5C8) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | ## Testnets ### Arbitrum Sepolia ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2LockupDynamic | [`0x8127E8081C22807c8a786Af1e1b174939577144A`](https://sepolia.arbiscan.io/address/0x8127E8081C22807c8a786Af1e1b174939577144A) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupLinear | [`0x9D1C257d9bc09E6E6B8E7e7c2496C12000f55457`](https://sepolia.arbiscan.io/address/0x9D1C257d9bc09E6E6B8E7e7c2496C12000f55457) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupTranched | [`0xaff2efFCF38Ea4A92E0cC5D7c48456C53358fE1a`](https://sepolia.arbiscan.io/address/0xaff2efFCF38Ea4A92E0cC5D7c48456C53358fE1a) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2NFTDescriptor | [`0x46AEd4FE32aE1306d8073FE54A4E844e10a3ca16`](https://sepolia.arbiscan.io/address/0x46AEd4FE32aE1306d8073FE54A4E844e10a3ca16) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2BatchLockup | [`0xC1FD380b3B0fF989C259D0b45B97F9663B638aA4`](https://sepolia.arbiscan.io/address/0xC1FD380b3B0fF989C259D0b45B97F9663B638aA4) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2MerkleLockupFactory | [`0xa11561F9e418f2C431B411E1CA22FD3F85D4c831`](https://sepolia.arbiscan.io/address/0xa11561F9e418f2C431B411E1CA22FD3F85D4c831) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | ### Base Sepolia ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2LockupDynamic | [`0x6DCB73E5F7e8e70bE20b3B9CF50E3be4625A91C3`](https://sepolia.basescan.org/address/0x6DCB73E5F7e8e70bE20b3B9CF50E3be4625A91C3) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupLinear | [`0xFE7fc0Bbde84C239C0aB89111D617dC7cc58049f`](https://sepolia.basescan.org/address/0xFE7fc0Bbde84C239C0aB89111D617dC7cc58049f) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupTranched | [`0xb8c724df3eC8f2Bf8fA808dF2cB5dbab22f3E68c`](https://sepolia.basescan.org/address/0xb8c724df3eC8f2Bf8fA808dF2cB5dbab22f3E68c) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2NFTDescriptor | [`0x474dFf3Cdd6489523947bf08D538F56d07Ca699e`](https://sepolia.basescan.org/address/0x474dFf3Cdd6489523947bf08D538F56d07Ca699e) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2BatchLockup | [`0x23d0B7691F4Ca0E5477132a7C7F54fdCEd1814B9`](https://sepolia.basescan.org/address/0x23d0B7691F4Ca0E5477132a7C7F54fdCEd1814B9) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2MerkleLockupFactory | [`0x899a05feb160fe912f621733A1d0b39C1446B3eB`](https://sepolia.basescan.org/address/0x899a05feb160fe912f621733A1d0b39C1446B3eB) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | ### Linea Sepolia ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2LockupDynamic | [`0x95D29708be647BDD8dA0bdF82B84eB5f42d45918`](https://sepolia.lineascan.build/address/0x95D29708be647BDD8dA0bdF82B84eB5f42d45918) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupLinear | [`0x435F33C21B9Ea8BF207785616Bb28C46eDeD7366`](https://sepolia.lineascan.build/address/0x435F33C21B9Ea8BF207785616Bb28C46eDeD7366) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupTranched | [`0x5A52E9F4dFcdBcd68E50386D484378718167aB60`](https://sepolia.lineascan.build/address/0x5A52E9F4dFcdBcd68E50386D484378718167aB60) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2NFTDescriptor | [`0x237f114a9cF62b87383684529d889DdfEd917f0c`](https://sepolia.lineascan.build/address/0x237f114a9cF62b87383684529d889DdfEd917f0c) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2BatchLockup | [`0x8224eb5D7d76B2D7Df43b868D875E79B11500eA8`](https://sepolia.lineascan.build/address/0x8224eb5D7d76B2D7Df43b868D875E79B11500eA8) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2MerkleLockupFactory | [`0x83Dd52FCA44E069020b58155b761A590F12B59d3`](https://sepolia.lineascan.build/address/0x83Dd52FCA44E069020b58155b761A590F12B59d3) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | ### OP Sepolia ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2LockupDynamic | [`0x89EC3830040dec63E9dF0C904d649fda4d49DF16`](https://optimism-sepolia.blockscout.com/address/0x89EC3830040dec63E9dF0C904d649fda4d49DF16) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupLinear | [`0x0a881bbd71a21710D56Ff1931EC8189d94019D60`](https://optimism-sepolia.blockscout.com/address/0x0a881bbd71a21710D56Ff1931EC8189d94019D60) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupTranched | [`0xb971A93608413C54F407eE86C7c15b295E0004bB`](https://optimism-sepolia.blockscout.com/address/0xb971A93608413C54F407eE86C7c15b295E0004bB) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2NFTDescriptor | [`0x48F8C05C721E27FA82aD6c8ddB1a88eF43864A9A`](https://optimism-sepolia.blockscout.com/address/0x48F8C05C721E27FA82aD6c8ddB1a88eF43864A9A) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2BatchLockup | [`0xd9dD971D4800100aED0BfF3535aB116D4Be5c420`](https://optimism-sepolia.blockscout.com/address/0xd9dD971D4800100aED0BfF3535aB116D4Be5c420) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2MerkleLockupFactory | [`0x6CBe6e298A9354306e6ee65f63FF85CFA7062a39`](https://optimism-sepolia.blockscout.com/address/0x6CBe6e298A9354306e6ee65f63FF85CFA7062a39) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | ### Sepolia ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2LockupDynamic | [`0x73BB6dD3f5828d60F8b3dBc8798EB10fbA2c5636`](https://sepolia.etherscan.io/address/0x73BB6dD3f5828d60F8b3dBc8798EB10fbA2c5636) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupLinear | [`0x3E435560fd0a03ddF70694b35b673C25c65aBB6C`](https://sepolia.etherscan.io/address/0x3E435560fd0a03ddF70694b35b673C25c65aBB6C) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupTranched | [`0x3a1beA13A8C24c0EA2b8fAE91E4b2762A59D7aF5`](https://sepolia.etherscan.io/address/0x3a1beA13A8C24c0EA2b8fAE91E4b2762A59D7aF5) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2NFTDescriptor | [`0x56F2f7f4d15d1A9FF9d3782b6F6bB8f6fd690D33`](https://sepolia.etherscan.io/address/0x56F2f7f4d15d1A9FF9d3782b6F6bB8f6fd690D33) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2BatchLockup | [`0x04A9c14b7a000640419aD5515Db4eF4172C00E31`](https://sepolia.etherscan.io/address/0x04A9c14b7a000640419aD5515Db4eF4172C00E31) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2MerkleLockupFactory | [`0x56E9180A8d2C35c99F2F8a1A5Ab8aBe79E876E8c`](https://sepolia.etherscan.io/address/0x56E9180A8d2C35c99F2F8a1A5Ab8aBe79E876E8c) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | ### Superseed Sepolia ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2LockupDynamic | [`0xCff4a803b0Bf55dD1BE38Fb96088478F3D2eeCF2`](https://sepolia-explorer.superseed.xyz/address/0xCff4a803b0Bf55dD1BE38Fb96088478F3D2eeCF2) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupLinear | [`0xDf578C2c70A86945999c65961417057363530a1c`](https://sepolia-explorer.superseed.xyz/address/0xDf578C2c70A86945999c65961417057363530a1c) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupTranched | [`0xcb099EfC90e88690e287259410B9AE63e1658CC6`](https://sepolia-explorer.superseed.xyz/address/0xcb099EfC90e88690e287259410B9AE63e1658CC6) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2NFTDescriptor | [`0x2De92156000269fa2fde7544F10f01E8cBC80fFa`](https://sepolia-explorer.superseed.xyz/address/0x2De92156000269fa2fde7544F10f01E8cBC80fFa) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2BatchLockup | [`0x0eDA15D606733f6CDe9DB67263E546bfcDDe9264`](https://sepolia-explorer.superseed.xyz/address/0x0eDA15D606733f6CDe9DB67263E546bfcDDe9264) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2MerkleLockupFactory | [`0x92FC05e49c27884d554D98a5C01Ff0894a9DC29a`](https://sepolia-explorer.superseed.xyz/address/0x92FC05e49c27884d554D98a5C01Ff0894a9DC29a) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | ### ZKsync Sepolia Testnet ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierV2LockupDynamic | [`0xc4311a5913953162111bF75530f7BB14ec24e014`](https://sepolia.explorer.zksync.io/address/0xc4311a5913953162111bF75530f7BB14ec24e014) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupLinear | [`0x43864C567b89FA5fEE8010f92d4473Bf19169BBA`](https://sepolia.explorer.zksync.io/address/0x43864C567b89FA5fEE8010f92d4473Bf19169BBA) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2LockupTranched | [`0xF6e869b73E20b812dcf0E850AA8822F74f67f670`](https://sepolia.explorer.zksync.io/address/0xF6e869b73E20b812dcf0E850AA8822F74f67f670) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2NFTDescriptor | [`0x477DDC91a7e13CBaC01c06737abF96d50ECa7961`](https://sepolia.explorer.zksync.io/address/0x477DDC91a7e13CBaC01c06737abF96d50ECa7961) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2BatchLockup | [`0x1D68417ff71855Eb0237Ff03a8FfF02Ef67e4AFb`](https://sepolia.explorer.zksync.io/address/0x1D68417ff71855Eb0237Ff03a8FfF02Ef67e4AFb) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | | SablierV2MerkleLockupFactory | [`0x2CEf8C06dDF7a1440Ad2561c53821e43adDbfA31`](https://sepolia.explorer.zksync.io/address/0x2CEf8C06dDF7a1440Ad2561c53821e43adDbfA31) | [`lockup-v1.2`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v1.2) | --- ## Lockup v2.0 Source: https://docs.sablier.com/guides/lockup/previous-deployments/v2.0 # Lockup v2.0 This section contains the deployment addresses for the v2.0 release of [@sablier/lockup@2.0.1](https://npmjs.com/package/@sablier/lockup/v/2.0.1). A few noteworthy details about the deployments: - The addresses are final - All contracts are non-upgradeable - The source code is verified on Etherscan across all chains :::info This is an outdated version of the Lockup protocol. See the latest version [here](/guides/lockup/deployments). ::: ## Mainnets ### Ethereum ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x7C01AA3783577E15fD7e272443D44B92d5b21056`](https://etherscan.io/address/0x7C01AA3783577E15fD7e272443D44B92d5b21056) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0x3F6E8a8Cffe377c4649aCeB01e6F20c60fAA356c`](https://etherscan.io/address/0x3F6E8a8Cffe377c4649aCeB01e6F20c60fAA356c) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc`](https://etherscan.io/address/0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0xA9dC6878C979B5cc1d98a1803F0664ad725A1f56`](https://etherscan.io/address/0xA9dC6878C979B5cc1d98a1803F0664ad725A1f56) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d`](https://etherscan.io/address/0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | ### Abstract ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x14Eb4AB47B2ec2a71763eaBa202a252E176FAE88`](https://abscan.org/address/0x14Eb4AB47B2ec2a71763eaBa202a252E176FAE88) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0x3409308357BB704f79f70d748da502F363Dc2f1D`](https://abscan.org/address/0x3409308357BB704f79f70d748da502F363Dc2f1D) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0x07c194dFE7DCe9Ae7Ffe4bF32683cf1F8CDD4aEa`](https://abscan.org/address/0x07c194dFE7DCe9Ae7Ffe4bF32683cf1F8CDD4aEa) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0x63Ff2E370788C163D5a1909B5FCb299DB327AEF9`](https://abscan.org/address/0x63Ff2E370788C163D5a1909B5FCb299DB327AEF9) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0xbB2e2884AE40003BB55fd3A85A9f8f7f72Aa441F`](https://abscan.org/address/0xbB2e2884AE40003BB55fd3A85A9f8f7f72Aa441F) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | ### Arbitrum ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x467D5Bf8Cfa1a5f99328fBdCb9C751c78934b725`](https://arbiscan.io/address/0x467D5Bf8Cfa1a5f99328fBdCb9C751c78934b725) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0xB11Ead48F572155C5F8dB6201701e91A936896f7`](https://arbiscan.io/address/0xB11Ead48F572155C5F8dB6201701e91A936896f7) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc`](https://arbiscan.io/address/0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0xd5c6a0Dd2E1822865c308850b8b3E2CcE762D061`](https://arbiscan.io/address/0xd5c6a0Dd2E1822865c308850b8b3E2CcE762D061) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d`](https://arbiscan.io/address/0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | ### Avalanche ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x3C81BBBe72EF8eF3fb1D19B0bd6310Ad0dd27E82`](https://snowscan.xyz/address/0x3C81BBBe72EF8eF3fb1D19B0bd6310Ad0dd27E82) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0xABDE228d84D86D78029C31A37Ae2435C8f923c8b`](https://snowscan.xyz/address/0xABDE228d84D86D78029C31A37Ae2435C8f923c8b) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc`](https://snowscan.xyz/address/0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0x906A4BD5dD0EF13654eA29bFD6185d0d64A4b674`](https://snowscan.xyz/address/0x906A4BD5dD0EF13654eA29bFD6185d0d64A4b674) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d`](https://snowscan.xyz/address/0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | ### Base ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xb5D78DD3276325f5FAF3106Cc4Acc56E28e0Fe3B`](https://basescan.org/address/0xb5D78DD3276325f5FAF3106Cc4Acc56E28e0Fe3B) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0xC26CdAFd6ec3c91AD9aEeB237Ee1f37205ED26a4`](https://basescan.org/address/0xC26CdAFd6ec3c91AD9aEeB237Ee1f37205ED26a4) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc`](https://basescan.org/address/0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0x87e437030b7439150605a641483de98672E26317`](https://basescan.org/address/0x87e437030b7439150605a641483de98672E26317) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d`](https://basescan.org/address/0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | ### Berachain ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xC19A2542156b5d7960e0eF46E9787E7d336cF428`](https://berascan.com/address/0xC19A2542156b5d7960e0eF46E9787E7d336cF428) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0x75838C66Dfa2296bB9758f75fC7ad219718C8a88`](https://berascan.com/address/0x75838C66Dfa2296bB9758f75fC7ad219718C8a88) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc`](https://berascan.com/address/0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0x3bbE0a21792564604B0fDc00019532Adeffa70eb`](https://berascan.com/address/0x3bbE0a21792564604B0fDc00019532Adeffa70eb) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d`](https://berascan.com/address/0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | ### Blast ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xDbB6e9653d7e41766712Db22eB08ED3F21009fdd`](https://blastscan.io/address/0xDbB6e9653d7e41766712Db22eB08ED3F21009fdd) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0x193c2af965FEAca8D893c974712e5b6BD3cBc5ec`](https://blastscan.io/address/0x193c2af965FEAca8D893c974712e5b6BD3cBc5ec) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc`](https://blastscan.io/address/0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0x959c412d5919b1Ec5D07bee3443ea68c91d57dd7`](https://blastscan.io/address/0x959c412d5919b1Ec5D07bee3443ea68c91d57dd7) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d`](https://blastscan.io/address/0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | ### BNB Chain ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x6E0baD2c077d699841F1929b45bfb93FAfBEd395`](https://bscscan.com/address/0x6E0baD2c077d699841F1929b45bfb93FAfBEd395) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0xcf990fA3267F0945bBf7cf40A0c03F9dFE6a1804`](https://bscscan.com/address/0xcf990fA3267F0945bBf7cf40A0c03F9dFE6a1804) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc`](https://bscscan.com/address/0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0x56831a5a932793E02251126831174Ab8Bf2f7695`](https://bscscan.com/address/0x56831a5a932793E02251126831174Ab8Bf2f7695) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d`](https://bscscan.com/address/0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | ### Chiliz ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x711900e5f55d427cd88e5E3FCAe54Ccf02De71F4`](https://chiliscan.com/address/0x711900e5f55d427cd88e5E3FCAe54Ccf02De71F4) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0x179536f3289fb50076968b339C7EF0Dc0B38E3AF`](https://chiliscan.com/address/0x179536f3289fb50076968b339C7EF0Dc0B38E3AF) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0x6FcAB41e3b62d05aB4fC729586CB06Af2a2662D0`](https://chiliscan.com/address/0x6FcAB41e3b62d05aB4fC729586CB06Af2a2662D0) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0x8A96f827082FB349B6e268baa0a7A5584c4Ccda6`](https://chiliscan.com/address/0x8A96f827082FB349B6e268baa0a7A5584c4Ccda6) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0x84f092cf4D7D36C2d4987F672df81a39200a7146`](https://chiliscan.com/address/0x84f092cf4D7D36C2d4987F672df81a39200a7146) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | ### Core Dao ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x4fff53bfe86a0bd59a81c89d8ba84c67cf947764`](https://scan.coredao.org/address/0x4fff53bfe86a0bd59a81c89d8ba84c67cf947764) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0x96dadeeab25413de04a1b8e40c4de41bd9d7fd29`](https://scan.coredao.org/address/0x96dadeeab25413de04a1b8e40c4de41bd9d7fd29) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc`](https://scan.coredao.org/address/0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0xac0cf0f2a96ed7ec3cfa4d0be621c67adc9dd903`](https://scan.coredao.org/address/0xac0cf0f2a96ed7ec3cfa4d0be621c67adc9dd903) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d`](https://scan.coredao.org/address/0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | ### Gnosis ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x007aF5dC7b1CaA66Cf7Ebcc01E2e6ba4D55D3e92`](https://gnosisscan.io/address/0x007aF5dC7b1CaA66Cf7Ebcc01E2e6ba4D55D3e92) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0xe89EE0b2B31A296C5cCb631C3670F94bDD64a0D2`](https://gnosisscan.io/address/0xe89EE0b2B31A296C5cCb631C3670F94bDD64a0D2) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc`](https://gnosisscan.io/address/0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0x3140a6900AA2FF3186730741ad8255ee4e6d8Ff1`](https://gnosisscan.io/address/0x3140a6900AA2FF3186730741ad8255ee4e6d8Ff1) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d`](https://gnosisscan.io/address/0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | ### HyperEVM ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x856167EE3e09Ba562d69A542Ab6A939903ad738e`](https://hyperevmscan.io/address/0x856167EE3e09Ba562d69A542Ab6A939903ad738e) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0x5444eA1B8B636A8FdF7cE814077E664c28dE30Ec`](https://hyperevmscan.io/address/0x5444eA1B8B636A8FdF7cE814077E664c28dE30Ec) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0xe2f66eEe2E227c40074668ba53021423ED7D4ea1`](https://hyperevmscan.io/address/0xe2f66eEe2E227c40074668ba53021423ED7D4ea1) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0x7263d77e9e872f82A15e5E1a9816440D23758708`](https://hyperevmscan.io/address/0x7263d77e9e872f82A15e5E1a9816440D23758708) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0xf91e1a8fA643F8062C12Ca2865f1eb2258d6422F`](https://hyperevmscan.io/address/0xf91e1a8fA643F8062C12Ca2865f1eb2258d6422F) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | ### Lightlink ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x6c65aAf03186d1DA60127D3d7792cF36eD99D909`](https://phoenix.lightlink.io/address/0x6c65aAf03186d1DA60127D3d7792cF36eD99D909) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0xe8fa70D0172BB36c952E3e20e2f3550Ca4557761`](https://phoenix.lightlink.io/address/0xe8fa70D0172BB36c952E3e20e2f3550Ca4557761) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc`](https://phoenix.lightlink.io/address/0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0xCFB5F90370A7884DEc59C55533782B45FA24f4d1`](https://phoenix.lightlink.io/address/0xCFB5F90370A7884DEc59C55533782B45FA24f4d1) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d`](https://phoenix.lightlink.io/address/0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | ### Linea Mainnet ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x6964252561e8762dD10267176EaC5078b6291e51`](https://lineascan.build/address/0x6964252561e8762dD10267176EaC5078b6291e51) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0xEdf0A4b30defD14449604d1b97e2c39128c136CA`](https://lineascan.build/address/0xEdf0A4b30defD14449604d1b97e2c39128c136CA) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0x058aD99662FE7ecB8c3109920C99439a302b6573`](https://lineascan.build/address/0x058aD99662FE7ecB8c3109920C99439a302b6573) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0x1514a869D29a8B22961e8F9eBa3DC64000b96BCe`](https://lineascan.build/address/0x1514a869D29a8B22961e8F9eBa3DC64000b96BCe) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0xdEe57959770667d97A90C94fE70C055496B7a791`](https://lineascan.build/address/0xdEe57959770667d97A90C94fE70C055496B7a791) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | ### Mode ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x3aEbaDFC423fD08BE4715986F68D5E9A597ec974`](https://modescan.io/address/0x3aEbaDFC423fD08BE4715986F68D5E9A597ec974) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0x34dBab20FD097F63DDbf3092D83B1005D2573082`](https://modescan.io/address/0x34dBab20FD097F63DDbf3092D83B1005D2573082) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc`](https://modescan.io/address/0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0x64e7879558b6dfE2f510bd4b9Ad196ef0371EAA8`](https://modescan.io/address/0x64e7879558b6dfE2f510bd4b9Ad196ef0371EAA8) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d`](https://modescan.io/address/0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | ### Morph ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xF3cd08105b6745965149eF02b8aBdCEa0Ae51241`](https://explorer.morphl2.io/address/0xF3cd08105b6745965149eF02b8aBdCEa0Ae51241) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0x081BBbd4861BaBACE3E7eDC8a45741129DfC02fE`](https://explorer.morphl2.io/address/0x081BBbd4861BaBACE3E7eDC8a45741129DfC02fE) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc`](https://explorer.morphl2.io/address/0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0x660314f09ac3B65E216B6De288aAdc2599AF14e2`](https://explorer.morphl2.io/address/0x660314f09ac3B65E216B6De288aAdc2599AF14e2) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d`](https://explorer.morphl2.io/address/0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | ### OP Mainnet ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x822e9c4852E978104d82F0f785bFA663c2b700c1`](https://optimistic.etherscan.io/address/0x822e9c4852E978104d82F0f785bFA663c2b700c1) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0x0c4Cd6087DbFa3F74661BAbbFaa35273baC1c4b1`](https://optimistic.etherscan.io/address/0x0c4Cd6087DbFa3F74661BAbbFaa35273baC1c4b1) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc`](https://optimistic.etherscan.io/address/0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0x41dBa1AfBB6DF91b3330dc009842327A9858Cbae`](https://optimistic.etherscan.io/address/0x41dBa1AfBB6DF91b3330dc009842327A9858Cbae) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d`](https://optimistic.etherscan.io/address/0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | ### Polygon ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xE0BFe071Da104e571298f8b6e0fcE44C512C1Ff4`](https://polygonscan.com/address/0xE0BFe071Da104e571298f8b6e0fcE44C512C1Ff4) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0x1aDd9385F2C5c8e446bbB77c7A36839aB7743AF4`](https://polygonscan.com/address/0x1aDd9385F2C5c8e446bbB77c7A36839aB7743AF4) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc`](https://polygonscan.com/address/0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0xf5e12d0bA25FCa0D738Ec57f149736B2e4C46980`](https://polygonscan.com/address/0xf5e12d0bA25FCa0D738Ec57f149736B2e4C46980) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d`](https://polygonscan.com/address/0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | ### Scroll ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xcB0B1f1D116eD62135848d8C90EB61afDA936Da8`](https://scrollscan.com/address/0xcB0B1f1D116eD62135848d8C90EB61afDA936Da8) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0x8234Ad3CC4D29a4619C36a15286dac73078672a8`](https://scrollscan.com/address/0x8234Ad3CC4D29a4619C36a15286dac73078672a8) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc`](https://scrollscan.com/address/0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0x00Ff6443E902874924dd217c1435e3be04f57431`](https://scrollscan.com/address/0x00Ff6443E902874924dd217c1435e3be04f57431) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d`](https://scrollscan.com/address/0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | ### Sei Network ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x736A6E895790e089aEC2Bf76B2D7f368ce6Efff5`](https://seiscan.io/address/0x736A6E895790e089aEC2Bf76B2D7f368ce6Efff5) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0x34686937bef23c6441248Cc5A63A79a3a707e7E4`](https://seiscan.io/address/0x34686937bef23c6441248Cc5A63A79a3a707e7E4) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc`](https://seiscan.io/address/0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0xeaFB40669fe3523b073904De76410b46e79a56D7`](https://seiscan.io/address/0xeaFB40669fe3523b073904De76410b46e79a56D7) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d`](https://seiscan.io/address/0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | ### Sonic ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xc37462eE6500D2C36c9131b921fAADBD6cb7B60b`](https://sonicscan.org/address/0xc37462eE6500D2C36c9131b921fAADBD6cb7B60b) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0xc91b20dC29E19120BF3f54277947327AfD50fBa2`](https://sonicscan.org/address/0xc91b20dC29E19120BF3f54277947327AfD50fBa2) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc`](https://sonicscan.org/address/0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0xAE8FE47765f88e0A2D7E8fbaf33583DBf0373f01`](https://sonicscan.org/address/0xAE8FE47765f88e0A2D7E8fbaf33583DBf0373f01) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d`](https://sonicscan.org/address/0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | ### Sophon ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x28fCAE6bda2546C93183EeC8638691B2EB184003`](https://sophscan.xyz/address/0x28fCAE6bda2546C93183EeC8638691B2EB184003) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0x7282d83E49363f373102d195F66649eBD6C57B9B`](https://sophscan.xyz/address/0x7282d83E49363f373102d195F66649eBD6C57B9B) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0x424B27529B49EF4Bfa0727aFcFE4387Ac2932944`](https://sophscan.xyz/address/0x424B27529B49EF4Bfa0727aFcFE4387Ac2932944) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0xAc2E42b520364940c90Ce164412Ca9BA212d014B`](https://sophscan.xyz/address/0xAc2E42b520364940c90Ce164412Ca9BA212d014B) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0x9971914DA16787F6cCfb27bEfB4404e33C8b869D`](https://sophscan.xyz/address/0x9971914DA16787F6cCfb27bEfB4404e33C8b869D) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | ### Superseed ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xF46D1f8C85f215A515F6D738ab3E3bA081f6C083`](https://explorer.superseed.xyz/address/0xF46D1f8C85f215A515F6D738ab3E3bA081f6C083) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0x89e9F2473836d9ab7D28Df6F180E30992b8CB5d6`](https://explorer.superseed.xyz/address/0x89e9F2473836d9ab7D28Df6F180E30992b8CB5d6) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc`](https://explorer.superseed.xyz/address/0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0xa4576b58Ec760A8282D081dc94F3dc716DFc61e9`](https://explorer.superseed.xyz/address/0xa4576b58Ec760A8282D081dc94F3dc716DFc61e9) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d`](https://explorer.superseed.xyz/address/0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | ### Taiko ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x628E88cDF558c0F4796c8CeB5068a023a7159aA7`](https://taikoscan.io/address/0x628E88cDF558c0F4796c8CeB5068a023a7159aA7) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0xcBbA08768C4a9D9131dE0467Ae136b8450dC13B2`](https://taikoscan.io/address/0xcBbA08768C4a9D9131dE0467Ae136b8450dC13B2) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc`](https://taikoscan.io/address/0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0x79F1fD8bB2D455f64010063Fc79E27561980FE10`](https://taikoscan.io/address/0x79F1fD8bB2D455f64010063Fc79E27561980FE10) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d`](https://taikoscan.io/address/0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | ### Tangle ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x1cAe76b71913598d7664d16641CCB6037d8Ed61a`](https://explorer.tangle.tools/address/0x1cAe76b71913598d7664d16641CCB6037d8Ed61a) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0xF5AC60870E1CCc4Bfce23cfbb7a796A0d8dBAf47`](https://explorer.tangle.tools/address/0xF5AC60870E1CCc4Bfce23cfbb7a796A0d8dBAf47) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0x3D664B2Da905DDD0Db931982FD9a759ea950D6e1`](https://explorer.tangle.tools/address/0x3D664B2Da905DDD0Db931982FD9a759ea950D6e1) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0x92FC05e49c27884d554D98a5C01Ff0894a9DC29a`](https://explorer.tangle.tools/address/0x92FC05e49c27884d554D98a5C01Ff0894a9DC29a) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0x9EfC8663cAB0e2d97ad17C9fbfc8392445517E94`](https://explorer.tangle.tools/address/0x9EfC8663cAB0e2d97ad17C9fbfc8392445517E94) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | ### Unichain ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x26C341C4D79bA8F6BFB450a49E9165c936316B14`](https://uniscan.xyz/address/0x26C341C4D79bA8F6BFB450a49E9165c936316B14) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0xaf875A2bDb74bA8872292FC371131eb45a9b570C`](https://uniscan.xyz/address/0xaf875A2bDb74bA8872292FC371131eb45a9b570C) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0xf8076e4fb5cfe8be1c26e61222dc51828db8c1dc`](https://uniscan.xyz/address/0xf8076e4fb5cfe8be1c26e61222dc51828db8c1dc) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0xa5F12D63E18a28C9BE27B6f3d91ce693320067ba`](https://uniscan.xyz/address/0xa5F12D63E18a28C9BE27B6f3d91ce693320067ba) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0x5522ca06ce080800ab59ba4c091e63f6f54c5e6d`](https://uniscan.xyz/address/0x5522ca06ce080800ab59ba4c091e63f6f54c5e6d) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | ### XDC ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x489e0DC5E62A751A2b209f1cC03E189fd6257176`](https://xdcscan.com/address/0x489e0DC5E62A751A2b209f1cC03E189fd6257176) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0x6d36227Dd84e2A3d898B192Bc82a005c3cc2320C`](https://xdcscan.com/address/0x6d36227Dd84e2A3d898B192Bc82a005c3cc2320C) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0xf8076e4fb5cfe8be1c26e61222dc51828db8c1dc`](https://xdcscan.com/address/0xf8076e4fb5cfe8be1c26e61222dc51828db8c1dc) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0x4c1311a9d88BFb7023148aB04F7321C2E91c29bf`](https://xdcscan.com/address/0x4c1311a9d88BFb7023148aB04F7321C2E91c29bf) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0x5522ca06ce080800ab59ba4c091e63f6f54c5e6d`](https://xdcscan.com/address/0x5522ca06ce080800ab59ba4c091e63f6f54c5e6d) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | ### ZKsync Era ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x7BCcB3595Aa81Dbe8A69DD8C46f5C2A3cf76594A`](https://explorer.zksync.io/address/0x7BCcB3595Aa81Dbe8A69DD8C46f5C2A3cf76594A) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0xCC926359DBE6b6311D63f8155fcC3B57F3fAAE80`](https://explorer.zksync.io/address/0xCC926359DBE6b6311D63f8155fcC3B57F3fAAE80) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0x37De3Fc44a07A40411AD0Cea4310990C9F88c1C1`](https://explorer.zksync.io/address/0x37De3Fc44a07A40411AD0Cea4310990C9F88c1C1) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0xCB2d53c58496C2aA114bce4ED5C7fe768ce86542`](https://explorer.zksync.io/address/0xCB2d53c58496C2aA114bce4ED5C7fe768ce86542) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0xEE4a32E026aC2FD6BF71d9D7eB00803576aD314d`](https://explorer.zksync.io/address/0xEE4a32E026aC2FD6BF71d9D7eB00803576aD314d) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | ## Testnets ### Arbitrum Sepolia ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x83Dd52FCA44E069020b58155b761A590F12B59d3`](https://sepolia.arbiscan.io/address/0x83Dd52FCA44E069020b58155b761A590F12B59d3) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0xbf85cD17cA59b7A2b81d3D776cE1602a7C0aF9D9`](https://sepolia.arbiscan.io/address/0xbf85cD17cA59b7A2b81d3D776cE1602a7C0aF9D9) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc`](https://sepolia.arbiscan.io/address/0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0x8224eb5D7d76B2D7Df43b868D875E79B11500eA8`](https://sepolia.arbiscan.io/address/0x8224eb5D7d76B2D7Df43b868D875E79B11500eA8) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d`](https://sepolia.arbiscan.io/address/0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | ### Base Sepolia ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xa4777CA525d43a7aF55D45b11b430606d7416f8d`](https://sepolia.basescan.org/address/0xa4777CA525d43a7aF55D45b11b430606d7416f8d) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0xEdc716E9672f672456d22b02532395c1e62B8C16`](https://sepolia.basescan.org/address/0xEdc716E9672f672456d22b02532395c1e62B8C16) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc`](https://sepolia.basescan.org/address/0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0xCA2593027BA24856c292Fdcb5F987E0c25e755a4`](https://sepolia.basescan.org/address/0xCA2593027BA24856c292Fdcb5F987E0c25e755a4) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d`](https://sepolia.basescan.org/address/0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | ### Linea Sepolia ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xbb4A14868A4BEc78b7354582b8C818ba520d7A4E`](https://sepolia.lineascan.build/address/0xbb4A14868A4BEc78b7354582b8C818ba520d7A4E) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0x9A987181BF05b7C154118A3216d522fa2407a8Be`](https://sepolia.lineascan.build/address/0x9A987181BF05b7C154118A3216d522fa2407a8Be) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0x058aD99662FE7ecB8c3109920C99439a302b6573`](https://sepolia.lineascan.build/address/0x058aD99662FE7ecB8c3109920C99439a302b6573) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0xCE94BE25320A51Ac868d0C133c251aE10682DabD`](https://sepolia.lineascan.build/address/0xCE94BE25320A51Ac868d0C133c251aE10682DabD) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0xdEe57959770667d97A90C94fE70C055496B7a791`](https://sepolia.lineascan.build/address/0xdEe57959770667d97A90C94fE70C055496B7a791) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | ### OP Sepolia ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x1f898895eAB949FfD34c29Cf859C035DC4525DF4`](https://optimism-sepolia.blockscout.com/address/0x1f898895eAB949FfD34c29Cf859C035DC4525DF4) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0xF7BA8a7dc96d1939b789b91865bdb05596EBB558`](https://optimism-sepolia.blockscout.com/address/0xF7BA8a7dc96d1939b789b91865bdb05596EBB558) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc`](https://optimism-sepolia.blockscout.com/address/0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0xDf6163ddD3Ebcb552Cc1379a9c65AFe68683534e`](https://optimism-sepolia.blockscout.com/address/0xDf6163ddD3Ebcb552Cc1379a9c65AFe68683534e) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d`](https://optimism-sepolia.blockscout.com/address/0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | ### Sepolia ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xd116c275541cdBe7594A202bD6AE4DBca4578462`](https://sepolia.etherscan.io/address/0xd116c275541cdBe7594A202bD6AE4DBca4578462) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0xB655ecD83D27f7c683A9605783bd2866a4dCEB04`](https://sepolia.etherscan.io/address/0xB655ecD83D27f7c683A9605783bd2866a4dCEB04) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc`](https://sepolia.etherscan.io/address/0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0x08D3C81626d9Cb19760835e8730Ec0D3F1899976`](https://sepolia.etherscan.io/address/0x08D3C81626d9Cb19760835e8730Ec0D3F1899976) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d`](https://sepolia.etherscan.io/address/0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | ### Superseed Sepolia ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x4E83EC1Ea3B885C1a3698dA7DC42F32575688ABE`](https://sepolia-explorer.superseed.xyz/address/0x4E83EC1Ea3B885C1a3698dA7DC42F32575688ABE) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | SablierBatchLockup | [`0xB2C6C57ee10B88E8344f34ffeCe39B0C6573c23D`](https://sepolia-explorer.superseed.xyz/address/0xB2C6C57ee10B88E8344f34ffeCe39B0C6573c23D) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | Helpers | [`0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc`](https://sepolia-explorer.superseed.xyz/address/0xf8076E4Fb5cfE8be1C26E61222DC51828Db8C1dc) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | LockupNFTDescriptor | [`0xc5D8E4317CE4a2E323192A5d856C90372bDE1558`](https://sepolia-explorer.superseed.xyz/address/0xc5D8E4317CE4a2E323192A5d856C90372bDE1558) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | | VestingMath | [`0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d`](https://sepolia-explorer.superseed.xyz/address/0x5522CA06Ce080800AB59BA4C091e63f6f54C5E6d) | [`lockup-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v2.0) | --- ## Lockup v3.0 Source: https://docs.sablier.com/guides/lockup/previous-deployments/v3.0 # Lockup v3.0 This section contains the deployment addresses for the v3.0 release of [@sablier/lockup@3.0.1](https://npmjs.com/package/@sablier/lockup/v/3.0.1). A few noteworthy details about the deployments: - The addresses are final - All contracts are non-upgradeable - The source code is verified on Etherscan across all chains :::info This is an outdated version of the Lockup protocol. See the latest version [here](/guides/lockup/deployments). ::: ## Mainnets ### Ethereum ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xcF8ce57fa442ba50aCbC57147a62aD03873FfA73`](https://etherscan.io/address/0xcF8ce57fa442ba50aCbC57147a62aD03873FfA73) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | SablierBatchLockup | [`0x0636d83b184d65c242c43de6aad10535bfb9d45a`](https://etherscan.io/address/0x0636d83b184d65c242c43de6aad10535bfb9d45a) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | Helpers | [`0xa0a1ac47260b95d334763473b868117ef7343aa0`](https://etherscan.io/address/0xa0a1ac47260b95d334763473b868117ef7343aa0) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupMath | [`0x1feb172238638897b13b69c65feb508a0a96b35d`](https://etherscan.io/address/0x1feb172238638897b13b69c65feb508a0a96b35d) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupNFTDescriptor | [`0xA9dC6878C979B5cc1d98a1803F0664ad725A1f56`](https://etherscan.io/address/0xA9dC6878C979B5cc1d98a1803F0664ad725A1f56) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | ### Abstract ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x293d8d192C0C93225FF6bBE7415a56B57379bbA3`](https://abscan.org/address/0x293d8d192C0C93225FF6bBE7415a56B57379bbA3) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | SablierBatchLockup | [`0x47a27E76b32A9aA168297A32c5F081740600A16F`](https://abscan.org/address/0x47a27E76b32A9aA168297A32c5F081740600A16F) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | Helpers | [`0xa0a1ac47260b95d334763473b868117ef7343aa0`](https://abscan.org/address/0xa0a1ac47260b95d334763473b868117ef7343aa0) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupMath | [`0x1feb172238638897b13b69c65feb508a0a96b35d`](https://abscan.org/address/0x1feb172238638897b13b69c65feb508a0a96b35d) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupNFTDescriptor | [`0x63Ff2E370788C163D5a1909B5FCb299DB327AEF9`](https://abscan.org/address/0x63Ff2E370788C163D5a1909B5FCb299DB327AEF9) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | ### Arbitrum ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xF12AbfB041b5064b839Ca56638cDB62fEA712Db5`](https://arbiscan.io/address/0xF12AbfB041b5064b839Ca56638cDB62fEA712Db5) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | SablierBatchLockup | [`0xf094baa1b754f54d8f282bc79a74bd76aff29d25`](https://arbiscan.io/address/0xf094baa1b754f54d8f282bc79a74bd76aff29d25) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | Helpers | [`0xa0a1ac47260b95d334763473b868117ef7343aa0`](https://arbiscan.io/address/0xa0a1ac47260b95d334763473b868117ef7343aa0) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupMath | [`0x1feb172238638897b13b69c65feb508a0a96b35d`](https://arbiscan.io/address/0x1feb172238638897b13b69c65feb508a0a96b35d) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupNFTDescriptor | [`0xd5c6a0Dd2E1822865c308850b8b3E2CcE762D061`](https://arbiscan.io/address/0xd5c6a0Dd2E1822865c308850b8b3E2CcE762D061) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | ### Avalanche ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x7e146250Ed5CCCC6Ada924D456947556902acaFD`](https://snowscan.xyz/address/0x7e146250Ed5CCCC6Ada924D456947556902acaFD) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | SablierBatchLockup | [`0x7125669bFbCA422bE806d62B6b21E42ED0D78494`](https://snowscan.xyz/address/0x7125669bFbCA422bE806d62B6b21E42ED0D78494) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | Helpers | [`0xa0a1ac47260b95d334763473b868117ef7343aa0`](https://snowscan.xyz/address/0xa0a1ac47260b95d334763473b868117ef7343aa0) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupMath | [`0x1feb172238638897b13b69c65feb508a0a96b35d`](https://snowscan.xyz/address/0x1feb172238638897b13b69c65feb508a0a96b35d) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupNFTDescriptor | [`0x906A4BD5dD0EF13654eA29bFD6185d0d64A4b674`](https://snowscan.xyz/address/0x906A4BD5dD0EF13654eA29bFD6185d0d64A4b674) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | ### Base ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xe261b366f231b12fcb58d6bbd71e57faee82431d`](https://basescan.org/address/0xe261b366f231b12fcb58d6bbd71e57faee82431d) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | SablierBatchLockup | [`0x8882549b29dfed283738918d90b5f6e2ab0baeb6`](https://basescan.org/address/0x8882549b29dfed283738918d90b5f6e2ab0baeb6) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | Helpers | [`0xa0a1ac47260b95d334763473b868117ef7343aa0`](https://basescan.org/address/0xa0a1ac47260b95d334763473b868117ef7343aa0) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupMath | [`0x1feb172238638897b13b69c65feb508a0a96b35d`](https://basescan.org/address/0x1feb172238638897b13b69c65feb508a0a96b35d) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupNFTDescriptor | [`0x87e437030b7439150605a641483de98672E26317`](https://basescan.org/address/0x87e437030b7439150605a641483de98672E26317) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | ### Berachain ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xC37B51a3c3Be55f0B34Fbd8Bd1F30cFF6d251408`](https://berascan.com/address/0xC37B51a3c3Be55f0B34Fbd8Bd1F30cFF6d251408) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | SablierBatchLockup | [`0x35860B173573CbDB7a14dE5F9fBB7489c57a5727`](https://berascan.com/address/0x35860B173573CbDB7a14dE5F9fBB7489c57a5727) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | Helpers | [`0xa0a1ac47260b95d334763473b868117ef7343aa0`](https://berascan.com/address/0xa0a1ac47260b95d334763473b868117ef7343aa0) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupMath | [`0x1feb172238638897b13b69c65feb508a0a96b35d`](https://berascan.com/address/0x1feb172238638897b13b69c65feb508a0a96b35d) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupNFTDescriptor | [`0x3bbE0a21792564604B0fDc00019532Adeffa70eb`](https://berascan.com/address/0x3bbE0a21792564604B0fDc00019532Adeffa70eb) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | ### Blast ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xcD16d89cc79Ab0b52717A46b8A3F73E61014c7dc`](https://blastscan.io/address/0xcD16d89cc79Ab0b52717A46b8A3F73E61014c7dc) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | SablierBatchLockup | [`0xd2187e309a93895fcd1c43fb5eae903b583d7e38`](https://blastscan.io/address/0xd2187e309a93895fcd1c43fb5eae903b583d7e38) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | Helpers | [`0xa0a1ac47260b95d334763473b868117ef7343aa0`](https://blastscan.io/address/0xa0a1ac47260b95d334763473b868117ef7343aa0) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupMath | [`0x1feb172238638897b13b69c65feb508a0a96b35d`](https://blastscan.io/address/0x1feb172238638897b13b69c65feb508a0a96b35d) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupNFTDescriptor | [`0x959c412d5919b1Ec5D07bee3443ea68c91d57dd7`](https://blastscan.io/address/0x959c412d5919b1Ec5D07bee3443ea68c91d57dd7) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | ### BNB Chain ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x06bd1Ec1d80acc45ba332f79B08d2d9e24240C74`](https://bscscan.com/address/0x06bd1Ec1d80acc45ba332f79B08d2d9e24240C74) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | SablierBatchLockup | [`0xFEd01907959CD5d470F438daad232a99cAffe67f`](https://bscscan.com/address/0xFEd01907959CD5d470F438daad232a99cAffe67f) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | Helpers | [`0xa0a1ac47260b95d334763473b868117ef7343aa0`](https://bscscan.com/address/0xa0a1ac47260b95d334763473b868117ef7343aa0) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupMath | [`0x1feb172238638897b13b69c65feb508a0a96b35d`](https://bscscan.com/address/0x1feb172238638897b13b69c65feb508a0a96b35d) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupNFTDescriptor | [`0x56831a5a932793E02251126831174Ab8Bf2f7695`](https://bscscan.com/address/0x56831a5a932793E02251126831174Ab8Bf2f7695) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | ### Chiliz ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x957a54aC691893B20c705e0b2EecbDDF5220d019`](https://chiliscan.com/address/0x957a54aC691893B20c705e0b2EecbDDF5220d019) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | SablierBatchLockup | [`0x202628bCF205BC57a2D181D6B4eF1f7E5538EC35`](https://chiliscan.com/address/0x202628bCF205BC57a2D181D6B4eF1f7E5538EC35) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | Helpers | [`0xa0a1ac47260b95d334763473b868117ef7343aa0`](https://chiliscan.com/address/0xa0a1ac47260b95d334763473b868117ef7343aa0) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupMath | [`0x1feb172238638897b13b69c65feb508a0a96b35d`](https://chiliscan.com/address/0x1feb172238638897b13b69c65feb508a0a96b35d) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupNFTDescriptor | [`0x8A96f827082FB349B6e268baa0a7A5584c4Ccda6`](https://chiliscan.com/address/0x8A96f827082FB349B6e268baa0a7A5584c4Ccda6) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | ### Core Dao ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x01Fed2aB51A830a3AF3AE1AB817dF1bA4F152bB0`](https://scan.coredao.org/address/0x01Fed2aB51A830a3AF3AE1AB817dF1bA4F152bB0) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | SablierBatchLockup | [`0x80054b6797C5F657CaCaEC05E4890acfC4Ef79dB`](https://scan.coredao.org/address/0x80054b6797C5F657CaCaEC05E4890acfC4Ef79dB) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | Helpers | [`0xa0a1ac47260b95d334763473b868117ef7343aa0`](https://scan.coredao.org/address/0xa0a1ac47260b95d334763473b868117ef7343aa0) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupMath | [`0x1feb172238638897b13b69c65feb508a0a96b35d`](https://scan.coredao.org/address/0x1feb172238638897b13b69c65feb508a0a96b35d) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupNFTDescriptor | [`0xac0cf0f2a96ed7ec3cfa4d0be621c67adc9dd903`](https://scan.coredao.org/address/0xac0cf0f2a96ed7ec3cfa4d0be621c67adc9dd903) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | ### Denergy ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x9f5d28C8ed7F09e65519C1f6f394e523524cA38F`](https://explorer.denergychain.com/address/0x9f5d28C8ed7F09e65519C1f6f394e523524cA38F) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | SablierBatchLockup | [`0x6Fe81F4Bf1aF1b829f0E701647808f3Aa4b0BdE7`](https://explorer.denergychain.com/address/0x6Fe81F4Bf1aF1b829f0E701647808f3Aa4b0BdE7) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | Helpers | [`0xd595D34ed96b253E7c7a934a7624F330a8411953`](https://explorer.denergychain.com/address/0xd595D34ed96b253E7c7a934a7624F330a8411953) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupMath | [`0xB8aB2E66d5E4931e675CBE499d8c01B778694fd3`](https://explorer.denergychain.com/address/0xB8aB2E66d5E4931e675CBE499d8c01B778694fd3) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupNFTDescriptor | [`0x4749dB4834be9b473D586Ad4d98133dAfC678313`](https://explorer.denergychain.com/address/0x4749dB4834be9b473D586Ad4d98133dAfC678313) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | ### Gnosis ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x87f87Eb0b59421D1b2Df7301037e923932176681`](https://gnosisscan.io/address/0x87f87Eb0b59421D1b2Df7301037e923932176681) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | SablierBatchLockup | [`0xb778B396dD6f3a770C4B4AE7b0983345b231C16C`](https://gnosisscan.io/address/0xb778B396dD6f3a770C4B4AE7b0983345b231C16C) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | Helpers | [`0xa0a1ac47260b95d334763473b868117ef7343aa0`](https://gnosisscan.io/address/0xa0a1ac47260b95d334763473b868117ef7343aa0) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupMath | [`0x1feb172238638897b13b69c65feb508a0a96b35d`](https://gnosisscan.io/address/0x1feb172238638897b13b69c65feb508a0a96b35d) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupNFTDescriptor | [`0x3140a6900AA2FF3186730741ad8255ee4e6d8Ff1`](https://gnosisscan.io/address/0x3140a6900AA2FF3186730741ad8255ee4e6d8Ff1) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | ### HyperEVM ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x50ff828e66612A4D1F7141936F2B4078C7356329`](https://hyperevmscan.io/address/0x50ff828e66612A4D1F7141936F2B4078C7356329) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | SablierBatchLockup | [`0xF66b45FA5Be1F633Cd521d480A1832645DE0C710`](https://hyperevmscan.io/address/0xF66b45FA5Be1F633Cd521d480A1832645DE0C710) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | Helpers | [`0xa0a1ac47260b95d334763473b868117ef7343aa0`](https://hyperevmscan.io/address/0xa0a1ac47260b95d334763473b868117ef7343aa0) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupMath | [`0x1feb172238638897b13b69c65feb508a0a96b35d`](https://hyperevmscan.io/address/0x1feb172238638897b13b69c65feb508a0a96b35d) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupNFTDescriptor | [`0x7263d77e9e872f82A15e5E1a9816440D23758708`](https://hyperevmscan.io/address/0x7263d77e9e872f82A15e5E1a9816440D23758708) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | ### Lightlink ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xA4f1f4a5C55b5d9372CBB29112b14e1912A23d9D`](https://phoenix.lightlink.io/address/0xA4f1f4a5C55b5d9372CBB29112b14e1912A23d9D) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | SablierBatchLockup | [`0xc1d996e119f82D3Cb540687463804545A13065db`](https://phoenix.lightlink.io/address/0xc1d996e119f82D3Cb540687463804545A13065db) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | Helpers | [`0xa0a1ac47260b95d334763473b868117ef7343aa0`](https://phoenix.lightlink.io/address/0xa0a1ac47260b95d334763473b868117ef7343aa0) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupMath | [`0x1feb172238638897b13b69c65feb508a0a96b35d`](https://phoenix.lightlink.io/address/0x1feb172238638897b13b69c65feb508a0a96b35d) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupNFTDescriptor | [`0xCFB5F90370A7884DEc59C55533782B45FA24f4d1`](https://phoenix.lightlink.io/address/0xCFB5F90370A7884DEc59C55533782B45FA24f4d1) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | ### Linea Mainnet ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xc853DB30a908dC1b655bbd4A8B9d5DB8588C13c8`](https://lineascan.build/address/0xc853DB30a908dC1b655bbd4A8B9d5DB8588C13c8) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | SablierBatchLockup | [`0x145Ca6411bD62172fbE8728893E8e8E75a2b066c`](https://lineascan.build/address/0x145Ca6411bD62172fbE8728893E8e8E75a2b066c) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | Helpers | [`0x1b06a7aed1f6e5d8c0f4b410687b37c867fa8987`](https://lineascan.build/address/0x1b06a7aed1f6e5d8c0f4b410687b37c867fa8987) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupMath | [`0x8e6f2e21df39ffb23fbd5a51a5903952334435e3`](https://lineascan.build/address/0x8e6f2e21df39ffb23fbd5a51a5903952334435e3) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupNFTDescriptor | [`0x1514a869D29a8B22961e8F9eBa3DC64000b96BCe`](https://lineascan.build/address/0x1514a869D29a8B22961e8F9eBa3DC64000b96BCe) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | ### Mode ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x9513CE572D4f4AAc1Dd493bcd50866235D1c698d`](https://modescan.io/address/0x9513CE572D4f4AAc1Dd493bcd50866235D1c698d) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | SablierBatchLockup | [`0x9ba4cE334706F822cbCaa4F6fA650e0054D5fa99`](https://modescan.io/address/0x9ba4cE334706F822cbCaa4F6fA650e0054D5fa99) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | Helpers | [`0xa0a1ac47260b95d334763473b868117ef7343aa0`](https://modescan.io/address/0xa0a1ac47260b95d334763473b868117ef7343aa0) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupMath | [`0x1feb172238638897b13b69c65feb508a0a96b35d`](https://modescan.io/address/0x1feb172238638897b13b69c65feb508a0a96b35d) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupNFTDescriptor | [`0x64e7879558b6dfE2f510bd4b9Ad196ef0371EAA8`](https://modescan.io/address/0x64e7879558b6dfE2f510bd4b9Ad196ef0371EAA8) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | ### Monad ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x003F5393F4836f710d492AD98D89F5BFCCF1C962`](https://monadscan.com/address/0x003F5393F4836f710d492AD98D89F5BFCCF1C962) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | SablierBatchLockup | [`0x4FCACf614E456728CaEa87f475bd78EC3550E20B`](https://monadscan.com/address/0x4FCACf614E456728CaEa87f475bd78EC3550E20B) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | Helpers | [`0xa0A1aC47260B95D334763473B868117EF7343aA0`](https://monadscan.com/address/0xa0A1aC47260B95D334763473B868117EF7343aA0) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupMath | [`0x1feB172238638897B13b69C65feB508a0a96b35D`](https://monadscan.com/address/0x1feB172238638897B13b69C65feB508a0a96b35D) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupNFTDescriptor | [`0x619E7f9832522EDeBd883482Cd3d84653A050725`](https://monadscan.com/address/0x619E7f9832522EDeBd883482Cd3d84653A050725) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | ### Morph ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xE646D9A037c6B62e4d417592A10f57e77f007a27`](https://explorer.morphl2.io/address/0xE646D9A037c6B62e4d417592A10f57e77f007a27) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | SablierBatchLockup | [`0xf81d757E270790c03E3cd2dbFBc62d38ad2e17f9`](https://explorer.morphl2.io/address/0xf81d757E270790c03E3cd2dbFBc62d38ad2e17f9) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | Helpers | [`0xa0a1ac47260b95d334763473b868117ef7343aa0`](https://explorer.morphl2.io/address/0xa0a1ac47260b95d334763473b868117ef7343aa0) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupMath | [`0x1feb172238638897b13b69c65feb508a0a96b35d`](https://explorer.morphl2.io/address/0x1feb172238638897b13b69c65feb508a0a96b35d) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupNFTDescriptor | [`0x660314f09ac3B65E216B6De288aAdc2599AF14e2`](https://explorer.morphl2.io/address/0x660314f09ac3B65E216B6De288aAdc2599AF14e2) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | ### OP Mainnet ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xe2620fB20fC9De61CD207d921691F4eE9d0fffd0`](https://optimistic.etherscan.io/address/0xe2620fB20fC9De61CD207d921691F4eE9d0fffd0) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | SablierBatchLockup | [`0xf3aBc38b5e0f372716F9bc00fC9994cbd5A8e6FC`](https://optimistic.etherscan.io/address/0xf3aBc38b5e0f372716F9bc00fC9994cbd5A8e6FC) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | Helpers | [`0xa0a1ac47260b95d334763473b868117ef7343aa0`](https://optimistic.etherscan.io/address/0xa0a1ac47260b95d334763473b868117ef7343aa0) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupMath | [`0x1feb172238638897b13b69c65feb508a0a96b35d`](https://optimistic.etherscan.io/address/0x1feb172238638897b13b69c65feb508a0a96b35d) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupNFTDescriptor | [`0x41dBa1AfBB6DF91b3330dc009842327A9858Cbae`](https://optimistic.etherscan.io/address/0x41dBa1AfBB6DF91b3330dc009842327A9858Cbae) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | ### Polygon ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x1E901b0E05A78C011D6D4cfFdBdb28a42A1c32EF`](https://polygonscan.com/address/0x1E901b0E05A78C011D6D4cfFdBdb28a42A1c32EF) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | SablierBatchLockup | [`0x3395Db92edb3a992E4F0eC1dA203C92D5075b845`](https://polygonscan.com/address/0x3395Db92edb3a992E4F0eC1dA203C92D5075b845) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | Helpers | [`0xa0a1ac47260b95d334763473b868117ef7343aa0`](https://polygonscan.com/address/0xa0a1ac47260b95d334763473b868117ef7343aa0) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupMath | [`0x1feb172238638897b13b69c65feb508a0a96b35d`](https://polygonscan.com/address/0x1feb172238638897b13b69c65feb508a0a96b35d) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupNFTDescriptor | [`0xf5e12d0bA25FCa0D738Ec57f149736B2e4C46980`](https://polygonscan.com/address/0xf5e12d0bA25FCa0D738Ec57f149736B2e4C46980) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | ### Scroll ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xcb60a39942CD5D1c2a1C8aBBEd99C43A73dF3f8d`](https://scrollscan.com/address/0xcb60a39942CD5D1c2a1C8aBBEd99C43A73dF3f8d) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | SablierBatchLockup | [`0xa57C667E78BA165e8f09899fdE4e8C974C2dD000`](https://scrollscan.com/address/0xa57C667E78BA165e8f09899fdE4e8C974C2dD000) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | Helpers | [`0xa0a1ac47260b95d334763473b868117ef7343aa0`](https://scrollscan.com/address/0xa0a1ac47260b95d334763473b868117ef7343aa0) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupMath | [`0x1feb172238638897b13b69c65feb508a0a96b35d`](https://scrollscan.com/address/0x1feb172238638897b13b69c65feb508a0a96b35d) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupNFTDescriptor | [`0x00Ff6443E902874924dd217c1435e3be04f57431`](https://scrollscan.com/address/0x00Ff6443E902874924dd217c1435e3be04f57431) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | ### Sei Network ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x1d96e9d05f6910d22876177299261290537cfBBc`](https://seiscan.io/address/0x1d96e9d05f6910d22876177299261290537cfBBc) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | SablierBatchLockup | [`0xB33f16e55Ac8Ff6CCdfe466C0D9A25c706952EF8`](https://seiscan.io/address/0xB33f16e55Ac8Ff6CCdfe466C0D9A25c706952EF8) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | Helpers | [`0xa0a1ac47260b95d334763473b868117ef7343aa0`](https://seiscan.io/address/0xa0a1ac47260b95d334763473b868117ef7343aa0) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupMath | [`0x1feb172238638897b13b69c65feb508a0a96b35d`](https://seiscan.io/address/0x1feb172238638897b13b69c65feb508a0a96b35d) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupNFTDescriptor | [`0xeaFB40669fe3523b073904De76410b46e79a56D7`](https://seiscan.io/address/0xeaFB40669fe3523b073904De76410b46e79a56D7) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | ### Sonic ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x763Cfb7DF1D1BFe50e35E295688b3Df789D2feBB`](https://sonicscan.org/address/0x763Cfb7DF1D1BFe50e35E295688b3Df789D2feBB) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | SablierBatchLockup | [`0x84A865542640B24301F1C8A8C60Eb098a7e1df9b`](https://sonicscan.org/address/0x84A865542640B24301F1C8A8C60Eb098a7e1df9b) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | Helpers | [`0xa0a1ac47260b95d334763473b868117ef7343aa0`](https://sonicscan.org/address/0xa0a1ac47260b95d334763473b868117ef7343aa0) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupMath | [`0x1feb172238638897b13b69c65feb508a0a96b35d`](https://sonicscan.org/address/0x1feb172238638897b13b69c65feb508a0a96b35d) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupNFTDescriptor | [`0x955dC7A2170782344FA9Ac11De0C0C42C05De2Fc`](https://sonicscan.org/address/0x955dC7A2170782344FA9Ac11De0C0C42C05De2Fc) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | ### Superseed ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x2F1c6AD6306Bd0200D55b59AD54d4b44067D00E6`](https://explorer.superseed.xyz/address/0x2F1c6AD6306Bd0200D55b59AD54d4b44067D00E6) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | SablierBatchLockup | [`0x48c45c1DF54bDA1F1819AD07B6B4f8B2dcF85BAA`](https://explorer.superseed.xyz/address/0x48c45c1DF54bDA1F1819AD07B6B4f8B2dcF85BAA) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | Helpers | [`0xa0a1ac47260b95d334763473b868117ef7343aa0`](https://explorer.superseed.xyz/address/0xa0a1ac47260b95d334763473b868117ef7343aa0) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupMath | [`0x1feb172238638897b13b69c65feb508a0a96b35d`](https://explorer.superseed.xyz/address/0x1feb172238638897b13b69c65feb508a0a96b35d) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupNFTDescriptor | [`0xa4576b58Ec760A8282D081dc94F3dc716DFc61e9`](https://explorer.superseed.xyz/address/0xa4576b58Ec760A8282D081dc94F3dc716DFc61e9) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | ### Unichain ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xfFb540fC132dCefb0Fdef96ef63FE2f2F1BD7CFd`](https://uniscan.xyz/address/0xfFb540fC132dCefb0Fdef96ef63FE2f2F1BD7CFd) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | SablierBatchLockup | [`0xA8B4355D37C3660aE0C8fD14c39D44Ad5EA60b3e`](https://uniscan.xyz/address/0xA8B4355D37C3660aE0C8fD14c39D44Ad5EA60b3e) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | Helpers | [`0xa0a1ac47260b95d334763473b868117ef7343aa0`](https://uniscan.xyz/address/0xa0a1ac47260b95d334763473b868117ef7343aa0) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupMath | [`0x1feb172238638897b13b69c65feb508a0a96b35d`](https://uniscan.xyz/address/0x1feb172238638897b13b69c65feb508a0a96b35d) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupNFTDescriptor | [`0xa5F12D63E18a28C9BE27B6f3d91ce693320067ba`](https://uniscan.xyz/address/0xa5F12D63E18a28C9BE27B6f3d91ce693320067ba) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | ### XDC ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x2266901B1EcF499b4c91B6cBeA8e06700cFbde1e`](https://xdcscan.com/address/0x2266901B1EcF499b4c91B6cBeA8e06700cFbde1e) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | SablierBatchLockup | [`0xCf2491021E56E04e5ceF582c928d64b185843124`](https://xdcscan.com/address/0xCf2491021E56E04e5ceF582c928d64b185843124) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | Helpers | [`0xa0a1ac47260b95d334763473b868117ef7343aa0`](https://xdcscan.com/address/0xa0a1ac47260b95d334763473b868117ef7343aa0) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupMath | [`0x1feb172238638897b13b69c65feb508a0a96b35d`](https://xdcscan.com/address/0x1feb172238638897b13b69c65feb508a0a96b35d) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupNFTDescriptor | [`0x4c1311a9d88BFb7023148aB04F7321C2E91c29bf`](https://xdcscan.com/address/0x4c1311a9d88BFb7023148aB04F7321C2E91c29bf) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | ### ZKsync Era ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xC07E338Ce1aEd183A8b3c55f980548f5E463b5c5`](https://explorer.zksync.io/address/0xC07E338Ce1aEd183A8b3c55f980548f5E463b5c5) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | SablierBatchLockup | [`0x60dD755dB423EB27983B7000b81e434249670608`](https://explorer.zksync.io/address/0x60dD755dB423EB27983B7000b81e434249670608) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | Helpers | [`0xa0a1ac47260b95d334763473b868117ef7343aa0`](https://explorer.zksync.io/address/0xa0a1ac47260b95d334763473b868117ef7343aa0) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupMath | [`0x1feb172238638897b13b69c65feb508a0a96b35d`](https://explorer.zksync.io/address/0x1feb172238638897b13b69c65feb508a0a96b35d) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupNFTDescriptor | [`0x955dC7A2170782344FA9Ac11De0C0C42C05De2Fc`](https://explorer.zksync.io/address/0x955dC7A2170782344FA9Ac11De0C0C42C05De2Fc) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | ## Testnets ### Arbitrum Sepolia ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x5bd5a50100d0cbc93837a1d10c816614008554fe`](https://sepolia.arbiscan.io/address/0x5bd5a50100d0cbc93837a1d10c816614008554fe) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | SablierBatchLockup | [`0x1e963e9214679757ecbf2fa98499f1e61c44697c`](https://sepolia.arbiscan.io/address/0x1e963e9214679757ecbf2fa98499f1e61c44697c) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | Helpers | [`0xa0A1aC47260B95D334763473B868117EF7343aA0`](https://sepolia.arbiscan.io/address/0xa0A1aC47260B95D334763473B868117EF7343aA0) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupMath | [`0x1feB172238638897B13b69C65feB508a0a96b35D`](https://sepolia.arbiscan.io/address/0x1feB172238638897B13b69C65feB508a0a96b35D) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupNFTDescriptor | [`0x8224eb5D7d76B2D7Df43b868D875E79B11500eA8`](https://sepolia.arbiscan.io/address/0x8224eb5D7d76B2D7Df43b868D875E79B11500eA8) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | ### Base Sepolia ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x5C51EA827Bfa65f7c9AF699e19Ec9fB12A2D40E2`](https://sepolia.basescan.org/address/0x5C51EA827Bfa65f7c9AF699e19Ec9fB12A2D40E2) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | SablierBatchLockup | [`0xB88458246a0657a54cc7b32C3f3a19969cdD571A`](https://sepolia.basescan.org/address/0xB88458246a0657a54cc7b32C3f3a19969cdD571A) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | Helpers | [`0xa0A1aC47260B95D334763473B868117EF7343aA0`](https://sepolia.basescan.org/address/0xa0A1aC47260B95D334763473B868117EF7343aA0) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupMath | [`0x1feB172238638897B13b69C65feB508a0a96b35D`](https://sepolia.basescan.org/address/0x1feB172238638897B13b69C65feB508a0a96b35D) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupNFTDescriptor | [`0xCA2593027BA24856c292Fdcb5F987E0c25e755a4`](https://sepolia.basescan.org/address/0xCA2593027BA24856c292Fdcb5F987E0c25e755a4) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | ### OP Sepolia ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0xc9a70D7190bE5dafDce4B09c4e78bB7C4A71255D`](https://optimism-sepolia.blockscout.com/address/0xc9a70D7190bE5dafDce4B09c4e78bB7C4A71255D) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | SablierBatchLockup | [`0x279593Bb164c79Ae8220A48F616434203B171E6d`](https://optimism-sepolia.blockscout.com/address/0x279593Bb164c79Ae8220A48F616434203B171E6d) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | Helpers | [`0xa0A1aC47260B95D334763473B868117EF7343aA0`](https://optimism-sepolia.blockscout.com/address/0xa0A1aC47260B95D334763473B868117EF7343aA0) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupMath | [`0x1feB172238638897B13b69C65feB508a0a96b35D`](https://optimism-sepolia.blockscout.com/address/0x1feB172238638897B13b69C65feB508a0a96b35D) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupNFTDescriptor | [`0xDf6163ddD3Ebcb552Cc1379a9c65AFe68683534e`](https://optimism-sepolia.blockscout.com/address/0xDf6163ddD3Ebcb552Cc1379a9c65AFe68683534e) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | ### Sepolia ✅ Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierLockup | [`0x6b0307b4338f2963A62106028E3B074C2c0510DA`](https://sepolia.etherscan.io/address/0x6b0307b4338f2963A62106028E3B074C2c0510DA) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | SablierBatchLockup | [`0x44Fd5d5854833975E5Fc80666a10cF3376C088E0`](https://sepolia.etherscan.io/address/0x44Fd5d5854833975E5Fc80666a10cF3376C088E0) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | Helpers | [`0xa0A1aC47260B95D334763473B868117EF7343aA0`](https://sepolia.etherscan.io/address/0xa0A1aC47260B95D334763473B868117EF7343aA0) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupMath | [`0x1feB172238638897B13b69C65feB508a0a96b35D`](https://sepolia.etherscan.io/address/0x1feB172238638897B13b69C65feB508a0a96b35D) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | | LockupNFTDescriptor | [`0x955dC7A2170782344FA9Ac11De0C0C42C05De2Fc`](https://sepolia.etherscan.io/address/0x955dC7A2170782344FA9Ac11De0C0C42C05De2Fc) | [`lockup-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/lockup/v3.0) | --- ## Access Control Source: https://docs.sablier.com/reference/lockup/access-control # Access Control With the exception of the [admin functions](/concepts/governance#lockup), all functions in Lockup can only be triggered by users. The Comptroller has no control over any stream or any part of the protocol. This article will provide a comprehensive overview of the actions that can be performed on streams once they are created, as well as the corresponding user permissions for each action. :::note Every stream has a sender and a recipient. Recipients can approve third parties to take actions on their behalf. A 'public' caller is any address outside of sender and recipient. ::: ## Overview The table below offers a quick overview of the access control for each action that can be performed on a stream. | Action | Sender | Recipient / Approved third party | Public | | --- | :---: | :---: | :---: | | Burn NFT | ❌ | ✅ | ❌ | | Cancel | ✅ | ❌ | ❌ | | Cancel Multiple | ✅ | ❌ | ❌ | | Renounce | ✅ | ❌ | ❌ | | Transfer NFT | ❌ | ✅ | ❌ | | Withdraw to any address | ❌ | ✅ | ❌ | | Withdraw to recipient | ✅ | ✅ | ✅ | | Withdraw Multiple | ✅ | ✅ | ✅ | ## Burn NFT Either the recipient or an approved operator can burn the NFT associated with a stream. #### With Operator: ## Cancel Only the sender can cancel a stream. ## Cancel Multiple Only the sender can cancel multiple streams. ## Renounce Only the sender can renounce a stream. ## Transfer NFT Either the recipient or an approved operator can transfer the NFT associated with a stream. - Only if the stream is transferable. #### With Operator: ## Withdraw Multiple Anybody can withdraw tokens from multiple streams to the recipients of each stream. ## Withdraw to Any Address The tokens in a stream can be withdrawn to any address only by the recipient or an approved third party. #### With Operator: ## Withdraw to Recipient The tokens in a stream can be withdrawn to the recipient by anyone including the sender, recipient, or an approved third party. ```mermaid sequenceDiagram actor Recipient Recipient ->> Lockup: burn() Recipient -->> address(0): Transfer stream NFT ``` ```mermaid sequenceDiagram actor Recipient actor Operator Recipient ->> Lockup: approve(operator) Operator ->> Lockup: burn() Recipient -->> address(0): Transfer stream NFT ``` ```mermaid sequenceDiagram actor Sender Sender ->> Lockup: cancel() Lockup -->> Sender: Transfer unvested tokens ``` ```mermaid sequenceDiagram actor Sender Sender ->> Lockup: cancelMultiple() Lockup -->> Sender: Transfer unvested tokens from multiple streams ``` ```mermaid sequenceDiagram actor Sender Sender ->> Lockup: renounce() ``` ```mermaid sequenceDiagram actor Recipient Recipient ->> Lockup: transfer(toAddress) Create actor toAddress Recipient -->> toAddress: Transfer NFT ``` ```mermaid sequenceDiagram actor Recipient actor Operator Recipient ->> Lockup: approve(operator) Operator ->> Lockup: transfer(toAddress) Create actor toAddress Recipient -->> toAddress: Transfer NFT ``` ```mermaid sequenceDiagram actor Anyone Anyone ->> Lockup: withdrawMultiple() Create actor getRecipient(1) Lockup -->> getRecipient(1): Transfer vested tokens from stream 1 Create actor getRecipient(2) Lockup -->> getRecipient(2): Transfer vested tokens from stream 2 Create actor getRecipient(3) Lockup -->> getRecipient(3): Transfer vested tokens from stream 3 ``` ```mermaid sequenceDiagram actor Recipient Recipient ->> Lockup: withdraw(toAddress) Create actor toAddress Lockup -->> toAddress: Transfer vested tokens ``` ```mermaid sequenceDiagram actor Recipient actor Operator Recipient ->> Lockup: approve(operator) Operator ->> Lockup: withdraw(toAddress) Create actor toAddress Lockup -->> toAddress: Transfer vested tokens ``` ```mermaid sequenceDiagram actor Anyone Anyone ->> Lockup: withdraw(recipient) Create actor Recipient Lockup -->> Recipient: Transfer vested tokens ``` --- ## Batch Source: https://docs.sablier.com/reference/lockup/contracts/abstracts/abstract.Batch # Batch [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/Batch.sol) **Inherits:** [IBatch](/reference/lockup/contracts/interfaces/interface.IBatch) **Title:** Batch See the documentation in [IBatch](/reference/lockup/contracts/interfaces/interface.IBatch). ## Functions ### batch Allows batched calls to self, i.e., `this` contract. Since `msg.value` can be reused across calls, be VERY CAREFUL when using it. Refer to [https://paradigm.xyz/2021/08/two-rights-might-make-a-wrong](https://paradigm.xyz/2021/08/two-rights-might-make-a-wrong) for more information. ```solidity function batch(bytes[] calldata calls) external payable virtual override returns (bytes[] memory results); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `calls` | `bytes[]` | An array of inputs for each call. | **Returns** | Name | Type | Description | | --- | --- | --- | | `results` | `bytes[]` | An array of results from each call. Empty when the calls do not return anything. | --- ## Comptrollerable Source: https://docs.sablier.com/reference/lockup/contracts/abstracts/abstract.Comptrollerable # Comptrollerable [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/Comptrollerable.sol) **Inherits:** [IComptrollerable](/reference/lockup/contracts/interfaces/interface.IComptrollerable) **Title:** Comptrollerable See the documentation in [IComptrollerable](/reference/lockup/contracts/interfaces/interface.IComptrollerable). ## State Variables ### comptroller Retrieves the address of the comptroller contract. ```solidity ISablierComptroller public override comptroller ``` ## Functions ### onlyComptroller Reverts if called by any account other than the comptroller. ```solidity modifier onlyComptroller() ; ``` ### constructor ```solidity constructor(address initialComptroller) ; ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `initialComptroller` | `address` | The address of the initial comptroller contract. | ### setComptroller Sets the comptroller to a new address. Emits a {SetComptroller} event. Requirements: - `msg.sender` must be the current comptroller. - The new comptroller must return `true` from {supportsInterface} with the comptroller's minimal interface ID which is defined as the XOR of the following function selectors: 1. {calculateMinFeeWeiFor} 2. {convertUSDFeeToWei} 3. {execute} 4. {getMinFeeUSDFor} ```solidity function setComptroller(ISablierComptroller newComptroller) external override onlyComptroller; ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `newComptroller` | `ISablierComptroller` | The address of the new comptroller contract. | ### transferFeesToComptroller Transfers the fees to the comptroller contract. Emits a {TransferFeesToComptroller} event. ```solidity function transferFeesToComptroller() external override; ``` ### \_checkComptroller See the documentation for the user-facing functions that call this private function. ```solidity function _checkComptroller() private view; ``` ### \_setComptroller See the documentation for the user-facing functions that call this private function. ```solidity function _setComptroller( ISablierComptroller previousComptroller, ISablierComptroller newComptroller, bytes4 minimalInterfaceId ) private; ``` --- ## NoDelegateCall Source: https://docs.sablier.com/reference/lockup/contracts/abstracts/abstract.NoDelegateCall # NoDelegateCall [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/NoDelegateCall.sol) **Title:** NoDelegateCall This contract implements logic to prevent delegate calls. ## Constants ### ORIGINAL The address of the original contract that was deployed. ```solidity address private immutable ORIGINAL ``` ## Functions ### noDelegateCall Prevents delegate calls. ```solidity modifier noDelegateCall() ; ``` ### constructor Sets the original contract address. ```solidity constructor() ; ``` ### \_preventDelegateCall This function checks whether the current call is a delegate call, and reverts if it is. - A private function is used instead of inlining this logic in a modifier because Solidity copies modifiers into every function that uses them. The `ORIGINAL` address would get copied in every place the modifier is used, which would increase the contract size. By using a function instead, we can avoid this duplication of code and reduce the overall size of the contract. ```solidity function _preventDelegateCall() private view; ``` --- ## SablierLockupDynamic Source: https://docs.sablier.com/reference/lockup/contracts/abstracts/abstract.SablierLockupDynamic # SablierLockupDynamic [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/abstracts/SablierLockupDynamic.sol) **Inherits:** [ISablierLockupDynamic](/reference/lockup/contracts/interfaces/interface.ISablierLockupDynamic), [NoDelegateCall](/reference/lockup/contracts/abstracts/abstract.NoDelegateCall), [SablierLockupState](/reference/lockup/contracts/abstracts/abstract.SablierLockupState) **Title:** SablierLockupDynamic See the documentation in [ISablierLockupDynamic](/reference/lockup/contracts/interfaces/interface.ISablierLockupDynamic). ## Functions ### createWithDurationsLD Creates a stream by setting the start time to `block.timestamp`, and the end time to the sum of `block.timestamp` and all specified time durations. The segment timestamps are derived from these durations. The stream is funded by `msg.sender` and is wrapped in an ERC-721 NFT. Emits a {Transfer}, {CreateLockupDynamicStream} and {MetadataUpdate} event. Requirements: - All requirements in {createWithTimestampsLD} must be met for the calculated parameters. ```solidity function createWithDurationsLD( Lockup.CreateWithDurations calldata params, LockupDynamic.SegmentWithDuration[] calldata segmentsWithDuration ) external payable override noDelegateCall returns (uint256 streamId); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `params` | `Lockup.CreateWithDurations` | Struct encapsulating the function parameters, which are documented in {Lockup} type. | | `segmentsWithDuration` | `LockupDynamic.SegmentWithDuration[]` | Segments with durations used to compose the dynamic distribution function. Timestamps are calculated by starting from `block.timestamp` and adding each duration to the previous timestamp. | **Returns** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the newly created stream. | ### createWithTimestampsLD Creates a stream with the provided segment timestamps, implying the end time from the last timestamp. The stream is funded by `msg.sender` and is wrapped in an ERC-721 NFT. Emits a {Transfer}, {CreateLockupDynamicStream} and {MetadataUpdate} event. Notes: - As long as the segment timestamps are arranged in ascending order, it is not an error for some of them to be in the past. Requirements: - Must not be delegate called. - `params.depositAmount` must be greater than zero. - `params.timestamps.start` must be greater than zero and less than the first segment's timestamp. - `segments` must have at least one segment. - The segment timestamps must be arranged in ascending order. - `params.timestamps.end` must be equal to the last segment's timestamp. - The sum of the segment amounts must equal the deposit amount. - `params.recipient` must not be the zero address. - `params.sender` must not be the zero address. - `msg.sender` must have allowed this contract to spend at least `params.depositAmount` tokens. - `params.token` must not be the native token. - `params.shape.length` must not be greater than 32 characters. ```solidity function createWithTimestampsLD( Lockup.CreateWithTimestamps calldata params, LockupDynamic.Segment[] calldata segments ) external payable override noDelegateCall returns (uint256 streamId); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `params` | `Lockup.CreateWithTimestamps` | Struct encapsulating the function parameters, which are documented in {Lockup} type. | | `segments` | `LockupDynamic.Segment[]` | Segments used to compose the dynamic distribution function. | **Returns** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the newly created stream. | ### \_createLD See the documentation for the user-facing functions that call this private function. ```solidity function _createLD( bool cancelable, uint128 depositAmount, address recipient, LockupDynamic.Segment[] memory segments, address sender, string memory shape, Lockup.Timestamps memory timestamps, IERC20 token, bool transferable ) private returns (uint256 streamId); ``` --- ## SablierLockupLinear Source: https://docs.sablier.com/reference/lockup/contracts/abstracts/abstract.SablierLockupLinear # SablierLockupLinear [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/abstracts/SablierLockupLinear.sol) **Inherits:** [ISablierLockupLinear](/reference/lockup/contracts/interfaces/interface.ISablierLockupLinear), [NoDelegateCall](/reference/lockup/contracts/abstracts/abstract.NoDelegateCall), [SablierLockupState](/reference/lockup/contracts/abstracts/abstract.SablierLockupState) **Title:** SablierLockupLinear See the documentation in [ISablierLockupLinear](/reference/lockup/contracts/interfaces/interface.ISablierLockupLinear). ## Functions ### createWithDurationsLL Creates a stream by setting the start time to `block.timestamp`, and the end time to the sum of `block.timestamp` and `durations.total`. The stream is funded by `msg.sender` and is wrapped in an ERC-721 NFT. Emits a {Transfer}, {CreateLockupLinearStream} and {MetadataUpdate} event. Requirements: - All requirements in {createWithTimestampsLL} must be met for the calculated parameters. ```solidity function createWithDurationsLL( Lockup.CreateWithDurations calldata params, LockupLinear.UnlockAmounts calldata unlockAmounts, uint40 granularity, LockupLinear.Durations calldata durations ) external payable override noDelegateCall returns (uint256 streamId); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `params` | `Lockup.CreateWithDurations` | Struct encapsulating the function parameters, which are documented in {Lockup} type. | | `unlockAmounts` | `LockupLinear.UnlockAmounts` | Struct encapsulating (i) the amount to unlock at the start time and (ii) the amount to unlock at the cliff time. | | `granularity` | `uint40` | The smallest step in time between two consecutive token unlocks. Zero is a sentinel value for 1 second. | | `durations` | `LockupLinear.Durations` | Struct encapsulating (i) cliff period duration and (ii) total stream duration, both in seconds. | **Returns** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the newly created stream. | ### createWithTimestampsLL Creates a stream with the provided start time and end time. The stream is funded by `msg.sender` and is wrapped in an ERC-721 NFT. Emits a {Transfer}, {CreateLockupLinearStream} and {MetadataUpdate} event. Notes: - A cliff time of zero means there is no cliff. - As long as the times are ordered, it is not an error for the start or the cliff time to be in the past. Requirements: - Must not be delegate called. - `params.depositAmount` must be greater than zero. - `params.timestamps.start` must be greater than zero and less than `params.timestamps.end`. - If set, `cliffTime` must be greater than `params.timestamps.start` and less than `params.timestamps.end`. - `params.recipient` must not be the zero address. - `params.sender` must not be the zero address. - The sum of `params.unlockAmounts.start` and `params.unlockAmounts.cliff` must be less than or equal to deposit amount. - If `params.timestamps.cliff` is not set, the `params.unlockAmounts.cliff` must be zero. - `granularity` must not exceed the streamable range which is `params.timestamps.end - cliffTime` if cliff is set, `params.timestamps.end - params.timestamps.start` otherwise. - `msg.sender` must have allowed this contract to spend at least `params.depositAmount` tokens. - `params.token` must not be the native token. - `params.shape.length` must not be greater than 32 characters. ```solidity function createWithTimestampsLL( Lockup.CreateWithTimestamps calldata params, LockupLinear.UnlockAmounts calldata unlockAmounts, uint40 granularity, uint40 cliffTime ) external payable override noDelegateCall returns (uint256 streamId); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `params` | `Lockup.CreateWithTimestamps` | Struct encapsulating the function parameters, which are documented in {Lockup} type. | | `unlockAmounts` | `LockupLinear.UnlockAmounts` | Struct encapsulating (i) the amount to unlock at the start time and (ii) the amount to unlock at the cliff time. | | `granularity` | `uint40` | The smallest step in time between two consecutive token unlocks. Zero is a sentinel value for 1 second. | | `cliffTime` | `uint40` | The Unix timestamp for the cliff period's end. A value of zero means there is no cliff. | **Returns** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the newly created stream. | ### \_createLL See the documentation for the user-facing functions that call this private function. ```solidity function _createLL( bool cancelable, uint40 cliffTime, uint128 depositAmount, address recipient, address sender, string memory shape, Lockup.Timestamps memory timestamps, IERC20 token, bool transferable, LockupLinear.UnlockAmounts memory unlockAmounts, uint40 granularity ) private returns (uint256 streamId); ``` --- ## SablierLockupPriceGated Source: https://docs.sablier.com/reference/lockup/contracts/abstracts/abstract.SablierLockupPriceGated # SablierLockupPriceGated [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/abstracts/SablierLockupPriceGated.sol) **Inherits:** [ISablierLockupPriceGated](/reference/lockup/contracts/interfaces/interface.ISablierLockupPriceGated), [NoDelegateCall](/reference/lockup/contracts/abstracts/abstract.NoDelegateCall), [SablierLockupState](/reference/lockup/contracts/abstracts/abstract.SablierLockupState) **Title:** SablierLockupPriceGated See the documentation in [ISablierLockupPriceGated](/reference/lockup/contracts/interfaces/interface.ISablierLockupPriceGated). ## Functions ### createWithTimestampsLPG Creates a stream with the provided start time and end time. The stream is funded by `msg.sender` and is wrapped in an ERC-721 NFT. Emits a {Transfer}, {CreateLockupPriceGatedStream} and {MetadataUpdate} event. Notes: - The recipient can withdraw the full deposited amount when either: 1. The oracle price reaches or exceeds the target price, OR 2. Current time is greater than or equal to the stream's end time. - The sender can cancel the stream when price is less than target price AND end time is in the future. - The function does not check if the provided oracle reports the price for the deposited token. It may be possible that stream creator has used a different token for the oracle. In such cases, integrators and recipients are requested to verify the oracle correctness on their own. - The LPG model does not support a "createWithDuration" function because the [SablierLockup](/reference/lockup/contracts/contract.SablierLockup) contract is at the size limit. If the EVM contract size limit is increased in the future, this function will be added. Requirements: - Must not be delegate called. - `params.depositAmount` must be greater than zero. - `params.sender` must not be the zero address. - `params.recipient` must not be the zero address. - `params.timestamps.start` must not be zero. - `params.timestamps.start` must be less than `params.timestamps.end`. - `unlockParams.oracle` must implement Chainlink's {AggregatorV3Interface} interface. - `unlockParams.oracle` must return a non-zero value no greater than 36 when the `decimals()` function is called. - `unlockParams.oracle` must return a positive price when the `latestRoundData()` function is called. - `unlockParams.targetPrice` must be greater than the current oracle price. - `msg.sender` must have allowed this contract to spend at least `params.depositAmount` tokens. - `params.token` must not be the native token. - `params.shape.length` must not be greater than 32 characters. ```solidity function createWithTimestampsLPG( Lockup.CreateWithTimestamps calldata params, LockupPriceGated.UnlockParams calldata unlockParams ) external payable override noDelegateCall returns (uint256 streamId); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `params` | `Lockup.CreateWithTimestamps` | Struct encapsulating the function parameters, which are documented in {Lockup} type. | | `unlockParams` | `LockupPriceGated.UnlockParams` | Struct encapsulating the unlock parameters, documented in {LockupPriceGated}. | **Returns** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the newly created stream. | ### \_createLPG See the documentation for the user-facing functions that call this private function. ```solidity function _createLPG( Lockup.CreateWithTimestamps calldata params, LockupPriceGated.UnlockParams calldata unlockParams ) private returns (uint256 streamId); ``` --- ## SablierLockupState Source: https://docs.sablier.com/reference/lockup/contracts/abstracts/abstract.SablierLockupState # SablierLockupState [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/abstracts/SablierLockupState.sol) **Inherits:** [ISablierLockupState](/reference/lockup/contracts/interfaces/interface.ISablierLockupState) **Title:** SablierLockupState See the documentation in [ISablierLockupState](/reference/lockup/contracts/interfaces/interface.ISablierLockupState). ## State Variables ### aggregateAmount ```solidity mapping(IERC20 token => uint256 amount) public override aggregateAmount ``` ### nativeToken Retrieves the address of the ERC-20 interface of the native token, if it exists. The native tokens on some chains have a dual interface as ERC-20. For example, on Polygon the $POL token is the native token and has an ERC-20 version at 0x0000000000000000000000000000000000001010. This means that `address(this).balance` returns the same value as `balanceOf(address(this))`. To avoid any unintended behavior, these tokens cannot be used in Sablier. As an alternative, users can use the Wrapped version of the token, i.e. WMATIC, which is a standard ERC-20 token. ```solidity address public override nativeToken ``` ### nextStreamId Counter for stream IDs, used in the create functions. ```solidity uint256 public override nextStreamId ``` ### nftDescriptor Contract that generates the non-fungible token URI. ```solidity ILockupNFTDescriptor public override nftDescriptor ``` ### \_allowedToHook Mapping of contracts allowed to hook to Sablier when a stream is canceled or when tokens are withdrawn. ```solidity mapping(address recipient => bool allowed) internal _allowedToHook ``` ### \_cliffs Cliff timestamp mapped by stream IDs, used in LL streams. ```solidity mapping(uint256 streamId => uint40 cliffTime) internal _cliffs ``` ### \_granularities Granularity mapped by stream IDs, used in LL streams. ```solidity mapping(uint256 streamId => uint40 granularity) internal _granularities ``` ### \_priceGatedUnlockParams Unlock parameters mapped by stream IDs, used in LPG streams. ```solidity mapping(uint256 streamId => LockupPriceGated.UnlockParams unlockParams) internal _priceGatedUnlockParams ``` ### \_segments Stream segments mapped by stream IDs, used in LD streams. ```solidity mapping(uint256 streamId => LockupDynamic.Segment[] segments) internal _segments ``` ### \_streams Lockup streams mapped by unsigned integers. ```solidity mapping(uint256 id => Lockup.Stream stream) internal _streams ``` ### \_tranches Stream tranches mapped by stream IDs, used in LT streams. ```solidity mapping(uint256 streamId => LockupTranched.Tranche[] tranches) internal _tranches ``` ### \_unlockAmounts Unlock amounts mapped by stream IDs, used in LL streams. ```solidity mapping(uint256 streamId => LockupLinear.UnlockAmounts unlockAmounts) internal _unlockAmounts ``` ## Functions ### modelCheck Checks that actual model and expected model are equal. ```solidity modifier modelCheck(Lockup.Model actualModel, Lockup.Model expectedModel) ; ``` ### notNull Checks that `streamId` does not reference a null stream. ```solidity modifier notNull(uint256 streamId) ; ``` ### constructor ```solidity constructor(address initialNFTDescriptor) ; ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `initialNFTDescriptor` | `address` | The address of the initial NFT descriptor. | ### getCliffTime Retrieves the stream's cliff time, which is a Unix timestamp. A value of zero means there is no cliff. Reverts if `streamId` references either a null stream or a non-LL stream. ```solidity function getCliffTime(uint256 streamId) external view override notNull(streamId) modelCheck(_streams[streamId].lockupModel, Lockup.Model.LOCKUP_LINEAR) returns (uint40 cliffTime); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### getDepositedAmount Retrieves the amount deposited in the stream, denoted in units of the token's decimals. Reverts if `streamId` references a null stream. ```solidity function getDepositedAmount(uint256 streamId) external view override notNull(streamId) returns (uint128 depositedAmount); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### getEndTime Retrieves the stream's end time, which is a Unix timestamp. Reverts if `streamId` references a null stream. ```solidity function getEndTime(uint256 streamId) external view override notNull(streamId) returns (uint40 endTime); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### getGranularity Retrieves the smallest step in time between two consecutive token unlocks. Reverts if `streamId` references either a null stream or a non-LL stream. ```solidity function getGranularity(uint256 streamId) external view override notNull(streamId) modelCheck(_streams[streamId].lockupModel, Lockup.Model.LOCKUP_LINEAR) returns (uint40 granularity); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### getLockupModel Retrieves the distribution models used to create the stream. Reverts if `streamId` references a null stream. ```solidity function getLockupModel(uint256 streamId) external view override notNull(streamId) returns (Lockup.Model lockupModel); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### getPriceGatedUnlockParams Retrieves the unlock parameters of a price-gated stream. Reverts if `streamId` references either a null stream or a non-LPG stream. ```solidity function getPriceGatedUnlockParams(uint256 streamId) external view override notNull(streamId) modelCheck(_streams[streamId].lockupModel, Lockup.Model.LOCKUP_PRICE_GATED) returns (LockupPriceGated.UnlockParams memory unlockParams); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | **Returns** | Name | Type | Description | | --- | --- | --- | | `unlockParams` | `LockupPriceGated.UnlockParams` | See the documentation in {LockupPriceGated} type. | ### getRefundedAmount Retrieves the amount refunded to the sender after a cancellation, denoted in units of the token's decimals. This amount is always zero unless the stream was canceled. Reverts if `streamId` references a null stream. ```solidity function getRefundedAmount(uint256 streamId) external view override notNull(streamId) returns (uint128 refundedAmount); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### getSegments Retrieves the segments used to compose the dynamic distribution function. Reverts if `streamId` references either a null stream or a non-LD stream. ```solidity function getSegments(uint256 streamId) external view override notNull(streamId) modelCheck(_streams[streamId].lockupModel, Lockup.Model.LOCKUP_DYNAMIC) returns (LockupDynamic.Segment[] memory segments); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | **Returns** | Name | Type | Description | | --- | --- | --- | | `segments` | `LockupDynamic.Segment[]` | See the documentation in {LockupDynamic} type. | ### getSender Retrieves the stream's sender. Reverts if `streamId` references a null stream. ```solidity function getSender(uint256 streamId) external view override notNull(streamId) returns (address sender); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### getStartTime Retrieves the stream's start time, which is a Unix timestamp. Reverts if `streamId` references a null stream. ```solidity function getStartTime(uint256 streamId) external view override notNull(streamId) returns (uint40 startTime); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### getTranches Retrieves the tranches used to compose the tranched distribution function. Reverts if `streamId` references either a null stream or a non-LT stream. ```solidity function getTranches(uint256 streamId) external view override notNull(streamId) modelCheck(_streams[streamId].lockupModel, Lockup.Model.LOCKUP_TRANCHED) returns (LockupTranched.Tranche[] memory tranches); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | **Returns** | Name | Type | Description | | --- | --- | --- | | `tranches` | `LockupTranched.Tranche[]` | See the documentation in {LockupTranched} type. | ### getUnderlyingToken Retrieves the address of the underlying ERC-20 token being distributed. Reverts if `streamId` references a null stream. ```solidity function getUnderlyingToken(uint256 streamId) external view override notNull(streamId) returns (IERC20 token); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### getUnlockAmounts Retrieves the unlock amounts used to compose the linear distribution function. Reverts if `streamId` references either a null stream or a non-LL stream. ```solidity function getUnlockAmounts(uint256 streamId) external view override notNull(streamId) modelCheck(_streams[streamId].lockupModel, Lockup.Model.LOCKUP_LINEAR) returns (LockupLinear.UnlockAmounts memory unlockAmounts); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | **Returns** | Name | Type | Description | | --- | --- | --- | | `unlockAmounts` | `LockupLinear.UnlockAmounts` | See the documentation in {LockupLinear} type. | ### getWithdrawnAmount Retrieves the amount withdrawn from the stream, denoted in units of the token's decimals. Reverts if `streamId` references a null stream. ```solidity function getWithdrawnAmount(uint256 streamId) external view override notNull(streamId) returns (uint128 withdrawnAmount); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### isAllowedToHook Retrieves a flag indicating whether the provided address is a contract allowed to hook to Sablier when a stream is canceled or when tokens are withdrawn. See [ISablierLockupRecipient](/reference/lockup/contracts/interfaces/interface.ISablierLockupRecipient) for more information. ```solidity function isAllowedToHook(address recipient) external view returns (bool result); ``` ### isCancelable Retrieves a flag indicating whether the stream can be canceled. When the stream is cold, this flag is always `false`. Reverts if `streamId` references a null stream. ```solidity function isCancelable(uint256 streamId) external view override notNull(streamId) returns (bool result); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### isDepleted Retrieves a flag indicating whether the stream is depleted. Reverts if `streamId` references a null stream. ```solidity function isDepleted(uint256 streamId) external view override notNull(streamId) returns (bool result); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### isStream Retrieves a flag indicating whether the stream exists. Does not revert if `streamId` references a null stream. ```solidity function isStream(uint256 streamId) external view override returns (bool result); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### isTransferable Retrieves a flag indicating whether the stream NFT can be transferred. Reverts if `streamId` references a null stream. ```solidity function isTransferable(uint256 streamId) external view override notNull(streamId) returns (bool result); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### wasCanceled Retrieves a flag indicating whether the stream was canceled. Reverts if `streamId` references a null stream. ```solidity function wasCanceled(uint256 streamId) external view override notNull(streamId) returns (bool result); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### \_statusOf Retrieves the stream's status without performing a null check. ```solidity function _statusOf(uint256 streamId) internal view returns (Lockup.Status); ``` ### \_streamedAmountOf Calculates the streamed amount of the stream. This function is implemented by child contract. The logic varies according to the distribution model. ```solidity function _streamedAmountOf(uint256 streamId) internal view virtual returns (uint128); ``` ### \_create This function is implemented by [SablierLockup](/reference/lockup/contracts/contract.SablierLockup) and is used in the [SablierLockupDynamic](/reference/lockup/contracts/abstracts/abstract.SablierLockupDynamic), [SablierLockupLinear](/reference/lockup/contracts/abstracts/abstract.SablierLockupLinear) and [SablierLockupTranched](/reference/lockup/contracts/abstracts/abstract.SablierLockupTranched) contracts. It updates state variables based on the stream parameters, mints an NFT to the recipient, bumps stream ID, and transfers the deposit amount. ```solidity function _create( bool cancelable, uint128 depositAmount, Lockup.Model lockupModel, address recipient, address sender, uint256 streamId, Lockup.Timestamps memory timestamps, IERC20 token, bool transferable ) internal virtual; ``` ### \_modelCheck Reverts if actual model and expected model are not equal. ```solidity function _modelCheck(Lockup.Model actualModel, Lockup.Model expectedModel) private pure; ``` ### \_notNull A private function is used instead of inlining this logic in a 3 because Solidity copies modifiers into every function that uses them. ```solidity function _notNull(uint256 streamId) private view; ``` --- ## SablierLockupTranched Source: https://docs.sablier.com/reference/lockup/contracts/abstracts/abstract.SablierLockupTranched # SablierLockupTranched [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/abstracts/SablierLockupTranched.sol) **Inherits:** [ISablierLockupTranched](/reference/lockup/contracts/interfaces/interface.ISablierLockupTranched), [NoDelegateCall](/reference/lockup/contracts/abstracts/abstract.NoDelegateCall), [SablierLockupState](/reference/lockup/contracts/abstracts/abstract.SablierLockupState) **Title:** SablierLockupTranched See the documentation in [ISablierLockupTranched](/reference/lockup/contracts/interfaces/interface.ISablierLockupTranched). ## Functions ### createWithDurationsLT Creates a stream by setting the start time to `block.timestamp`, and the end time to the sum of `block.timestamp` and all specified time durations. The tranche timestamps are derived from these durations. The stream is funded by `msg.sender` and is wrapped in an ERC-721 NFT. Emits a {Transfer}, {CreateLockupTrancheStream} and {MetadataUpdate} event. Requirements: - All requirements in {createWithTimestampsLT} must be met for the calculated parameters. ```solidity function createWithDurationsLT( Lockup.CreateWithDurations calldata params, LockupTranched.TrancheWithDuration[] calldata tranchesWithDuration ) external payable override noDelegateCall returns (uint256 streamId); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `params` | `Lockup.CreateWithDurations` | Struct encapsulating the function parameters, which are documented in {Lockup} type. | | `tranchesWithDuration` | `LockupTranched.TrancheWithDuration[]` | Tranches with durations used to compose the tranched distribution function. Timestamps are calculated by starting from `block.timestamp` and adding each duration to the previous timestamp. | **Returns** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the newly created stream. | ### createWithTimestampsLT Creates a stream with the provided tranche timestamps, implying the end time from the last timestamp. The stream is funded by `msg.sender` and is wrapped in an ERC-721 NFT. Emits a {Transfer}, {CreateLockupTrancheStream} and {MetadataUpdate} event. Notes: - As long as the tranche timestamps are arranged in ascending order, it is not an error for some of them to be in the past. Requirements: - Must not be delegate called. - `params.depositAmount` must be greater than zero. - `params.timestamps.start` must be greater than zero and less than the first tranche's timestamp. - `tranches` must have at least one tranche. - The tranche timestamps must be arranged in ascending order. - `params.timestamps.end` must be equal to the last tranche's timestamp. - The sum of the tranche amounts must equal the deposit amount. - `params.recipient` must not be the zero address. - `params.sender` must not be the zero address. - `msg.sender` must have allowed this contract to spend at least `params.depositAmount` tokens. - `params.token` must not be the native token. - `params.shape.length` must not be greater than 32 characters. ```solidity function createWithTimestampsLT( Lockup.CreateWithTimestamps calldata params, LockupTranched.Tranche[] calldata tranches ) external payable override noDelegateCall returns (uint256 streamId); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `params` | `Lockup.CreateWithTimestamps` | Struct encapsulating the function parameters, which are documented in {Lockup} type. | | `tranches` | `LockupTranched.Tranche[]` | Tranches used to compose the tranched distribution function. | **Returns** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the newly created stream. | ### \_createLT See the documentation for the user-facing functions that call this private function. ```solidity function _createLT( bool cancelable, uint128 depositAmount, address recipient, address sender, string memory shape, Lockup.Timestamps memory timestamps, IERC20 token, bool transferable, LockupTranched.Tranche[] memory tranches ) private returns (uint256 streamId); ``` --- ## LockupNFTDescriptor Source: https://docs.sablier.com/reference/lockup/contracts/contract.LockupNFTDescriptor # LockupNFTDescriptor [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/LockupNFTDescriptor.sol) **Inherits:** [ILockupNFTDescriptor](/reference/lockup/contracts/interfaces/interface.ILockupNFTDescriptor) **Title:** LockupNFTDescriptor See the documentation in [ILockupNFTDescriptor](/reference/lockup/contracts/interfaces/interface.ILockupNFTDescriptor). ## Functions ### tokenURI Produces the URI describing a particular stream NFT. This is a data URI with the JSON contents directly inlined. ```solidity function tokenURI(IERC721Metadata lockup, uint256 streamId) external view override returns (string memory uri); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `lockup` | `IERC721Metadata` | | | `streamId` | `uint256` | The ID of the stream for which to produce a description. | **Returns** | Name | Type | Description | | --- | --- | --- | | `uri` | `string` | The URI of the ERC721-compliant metadata. | ### abbreviateAmount Creates an abbreviated representation of the provided amount, rounded down and prefixed with ">= ". The abbreviation uses these suffixes: - "K" for thousands - "M" for millions - "B" for billions - "T" for trillions For example, if the input is 1,234,567, the output is ">= 1.23M". ```solidity function abbreviateAmount(uint256 amount, uint256 decimals) internal pure returns (string memory); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `amount` | `uint256` | The amount to abbreviate, denoted in units of `decimals`. | | `decimals` | `uint256` | The number of decimals to assume when abbreviating the amount. | **Returns** | Name | Type | Description | | --- | --- | --- | | `` | `string` | abbreviation The abbreviated representation of the provided amount, as a string. | ### calculateDurationInDays Calculates the stream's duration in days, rounding down. ```solidity function calculateDurationInDays(uint256 startTime, uint256 endTime) internal pure returns (string memory); ``` ### calculateStreamedPercentage Calculates how much of the deposited amount has been streamed so far, as a percentage with 4 implied decimals. ```solidity function calculateStreamedPercentage( uint128 streamedAmount, uint128 depositedAmount ) internal pure returns (uint256); ``` ### generateAccentColor Generates a pseudo-random HSL color by hashing together the `chainid`, the `sablier` address, and the `streamId`. This will be used as the accent color for the SVG. ```solidity function generateAccentColor(address sablier, uint256 streamId) internal view returns (string memory); ``` ### generateAttributes Generates an array of JSON objects that represent the NFT's attributes: - Token symbol - Sender address - Status These attributes are useful for filtering and sorting the NFTs. ```solidity function generateAttributes( string memory tokenSymbol, string memory sender, string memory status ) internal pure returns (string memory); ``` ### generateDescription Generates a string with the NFT's JSON metadata description, which provides a high-level overview. ```solidity function generateDescription( string memory tokenSymbol, string memory lockupStringified, string memory tokenAddress, string memory streamId, bool isTransferable ) internal pure returns (string memory); ``` ### safeTokenDecimals Retrieves the token's decimals safely, defaulting to "0" if an error occurs. Performs a low-level call to handle tokens in which the decimals are not implemented. ```solidity function safeTokenDecimals(address token) internal view returns (uint8); ``` ### stringifyFractionalAmount Converts the provided fractional amount to a string prefixed by a dot. ```solidity function stringifyFractionalAmount(uint256 fractionalAmount) internal pure returns (string memory); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `fractionalAmount` | `uint256` | A numerical value with 2 implied decimals. | ### stringifyPercentage Converts the provided percentage to a string. ```solidity function stringifyPercentage(uint256 percentage) internal pure returns (string memory); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `percentage` | `uint256` | A numerical value with 4 implied decimals. | ### stringifyStatus Retrieves the stream's status as a string. ```solidity function stringifyStatus(Lockup.Status status) internal pure returns (string memory); ``` ## Structs ### TokenURIVars Needed to avoid Stack Too Deep. ```solidity struct TokenURIVars { address token; string tokenSymbol; uint128 depositedAmount; string json; ISablierLockup lockup; string lockupStringified; string status; string svg; uint256 streamedPercentage; } ``` --- ## SablierBatchLockup Source: https://docs.sablier.com/reference/lockup/contracts/contract.SablierBatchLockup # SablierBatchLockup [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/SablierBatchLockup.sol) **Inherits:** [ISablierBatchLockup](/reference/lockup/contracts/interfaces/interface.ISablierBatchLockup) **Title:** SablierBatchLockup See the documentation in [ISablierBatchLockup](/reference/lockup/contracts/interfaces/interface.ISablierBatchLockup). ## Functions ### createWithDurationsLD Creates a batch of LD streams using `createWithDurationsLD`. Requirements: - There must be at least one element in `batch`. - All requirements from {ISablierLockupDynamic.createWithDurationsLD} must be met for each stream. ```solidity function createWithDurationsLD( ISablierLockup lockup, IERC20 token, BatchLockup.CreateWithDurationsLD[] calldata batch ) external override returns (uint256[] memory streamIds); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `lockup` | `ISablierLockup` | The address of the [SablierLockup](/reference/lockup/contracts/contract.SablierLockup) contract. | | `token` | `IERC20` | The contract address of the ERC-20 token to be distributed. | | `batch` | `BatchLockup.CreateWithDurationsLD[]` | An array of structs, each encapsulating a subset of the parameters of {ISablierLockupDynamic.createWithDurationsLD}. | **Returns** | Name | Type | Description | | --- | --- | --- | | `streamIds` | `uint256[]` | The ids of the newly created streams. | ### createWithTimestampsLD Creates a batch of LD streams using `createWithTimestampsLD`. Requirements: - There must be at least one element in `batch`. - All requirements from {ISablierLockupDynamic.createWithTimestampsLD} must be met for each stream. ```solidity function createWithTimestampsLD( ISablierLockup lockup, IERC20 token, BatchLockup.CreateWithTimestampsLD[] calldata batch ) external override returns (uint256[] memory streamIds); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `lockup` | `ISablierLockup` | The address of the [SablierLockup](/reference/lockup/contracts/contract.SablierLockup) contract. | | `token` | `IERC20` | The contract address of the ERC-20 token to be distributed. | | `batch` | `BatchLockup.CreateWithTimestampsLD[]` | An array of structs, each encapsulating a subset of the parameters of {ISablierLockupDynamic.createWithTimestampsLD}. | **Returns** | Name | Type | Description | | --- | --- | --- | | `streamIds` | `uint256[]` | The ids of the newly created streams. | ### createWithDurationsLL Creates a batch of LL streams using `createWithDurationsLL`. Requirements: - There must be at least one element in `batch`. - All requirements from {ISablierLockupLinear.createWithDurationsLL} must be met for each stream. ```solidity function createWithDurationsLL( ISablierLockup lockup, IERC20 token, BatchLockup.CreateWithDurationsLL[] calldata batch ) external override returns (uint256[] memory streamIds); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `lockup` | `ISablierLockup` | The address of the [SablierLockup](/reference/lockup/contracts/contract.SablierLockup) contract. | | `token` | `IERC20` | The contract address of the ERC-20 token to be distributed. | | `batch` | `BatchLockup.CreateWithDurationsLL[]` | An array of structs, each encapsulating a subset of the parameters of {ISablierLockupLinear.createWithDurationsLL}. | **Returns** | Name | Type | Description | | --- | --- | --- | | `streamIds` | `uint256[]` | The ids of the newly created streams. | ### createWithTimestampsLL Creates a batch of LL streams using `createWithTimestampsLL`. Requirements: - There must be at least one element in `batch`. - All requirements from {ISablierLockupLinear.createWithTimestampsLL} must be met for each stream. ```solidity function createWithTimestampsLL( ISablierLockup lockup, IERC20 token, BatchLockup.CreateWithTimestampsLL[] calldata batch ) external override returns (uint256[] memory streamIds); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `lockup` | `ISablierLockup` | The address of the [SablierLockup](/reference/lockup/contracts/contract.SablierLockup) contract. | | `token` | `IERC20` | The contract address of the ERC-20 token to be distributed. | | `batch` | `BatchLockup.CreateWithTimestampsLL[]` | An array of structs, each encapsulating a subset of the parameters of {ISablierLockupLinear.createWithTimestampsLL}. | **Returns** | Name | Type | Description | | --- | --- | --- | | `streamIds` | `uint256[]` | The ids of the newly created streams. | ### createWithTimestampsLPG Creates a batch of LPG streams using `createWithTimestampsLPG`. Notes: - The LPG model does not support a "createWithDuration" function because the [SablierLockup](/reference/lockup/contracts/contract.SablierLockup) contract is at the size limit. If the EVM contract size limit is increased in the future, this function will be added. Requirements: - There must be at least one element in `batch`. - All requirements from {ISablierLockupPriceGated.createWithTimestampsLPG} must be met for each stream. ```solidity function createWithTimestampsLPG( ISablierLockup lockup, IERC20 token, BatchLockup.CreateWithTimestampsLPG[] calldata batch ) external override returns (uint256[] memory streamIds); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `lockup` | `ISablierLockup` | The address of the [SablierLockup](/reference/lockup/contracts/contract.SablierLockup) contract. | | `token` | `IERC20` | The contract address of the ERC-20 token to be distributed. | | `batch` | `BatchLockup.CreateWithTimestampsLPG[]` | An array of structs, each encapsulating a subset of the parameters of {ISablierLockupPriceGated.createWithTimestampsLPG}. | **Returns** | Name | Type | Description | | --- | --- | --- | | `streamIds` | `uint256[]` | The ids of the newly created streams. | ### createWithDurationsLT Creates a batch of LT streams using `createWithDurationsLT`. Requirements: - There must be at least one element in `batch`. - All requirements from {ISablierLockupTranched.createWithDurationsLT} must be met for each stream. ```solidity function createWithDurationsLT( ISablierLockup lockup, IERC20 token, BatchLockup.CreateWithDurationsLT[] calldata batch ) external override returns (uint256[] memory streamIds); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `lockup` | `ISablierLockup` | The address of the [SablierLockup](/reference/lockup/contracts/contract.SablierLockup) contract. | | `token` | `IERC20` | The contract address of the ERC-20 token to be distributed. | | `batch` | `BatchLockup.CreateWithDurationsLT[]` | An array of structs, each encapsulating a subset of the parameters of {ISablierLockupTranched.createWithDurationsLT}. | **Returns** | Name | Type | Description | | --- | --- | --- | | `streamIds` | `uint256[]` | The ids of the newly created streams. | ### createWithTimestampsLT Creates a batch of LT streams using `createWithTimestampsLT`. Requirements: - There must be at least one element in `batch`. - All requirements from {ISablierLockupTranched.createWithTimestampsLT} must be met for each stream. ```solidity function createWithTimestampsLT( ISablierLockup lockup, IERC20 token, BatchLockup.CreateWithTimestampsLT[] calldata batch ) external override returns (uint256[] memory streamIds); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `lockup` | `ISablierLockup` | The address of the [SablierLockup](/reference/lockup/contracts/contract.SablierLockup) contract. | | `token` | `IERC20` | The contract address of the ERC-20 token to be distributed. | | `batch` | `BatchLockup.CreateWithTimestampsLT[]` | An array of structs, each encapsulating a subset of the parameters of {ISablierLockupTranched.createWithTimestampsLT}. | **Returns** | Name | Type | Description | | --- | --- | --- | | `streamIds` | `uint256[]` | The ids of the newly created streams. | ### \_approve Helper function to approve a Lockup contract to spend funds from the batchLockup. If the current allowance is insufficient, this function approves Lockup to spend the exact `amount`. The {SafeERC20.forceApprove} function is used to handle special ERC-20 tokens (e.g. USDT) that require the current allowance to be zero before setting it to a non-zero value. ```solidity function _approve(address lockup, IERC20 token, uint256 amount) internal; ``` ### \_handleTransfer Helper function to transfer tokens from the caller to the batchLockup contract and approve the Lockup contract. ```solidity function _handleTransfer(address lockup, IERC20 token, uint256 amount) internal; ``` --- ## SablierLockup Source: https://docs.sablier.com/reference/lockup/contracts/contract.SablierLockup # SablierLockup [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/SablierLockup.sol) **Inherits:** [Batch](/reference/lockup/contracts/abstracts/abstract.Batch), [Comptrollerable](/reference/lockup/contracts/abstracts/abstract.Comptrollerable), ERC721, [ISablierLockup](/reference/lockup/contracts/interfaces/interface.ISablierLockup), [SablierLockupDynamic](/reference/lockup/contracts/abstracts/abstract.SablierLockupDynamic), [SablierLockupLinear](/reference/lockup/contracts/abstracts/abstract.SablierLockupLinear), [SablierLockupPriceGated](/reference/lockup/contracts/abstracts/abstract.SablierLockupPriceGated), [SablierLockupTranched](/reference/lockup/contracts/abstracts/abstract.SablierLockupTranched) **Title:** SablierLockup See the documentation in [ISablierLockup](/reference/lockup/contracts/interfaces/interface.ISablierLockup). ## Functions ### constructor ```solidity constructor( address initialComptroller, address initialNFTDescriptor ) [Comptrollerable](/docs/reference/02-lockup/contracts/abstracts/abstract.Comptrollerable.md)(initialComptroller) ERC721("Sablier Lockup NFT", "SAB-LOCKUP") SablierLockupState(initialNFTDescriptor); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `initialComptroller` | `address` | The address of the initial comptroller contract. | | `initialNFTDescriptor` | `address` | The address of the NFT descriptor contract. | ### calculateMinFeeWei Calculates the minimum fee in wei required to withdraw from the given stream ID. Reverts if `streamId` references a null stream. ```solidity function calculateMinFeeWei(uint256 streamId) external view override notNull(streamId) returns (uint256 minFeeWei); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### getRecipient Retrieves the stream's recipient. Reverts if the NFT has been burned. ```solidity function getRecipient(uint256 streamId) external view override returns (address recipient); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### isCold Retrieves a flag indicating whether the stream is cold, i.e. settled, canceled, or depleted. Reverts if `streamId` references a null stream. ```solidity function isCold(uint256 streamId) external view override notNull(streamId) returns (bool result); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### isWarm Retrieves a flag indicating whether the stream is warm, i.e. either pending or streaming. Reverts if `streamId` references a null stream. ```solidity function isWarm(uint256 streamId) external view override notNull(streamId) returns (bool result); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### refundableAmountOf Calculates the amount that the sender would be refunded if the stream were canceled, denoted in units of the token's decimals. Reverts if `streamId` references a null stream. ```solidity function refundableAmountOf(uint256 streamId) external view override notNull(streamId) returns (uint128 refundableAmount); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### statusOf Retrieves the stream's status. Reverts if `streamId` references a null stream. ```solidity function statusOf(uint256 streamId) external view override notNull(streamId) returns (Lockup.Status status); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### streamedAmountOf Calculates the amount streamed to the recipient, denoted in units of the token's decimals. Reverts if `streamId` references a null stream. Notes: - Upon cancellation of the stream, the amount streamed is calculated as the difference between the deposited amount and the refunded amount. Ultimately, when the stream becomes depleted, the streamed amount is equivalent to the total amount withdrawn. ```solidity function streamedAmountOf(uint256 streamId) external view override notNull(streamId) returns (uint128 streamedAmount); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### supportsInterface See {IERC165-supportsInterface}. ```solidity function supportsInterface(bytes4 interfaceId) public view override(IERC165, ERC721) returns (bool); ``` ### tokenURI See {IERC721Metadata-tokenURI}. ```solidity function tokenURI(uint256 streamId) public view override(IERC721Metadata, ERC721) returns (string memory uri); ``` ### withdrawableAmountOf Calculates the amount that the recipient can withdraw from the stream, denoted in units of the token's decimals. Reverts if `streamId` references a null stream. ```solidity function withdrawableAmountOf(uint256 streamId) external view override notNull(streamId) returns (uint128 withdrawableAmount); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### allowToHook Allows a recipient contract to hook to Sablier when a stream is canceled or when tokens are withdrawn. Useful for implementing contracts that hold streams on behalf of users, such as vaults or staking contracts. Emits an {AllowToHook} event. Notes: - Does not revert if the contract is already on the allowlist. - This is an irreversible operation. The contract cannot be removed from the allowlist. Requirements: - `msg.sender` must be the comptroller contract. - `recipient` must implement [ISablierLockupRecipient](/reference/lockup/contracts/interfaces/interface.ISablierLockupRecipient). ```solidity function allowToHook(address recipient) external override onlyComptroller; ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `recipient` | `address` | The address of the contract to allow for hooks. | ### burn Burns the NFT associated with the stream. Emits a {Transfer} and {MetadataUpdate} event. Requirements: - Must not be delegate called. - `streamId` must reference a depleted stream. - The NFT must exist. - `msg.sender` must be either the NFT owner or an approved third party. ```solidity function burn(uint256 streamId) external payable override noDelegateCall notNull(streamId); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream NFT to burn. | ### cancel Cancels the stream and refunds any remaining tokens to the sender. Emits a {Transfer}, {CancelLockupStream} and {MetadataUpdate} event. Notes: - If there are any tokens left for the recipient to withdraw, the stream is marked as canceled. Otherwise, the stream is marked as depleted. - If the address is on the allowlist, this function will invoke a hook on the recipient. Requirements: - Must not be delegate called. - The stream must be warm and cancelable. - `msg.sender` must be the stream's sender. ```solidity function cancel(uint256 streamId) public payable override noDelegateCall notNull(streamId) returns (uint128 refundedAmount); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to cancel. | **Returns** | Name | Type | Description | | --- | --- | --- | | `refundedAmount` | `uint128` | The amount refunded to the sender, denoted in units of the token's decimals. | ### cancelMultiple Cancels multiple streams and refunds any remaining tokens to the sender. Emits multiple {Transfer}, {CancelLockupStream} and {MetadataUpdate} events. For each reverted cancellation, it emits an [InvalidStreamInCancelMultiple](/reference/lockup/contracts/interfaces/interface.ISablierLockup#invalidstreamincancelmultiple) event. Notes: - This function as a whole will not revert if one or more cancellations revert. A zero amount is returned for reverted streams. - Refer to the notes and requirements from {cancel}. ```solidity function cancelMultiple(uint256[] calldata streamIds) external payable override noDelegateCall returns (uint128[] memory refundedAmounts); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamIds` | `uint256[]` | The IDs of the streams to cancel. | **Returns** | Name | Type | Description | | --- | --- | --- | | `refundedAmounts` | `uint128[]` | The amounts refunded to the sender, denoted in units of the token's decimals. | ### recover Recover the surplus amount of tokens. Notes: - The surplus amount is defined as the difference between the total balance of the contract for the provided ERC-20 token and the sum of balances of all streams created using the same ERC-20 token. Requirements: - `msg.sender` must be the comptroller contract. - The surplus amount must be greater than zero. ```solidity function recover(IERC20 token, address to) external override onlyComptroller; ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `token` | `IERC20` | The contract address of the ERC-20 token to recover for. | | `to` | `address` | The address to send the surplus amount. | ### renounce Removes the right of the stream's sender to cancel the stream. Emits a {RenounceLockupStream} event. Notes: - This is an irreversible operation. Requirements: - Must not be delegate called. - `streamId` must reference a warm stream. - `msg.sender` must be the stream's sender. - The stream must be cancelable. ```solidity function renounce(uint256 streamId) public payable override noDelegateCall notNull(streamId); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to renounce. | ### setNativeToken Sets the native token address. Once set, it cannot be changed. For more information, see the documentation for {nativeToken}. Notes: - If `newNativeToken` is the zero address, the function does not revert. Requirements: - `msg.sender` must be the comptroller contract. - The current native token must be the zero address. ```solidity function setNativeToken(address newNativeToken) external override onlyComptroller; ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `newNativeToken` | `address` | The address of the native token. | ### setNFTDescriptor Sets a new NFT descriptor contract, which produces the URI describing the Sablier stream NFTs. Emits a {SetNFTDescriptor} and {BatchMetadataUpdate} event. Notes: - Does not revert if the NFT descriptor is the same. Requirements: - `msg.sender` must be the comptroller contract. ```solidity function setNFTDescriptor(ILockupNFTDescriptor newNFTDescriptor) external override onlyComptroller; ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `newNFTDescriptor` | `ILockupNFTDescriptor` | The address of the new NFT descriptor contract. | ### withdraw Withdraws the provided amount of tokens from the stream to the `to` address. Emits a {Transfer}, {WithdrawFromLockupStream} and {MetadataUpdate} event. Notes: - If `msg.sender` is not the recipient and the address is on the allowlist, this function will invoke a hook on the recipient. - The minimum fee in wei is calculated for the stream's sender using the **SablierComptroller** contract. Requirements: - Must not be delegate called. - `streamId` must not reference a null or depleted stream. - `to` must not be the zero address. - `amount` must be greater than zero and must not exceed the withdrawable amount. - `to` must be the recipient if `msg.sender` is not the stream's recipient or an approved third party. - `msg.value` must be greater than or equal to the minimum fee in wei for the stream's sender. ```solidity function withdraw( uint256 streamId, address to, uint128 amount ) public payable override noDelegateCall notNull(streamId); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to withdraw from. | | `to` | `address` | The address receiving the withdrawn tokens. | | `amount` | `uint128` | The amount to withdraw, denoted in units of the token's decimals. | ### withdrawMax Withdraws the maximum withdrawable amount from the stream to the provided address `to`. Emits a {Transfer}, {WithdrawFromLockupStream} and {MetadataUpdate} event. Notes: - Refer to the notes in {withdraw}. Requirements: - Refer to the requirements in {withdraw}. ```solidity function withdrawMax(uint256 streamId, address to) external payable override returns (uint128 withdrawnAmount); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to withdraw from. | | `to` | `address` | The address receiving the withdrawn tokens. | **Returns** | Name | Type | Description | | --- | --- | --- | | `withdrawnAmount` | `uint128` | The amount withdrawn, denoted in units of the token's decimals. | ### withdrawMaxAndTransfer Withdraws the maximum withdrawable amount from the stream to the current recipient, and transfers the NFT to `newRecipient`. Emits a {WithdrawFromLockupStream}, {Transfer} and {MetadataUpdate} event. Notes: - If the withdrawable amount is zero, the withdrawal is skipped. - Refer to the notes in {withdraw}. Requirements: - `msg.sender` must be either the NFT owner or an approved third party. - Refer to the requirements in {withdraw}. - Refer to the requirements in {IERC721.transferFrom}. ```solidity function withdrawMaxAndTransfer( uint256 streamId, address newRecipient ) external payable override noDelegateCall notNull(streamId) returns (uint128 withdrawnAmount); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream NFT to transfer. | | `newRecipient` | `address` | The address of the new owner of the stream NFT. | **Returns** | Name | Type | Description | | --- | --- | --- | | `withdrawnAmount` | `uint128` | The amount withdrawn, denoted in units of the token's decimals. | ### withdrawMultiple Withdraws tokens from streams to the recipient of each stream. Emits multiple {Transfer}, {WithdrawFromLockupStream} and {MetadataUpdate} events. For each reverting withdrawal, it emits an [InvalidWithdrawalInWithdrawMultiple](/reference/lockup/contracts/interfaces/interface.ISablierLockup#invalidwithdrawalinwithdrawmultiple) event. Notes: - This function as a whole will not revert if one or more withdrawals revert. - This function attempts to call a hook on the recipient of each stream, unless `msg.sender` is the recipient. - Refer to the notes and requirements from {withdraw}. Requirements: - Must not be delegate called. - There must be an equal number of `streamIds` and `amounts`. ```solidity function withdrawMultiple( uint256[] calldata streamIds, uint128[] calldata amounts ) external payable override noDelegateCall; ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamIds` | `uint256[]` | The IDs of the streams to withdraw from. | | `amounts` | `uint128[]` | The amounts to withdraw, denoted in units of the token's decimals. | ### \_streamedAmountOf Calculates the streamed amount of the stream. This function is implemented by child contract. The logic varies according to the distribution model. ```solidity function _streamedAmountOf(uint256 streamId) internal view override returns (uint128 streamedAmount); ``` ### \_create This function is implemented by [SablierLockup](/reference/lockup/contracts/contract.SablierLockup) and is used in the [SablierLockupDynamic](/reference/lockup/contracts/abstracts/abstract.SablierLockupDynamic), [SablierLockupLinear](/reference/lockup/contracts/abstracts/abstract.SablierLockupLinear) and [SablierLockupTranched](/reference/lockup/contracts/abstracts/abstract.SablierLockupTranched) contracts. It updates state variables based on the stream parameters, mints an NFT to the recipient, bumps stream ID, and transfers the deposit amount. ```solidity function _create( bool cancelable, uint128 depositAmount, Lockup.Model lockupModel, address recipient, address sender, uint256 streamId, Lockup.Timestamps memory timestamps, IERC20 token, bool transferable ) internal override; ``` ### \_update Overrides the {ERC721.\_update} function to check that the stream is transferable, and emits an ERC-4906 event. There are two cases when the transferable flag is ignored: - If the current owner is 0, then the update is a mint and is allowed. - If `to` is 0, then the update is a burn and is also allowed. ```solidity function _update(address to, uint256 streamId, address auth) internal override returns (address); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `to` | `address` | The address of the new recipient of the stream. | | `streamId` | `uint256` | ID of the stream to update. | | `auth` | `address` | Optional parameter. If the value is not zero, the overridden implementation will check that `auth` is either the recipient of the stream, or an approved third party. | **Returns** | Name | Type | Description | | --- | --- | --- | | `` | `address` | The original recipient of the `streamId` before the update. | ### \_isCallerStreamRecipientOrApproved Checks whether `msg.sender` is the stream's recipient or an approved third party, when the `recipient` is known in advance. ```solidity function _isCallerStreamRecipientOrApproved(uint256 streamId, address recipient) private view returns (bool); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | | `recipient` | `address` | The address of the stream's recipient. | ### \_withdrawableAmountOf See the documentation for the user-facing functions that call this private function. ```solidity function _withdrawableAmountOf(uint256 streamId) private view returns (uint128); ``` ### \_cancel See the documentation for the user-facing functions that call this private function. ```solidity function _cancel(uint256 streamId) private returns (uint128 senderAmount); ``` ### \_withdraw See the documentation for the user-facing functions that call this private function. ```solidity function _withdraw(uint256 streamId, address to, uint128 amount) private; ``` --- ## IBatch Source: https://docs.sablier.com/reference/lockup/contracts/interfaces/interface.IBatch # IBatch [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/interfaces/IBatch.sol) This contract implements logic to batch call any function. ## Functions ### batch Allows batched calls to self, i.e., `this` contract. Since `msg.value` can be reused across calls, be VERY CAREFUL when using it. Refer to [https://paradigm.xyz/2021/08/two-rights-might-make-a-wrong](https://paradigm.xyz/2021/08/two-rights-might-make-a-wrong) for more information. ```solidity function batch(bytes[] calldata calls) external payable returns (bytes[] memory results); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `calls` | `bytes[]` | An array of inputs for each call. | **Returns** | Name | Type | Description | | --- | --- | --- | | `results` | `bytes[]` | An array of results from each call. Empty when the calls do not return anything. | --- ## IComptrollerable Source: https://docs.sablier.com/reference/lockup/contracts/interfaces/interface.IComptrollerable # IComptrollerable [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/interfaces/IComptrollerable.sol) **Title:** IComptrollerable Contract module that provides a setter and getter for the Sablier Comptroller. ## Functions ### comptroller Retrieves the address of the comptroller contract. ```solidity function comptroller() external view returns (ISablierComptroller); ``` ### setComptroller Sets the comptroller to a new address. Emits a [SetComptroller](#setcomptroller) event. Requirements: - `msg.sender` must be the current comptroller. - The new comptroller must return `true` from {supportsInterface} with the comptroller's minimal interface ID which is defined as the XOR of the following function selectors: 1. {calculateMinFeeWeiFor} 2. {convertUSDFeeToWei} 3. {execute} 4. {getMinFeeUSDFor} ```solidity function setComptroller(ISablierComptroller newComptroller) external; ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `newComptroller` | `ISablierComptroller` | The address of the new comptroller contract. | ### transferFeesToComptroller Transfers the fees to the comptroller contract. Emits a [TransferFeesToComptroller](#transferfeestocomptroller) event. ```solidity function transferFeesToComptroller() external; ``` ## Events ### SetComptroller Emitted when the comptroller address is set by the admin. ```solidity event SetComptroller(ISablierComptroller oldComptroller, ISablierComptroller newComptroller); ``` ### TransferFeesToComptroller Emitted when the fees are transferred to the comptroller contract. ```solidity event TransferFeesToComptroller(address indexed comptroller, uint256 feeAmount); ``` --- ## ILockupNFTDescriptor Source: https://docs.sablier.com/reference/lockup/contracts/interfaces/interface.ILockupNFTDescriptor # ILockupNFTDescriptor [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/interfaces/ILockupNFTDescriptor.sol) **Title:** ILockupNFTDescriptor This contract generates the URI describing the Sablier stream NFTs. Inspired by Uniswap V3 Positions NFTs. ## Functions ### tokenURI Produces the URI describing a particular stream NFT. This is a data URI with the JSON contents directly inlined. ```solidity function tokenURI(IERC721Metadata sablier, uint256 streamId) external view returns (string memory uri); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `sablier` | `IERC721Metadata` | The address of the Sablier contract the stream was created in. | | `streamId` | `uint256` | The ID of the stream for which to produce a description. | **Returns** | Name | Type | Description | | --- | --- | --- | | `uri` | `string` | The URI of the ERC721-compliant metadata. | --- ## ISablierBatchLockup Source: https://docs.sablier.com/reference/lockup/contracts/interfaces/interface.ISablierBatchLockup # ISablierBatchLockup [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/interfaces/ISablierBatchLockup.sol) **Title:** ISablierBatchLockup Helper to batch create Lockup streams. ## Functions ### createWithDurationsLD Creates a batch of LD streams using `createWithDurationsLD`. Requirements: - There must be at least one element in `batch`. - All requirements from {ISablierLockupDynamic.createWithDurationsLD} must be met for each stream. ```solidity function createWithDurationsLD( ISablierLockup lockup, IERC20 token, BatchLockup.CreateWithDurationsLD[] calldata batch ) external returns (uint256[] memory streamIds); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `lockup` | `ISablierLockup` | The address of the [SablierLockup](/reference/lockup/contracts/contract.SablierLockup) contract. | | `token` | `IERC20` | The contract address of the ERC-20 token to be distributed. | | `batch` | `BatchLockup.CreateWithDurationsLD[]` | An array of structs, each encapsulating a subset of the parameters of {ISablierLockupDynamic.createWithDurationsLD}. | **Returns** | Name | Type | Description | | --- | --- | --- | | `streamIds` | `uint256[]` | The ids of the newly created streams. | ### createWithTimestampsLD Creates a batch of LD streams using `createWithTimestampsLD`. Requirements: - There must be at least one element in `batch`. - All requirements from {ISablierLockupDynamic.createWithTimestampsLD} must be met for each stream. ```solidity function createWithTimestampsLD( ISablierLockup lockup, IERC20 token, BatchLockup.CreateWithTimestampsLD[] calldata batch ) external returns (uint256[] memory streamIds); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `lockup` | `ISablierLockup` | The address of the [SablierLockup](/reference/lockup/contracts/contract.SablierLockup) contract. | | `token` | `IERC20` | The contract address of the ERC-20 token to be distributed. | | `batch` | `BatchLockup.CreateWithTimestampsLD[]` | An array of structs, each encapsulating a subset of the parameters of {ISablierLockupDynamic.createWithTimestampsLD}. | **Returns** | Name | Type | Description | | --- | --- | --- | | `streamIds` | `uint256[]` | The ids of the newly created streams. | ### createWithDurationsLL Creates a batch of LL streams using `createWithDurationsLL`. Requirements: - There must be at least one element in `batch`. - All requirements from {ISablierLockupLinear.createWithDurationsLL} must be met for each stream. ```solidity function createWithDurationsLL( ISablierLockup lockup, IERC20 token, BatchLockup.CreateWithDurationsLL[] calldata batch ) external returns (uint256[] memory streamIds); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `lockup` | `ISablierLockup` | The address of the [SablierLockup](/reference/lockup/contracts/contract.SablierLockup) contract. | | `token` | `IERC20` | The contract address of the ERC-20 token to be distributed. | | `batch` | `BatchLockup.CreateWithDurationsLL[]` | An array of structs, each encapsulating a subset of the parameters of {ISablierLockupLinear.createWithDurationsLL}. | **Returns** | Name | Type | Description | | --- | --- | --- | | `streamIds` | `uint256[]` | The ids of the newly created streams. | ### createWithTimestampsLL Creates a batch of LL streams using `createWithTimestampsLL`. Requirements: - There must be at least one element in `batch`. - All requirements from {ISablierLockupLinear.createWithTimestampsLL} must be met for each stream. ```solidity function createWithTimestampsLL( ISablierLockup lockup, IERC20 token, BatchLockup.CreateWithTimestampsLL[] calldata batch ) external returns (uint256[] memory streamIds); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `lockup` | `ISablierLockup` | The address of the [SablierLockup](/reference/lockup/contracts/contract.SablierLockup) contract. | | `token` | `IERC20` | The contract address of the ERC-20 token to be distributed. | | `batch` | `BatchLockup.CreateWithTimestampsLL[]` | An array of structs, each encapsulating a subset of the parameters of {ISablierLockupLinear.createWithTimestampsLL}. | **Returns** | Name | Type | Description | | --- | --- | --- | | `streamIds` | `uint256[]` | The ids of the newly created streams. | ### createWithTimestampsLPG Creates a batch of LPG streams using `createWithTimestampsLPG`. Notes: - The LPG model does not support a "createWithDuration" function because the [SablierLockup](/reference/lockup/contracts/contract.SablierLockup) contract is at the size limit. If the EVM contract size limit is increased in the future, this function will be added. Requirements: - There must be at least one element in `batch`. - All requirements from {ISablierLockupPriceGated.createWithTimestampsLPG} must be met for each stream. ```solidity function createWithTimestampsLPG( ISablierLockup lockup, IERC20 token, BatchLockup.CreateWithTimestampsLPG[] calldata batch ) external returns (uint256[] memory streamIds); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `lockup` | `ISablierLockup` | The address of the [SablierLockup](/reference/lockup/contracts/contract.SablierLockup) contract. | | `token` | `IERC20` | The contract address of the ERC-20 token to be distributed. | | `batch` | `BatchLockup.CreateWithTimestampsLPG[]` | An array of structs, each encapsulating a subset of the parameters of {ISablierLockupPriceGated.createWithTimestampsLPG}. | **Returns** | Name | Type | Description | | --- | --- | --- | | `streamIds` | `uint256[]` | The ids of the newly created streams. | ### createWithDurationsLT Creates a batch of LT streams using `createWithDurationsLT`. Requirements: - There must be at least one element in `batch`. - All requirements from {ISablierLockupTranched.createWithDurationsLT} must be met for each stream. ```solidity function createWithDurationsLT( ISablierLockup lockup, IERC20 token, BatchLockup.CreateWithDurationsLT[] calldata batch ) external returns (uint256[] memory streamIds); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `lockup` | `ISablierLockup` | The address of the [SablierLockup](/reference/lockup/contracts/contract.SablierLockup) contract. | | `token` | `IERC20` | The contract address of the ERC-20 token to be distributed. | | `batch` | `BatchLockup.CreateWithDurationsLT[]` | An array of structs, each encapsulating a subset of the parameters of {ISablierLockupTranched.createWithDurationsLT}. | **Returns** | Name | Type | Description | | --- | --- | --- | | `streamIds` | `uint256[]` | The ids of the newly created streams. | ### createWithTimestampsLT Creates a batch of LT streams using `createWithTimestampsLT`. Requirements: - There must be at least one element in `batch`. - All requirements from {ISablierLockupTranched.createWithTimestampsLT} must be met for each stream. ```solidity function createWithTimestampsLT( ISablierLockup lockup, IERC20 token, BatchLockup.CreateWithTimestampsLT[] calldata batch ) external returns (uint256[] memory streamIds); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `lockup` | `ISablierLockup` | The address of the [SablierLockup](/reference/lockup/contracts/contract.SablierLockup) contract. | | `token` | `IERC20` | The contract address of the ERC-20 token to be distributed. | | `batch` | `BatchLockup.CreateWithTimestampsLT[]` | An array of structs, each encapsulating a subset of the parameters of {ISablierLockupTranched.createWithTimestampsLT}. | **Returns** | Name | Type | Description | | --- | --- | --- | | `streamIds` | `uint256[]` | The ids of the newly created streams. | ## Events ### CreateLockupBatch Emitted when a batch of Lockup streams are created. ```solidity event CreateLockupBatch(address indexed funder, ISablierLockup indexed lockup, uint256[] streamIds); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `funder` | `address` | The address funding the streams. | | `lockup` | `ISablierLockup` | The address of the [SablierLockup](/reference/lockup/contracts/contract.SablierLockup) contract used to create the streams. | | `streamIds` | `uint256[]` | The ids of the newly created streams, the ones that were successfully created. | --- ## ISablierLockup Source: https://docs.sablier.com/reference/lockup/contracts/interfaces/interface.ISablierLockup # ISablierLockup [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/interfaces/ISablierLockup.sol) **Inherits:** IBatch, IComptrollerable, IERC4906, IERC721Metadata, [ISablierLockupDynamic](/reference/lockup/contracts/interfaces/interface.ISablierLockupDynamic), [ISablierLockupLinear](/reference/lockup/contracts/interfaces/interface.ISablierLockupLinear), [ISablierLockupPriceGated](/reference/lockup/contracts/interfaces/interface.ISablierLockupPriceGated), [ISablierLockupTranched](/reference/lockup/contracts/interfaces/interface.ISablierLockupTranched) **Title:** ISablierLockup Interface to manage Lockup streams with various distribution models. ## Functions ### calculateMinFeeWei Calculates the minimum fee in wei required to withdraw from the given stream ID. Reverts if `streamId` references a null stream. ```solidity function calculateMinFeeWei(uint256 streamId) external view returns (uint256 minFeeWei); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### getRecipient Retrieves the stream's recipient. Reverts if the NFT has been burned. ```solidity function getRecipient(uint256 streamId) external view returns (address recipient); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### isCold Retrieves a flag indicating whether the stream is cold, i.e. settled, canceled, or depleted. Reverts if `streamId` references a null stream. ```solidity function isCold(uint256 streamId) external view returns (bool result); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### isWarm Retrieves a flag indicating whether the stream is warm, i.e. either pending or streaming. Reverts if `streamId` references a null stream. ```solidity function isWarm(uint256 streamId) external view returns (bool result); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### refundableAmountOf Calculates the amount that the sender would be refunded if the stream were canceled, denoted in units of the token's decimals. Reverts if `streamId` references a null stream. ```solidity function refundableAmountOf(uint256 streamId) external view returns (uint128 refundableAmount); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### statusOf Retrieves the stream's status. Reverts if `streamId` references a null stream. ```solidity function statusOf(uint256 streamId) external view returns (Lockup.Status status); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### streamedAmountOf Calculates the amount streamed to the recipient, denoted in units of the token's decimals. Reverts if `streamId` references a null stream. Notes: - Upon cancellation of the stream, the amount streamed is calculated as the difference between the deposited amount and the refunded amount. Ultimately, when the stream becomes depleted, the streamed amount is equivalent to the total amount withdrawn. ```solidity function streamedAmountOf(uint256 streamId) external view returns (uint128 streamedAmount); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### withdrawableAmountOf Calculates the amount that the recipient can withdraw from the stream, denoted in units of the token's decimals. Reverts if `streamId` references a null stream. ```solidity function withdrawableAmountOf(uint256 streamId) external view returns (uint128 withdrawableAmount); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### allowToHook Allows a recipient contract to hook to Sablier when a stream is canceled or when tokens are withdrawn. Useful for implementing contracts that hold streams on behalf of users, such as vaults or staking contracts. Emits an [AllowToHook](/reference/lockup/contracts/interfaces/interface.ISablierLockup#allowtohook) event. Notes: - Does not revert if the contract is already on the allowlist. - This is an irreversible operation. The contract cannot be removed from the allowlist. Requirements: - `msg.sender` must be the comptroller contract. - `recipient` must implement [ISablierLockupRecipient](/reference/lockup/contracts/interfaces/interface.ISablierLockupRecipient). ```solidity function allowToHook(address recipient) external; ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `recipient` | `address` | The address of the contract to allow for hooks. | ### burn Burns the NFT associated with the stream. Emits a {Transfer} and {MetadataUpdate} event. Requirements: - Must not be delegate called. - `streamId` must reference a depleted stream. - The NFT must exist. - `msg.sender` must be either the NFT owner or an approved third party. ```solidity function burn(uint256 streamId) external payable; ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream NFT to burn. | ### cancel Cancels the stream and refunds any remaining tokens to the sender. Emits a {Transfer}, [CancelLockupStream](/reference/lockup/contracts/interfaces/interface.ISablierLockup#cancellockupstream) and {MetadataUpdate} event. Notes: - If there are any tokens left for the recipient to withdraw, the stream is marked as canceled. Otherwise, the stream is marked as depleted. - If the address is on the allowlist, this function will invoke a hook on the recipient. Requirements: - Must not be delegate called. - The stream must be warm and cancelable. - `msg.sender` must be the stream's sender. ```solidity function cancel(uint256 streamId) external payable returns (uint128 refundedAmount); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to cancel. | **Returns** | Name | Type | Description | | --- | --- | --- | | `refundedAmount` | `uint128` | The amount refunded to the sender, denoted in units of the token's decimals. | ### cancelMultiple Cancels multiple streams and refunds any remaining tokens to the sender. Emits multiple {Transfer}, [CancelLockupStream](/reference/lockup/contracts/interfaces/interface.ISablierLockup#cancellockupstream) and {MetadataUpdate} events. For each reverted cancellation, it emits an [InvalidStreamInCancelMultiple](/reference/lockup/contracts/interfaces/interface.ISablierLockup#invalidstreamincancelmultiple) event. Notes: - This function as a whole will not revert if one or more cancellations revert. A zero amount is returned for reverted streams. - Refer to the notes and requirements from [cancel](/reference/lockup/contracts/interfaces/interface.ISablierLockup#cancel). ```solidity function cancelMultiple(uint256[] calldata streamIds) external payable returns (uint128[] memory refundedAmounts); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamIds` | `uint256[]` | The IDs of the streams to cancel. | **Returns** | Name | Type | Description | | --- | --- | --- | | `refundedAmounts` | `uint128[]` | The amounts refunded to the sender, denoted in units of the token's decimals. | ### recover Recover the surplus amount of tokens. Notes: - The surplus amount is defined as the difference between the total balance of the contract for the provided ERC-20 token and the sum of balances of all streams created using the same ERC-20 token. Requirements: - `msg.sender` must be the comptroller contract. - The surplus amount must be greater than zero. ```solidity function recover(IERC20 token, address to) external; ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `token` | `IERC20` | The contract address of the ERC-20 token to recover for. | | `to` | `address` | The address to send the surplus amount. | ### renounce Removes the right of the stream's sender to cancel the stream. Emits a [RenounceLockupStream](/reference/lockup/contracts/interfaces/interface.ISablierLockup#renouncelockupstream) event. Notes: - This is an irreversible operation. Requirements: - Must not be delegate called. - `streamId` must reference a warm stream. - `msg.sender` must be the stream's sender. - The stream must be cancelable. ```solidity function renounce(uint256 streamId) external payable; ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to renounce. | ### setNativeToken Sets the native token address. Once set, it cannot be changed. For more information, see the documentation for {nativeToken}. Notes: - If `newNativeToken` is the zero address, the function does not revert. Requirements: - `msg.sender` must be the comptroller contract. - The current native token must be the zero address. ```solidity function setNativeToken(address newNativeToken) external; ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `newNativeToken` | `address` | The address of the native token. | ### setNFTDescriptor Sets a new NFT descriptor contract, which produces the URI describing the Sablier stream NFTs. Emits a [SetNFTDescriptor](/reference/lockup/contracts/interfaces/interface.ISablierLockup#setnftdescriptor) and {BatchMetadataUpdate} event. Notes: - Does not revert if the NFT descriptor is the same. Requirements: - `msg.sender` must be the comptroller contract. ```solidity function setNFTDescriptor(ILockupNFTDescriptor newNFTDescriptor) external; ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `newNFTDescriptor` | `ILockupNFTDescriptor` | The address of the new NFT descriptor contract. | ### withdraw Withdraws the provided amount of tokens from the stream to the `to` address. Emits a {Transfer}, [WithdrawFromLockupStream](/reference/lockup/contracts/interfaces/interface.ISablierLockup#withdrawfromlockupstream) and {MetadataUpdate} event. Notes: - If `msg.sender` is not the recipient and the address is on the allowlist, this function will invoke a hook on the recipient. - The minimum fee in wei is calculated for the stream's sender using the **SablierComptroller** contract. Requirements: - Must not be delegate called. - `streamId` must not reference a null or depleted stream. - `to` must not be the zero address. - `amount` must be greater than zero and must not exceed the withdrawable amount. - `to` must be the recipient if `msg.sender` is not the stream's recipient or an approved third party. - `msg.value` must be greater than or equal to the minimum fee in wei for the stream's sender. ```solidity function withdraw(uint256 streamId, address to, uint128 amount) external payable; ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to withdraw from. | | `to` | `address` | The address receiving the withdrawn tokens. | | `amount` | `uint128` | The amount to withdraw, denoted in units of the token's decimals. | ### withdrawMax Withdraws the maximum withdrawable amount from the stream to the provided address `to`. Emits a {Transfer}, [WithdrawFromLockupStream](/reference/lockup/contracts/interfaces/interface.ISablierLockup#withdrawfromlockupstream) and {MetadataUpdate} event. Notes: - Refer to the notes in [withdraw](/reference/lockup/contracts/interfaces/interface.ISablierLockup#withdraw). Requirements: - Refer to the requirements in [withdraw](/reference/lockup/contracts/interfaces/interface.ISablierLockup#withdraw). ```solidity function withdrawMax(uint256 streamId, address to) external payable returns (uint128 withdrawnAmount); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to withdraw from. | | `to` | `address` | The address receiving the withdrawn tokens. | **Returns** | Name | Type | Description | | --- | --- | --- | | `withdrawnAmount` | `uint128` | The amount withdrawn, denoted in units of the token's decimals. | ### withdrawMaxAndTransfer Withdraws the maximum withdrawable amount from the stream to the current recipient, and transfers the NFT to `newRecipient`. Emits a [WithdrawFromLockupStream](/reference/lockup/contracts/interfaces/interface.ISablierLockup#withdrawfromlockupstream), {Transfer} and {MetadataUpdate} event. Notes: - If the withdrawable amount is zero, the withdrawal is skipped. - Refer to the notes in [withdraw](/reference/lockup/contracts/interfaces/interface.ISablierLockup#withdraw). Requirements: - `msg.sender` must be either the NFT owner or an approved third party. - Refer to the requirements in [withdraw](/reference/lockup/contracts/interfaces/interface.ISablierLockup#withdraw). - Refer to the requirements in {IERC721.transferFrom}. ```solidity function withdrawMaxAndTransfer( uint256 streamId, address newRecipient ) external payable returns (uint128 withdrawnAmount); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream NFT to transfer. | | `newRecipient` | `address` | The address of the new owner of the stream NFT. | **Returns** | Name | Type | Description | | --- | --- | --- | | `withdrawnAmount` | `uint128` | The amount withdrawn, denoted in units of the token's decimals. | ### withdrawMultiple Withdraws tokens from streams to the recipient of each stream. Emits multiple {Transfer}, [WithdrawFromLockupStream](/reference/lockup/contracts/interfaces/interface.ISablierLockup#withdrawfromlockupstream) and {MetadataUpdate} events. For each reverting withdrawal, it emits an [InvalidWithdrawalInWithdrawMultiple](/reference/lockup/contracts/interfaces/interface.ISablierLockup#invalidwithdrawalinwithdrawmultiple) event. Notes: - This function as a whole will not revert if one or more withdrawals revert. - This function attempts to call a hook on the recipient of each stream, unless `msg.sender` is the recipient. - Refer to the notes and requirements from [withdraw](/reference/lockup/contracts/interfaces/interface.ISablierLockup#withdraw). Requirements: - Must not be delegate called. - There must be an equal number of `streamIds` and `amounts`. ```solidity function withdrawMultiple(uint256[] calldata streamIds, uint128[] calldata amounts) external payable; ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamIds` | `uint256[]` | The IDs of the streams to withdraw from. | | `amounts` | `uint128[]` | The amounts to withdraw, denoted in units of the token's decimals. | ## Events ### AllowToHook Emitted when the comptroller allows a new recipient contract to hook to Sablier. ```solidity event AllowToHook(ISablierComptroller indexed comptroller, address indexed recipient); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `comptroller` | `ISablierComptroller` | The address of the current comptroller. | | `recipient` | `address` | The address of the recipient contract put on the allowlist. | ### CancelLockupStream Emitted when a stream is canceled. ```solidity event CancelLockupStream( uint256 streamId, address indexed sender, address indexed recipient, IERC20 indexed token, uint128 senderAmount, uint128 recipientAmount ); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream. | | `sender` | `address` | The address of the stream's sender. | | `recipient` | `address` | The address of the stream's recipient. | | `token` | `IERC20` | The contract address of the ERC-20 token that has been distributed. | | `senderAmount` | `uint128` | The amount of tokens refunded to the stream's sender, denoted in units of the token's decimals. | | `recipientAmount` | `uint128` | The amount of tokens left for the stream's recipient to withdraw, denoted in units of the token's decimals. | ### InvalidStreamInCancelMultiple Emitted when canceling multiple streams and one particular cancellation reverts. ```solidity event InvalidStreamInCancelMultiple(uint256 indexed streamId, bytes revertData); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream that reverted the cancellation. | | `revertData` | `bytes` | The error data returned by the reverted cancel. | ### InvalidWithdrawalInWithdrawMultiple Emitted when withdrawing from multiple streams and one particular withdrawal reverts. ```solidity event InvalidWithdrawalInWithdrawMultiple(uint256 indexed streamId, bytes revertData); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream that reverted the withdrawal. | | `revertData` | `bytes` | The error data returned by the reverted withdraw. | ### RenounceLockupStream Emitted when a sender gives up the right to cancel a stream. ```solidity event RenounceLockupStream(uint256 indexed streamId); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream. | ### SetNFTDescriptor Emitted when the comptroller sets a new NFT descriptor contract. ```solidity event SetNFTDescriptor( ISablierComptroller indexed comptroller, ILockupNFTDescriptor indexed oldNFTDescriptor, ILockupNFTDescriptor indexed newNFTDescriptor ); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `comptroller` | `ISablierComptroller` | The address of the current comptroller. | | `oldNFTDescriptor` | `ILockupNFTDescriptor` | The address of the old NFT descriptor contract. | | `newNFTDescriptor` | `ILockupNFTDescriptor` | The address of the new NFT descriptor contract. | ### WithdrawFromLockupStream Emitted when tokens are withdrawn from a stream. ```solidity event WithdrawFromLockupStream(uint256 indexed streamId, address indexed to, IERC20 indexed token, uint128 amount); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream. | | `to` | `address` | The address that has received the withdrawn tokens. | | `token` | `IERC20` | The contract address of the ERC-20 token that has been withdrawn. | | `amount` | `uint128` | The amount of tokens withdrawn, denoted in units of the token's decimals. | --- ## ISablierLockupDynamic Source: https://docs.sablier.com/reference/lockup/contracts/interfaces/interface.ISablierLockupDynamic # ISablierLockupDynamic [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/interfaces/ISablierLockupDynamic.sol) **Inherits:** [ISablierLockupState](/reference/lockup/contracts/interfaces/interface.ISablierLockupState) **Title:** ISablierLockupDynamic Creates Lockup streams with dynamic distribution model. ## Functions ### createWithDurationsLD Creates a stream by setting the start time to `block.timestamp`, and the end time to the sum of `block.timestamp` and all specified time durations. The segment timestamps are derived from these durations. The stream is funded by `msg.sender` and is wrapped in an ERC-721 NFT. Emits a {Transfer}, [CreateLockupDynamicStream](/reference/lockup/contracts/interfaces/interface.ISablierLockupDynamic#createlockupdynamicstream) and {MetadataUpdate} event. Requirements: - All requirements in [createWithTimestampsLD](/reference/lockup/contracts/interfaces/interface.ISablierLockupDynamic#createwithtimestampsld) must be met for the calculated parameters. ```solidity function createWithDurationsLD( Lockup.CreateWithDurations calldata params, LockupDynamic.SegmentWithDuration[] calldata segmentsWithDuration ) external payable returns (uint256 streamId); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `params` | `Lockup.CreateWithDurations` | Struct encapsulating the function parameters, which are documented in {Lockup} type. | | `segmentsWithDuration` | `LockupDynamic.SegmentWithDuration[]` | Segments with durations used to compose the dynamic distribution function. Timestamps are calculated by starting from `block.timestamp` and adding each duration to the previous timestamp. | **Returns** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the newly created stream. | ### createWithTimestampsLD Creates a stream with the provided segment timestamps, implying the end time from the last timestamp. The stream is funded by `msg.sender` and is wrapped in an ERC-721 NFT. Emits a {Transfer}, [CreateLockupDynamicStream](/reference/lockup/contracts/interfaces/interface.ISablierLockupDynamic#createlockupdynamicstream) and {MetadataUpdate} event. Notes: - As long as the segment timestamps are arranged in ascending order, it is not an error for some of them to be in the past. Requirements: - Must not be delegate called. - `params.depositAmount` must be greater than zero. - `params.timestamps.start` must be greater than zero and less than the first segment's timestamp. - `segments` must have at least one segment. - The segment timestamps must be arranged in ascending order. - `params.timestamps.end` must be equal to the last segment's timestamp. - The sum of the segment amounts must equal the deposit amount. - `params.recipient` must not be the zero address. - `params.sender` must not be the zero address. - `msg.sender` must have allowed this contract to spend at least `params.depositAmount` tokens. - `params.token` must not be the native token. - `params.shape.length` must not be greater than 32 characters. ```solidity function createWithTimestampsLD( Lockup.CreateWithTimestamps calldata params, LockupDynamic.Segment[] calldata segments ) external payable returns (uint256 streamId); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `params` | `Lockup.CreateWithTimestamps` | Struct encapsulating the function parameters, which are documented in {Lockup} type. | | `segments` | `LockupDynamic.Segment[]` | Segments used to compose the dynamic distribution function. | **Returns** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the newly created stream. | ## Events ### CreateLockupDynamicStream Emitted when an LD stream is created. ```solidity event CreateLockupDynamicStream( uint256 indexed streamId, Lockup.CreateEventCommon commonParams, LockupDynamic.Segment[] segments ); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the newly created stream. | | `commonParams` | `Lockup.CreateEventCommon` | Common parameters emitted in Create events across all Lockup models. | | `segments` | `LockupDynamic.Segment[]` | The segments the protocol uses to compose the dynamic distribution function. | --- ## ISablierLockupLinear Source: https://docs.sablier.com/reference/lockup/contracts/interfaces/interface.ISablierLockupLinear # ISablierLockupLinear [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/interfaces/ISablierLockupLinear.sol) **Inherits:** [ISablierLockupState](/reference/lockup/contracts/interfaces/interface.ISablierLockupState) **Title:** ISablierLockupLinear Creates Lockup streams with linear distribution model. ## Functions ### createWithDurationsLL Creates a stream by setting the start time to `block.timestamp`, and the end time to the sum of `block.timestamp` and `durations.total`. The stream is funded by `msg.sender` and is wrapped in an ERC-721 NFT. Emits a {Transfer}, [CreateLockupLinearStream](/reference/lockup/contracts/interfaces/interface.ISablierLockupLinear#createlockuplinearstream) and {MetadataUpdate} event. Requirements: - All requirements in [createWithTimestampsLL](/reference/lockup/contracts/interfaces/interface.ISablierLockupLinear#createwithtimestampsll) must be met for the calculated parameters. ```solidity function createWithDurationsLL( Lockup.CreateWithDurations calldata params, LockupLinear.UnlockAmounts calldata unlockAmounts, uint40 granularity, LockupLinear.Durations calldata durations ) external payable returns (uint256 streamId); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `params` | `Lockup.CreateWithDurations` | Struct encapsulating the function parameters, which are documented in {Lockup} type. | | `unlockAmounts` | `LockupLinear.UnlockAmounts` | Struct encapsulating (i) the amount to unlock at the start time and (ii) the amount to unlock at the cliff time. | | `granularity` | `uint40` | The smallest step in time between two consecutive token unlocks. Zero is a sentinel value for 1 second. | | `durations` | `LockupLinear.Durations` | Struct encapsulating (i) cliff period duration and (ii) total stream duration, both in seconds. | **Returns** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the newly created stream. | ### createWithTimestampsLL Creates a stream with the provided start time and end time. The stream is funded by `msg.sender` and is wrapped in an ERC-721 NFT. Emits a {Transfer}, [CreateLockupLinearStream](/reference/lockup/contracts/interfaces/interface.ISablierLockupLinear#createlockuplinearstream) and {MetadataUpdate} event. Notes: - A cliff time of zero means there is no cliff. - As long as the times are ordered, it is not an error for the start or the cliff time to be in the past. Requirements: - Must not be delegate called. - `params.depositAmount` must be greater than zero. - `params.timestamps.start` must be greater than zero and less than `params.timestamps.end`. - If set, `cliffTime` must be greater than `params.timestamps.start` and less than `params.timestamps.end`. - `params.recipient` must not be the zero address. - `params.sender` must not be the zero address. - The sum of `params.unlockAmounts.start` and `params.unlockAmounts.cliff` must be less than or equal to deposit amount. - If `params.timestamps.cliff` is not set, the `params.unlockAmounts.cliff` must be zero. - `granularity` must not exceed the streamable range which is `params.timestamps.end - cliffTime` if cliff is set, `params.timestamps.end - params.timestamps.start` otherwise. - `msg.sender` must have allowed this contract to spend at least `params.depositAmount` tokens. - `params.token` must not be the native token. - `params.shape.length` must not be greater than 32 characters. ```solidity function createWithTimestampsLL( Lockup.CreateWithTimestamps calldata params, LockupLinear.UnlockAmounts calldata unlockAmounts, uint40 granularity, uint40 cliffTime ) external payable returns (uint256 streamId); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `params` | `Lockup.CreateWithTimestamps` | Struct encapsulating the function parameters, which are documented in {Lockup} type. | | `unlockAmounts` | `LockupLinear.UnlockAmounts` | Struct encapsulating (i) the amount to unlock at the start time and (ii) the amount to unlock at the cliff time. | | `granularity` | `uint40` | The smallest step in time between two consecutive token unlocks. Zero is a sentinel value for 1 second. | | `cliffTime` | `uint40` | The Unix timestamp for the cliff period's end. A value of zero means there is no cliff. | **Returns** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the newly created stream. | ## Events ### CreateLockupLinearStream Emitted when an LL stream is created. ```solidity event CreateLockupLinearStream( uint256 indexed streamId, Lockup.CreateEventCommon commonParams, uint40 cliffTime, uint40 granularity, LockupLinear.UnlockAmounts unlockAmounts ); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the newly created stream. | | `commonParams` | `Lockup.CreateEventCommon` | Common parameters emitted in Create events across all Lockup models. | | `cliffTime` | `uint40` | The Unix timestamp for the cliff period's end. A value of zero means there is no cliff. | | `granularity` | `uint40` | The smallest step in time between two consecutive token unlocks. | | `unlockAmounts` | `LockupLinear.UnlockAmounts` | Struct encapsulating (i) the amount to unlock at the start time and (ii) the amount to unlock at the cliff time. | --- ## ISablierLockupPriceGated Source: https://docs.sablier.com/reference/lockup/contracts/interfaces/interface.ISablierLockupPriceGated # ISablierLockupPriceGated [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/interfaces/ISablierLockupPriceGated.sol) **Inherits:** [ISablierLockupState](/reference/lockup/contracts/interfaces/interface.ISablierLockupState) **Title:** ISablierLockupPriceGated Creates Lockup streams with price-gated distribution model. ## Functions ### createWithTimestampsLPG Creates a stream with the provided start time and end time. The stream is funded by `msg.sender` and is wrapped in an ERC-721 NFT. Emits a {Transfer}, [CreateLockupPriceGatedStream](/reference/lockup/contracts/interfaces/interface.ISablierLockupPriceGated#createlockuppricegatedstream) and {MetadataUpdate} event. Notes: - The recipient can withdraw the full deposited amount when either: 1. The oracle price reaches or exceeds the target price, OR 2. Current time is greater than or equal to the stream's end time. - The sender can cancel the stream when price is less than target price AND end time is in the future. - The function does not check if the provided oracle reports the price for the deposited token. It may be possible that stream creator has used a different token for the oracle. In such cases, integrators and recipients are requested to verify the oracle correctness on their own. - The LPG model does not support a "createWithDuration" function because the [SablierLockup](/reference/lockup/contracts/contract.SablierLockup) contract is at the size limit. If the EVM contract size limit is increased in the future, this function will be added. Requirements: - Must not be delegate called. - `params.depositAmount` must be greater than zero. - `params.sender` must not be the zero address. - `params.recipient` must not be the zero address. - `params.timestamps.start` must not be zero. - `params.timestamps.start` must be less than `params.timestamps.end`. - `unlockParams.oracle` must implement Chainlink's {AggregatorV3Interface} interface. - `unlockParams.oracle` must return a non-zero value no greater than 36 when the `decimals()` function is called. - `unlockParams.oracle` must return a positive price when the `latestRoundData()` function is called. - `unlockParams.targetPrice` must be greater than the current oracle price. - `msg.sender` must have allowed this contract to spend at least `params.depositAmount` tokens. - `params.token` must not be the native token. - `params.shape.length` must not be greater than 32 characters. ```solidity function createWithTimestampsLPG( Lockup.CreateWithTimestamps calldata params, LockupPriceGated.UnlockParams calldata unlockParams ) external payable returns (uint256 streamId); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `params` | `Lockup.CreateWithTimestamps` | Struct encapsulating the function parameters, which are documented in {Lockup} type. | | `unlockParams` | `LockupPriceGated.UnlockParams` | Struct encapsulating the unlock parameters, documented in {LockupPriceGated}. | **Returns** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the newly created stream. | ## Events ### CreateLockupPriceGatedStream Emitted when an LPG stream is created. ```solidity event CreateLockupPriceGatedStream( uint256 indexed streamId, AggregatorV3Interface indexed oracle, uint128 targetPrice ); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the newly created stream. | | `oracle` | `AggregatorV3Interface` | The price feed oracle used for retrieving the latest price. | | `targetPrice` | `uint128` | The price that must be reached to unlock the tokens, denominated in Chainlink's 8-decimal, where 1e8 = $1. | --- ## ISablierLockupRecipient Source: https://docs.sablier.com/reference/lockup/contracts/interfaces/interface.ISablierLockupRecipient # ISablierLockupRecipient [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/interfaces/ISablierLockupRecipient.sol) **Inherits:** IERC165 **Title:** ISablierLockupRecipient Interface for recipient contracts capable of reacting to cancellations and withdrawals. For this to be able to hook into Sablier, it must fully implement this interface and it must have been allowlisted in the Lockup contract. See \[IERC165-supportsInterface\]([https://eips.ethereum.org/EIPS/eip-165](https://eips.ethereum.org/EIPS/eip-165) --- ## ISablierLockupState Source: https://docs.sablier.com/reference/lockup/contracts/interfaces/interface.ISablierLockupState # ISablierLockupState [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/interfaces/ISablierLockupState.sol) **Title:** ISablierLockupState Contract with state variables (storage and constants) for the [SablierLockup](/reference/lockup/contracts/contract.SablierLockup) contract, their respective getters and helpful modifiers. ## Functions ### aggregateAmount Retrieves the aggregate amount across all streams, denoted in units of the token's decimals. If tokens are directly transferred to the contract without using the stream creation functions, the ERC-20 balance may be greater than the aggregate amount. ```solidity function aggregateAmount(IERC20 token) external view returns (uint256); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `token` | `IERC20` | The ERC-20 token for the query. | ### getCliffTime Retrieves the stream's cliff time, which is a Unix timestamp. A value of zero means there is no cliff. Reverts if `streamId` references either a null stream or a non-LL stream. ```solidity function getCliffTime(uint256 streamId) external view returns (uint40 cliffTime); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### getDepositedAmount Retrieves the amount deposited in the stream, denoted in units of the token's decimals. Reverts if `streamId` references a null stream. ```solidity function getDepositedAmount(uint256 streamId) external view returns (uint128 depositedAmount); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### getGranularity Retrieves the smallest step in time between two consecutive token unlocks. Reverts if `streamId` references either a null stream or a non-LL stream. ```solidity function getGranularity(uint256 streamId) external view returns (uint40 granularity); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### getEndTime Retrieves the stream's end time, which is a Unix timestamp. Reverts if `streamId` references a null stream. ```solidity function getEndTime(uint256 streamId) external view returns (uint40 endTime); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### getLockupModel Retrieves the distribution models used to create the stream. Reverts if `streamId` references a null stream. ```solidity function getLockupModel(uint256 streamId) external view returns (Lockup.Model lockupModel); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### getPriceGatedUnlockParams Retrieves the unlock parameters of a price-gated stream. Reverts if `streamId` references either a null stream or a non-LPG stream. ```solidity function getPriceGatedUnlockParams(uint256 streamId) external view returns (LockupPriceGated.UnlockParams memory unlockParams); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | **Returns** | Name | Type | Description | | --- | --- | --- | | `unlockParams` | `LockupPriceGated.UnlockParams` | See the documentation in {LockupPriceGated} type. | ### getRefundedAmount Retrieves the amount refunded to the sender after a cancellation, denoted in units of the token's decimals. This amount is always zero unless the stream was canceled. Reverts if `streamId` references a null stream. ```solidity function getRefundedAmount(uint256 streamId) external view returns (uint128 refundedAmount); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### getSegments Retrieves the segments used to compose the dynamic distribution function. Reverts if `streamId` references either a null stream or a non-LD stream. ```solidity function getSegments(uint256 streamId) external view returns (LockupDynamic.Segment[] memory segments); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | **Returns** | Name | Type | Description | | --- | --- | --- | | `segments` | `LockupDynamic.Segment[]` | See the documentation in {LockupDynamic} type. | ### getSender Retrieves the stream's sender. Reverts if `streamId` references a null stream. ```solidity function getSender(uint256 streamId) external view returns (address sender); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### getStartTime Retrieves the stream's start time, which is a Unix timestamp. Reverts if `streamId` references a null stream. ```solidity function getStartTime(uint256 streamId) external view returns (uint40 startTime); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### getTranches Retrieves the tranches used to compose the tranched distribution function. Reverts if `streamId` references either a null stream or a non-LT stream. ```solidity function getTranches(uint256 streamId) external view returns (LockupTranched.Tranche[] memory tranches); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | **Returns** | Name | Type | Description | | --- | --- | --- | | `tranches` | `LockupTranched.Tranche[]` | See the documentation in {LockupTranched} type. | ### getUnderlyingToken Retrieves the address of the underlying ERC-20 token being distributed. Reverts if `streamId` references a null stream. ```solidity function getUnderlyingToken(uint256 streamId) external view returns (IERC20 token); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### getUnlockAmounts Retrieves the unlock amounts used to compose the linear distribution function. Reverts if `streamId` references either a null stream or a non-LL stream. ```solidity function getUnlockAmounts(uint256 streamId) external view returns (LockupLinear.UnlockAmounts memory unlockAmounts); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | **Returns** | Name | Type | Description | | --- | --- | --- | | `unlockAmounts` | `LockupLinear.UnlockAmounts` | See the documentation in {LockupLinear} type. | ### getWithdrawnAmount Retrieves the amount withdrawn from the stream, denoted in units of the token's decimals. Reverts if `streamId` references a null stream. ```solidity function getWithdrawnAmount(uint256 streamId) external view returns (uint128 withdrawnAmount); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### isAllowedToHook Retrieves a flag indicating whether the provided address is a contract allowed to hook to Sablier when a stream is canceled or when tokens are withdrawn. See [ISablierLockupRecipient](/reference/lockup/contracts/interfaces/interface.ISablierLockupRecipient) for more information. ```solidity function isAllowedToHook(address recipient) external view returns (bool result); ``` ### isCancelable Retrieves a flag indicating whether the stream can be canceled. When the stream is cold, this flag is always `false`. Reverts if `streamId` references a null stream. ```solidity function isCancelable(uint256 streamId) external view returns (bool result); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### isDepleted Retrieves a flag indicating whether the stream is depleted. Reverts if `streamId` references a null stream. ```solidity function isDepleted(uint256 streamId) external view returns (bool result); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### isStream Retrieves a flag indicating whether the stream exists. Does not revert if `streamId` references a null stream. ```solidity function isStream(uint256 streamId) external view returns (bool result); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### isTransferable Retrieves a flag indicating whether the stream NFT can be transferred. Reverts if `streamId` references a null stream. ```solidity function isTransferable(uint256 streamId) external view returns (bool result); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### nativeToken Retrieves the address of the ERC-20 interface of the native token, if it exists. The native tokens on some chains have a dual interface as ERC-20. For example, on Polygon the $POL token is the native token and has an ERC-20 version at 0x0000000000000000000000000000000000001010. This means that `address(this).balance` returns the same value as `balanceOf(address(this))`. To avoid any unintended behavior, these tokens cannot be used in Sablier. As an alternative, users can use the Wrapped version of the token, i.e. WMATIC, which is a standard ERC-20 token. ```solidity function nativeToken() external view returns (address); ``` ### nextStreamId Counter for stream IDs, used in the create functions. ```solidity function nextStreamId() external view returns (uint256); ``` ### nftDescriptor Contract that generates the non-fungible token URI. ```solidity function nftDescriptor() external view returns (ILockupNFTDescriptor); ``` ### wasCanceled Retrieves a flag indicating whether the stream was canceled. Reverts if `streamId` references a null stream. ```solidity function wasCanceled(uint256 streamId) external view returns (bool result); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | --- ## ISablierLockupTranched Source: https://docs.sablier.com/reference/lockup/contracts/interfaces/interface.ISablierLockupTranched # ISablierLockupTranched [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/interfaces/ISablierLockupTranched.sol) **Inherits:** [ISablierLockupState](/reference/lockup/contracts/interfaces/interface.ISablierLockupState) **Title:** ISablierLockupTranched Creates Lockup streams with tranched distribution model. ## Functions ### createWithDurationsLT Creates a stream by setting the start time to `block.timestamp`, and the end time to the sum of `block.timestamp` and all specified time durations. The tranche timestamps are derived from these durations. The stream is funded by `msg.sender` and is wrapped in an ERC-721 NFT. Emits a {Transfer}, {CreateLockupTrancheStream} and {MetadataUpdate} event. Requirements: - All requirements in [createWithTimestampsLT](/reference/lockup/contracts/interfaces/interface.ISablierLockupTranched#createwithtimestampslt) must be met for the calculated parameters. ```solidity function createWithDurationsLT( Lockup.CreateWithDurations calldata params, LockupTranched.TrancheWithDuration[] calldata tranchesWithDuration ) external payable returns (uint256 streamId); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `params` | `Lockup.CreateWithDurations` | Struct encapsulating the function parameters, which are documented in {Lockup} type. | | `tranchesWithDuration` | `LockupTranched.TrancheWithDuration[]` | Tranches with durations used to compose the tranched distribution function. Timestamps are calculated by starting from `block.timestamp` and adding each duration to the previous timestamp. | **Returns** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the newly created stream. | ### createWithTimestampsLT Creates a stream with the provided tranche timestamps, implying the end time from the last timestamp. The stream is funded by `msg.sender` and is wrapped in an ERC-721 NFT. Emits a {Transfer}, {CreateLockupTrancheStream} and {MetadataUpdate} event. Notes: - As long as the tranche timestamps are arranged in ascending order, it is not an error for some of them to be in the past. Requirements: - Must not be delegate called. - `params.depositAmount` must be greater than zero. - `params.timestamps.start` must be greater than zero and less than the first tranche's timestamp. - `tranches` must have at least one tranche. - The tranche timestamps must be arranged in ascending order. - `params.timestamps.end` must be equal to the last tranche's timestamp. - The sum of the tranche amounts must equal the deposit amount. - `params.recipient` must not be the zero address. - `params.sender` must not be the zero address. - `msg.sender` must have allowed this contract to spend at least `params.depositAmount` tokens. - `params.token` must not be the native token. - `params.shape.length` must not be greater than 32 characters. ```solidity function createWithTimestampsLT( Lockup.CreateWithTimestamps calldata params, LockupTranched.Tranche[] calldata tranches ) external payable returns (uint256 streamId); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `params` | `Lockup.CreateWithTimestamps` | Struct encapsulating the function parameters, which are documented in {Lockup} type. | | `tranches` | `LockupTranched.Tranche[]` | Tranches used to compose the tranched distribution function. | **Returns** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the newly created stream. | ## Events ### CreateLockupTranchedStream Emitted when an LT stream is created. ```solidity event CreateLockupTranchedStream( uint256 indexed streamId, Lockup.CreateEventCommon commonParams, LockupTranched.Tranche[] tranches ); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the newly created stream. | | `commonParams` | `Lockup.CreateEventCommon` | Common parameters emitted in Create events across all Lockup models. | | `tranches` | `LockupTranched.Tranche[]` | The tranches the protocol uses to compose the tranched distribution function. | --- ## Errors Source: https://docs.sablier.com/reference/lockup/contracts/libraries/library.Errors # Errors [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/libraries/Errors.sol) **Title:** Errors Library containing all custom errors the protocol may revert with. ## Errors ### SablierBatchLockup\_BatchSizeZero ```solidity error SablierBatchLockup_BatchSizeZero(); ``` ### SablierLockupHelpers\_CliffTimeNotLessThanEndTime Thrown when trying to create a linear stream with a cliff time not strictly less than the end time. ```solidity error SablierLockupHelpers_CliffTimeNotLessThanEndTime(uint40 cliffTime, uint40 endTime); ``` ### SablierLockupHelpers\_CliffTimeZeroUnlockAmountNotZero Thrown when trying to create a stream with a non zero cliff unlock amount when the cliff time is zero. ```solidity error SablierLockupHelpers_CliffTimeZeroUnlockAmountNotZero(uint128 cliffUnlockAmount); ``` ### SablierLockupHelpers\_CreateNativeToken Thrown when trying to create a stream with the native token. ```solidity error SablierLockupHelpers_CreateNativeToken(address nativeToken); ``` ### SablierLockupHelpers\_DepositAmountNotEqualToSegmentAmountsSum Thrown when trying to create a dynamic stream with a deposit amount not equal to the sum of the segment amounts. ```solidity error SablierLockupHelpers_DepositAmountNotEqualToSegmentAmountsSum( uint128 depositAmount, uint128 segmentAmountsSum ); ``` ### SablierLockupHelpers\_DepositAmountNotEqualToTrancheAmountsSum Thrown when trying to create a tranched stream with a deposit amount not equal to the sum of the tranche amounts. ```solidity error SablierLockupHelpers_DepositAmountNotEqualToTrancheAmountsSum( uint128 depositAmount, uint128 trancheAmountsSum ); ``` ### SablierLockupHelpers\_DepositAmountZero Thrown when trying to create a stream with a zero deposit amount. ```solidity error SablierLockupHelpers_DepositAmountZero(); ``` ### SablierLockupHelpers\_EndTimeNotEqualToLastSegmentTimestamp Thrown when trying to create a dynamic stream with end time not equal to the last segment's timestamp. ```solidity error SablierLockupHelpers_EndTimeNotEqualToLastSegmentTimestamp(uint40 endTime, uint40 lastSegmentTimestamp); ``` ### SablierLockupHelpers\_EndTimeNotEqualToLastTrancheTimestamp Thrown when trying to create a tranched stream with end time not equal to the last tranche's timestamp. ```solidity error SablierLockupHelpers_EndTimeNotEqualToLastTrancheTimestamp(uint40 endTime, uint40 lastTrancheTimestamp); ``` ### SablierLockupHelpers\_GranularityTooHigh Thrown when trying to create a linear stream with granularity greater than the streamable range. ```solidity error SablierLockupHelpers_GranularityTooHigh(uint40 granularity, uint40 streamableRange); ``` ### SablierLockupHelpers\_SegmentCountZero Thrown when trying to create a dynamic stream with no segments. ```solidity error SablierLockupHelpers_SegmentCountZero(); ``` ### SablierLockupHelpers\_SegmentTimestampsNotOrdered Thrown when trying to create a dynamic stream with unordered segment timestamps. ```solidity error SablierLockupHelpers_SegmentTimestampsNotOrdered( uint256 index, uint40 previousTimestamp, uint40 currentTimestamp ); ``` ### SablierLockupHelpers\_SenderZeroAddress Thrown when trying to create a stream with the sender as the zero address. ```solidity error SablierLockupHelpers_SenderZeroAddress(); ``` ### SablierLockupHelpers\_ShapeExceeds32Bytes Thrown when trying to create a stream with a shape string exceeding 32 bytes. ```solidity error SablierLockupHelpers_ShapeExceeds32Bytes(uint256 shapeLength); ``` ### SablierLockupHelpers\_StartTimeNotLessThanCliffTime Thrown when trying to create a linear stream with a start time not strictly less than the cliff time, when the cliff time does not have a zero value. ```solidity error SablierLockupHelpers_StartTimeNotLessThanCliffTime(uint40 startTime, uint40 cliffTime); ``` ### SablierLockupHelpers\_StartTimeNotLessThanEndTime Thrown when trying to create a linear stream with a start time not strictly less than the end time. ```solidity error SablierLockupHelpers_StartTimeNotLessThanEndTime(uint40 startTime, uint40 endTime); ``` ### SablierLockupHelpers\_StartTimeNotLessThanFirstSegmentTimestamp Thrown when trying to create a dynamic stream with a start time not strictly less than the first segment timestamp. ```solidity error SablierLockupHelpers_StartTimeNotLessThanFirstSegmentTimestamp( uint40 startTime, uint40 firstSegmentTimestamp ); ``` ### SablierLockupHelpers\_StartTimeNotLessThanFirstTrancheTimestamp Thrown when trying to create a tranched stream with a start time not strictly less than the first tranche timestamp. ```solidity error SablierLockupHelpers_StartTimeNotLessThanFirstTrancheTimestamp( uint40 startTime, uint40 firstTrancheTimestamp ); ``` ### SablierLockupHelpers\_StartTimeZero Thrown when trying to create a stream with a zero start time. ```solidity error SablierLockupHelpers_StartTimeZero(); ``` ### SablierLockupHelpers\_TrancheCountZero Thrown when trying to create a tranched stream with no tranches. ```solidity error SablierLockupHelpers_TrancheCountZero(); ``` ### SablierLockupHelpers\_TrancheTimestampsNotOrdered Thrown when trying to create a tranched stream with unordered tranche timestamps. ```solidity error SablierLockupHelpers_TrancheTimestampsNotOrdered( uint256 index, uint40 previousTimestamp, uint40 currentTimestamp ); ``` ### SablierLockupHelpers\_UnlockAmountsSumTooHigh Thrown when trying to create a stream with the sum of the unlock amounts greater than the deposit amount. ```solidity error SablierLockupHelpers_UnlockAmountsSumTooHigh( uint128 depositAmount, uint128 startUnlockAmount, uint128 cliffUnlockAmount ); ``` ### SablierLockup\_AllowToHookUnsupportedInterface Thrown when trying to allow to hook a contract that doesn't implement the interface correctly. ```solidity error SablierLockup_AllowToHookUnsupportedInterface(address recipient); ``` ### SablierLockup\_AllowToHookZeroCodeSize Thrown when trying to allow to hook an address with no code. ```solidity error SablierLockup_AllowToHookZeroCodeSize(address recipient); ``` ### SablierLockup\_FeeTransferFailed Thrown when the fee transfer fails. ```solidity error SablierLockup_FeeTransferFailed(address comptroller, uint256 feeAmount); ``` ### SablierLockup\_InsufficientFeePayment Thrown when trying to withdraw with a fee amount less than the minimum fee. ```solidity error SablierLockup_InsufficientFeePayment(uint256 feePaid, uint256 minFeeWei); ``` ### SablierLockup\_InvalidHookSelector Thrown when the hook does not return the correct selector. ```solidity error SablierLockup_InvalidHookSelector(address recipient); ``` ### SablierLockup\_NativeTokenAlreadySet Thrown when trying to set the native token address when it is already set. ```solidity error SablierLockup_NativeTokenAlreadySet(address nativeToken); ``` ### SablierLockup\_NotTransferable Thrown when trying to transfer Stream NFT when transferability is disabled. ```solidity error SablierLockup_NotTransferable(uint256 tokenId); ``` ### SablierLockup\_Overdraw Thrown when trying to withdraw an amount greater than the withdrawable amount. ```solidity error SablierLockup_Overdraw(uint256 streamId, uint128 amount, uint128 withdrawableAmount); ``` ### SablierLockup\_WithdrawAmountNotEqualWithdrawableAmount Thrown when trying to withdraw a partial amount from a LPG stream. ```solidity error SablierLockup_WithdrawAmountNotEqualWithdrawableAmount( uint256 streamId, uint128 amount, uint128 withdrawableAmount ); ``` ### SablierLockup\_StreamCanceled Thrown when trying to cancel or renounce a canceled stream. ```solidity error SablierLockup_StreamCanceled(uint256 streamId); ``` ### SablierLockup\_StreamDepleted Thrown when trying to cancel, renounce, or withdraw from a depleted stream. ```solidity error SablierLockup_StreamDepleted(uint256 streamId); ``` ### SablierLockup\_StreamNotCancelable Thrown when trying to cancel or renounce a stream that is not cancelable. ```solidity error SablierLockup_StreamNotCancelable(uint256 streamId); ``` ### SablierLockup\_StreamNotDepleted Thrown when trying to burn a stream that is not depleted. ```solidity error SablierLockup_StreamNotDepleted(uint256 streamId); ``` ### SablierLockup\_StreamSettled Thrown when trying to cancel or renounce a settled stream. ```solidity error SablierLockup_StreamSettled(uint256 streamId); ``` ### SablierLockup\_TargetPriceTooLow Thrown when trying to create a price-gated stream with a target price not greater than the current oracle price. ```solidity error SablierLockup_TargetPriceTooLow(uint128 targetPrice, uint128 latestPrice); ``` ### SablierLockup\_Unauthorized Thrown when `msg.sender` lacks authorization to perform an action. ```solidity error SablierLockup_Unauthorized(uint256 streamId, address caller); ``` ### SablierLockup\_WithdrawalAddressNotRecipient Thrown when trying to withdraw to an address other than the recipient's. ```solidity error SablierLockup_WithdrawalAddressNotRecipient(uint256 streamId, address caller, address to); ``` ### SablierLockup\_WithdrawAmountZero Thrown when trying to withdraw zero tokens from a stream. ```solidity error SablierLockup_WithdrawAmountZero(uint256 streamId); ``` ### SablierLockup\_WithdrawArrayCountsNotEqual Thrown when trying to withdraw from multiple streams and the number of stream IDs does not match the number of withdraw amounts. ```solidity error SablierLockup_WithdrawArrayCountsNotEqual(uint256 streamIdsCount, uint256 amountsCount); ``` ### SablierLockup\_WithdrawToZeroAddress Thrown when trying to withdraw to the zero address. ```solidity error SablierLockup_WithdrawToZeroAddress(uint256 streamId); ``` ### SablierLockupState\_NotExpectedModel Thrown when a function is called on a stream that does not use the expected Lockup model. ```solidity error SablierLockupState_NotExpectedModel(Lockup.Model actualLockupModel, Lockup.Model expectedLockupModel); ``` ### SablierLockupState\_Null Thrown when the ID references a null stream. ```solidity error SablierLockupState_Null(uint256 streamId); ``` --- ## LockupHelpers Source: https://docs.sablier.com/reference/lockup/contracts/libraries/library.LockupHelpers # LockupHelpers [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/libraries/LockupHelpers.sol) **Title:** LockupHelpers Library with functions needed to validate input parameters across Lockup streams. ## Functions ### calculateSegmentTimestamps Calculate the timestamps and return the segments. ```solidity function calculateSegmentTimestamps( LockupDynamic.SegmentWithDuration[] memory segmentsWithDuration, uint40 startTime ) public pure returns (LockupDynamic.Segment[] memory segmentsWithTimestamps); ``` ### calculateTrancheTimestamps Calculate the timestamps and return the tranches. ```solidity function calculateTrancheTimestamps( LockupTranched.TrancheWithDuration[] memory tranchesWithDuration, uint40 startTime ) public pure returns (LockupTranched.Tranche[] memory tranchesWithTimestamps); ``` ### checkCreateLD Checks the parameters of the [SablierLockup-\_createLD](/reference/lockup/contracts/abstracts/abstract.SablierLockupDynamic#_createld) function. ```solidity function checkCreateLD( address sender, Lockup.Timestamps memory timestamps, uint128 depositAmount, LockupDynamic.Segment[] memory segments, address token, address nativeToken, string memory shape ) public pure; ``` ### checkCreateLL Checks the parameters of the [SablierLockup-\_createLL](/reference/lockup/contracts/abstracts/abstract.SablierLockupLinear#_createll) function. ```solidity function checkCreateLL( uint40 cliffTime, uint128 depositAmount, uint40 granularity, address nativeToken, address sender, string memory shape, Lockup.Timestamps memory timestamps, address token, LockupLinear.UnlockAmounts memory unlockAmounts ) public pure; ``` ### checkCreateLPG Checks the parameters of the [SablierLockup-\_createLPG](/reference/lockup/contracts/abstracts/abstract.SablierLockupPriceGated#_createlpg) function. ```solidity function checkCreateLPG( address sender, Lockup.Timestamps memory timestamps, uint128 depositAmount, address token, address nativeToken, string memory shape, LockupPriceGated.UnlockParams memory unlockParams ) public view; ``` ### checkCreateLT Checks the parameters of the [SablierLockup-\_createLT](/reference/lockup/contracts/abstracts/abstract.SablierLockupTranched#_createlt) function. ```solidity function checkCreateLT( address sender, Lockup.Timestamps memory timestamps, uint128 depositAmount, LockupTranched.Tranche[] memory tranches, address token, address nativeToken, string memory shape ) public pure; ``` ### \_checkTimestampsAndUnlockAmounts Checks the user-provided timestamps of an LL stream. ```solidity function _checkTimestampsAndUnlockAmounts( uint40 cliffTime, uint128 depositAmount, uint40 granularity, Lockup.Timestamps memory timestamps, LockupLinear.UnlockAmounts memory unlockAmounts ) private pure; ``` ### \_checkCreateStream Checks the user-provided common parameters across Lockup streams. ```solidity function _checkCreateStream( address sender, uint128 depositAmount, uint40 startTime, address token, address nativeToken, string memory shape ) private pure; ``` ### \_checkSegments Checks: 1. The first timestamp is strictly greater than the start time. 2. The timestamps are ordered chronologically. 3. There are no duplicate timestamps. 4. The deposit amount is equal to the sum of all segment amounts. 5. The end time equals the last segment's timestamp. ```solidity function _checkSegments( LockupDynamic.Segment[] memory segments, uint128 depositAmount, Lockup.Timestamps memory timestamps ) private pure; ``` ### \_checkTranches Checks: 1. The first timestamp is strictly greater than the start time. 2. The timestamps are ordered chronologically. 3. There are no duplicate timestamps. 4. The deposit amount is equal to the sum of all tranche amounts. 5. The end time equals the last tranche's timestamp. ```solidity function _checkTranches( LockupTranched.Tranche[] memory tranches, uint128 depositAmount, Lockup.Timestamps memory timestamps ) private pure; ``` --- ## LockupMath Source: https://docs.sablier.com/reference/lockup/contracts/libraries/library.LockupMath # LockupMath [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/libraries/LockupMath.sol) **Title:** LockupMath Provides functions for calculating the streamed amounts in Lockup streams. Note that 'streamed' is synonymous with 'vested'. ## Functions ### calculateStreamedAmountLD Calculates the streamed amount of LD streams. The LD streaming model uses the following distribution function: $$ f(x) = x^{exp} \cdot csa + \Sigma(esa) $$ Where: - $x$ is the elapsed time divided by the total duration of the current segment. - $exp$ is the current segment exponent. - $csa$ is the current segment amount. - $\Sigma(esa)$ is the sum of all streamed segments' amounts. Notes: 1. Normalization to 18 decimals is not needed because there is no mix of amounts with different decimals. 2. The stream's start time must be in the past so that the calculations below do not overflow. 3. The stream's end time must be in the future so that the loop below does not panic with an "index out of bounds" error. Assumptions: 4. The sum of all segment amounts does not overflow uint128 and equals the deposited amount. 5. The first segment's timestamp is greater than the start time. 6. The last segment's timestamp equals the end time. 7. The segment timestamps are arranged in ascending order. ```solidity function calculateStreamedAmountLD( uint128 depositedAmount, uint40 endTime, LockupDynamic.Segment[] calldata segments, uint40 startTime, uint128 withdrawnAmount ) external view returns (uint128); ``` ### calculateStreamedAmountLL Calculates the streamed amount of LL streams. The LL streaming model uses the following distribution function: $$ f(x) = \begin{cases} s & \text{if block timestamp} < \text{cliff time} \\ x \cdot sa + s + c & \text{if block timestamp} \geq \text{cliff time} \end{cases} $$ Where: - $sa$ is the streamable amount, i.e. deposited amount minus unlock amounts' sum. - $s$ is the start unlock amount. - $c$ is the cliff unlock amount. - $x$ is the elapsed time percentage with discrete unlocks: $$ x = \frac{\lfloor \text{time elapsed} / \text{granularity} \rfloor \cdot \text{granularity}}{\text{streamable time}} $$ The floor division in the numerator creates discrete unlock steps at every granularity seconds. Assumptions: 1. The sum of the unlock amounts (start and cliff) does not overflow uint128 and is less than or equal to the deposit amount. 2. The start time is before the end time. 3. If the cliff time is not zero, it is after the start time and before the end time. 4. Granularity is less than or equal to the streamable range. 5. Granularity is not zero. ```solidity function calculateStreamedAmountLL( uint40 cliffTime, uint128 depositedAmount, uint40 endTime, uint40 granularity, uint40 startTime, LockupLinear.UnlockAmounts calldata unlockAmounts, uint128 withdrawnAmount ) external view returns (uint128); ``` ### calculateStreamedAmountLPG Calculates the streamed amount of LPG streams. The LPG streaming model uses all-or-nothing unlock based on price threshold: $$ f(x) = \begin{cases} \text{deposited} & \text{if block timestamp} \geq \text{end time or latest price} \geq \text{target price} \\ 0 & \text{otherwise} \end{cases} $$ Assumptions: 1. The stream is not canceled. 2. The oracle is assumed to be returning the correct price. ```solidity function calculateStreamedAmountLPG( uint128 deposited, uint40 endTime, LockupPriceGated.UnlockParams memory unlockParams ) external view returns (uint128); ``` ### calculateStreamedAmountLT Calculates the streamed amount of LT streams. The LT streaming model uses the following distribution function: $$ f(x) = \Sigma(eta) $$ Where: - $\Sigma(eta)$ is the sum of all streamed tranches' amounts. Assumptions: 1. The sum of all tranche amounts does not overflow uint128, and equals the deposited amount. 2. The first tranche's timestamp is greater than the start time. 3. The last tranche's timestamp equals the end time. 4. The tranche timestamps are arranged in ascending order. ```solidity function calculateStreamedAmountLT( uint128 depositedAmount, uint40 endTime, uint40 startTime, LockupTranched.Tranche[] calldata tranches ) external view returns (uint128); ``` --- ## NFTSVG Source: https://docs.sablier.com/reference/lockup/contracts/libraries/library.NFTSVG # NFTSVG [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/libraries/NFTSVG.sol) ## Constants ### CARD\_MARGIN ```solidity uint256 internal constant CARD_MARGIN = 16 ``` ## Functions ### generateSVG ```solidity function generateSVG(SVGParams memory params) internal pure returns (string memory); ``` ### generateDefs ```solidity function generateDefs( string memory accentColor, string memory status, string memory cards ) internal pure returns (string memory); ``` ### generateFloatingText ```solidity function generateFloatingText( string memory lockupAddress, string memory tokenAddress, string memory tokenSymbol ) internal pure returns (string memory); ``` ### generateHrefs ```solidity function generateHrefs( uint256 progressXPosition, uint256 statusXPosition, uint256 amountXPosition, uint256 durationXPosition ) internal pure returns (string memory); ``` ## Structs ### SVGParams ```solidity struct SVGParams { string accentColor; string amount; string tokenAddress; string tokenSymbol; string duration; string lockupAddress; string progress; uint256 progressNumerical; string status; } ``` ### SVGVars ```solidity struct SVGVars { string amountCard; uint256 amountWidth; uint256 amountXPosition; string cards; uint256 cardsWidth; string durationCard; uint256 durationWidth; uint256 durationXPosition; string progressCard; uint256 progressWidth; uint256 progressXPosition; string statusCard; uint256 statusWidth; uint256 statusXPosition; } ``` --- ## SVGElements Source: https://docs.sablier.com/reference/lockup/contracts/libraries/library.SVGElements # SVGElements [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/libraries/SVGElements.sol) ## Constants ### BACKGROUND ```solidity string internal constant BACKGROUND = '' ``` ### BACKGROUND\_COLOR ```solidity string internal constant BACKGROUND_COLOR = "hsl(230,21%,11%)" ``` ### FLOATING\_TEXT ```solidity string internal constant FLOATING_TEXT = '' ``` ### GLOW ```solidity string internal constant GLOW = '' ``` ### LOGO ```solidity string internal constant LOGO = '