# Sablier Flow Documentation > Sablier Flow is useful for payroll, grants, insurance premiums, loans interest and ESOPs. ## 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) | --- ## Payments Source: https://docs.sablier.com/apps/features/payments # Payments The Sablier Interface displays [Flow](/concepts/flow/overview) streams under the Payments tab. These are token streams with no end time, an ever-increasing amount (meaning the streams can be topped up), and a flexible rate per second. ![Banner Payments](/assets/images/docs-payments-e60c94c256d135173aeb444b1edb5ddc.webp) ## Features ### Flexible terms Increase the rate/second, fund the stream with more tokens and keep it alive indefinitely! With Flow streams in the Payments tab you have the freedom to adapt your distribution schedule based on external KPIs, pivots or executive decisions. ### Explore the dashboard Enter the Dashboard and discover a detailed overview of your incoming and outgoing flow streams. Take advantage of the Search functionality to explore the chain and gain more insight into how others are using Flow for continuous payments, grants, salaries and more. | | | --- | | ![All](/assets/images/payments-dashboard-split-43e04c88575b87d42fe56987b8020ebd.webp) | ### Top up multiple streams Select the streams you want to top up, provide the deposit amount for each stream, and confirm the batched top-up with only one transaction. You can specify custom amounts for each stream, or top up all streams with the same amount. | | | --- | | ![Top up multiple streams](/assets/images/topup-multiple-streams-eb50e1706c28c4eba13ff80764cbe7bc.gif) | ### 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/payments-profile-fd122b125464b5c737ec94132d19501e.webp) | ### Save URL and top up later Create a unique URL to easily search the selected group of streams. Use this URL or share it with your partners to top up the streams at a later time. | | | --- | | ![URL for batch streams](/assets/images/batch-streams-url-fd395e71142881779aa1c58cf8b9424d.gif) | ### 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 enable payments on 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 with CSV As an alternative to manually filling out the form, you can upload a CSV file with your user data. ### 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 Flow 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 | βœ… | ❌ | \- | | Deposit / Top-up | βœ… | \- | βœ… | | Adjust rate | βœ… | ❌ | \- | | Refund | βœ… | ❌ | \- | | Void | βœ… | βœ… | \- | | Pause | βœ… | ❌ | \- | | Restart | βœ… | ❌ | \- | | Transfer | ❌ | βœ… | \- | | Withdraw | βœ… | βœ… | βœ… | ### Safe multisig support Payments are 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. --- ## Overview Source: https://docs.sablier.com/concepts/flow/overview # Overview Flow is a debt tracking protocol that tracks tokens owed between two parties, enabling indefinite token streaming. A Flow stream is characterized by its rate per second (rps). The relationship between the amount owed and time elapsed is linear and can be defined as: $$ \text{amount owed} = rps \cdot \text{elapsed time} $$ The Flow protocol can be used in several areas of everyday finance, such as payroll, distributing grants, insurance premiums, loans interest, token ESOPs etc. If you are looking for vesting and airdrops, please refer to our [Lockup](/concepts/lockup/overview) protocol. ## Features 1. **Flexible deposit:** A stream can be funded with any amount, at any time, by anyone, in full or in parts. 2. **Flexible start time:** A stream can be created with a start time in past, present or in future. 3. **Flexible end time:** A stream can be created without specifying any end time so that it can run indefinitely. 4. **Pause:** A stream can be paused by the sender and can later be restarted without losing track of previously accrued debt. 5. **Refund:** Unstreamed amount can be refunded back to the sender at any time. 6. **Void:** Voiding a stream implies it cannot be restarted anymore. Voiding an insolvent stream forfeits the uncovered debt. Either party can void a stream at any time. 7. **Withdraw:** it is publicly callable as long as `to` is set to the recipient. However, a stream’s recipient is allowed to withdraw funds to any address. ## Key Definitions The definitions below will help you understand some terms used in Sablier Flow: ## Open-Ended Streams Open-ended streams on Sablier are token streams without a predefined end date. They continue indefinitely until voided by the sender. Key traits: - No end time: the stream has a start time but no fixed end time. - Funds are streamed continuously at a fixed rate per second. - Stream must be topped up periodically to maintain solvency, [debt is otherwise accumulated](/concepts/flow/overview#total-debt). - Useful for ongoing payments like salaries, grants, or subscriptions. ### Stream balance Stream balance is the token balance of a stream. It increases when funds are deposited into a stream, and decreases when the sender refunds from it or when a withdrawal happens. ### Total debt Total debt is the amount of tokens owed to the recipient. This value is further divided into two sub-categories: - **Covered debt:** The part of the total debt that is covered by the stream balance. This is the same as the **withdrawable amount**, which is an alias. - **Uncovered debt:** The part of the total debt that is not covered by the stream balance. This is what the sender owes to the stream. $$ \text{total debt} = \text{covered debt} + \text{uncovered debt} $$ ### Snapshot debt and Snapshot time A snapshot is an event during which snapshot debt and snapshot time of a Flow stream are updated. **Snapshot debt** is the debt accumulated until the previous snapshot. The UNIX timestamp at which snapshot debt is updated is called **Snapshot time**. At snapshot, the following operations are taking place: $$ \text{snapshot debt} = \text{previous snapshot debt} + \underbrace{ rps \cdot (\text{block.timestamp} - \text{snapshot time})}_\text{ongoing debt} $$ $$ \text{snapshot time} = \text{block.timestamp} $$ ### Ongoing debt Ongoing debt is the debt accumulated since the previous snapshot. It is calculated as the following: $$ \text{ongoing debt} = rps \cdot (\text{block.timestamp} - \text{snapshot time}) $$ Therefore, at any point in time, total debt can also be defined as: $$ \text{total debt} = \text{snapshot debt} + \text{ongoing debt} $$ --- ## Stream Statuses Source: https://docs.sablier.com/concepts/flow/statuses # Stream Statuses A Flow stream can have one of five distinct statuses: | Status | Description | | --- | --- | | `PENDING` | Stream created but not started. | | `STREAMING_SOLVENT` | Active stream with total debt not exceeding stream balance. | | `STREAMING_INSOLVENT` | Active stream with total debt exceeding stream balance. | | `PAUSED_SOLVENT` | Paused stream with total debt not exceeding stream balance. | | `PAUSED_INSOLVENT` | Paused stream with total debt exceeding stream balance. | | `VOIDED` | Paused stream that can no longer be restarted and has forfeited its uncovered debt. | ## Stream characteristics A stream can have the following characteristics: | Characteristic | Statuses | Description | | --- | --- | --- | | Pending | `PENDING` | Snapshot time in future, and non-zero rps. | | Streaming | `STREAMING_SOLVENT`, `STREAMING_INSOLVENT` | Non-zero rps. | | Paused | `PAUSED_SOLVENT`, `PAUSED_INSOLVENT`, `VOIDED` | Zero rps. | | Solvent | `STREAMING_SOLVENT`, `PAUSED_SOLVENT`, `VOIDED` | Total debt not exceeding the stream balance. | | Insolvent | `STREAMING_INSOLVENT`, `PAUSED_INSOLVENT` | Total debt exceeding the stream balance. | ## State transitions The following diagram illustrates the statuses and the allowed transitions between them: ## Functions Statuses Interaction ### NULL stream ### PENDING stream ### STREAMING stream ### PAUSED stream ### VOIDED stream ## 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 `STREAMING_INSOLVENT`, that means the stream is active but has insufficient balance. As a sender, you should deposit into the stream so that your recipient can withdraw the streamed amount without any hiccups. And if you don't want to continue the stream, you can pause it. ### Q: Who can make a stream `VOIDED`? A: Both sender and recipient can void the stream. This is especially useful when either party wants to stop the stream immediately. Once a stream is voided, it cannot be restarted. If there is uncovered debt, it will be reset to 0. So to ensure that your recipient does not lose on any streamed amount, you can deposit into the stream before voiding it. ```mermaid flowchart LR subgraph PAUSED direction RL PS(SOLVENT) PI(INSOLVENT) PI -- "deposit" --> PS end subgraph STREAMING direction LR SS(SOLVENT) SI(INSOLVENT) SI -- "deposit" --> SS SS -- "time" --> SI end PENDING -- "time" --> STREAMING STREAMING -- pause --> PAUSED STREAMING -- void --> VOIDED PAUSED -- restart --> STREAMING PAUSED -- void --> VOIDED PENDING -- void --> VOIDED NULL -- create (rps > 0, startTime > blockTime) --> PENDING NULL -- create (rps > 0, startTime <= blockTime) --> STREAMING NULL -- create (rps = 0, startTime <= blockTime) --> PAUSED ``` ```mermaid flowchart LR CR([CREATE]) --> NULL((NULL)) ``` ```mermaid flowchart TD P((PENDING)) ADJRPS([ADJUST_RPS]) --> P DP([DEPOSIT]) --> P RFD([REFUND]) --> P VD([VOID]) --> P ``` ```mermaid flowchart TD STR((STREAMING)) ADJRPS([ADJUST_RPS]) --> STR DP([DEPOSIT]) --> STR RFD([REFUND]) --> STR PS([PAUSE]) --> STR VD([VOID]) --> STR WTD([WITHDRAW]) --> STR ``` ```mermaid flowchart TD PSED((PAUSED)) DP([DEPOSIT]) --> PSED RFD([REFUND]) --> PSED RST([RESTART]) --> PSED VD([VOID]) --> PSED WTD([WITHDRAW]) --> PSED ``` ```mermaid flowchart LR VOID((VOIDED)) RFD([REFUND]) --> VOID WTD([WITHDRAW]) --> VOID ``` --- ## Flow Deployments Source: https://docs.sablier.com/guides/flow/deployments # Flow Deployments This section contains the deployment addresses for the v3.0 release of [@sablier/flow](https://npmjs.com/package/@sablier/flow). 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 ## 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 deployments? Click below to see other versions. | Version | Release Date | UI Aliases | | --- | --- | --- | | [v3.0](/guides/flow/deployments) (latest) | March 2026 | - `FL4` | | [v2.0](/guides/flow/previous-deployments/v2.0) | October 2025 | - `FL3` | | [v1.1](/guides/flow/previous-deployments/v1.1) | February 2025 | - `FL2` | | [v1.0](/guides/flow/previous-deployments/v1.0) | December 2024 | - `FL` | :::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 | | --- | --- | --- | | SablierFlow | [`0x844344cd871b28221d725ece9630e8bde4e3a181`](https://etherscan.io/address/0x844344cd871b28221d725ece9630e8bde4e3a181) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | | FlowNFTDescriptor | [`0x24bE13897eE1F83367661B6bA616a72523fC55C9`](https://etherscan.io/address/0x24bE13897eE1F83367661B6bA616a72523fC55C9) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | ### Abstract βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x2fac86e709bac0d970c0e103d3b9580d2df4be5d`](https://abscan.org/address/0x2fac86e709bac0d970c0e103d3b9580d2df4be5d) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | | FlowNFTDescriptor | [`0x6CefdBc5Ba80937235F012c83d6aA83F1200d6cC`](https://abscan.org/address/0x6CefdBc5Ba80937235F012c83d6aA83F1200d6cC) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | ### Arbitrum βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xa70b8555157500b11f41a37dd93f4b4e997d583d`](https://arbiscan.io/address/0xa70b8555157500b11f41a37dd93f4b4e997d583d) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | | FlowNFTDescriptor | [`0x5F23eF12A7e861CB92c24B4314Af2A5F363CDD4F`](https://arbiscan.io/address/0x5F23eF12A7e861CB92c24B4314Af2A5F363CDD4F) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | ### Avalanche βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x980878b890e755c788bce5db7725bcc6df76bf5b`](https://snowscan.xyz/address/0x980878b890e755c788bce5db7725bcc6df76bf5b) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | | FlowNFTDescriptor | [`0xb09b714B0feC83675E09fc997B7D532cF6620326`](https://snowscan.xyz/address/0xb09b714B0feC83675E09fc997B7D532cF6620326) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | ### Base βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x0cbfe6ce6f05c47d6243bb3818837971c6ccb46b`](https://basescan.org/address/0x0cbfe6ce6f05c47d6243bb3818837971c6ccb46b) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | | FlowNFTDescriptor | [`0x5b5e742305Be3A484EacCB124C83456463c24E6a`](https://basescan.org/address/0x5b5e742305Be3A484EacCB124C83456463c24E6a) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | ### Berachain βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x1794f514d7c1d771055ffd2a880148f619107945`](https://berascan.com/address/0x1794f514d7c1d771055ffd2a880148f619107945) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | | FlowNFTDescriptor | [`0x581250eE4311F7Dc1afCF965cF8024004B423e9E`](https://berascan.com/address/0x581250eE4311F7Dc1afCF965cF8024004B423e9E) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | ### BNB Chain βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xa9b86b045caedb791af729f6c15435b978c34f7f`](https://bscscan.com/address/0xa9b86b045caedb791af729f6c15435b978c34f7f) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | | FlowNFTDescriptor | [`0xAE557c04B46d47Ecac24edA63F22cabB4571Da61`](https://bscscan.com/address/0xAE557c04B46d47Ecac24edA63F22cabB4571Da61) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | ### Chiliz βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x4d3cecb8eeddd5e69c201017e884ae5e8338474f`](https://chiliscan.com/address/0x4d3cecb8eeddd5e69c201017e884ae5e8338474f) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | | FlowNFTDescriptor | [`0xC7fd18CA19938d559dC45aDE362a850015CF0bd8`](https://chiliscan.com/address/0xC7fd18CA19938d559dC45aDE362a850015CF0bd8) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | ### Denergy βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x0B5f82Fa564D2B7F97d6048308167aA8B710e20E`](https://explorer.denergychain.com/address/0x0B5f82Fa564D2B7F97d6048308167aA8B710e20E) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | | FlowNFTDescriptor | [`0x8C4bCE3A96CA4E1275B11FDcC38d00D142af2C3f`](https://explorer.denergychain.com/address/0x8C4bCE3A96CA4E1275B11FDcC38d00D142af2C3f) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | ### Gnosis βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xb3a9a358794b101962a3741ef882b367e9e56c72`](https://gnosisscan.io/address/0xb3a9a358794b101962a3741ef882b367e9e56c72) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | | FlowNFTDescriptor | [`0x5A47FC8732d399a2f3845c4FC91aB91bb97da31F`](https://gnosisscan.io/address/0x5A47FC8732d399a2f3845c4FC91aB91bb97da31F) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | ### HyperEVM βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x91B9B0e3be6EeE0556f1cf5bCba2f2673AA28dFE`](https://hyperevmscan.io/address/0x91B9B0e3be6EeE0556f1cf5bCba2f2673AA28dFE) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | | FlowNFTDescriptor | [`0x81Cc8C4B57B9A60a56330d087D6854A8E17Dfc7A`](https://hyperevmscan.io/address/0x81Cc8C4B57B9A60a56330d087D6854A8E17Dfc7A) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | ### Lightlink βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x95f0d947befaecafa8b1e89bbada723d81783d4b`](https://phoenix.lightlink.io/address/0x95f0d947befaecafa8b1e89bbada723d81783d4b) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | | FlowNFTDescriptor | [`0xc58E948Cb0a010105467C92856bcd4842B759fb1`](https://phoenix.lightlink.io/address/0xc58E948Cb0a010105467C92856bcd4842B759fb1) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | ### Linea Mainnet βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x7a92392b7c35610a861f82c42043e6705979369c`](https://lineascan.build/address/0x7a92392b7c35610a861f82c42043e6705979369c) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | | FlowNFTDescriptor | [`0x294D7fceBa43C4507771707CeBBB7b6d81d0BFdE`](https://lineascan.build/address/0x294D7fceBa43C4507771707CeBBB7b6d81d0BFdE) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | ### Mode βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x5a51fd153874429f4cad36cc54560beffeead6df`](https://modescan.io/address/0x5a51fd153874429f4cad36cc54560beffeead6df) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | | FlowNFTDescriptor | [`0xD9E2822a33606741BeDbA31614E68A745e430102`](https://modescan.io/address/0xD9E2822a33606741BeDbA31614E68A745e430102) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | ### Monad βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x95004df5abe86a246664d8f5fb2683f24df768d1`](https://monadscan.com/address/0x95004df5abe86a246664d8f5fb2683f24df768d1) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | | FlowNFTDescriptor | [`0xf51BB8bd1cfc7C890dB68c39dCCA67CAd7810Ce4`](https://monadscan.com/address/0xf51BB8bd1cfc7C890dB68c39dCCA67CAd7810Ce4) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | ### Morph βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x5ba4cc0a1014faf0967624f3f1c3d63b9ffeb287`](https://explorer.morphl2.io/address/0x5ba4cc0a1014faf0967624f3f1c3d63b9ffeb287) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | | FlowNFTDescriptor | [`0x1dd4dcE2BB742908b4062E583d9c035973413A3F`](https://explorer.morphl2.io/address/0x1dd4dcE2BB742908b4062E583d9c035973413A3F) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | ### OP Mainnet βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xe8a69dabae3003df4cb0901389766c4b2d34c2eb`](https://optimistic.etherscan.io/address/0xe8a69dabae3003df4cb0901389766c4b2d34c2eb) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | | FlowNFTDescriptor | [`0x7AD245b74bBC1B71Da1713D53238931F791b90A3`](https://optimistic.etherscan.io/address/0x7AD245b74bBC1B71Da1713D53238931F791b90A3) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | ### Polygon βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x20080f7e2d58b5cfc4e6d997c841999e3416843c`](https://polygonscan.com/address/0x20080f7e2d58b5cfc4e6d997c841999e3416843c) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | | FlowNFTDescriptor | [`0x87B836a9e26673feB3E409A0da2EAf99C79f26C3`](https://polygonscan.com/address/0x87B836a9e26673feB3E409A0da2EAf99C79f26C3) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | ### Scroll βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xd3dec781af1f5ccb828f97d3e5deb86f6efc5e5a`](https://scrollscan.com/address/0xd3dec781af1f5ccb828f97d3e5deb86f6efc5e5a) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | | FlowNFTDescriptor | [`0x797Fe78c41d9cbE81BBEA2f420101be5e47d2aFf`](https://scrollscan.com/address/0x797Fe78c41d9cbE81BBEA2f420101be5e47d2aFf) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | ### Sonic βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x1598ed7ffb006a4e233268e7846faa9e17ac9c16`](https://sonicscan.org/address/0x1598ed7ffb006a4e233268e7846faa9e17ac9c16) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | | FlowNFTDescriptor | [`0xAab30e5CB903f67F109aFc7102ac8ED803681EA5`](https://sonicscan.org/address/0xAab30e5CB903f67F109aFc7102ac8ED803681EA5) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | ### Superseed βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xa80de83ea03335396161bb267e1250fb5cc99cdf`](https://explorer.superseed.xyz/address/0xa80de83ea03335396161bb267e1250fb5cc99cdf) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | | FlowNFTDescriptor | [`0xd932fDA016eE9d9F70f745544b4F56715b1E723b`](https://explorer.superseed.xyz/address/0xd932fDA016eE9d9F70f745544b4F56715b1E723b) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | ### Unichain βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x12a6a5f809d451d29e4c1a6bca31b88c914100ac`](https://uniscan.xyz/address/0x12a6a5f809d451d29e4c1a6bca31b88c914100ac) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | | FlowNFTDescriptor | [`0x89824A7e48dcf6B7AE9DeE6E566f62A5aDF037F2`](https://uniscan.xyz/address/0x89824A7e48dcf6B7AE9DeE6E566f62A5aDF037F2) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | ### XDC βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x2a89ddeafebf51cb8517da2d00df2365bf3ef49e`](https://xdcscan.com/address/0x2a89ddeafebf51cb8517da2d00df2365bf3ef49e) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | | FlowNFTDescriptor | [`0x9D3F0122b260D2218ecf681c416495882003deDd`](https://xdcscan.com/address/0x9D3F0122b260D2218ecf681c416495882003deDd) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | ### ZKsync Era βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xa1b75ac1e36504c93279c69c2583ff0c73eb036b`](https://explorer.zksync.io/address/0xa1b75ac1e36504c93279c69c2583ff0c73eb036b) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | | FlowNFTDescriptor | [`0x423C1b454250992Ede8516D36DE456F609714B53`](https://explorer.zksync.io/address/0x423C1b454250992Ede8516D36DE456F609714B53) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | ## Testnets ### Arbitrum Sepolia ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x2c81c86a66c9459f461eb0c49963b9539eca87ef`](https://sepolia.arbiscan.io/address/0x2c81c86a66c9459f461eb0c49963b9539eca87ef) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | | FlowNFTDescriptor | [`0x3E64A31C3974b6ae9f09a8fbc784519bF551e795`](https://sepolia.arbiscan.io/address/0x3E64A31C3974b6ae9f09a8fbc784519bF551e795) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | ### Base Sepolia βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xc1ba5a41936aaab0ff920446db556efe17fc1c5d`](https://sepolia.basescan.org/address/0xc1ba5a41936aaab0ff920446db556efe17fc1c5d) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | | FlowNFTDescriptor | [`0xcb5591F6d0e0fFC03037ef7b006D1361C6D33D25`](https://sepolia.basescan.org/address/0xcb5591F6d0e0fFC03037ef7b006D1361C6D33D25) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | ### BattleChain Testnet ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x711900e5f55d427cd88e5E3FCAe54Ccf02De71F4`](https://explorer.testnet.battlechain.com/address/0x711900e5f55d427cd88e5E3FCAe54Ccf02De71F4) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | | FlowNFTDescriptor | [`0x28eAB88ee8a951F78e1028557D0C3fD97af61A33`](https://explorer.testnet.battlechain.com/address/0x28eAB88ee8a951F78e1028557D0C3fD97af61A33) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | ### OP Sepolia ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xf1c1411b446bd630791de95b6a503871f7bbac5f`](https://optimism-sepolia.blockscout.com/address/0xf1c1411b446bd630791de95b6a503871f7bbac5f) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | | FlowNFTDescriptor | [`0x4739327acfb56E90177d44Cb0845e759276BCA88`](https://optimism-sepolia.blockscout.com/address/0x4739327acfb56E90177d44Cb0845e759276BCA88) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | ### Sepolia βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xbd9326f6366c95e39bd8ef825c1b2f2ee0dceaa1`](https://sepolia.etherscan.io/address/0xbd9326f6366c95e39bd8ef825c1b2f2ee0dceaa1) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | | FlowNFTDescriptor | [`0xc9dBf2D207D178875b698e5f7493ce2d8BA88994`](https://sepolia.etherscan.io/address/0xc9dBf2D207D178875b698e5f7493ce2d8BA88994) | [`flow-v3.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v3.0) | --- ## Batching Functions Source: https://docs.sablier.com/guides/flow/examples/batchable # Batching Functions A neat feature of Sablier Flow is the ability to batch multiple function calls into a single transaction. This is made possible by the [`Batch`](/reference/flow/contracts/abstracts/abstract.Batch) contract, which is inherited by `SablierFlow`. With this, you can efficiently batch multiple function calls in a single transaction. :::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: ```solidity import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ud21x18, UD21x18 } from "@prb/math/src/UD21x18.sol"; import { ISablierFlow } from "@sablier/flow/src/interfaces/ISablierFlow.sol"; ``` Create a contract called `FlowBatchable`, and declare a constant `USDC` of type `IERC20` and a constant `FLOW` of type `ISablierFlow`: ```solidity // Mainnet addresses IERC20 public constant USDC = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); ISablierFlow public constant FLOW = ISablierFlow(0x844344Cd871B28221d725ecE9630E8bDE4E3a181); ``` 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, Flow addresses can be obtained from the [Flow Deployments](/guides/flow/deployments) page. ## Create multiple streams One of the most useful features achieved by `batch` is the ability to create multiple streams in a single transaction. Let's focus on it in this example. Define a function that creates multiple streams and returns their respective stream IDs: ```solidity function createMultiple() external returns (uint256[] memory streamIds) { address sender = msg.sender; address firstRecipient = address(0xCAFE); address secondRecipient = address(0xBEEF); UD21x18 firstRatePerSecond = ud21x18(0.0001e18); UD21x18 secondRatePerSecond = ud21x18(0.0002e18); bool transferable = true; // The call data declared as bytes bytes[] memory calls = new bytes[](2); calls[0] = abi.encodeCall( FLOW.create, (sender, firstRecipient, firstRatePerSecond, uint40(block.timestamp), USDC, transferable) ); calls[1] = abi.encodeCall( FLOW.create, (sender, secondRecipient, secondRatePerSecond, uint40(block.timestamp), USDC, transferable) ); // Prepare the `streamIds` array to return them uint256 nextStreamId = FLOW.nextStreamId(); streamIds = new uint256[](2); streamIds[0] = nextStreamId; streamIds[1] = nextStreamId + 1; // Execute multiple calls in a single transaction using the prepared call data. FLOW.batch(calls); } /// @dev A function to create multiple streams and deposit into all the streams in a single transaction. function createMultipleAndDeposit() external returns (uint256[] memory streamIds) { address sender = msg.sender; address firstRecipient = address(0xCAFE); address secondRecipient = address(0xBEEF); UD21x18 ratePerSecond = ud21x18(0.0001e18); uint128 depositAmount = 1000e6; bool transferable = true; // Transfer the deposit amount of USDC tokens to this contract for both streams USDC.transferFrom(msg.sender, address(this), 2 * depositAmount); // Approve the Sablier contract to spend USDC. USDC.approve(address(FLOW), 2 * depositAmount); uint256 nextStreamId = FLOW.nextStreamId(); streamIds = new uint256[](2); streamIds[0] = nextStreamId; streamIds[1] = nextStreamId + 1; // We need to have 4 different function calls, 2 for creating streams and 2 for depositing bytes[] memory calls = new bytes[](4); calls[0] = abi.encodeCall( FLOW.create, (sender, firstRecipient, ratePerSecond, uint40(block.timestamp), USDC, transferable) ); calls[1] = abi.encodeCall( FLOW.create, (sender, secondRecipient, ratePerSecond, uint40(block.timestamp), USDC, transferable) ); calls[2] = abi.encodeCall(FLOW.deposit, (streamIds[0], depositAmount, sender, firstRecipient)); calls[3] = abi.encodeCall(FLOW.deposit, (streamIds[1], depositAmount, sender, secondRecipient)); // Execute multiple calls in a single transaction using the prepared call data. FLOW.batch(calls); } /// @dev A function to pause a stream and withdraw the maximum available funds. /// Note: The streamId's sender must be this contract, otherwise, the call will fail due to no authorization. function pauseAndWithdrawMax(uint256 streamId) external payable { // The call data declared as bytes. bytes[] memory calls = new bytes[](2); calls[0] = abi.encodeCall(FLOW.pause, (streamId)); calls[1] = abi.encodeCall(FLOW.withdrawMax, (streamId, address(0xCAFE))); // Calculate the fee. uint256 fee = FLOW.calculateMinFeeWei(streamId); // Execute multiple calls in a single transaction using the prepared call data. FLOW.batch{ value: fee }(calls); } /// @dev A function to void a stream and withdraw what is left. /// Note: The streamId's sender must be this contract, otherwise, the call will fail due to no authorization. function voidAndWithdrawMax(uint256 streamId) external payable { // The call data declared as bytes bytes[] memory calls = new bytes[](2); calls[0] = abi.encodeCall(FLOW.void, (streamId)); calls[1] = abi.encodeCall(FLOW.withdrawMax, (streamId, address(0xCAFE))); // Calculate the fee. uint256 fee = FLOW.calculateMinFeeWei(streamId); // Execute multiple calls in a single transaction using the prepared call data. FLOW.batch{ value: fee }(calls); } /// @dev A function to withdraw maximum available funds from multiple streams in a single transaction. function withdrawMaxMultiple(uint256[] calldata streamIds) external payable { uint256 count = streamIds.length; uint256 maxFeeRequired; // Iterate over the streamIds and prepare the call data for each stream. bytes[] memory calls = new bytes[](count); for (uint256 i = 0; i < count; ++i) { address recipient = FLOW.getRecipient(streamIds[i]); calls[i] = abi.encodeCall(FLOW.withdrawMax, (streamIds[i], recipient)); // Calculate the fee required to withdraw the amount. It is the maximum of the fees required to withdraw // each stream. uint256 feeForStreamId = FLOW.calculateMinFeeWei(streamIds[i]); if (feeForStreamId > maxFeeRequired) { maxFeeRequired = feeForStreamId; } } // Execute multiple calls in a single transaction using the prepared call data. FLOW.batch{ value: maxFeeRequired }(calls); } } ``` ### Parameters We will create two streams with same stream parameters required by the `create` function. ```solidity address sender = msg.sender; address firstRecipient = address(0xCAFE); address secondRecipient = address(0xBEEF); UD21x18 firstRatePerSecond = ud21x18(0.0001e18); UD21x18 secondRatePerSecond = ud21x18(0.0002e18); bool transferable = true; ``` Construct an array of `bytes` of length 2 to be passed into the `batch` function: ```solidity // The call data declared as bytes bytes[] memory calls = new bytes[](2); calls[0] = abi.encodeCall( FLOW.create, (sender, firstRecipient, firstRatePerSecond, uint40(block.timestamp), USDC, transferable) ); calls[1] = abi.encodeCall( FLOW.create, (sender, secondRecipient, secondRatePerSecond, uint40(block.timestamp), USDC, transferable) ); ``` Since we are creating two streams, the function will return an array containing the two generated stream IDs: ```solidity // Prepare the `streamIds` array to return them uint256 nextStreamId = FLOW.nextStreamId(); streamIds = new uint256[](2); streamIds[0] = nextStreamId; streamIds[1] = nextStreamId + 1; ``` Execute the `batch`: ```solidity FLOW.batch(calls); } /// @dev A function to create multiple streams and deposit into all the streams in a single transaction. function createMultipleAndDeposit() external returns (uint256[] memory streamIds) { address sender = msg.sender; address firstRecipient = address(0xCAFE); address secondRecipient = address(0xBEEF); UD21x18 ratePerSecond = ud21x18(0.0001e18); uint128 depositAmount = 1000e6; bool transferable = true; // Transfer the deposit amount of USDC tokens to this contract for both streams USDC.transferFrom(msg.sender, address(this), 2 * depositAmount); // Approve the Sablier contract to spend USDC. USDC.approve(address(FLOW), 2 * depositAmount); uint256 nextStreamId = FLOW.nextStreamId(); streamIds = new uint256[](2); streamIds[0] = nextStreamId; streamIds[1] = nextStreamId + 1; // We need to have 4 different function calls, 2 for creating streams and 2 for depositing bytes[] memory calls = new bytes[](4); calls[0] = abi.encodeCall( FLOW.create, (sender, firstRecipient, ratePerSecond, uint40(block.timestamp), USDC, transferable) ); calls[1] = abi.encodeCall( FLOW.create, (sender, secondRecipient, ratePerSecond, uint40(block.timestamp), USDC, transferable) ); calls[2] = abi.encodeCall(FLOW.deposit, (streamIds[0], depositAmount, sender, firstRecipient)); calls[3] = abi.encodeCall(FLOW.deposit, (streamIds[1], depositAmount, sender, secondRecipient)); // Execute multiple calls in a single transaction using the prepared call data. FLOW.batch(calls); } /// @dev A function to pause a stream and withdraw the maximum available funds. /// Note: The streamId's sender must be this contract, otherwise, the call will fail due to no authorization. function pauseAndWithdrawMax(uint256 streamId) external payable { // The call data declared as bytes. bytes[] memory calls = new bytes[](2); calls[0] = abi.encodeCall(FLOW.pause, (streamId)); calls[1] = abi.encodeCall(FLOW.withdrawMax, (streamId, address(0xCAFE))); // Calculate the fee. uint256 fee = FLOW.calculateMinFeeWei(streamId); // Execute multiple calls in a single transaction using the prepared call data. FLOW.batch{ value: fee }(calls); } /// @dev A function to void a stream and withdraw what is left. /// Note: The streamId's sender must be this contract, otherwise, the call will fail due to no authorization. function voidAndWithdrawMax(uint256 streamId) external payable { // The call data declared as bytes bytes[] memory calls = new bytes[](2); calls[0] = abi.encodeCall(FLOW.void, (streamId)); calls[1] = abi.encodeCall(FLOW.withdrawMax, (streamId, address(0xCAFE))); // Calculate the fee. uint256 fee = FLOW.calculateMinFeeWei(streamId); // Execute multiple calls in a single transaction using the prepared call data. FLOW.batch{ value: fee }(calls); } /// @dev A function to withdraw maximum available funds from multiple streams in a single transaction. function withdrawMaxMultiple(uint256[] calldata streamIds) external payable { uint256 count = streamIds.length; uint256 maxFeeRequired; // Iterate over the streamIds and prepare the call data for each stream. bytes[] memory calls = new bytes[](count); for (uint256 i = 0; i < count; ++i) { address recipient = FLOW.getRecipient(streamIds[i]); calls[i] = abi.encodeCall(FLOW.withdrawMax, (streamIds[i], recipient)); // Calculate the fee required to withdraw the amount. It is the maximum of the fees required to withdraw // each stream. uint256 feeForStreamId = FLOW.calculateMinFeeWei(streamIds[i]); if (feeForStreamId > maxFeeRequired) { maxFeeRequired = feeForStreamId; } } // Execute multiple calls in a single transaction using the prepared call data. FLOW.batch{ value: maxFeeRequired }(calls); } } ``` ## Homework Try to implement the following ideas using `batch` function: - Adjust Rate Per Second and Deposit - Pause and Withdraw Max - Void and Withdraw Max - Multiple Withdraw Max :::note If you include any `withdraw` function in your batch, keep in mind that it requires a fee. Make sure to send the correct amount in `msg.value`, as shown below. ::: Below, you will find the full code for it. ## Other ideas There are plenty of other possibilities as well! Feel free to experiment and come up with combinations that suit your system. πŸš€ ## Full code Below you can see the complete `FlowBatchable` contract: ```solidity // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.22; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ud21x18, UD21x18 } from "@prb/math/src/UD21x18.sol"; import { ISablierFlow } from "@sablier/flow/src/interfaces/ISablierFlow.sol"; /// @notice The `Batch` contract, inherited in SablierFlow, allows multiple function calls to be batched together. This /// enables any possible combination of functions to be executed within a single transaction. /// @dev For some functions to work, `msg.sender` must have approved this contract to spend USDC. contract FlowBatchable { // Mainnet addresses IERC20 public constant USDC = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); ISablierFlow public constant FLOW = ISablierFlow(0x844344Cd871B28221d725ecE9630E8bDE4E3a181); /// @dev A function to adjust the rate per second and deposit into a stream in a single transaction. /// Note: The streamId's sender must be this contract, otherwise, the call will fail due to no authorization. function adjustRatePerSecondAndDeposit(uint256 streamId) external { UD21x18 newRatePerSecond = ud21x18(0.0002e18); uint128 depositAmount = 1000e6; // Transfer to this contract the amount to deposit in the stream. USDC.transferFrom(msg.sender, address(this), depositAmount); // Approve the Sablier contract to spend USDC. USDC.approve(address(FLOW), depositAmount); // Fetch the stream recipient. address recipient = FLOW.getRecipient(streamId); // The call data declared as bytes. bytes[] memory calls = new bytes[](2); calls[0] = abi.encodeCall(FLOW.adjustRatePerSecond, (streamId, newRatePerSecond)); calls[1] = abi.encodeCall(FLOW.deposit, (streamId, depositAmount, msg.sender, recipient)); FLOW.batch(calls); } /// @dev A function to create a stream and deposit in a single transaction. function createAndDeposit() external returns (uint256 streamId) { address sender = msg.sender; address recipient = address(0xCAFE); UD21x18 ratePerSecond = ud21x18(0.0001e18); uint128 depositAmount = 1000e6; bool transferable = true; // Transfer to this contract the amount to deposit in the stream. USDC.transferFrom(msg.sender, address(this), depositAmount); // Approve the Sablier contract to spend USDC. USDC.approve(address(FLOW), depositAmount); streamId = FLOW.nextStreamId(); // The call data declared as bytes bytes[] memory calls = new bytes[](2); calls[0] = abi.encodeCall( FLOW.create, (sender, recipient, ratePerSecond, uint40(block.timestamp), USDC, transferable) ); calls[1] = abi.encodeCall(FLOW.deposit, (streamId, depositAmount, sender, recipient)); // Execute multiple calls in a single transaction using the prepared call data. FLOW.batch(calls); } /// @dev A function to create multiple streams in a single transaction. function createMultiple() external returns (uint256[] memory streamIds) { address sender = msg.sender; address firstRecipient = address(0xCAFE); address secondRecipient = address(0xBEEF); UD21x18 firstRatePerSecond = ud21x18(0.0001e18); UD21x18 secondRatePerSecond = ud21x18(0.0002e18); bool transferable = true; // The call data declared as bytes bytes[] memory calls = new bytes[](2); calls[0] = abi.encodeCall( FLOW.create, (sender, firstRecipient, firstRatePerSecond, uint40(block.timestamp), USDC, transferable) ); calls[1] = abi.encodeCall( FLOW.create, (sender, secondRecipient, secondRatePerSecond, uint40(block.timestamp), USDC, transferable) ); // Prepare the `streamIds` array to return them uint256 nextStreamId = FLOW.nextStreamId(); streamIds = new uint256[](2); streamIds[0] = nextStreamId; streamIds[1] = nextStreamId + 1; // Execute multiple calls in a single transaction using the prepared call data. FLOW.batch(calls); } /// @dev A function to create multiple streams and deposit into all the streams in a single transaction. function createMultipleAndDeposit() external returns (uint256[] memory streamIds) { address sender = msg.sender; address firstRecipient = address(0xCAFE); address secondRecipient = address(0xBEEF); UD21x18 ratePerSecond = ud21x18(0.0001e18); uint128 depositAmount = 1000e6; bool transferable = true; // Transfer the deposit amount of USDC tokens to this contract for both streams USDC.transferFrom(msg.sender, address(this), 2 * depositAmount); // Approve the Sablier contract to spend USDC. USDC.approve(address(FLOW), 2 * depositAmount); uint256 nextStreamId = FLOW.nextStreamId(); streamIds = new uint256[](2); streamIds[0] = nextStreamId; streamIds[1] = nextStreamId + 1; // We need to have 4 different function calls, 2 for creating streams and 2 for depositing bytes[] memory calls = new bytes[](4); calls[0] = abi.encodeCall( FLOW.create, (sender, firstRecipient, ratePerSecond, uint40(block.timestamp), USDC, transferable) ); calls[1] = abi.encodeCall( FLOW.create, (sender, secondRecipient, ratePerSecond, uint40(block.timestamp), USDC, transferable) ); calls[2] = abi.encodeCall(FLOW.deposit, (streamIds[0], depositAmount, sender, firstRecipient)); calls[3] = abi.encodeCall(FLOW.deposit, (streamIds[1], depositAmount, sender, secondRecipient)); // Execute multiple calls in a single transaction using the prepared call data. FLOW.batch(calls); } /// @dev A function to pause a stream and withdraw the maximum available funds. /// Note: The streamId's sender must be this contract, otherwise, the call will fail due to no authorization. function pauseAndWithdrawMax(uint256 streamId) external payable { // The call data declared as bytes. bytes[] memory calls = new bytes[](2); calls[0] = abi.encodeCall(FLOW.pause, (streamId)); calls[1] = abi.encodeCall(FLOW.withdrawMax, (streamId, address(0xCAFE))); // Calculate the fee. uint256 fee = FLOW.calculateMinFeeWei(streamId); // Execute multiple calls in a single transaction using the prepared call data. FLOW.batch{ value: fee }(calls); } /// @dev A function to void a stream and withdraw what is left. /// Note: The streamId's sender must be this contract, otherwise, the call will fail due to no authorization. function voidAndWithdrawMax(uint256 streamId) external payable { // The call data declared as bytes bytes[] memory calls = new bytes[](2); calls[0] = abi.encodeCall(FLOW.void, (streamId)); calls[1] = abi.encodeCall(FLOW.withdrawMax, (streamId, address(0xCAFE))); // Calculate the fee. uint256 fee = FLOW.calculateMinFeeWei(streamId); // Execute multiple calls in a single transaction using the prepared call data. FLOW.batch{ value: fee }(calls); } /// @dev A function to withdraw maximum available funds from multiple streams in a single transaction. function withdrawMaxMultiple(uint256[] calldata streamIds) external payable { uint256 count = streamIds.length; uint256 maxFeeRequired; // Iterate over the streamIds and prepare the call data for each stream. bytes[] memory calls = new bytes[](count); for (uint256 i = 0; i < count; ++i) { address recipient = FLOW.getRecipient(streamIds[i]); calls[i] = abi.encodeCall(FLOW.withdrawMax, (streamIds[i], recipient)); // Calculate the fee required to withdraw the amount. It is the maximum of the fees required to withdraw // each stream. uint256 feeForStreamId = FLOW.calculateMinFeeWei(streamIds[i]); if (feeForStreamId > maxFeeRequired) { maxFeeRequired = feeForStreamId; } } // Execute multiple calls in a single transaction using the prepared call data. FLOW.batch{ value: maxFeeRequired }(calls); } } ``` --- ## Create a Flow stream Source: https://docs.sablier.com/guides/flow/examples/create-stream # Create a Flow stream In this guide, we will show you how you can create a Flow stream using Solidity. It is important to note that a Flow stream has no end date, which means it will continue to accumulate debt even if no funds are deposited. This guide assumes that you have already gone through the [Calculate Rate per Second](/guides/flow/examples/flow-calculate-rps) 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/flow`, and the `FlowUtilities` library: ```solidity import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { UD21x18 } from "@prb/math/src/UD21x18.sol"; import { ISablierFlow } from "@sablier/flow/src/interfaces/ISablierFlow.sol"; import { FlowUtilities } from "./FlowUtilities.sol"; ``` Create a contract called `FlowStreamCreator`, and declare a constant `USDC` of type `IERC20` and a constant `FLOW` of type `ISablierFlow`: ```solidity // Mainnet addresses IERC20 public constant USDC = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); ISablierFlow public constant FLOW = ISablierFlow(0x844344Cd871B28221d725ecE9630E8bDE4E3a181); ``` 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, Flow addresses can be obtained from the [Flow Deployments](/guides/flow/deployments) page. We will declare two functions, based on the amount desired to stream over a period of time. ## Define a function Define a function to stream a salary of 1000 USDC per month, call it `createStream_1K_PerMonth` which returns the newly created stream ID: ```solidity function createStream_1K_PerMonth() external returns (uint256 streamId) { ``` ## Input parameters ### Rate Per Second Use the [`FlowUtilities`](/guides/flow/examples/flow-calculate-rps) library to calculate the rate per second for the desired amount: ```solidity UD21x18 ratePerSecond = FlowUtilities.ratePerSecondWithDuration({ token: address(USDC), amount: 1000e6, duration: 30 days }); ``` ### Sender The address streaming the tokens, with the ability to `pause` the stream: ```solidity sender: msg.sender, // The sender will be able to manage the stream ``` ### Recipient The address receiving the tokens: ```solidity recipient: address(0xCAFE), // The recipient of the streamed tokens ``` ### Start time The timestamp when the stream starts. ```solidity startTime: uint40(block.timestamp), // The stream starts now ``` ### Token The contract address of the ERC-20 token used for streaming. In this example, we will stream `USDC`: ```solidity token: USDC, // The token to be streamed ``` ### Transferable Boolean that indicates whether the stream will be transferable or not. ```solidity transferable: true // Whether the stream will be transferable or not ``` ### Invoke the create function With all the parameters, we can call the `create` function on the `FLOW` contract and assign the newly created stream to `streamId` variable: ```solidity streamId = FLOW.create({ sender: msg.sender, // The sender will be able to manage the stream recipient: address(0xCAFE), // The recipient of the streamed tokens ratePerSecond: ratePerSecond, // The rate per second equivalent to 1000 USDC per month startTime: uint40(block.timestamp), // The stream starts now token: USDC, // The token to be streamed transferable: true // Whether the stream will be transferable or not }); ``` ## Full code Below you can see the complete `FlowStreamCreator` contract: ```solidity // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.22; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { UD21x18 } from "@prb/math/src/UD21x18.sol"; import { ISablierFlow } from "@sablier/flow/src/interfaces/ISablierFlow.sol"; import { FlowUtilities } from "./FlowUtilities.sol"; contract FlowStreamCreator { // Mainnet addresses IERC20 public constant USDC = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); ISablierFlow public constant FLOW = ISablierFlow(0x844344Cd871B28221d725ecE9630E8bDE4E3a181); // Create a stream that sends 1000 USDC per month. function createStream_1K_PerMonth() external returns (uint256 streamId) { UD21x18 ratePerSecond = FlowUtilities.ratePerSecondWithDuration({ token: address(USDC), amount: 1000e6, duration: 30 days }); streamId = FLOW.create({ sender: msg.sender, // The sender will be able to manage the stream recipient: address(0xCAFE), // The recipient of the streamed tokens ratePerSecond: ratePerSecond, // The rate per second equivalent to 1000 USDC per month startTime: uint40(block.timestamp), // The stream starts now token: USDC, // The token to be streamed transferable: true // Whether the stream will be transferable or not }); } // Create a stream that sends 1,000,000 USDC per year. function createStream_1M_PerYear() external returns (uint256 streamId) { UD21x18 ratePerSecond = FlowUtilities.ratePerSecondWithDuration({ token: address(USDC), amount: 1_000_000e6, duration: 365 days }); streamId = FLOW.create({ sender: msg.sender, // The sender will be able to manage the stream recipient: address(0xCAFE), // The recipient of the streamed tokens ratePerSecond: ratePerSecond, // The rate per second equivalent to 1,000,00 USDC per year startTime: uint40(block.timestamp), // The stream starts now token: USDC, // The token to be streamed transferable: true // Whether the stream will be transferable or not }); } } ``` --- ## Calculate Rate per Second Source: https://docs.sablier.com/guides/flow/examples/flow-calculate-rps # Calculate Rate per Second This guide explains how to calculate the rate per second when creating a Flow stream. It is the most important step in setting up a stream since the rate per second is a key parameter in the stream's configuration. We assume that you have already gone through the [Protocol Concepts](/concepts/streaming) and the [Flow Overview](/concepts/flow/overview) sections. :::caution The code in this guide is not production-ready, and is implemented in a simplistic manner for the purpose of learning. ::: The rate per second is the amount of tokens streamed in one second. It is represented as a fixed-point number with 18 decimals, specifically as a `UD21x18` type from the `PRBMath` library. The underlying native Solidity type associated with `UD21x18` is `uint128`. Depending on how you receive payments, you have to calculate the rate per second and scale its value to 18 decimals format as below: 1. Based on a duration, e.g., 3 months 2. Between two points in time, e.g., January 1, 2025 to April, 1 2025 The calculation method is the same in either case. ## Set up a library Declare the Solidity version used to compile the library: ```solidity pragma solidity >=0.8.22; import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import { ud21x18, UD21x18 } from "@prb/math/src/UD21x18.sol"; /// @dev A utility library to calculate rate per second and streamed amount based on a given time frame. library FlowUtilities { /// @notice This function calculates the rate per second based on a given amount of tokens and a specified duration. /// @dev The rate per second is a 18-decimal fixed-point number and it is calculated as `amount / duration`. /// @param token The address of the token. /// @param amount The amount of tokens, denoted in token's decimals. /// @param duration The duration in seconds user wishes to stream. /// @return ratePerSecond The rate per second as a 18-decimal fixed-point number. function ratePerSecondWithDuration( address token, uint128 amount, uint40 duration ) internal view returns (UD21x18 ratePerSecond) { // Get the decimals of the token. uint8 decimals = IERC20Metadata(token).decimals(); // If the token has 18 decimals, we can simply divide the amount by the duration as it returns a 18 decimal // fixed-point number. if (decimals == 18) { return ud21x18(amount / duration); } // Calculate the scale factor from the token's decimals. uint128 scaleFactor = uint128(10 ** (18 - decimals)); // Multiply the amount by the scale factor and divide by the duration. ratePerSecond = ud21x18((scaleFactor * amount) / duration); } /// @notice This function calculates the rate per second based on a given amount of tokens and a specified range. /// @dev The rate per second is a 18-decimal fixed-point number and it is calculated as `amount / (end - start)`. /// @param token The address of the token. /// @param amount The amount of tokens, denoted in token's decimals. /// @param start The start timestamp. /// @param end The end timestamp. /// @return ratePerSecond The rate per second as a 18-decimal fixed-point number. function ratePerSecondForTimestamps( address token, uint128 amount, uint40 start, uint40 end ) internal view returns (UD21x18 ratePerSecond) { // Calculate the duration. uint40 duration = end - start; // Get the decimals of the token. uint8 decimals = IERC20Metadata(token).decimals(); if (decimals == 18) { return ud21x18(amount / duration); } // Calculate the scale factor from the token's decimals. uint128 scaleFactor = uint128(10 ** (18 - decimals)); // Multiply the amount by the scale factor and divide by the duration. ratePerSecond = ud21x18((scaleFactor * amount) / duration); } /// @notice This function calculates the amount streamed over a week for a given rate per second. /// @param ratePerSecond The rate per second as a 18-decimal fixed-point number. /// @return amountPerWeek The amount streamed over a week. function calculateAmountStreamedPerWeek(UD21x18 ratePerSecond) internal pure returns (uint128 amountPerWeek) { amountPerWeek = ratePerSecond.unwrap() * 1 weeks; } /// @notice This function calculates the amount streamed over a month for a given rate per second. /// @dev For simplicity, we have assumed that there are 30 days in a month. /// @param ratePerSecond The rate per second as a 18-decimal fixed-point number. /// @return amountPerMonth The amount streamed over a month. function calculateAmountStreamedPerMonth(UD21x18 ratePerSecond) internal pure returns (uint128 amountPerMonth) { amountPerMonth = ratePerSecond.unwrap() * 30 days; } /// @notice This function calculates the amount streamed over a year for a given rate per second. /// @dev For simplicity, we have assumed that there are 365 days in a year. /// @param ratePerSecond The rate per second as a fixed-point number. /// @return amountPerYear The amount streamed over a year. function calculateAmountStreamedPerYear(UD21x18 ratePerSecond) internal pure returns (uint128 amountPerYear) { amountPerYear = ratePerSecond.unwrap() * 365 days; } } ``` Import the relevant symbols: ```solidity import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import { ud21x18, UD21x18 } from "@prb/math/src/UD21x18.sol"; ``` Declare a library that can be used in other contracts: ```solidity library FlowUtilities { /// @notice This function calculates the rate per second based on a given amount of tokens and a specified duration. /// @dev The rate per second is a 18-decimal fixed-point number and it is calculated as `amount / duration`. /// @param token The address of the token. /// @param amount The amount of tokens, denoted in token's decimals. /// @param duration The duration in seconds user wishes to stream. /// @return ratePerSecond The rate per second as a 18-decimal fixed-point number. function ratePerSecondWithDuration( address token, uint128 amount, uint40 duration ) internal view returns (UD21x18 ratePerSecond) { // Get the decimals of the token. uint8 decimals = IERC20Metadata(token).decimals(); // If the token has 18 decimals, we can simply divide the amount by the duration as it returns a 18 decimal // fixed-point number. if (decimals == 18) { return ud21x18(amount / duration); } // Calculate the scale factor from the token's decimals. uint128 scaleFactor = uint128(10 ** (18 - decimals)); // Multiply the amount by the scale factor and divide by the duration. ratePerSecond = ud21x18((scaleFactor * amount) / duration); } /// @notice This function calculates the rate per second based on a given amount of tokens and a specified range. /// @dev The rate per second is a 18-decimal fixed-point number and it is calculated as `amount / (end - start)`. /// @param token The address of the token. /// @param amount The amount of tokens, denoted in token's decimals. /// @param start The start timestamp. /// @param end The end timestamp. /// @return ratePerSecond The rate per second as a 18-decimal fixed-point number. function ratePerSecondForTimestamps( address token, uint128 amount, uint40 start, uint40 end ) internal view returns (UD21x18 ratePerSecond) { // Calculate the duration. uint40 duration = end - start; // Get the decimals of the token. uint8 decimals = IERC20Metadata(token).decimals(); if (decimals == 18) { return ud21x18(amount / duration); } // Calculate the scale factor from the token's decimals. uint128 scaleFactor = uint128(10 ** (18 - decimals)); // Multiply the amount by the scale factor and divide by the duration. ratePerSecond = ud21x18((scaleFactor * amount) / duration); } /// @notice This function calculates the amount streamed over a week for a given rate per second. /// @param ratePerSecond The rate per second as a 18-decimal fixed-point number. /// @return amountPerWeek The amount streamed over a week. function calculateAmountStreamedPerWeek(UD21x18 ratePerSecond) internal pure returns (uint128 amountPerWeek) { amountPerWeek = ratePerSecond.unwrap() * 1 weeks; } /// @notice This function calculates the amount streamed over a month for a given rate per second. /// @dev For simplicity, we have assumed that there are 30 days in a month. /// @param ratePerSecond The rate per second as a 18-decimal fixed-point number. /// @return amountPerMonth The amount streamed over a month. function calculateAmountStreamedPerMonth(UD21x18 ratePerSecond) internal pure returns (uint128 amountPerMonth) { amountPerMonth = ratePerSecond.unwrap() * 30 days; } /// @notice This function calculates the amount streamed over a year for a given rate per second. /// @dev For simplicity, we have assumed that there are 365 days in a year. /// @param ratePerSecond The rate per second as a fixed-point number. /// @return amountPerYear The amount streamed over a year. function calculateAmountStreamedPerYear(UD21x18 ratePerSecond) internal pure returns (uint128 amountPerYear) { amountPerYear = ratePerSecond.unwrap() * 365 days; } } ``` ## Calculate the rate per second on a duration Define a function called `ratePerSecondWithDuration` that takes the following parameters and the returned value: ```solidity function ratePerSecondWithDuration( address token, uint128 amount, uint40 duration ) internal view returns (UD21x18 ratePerSecond) ``` First, retrieve the token's decimals. Note that not all ERC-20 tokens use the 18-decimal standard. ```solidity uint8 decimals = IERC20Metadata(token).decimals(); // If the token has 18 decimals, we can simply divide the amount by the duration as it returns a 18 decimal // fixed-point number. if (decimals == 18) { return ud21x18(amount / duration); } // Calculate the scale factor from the token's decimals. uint128 scaleFactor = uint128(10 ** (18 - decimals)); // Multiply the amount by the scale factor and divide by the duration. ratePerSecond = ud21x18((scaleFactor * amount) / duration); } /// @notice This function calculates the rate per second based on a given amount of tokens and a specified range. /// @dev The rate per second is a 18-decimal fixed-point number and it is calculated as `amount / (end - start)`. /// @param token The address of the token. /// @param amount The amount of tokens, denoted in token's decimals. /// @param start The start timestamp. /// @param end The end timestamp. /// @return ratePerSecond The rate per second as a 18-decimal fixed-point number. function ratePerSecondForTimestamps( address token, uint128 amount, uint40 start, uint40 end ) internal view returns (UD21x18 ratePerSecond) { // Calculate the duration. uint40 duration = end - start; // Get the decimals of the token. uint8 decimals = IERC20Metadata(token).decimals(); if (decimals == 18) { return ud21x18(amount / duration); } // Calculate the scale factor from the token's decimals. uint128 scaleFactor = uint128(10 ** (18 - decimals)); // Multiply the amount by the scale factor and divide by the duration. ratePerSecond = ud21x18((scaleFactor * amount) / duration); } /// @notice This function calculates the amount streamed over a week for a given rate per second. /// @param ratePerSecond The rate per second as a 18-decimal fixed-point number. /// @return amountPerWeek The amount streamed over a week. function calculateAmountStreamedPerWeek(UD21x18 ratePerSecond) internal pure returns (uint128 amountPerWeek) { amountPerWeek = ratePerSecond.unwrap() * 1 weeks; } /// @notice This function calculates the amount streamed over a month for a given rate per second. /// @dev For simplicity, we have assumed that there are 30 days in a month. /// @param ratePerSecond The rate per second as a 18-decimal fixed-point number. /// @return amountPerMonth The amount streamed over a month. function calculateAmountStreamedPerMonth(UD21x18 ratePerSecond) internal pure returns (uint128 amountPerMonth) { amountPerMonth = ratePerSecond.unwrap() * 30 days; } /// @notice This function calculates the amount streamed over a year for a given rate per second. /// @dev For simplicity, we have assumed that there are 365 days in a year. /// @param ratePerSecond The rate per second as a fixed-point number. /// @return amountPerYear The amount streamed over a year. function calculateAmountStreamedPerYear(UD21x18 ratePerSecond) internal pure returns (uint128 amountPerYear) { amountPerYear = ratePerSecond.unwrap() * 365 days; } } ``` If the token uses 18 decimals, simply divide the amount by the duration: ```solidity if (decimals == 18) { return ud21x18(amount / duration); } ``` If the token has less than 18 decimals, calculate the scale factor from the token's decimals: ```solidity uint128 scaleFactor = uint128(10 ** (18 - decimals)); // Multiply the amount by the scale factor and divide by the duration. ratePerSecond = ud21x18((scaleFactor * amount) / duration); } /// @notice This function calculates the rate per second based on a given amount of tokens and a specified range. /// @dev The rate per second is a 18-decimal fixed-point number and it is calculated as `amount / (end - start)`. /// @param token The address of the token. /// @param amount The amount of tokens, denoted in token's decimals. /// @param start The start timestamp. /// @param end The end timestamp. /// @return ratePerSecond The rate per second as a 18-decimal fixed-point number. function ratePerSecondForTimestamps( address token, uint128 amount, uint40 start, uint40 end ) internal view returns (UD21x18 ratePerSecond) { // Calculate the duration. uint40 duration = end - start; // Get the decimals of the token. uint8 decimals = IERC20Metadata(token).decimals(); if (decimals == 18) { return ud21x18(amount / duration); } // Calculate the scale factor from the token's decimals. uint128 scaleFactor = uint128(10 ** (18 - decimals)); // Multiply the amount by the scale factor and divide by the duration. ratePerSecond = ud21x18((scaleFactor * amount) / duration); } /// @notice This function calculates the amount streamed over a week for a given rate per second. /// @param ratePerSecond The rate per second as a 18-decimal fixed-point number. /// @return amountPerWeek The amount streamed over a week. function calculateAmountStreamedPerWeek(UD21x18 ratePerSecond) internal pure returns (uint128 amountPerWeek) { amountPerWeek = ratePerSecond.unwrap() * 1 weeks; } /// @notice This function calculates the amount streamed over a month for a given rate per second. /// @dev For simplicity, we have assumed that there are 30 days in a month. /// @param ratePerSecond The rate per second as a 18-decimal fixed-point number. /// @return amountPerMonth The amount streamed over a month. function calculateAmountStreamedPerMonth(UD21x18 ratePerSecond) internal pure returns (uint128 amountPerMonth) { amountPerMonth = ratePerSecond.unwrap() * 30 days; } /// @notice This function calculates the amount streamed over a year for a given rate per second. /// @dev For simplicity, we have assumed that there are 365 days in a year. /// @param ratePerSecond The rate per second as a fixed-point number. /// @return amountPerYear The amount streamed over a year. function calculateAmountStreamedPerYear(UD21x18 ratePerSecond) internal pure returns (uint128 amountPerYear) { amountPerYear = ratePerSecond.unwrap() * 365 days; } } ``` Then, multiply the amount by the scale factor and divide it by the duration: ```solidity ratePerSecond = ud21x18((scaleFactor * amount) / duration); } /// @notice This function calculates the rate per second based on a given amount of tokens and a specified range. /// @dev The rate per second is a 18-decimal fixed-point number and it is calculated as `amount / (end - start)`. /// @param token The address of the token. /// @param amount The amount of tokens, denoted in token's decimals. /// @param start The start timestamp. /// @param end The end timestamp. /// @return ratePerSecond The rate per second as a 18-decimal fixed-point number. function ratePerSecondForTimestamps( address token, uint128 amount, uint40 start, uint40 end ) internal view returns (UD21x18 ratePerSecond) { // Calculate the duration. uint40 duration = end - start; // Get the decimals of the token. uint8 decimals = IERC20Metadata(token).decimals(); if (decimals == 18) { return ud21x18(amount / duration); } // Calculate the scale factor from the token's decimals. uint128 scaleFactor = uint128(10 ** (18 - decimals)); // Multiply the amount by the scale factor and divide by the duration. ratePerSecond = ud21x18((scaleFactor * amount) / duration); } /// @notice This function calculates the amount streamed over a week for a given rate per second. /// @param ratePerSecond The rate per second as a 18-decimal fixed-point number. /// @return amountPerWeek The amount streamed over a week. function calculateAmountStreamedPerWeek(UD21x18 ratePerSecond) internal pure returns (uint128 amountPerWeek) { amountPerWeek = ratePerSecond.unwrap() * 1 weeks; } /// @notice This function calculates the amount streamed over a month for a given rate per second. /// @dev For simplicity, we have assumed that there are 30 days in a month. /// @param ratePerSecond The rate per second as a 18-decimal fixed-point number. /// @return amountPerMonth The amount streamed over a month. function calculateAmountStreamedPerMonth(UD21x18 ratePerSecond) internal pure returns (uint128 amountPerMonth) { amountPerMonth = ratePerSecond.unwrap() * 30 days; } /// @notice This function calculates the amount streamed over a year for a given rate per second. /// @dev For simplicity, we have assumed that there are 365 days in a year. /// @param ratePerSecond The rate per second as a fixed-point number. /// @return amountPerYear The amount streamed over a year. function calculateAmountStreamedPerYear(UD21x18 ratePerSecond) internal pure returns (uint128 amountPerYear) { amountPerYear = ratePerSecond.unwrap() * 365 days; } } ``` ## Calculate the rate per second on timestamps Here, there are two time parameters, a start and an end time, instead of a duration. Let's define the function: ```solidity function ratePerSecondForTimestamps( address token, uint128 amount, uint40 start, uint40 end ) internal view returns (UD21x18 ratePerSecond) ``` The first step is to calculate the duration between the two timestamps: ```solidity uint40 duration = end - start; // Get the decimals of the token. uint8 decimals = IERC20Metadata(token).decimals(); if (decimals == 18) { return ud21x18(amount / duration); } // Calculate the scale factor from the token's decimals. uint128 scaleFactor = uint128(10 ** (18 - decimals)); // Multiply the amount by the scale factor and divide by the duration. ratePerSecond = ud21x18((scaleFactor * amount) / duration); } /// @notice This function calculates the amount streamed over a week for a given rate per second. /// @param ratePerSecond The rate per second as a 18-decimal fixed-point number. /// @return amountPerWeek The amount streamed over a week. function calculateAmountStreamedPerWeek(UD21x18 ratePerSecond) internal pure returns (uint128 amountPerWeek) { amountPerWeek = ratePerSecond.unwrap() * 1 weeks; } /// @notice This function calculates the amount streamed over a month for a given rate per second. /// @dev For simplicity, we have assumed that there are 30 days in a month. /// @param ratePerSecond The rate per second as a 18-decimal fixed-point number. /// @return amountPerMonth The amount streamed over a month. function calculateAmountStreamedPerMonth(UD21x18 ratePerSecond) internal pure returns (uint128 amountPerMonth) { amountPerMonth = ratePerSecond.unwrap() * 30 days; } /// @notice This function calculates the amount streamed over a year for a given rate per second. /// @dev For simplicity, we have assumed that there are 365 days in a year. /// @param ratePerSecond The rate per second as a fixed-point number. /// @return amountPerYear The amount streamed over a year. function calculateAmountStreamedPerYear(UD21x18 ratePerSecond) internal pure returns (uint128 amountPerYear) { amountPerYear = ratePerSecond.unwrap() * 365 days; } } ``` The remaining logic is identical to the duration-based calculation: ```solidity uint8 decimals = IERC20Metadata(token).decimals(); if (decimals == 18) { return ud21x18(amount / duration); } // Calculate the scale factor from the token's decimals. uint128 scaleFactor = uint128(10 ** (18 - decimals)); // Multiply the amount by the scale factor and divide by the duration. ratePerSecond = ud21x18((scaleFactor * amount) / duration); ``` ## Additional utilities To calculate earnings for specific durations from an existing stream, you can use the following functions: ```solidity function calculateAmountStreamedPerWeek(UD21x18 ratePerSecond) internal pure returns (uint128 amountPerWeek) { amountPerWeek = ratePerSecond.unwrap() * 1 weeks; } /// @notice This function calculates the amount streamed over a month for a given rate per second. /// @dev For simplicity, we have assumed that there are 30 days in a month. /// @param ratePerSecond The rate per second as a 18-decimal fixed-point number. /// @return amountPerMonth The amount streamed over a month. function calculateAmountStreamedPerMonth(UD21x18 ratePerSecond) internal pure returns (uint128 amountPerMonth) { amountPerMonth = ratePerSecond.unwrap() * 30 days; } /// @notice This function calculates the amount streamed over a year for a given rate per second. /// @dev For simplicity, we have assumed that there are 365 days in a year. /// @param ratePerSecond The rate per second as a fixed-point number. /// @return amountPerYear The amount streamed over a year. function calculateAmountStreamedPerYear(UD21x18 ratePerSecond) internal pure returns (uint128 amountPerYear) { amountPerYear = ratePerSecond.unwrap() * 365 days; } ``` ## Full code Below you can see the complete `FlowUtilities` library: ```solidity // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.22; import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import { ud21x18, UD21x18 } from "@prb/math/src/UD21x18.sol"; /// @dev A utility library to calculate rate per second and streamed amount based on a given time frame. library FlowUtilities { /// @notice This function calculates the rate per second based on a given amount of tokens and a specified duration. /// @dev The rate per second is a 18-decimal fixed-point number and it is calculated as `amount / duration`. /// @param token The address of the token. /// @param amount The amount of tokens, denoted in token's decimals. /// @param duration The duration in seconds user wishes to stream. /// @return ratePerSecond The rate per second as a 18-decimal fixed-point number. function ratePerSecondWithDuration( address token, uint128 amount, uint40 duration ) internal view returns (UD21x18 ratePerSecond) { // Get the decimals of the token. uint8 decimals = IERC20Metadata(token).decimals(); // If the token has 18 decimals, we can simply divide the amount by the duration as it returns a 18 decimal // fixed-point number. if (decimals == 18) { return ud21x18(amount / duration); } // Calculate the scale factor from the token's decimals. uint128 scaleFactor = uint128(10 ** (18 - decimals)); // Multiply the amount by the scale factor and divide by the duration. ratePerSecond = ud21x18((scaleFactor * amount) / duration); } /// @notice This function calculates the rate per second based on a given amount of tokens and a specified range. /// @dev The rate per second is a 18-decimal fixed-point number and it is calculated as `amount / (end - start)`. /// @param token The address of the token. /// @param amount The amount of tokens, denoted in token's decimals. /// @param start The start timestamp. /// @param end The end timestamp. /// @return ratePerSecond The rate per second as a 18-decimal fixed-point number. function ratePerSecondForTimestamps( address token, uint128 amount, uint40 start, uint40 end ) internal view returns (UD21x18 ratePerSecond) { // Calculate the duration. uint40 duration = end - start; // Get the decimals of the token. uint8 decimals = IERC20Metadata(token).decimals(); if (decimals == 18) { return ud21x18(amount / duration); } // Calculate the scale factor from the token's decimals. uint128 scaleFactor = uint128(10 ** (18 - decimals)); // Multiply the amount by the scale factor and divide by the duration. ratePerSecond = ud21x18((scaleFactor * amount) / duration); } /// @notice This function calculates the amount streamed over a week for a given rate per second. /// @param ratePerSecond The rate per second as a 18-decimal fixed-point number. /// @return amountPerWeek The amount streamed over a week. function calculateAmountStreamedPerWeek(UD21x18 ratePerSecond) internal pure returns (uint128 amountPerWeek) { amountPerWeek = ratePerSecond.unwrap() * 1 weeks; } /// @notice This function calculates the amount streamed over a month for a given rate per second. /// @dev For simplicity, we have assumed that there are 30 days in a month. /// @param ratePerSecond The rate per second as a 18-decimal fixed-point number. /// @return amountPerMonth The amount streamed over a month. function calculateAmountStreamedPerMonth(UD21x18 ratePerSecond) internal pure returns (uint128 amountPerMonth) { amountPerMonth = ratePerSecond.unwrap() * 30 days; } /// @notice This function calculates the amount streamed over a year for a given rate per second. /// @dev For simplicity, we have assumed that there are 365 days in a year. /// @param ratePerSecond The rate per second as a fixed-point number. /// @return amountPerYear The amount streamed over a year. function calculateAmountStreamedPerYear(UD21x18 ratePerSecond) internal pure returns (uint128 amountPerYear) { amountPerYear = ratePerSecond.unwrap() * 365 days; } } ``` --- ## Configure Your Local Environment Source: https://docs.sablier.com/guides/flow/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 Flow. We will use Foundry to install Flow 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 Flow imports, and a selection of sample contracts and tests. To install the template, simply execute the following commands: ```bash $ mkdir flow-integration-template $ cd flow-integration-template $ forge init --template sablier-labs/flow-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 Flow 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 Flow Node.js packages using Bun: ```bash $ bun add @sablier/flow ``` Bun will download the Flow 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 Flow 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 { UD21x18 } from "@prb/math/src/UD21x18.sol"; import { ISablierFlow } from "@sablier/flow/src/interfaces/ISablierFlow.sol"; /// @title FlowStreamCreator /// @dev This contract allows users to create Sablier flow streams. contract FlowStreamCreator { IERC20 public constant DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); ISablierFlow public constant FLOW = ISablierFlow(0x7a86d3e6894f9c5B5f25FFBDAaE658CFc7569623); /// @notice Creates a new Sablier flow stream without upfront deposit. function createFlowStream() external returns (uint256 streamId) { // Create the flow stream using the `create` function. streamId = FLOW.create({ sender: msg.sender, // The sender will be able to pause the stream or change rate per second recipient: address(0xCAFE), // The recipient of the streamed tokens ratePerSecond: UD21x18.wrap(1_157_407_407_407_407), // Equivalent to 100e18 DAI per day startTime: uint40(block.timestamp), // The stream starts now token: DAI, // The streaming token transferable: true // Whether the stream will be transferable or not }); } /// @notice Creates a new Sablier flow stream with some upfront deposit. /// @dev Before calling this function, the user must first approve this contract to spend the tokens from the user's /// address. function createFlowStreamAndDeposit(uint128 depositAmount) external returns (uint256 streamId) { // Transfer the provided amount of DAI tokens to this contract DAI.transferFrom(msg.sender, address(this), depositAmount); // Approve the Flow contract to spend DAI DAI.approve(address(FLOW), depositAmount); // Create the flow stream using the `createAndDeposit` function which would also deposit tokens into the stream. streamId = FLOW.createAndDeposit({ sender: msg.sender, // The sender will be able to pause the stream or change rate per second recipient: address(0xCAFE), // The recipient of the streamed tokens ratePerSecond: UD21x18.wrap(1_157_407_407_407_407), // Equivalent to 100e18 DAI per day startTime: uint40(block.timestamp), // The stream starts now token: DAI, // The streaming token transferable: true, // Whether the stream will be transferable or not amount: depositAmount // The amount to deposit into the stream }); } } ``` 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 Flow 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 { FlowStreamCreator } from "../src/FlowStreamCreator.sol"; contract FlowStreamCreatorTest is Test { // Test contracts FlowStreamCreator internal creator; address internal user; function setUp() public { // Fork Ethereum Mainnet at the latest block vm.createSelectFork({ urlOrAlias: "mainnet" }); // Deploy the stream creator contract creator = new FlowStreamCreator(); // Create a test user user = payable(makeAddr("User")); vm.deal({ account: user, newBalance: 1 ether }); // Mint some DAI tokens to the test user. deal({ token: address(creator.DAI()), to: user, give: 1337e18 }); // Make the test user the `msg.sender` in all following calls vm.startPrank({ msgSender: user }); } function test_CreateFlowStream() public { uint256 expectedStreamId = creator.FLOW().nextStreamId(); uint256 actualStreamId = creator.createFlowStream(); // Check that creating flow stream works by checking the stream ids assertEq(actualStreamId, expectedStreamId); // Check that stream is created with no initial balance assertEq(creator.FLOW().getBalance(actualStreamId), 0); } function test_CreateFlowStreamAndDeposit() public { // Approve the creator contract to pull DAI tokens from the test user creator.DAI().approve({ spender: address(creator), value: 1337e18 }); uint256 expectedStreamId = creator.FLOW().nextStreamId(); uint256 actualStreamId = creator.createFlowStreamAndDeposit({ depositAmount: 1337e18 }); // Check that creating flow stream works by checking the stream ids assertEq(actualStreamId, expectedStreamId); // Check that the stream is created with the deposit balance assertEq(creator.FLOW().getBalance(actualStreamId), 1337e18); } } ``` 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/FlowStreamCreator.t.sol:FlowStreamCreatorTest [PASS] test_CreateFlowStream() (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 Flow 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 Flow protocol. --- ## Managing a Stream Source: https://docs.sablier.com/guides/flow/examples/stream-management # Managing a Stream This section will guide you through the different functions of Flow and how to interact with them. Before diving in, please note the following: 1. We assume you are already familiar with [creating Flow streams](/guides/flow/examples/create-stream). 2. We also assume that the stream management contract is authorized to invoke each respective function. To learn more about access control in Flow, see the [Access Control](/reference/flow/access-control) guide. :::caution The code in this guide is not production-ready, and is implemented in a simplistic manner for the purpose of learning. ::: # Set up your 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/flow` and `@prb/math`: ```solidity import { ud21x18 } from "@prb/math/src/UD21x18.sol"; import { ISablierFlow } from "@sablier/flow/src/interfaces/ISablierFlow.sol"; ``` Declare a contract `FlowStreamManager` and add the Flow address as a constant: ```solidity // Mainnet address ISablierFlow public constant FLOW = ISablierFlow(0x844344Cd871B28221d725ecE9630E8bDE4E3a181); ``` 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, Flow addresses can be obtained from the [Flow Deployments](/guides/flow/deployments) page. ## Deposit Depositing into streams means adding tokens to the stream, which will then be distributed to the recipient based on the value of rate per second. :::info A deposit is also referred to as a top-up. ::: There are three deposit functions: 1. [`deposit`](/reference/flow/contracts/contract.SablierFlow#deposit): deposits an amount of tokens. 2. [`depositAndPause`](/reference/flow/contracts/contract.SablierFlow#depositandpause): deposits an amount of tokens and then pauses the stream. ```solidity function deposit(uint256 streamId) external { FLOW.deposit({ streamId: streamId, amount: 3.14159e18, sender: msg.sender, recipient: address(0xCAFE) }); } function depositAndPause(uint256 streamId) external { FLOW.depositAndPause(streamId, 3.14159e18); } ``` ## Withdraw The recipient of a stream can withdraw any amount, not exceeding the withdrawable amount. The recipient also has the option to withdraw the tokens to an alternate address of their choice. There are two withdrawal functions: 1. [`withdraw`](/reference/flow/contracts/contract.SablierFlow#withdraw): withdraws an amount of tokens not exceeding the withdrawable amount. 2. [`withdrawMax`](/reference/flow/contracts/contract.SablierFlow#withdrawmax): withdraws the entire withdrawable amount of tokens. :::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 = FLOW.calculateMinFeeWei(streamId); FLOW.withdraw{ value: fee }({ streamId: streamId, to: address(0xCAFE), amount: 2.71828e18 }); } function withdrawMax(uint256 streamId) external payable { uint256 fee = FLOW.calculateMinFeeWei(streamId); FLOW.withdrawMax{ value: fee }({ streamId: streamId, to: address(0xCAFE) }); } ``` ## Adjust Rate per Second Adjusting the rate per second means changing the amount of tokens that is streamed each second. ```solidity function adjustRatePerSecond(uint256 streamId) external { FLOW.adjustRatePerSecond({ streamId: streamId, newRatePerSecond: ud21x18(0.0001e18) }); } ``` ## Pause Pausing a stream means setting the rate per second to zero, which means no more streaming. ```solidity function pause(uint256 streamId) external { FLOW.pause(streamId); } ``` ## Restart There are two restart functions: 1. [`restart`](/reference/flow/contracts/contract.SablierFlow#restart): restarts a stream. 2. [`restartAndDeposit`](/reference/flow/contracts/contract.SablierFlow#restartanddeposit): restarts a stream followed by depositing an amount of tokens into it. ```solidity function restart(uint256 streamId) external { FLOW.restart({ streamId: streamId, ratePerSecond: ud21x18(0.0001e18) }); } function restartAndDeposit(uint256 streamId) external { FLOW.restartAndDeposit({ streamId: streamId, ratePerSecond: ud21x18(0.0001e18), amount: 2.71828e18 }); } ``` ## Refund There are three refund functions: 1. [`refund`](/reference/flow/contracts/contract.SablierFlow#refund): refunds an amount of tokens not exceeding the refundable amount. 2. [`refundAndPause`](/reference/flow/contracts/contract.SablierFlow#refundandpause): refunds an amount of tokens, and then pauses the stream. 3. [`refundMax`](/reference/flow/contracts/contract.SablierFlow#refundmax): refunds the entire refundable amount of tokens. ```solidity function refund(uint256 streamId) external { FLOW.refund({ streamId: streamId, amount: 1.61803e18 }); } function refundAndPause(uint256 streamId) external { FLOW.refundAndPause({ streamId: streamId, amount: 1.61803e18 }); } function refundMax(uint256 streamId) external { FLOW.refundMax(streamId); } ``` ## Void Voiding a stream means permanently stopping it from streaming any tokens. This is slightly different from pausing a stream because it also sets the stream's uncovered debt to zero. ```solidity function void(uint256 streamId) external { FLOW.void(streamId); } ``` ## Full code Below you can see the complete `FlowStreamManager` contract: ```solidity // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.22; import { ud21x18 } from "@prb/math/src/UD21x18.sol"; import { ISablierFlow } from "@sablier/flow/src/interfaces/ISablierFlow.sol"; contract FlowStreamManager { // Mainnet address ISablierFlow public constant FLOW = ISablierFlow(0x844344Cd871B28221d725ecE9630E8bDE4E3a181); function adjustRatePerSecond(uint256 streamId) external { FLOW.adjustRatePerSecond({ streamId: streamId, newRatePerSecond: ud21x18(0.0001e18) }); } function deposit(uint256 streamId) external { FLOW.deposit({ streamId: streamId, amount: 3.14159e18, sender: msg.sender, recipient: address(0xCAFE) }); } function depositAndPause(uint256 streamId) external { FLOW.depositAndPause(streamId, 3.14159e18); } function pause(uint256 streamId) external { FLOW.pause(streamId); } function refund(uint256 streamId) external { FLOW.refund({ streamId: streamId, amount: 1.61803e18 }); } function refundAndPause(uint256 streamId) external { FLOW.refundAndPause({ streamId: streamId, amount: 1.61803e18 }); } function refundMax(uint256 streamId) external { FLOW.refundMax(streamId); } function restart(uint256 streamId) external { FLOW.restart({ streamId: streamId, ratePerSecond: ud21x18(0.0001e18) }); } function restartAndDeposit(uint256 streamId) external { FLOW.restartAndDeposit({ streamId: streamId, ratePerSecond: ud21x18(0.0001e18), amount: 2.71828e18 }); } function void(uint256 streamId) external { FLOW.void(streamId); } function withdraw(uint256 streamId) external payable { uint256 fee = FLOW.calculateMinFeeWei(streamId); FLOW.withdraw{ value: fee }({ streamId: streamId, to: address(0xCAFE), amount: 2.71828e18 }); } function withdrawMax(uint256 streamId) external payable { uint256 fee = FLOW.calculateMinFeeWei(streamId); FLOW.withdrawMax{ value: fee }({ streamId: streamId, to: address(0xCAFE) }); } } ``` --- ## Gas Benchmarks Source: https://docs.sablier.com/guides/flow/gas-benchmarks # Gas Benchmarks The gas usage of the Flow 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). ::: ## SablierFlow With USDC as the streaming token. | Function | Stream Solvency | Gas Usage | | :-------------------- | :-------------- | :-------- | | \`adjustRatePerSecond\` | N/A | 44,520 | | \`create\` | N/A | 127,027 | | \`deposit\` | N/A | 37,028 | | \`pause\` | N/A | 8312 | | \`refund\` | Solvent | 24,767 | | \`refundMax\` | Solvent | 25,802 | | \`restart\` | N/A | 7536 | | \`void\` | Solvent | 10,100 | | \`void\` | Insolvent | 37,601 | | \`withdraw\` | Insolvent | 69,289 | | \`withdraw\` | Solvent | 47,757 | | \`withdrawMax\` | Solvent | 61,693 | --- ## Sablier Flow Source: https://docs.sablier.com/guides/flow/overview # Sablier Flow Welcome to the Sablier Flow documentation. This section contains detailed guides and technical references for the Flow protocol. 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. If you want to setup your local environment, head over to [the guide](/guides/flow/examples/local-environment). # Reference For a deeper dive into the protocol specifications, read through the [technical reference](/reference/flow/diagrams). # Resources - [Source Code](https://github.com/sablier-labs/evm-monorepo/blob/main/flow) - [Integration Templates](https://github.com/sablier-labs/flow-integration-template) - [Examples](https://github.com/sablier-labs/evm-monorepo/tree/main/misc/examples/flow/) - [Foundry Book](https://book.getfoundry.sh/) --- ## Flow v1.0 Source: https://docs.sablier.com/guides/flow/previous-deployments/v1.0 # Flow v1.0 This section contains the deployment addresses for the v1.0 release of [@sablier/flow@1.0.0](https://npmjs.com/package/@sablier/flow/v/1.0.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 Flow protocol. See the latest version [here](/guides/flow/deployments). ::: ## Mainnets ### Ethereum βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x2d9221a63e12aa796619cb381ec4a71b201281f5`](https://etherscan.io/address/0x2d9221a63e12aa796619cb381ec4a71b201281f5) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | | FlowNFTDescriptor | [`0xb69b27073fa0366cddf432f5976c34c9baf7eae6`](https://etherscan.io/address/0xb69b27073fa0366cddf432f5976c34c9baf7eae6) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | ### Abstract βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x001F1408515Ccd5C1A19A682455ed4eFa39DadD6`](https://abscan.org/address/0x001F1408515Ccd5C1A19A682455ed4eFa39DadD6) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | | FlowNFTDescriptor | [`0x20C9A3E27322Fc2b21Ced430D1B2e12d90804db6`](https://abscan.org/address/0x20C9A3E27322Fc2b21Ced430D1B2e12d90804db6) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | ### Arbitrum βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x18a12a7035aa56240bcd236bc019aa245dcc015a`](https://arbiscan.io/address/0x18a12a7035aa56240bcd236bc019aa245dcc015a) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | | FlowNFTDescriptor | [`0x900ebdb9ecfb19f9463d68d1fd6e5fa7ab9c6897`](https://arbiscan.io/address/0x900ebdb9ecfb19f9463d68d1fd6e5fa7ab9c6897) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | ### Avalanche βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x8c172e42c06302e3cfe555dc4d6b71a756ee186b`](https://snowscan.xyz/address/0x8c172e42c06302e3cfe555dc4d6b71a756ee186b) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | | FlowNFTDescriptor | [`0x82ea83ab59b106c125168492cd468c322bd0d195`](https://snowscan.xyz/address/0x82ea83ab59b106c125168492cd468c322bd0d195) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | ### Base βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x1a9adc0e2114c8407cc31669baafeee031d15dd2`](https://basescan.org/address/0x1a9adc0e2114c8407cc31669baafeee031d15dd2) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | | FlowNFTDescriptor | [`0x8e64f389a4697e004647162ec6ea0a7779d5d899`](https://basescan.org/address/0x8e64f389a4697e004647162ec6ea0a7779d5d899) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | ### Blast βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xfdac2799644141856e20e021ac06f231cafc731f`](https://blastscan.io/address/0xfdac2799644141856e20e021ac06f231cafc731f) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | | FlowNFTDescriptor | [`0xb40624ce2af67227529f713bac46e2b7064b7b92`](https://blastscan.io/address/0xb40624ce2af67227529f713bac46e2b7064b7b92) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | ### BNB Chain βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xfce01f79247cf450062545e7155d7bd568551d0e`](https://bscscan.com/address/0xfce01f79247cf450062545e7155d7bd568551d0e) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | | FlowNFTDescriptor | [`0xbc6fdd3f59900b9fcd445f8df159e2e794f098ec`](https://bscscan.com/address/0xbc6fdd3f59900b9fcd445f8df159e2e794f098ec) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | ### Chiliz βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x9EfC8663cAB0e2d97ad17C9fbfc8392445517E94`](https://chiliscan.com/address/0x9EfC8663cAB0e2d97ad17C9fbfc8392445517E94) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | | FlowNFTDescriptor | [`0x3D664B2Da905DDD0Db931982FD9a759ea950D6e1`](https://chiliscan.com/address/0x3D664B2Da905DDD0Db931982FD9a759ea950D6e1) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | ### Core Dao ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x447c6ea25540611541ff98fc677ca865f4e92450`](https://scan.coredao.org/address/0x447c6ea25540611541ff98fc677ca865f4e92450) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | | FlowNFTDescriptor | [`0xbfaa055ecfe503e1323dc9fc26b7d3aa3bf54364`](https://scan.coredao.org/address/0xbfaa055ecfe503e1323dc9fc26b7d3aa3bf54364) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | ### Gnosis βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x5515f774a4db42820802333ba575f68a6e85bd13`](https://gnosisscan.io/address/0x5515f774a4db42820802333ba575f68a6e85bd13) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | | FlowNFTDescriptor | [`0xc07c1128c19c2bf303b68ae061eff5293927630e`](https://gnosisscan.io/address/0xc07c1128c19c2bf303b68ae061eff5293927630e) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | ### Lightlink βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x46fa0164c5af9382d330e5a245a2ca8a18398950`](https://phoenix.lightlink.io/address/0x46fa0164c5af9382d330e5a245a2ca8a18398950) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | | FlowNFTDescriptor | [`0xa2a48b83b6c96e1536336df9ead024d557a97a23`](https://phoenix.lightlink.io/address/0xa2a48b83b6c96e1536336df9ead024d557a97a23) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | ### Linea Mainnet βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x949bFa08f1632432A2656a9dB17CA34d54Da8296`](https://lineascan.build/address/0x949bFa08f1632432A2656a9dB17CA34d54Da8296) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | | FlowNFTDescriptor | [`0xF430f0d2f798c42fDFAc35b5e32BD4f63Bf51130`](https://lineascan.build/address/0xF430f0d2f798c42fDFAc35b5e32BD4f63Bf51130) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | ### Mode βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x75970dde488431fc4961494569def3269f20d6b3`](https://modescan.io/address/0x75970dde488431fc4961494569def3269f20d6b3) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | | FlowNFTDescriptor | [`0x46fa0164c5af9382d330e5a245a2ca8a18398950`](https://modescan.io/address/0x46fa0164c5af9382d330e5a245a2ca8a18398950) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | ### Morph βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xfe6972d0ae797fae343e5a58d0c7d8513937f092`](https://explorer.morphl2.io/address/0xfe6972d0ae797fae343e5a58d0c7d8513937f092) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | | FlowNFTDescriptor | [`0xab281bbc2bc34a1f202ddff17ffd1c00edf73f3a`](https://explorer.morphl2.io/address/0xab281bbc2bc34a1f202ddff17ffd1c00edf73f3a) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | ### OP Mainnet βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x906356e4e6410ea0a97dbc5b071cf394ab0dcd69`](https://optimistic.etherscan.io/address/0x906356e4e6410ea0a97dbc5b071cf394ab0dcd69) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | | FlowNFTDescriptor | [`0xe674fb603d6f72b88bf297c1ba69f57b588a8f6d`](https://optimistic.etherscan.io/address/0xe674fb603d6f72b88bf297c1ba69f57b588a8f6d) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | ### Polygon βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xcf2d812d5aad4e6fec3b05850ff056b21159d496`](https://polygonscan.com/address/0xcf2d812d5aad4e6fec3b05850ff056b21159d496) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | | FlowNFTDescriptor | [`0x011277c87158e52cfbd8a1dd4a29118d602dda3a`](https://polygonscan.com/address/0x011277c87158e52cfbd8a1dd4a29118d602dda3a) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | ### Scroll βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x66826f53bffeaab71adc7fe1a77e86f8268848d8`](https://scrollscan.com/address/0x66826f53bffeaab71adc7fe1a77e86f8268848d8) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | | FlowNFTDescriptor | [`0x57fd892b3dc20eadb83cd8fb0240a87960046daa`](https://scrollscan.com/address/0x57fd892b3dc20eadb83cd8fb0240a87960046daa) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | ### Superseed βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x4f5f9b3fb57bba43aaf90e3f71d8f8f384e88e20`](https://explorer.superseed.xyz/address/0x4f5f9b3fb57bba43aaf90e3f71d8f8f384e88e20) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | | FlowNFTDescriptor | [`0xac2c36347869d8d779f7872c6202de3efd6ef2bb`](https://explorer.superseed.xyz/address/0xac2c36347869d8d779f7872c6202de3efd6ef2bb) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | ### Taiko ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x3d303e4c61285f87da9f61aaadc8c89b7d55dfa2`](https://taikoscan.io/address/0x3d303e4c61285f87da9f61aaadc8c89b7d55dfa2) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | | FlowNFTDescriptor | [`0xe790b6178612eeba6faeec16a2e1354c872f8bde`](https://taikoscan.io/address/0xe790b6178612eeba6faeec16a2e1354c872f8bde) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | ### Tangle βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xCff4a803b0Bf55dD1BE38Fb96088478F3D2eeCF2`](https://explorer.tangle.tools/address/0xCff4a803b0Bf55dD1BE38Fb96088478F3D2eeCF2) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | | FlowNFTDescriptor | [`0x2De92156000269fa2fde7544F10f01E8cBC80fFa`](https://explorer.tangle.tools/address/0x2De92156000269fa2fde7544F10f01E8cBC80fFa) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | ### ZKsync Era βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x015899a075B7C181e357Cd0ed000683DBB4F1FcE`](https://explorer.zksync.io/address/0x015899a075B7C181e357Cd0ed000683DBB4F1FcE) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | | FlowNFTDescriptor | [`0x01C40608f2822816cF25a0a911c1df330487ba62`](https://explorer.zksync.io/address/0x01C40608f2822816cF25a0a911c1df330487ba62) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | ## Testnets ### Arbitrum Sepolia ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x781b3b2527f2a0a1e6b429161f2717a8a28b9f46`](https://sepolia.arbiscan.io/address/0x781b3b2527f2a0a1e6b429161f2717a8a28b9f46) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | | FlowNFTDescriptor | [`0x9a08e6ae67c28002ee2c7cff9badecd33ae2151c`](https://sepolia.arbiscan.io/address/0x9a08e6ae67c28002ee2c7cff9badecd33ae2151c) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | ### Base Sepolia βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xd5f78708d83ac2bc8734a8cdf2d112c1bb3b62a2`](https://sepolia.basescan.org/address/0xd5f78708d83ac2bc8734a8cdf2d112c1bb3b62a2) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | | FlowNFTDescriptor | [`0x168ad0b246f604bc70bef87ecde585c3f1d49617`](https://sepolia.basescan.org/address/0x168ad0b246f604bc70bef87ecde585c3f1d49617) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | ### Linea Sepolia ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xb0255ed1ee5c01dfe865c1b21bbf56a80f9ae739`](https://sepolia.lineascan.build/address/0xb0255ed1ee5c01dfe865c1b21bbf56a80f9ae739) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | | FlowNFTDescriptor | [`0xcd8871a22640c57ba36984fb57e9c794f5df7f40`](https://sepolia.lineascan.build/address/0xcd8871a22640c57ba36984fb57e9c794f5df7f40) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | ### OP Sepolia ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x417db0f2bd020fc4d6bccea6b2bb6be0c541862e`](https://optimism-sepolia.blockscout.com/address/0x417db0f2bd020fc4d6bccea6b2bb6be0c541862e) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | | FlowNFTDescriptor | [`0x28401987a23ed9b8926b07f3b6855222a70c2128`](https://optimism-sepolia.blockscout.com/address/0x28401987a23ed9b8926b07f3b6855222a70c2128) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | ### Sepolia βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x5ae8c13f6ae094887322012425b34b0919097d8a`](https://sepolia.etherscan.io/address/0x5ae8c13f6ae094887322012425b34b0919097d8a) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | | FlowNFTDescriptor | [`0xbc4da2fbdfe5c5eaa11bd0e282201e2abf40b1ee`](https://sepolia.etherscan.io/address/0xbc4da2fbdfe5c5eaa11bd0e282201e2abf40b1ee) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | ### Superseed Sepolia ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x905756b52efeaf75f6b1bb1bb0fc35eea15ae260`](https://sepolia-explorer.superseed.xyz/address/0x905756b52efeaf75f6b1bb1bb0fc35eea15ae260) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | | FlowNFTDescriptor | [`0xc43fb9fe4477d8e8bf68b9fd3a0163a4cffcbb31`](https://sepolia-explorer.superseed.xyz/address/0xc43fb9fe4477d8e8bf68b9fd3a0163a4cffcbb31) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | ### ZKsync Sepolia Testnet ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x8e70296F8972eBE94d885B1Caf94Da4836976140`](https://sepolia.explorer.zksync.io/address/0x8e70296F8972eBE94d885B1Caf94Da4836976140) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | | FlowNFTDescriptor | [`0x900277DBB45a04eB79028b3A44c650Ac81Ca86c4`](https://sepolia.explorer.zksync.io/address/0x900277DBB45a04eB79028b3A44c650Ac81Ca86c4) | [`flow-v1.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.0) | --- ## Flow v1.1 Source: https://docs.sablier.com/guides/flow/previous-deployments/v1.1 # Flow v1.1 This section contains the deployment addresses for the v1.1 release of [@sablier/flow@1.1.1](https://npmjs.com/package/@sablier/flow/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 Flow protocol. See the latest version [here](/guides/flow/deployments). ::: ## Mainnets ### Ethereum βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x3DF2AAEdE81D2F6b261F79047517713B8E844E04`](https://etherscan.io/address/0x3DF2AAEdE81D2F6b261F79047517713B8E844E04) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0x24bE13897eE1F83367661B6bA616a72523fC55C9`](https://etherscan.io/address/0x24bE13897eE1F83367661B6bA616a72523fC55C9) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ### Abstract βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x555B0766f494c641bb522086da4E728AC08c1420`](https://abscan.org/address/0x555B0766f494c641bb522086da4E728AC08c1420) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0x6CefdBc5Ba80937235F012c83d6aA83F1200d6cC`](https://abscan.org/address/0x6CefdBc5Ba80937235F012c83d6aA83F1200d6cC) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ### Arbitrum βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x87CF87ec5de33DeB4a88787065373563Ba85Ee72`](https://arbiscan.io/address/0x87CF87ec5de33DeB4a88787065373563Ba85Ee72) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0x5F23eF12A7e861CB92c24B4314Af2A5F363CDD4F`](https://arbiscan.io/address/0x5F23eF12A7e861CB92c24B4314Af2A5F363CDD4F) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ### Avalanche βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xac7CB985d4022A5Ebd4a385374ac3d3B487b3C63`](https://snowscan.xyz/address/0xac7CB985d4022A5Ebd4a385374ac3d3B487b3C63) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0xb09b714B0feC83675E09fc997B7D532cF6620326`](https://snowscan.xyz/address/0xb09b714B0feC83675E09fc997B7D532cF6620326) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ### Base βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x6FE93c7f6cd1DC394e71591E3c42715Be7180A6A`](https://basescan.org/address/0x6FE93c7f6cd1DC394e71591E3c42715Be7180A6A) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0x5b5e742305Be3A484EacCB124C83456463c24E6a`](https://basescan.org/address/0x5b5e742305Be3A484EacCB124C83456463c24E6a) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ### Berachain βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xA031544946ED769377128fBD961c9d621c4b4179`](https://berascan.com/address/0xA031544946ED769377128fBD961c9d621c4b4179) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0x581250eE4311F7Dc1afCF965cF8024004B423e9E`](https://berascan.com/address/0x581250eE4311F7Dc1afCF965cF8024004B423e9E) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ### Blast βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x16b50eb5eAeF0366f1A4da594e2A8c8943A297e0`](https://blastscan.io/address/0x16b50eb5eAeF0366f1A4da594e2A8c8943A297e0) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0x92f1dB592C771D9Ec7708abFEe79771AbC1b4fAd`](https://blastscan.io/address/0x92f1dB592C771D9Ec7708abFEe79771AbC1b4fAd) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ### BNB Chain βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x4C4610aF3f3861EC99b6F6F8066C03E4C3a0E023`](https://bscscan.com/address/0x4C4610aF3f3861EC99b6F6F8066C03E4C3a0E023) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0xAE557c04B46d47Ecac24edA63F22cabB4571Da61`](https://bscscan.com/address/0xAE557c04B46d47Ecac24edA63F22cabB4571Da61) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ### Chiliz βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x28eAB88ee8a951F78e1028557D0C3fD97af61A33`](https://chiliscan.com/address/0x28eAB88ee8a951F78e1028557D0C3fD97af61A33) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0xC7fd18CA19938d559dC45aDE362a850015CF0bd8`](https://chiliscan.com/address/0xC7fd18CA19938d559dC45aDE362a850015CF0bd8) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ### Core Dao ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xa0aE7F1bE0DB024Beda05c80722413EDDe7231Bd`](https://scan.coredao.org/address/0xa0aE7F1bE0DB024Beda05c80722413EDDe7231Bd) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0x7293F2D4A4e676EF67C085E92277AdF560AECb88`](https://scan.coredao.org/address/0x7293F2D4A4e676EF67C085E92277AdF560AECb88) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ### Gnosis βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x34Bc0C2BF1F2DA51c65cd821bA4133aFCacdb8f5`](https://gnosisscan.io/address/0x34Bc0C2BF1F2DA51c65cd821bA4133aFCacdb8f5) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0x5A47FC8732d399a2f3845c4FC91aB91bb97da31F`](https://gnosisscan.io/address/0x5A47FC8732d399a2f3845c4FC91aB91bb97da31F) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ### HyperEVM βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x556d859DFEB58Ed3fa092B6526b09da6b74113e2`](https://hyperevmscan.io/address/0x556d859DFEB58Ed3fa092B6526b09da6b74113e2) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0x81Cc8C4B57B9A60a56330d087D6854A8E17Dfc7A`](https://hyperevmscan.io/address/0x81Cc8C4B57B9A60a56330d087D6854A8E17Dfc7A) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ### Lightlink βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x89d964E0b508234bCfDc7a32aE0aA0356f422B70`](https://phoenix.lightlink.io/address/0x89d964E0b508234bCfDc7a32aE0aA0356f422B70) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0xc58E948Cb0a010105467C92856bcd4842B759fb1`](https://phoenix.lightlink.io/address/0xc58E948Cb0a010105467C92856bcd4842B759fb1) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ### Linea Mainnet βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xEFc6e4C7DC5faA0CfBFEbB5e04eA7Cd47f64012f`](https://lineascan.build/address/0xEFc6e4C7DC5faA0CfBFEbB5e04eA7Cd47f64012f) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0x294D7fceBa43C4507771707CeBBB7b6d81d0BFdE`](https://lineascan.build/address/0x294D7fceBa43C4507771707CeBBB7b6d81d0BFdE) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ### Mode βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xc968E8eEFe19BD6De8868df40D9740Be127a172a`](https://modescan.io/address/0xc968E8eEFe19BD6De8868df40D9740Be127a172a) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0xD9E2822a33606741BeDbA31614E68A745e430102`](https://modescan.io/address/0xD9E2822a33606741BeDbA31614E68A745e430102) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ### Morph βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xf31c8E7D9a0Bd310a9d5Fb317ba67BB1f0101c6D`](https://explorer.morphl2.io/address/0xf31c8E7D9a0Bd310a9d5Fb317ba67BB1f0101c6D) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0x1dd4dcE2BB742908b4062E583d9c035973413A3F`](https://explorer.morphl2.io/address/0x1dd4dcE2BB742908b4062E583d9c035973413A3F) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ### OP Mainnet βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xC5612feA2D370127ac67048115bd6b1dF7b7F7C0`](https://optimistic.etherscan.io/address/0xC5612feA2D370127ac67048115bd6b1dF7b7F7C0) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0x7AD245b74bBC1B71Da1713D53238931F791b90A3`](https://optimistic.etherscan.io/address/0x7AD245b74bBC1B71Da1713D53238931F791b90A3) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ### Polygon βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x3e5c4130Ea7cfbD364FA5f170289d697865cA94b`](https://polygonscan.com/address/0x3e5c4130Ea7cfbD364FA5f170289d697865cA94b) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0x87B836a9e26673feB3E409A0da2EAf99C79f26C3`](https://polygonscan.com/address/0x87B836a9e26673feB3E409A0da2EAf99C79f26C3) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ### Scroll βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xC4F104cE12cb12484Ff67cF0C4Bd0561F0014ec2`](https://scrollscan.com/address/0xC4F104cE12cb12484Ff67cF0C4Bd0561F0014ec2) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0x797Fe78c41d9cbE81BBEA2f420101be5e47d2aFf`](https://scrollscan.com/address/0x797Fe78c41d9cbE81BBEA2f420101be5e47d2aFf) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ### Sei Network ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xdEF70082ebda4944A55311624900E42A720b4Ec9`](https://seiscan.io/address/0xdEF70082ebda4944A55311624900E42A720b4Ec9) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0xF3D18b06c87735a58DAb3baC45af058b3772fD54`](https://seiscan.io/address/0xF3D18b06c87735a58DAb3baC45af058b3772fD54) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ### Sonic βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x63815da47C97063cc24b28D0b6F59234f71D5c96`](https://sonicscan.org/address/0x63815da47C97063cc24b28D0b6F59234f71D5c96) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0xAab30e5CB903f67F109aFc7102ac8ED803681EA5`](https://sonicscan.org/address/0xAab30e5CB903f67F109aFc7102ac8ED803681EA5) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ### Sophon ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x20C9A3E27322Fc2b21Ced430D1B2e12d90804db6`](https://sophscan.xyz/address/0x20C9A3E27322Fc2b21Ced430D1B2e12d90804db6) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0x2F1eB117A87217E8bE9AA96795F69c9e380686Db`](https://sophscan.xyz/address/0x2F1eB117A87217E8bE9AA96795F69c9e380686Db) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ### Superseed βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x40E75bb2F2aA3507D3a332872829c71be19eF623`](https://explorer.superseed.xyz/address/0x40E75bb2F2aA3507D3a332872829c71be19eF623) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0xd932fDA016eE9d9F70f745544b4F56715b1E723b`](https://explorer.superseed.xyz/address/0xd932fDA016eE9d9F70f745544b4F56715b1E723b) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ### Taiko ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x9d4bc7f013cCddAE1658dc28F981C2D424d7F0Dd`](https://taikoscan.io/address/0x9d4bc7f013cCddAE1658dc28F981C2D424d7F0Dd) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0x80Bde7C505eFE9960b673567CB25Cd8af85552BE`](https://taikoscan.io/address/0x80Bde7C505eFE9960b673567CB25Cd8af85552BE) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ### Tangle βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xcb099EfC90e88690e287259410B9AE63e1658CC6`](https://explorer.tangle.tools/address/0xcb099EfC90e88690e287259410B9AE63e1658CC6) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0xDf578C2c70A86945999c65961417057363530a1c`](https://explorer.tangle.tools/address/0xDf578C2c70A86945999c65961417057363530a1c) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ### Unichain βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x9797B40340be0bFc9EC0dBb8712627Bcdd17E771`](https://uniscan.xyz/address/0x9797B40340be0bFc9EC0dBb8712627Bcdd17E771) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0x89824A7e48dcf6B7AE9DeE6E566f62A5aDF037F2`](https://uniscan.xyz/address/0x89824A7e48dcf6B7AE9DeE6E566f62A5aDF037F2) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ### XDC βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xD6482334242862951dA3E730F818c3f6E3f45A30`](https://xdcscan.com/address/0xD6482334242862951dA3E730F818c3f6E3f45A30) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0x9D3F0122b260D2218ecf681c416495882003deDd`](https://xdcscan.com/address/0x9D3F0122b260D2218ecf681c416495882003deDd) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ### ZKsync Era βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xE3747379bF7282e0ab5389A63eA053a5256042df`](https://explorer.zksync.io/address/0xE3747379bF7282e0ab5389A63eA053a5256042df) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0x423C1b454250992Ede8516D36DE456F609714B53`](https://explorer.zksync.io/address/0x423C1b454250992Ede8516D36DE456F609714B53) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ## Testnets ### Arbitrum Sepolia ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xF9cbfFAe10010475A2800a5eFC11f4D4780cA48d`](https://sepolia.arbiscan.io/address/0xF9cbfFAe10010475A2800a5eFC11f4D4780cA48d) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0x3E64A31C3974b6ae9f09a8fbc784519bF551e795`](https://sepolia.arbiscan.io/address/0x3E64A31C3974b6ae9f09a8fbc784519bF551e795) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ### Base Sepolia βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xFB6B72a5988A7701a9090C56936269241693a9CC`](https://sepolia.basescan.org/address/0xFB6B72a5988A7701a9090C56936269241693a9CC) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0xcb5591F6d0e0fFC03037ef7b006D1361C6D33D25`](https://sepolia.basescan.org/address/0xcb5591F6d0e0fFC03037ef7b006D1361C6D33D25) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ### Linea Sepolia ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x3D0804610dE1b8DC19B1DDf90C26d5a51ab2B6b6`](https://sepolia.lineascan.build/address/0x3D0804610dE1b8DC19B1DDf90C26d5a51ab2B6b6) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0xbd17DFd74078dB49f12101Ca929b5153E924e9C7`](https://sepolia.lineascan.build/address/0xbd17DFd74078dB49f12101Ca929b5153E924e9C7) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ### OP Sepolia ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x77873085a88189c8B82B3a01BcbC294108D02805`](https://optimism-sepolia.blockscout.com/address/0x77873085a88189c8B82B3a01BcbC294108D02805) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0x4739327acfb56E90177d44Cb0845e759276BCA88`](https://optimism-sepolia.blockscout.com/address/0x4739327acfb56E90177d44Cb0845e759276BCA88) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ### Sepolia βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x93FE8f86e881a23e5A2FEB4B160514Fd332576A6`](https://sepolia.etherscan.io/address/0x93FE8f86e881a23e5A2FEB4B160514Fd332576A6) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0xc9dBf2D207D178875b698e5f7493ce2d8BA88994`](https://sepolia.etherscan.io/address/0xc9dBf2D207D178875b698e5f7493ce2d8BA88994) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ### Superseed Sepolia ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x905756b52efeaf75f6b1bb1bb0fc35eea15ae260`](https://sepolia-explorer.superseed.xyz/address/0x905756b52efeaf75f6b1bb1bb0fc35eea15ae260) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0xc43fb9fe4477d8e8bf68b9fd3a0163a4cffcbb31`](https://sepolia-explorer.superseed.xyz/address/0xc43fb9fe4477d8e8bf68b9fd3a0163a4cffcbb31) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | ### ZKsync Sepolia Testnet ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xf499b35e2e932a05ecD6115Aa4DcCeb29aF55E3D`](https://sepolia.explorer.zksync.io/address/0xf499b35e2e932a05ecD6115Aa4DcCeb29aF55E3D) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | | FlowNFTDescriptor | [`0xb3eCE4451825f865479813d42f74a080D2CcC928`](https://sepolia.explorer.zksync.io/address/0xb3eCE4451825f865479813d42f74a080D2CcC928) | [`flow-v1.1`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v1.1) | --- ## Flow v2.0 Source: https://docs.sablier.com/guides/flow/previous-deployments/v2.0 # Flow v2.0 This section contains the deployment addresses for the v2.0 release of [@sablier/flow@2.0.1](https://npmjs.com/package/@sablier/flow/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 Flow protocol. See the latest version [here](/guides/flow/deployments). ::: ## Mainnets ### Ethereum βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x7a86d3e6894f9c5b5f25ffbdaae658cfc7569623`](https://etherscan.io/address/0x7a86d3e6894f9c5b5f25ffbdaae658cfc7569623) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | | FlowNFTDescriptor | [`0x24bE13897eE1F83367661B6bA616a72523fC55C9`](https://etherscan.io/address/0x24bE13897eE1F83367661B6bA616a72523fC55C9) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | ### Abstract βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xc415425e56cc6c42b87bacffb276db2292cc1e50`](https://abscan.org/address/0xc415425e56cc6c42b87bacffb276db2292cc1e50) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | | FlowNFTDescriptor | [`0x6CefdBc5Ba80937235F012c83d6aA83F1200d6cC`](https://abscan.org/address/0x6CefdBc5Ba80937235F012c83d6aA83F1200d6cC) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | ### Arbitrum βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xf0f6477422a346378458f73cf02f05a7492e0c25`](https://arbiscan.io/address/0xf0f6477422a346378458f73cf02f05a7492e0c25) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | | FlowNFTDescriptor | [`0x5F23eF12A7e861CB92c24B4314Af2A5F363CDD4F`](https://arbiscan.io/address/0x5F23eF12A7e861CB92c24B4314Af2A5F363CDD4F) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | ### Avalanche βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x64dc318ba879eca8222e963d319728f211c600c7`](https://snowscan.xyz/address/0x64dc318ba879eca8222e963d319728f211c600c7) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | | FlowNFTDescriptor | [`0xb09b714B0feC83675E09fc997B7D532cF6620326`](https://snowscan.xyz/address/0xb09b714B0feC83675E09fc997B7D532cF6620326) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | ### Base βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x8551208f75375abfaee1fbe0a69e390a94000ec2`](https://basescan.org/address/0x8551208f75375abfaee1fbe0a69e390a94000ec2) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | | FlowNFTDescriptor | [`0x5b5e742305Be3A484EacCB124C83456463c24E6a`](https://basescan.org/address/0x5b5e742305Be3A484EacCB124C83456463c24E6a) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | ### Berachain βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xb89cc68b2ef376ca1b9645f109f7a490b180cf1b`](https://berascan.com/address/0xb89cc68b2ef376ca1b9645f109f7a490b180cf1b) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | | FlowNFTDescriptor | [`0x581250eE4311F7Dc1afCF965cF8024004B423e9E`](https://berascan.com/address/0x581250eE4311F7Dc1afCF965cF8024004B423e9E) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | ### Blast βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x13ce2ca4602d5d1dd323014cd5a4e8414d310a06`](https://blastscan.io/address/0x13ce2ca4602d5d1dd323014cd5a4e8414d310a06) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | | FlowNFTDescriptor | [`0x92f1dB592C771D9Ec7708abFEe79771AbC1b4fAd`](https://blastscan.io/address/0x92f1dB592C771D9Ec7708abFEe79771AbC1b4fAd) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | ### BNB Chain βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x5505c2397B0BeBEEE64919F21Df84F83C008C51b`](https://bscscan.com/address/0x5505c2397B0BeBEEE64919F21Df84F83C008C51b) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | | FlowNFTDescriptor | [`0xAE557c04B46d47Ecac24edA63F22cabB4571Da61`](https://bscscan.com/address/0xAE557c04B46d47Ecac24edA63F22cabB4571Da61) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | ### Chiliz βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x21797da50e180d24d6a68e8be6f8daca1c06f0ee`](https://chiliscan.com/address/0x21797da50e180d24d6a68e8be6f8daca1c06f0ee) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | | FlowNFTDescriptor | [`0xC7fd18CA19938d559dC45aDE362a850015CF0bd8`](https://chiliscan.com/address/0xC7fd18CA19938d559dC45aDE362a850015CF0bd8) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | ### Core Dao ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x4cb7fb49e4b646b472a5609804004722b3b94f93`](https://scan.coredao.org/address/0x4cb7fb49e4b646b472a5609804004722b3b94f93) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | | FlowNFTDescriptor | [`0x7293F2D4A4e676EF67C085E92277AdF560AECb88`](https://scan.coredao.org/address/0x7293F2D4A4e676EF67C085E92277AdF560AECb88) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | ### Denergy βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xB2Fc49d89B72cD8Aadd7f07D602CF005D5A017Ea`](https://explorer.denergychain.com/address/0xB2Fc49d89B72cD8Aadd7f07D602CF005D5A017Ea) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | | FlowNFTDescriptor | [`0x8C4bCE3A96CA4E1275B11FDcC38d00D142af2C3f`](https://explorer.denergychain.com/address/0x8C4bCE3A96CA4E1275B11FDcC38d00D142af2C3f) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | ### Gnosis βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xcdd3eb5283e4a675f16ba83e9d8c28c871a550a2`](https://gnosisscan.io/address/0xcdd3eb5283e4a675f16ba83e9d8c28c871a550a2) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | | FlowNFTDescriptor | [`0x5A47FC8732d399a2f3845c4FC91aB91bb97da31F`](https://gnosisscan.io/address/0x5A47FC8732d399a2f3845c4FC91aB91bb97da31F) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | ### HyperEVM βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x70ce7795896c1e226C71360F9d77B743d8302182`](https://hyperevmscan.io/address/0x70ce7795896c1e226C71360F9d77B743d8302182) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | | FlowNFTDescriptor | [`0x81Cc8C4B57B9A60a56330d087D6854A8E17Dfc7A`](https://hyperevmscan.io/address/0x81Cc8C4B57B9A60a56330d087D6854A8E17Dfc7A) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | ### Lightlink βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x5f742f6becc61e76ae67b0dc29d58f5c964e2c78`](https://phoenix.lightlink.io/address/0x5f742f6becc61e76ae67b0dc29d58f5c964e2c78) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | | FlowNFTDescriptor | [`0xc58E948Cb0a010105467C92856bcd4842B759fb1`](https://phoenix.lightlink.io/address/0xc58E948Cb0a010105467C92856bcd4842B759fb1) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | ### Linea Mainnet βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x977FDf70abeD6b60eECcee85322beA4575B0b6Ed`](https://lineascan.build/address/0x977FDf70abeD6b60eECcee85322beA4575B0b6Ed) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | | FlowNFTDescriptor | [`0x294D7fceBa43C4507771707CeBBB7b6d81d0BFdE`](https://lineascan.build/address/0x294D7fceBa43C4507771707CeBBB7b6d81d0BFdE) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | ### Mode βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xbed2f163cc0aa3278261ef1c3fa51b98db270829`](https://modescan.io/address/0xbed2f163cc0aa3278261ef1c3fa51b98db270829) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | | FlowNFTDescriptor | [`0xD9E2822a33606741BeDbA31614E68A745e430102`](https://modescan.io/address/0xD9E2822a33606741BeDbA31614E68A745e430102) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | ### Monad βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x0340a829b6dC3aDF7710a5bAF1970914af4977f5`](https://monadscan.com/address/0x0340a829b6dC3aDF7710a5bAF1970914af4977f5) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | | FlowNFTDescriptor | [`0xf51BB8bd1cfc7C890dB68c39dCCA67CAd7810Ce4`](https://monadscan.com/address/0xf51BB8bd1cfc7C890dB68c39dCCA67CAd7810Ce4) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | ### Morph βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xbf407836021c993dfa27cb8232415d15faea709a`](https://explorer.morphl2.io/address/0xbf407836021c993dfa27cb8232415d15faea709a) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | | FlowNFTDescriptor | [`0x1dd4dcE2BB742908b4062E583d9c035973413A3F`](https://explorer.morphl2.io/address/0x1dd4dcE2BB742908b4062E583d9c035973413A3F) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | ### OP Mainnet βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xd18491649440d6338532f260761cee64e79d7bb2`](https://optimistic.etherscan.io/address/0xd18491649440d6338532f260761cee64e79d7bb2) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | | FlowNFTDescriptor | [`0x7AD245b74bBC1B71Da1713D53238931F791b90A3`](https://optimistic.etherscan.io/address/0x7AD245b74bBC1B71Da1713D53238931F791b90A3) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | ### Polygon βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x62b6d5a3ac0cc91ecebd019d1c70fe955d8c7426`](https://polygonscan.com/address/0x62b6d5a3ac0cc91ecebd019d1c70fe955d8c7426) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | | FlowNFTDescriptor | [`0x87B836a9e26673feB3E409A0da2EAf99C79f26C3`](https://polygonscan.com/address/0x87B836a9e26673feB3E409A0da2EAf99C79f26C3) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | ### Scroll βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xc3e92b9714ed01b51fdc29bb88b17af5cddd2c22`](https://scrollscan.com/address/0xc3e92b9714ed01b51fdc29bb88b17af5cddd2c22) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | | FlowNFTDescriptor | [`0x797Fe78c41d9cbE81BBEA2f420101be5e47d2aFf`](https://scrollscan.com/address/0x797Fe78c41d9cbE81BBEA2f420101be5e47d2aFf) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | ### Sei Network ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x9eaf5a3f23964148a1321078f9cce4c2325c603e`](https://seiscan.io/address/0x9eaf5a3f23964148a1321078f9cce4c2325c603e) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | | FlowNFTDescriptor | [`0xF3D18b06c87735a58DAb3baC45af058b3772fD54`](https://seiscan.io/address/0xF3D18b06c87735a58DAb3baC45af058b3772fD54) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | ### Sonic βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x3954146884425accb86a6476dad69ec3591838cd`](https://sonicscan.org/address/0x3954146884425accb86a6476dad69ec3591838cd) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | | FlowNFTDescriptor | [`0xAab30e5CB903f67F109aFc7102ac8ED803681EA5`](https://sonicscan.org/address/0xAab30e5CB903f67F109aFc7102ac8ED803681EA5) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | ### Superseed βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xe91bbae6c7d67b7c5055de1c9635c17af056211b`](https://explorer.superseed.xyz/address/0xe91bbae6c7d67b7c5055de1c9635c17af056211b) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | | FlowNFTDescriptor | [`0xd932fDA016eE9d9F70f745544b4F56715b1E723b`](https://explorer.superseed.xyz/address/0xd932fDA016eE9d9F70f745544b4F56715b1E723b) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | ### Unichain βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x170ecc032c96aa976fa702e94fbc9fa5bb64ee7c`](https://uniscan.xyz/address/0x170ecc032c96aa976fa702e94fbc9fa5bb64ee7c) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | | FlowNFTDescriptor | [`0x89824A7e48dcf6B7AE9DeE6E566f62A5aDF037F2`](https://uniscan.xyz/address/0x89824A7e48dcf6B7AE9DeE6E566f62A5aDF037F2) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | ### XDC βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x3F00b8334EBE2A85875D1F8b50a43a12db67ACAD`](https://xdcscan.com/address/0x3F00b8334EBE2A85875D1F8b50a43a12db67ACAD) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | | FlowNFTDescriptor | [`0x9D3F0122b260D2218ecf681c416495882003deDd`](https://xdcscan.com/address/0x9D3F0122b260D2218ecf681c416495882003deDd) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | ### ZKsync Era βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xa7f02e692973b6315eaca7fb4285ad2536a89cd0`](https://explorer.zksync.io/address/0xa7f02e692973b6315eaca7fb4285ad2536a89cd0) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | | FlowNFTDescriptor | [`0x423C1b454250992Ede8516D36DE456F609714B53`](https://explorer.zksync.io/address/0x423C1b454250992Ede8516D36DE456F609714B53) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | ## Testnets ### Arbitrum Sepolia ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x73a474c9995b659bc4736486f25501e0a4a671ed`](https://sepolia.arbiscan.io/address/0x73a474c9995b659bc4736486f25501e0a4a671ed) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | | FlowNFTDescriptor | [`0x3E64A31C3974b6ae9f09a8fbc784519bF551e795`](https://sepolia.arbiscan.io/address/0x3E64A31C3974b6ae9f09a8fbc784519bF551e795) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | ### Base Sepolia βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x19e99dcdbaf2fbf43c60cfd026d571860da29d43`](https://sepolia.basescan.org/address/0x19e99dcdbaf2fbf43c60cfd026d571860da29d43) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | | FlowNFTDescriptor | [`0xcb5591F6d0e0fFC03037ef7b006D1361C6D33D25`](https://sepolia.basescan.org/address/0xcb5591F6d0e0fFC03037ef7b006D1361C6D33D25) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | ### OP Sepolia ❌ Not supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0x4cc7b50b0856c607edee0b6547221360e82e768c`](https://optimism-sepolia.blockscout.com/address/0x4cc7b50b0856c607edee0b6547221360e82e768c) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | | FlowNFTDescriptor | [`0x4739327acfb56E90177d44Cb0845e759276BCA88`](https://optimism-sepolia.blockscout.com/address/0x4739327acfb56E90177d44Cb0845e759276BCA88) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | ### Sepolia βœ… Supported in Sablier UI | Contract | Address | Deployment | | --- | --- | --- | | SablierFlow | [`0xde489096eC9C718358c52a8BBe4ffD74857356e9`](https://sepolia.etherscan.io/address/0xde489096eC9C718358c52a8BBe4ffD74857356e9) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | | FlowNFTDescriptor | [`0xc9dBf2D207D178875b698e5f7493ce2d8BA88994`](https://sepolia.etherscan.io/address/0xc9dBf2D207D178875b698e5f7493ce2d8BA88994) | [`flow-v2.0`](https://github.com/sablier-labs/sdk/blob/main/deployments/flow/v2.0) | --- ## Access Control Source: https://docs.sablier.com/reference/flow/access-control # Access Control With the exception of the [admin functions](/concepts/governance#flow), all functions in Flow 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 | | --- | :---: | :---: | :---: | | AdjustRatePerSecond | βœ… | ❌ | ❌ | | Deposit | βœ… | βœ… | βœ… | | Pause | βœ… | ❌ | ❌ | | Refund | βœ… | ❌ | ❌ | | Restart | βœ… | ❌ | ❌ | | Transfer NFT | ❌ | βœ… | ❌ | | Withdraw to any address | ❌ | βœ… | ❌ | | Withdraw to recipient | βœ… | βœ… | βœ… | | Void | βœ… | βœ… | ❌ | ## Adjust rate per second Only the sender can adjust the rate per second of a stream. ## Create stream ## Deposit into a stream Anyone can deposit into a stream. ## Pause Only the sender can pause a stream. ## Refund from a stream Only the sender can refund from a stream. ## Restarting a stream Only the sender can restart a stream. ## Voiding a stream Both Sender and Recipient can void a stream. ## Withdraw from a stream Anyone can call withdraw on a stream as long as `to` address matches the recipient. If recipient/operator is calling withdraw on a stream, they can choose to withdraw to any address. ```mermaid sequenceDiagram actor Sender Sender ->> Flow: adjustRatePerSecond() Flow -->> Flow: update rps ``` ```mermaid sequenceDiagram actor Sender Sender ->> Flow: create() Create actor Recipient Flow -->> Recipient: mint NFT ``` ```mermaid sequenceDiagram actor Anyone Anyone ->> ERC20: approve() Anyone ->> Flow: deposit() Flow ->> ERC20: transferFrom() Anyone -->> Flow: Transfer tokens ``` ```mermaid sequenceDiagram actor Sender Sender ->> Flow: pause() Flow -->> Flow: set rps = 0 ``` ```mermaid sequenceDiagram actor Sender Sender ->> Flow: refund() Flow ->> ERC20: transfer() Flow -->> Sender: Transfer unstreamed tokens ``` ```mermaid sequenceDiagram actor Sender Sender ->> Flow: restart() Flow -->> Flow: set rps > 0 ``` ```mermaid sequenceDiagram actor Sender Sender ->> Flow: void() activate Flow Flow -->> Flow: set rps = 0, Flow -->> Flow: set st = now & sd = cd deactivate Flow actor Recipient Recipient ->> Flow: void() activate Flow Flow -->> Flow: set rps = 0 Flow -->> Flow: set st = now & sd = cd deactivate Flow ``` ```mermaid sequenceDiagram actor Anyone Anyone ->> Flow: withdraw() activate Flow Flow ->> ERC20: transfer() Create actor Recipient Flow -->> Recipient: Transfer tokens deactivate Flow Recipient ->> Flow: withdraw() activate Flow Flow ->> ERC20: transfer() Create actor Any Address Flow -->> Any Address : Transfer tokens deactivate Flow ``` --- ## Batch Source: https://docs.sablier.com/reference/flow/contracts/abstracts/abstract.Batch # Batch [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/Batch.sol) **Inherits:** [IBatch](/reference/flow/contracts/interfaces/interface.IBatch) **Title:** Batch See the documentation in [IBatch](/reference/flow/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/flow/contracts/abstracts/abstract.Comptrollerable # Comptrollerable [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/Comptrollerable.sol) **Inherits:** [IComptrollerable](/reference/flow/contracts/interfaces/interface.IComptrollerable) **Title:** Comptrollerable See the documentation in [IComptrollerable](/reference/flow/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/flow/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; ``` --- ## SablierFlowState Source: https://docs.sablier.com/reference/flow/contracts/abstracts/abstract.SablierFlowState # SablierFlowState [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/abstracts/SablierFlowState.sol) **Inherits:** [ISablierFlowState](/reference/flow/contracts/interfaces/interface.ISablierFlowState) **Title:** SablierFlowState See the documentation in [ISablierFlowState](/reference/flow/contracts/interfaces/interface.ISablierFlowState). ## 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. ```solidity uint256 public override nextStreamId ``` ### nftDescriptor Contract that generates the non-fungible token URI. ```solidity IFlowNFTDescriptor public override nftDescriptor ``` ### \_streams Sablier Flow streams mapped by unsigned integers. ```solidity mapping(uint256 id => Flow.Stream stream) internal _streams ``` ## Functions ### constructor ```solidity constructor(address initialNFTDescriptor) ; ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `initialNFTDescriptor` | `address` | The address of the initial NFT descriptor. | ### notNull Checks that `streamId` does not reference a null stream. ```solidity modifier notNull(uint256 streamId) ; ``` ### notPaused Checks that `streamId` does not reference a paused stream. Note that this implicitly checks that the stream is not voided either. ```solidity modifier notPaused(uint256 streamId) ; ``` ### notVoided Checks that `streamId` does not reference a voided stream. ```solidity modifier notVoided(uint256 streamId) ; ``` ### onlySender Checks the `msg.sender` is the stream's sender. ```solidity modifier onlySender(uint256 streamId) ; ``` ### getBalance Retrieves the balance of the stream, i.e. the total deposited amounts subtracted by the total withdrawn amounts, denoted in token's decimals. Reverts if `streamId` references a null stream. ```solidity function getBalance(uint256 streamId) external view override notNull(streamId) returns (uint128 balance); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### getRatePerSecond Retrieves the rate per second of the stream, denoted as a fixed-point number where 1e18 is 1 token per second. Reverts if `streamId` references a null stream. ```solidity function getRatePerSecond(uint256 streamId) external view override notNull(streamId) returns (UD21x18 ratePerSecond); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to make the query for. | ### 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. | ### getSnapshotDebtScaled Retrieves the snapshot debt of the stream, denoted as a fixed-point number where 1e18 is 1 token. Reverts if `streamId` references a null stream. ```solidity function getSnapshotDebtScaled(uint256 streamId) external view override notNull(streamId) returns (uint256 snapshotDebtScaled); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### getSnapshotTime Retrieves the snapshot time of the stream, which is a Unix timestamp. Reverts if `streamId` references a null stream. ```solidity function getSnapshotTime(uint256 streamId) external view override notNull(streamId) returns (uint40 snapshotTime); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to make the query for. | ### getStream Retrieves the stream entity. Reverts if `streamId` references a null stream. ```solidity function getStream(uint256 streamId) external view override notNull(streamId) returns (Flow.Stream memory stream); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### getToken Retrieves the token of the stream. Reverts if `streamId` references a null stream. ```solidity function getToken(uint256 streamId) external view override notNull(streamId) returns (IERC20 token); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to make the query for. | ### getTokenDecimals Retrieves the token decimals of the stream. Reverts if `streamId` references a null stream. ```solidity function getTokenDecimals(uint256 streamId) external view override notNull(streamId) returns (uint8 tokenDecimals); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to make the query for. | ### 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 is transferable. 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. | ### isVoided Retrieves a flag indicating whether the stream is voided. Reverts if `streamId` references a null stream. ```solidity function isVoided(uint256 streamId) external view override notNull(streamId) returns (bool result); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### \_notNull A private function is used instead of inlining this logic in a modifier because Solidity copies modifiers into every function that uses them. ```solidity function _notNull(uint256 streamId) private view; ``` --- ## FlowNFTDescriptor Source: https://docs.sablier.com/reference/flow/contracts/contract.FlowNFTDescriptor # FlowNFTDescriptor [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/FlowNFTDescriptor.sol) **Inherits:** [IFlowNFTDescriptor](/reference/flow/contracts/interfaces/interface.IFlowNFTDescriptor) **Title:** FlowNFTDescriptor See the documentation in [IFlowNFTDescriptor](/reference/flow/contracts/interfaces/interface.IFlowNFTDescriptor). ## Functions ### tokenURI Produces the URI describing a particular stream NFT. Currently it returns the Sablier logo as an SVG. In the future, it will return an NFT SVG. ```solidity function tokenURI( IERC721Metadata, /* sablierFlow */ uint256 /* streamId */ ) external pure override returns (string memory uri); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `` | `IERC721Metadata` | | | `` | `uint256` | | **Returns** | Name | Type | Description | | --- | --- | --- | | `uri` | `string` | The URI of the ERC721-compliant metadata. | --- ## SablierFlow Source: https://docs.sablier.com/reference/flow/contracts/contract.SablierFlow # SablierFlow [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/SablierFlow.sol) **Inherits:** [Batch](/reference/flow/contracts/abstracts/abstract.Batch), [Comptrollerable](/reference/flow/contracts/abstracts/abstract.Comptrollerable), ERC721, [ISablierFlow](/reference/flow/contracts/interfaces/interface.ISablierFlow), [NoDelegateCall](/reference/flow/contracts/abstracts/abstract.NoDelegateCall), [SablierFlowState](/reference/flow/contracts/abstracts/abstract.SablierFlowState) **Title:** SablierFlow See the documentation in [ISablierFlow](/reference/flow/contracts/interfaces/interface.ISablierFlow). ## Functions ### constructor ```solidity constructor( address initialComptroller, address initialNFTDescriptor ) [Comptrollerable](/docs/reference/04-flow/contracts/abstracts/abstract.Comptrollerable.md)(initialComptroller) ERC721("Sablier Flow NFT", "SAB-FLOW") SablierFlowState(initialNFTDescriptor); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `initialComptroller` | `address` | The address of the initial comptroller contract. | | `initialNFTDescriptor` | `address` | The address of the initial NFT descriptor. | ### updateMetadata Emits an ERC-4906 event to trigger an update of the NFT metadata. ```solidity modifier updateMetadata(uint256 streamId) ; ``` ### 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. | ### coveredDebtOf Returns the amount of debt covered by the stream balance, denoted in token's decimals. Reverts if `streamId` references a null stream. ```solidity function coveredDebtOf(uint256 streamId) external view override notNull(streamId) returns (uint128 coveredDebt); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### depletionTimeOf Returns the time at which the total debt exceeds stream balance. If the total debt exceeds the stream balance, it returns 0. Reverts on the following conditions: - If `streamId` references a paused or a null stream. - If stream balance is zero. ```solidity function depletionTimeOf(uint256 streamId) external view override notNull(streamId) notPaused(streamId) returns (uint256 depletionTime); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### getRecipient Retrieves the stream's recipient. Reverts if `streamId` references a null stream. ```solidity function getRecipient(uint256 streamId) external view override notNull(streamId) returns (address recipient); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### ongoingDebtScaledOf Returns the amount of debt accrued since the snapshot time until now, denoted as a fixed-point number where 1e18 is 1 token. If the stream is pending, it returns zero. Reverts if `streamId` references a null stream. ```solidity function ongoingDebtScaledOf(uint256 streamId) external view override notNull(streamId) returns (uint256 ongoingDebtScaled); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### refundableAmountOf Returns the amount that the sender can be refunded from the stream, denoted in 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 Returns the stream's status. Reverts if `streamId` references a null stream. Integrators should exercise caution when depending on the return value of this function as streams can be paused and resumed at any moment. ```solidity function statusOf(uint256 streamId) external view override notNull(streamId) returns (Flow.Status status); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### totalDebtOf Returns the total amount owed by the sender to the recipient, denoted in token's decimals. Reverts if `streamId` references a null stream. ```solidity function totalDebtOf(uint256 streamId) external view override notNull(streamId) returns (uint256 totalDebt); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### uncoveredDebtOf Returns the amount of debt not covered by the stream balance, denoted in token's decimals. Reverts if `streamId` references a null stream. ```solidity function uncoveredDebtOf(uint256 streamId) external view override notNull(streamId) returns (uint256 uncoveredDebt); ``` **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 token decimals. This is an alias for `coveredDebtOf`. 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. | **Returns** | Name | Type | Description | | --- | --- | --- | | `withdrawableAmount` | `uint128` | The amount that the recipient can withdraw. | ### adjustRatePerSecond Changes the stream's rate per second. Emits a {AdjustFlowStream} and {MetadataUpdate} event. Notes: - If the snapshot time is not in the future, it updates both the snapshot time and snapshot debt. Requirements: - Must not be delegate called. - `streamId` must not reference a null, paused, or voided stream. - `msg.sender` must be the stream's sender. - `newRatePerSecond` must be greater than zero and must be different from the current rate per second. ```solidity function adjustRatePerSecond( uint256 streamId, UD21x18 newRatePerSecond ) external payable override noDelegateCall notNull(streamId) notPaused(streamId) onlySender(streamId) updateMetadata(streamId); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to adjust. | | `newRatePerSecond` | `UD21x18` | The new rate per second, denoted as a fixed-point number where 1e18 is 1 token per second. | ### create Creates a new Flow stream by setting the snapshot time to `startTime` and leaving the balance to zero. The stream is wrapped in an ERC-721 NFT. Emits a {CreateFlowStream} and {MetadataUpdate} event. Requirements: - Must not be delegate called. - `sender` must not be the zero address. - `recipient` must not be the zero address. - If `startTime` is in the future, the `ratePerSecond` must be greater than zero. - The `token` must not be the native token. - The `token`'s decimals must be less than or equal to 18. ```solidity function create( address sender, address recipient, UD21x18 ratePerSecond, uint40 startTime, IERC20 token, bool transferable ) external payable override noDelegateCall returns (uint256 streamId); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `sender` | `address` | The address streaming the tokens, which is able to adjust and pause the stream. It doesn't have to be the same as `msg.sender`. | | `recipient` | `address` | The address receiving the tokens. | | `ratePerSecond` | `UD21x18` | The amount by which the debt is increasing every second, denoted as a fixed-point number where 1e18 is 1 token per second. | | `startTime` | `uint40` | The timestamp when the stream starts. A sentinel value of zero means the stream will be created with the snapshot time as `block.timestamp`. | | `token` | `IERC20` | The contract address of the ERC-20 token to be streamed. | | `transferable` | `bool` | Boolean indicating if the stream NFT is transferable. | **Returns** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the newly created stream. | ### createAndDeposit Creates a new Flow stream by setting the snapshot time to `startTime` and the balance to `amount`. The stream is wrapped in an ERC-721 NFT. Emits a {Transfer}, {CreateFlowStream}, {DepositFlowStream} and {MetadataUpdate} event. Notes: - Refer to the notes in {create} and {deposit}. Requirements: - Refer to the requirements in {create} and {deposit}. ```solidity function createAndDeposit( address sender, address recipient, UD21x18 ratePerSecond, uint40 startTime, IERC20 token, bool transferable, uint128 amount ) external payable override noDelegateCall returns (uint256 streamId); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `sender` | `address` | The address streaming the tokens. It doesn't have to be the same as `msg.sender`. | | `recipient` | `address` | The address receiving the tokens. | | `ratePerSecond` | `UD21x18` | The amount by which the debt is increasing every second, denoted as a fixed-point number where 1e18 is 1 token per second. | | `startTime` | `uint40` | The timestamp when the stream starts. A sentinel value of zero means the stream will be created with the snapshot time as `block.timestamp`. | | `token` | `IERC20` | The contract address of the ERC-20 token to be streamed. | | `transferable` | `bool` | Boolean indicating if the stream NFT is transferable. | | `amount` | `uint128` | The deposit amount, denoted in token's decimals. | **Returns** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the newly created stream. | ### deposit Makes a deposit in a stream. Emits a {Transfer}, {DepositFlowStream} and {MetadataUpdate} event. Requirements: - Must not be delegate called. - `streamId` must not reference a null or a voided stream. - `amount` must be greater than zero. - `sender` and `recipient` must match the stream's sender and recipient addresses. ```solidity function deposit( uint256 streamId, uint128 amount, address sender, address recipient ) external payable override noDelegateCall notNull(streamId) notVoided(streamId) updateMetadata(streamId); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to deposit to. | | `amount` | `uint128` | The deposit amount, denoted in token's decimals. | | `sender` | `address` | The stream's sender address. | | `recipient` | `address` | The stream's recipient address. | ### depositAndPause Deposits tokens in a stream and pauses it. Emits a {Transfer}, {DepositFlowStream}, {PauseFlowStream} and {MetadataUpdate} event. Notes: - Refer to the notes in {deposit} and {pause}. Requirements: - Refer to the requirements in {deposit} and {pause}. ```solidity function depositAndPause( uint256 streamId, uint128 amount ) external payable override noDelegateCall notNull(streamId) notPaused(streamId) onlySender(streamId) updateMetadata(streamId); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to deposit to, and then pause. | | `amount` | `uint128` | The deposit amount, denoted in token's decimals. | ### pause Pauses the stream. Emits a {PauseFlowStream} and {MetadataUpdate} event. Notes: - It updates snapshot debt and snapshot time. - It sets the rate per second to zero. Requirements: - Must not be delegate called. - `streamId` must not reference a null, pending or paused stream. - `msg.sender` must be the stream's sender. ```solidity function pause(uint256 streamId) external payable override noDelegateCall notNull(streamId) notPaused(streamId) onlySender(streamId) updateMetadata(streamId); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to pause. | ### 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. ```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. | ### refund Refunds the provided amount of tokens from the stream to the sender's address. Emits a {Transfer}, {RefundFromFlowStream} and {MetadataUpdate} event. Requirements: - Must not be delegate called. - `streamId` must not reference a null stream. - `msg.sender` must be the sender. - `amount` must be greater than zero and must not exceed the refundable amount. ```solidity function refund( uint256 streamId, uint128 amount ) external payable override noDelegateCall notNull(streamId) onlySender(streamId) updateMetadata(streamId); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to refund from. | | `amount` | `uint128` | The amount to refund, denoted in token's decimals. | ### refundAndPause Refunds the provided amount of tokens from the stream to the sender's address. Emits a {Transfer}, {RefundFromFlowStream}, {PauseFlowStream} and {MetadataUpdate} event. Notes: - Refer to the notes in {pause}. Requirements: - Refer to the requirements in {refund} and {pause}. ```solidity function refundAndPause( uint256 streamId, uint128 amount ) external payable override noDelegateCall notNull(streamId) notPaused(streamId) onlySender(streamId) updateMetadata(streamId); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to refund from and then pause. | | `amount` | `uint128` | The amount to refund, denoted in token's decimals. | ### refundMax Refunds the entire refundable amount of tokens from the stream to the sender's address. Emits a {Transfer}, {RefundFromFlowStream} and {MetadataUpdate} event. Requirements: - Refer to the requirements in {refund}. ```solidity function refundMax(uint256 streamId) external payable override noDelegateCall notNull(streamId) onlySender(streamId) updateMetadata(streamId) returns (uint128 refundedAmount); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to refund from. | **Returns** | Name | Type | Description | | --- | --- | --- | | `refundedAmount` | `uint128` | The amount refunded to the stream sender, denoted in token's decimals. | ### restart Restarts the stream with the provided rate per second. Emits a {RestartFlowStream} and {MetadataUpdate} event. Notes: - It updates snapshot debt and snapshot time. Requirements: - Must not be delegate called. - `streamId` must not reference a null stream, must be paused, and must not be voided. - `msg.sender` must be the stream's sender. - `ratePerSecond` must be greater than zero. ```solidity function restart( uint256 streamId, UD21x18 ratePerSecond ) external payable override noDelegateCall notNull(streamId) notVoided(streamId) onlySender(streamId) updateMetadata(streamId); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to restart. | | `ratePerSecond` | `UD21x18` | The amount by which the debt is increasing every second, denoted as a fixed-point number where 1e18 is 1 token per second. | ### restartAndDeposit Restarts the stream with the provided rate per second, and makes a deposit. Emits a {RestartFlowStream}, {Transfer}, {DepositFlowStream} and {MetadataUpdate} event. Notes: - Refer to the notes in {restart} and {deposit}. Requirements: - `amount` must be greater than zero. - Refer to the requirements in {restart}. ```solidity function restartAndDeposit( uint256 streamId, UD21x18 ratePerSecond, uint128 amount ) external payable override noDelegateCall notNull(streamId) notVoided(streamId) onlySender(streamId) updateMetadata(streamId); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to restart. | | `ratePerSecond` | `UD21x18` | The amount by which the debt is increasing every second, denoted as a fixed-point number where 1e18 is 1 token per second. | | `amount` | `uint128` | The deposit amount, denoted in token's decimals. | ### setNativeToken Sets the native token address. Once set, it cannot be changed. For more information, see the documentation for {nativeToken}. Emits a {SetNativeToken} event. Requirements: - `msg.sender` must be the comptroller contract. - `newNativeToken` must not be the zero address. - The native token must not be already set. ```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(IFlowNFTDescriptor newNFTDescriptor) external override onlyComptroller; ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `newNFTDescriptor` | `IFlowNFTDescriptor` | The address of the new NFT descriptor contract. | ### 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); ``` ### transferFromPayable Wrapper for {IERC721.transferFrom} with the `payable` specifier so that it can be called in conjunction with other functions using {IBatch.batch}. Requirements: - Refer to the requirements of `transferFrom` in: [https://github.com/OpenZeppelin/openzeppelin-contracts/blob/e4f70216d759d8e6a64144a9e1f7bbeed78e7079/contracts/token/ERC721/IERC721.sol#L75-L91](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/e4f70216d759d8e6a64144a9e1f7bbeed78e7079/contracts/token/ERC721/IERC721.sol#L75-L91). ```solidity function transferFromPayable(address from, address to, uint256 streamId) external payable; ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `from` | `address` | The owner of the stream ID. | | `to` | `address` | The address of the new owner of the stream ID. | | `streamId` | `uint256` | The ID of the stream NFT to transfer. | ### transferTokens A helper to transfer ERC-20 tokens from the caller to the provided address. Useful for paying one-time bonuses. Emits a {Transfer} event. Requirements: - `msg.sender` must have approved this contract to spend at least `amount` tokens. ```solidity function transferTokens(IERC20 token, address to, uint128 amount) external payable; ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `token` | `IERC20` | The contract address of the ERC-20 token to be transferred. | | `to` | `address` | The address receiving the tokens. | | `amount` | `uint128` | The amount of tokens to transfer, denoted in token's decimals. | ### void Voids a stream. Emits a {VoidFlowStream} and {MetadataUpdate} event. Notes: - It sets snapshot time to the `block.timestamp`. - Voiding an insolvent stream sets the snapshot debt to the stream's balance making the uncovered debt to become zero. - Voiding a solvent stream updates the snapshot debt by adding up ongoing debt. - It sets the rate per second to zero. - A voided stream cannot be restarted. Requirements: - Must not be delegate called. - `streamId` must not reference a null or a voided stream. - `msg.sender` must either be the stream's sender, recipient or an approved third party. ```solidity function void(uint256 streamId) external payable override noDelegateCall notNull(streamId) notVoided(streamId) updateMetadata(streamId); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to void. | ### withdraw Withdraws the provided `amount` to the provided `to` address. Emits a {Transfer}, {WithdrawFromFlowStream} and {MetadataUpdate} event. Notes: - It sets the snapshot time to the `block.timestamp` if `amount` is greater than snapshot debt. Requirements: - Must not be delegate called. - `streamId` must not reference a null stream. - `to` must not be the zero address. - `to` must be the recipient if `msg.sender` is not the stream's recipient or an approved third party. - `amount` must be greater than zero and must not exceed the withdrawable amount. - `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 override noDelegateCall notNull(streamId) updateMetadata(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 token's decimals. | ### withdrawMax Withdraws the entire withdrawable amount to the provided `to` address. Emits a {Transfer}, {WithdrawFromFlowStream} 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 noDelegateCall notNull(streamId) updateMetadata(streamId) 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 to the recipient, denoted in token's decimals. | ### \_update Overrides the {ERC721.\_update} function to check that the stream is transferable. The transferable flag is ignored if the current owner is 0, as the update in this case is a mint and is allowed. Transfers to the zero address are not allowed, preventing accidental burns. ```solidity function _update( address to, uint256 streamId, address auth ) internal override updateMetadata(streamId) 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. | ### \_coveredDebtOf Calculates the amount of covered debt by the stream balance. ```solidity function _coveredDebtOf(uint256 streamId) private view returns (uint128); ``` ### \_isCallerStreamRecipientOrApproved Checks whether `msg.sender` is the stream's recipient or an approved third party. ```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` | | ### \_ongoingDebtScaledOf Calculates the ongoing debt, as a 18-decimals fixed point number, accrued since last snapshot. Return 0 if the stream is paused or `block.timestamp` is less than or equal to snapshot time. ```solidity function _ongoingDebtScaledOf(uint256 streamId) private view returns (uint256); ``` ### \_refundableAmountOf Calculates the refundable amount. ```solidity function _refundableAmountOf(uint256 streamId) private view returns (uint128); ``` ### \_totalDebtOf The total debt is the sum of the snapshot debt and the ongoing debt descaled to token's decimal. This value is independent of the stream's balance. ```solidity function _totalDebtOf(uint256 streamId) private view returns (uint256); ``` ### \_uncoveredDebtOf Calculates the uncovered debt. ```solidity function _uncoveredDebtOf(uint256 streamId) private view returns (uint256); ``` ### \_verifyStreamSenderRecipient Checks whether the provided addresses matches stream's sender and recipient. ```solidity function _verifyStreamSenderRecipient(uint256 streamId, address sender, address recipient) private view; ``` ### \_adjustRatePerSecond See the documentation for the user-facing functions that call this private function. ```solidity function _adjustRatePerSecond(uint256 streamId, UD21x18 newRatePerSecond) private; ``` ### \_create See the documentation for the user-facing functions that call this private function. ```solidity function _create( address sender, address recipient, UD21x18 ratePerSecond, uint40 startTime, IERC20 token, bool transferable ) private returns (uint256 streamId); ``` ### \_deposit See the documentation for the user-facing functions that call this private function. ```solidity function _deposit(uint256 streamId, uint128 amount) private; ``` ### \_pause See the documentation for the user-facing functions that call this private function. ```solidity function _pause(uint256 streamId) private; ``` ### \_refund See the documentation for the user-facing functions that call this private function. ```solidity function _refund(uint256 streamId, uint128 amount) private; ``` ### \_restart See the documentation for the user-facing functions that call this private function. ```solidity function _restart(uint256 streamId, UD21x18 ratePerSecond) private; ``` ### \_void See the documentation for the user-facing functions that call this private function. ```solidity function _void(uint256 streamId) private; ``` ### \_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/flow/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/flow/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); ``` --- ## IFlowNFTDescriptor Source: https://docs.sablier.com/reference/flow/contracts/interfaces/interface.IFlowNFTDescriptor # IFlowNFTDescriptor [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/interfaces/IFlowNFTDescriptor.sol) **Title:** IFlowNFTDescriptor This contract generates the URI describing the Sablier Flow stream NFTs. ## Functions ### tokenURI Produces the URI describing a particular stream NFT. Currently it returns the Sablier logo as an SVG. In the future, it will return an NFT SVG. ```solidity function tokenURI(IERC721Metadata sablierFlow, uint256 streamId) external view returns (string memory uri); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `sablierFlow` | `IERC721Metadata` | The address of the Sablier Flow 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. | --- ## ISablierFlow Source: https://docs.sablier.com/reference/flow/contracts/interfaces/interface.ISablierFlow # ISablierFlow [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/interfaces/ISablierFlow.sol) **Inherits:** IBatch, IComptrollerable, IERC4906, IERC721Metadata, [ISablierFlowState](/reference/flow/contracts/interfaces/interface.ISablierFlowState) **Title:** ISablierFlow Creates and manages Flow streams with linear streaming functions. ## 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. | ### coveredDebtOf Returns the amount of debt covered by the stream balance, denoted in token's decimals. Reverts if `streamId` references a null stream. ```solidity function coveredDebtOf(uint256 streamId) external view returns (uint128 coveredDebt); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### depletionTimeOf Returns the time at which the total debt exceeds stream balance. If the total debt exceeds the stream balance, it returns 0. Reverts on the following conditions: - If `streamId` references a paused or a null stream. - If stream balance is zero. ```solidity function depletionTimeOf(uint256 streamId) external view returns (uint256 depletionTime); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### getRecipient Retrieves the stream's recipient. Reverts if `streamId` references a null stream. ```solidity function getRecipient(uint256 streamId) external view returns (address recipient); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### ongoingDebtScaledOf Returns the amount of debt accrued since the snapshot time until now, denoted as a fixed-point number where 1e18 is 1 token. If the stream is pending, it returns zero. Reverts if `streamId` references a null stream. ```solidity function ongoingDebtScaledOf(uint256 streamId) external view returns (uint256 ongoingDebtScaled); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### refundableAmountOf Returns the amount that the sender can be refunded from the stream, denoted in 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 Returns the stream's status. Reverts if `streamId` references a null stream. Integrators should exercise caution when depending on the return value of this function as streams can be paused and resumed at any moment. ```solidity function statusOf(uint256 streamId) external view returns (Flow.Status status); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### totalDebtOf Returns the total amount owed by the sender to the recipient, denoted in token's decimals. Reverts if `streamId` references a null stream. ```solidity function totalDebtOf(uint256 streamId) external view returns (uint256 totalDebt); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### uncoveredDebtOf Returns the amount of debt not covered by the stream balance, denoted in token's decimals. Reverts if `streamId` references a null stream. ```solidity function uncoveredDebtOf(uint256 streamId) external view returns (uint256 uncoveredDebt); ``` **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 token decimals. This is an alias for `coveredDebtOf`. 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. | **Returns** | Name | Type | Description | | --- | --- | --- | | `withdrawableAmount` | `uint128` | The amount that the recipient can withdraw. | ### adjustRatePerSecond Changes the stream's rate per second. Emits a [AdjustFlowStream](/reference/flow/contracts/interfaces/interface.ISablierFlow#adjustflowstream) and {MetadataUpdate} event. Notes: - If the snapshot time is not in the future, it updates both the snapshot time and snapshot debt. Requirements: - Must not be delegate called. - `streamId` must not reference a null, paused, or voided stream. - `msg.sender` must be the stream's sender. - `newRatePerSecond` must be greater than zero and must be different from the current rate per second. ```solidity function adjustRatePerSecond(uint256 streamId, UD21x18 newRatePerSecond) external payable; ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to adjust. | | `newRatePerSecond` | `UD21x18` | The new rate per second, denoted as a fixed-point number where 1e18 is 1 token per second. | ### create Creates a new Flow stream by setting the snapshot time to `startTime` and leaving the balance to zero. The stream is wrapped in an ERC-721 NFT. Emits a [CreateFlowStream](/reference/flow/contracts/interfaces/interface.ISablierFlow#createflowstream) and {MetadataUpdate} event. Requirements: - Must not be delegate called. - `sender` must not be the zero address. - `recipient` must not be the zero address. - If `startTime` is in the future, the `ratePerSecond` must be greater than zero. - The `token` must not be the native token. - The `token`'s decimals must be less than or equal to 18. ```solidity function create( address sender, address recipient, UD21x18 ratePerSecond, uint40 startTime, IERC20 token, bool transferable ) external payable returns (uint256 streamId); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `sender` | `address` | The address streaming the tokens, which is able to adjust and pause the stream. It doesn't have to be the same as `msg.sender`. | | `recipient` | `address` | The address receiving the tokens. | | `ratePerSecond` | `UD21x18` | The amount by which the debt is increasing every second, denoted as a fixed-point number where 1e18 is 1 token per second. | | `startTime` | `uint40` | The timestamp when the stream starts. A sentinel value of zero means the stream will be created with the snapshot time as `block.timestamp`. | | `token` | `IERC20` | The contract address of the ERC-20 token to be streamed. | | `transferable` | `bool` | Boolean indicating if the stream NFT is transferable. | **Returns** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the newly created stream. | ### createAndDeposit Creates a new Flow stream by setting the snapshot time to `startTime` and the balance to `amount`. The stream is wrapped in an ERC-721 NFT. Emits a {Transfer}, [CreateFlowStream](/reference/flow/contracts/interfaces/interface.ISablierFlow#createflowstream), [DepositFlowStream](/reference/flow/contracts/interfaces/interface.ISablierFlow#depositflowstream) and {MetadataUpdate} event. Notes: - Refer to the notes in [create](/reference/flow/contracts/interfaces/interface.ISablierFlow#create) and [deposit](/reference/flow/contracts/interfaces/interface.ISablierFlow#deposit). Requirements: - Refer to the requirements in [create](/reference/flow/contracts/interfaces/interface.ISablierFlow#create) and [deposit](/reference/flow/contracts/interfaces/interface.ISablierFlow#deposit). ```solidity function createAndDeposit( address sender, address recipient, UD21x18 ratePerSecond, uint40 startTime, IERC20 token, bool transferable, uint128 amount ) external payable returns (uint256 streamId); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `sender` | `address` | The address streaming the tokens. It doesn't have to be the same as `msg.sender`. | | `recipient` | `address` | The address receiving the tokens. | | `ratePerSecond` | `UD21x18` | The amount by which the debt is increasing every second, denoted as a fixed-point number where 1e18 is 1 token per second. | | `startTime` | `uint40` | The timestamp when the stream starts. A sentinel value of zero means the stream will be created with the snapshot time as `block.timestamp`. | | `token` | `IERC20` | The contract address of the ERC-20 token to be streamed. | | `transferable` | `bool` | Boolean indicating if the stream NFT is transferable. | | `amount` | `uint128` | The deposit amount, denoted in token's decimals. | **Returns** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the newly created stream. | ### deposit Makes a deposit in a stream. Emits a {Transfer}, [DepositFlowStream](/reference/flow/contracts/interfaces/interface.ISablierFlow#depositflowstream) and {MetadataUpdate} event. Requirements: - Must not be delegate called. - `streamId` must not reference a null or a voided stream. - `amount` must be greater than zero. - `sender` and `recipient` must match the stream's sender and recipient addresses. ```solidity function deposit(uint256 streamId, uint128 amount, address sender, address recipient) external payable; ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to deposit to. | | `amount` | `uint128` | The deposit amount, denoted in token's decimals. | | `sender` | `address` | The stream's sender address. | | `recipient` | `address` | The stream's recipient address. | ### depositAndPause Deposits tokens in a stream and pauses it. Emits a {Transfer}, [DepositFlowStream](/reference/flow/contracts/interfaces/interface.ISablierFlow#depositflowstream), [PauseFlowStream](/reference/flow/contracts/interfaces/interface.ISablierFlow#pauseflowstream) and {MetadataUpdate} event. Notes: - Refer to the notes in [deposit](/reference/flow/contracts/interfaces/interface.ISablierFlow#deposit) and [pause](/reference/flow/contracts/interfaces/interface.ISablierFlow#pause). Requirements: - Refer to the requirements in [deposit](/reference/flow/contracts/interfaces/interface.ISablierFlow#deposit) and [pause](/reference/flow/contracts/interfaces/interface.ISablierFlow#pause). ```solidity function depositAndPause(uint256 streamId, uint128 amount) external payable; ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to deposit to, and then pause. | | `amount` | `uint128` | The deposit amount, denoted in token's decimals. | ### pause Pauses the stream. Emits a [PauseFlowStream](/reference/flow/contracts/interfaces/interface.ISablierFlow#pauseflowstream) and {MetadataUpdate} event. Notes: - It updates snapshot debt and snapshot time. - It sets the rate per second to zero. Requirements: - Must not be delegate called. - `streamId` must not reference a null, pending or paused stream. - `msg.sender` must be the stream's sender. ```solidity function pause(uint256 streamId) external payable; ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to pause. | ### 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. ```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. | ### refund Refunds the provided amount of tokens from the stream to the sender's address. Emits a {Transfer}, [RefundFromFlowStream](/reference/flow/contracts/interfaces/interface.ISablierFlow#refundfromflowstream) and {MetadataUpdate} event. Requirements: - Must not be delegate called. - `streamId` must not reference a null stream. - `msg.sender` must be the sender. - `amount` must be greater than zero and must not exceed the refundable amount. ```solidity function refund(uint256 streamId, uint128 amount) external payable; ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to refund from. | | `amount` | `uint128` | The amount to refund, denoted in token's decimals. | ### refundAndPause Refunds the provided amount of tokens from the stream to the sender's address. Emits a {Transfer}, [RefundFromFlowStream](/reference/flow/contracts/interfaces/interface.ISablierFlow#refundfromflowstream), [PauseFlowStream](/reference/flow/contracts/interfaces/interface.ISablierFlow#pauseflowstream) and {MetadataUpdate} event. Notes: - Refer to the notes in [pause](/reference/flow/contracts/interfaces/interface.ISablierFlow#pause). Requirements: - Refer to the requirements in [refund](/reference/flow/contracts/interfaces/interface.ISablierFlow#refund) and [pause](/reference/flow/contracts/interfaces/interface.ISablierFlow#pause). ```solidity function refundAndPause(uint256 streamId, uint128 amount) external payable; ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to refund from and then pause. | | `amount` | `uint128` | The amount to refund, denoted in token's decimals. | ### refundMax Refunds the entire refundable amount of tokens from the stream to the sender's address. Emits a {Transfer}, [RefundFromFlowStream](/reference/flow/contracts/interfaces/interface.ISablierFlow#refundfromflowstream) and {MetadataUpdate} event. Requirements: - Refer to the requirements in [refund](/reference/flow/contracts/interfaces/interface.ISablierFlow#refund). ```solidity function refundMax(uint256 streamId) external payable returns (uint128 refundedAmount); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to refund from. | **Returns** | Name | Type | Description | | --- | --- | --- | | `refundedAmount` | `uint128` | The amount refunded to the stream sender, denoted in token's decimals. | ### restart Restarts the stream with the provided rate per second. Emits a [RestartFlowStream](/reference/flow/contracts/interfaces/interface.ISablierFlow#restartflowstream) and {MetadataUpdate} event. Notes: - It updates snapshot debt and snapshot time. Requirements: - Must not be delegate called. - `streamId` must not reference a null stream, must be paused, and must not be voided. - `msg.sender` must be the stream's sender. - `ratePerSecond` must be greater than zero. ```solidity function restart(uint256 streamId, UD21x18 ratePerSecond) external payable; ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to restart. | | `ratePerSecond` | `UD21x18` | The amount by which the debt is increasing every second, denoted as a fixed-point number where 1e18 is 1 token per second. | ### restartAndDeposit Restarts the stream with the provided rate per second, and makes a deposit. Emits a [RestartFlowStream](/reference/flow/contracts/interfaces/interface.ISablierFlow#restartflowstream), {Transfer}, [DepositFlowStream](/reference/flow/contracts/interfaces/interface.ISablierFlow#depositflowstream) and {MetadataUpdate} event. Notes: - Refer to the notes in [restart](/reference/flow/contracts/interfaces/interface.ISablierFlow#restart) and [deposit](/reference/flow/contracts/interfaces/interface.ISablierFlow#deposit). Requirements: - `amount` must be greater than zero. - Refer to the requirements in [restart](/reference/flow/contracts/interfaces/interface.ISablierFlow#restart). ```solidity function restartAndDeposit(uint256 streamId, UD21x18 ratePerSecond, uint128 amount) external payable; ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to restart. | | `ratePerSecond` | `UD21x18` | The amount by which the debt is increasing every second, denoted as a fixed-point number where 1e18 is 1 token per second. | | `amount` | `uint128` | The deposit amount, denoted in token's decimals. | ### setNativeToken Sets the native token address. Once set, it cannot be changed. For more information, see the documentation for {nativeToken}. Emits a [SetNativeToken](/reference/flow/contracts/interfaces/interface.ISablierFlow#setnativetoken) event. Requirements: - `msg.sender` must be the comptroller contract. - `newNativeToken` must not be the zero address. - The native token must not be already set. ```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/flow/contracts/interfaces/interface.ISablierFlow#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(IFlowNFTDescriptor newNFTDescriptor) external; ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `newNFTDescriptor` | `IFlowNFTDescriptor` | The address of the new NFT descriptor contract. | ### transferFromPayable Wrapper for {IERC721.transferFrom} with the `payable` specifier so that it can be called in conjunction with other functions using {IBatch.batch}. Requirements: - Refer to the requirements of `transferFrom` in: [https://github.com/OpenZeppelin/openzeppelin-contracts/blob/e4f70216d759d8e6a64144a9e1f7bbeed78e7079/contracts/token/ERC721/IERC721.sol#L75-L91](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/e4f70216d759d8e6a64144a9e1f7bbeed78e7079/contracts/token/ERC721/IERC721.sol#L75-L91). ```solidity function transferFromPayable(address from, address to, uint256 streamId) external payable; ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `from` | `address` | The owner of the stream ID. | | `to` | `address` | The address of the new owner of the stream ID. | | `streamId` | `uint256` | The ID of the stream NFT to transfer. | ### transferTokens A helper to transfer ERC-20 tokens from the caller to the provided address. Useful for paying one-time bonuses. Emits a {Transfer} event. Requirements: - `msg.sender` must have approved this contract to spend at least `amount` tokens. ```solidity function transferTokens(IERC20 token, address to, uint128 amount) external payable; ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `token` | `IERC20` | The contract address of the ERC-20 token to be transferred. | | `to` | `address` | The address receiving the tokens. | | `amount` | `uint128` | The amount of tokens to transfer, denoted in token's decimals. | ### void Voids a stream. Emits a [VoidFlowStream](/reference/flow/contracts/interfaces/interface.ISablierFlow#voidflowstream) and {MetadataUpdate} event. Notes: - It sets snapshot time to the `block.timestamp`. - Voiding an insolvent stream sets the snapshot debt to the stream's balance making the uncovered debt to become zero. - Voiding a solvent stream updates the snapshot debt by adding up ongoing debt. - It sets the rate per second to zero. - A voided stream cannot be restarted. Requirements: - Must not be delegate called. - `streamId` must not reference a null or a voided stream. - `msg.sender` must either be the stream's sender, recipient or an approved third party. ```solidity function void(uint256 streamId) external payable; ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to void. | ### withdraw Withdraws the provided `amount` to the provided `to` address. Emits a {Transfer}, [WithdrawFromFlowStream](/reference/flow/contracts/interfaces/interface.ISablierFlow#withdrawfromflowstream) and {MetadataUpdate} event. Notes: - It sets the snapshot time to the `block.timestamp` if `amount` is greater than snapshot debt. Requirements: - Must not be delegate called. - `streamId` must not reference a null stream. - `to` must not be the zero address. - `to` must be the recipient if `msg.sender` is not the stream's recipient or an approved third party. - `amount` must be greater than zero and must not exceed the withdrawable amount. - `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 token's decimals. | ### withdrawMax Withdraws the entire withdrawable amount to the provided `to` address. Emits a {Transfer}, [WithdrawFromFlowStream](/reference/flow/contracts/interfaces/interface.ISablierFlow#withdrawfromflowstream) and {MetadataUpdate} event. Notes: - Refer to the notes in [withdraw](/reference/flow/contracts/interfaces/interface.ISablierFlow#withdraw). Requirements: - Refer to the requirements in [withdraw](/reference/flow/contracts/interfaces/interface.ISablierFlow#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 to the recipient, denoted in token's decimals. | ## Events ### AdjustFlowStream Emitted when the rate per second is updated by the sender. ```solidity event AdjustFlowStream( uint256 indexed streamId, uint256 totalDebt, UD21x18 oldRatePerSecond, UD21x18 newRatePerSecond ); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream. | | `totalDebt` | `uint256` | The total debt at the time of the update, denoted in token's decimals. | | `oldRatePerSecond` | `UD21x18` | The old rate per second, denoted as a fixed-point number where 1e18 is 1 token per second. | | `newRatePerSecond` | `UD21x18` | The new rate per second, denoted as a fixed-point number where 1e18 is 1 token per second. | ### CreateFlowStream Emitted when a Flow stream is created. ```solidity event CreateFlowStream( uint256 streamId, address creator, address indexed sender, address indexed recipient, UD21x18 ratePerSecond, uint40 snapshotTime, IERC20 indexed token, bool transferable ); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the newly created stream. | | `creator` | `address` | The address creating the stream. | | `sender` | `address` | The address streaming the tokens, which is able to adjust and pause the stream. | | `recipient` | `address` | The address receiving the tokens, as well as the NFT owner. | | `ratePerSecond` | `UD21x18` | The amount by which the debt is increasing every second, denoted as a fixed-point number where 1e18 is 1 token per second. | | `snapshotTime` | `uint40` | The timestamp when the stream begins accumulating debt. | | `token` | `IERC20` | The contract address of the ERC-20 token to be streamed. | | `transferable` | `bool` | Boolean indicating whether the stream NFT is transferable or not. | ### DepositFlowStream Emitted when a stream is funded. ```solidity event DepositFlowStream(uint256 indexed streamId, address indexed funder, uint128 amount); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream. | | `funder` | `address` | The address that made the deposit. | | `amount` | `uint128` | The amount of tokens deposited into the stream, denoted in token's decimals. | ### PauseFlowStream Emitted when a stream is paused by the sender. ```solidity event PauseFlowStream( uint256 indexed streamId, address indexed sender, address indexed recipient, uint256 totalDebt ); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream. | | `sender` | `address` | The stream's sender address. | | `recipient` | `address` | The stream's recipient address. | | `totalDebt` | `uint256` | The amount of tokens owed by the sender to the recipient, denoted in token's decimals. | ### RefundFromFlowStream Emitted when a sender is refunded from a stream. ```solidity event RefundFromFlowStream(uint256 indexed streamId, address indexed sender, uint128 amount); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream. | | `sender` | `address` | The stream's sender address. | | `amount` | `uint128` | The amount of tokens refunded to the sender, denoted in token's decimals. | ### RestartFlowStream Emitted when a stream is restarted by the sender. ```solidity event RestartFlowStream(uint256 indexed streamId, address indexed sender, UD21x18 ratePerSecond); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream. | | `sender` | `address` | The stream's sender address. | | `ratePerSecond` | `UD21x18` | The amount by which the debt is increasing every second, denoted as a fixed-point number where 1e18 is 1 token per second. | ### SetNativeToken Emitted when the native token address is set by the comptroller. ```solidity event SetNativeToken(ISablierComptroller indexed comptroller, address nativeToken); ``` ### SetNFTDescriptor Emitted when the comptroller sets a new NFT descriptor contract. ```solidity event SetNFTDescriptor( ISablierComptroller indexed comptroller, IFlowNFTDescriptor oldNFTDescriptor, IFlowNFTDescriptor newNFTDescriptor ); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `comptroller` | `ISablierComptroller` | The address of the current comptroller. | | `oldNFTDescriptor` | `IFlowNFTDescriptor` | The address of the old NFT descriptor contract. | | `newNFTDescriptor` | `IFlowNFTDescriptor` | The address of the new NFT descriptor contract. | ### VoidFlowStream Emitted when a stream is voided by the sender, recipient or an approved operator. ```solidity event VoidFlowStream( uint256 indexed streamId, address indexed sender, address indexed recipient, address caller, uint256 newTotalDebt, uint256 writtenOffDebt ); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream. | | `sender` | `address` | The stream's sender address. | | `recipient` | `address` | The stream's recipient address. | | `caller` | `address` | The address that performed the void, which can be the sender, recipient or an approved operator. | | `newTotalDebt` | `uint256` | The new total debt, denoted in token's decimals. | | `writtenOffDebt` | `uint256` | The amount of debt written off by the caller, denoted in token's decimals. | ### WithdrawFromFlowStream Emitted when tokens are withdrawn from a stream by a recipient or an approved operator. ```solidity event WithdrawFromFlowStream( uint256 indexed streamId, address indexed to, IERC20 indexed token, address caller, uint128 withdrawAmount ); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream. | | `to` | `address` | The address that received the withdrawn tokens. | | `token` | `IERC20` | The contract address of the ERC-20 token that was withdrawn. | | `caller` | `address` | The address that performed the withdrawal, which can be the recipient or an approved operator. | | `withdrawAmount` | `uint128` | The amount withdrawn to the recipient, denoted in token's decimals. | --- ## ISablierFlowState Source: https://docs.sablier.com/reference/flow/contracts/interfaces/interface.ISablierFlowState # ISablierFlowState [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/interfaces/ISablierFlowState.sol) **Title:** ISablierFlowState Contract with state variables (storage and constants) for the [SablierFlow](/reference/flow/contracts/contract.SablierFlow) 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. | ### getBalance Retrieves the balance of the stream, i.e. the total deposited amounts subtracted by the total withdrawn amounts, denoted in token's decimals. Reverts if `streamId` references a null stream. ```solidity function getBalance(uint256 streamId) external view returns (uint128 balance); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### getRatePerSecond Retrieves the rate per second of the stream, denoted as a fixed-point number where 1e18 is 1 token per second. Reverts if `streamId` references a null stream. ```solidity function getRatePerSecond(uint256 streamId) external view returns (UD21x18 ratePerSecond); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to make the query for. | ### 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. | ### getSnapshotDebtScaled Retrieves the snapshot debt of the stream, denoted as a fixed-point number where 1e18 is 1 token. Reverts if `streamId` references a null stream. ```solidity function getSnapshotDebtScaled(uint256 streamId) external view returns (uint256 snapshotDebtScaled); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### getSnapshotTime Retrieves the snapshot time of the stream, which is a Unix timestamp. Reverts if `streamId` references a null stream. ```solidity function getSnapshotTime(uint256 streamId) external view returns (uint40 snapshotTime); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to make the query for. | ### getStream Retrieves the stream entity. Reverts if `streamId` references a null stream. ```solidity function getStream(uint256 streamId) external view returns (Flow.Stream memory stream); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The stream ID for the query. | ### getToken Retrieves the token of the stream. Reverts if `streamId` references a null stream. ```solidity function getToken(uint256 streamId) external view returns (IERC20 token); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to make the query for. | ### getTokenDecimals Retrieves the token decimals of the stream. Reverts if `streamId` references a null stream. ```solidity function getTokenDecimals(uint256 streamId) external view returns (uint8 tokenDecimals); ``` **Parameters** | Name | Type | Description | | --- | --- | --- | | `streamId` | `uint256` | The ID of the stream to make the query for. | ### 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 is transferable. 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. | ### isVoided Retrieves a flag indicating whether the stream is voided. Reverts if `streamId` references a null stream. ```solidity function isVoided(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. ```solidity function nextStreamId() external view returns (uint256); ``` **Returns** | Name | Type | Description | | --- | --- | --- | | `` | `uint256` | The next stream ID. | ### nftDescriptor Contract that generates the non-fungible token URI. ```solidity function nftDescriptor() external view returns (IFlowNFTDescriptor); ``` --- ## Errors Source: https://docs.sablier.com/reference/flow/contracts/libraries/library.Errors # Errors [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/libraries/Errors.sol) **Title:** Errors Library with custom errors used across the Flow contract. ## Errors ### SablierFlow\_CreateNativeToken Thrown when trying to create a stream with the native token. ```solidity error SablierFlow_CreateNativeToken(address nativeToken); ``` ### SablierFlow\_CreateRatePerSecondZero Thrown when trying to create a pending stream with zero rate per second. ```solidity error SablierFlow_CreateRatePerSecondZero(); ``` ### SablierFlow\_DepositAmountZero Thrown when trying to create a stream with a zero deposit amount. ```solidity error SablierFlow_DepositAmountZero(uint256 streamId); ``` ### SablierFlow\_InsufficientFeePayment Thrown when trying to withdraw with a fee amount less than the minimum fee. ```solidity error SablierFlow_InsufficientFeePayment(uint256 feePaid, uint256 minFeeWei); ``` ### SablierFlow\_InvalidCalculation Thrown when an unexpected error occurs during the calculation of an amount. ```solidity error SablierFlow_InvalidCalculation(uint256 streamId, uint128 availableAmount, uint128 amount); ``` ### SablierFlow\_InvalidTokenDecimals Thrown when trying to create a stream with a token with decimals greater than 18. ```solidity error SablierFlow_InvalidTokenDecimals(address token); ``` ### SablierFlow\_NativeTokenAlreadySet Thrown when trying to set the native token address when it is already set. ```solidity error SablierFlow_NativeTokenAlreadySet(address nativeToken); ``` ### SablierFlow\_NativeTokenZeroAddress Thrown when trying to set zero address as native token. ```solidity error SablierFlow_NativeTokenZeroAddress(); ``` ### SablierFlow\_NewRatePerSecondZero Thrown when trying to adjust the rate per second to zero. ```solidity error SablierFlow_NewRatePerSecondZero(uint256 streamId); ``` ### SablierFlow\_NotStreamRecipient Thrown when the recipient address does not match the stream's recipient. ```solidity error SablierFlow_NotStreamRecipient(address recipient, address streamRecipient); ``` ### SablierFlow\_NotStreamSender Thrown when the sender address does not match the stream's sender. ```solidity error SablierFlow_NotStreamSender(address sender, address streamSender); ``` ### SablierFlow\_NotTransferable Thrown when trying to transfer Stream NFT when transferability is disabled. ```solidity error SablierFlow_NotTransferable(uint256 streamId); ``` ### SablierFlow\_Overdraw Thrown when trying to withdraw an amount greater than the withdrawable amount. ```solidity error SablierFlow_Overdraw(uint256 streamId, uint128 amount, uint128 withdrawableAmount); ``` ### SablierFlow\_RatePerSecondNotDifferent Thrown when trying to change the rate per second with the same rate per second. ```solidity error SablierFlow_RatePerSecondNotDifferent(uint256 streamId, UD21x18 ratePerSecond); ``` ### SablierFlow\_RefundAmountZero Thrown when trying to refund zero tokens from a stream. ```solidity error SablierFlow_RefundAmountZero(uint256 streamId); ``` ### SablierFlow\_RefundOverflow Thrown when trying to refund an amount greater than the refundable amount. ```solidity error SablierFlow_RefundOverflow(uint256 streamId, uint128 refundAmount, uint128 refundableAmount); ``` ### SablierFlow\_SenderZeroAddress Thrown when trying to create a stream with the sender as the zero address. ```solidity error SablierFlow_SenderZeroAddress(); ``` ### SablierFlow\_StreamBalanceZero Thrown when trying to get depletion time of a stream with zero balance. ```solidity error SablierFlow_StreamBalanceZero(uint256 streamId); ``` ### SablierFlow\_StreamNotPaused Thrown when trying to perform a disallowed action on a non-paused stream. ```solidity error SablierFlow_StreamNotPaused(uint256 streamId); ``` ### SablierFlow\_StreamPending Thrown when trying to perform a disallowed action on a pending stream. ```solidity error SablierFlow_StreamPending(uint256 streamId, uint40 snapshotTime); ``` ### SablierFlow\_Unauthorized Thrown when `msg.sender` lacks authorization to perform an action. ```solidity error SablierFlow_Unauthorized(uint256 streamId, address caller); ``` ### SablierFlow\_WithdrawalAddressNotRecipient Thrown when trying to withdraw to an address other than the recipient's. ```solidity error SablierFlow_WithdrawalAddressNotRecipient(uint256 streamId, address caller, address to); ``` ### SablierFlow\_WithdrawAmountZero Thrown when trying to withdraw zero tokens from a stream. ```solidity error SablierFlow_WithdrawAmountZero(uint256 streamId); ``` ### SablierFlow\_WithdrawToZeroAddress Thrown when trying to withdraw to the zero address. ```solidity error SablierFlow_WithdrawToZeroAddress(uint256 streamId); ``` ### SablierFlowState\_Null Thrown when the ID references a null stream. ```solidity error SablierFlowState_Null(uint256 streamId); ``` ### SablierFlowState\_StreamPaused Thrown when trying to perform a disallowed action on a paused stream. ```solidity error SablierFlowState_StreamPaused(uint256 streamId); ``` ### SablierFlowState\_StreamVoided Thrown when trying to perform a disallowed action on a voided stream. ```solidity error SablierFlowState_StreamVoided(uint256 streamId); ``` ### SablierFlowState\_Unauthorized Thrown when `msg.sender` lacks authorization to perform an action. ```solidity error SablierFlowState_Unauthorized(uint256 streamId, address caller); ``` --- ## FlowHelpers Source: https://docs.sablier.com/reference/flow/contracts/libraries/library.FlowHelpers # FlowHelpers [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/libraries/FlowHelpers.sol) **Title:** FlowHelpers Library with helper functions in [SablierFlow](/reference/flow/contracts/contract.SablierFlow) contract. ## Functions ### descaleAmount Descales the provided `amount` from 18 decimals fixed-point number to token's decimals number. If `decimals` exceeds 18, it will cause an underflow. ```solidity function descaleAmount(uint256 amount, uint8 decimals) internal pure returns (uint256); ``` ### scaleAmount Scales the provided `amount` from token's decimals number to 18 decimals fixed-point number. If `decimals` exceeds 18, it will cause an underflow. If `amount` exceeds max value of `uint128`, the result may overflow `uint256`. ```solidity function scaleAmount(uint256 amount, uint8 decimals) internal pure returns (uint256); ``` --- ## Flow Source: https://docs.sablier.com/reference/flow/contracts/types/library.Flow # Flow [Git Source](https://github.com/sablier-labs/evm-monorepo/blob/8b6823c019ff7556ac9ad24cbb5ac62821854d2f/src/types/DataTypes.sol) ## Structs ### Stream Struct representing Flow streams. The fields are arranged like this to save gas via tight variable packing. ```solidity struct Stream { // slot 0 uint128 balance; UD21x18 ratePerSecond; // slot 1 address sender; uint40 snapshotTime; bool isStream; bool isTransferable; bool isVoided; // slot 2 IERC20 token; uint8 tokenDecimals; // slot 3 uint256 snapshotDebtScaled; } ``` **Properties** | Name | Type | Description | | --- | --- | --- | | `balance` | `uint128` | The amount of tokens that are currently available in the stream, denoted in the token's decimals. This is the sum of deposited amounts minus the sum of withdrawn amounts. | | `ratePerSecond` | `UD21x18` | The payment rate per second, denoted as a fixed-point number where 1e18 is 1 token per second. For example, to stream 1000 tokens per week, this parameter would have the value $(1000 * 10^18) / (7 days in seconds)$. | | `sender` | `address` | The address streaming the tokens, with the ability to pause the stream. | | `snapshotTime` | `uint40` | The Unix timestamp used for the ongoing debt calculation. | | `isStream` | `bool` | Boolean indicating if the struct entity exists. | | `isTransferable` | `bool` | Boolean indicating if the stream NFT is transferable. | | `isVoided` | `bool` | Boolean indicating if the stream is voided. Voiding any stream is non-reversible and it cannot be restarted. Voiding an insolvent stream sets its uncovered debt to zero. | | `token` | `IERC20` | The contract address of the ERC-20 token to stream. | | `tokenDecimals` | `uint8` | The decimals of the ERC-20 token to stream. | | `snapshotDebtScaled` | `uint256` | The amount of tokens that the sender owed to the recipient at snapshot time, denoted as a 18-decimals fixed-point number. This, along with the ongoing debt, can be used to calculate the total debt at any given point in time. | ## Enums ### Status Enum representing the different statuses of a stream. **Notes:** - value0: PENDING Stream scheduled to start in the future. - value1: STREAMING\_SOLVENT Streaming stream with no uncovered debt. - value2: STREAMING\_INSOLVENT Streaming stream with uncovered debt. - value3: PAUSED\_SOLVENT Paused stream with no uncovered debt. - value4: PAUSED\_INSOLVENT Paused stream with uncovered debt. - value5: VOIDED Paused stream with no uncovered debt, which cannot be restarted. ```solidity enum Status { PENDING, STREAMING_SOLVENT, STREAMING_INSOLVENT, PAUSED_SOLVENT, PAUSED_INSOLVENT, VOIDED } ``` --- ## Diagrams Source: https://docs.sablier.com/reference/flow/diagrams # Diagrams ## Token Flows The following three functions lead to tokens flow in and out of a stream: ### Deposit Anyone can deposit into a stream. ### Refund Only sender can refund from the stream that he created. ### Withdraw Anyone can call withdraw on a stream as long as `to` address matches the recipient. If recipient/operator is calling withdraw on a stream, they can choose to withdraw to any address. ## Abbreviations | Abbreviation | Full name | Description | | --- | --- | --- | | bal | Stream balance | Balance of the stream | | cd | Covered debt | Portion of the total debt covered by the stream balance | | elt | Elapsed time | Time elapsed in seconds since the last snapshot | | od | Ongoing debt | Debt accumulated since the last snapshot | | now | Current time | Same as `block.timestamp` | | rps | Rate per second | Rate at which tokens are streamed per second | | sd | Snapshot debt | Debt accumulated until the last snapshot | | st | Snapshot time | Time of the last snapshot | | td | Total debt | Sum of sd and od, also same as sum of cd and ud | | ud | Uncovered debt | Portion of the total debt not covered by the stream balance | ## Storage Layout Flow is a singleton contract that stores all streams created by that contract's users. The following diagrams provide insight into the storage layout of each stream. To see the full list of storage variables, check out [this reference](/reference/flow/contracts/types/library.Flow#structs). ## Debts ### Covered debt ### Ongoing Debt ### Uncovered Debt ### Total Debt ## Refundable Amount ```mermaid sequenceDiagram actor Anyone Anyone ->> Flow: deposit() Anyone -->> Flow: Transfer tokens ``` ```mermaid sequenceDiagram actor Sender Sender ->> Flow: refund() Flow -->> Sender: Transfer unstreamed tokens ``` ```mermaid sequenceDiagram actor Anyone Anyone ->> Flow: withdraw() activate Flow Create actor Recipient Flow -->> Recipient: Transfer streamed tokens deactivate Flow Recipient ->> Flow: withdraw() activate Flow Create actor toAddress Flow -->> toAddress: Transfer streamed tokens deactivate Flow ``` ```mermaid flowchart TD; F["Flow contract"]; S0[(Stream 1)]; b0([bal]) r0([rps]) sd0([sd]) st0([st]) F --> S0; S0 --> b0; S0 --> r0; S0 --> sd0; S0 --> st0; S1[(Stream 2)]; b1([bal]) r1([rps]) sd1([sd]) st1([st]) F --> S1; S1 --> b1; S1 --> r1; S1 --> sd1; S1 --> st1; ``` ```mermaid flowchart TD di0{ }:::blue0 di1{ }:::blue0 cd([cd]) res_0([0 ]) res_bal([bal]) res_sum([td]) cd --> di0 di0 -- "bal = 0" --> res_0 di0 -- "bal > 0" --> di1 di1 -- "ud > 0" --> res_bal di1 -- "ud = 0" --> res_sum ``` ```mermaid flowchart TD rca([od]) di0{ } di1{ } res_00([0 ]) res_01([0 ]) res_rca(["rps ⋅ elt"]) rca --> di0 di0 -- "rps > 0" --> di1 di0 -- "rps == 0" --> res_00 di1 -- "now <= st" --> res_01 di1 -- "now > st" --> res_rca ``` ```mermaid flowchart TD di0{ }:::red1 sd([ud]) res_sd(["td - bal"]) res_zero([0]) sd --> di0 di0 -- "bal < td" --> res_sd di0 -- "bal >= td" --> res_zero ``` ```mermaid flowchart TD rca([td]) di0{ } res_00([sd ]) res_01(["sd + od"]) rca --> di0 di0 -- "rps == 0" --> res_00 di0 -- "rps > 0" --> res_01 ``` ```mermaid flowchart TD ra([Refundable Amount]) res_ra([bal - cd]) ra --> res_ra ``` --- ## Contact Us Source: https://docs.sablier.com/support/contact # Contact Us If you need help with the Sablier protocols, the apps, or anything else, you can reach us through any of the following channels. ## Email For customer support, partnership inquiries, or anything that doesn't fit a public channel, email us at [contact@sablier.com](mailto:contact@sablier.com). We typically reply within one business day. ## Discord For real-time questions and community discussion, join our [Discord server](https://discord.sablier.com). The team and community members are active there and happy to help. - `#support` β€” for help with the apps and protocols - `#dev` β€” for integration and smart contract questions ## GitHub If you've found a bug or want to request a feature, open an issue in the relevant repository under the [`sablier-labs`](https://github.com/sablier-labs) organization on GitHub. --- ## FAQ Source: https://docs.sablier.com/support/faq # FAQ ### Where can I access the Sablier Protocol? You can access Sablier through the following web interfaces: - [app.sablier.com](https://app.sablier.com) - [app.safe.global](https://app.safe.global/share/safe-app?appUrl=https%3A%2F%2Fapp.sablier.com%2F&chain=arb1) (if you are using a Safe multisig wallet) ### What is token streaming? An alternative wording, coined by Andreas Antonopoulos in 2017. Just like you can stream movies on Netflix or music on Spotify, so you can stream tokens by the second on Sablier. ### How does streaming work on Sablier Lockup? Imagine Alice wants to stream 3000 DAI to Bob during the whole month of January. 1. Alice deposits the 3000 DAI in Lockup before Jan 1, setting the end time to Feb 1. 2. Bob's allocation of the DAI deposit increases every second beginning Jan 1. 3. On Jan 10, Bob will have earned approximately 1000 DAI. He can send a transaction to withdraw the tokens. 4. If at any point during January Alice wishes to get back her tokens, she can cancel the stream and recover what has not been streamed yet. ### How does Lockup calculate the payment rate? Dividing the deposit amount by the difference between the stop time and the start time gives us a payment rate per second. Lockup uses this rate to transfer a small portion of tokens from the sender to the recipient once every second. For instance, if the payment rate was 0.01 DAI per second, the recipient would receive: $0.01 * 60 = 0.6$ DAI / minute, $0.01 * 60 * 60 = 36$ DAI / hour, $0.01 * 60 * 60 * 24 = 864$ DAI / day ### How does streaming work on Sablier Flow? Imagine Alice wants to stream 3000 DAI on a monthly basis to Bob. 1. Alice creates a stream on Flow with a rate of 3000 DAI per month. 2. Alice deposits 200 DAI in Flow. 3. On Jan 10, Bob is owed 1000 DAI, but there is only 200 DAI in the stream. So he can only withdraw 200 DAI. 4. Alice deposits another 2800 DAI in Flow, and Bob can now withdraw the remaining 800 DAI. 5. On Feb 1, Bob is able to withdraw 2800 DAI. 6. The stream will continue indefinitely until it is paused (by Alice) or voided (by either Alice or Bob). ### How can I create a stream? You will need an EVM wallet (e.g. [Metamask](https://metamask.io), [Rainbow](https://rainbow.me), etc.), some ETH (or the network's token to pay gas fees) and an ERC-20 token like DAI. Then, choose your favorite interface for accessing the Sablier Protocol (such as [app.sablier.com](https://app.sablier.com)) and fill in the recipient's address, the deposit amount and the total duration. Alternatively, you can see [here](/guides/lockup/etherscan) how to manually create a stream using [Etherscan](https://etherscan.io). ### Can I create a stream with non-transferable tokens? Yes, it is possible to deposit non-transferable tokens in Sablier, but you need to whitelist the following two contracts: 1. `SablierLockup` 2. `SablierBatchLockup` You can get the addresses of the above contracts on the [Lockup Deployments](/guides/lockup/deployments) page. Pick the chain on which your token is deployed. A relevant diagram can be found [here](/reference/lockup/diagrams). ### Where are the tokens held? In the Sablier smart contracts. You can verify this assertion by inspecting Etherscan or any other blockchain explorer. ### How can recipients access their tokens? As the tokens are being streamed at the smart contract level, recipients can consider Sablier their real-time wallet for digital currency. To make withdrawals, recipients can: 1. Use a web interface (e.g. [app.sablier.com](https://app.sablier.com)). 2. Call the contract directly on a blockchain explorer. ### Can I cancel streams? Yes, both as a sender and a recipient. If the stream is canceled before the start time, the whole deposit amount is returned in full to you. If the stream is canceled while the stream is active, the smart contracts calculate how much has been streamed, transfer that to the recipient and return the remainder to you. If the stream is canceled after the stream has stopped, the smart contracts transfers all the remaining funds (if any) to the recipient. ### Can I modify the streaming rate? On Lockup, once a stream is created, it is set in stone on the Ethereum blockchain. Whereas on Flow, the streaming rate per second can be adjusted anytime by the sender. ### How are vested airdrops different from a batch of normal streams? Creating streams through the form (manual or CSV) will immediately start the vesting period. You click to create the transaction, pay for the gas, and you start all the streams for your recipients. Due to how block gas limits work, you can only fit a limited number of streams (usually 60) in a single block before running out of space. This is great for small distributions like paying your contractors or a set of investors. Meanwhile, vested airdrops leverage a "lazy creation" of streams. You deploy a contract that stores a list of recipients and the streams they are entitled to claim. Every recipient will manually claim their allocation by creating a stream. This allows for millions of recipients in your distribution campaign, with each recipient triggering the stream creation one after the other (as opposed to all at once). | Feature | Groups | Airdrops | | --- | --- | --- | | Maximum number of streams | ~280 (different limit per chain) | Millions | | Gas to create streams | Sender paying | Each recipient paying | | Gas to deploy Airdrop contract | N/A | Sender paying | | Streams show up onchain | Immediately | Lazily | | Good for | Investors, Employees | Large communities | ### What is real-time finance? A term coined by us to emphasize the wide-ranging use cases for the Sablier Protocol. We like to think about work as an attempt to rethink the way trust is established in financial contracts. --- ## Ethereum Source: https://docs.sablier.com/support/how-to # Ethereum ## Airdrops ### How to Create a Sablier Vested Airdrop Campaign [YouTube video player](https://www.youtube.com/watch?v=xDTLGGewjNU) ### How to create a Sablier Instant Airdrop Campaign [YouTube video player](https://www.youtube.com/watch?v=cgEFYDtL3RU) ### How to Claim a Sablier Vested Airdrop [YouTube video player](https://www.youtube.com/watch?v=vZTDZV8VaaA) ### How to Claim a Sablier Instant Airdrop [YouTube video player](https://www.youtube.com/watch?v=vTwqteJ9oAI) ## Vesting ### How to Withdraw From a Vesting Stream [YouTube video player](https://www.youtube.com/watch?v=XIcYied15BM) ### How to Create a Vesting Stream [YouTube video player](https://www.youtube.com/watch?v=0rgYaZhA0is) ### How to Batch-Cancel Streams [YouTube video player](https://www.youtube.com/watch?v=fRlUAMx2YFA) ### How to Create Vesting Streams Using the CSV Upload Feature [YouTube video player](https://www.youtube.com/watch?v=oqjx2muvcX4) ### How to Create a Vesting Stream Using a Safe Multisig Wallet [YouTube video player](https://www.youtube.com/watch?v=z94OHVk-BXU) ### How to Trade Vesting Streams on OpenSea [YouTube video player](https://www.youtube.com/watch?v=S_TTU3VyKOU) ### Distribution Shapes & Settings [YouTube video player](https://www.youtube.com/watch?v=37CZzlnOSRI) ## Payments ### How to Withdraw From Payment Streams [YouTube video player](https://www.youtube.com/watch?v=u8cGre6WW3E) ### How to Create Payment Streams [YouTube video player](https://www.youtube.com/watch?v=z8LFPedx8Kk) ### How to Pay Your Team Automatically with Sablier [YouTube video player](https://www.youtube.com/watch?v=ffqGPQz8oQY) ### How to Top Up a Sablier Payment Stream [YouTube video player](https://www.youtube.com/watch?v=lVSuWR-xC98) ### How to Manage Multiple Payment Streams [YouTube video player](https://www.youtube.com/watch?v=uzZH9UUjtZI) ## Others ### How to Create a zkSync Proposal Involving a Vesting Stream [YouTube video player](https://www.youtube.com/watch?v=3mWXLR8qtOs) ### Creating a Vesting Stream Using Fireblocks [YouTube video player](https://www.youtube.com/watch?v=40UZyctH5tY) ### Withdrawing Funds From Vesting Stream Using Fireblocks [YouTube video player](https://www.youtube.com/watch?v=t92yJmT3HkY) ### Launching and Vesting Tokens Made Easy with CreateMyToken and Sablier [YouTube video player](https://www.youtube.com/watch?v=izl9mlsXhpI) # Solana ### How to Claim a Solana-Based Sablier Airdrop [YouTube video player](https://www.youtube.com/watch?v=hKQ3eXdVk7k) ### How to Withdraw From Solana-Based Sablier Streams [YouTube video player](https://www.youtube.com/watch?v=xL4_yncbHYk) --- ## Technical Guides Source: https://docs.sablier.com/support/technical-guides # Technical Guides ## Guides for Apps Hands-on guides for app-specific features: - [CSV Support](/apps/guides/csv-support) (for streams and airdrops) - [URL Schemes](/apps/guides/url-schemes) ## Guides for Contracts - [Interactions with Etherscan](/guides/lockup/etherscan) - [Lockup Stream Management](/guides/lockup/examples/stream-management/withdraw) - And more... ## Guides for Indexers See the dedicated [API guides](/api/overview). - [Getting Started](/api/getting-started) - [Streams Indexer](/api/streams/indexers) ---