From ed052d2e3f40c4d6db038c9d978336d60159ccf2 Mon Sep 17 00:00:00 2001 From: Andrei Vlad Birgaoanu <99738872+andreivladbrg@users.noreply.github.com> Date: Wed, 20 Dec 2023 20:07:53 +0200 Subject: [PATCH] Update docs for V2.1 (#115) * feat: add transferable documentation feat: add parameters tables in Types section refactor: update core accordingly to 2.1 changes perf: overall improve hyperlinks fix: use release branch to cite from github * feat: add Merkle Streamer documentation in periphery feat: remove proxy documentation in periphery * feat: add LOCKUP * feat: include in sender in the withdraw multiple acces control * feat: remove sender hook guides * feat: add batch create streams section feat: remove proxy architecture section feat: add more snippets refactor: remove ERC20Spets snippet refactor: use constants in guides * refactor: update overview to not reference proxy system feat: add note in Flash Loans * feat: add a new page for older deployments feat: reference the old deployment page in the latest version * perf: improve wording in older deployments page * feat: reference examples in stream types section feat: remove outdated contracts and interfaces refactor: use yarn to install sablier Node.js packages chore: correct typos * feat: remove sender hook in protocol concepts * fix: correct reference hyperlinks perf: mention ISablierV2MerkleStreamer documentation chore: remove unnecessary word * feat: add airstream campaigns page in concepts feat: remove flashloans page in concepts feat: add diagram for airstream campaign * feat: add diagram image * chore: polish airstream campaign page * refactor: update sender/recipient permissions * refactor: update airstream concept page * perf: polish airstream concept page perf: improve airstream diagram refactore: remove png diagram from technical reference * refactor: update SablierV2NFTDescriptor contract * refactor: segregate v2.0 and v2.1 deployments feat: add v2.1 deployment addresses for Sepolia * feat: add v2.1 deployments * refactor: older deployments * feat: add Mainnet Periphery * fix: use correct hyperlink for aribtrum sepolia contracts * refactor: remove Avalanche addresses * refactor: update v2.1 deployment addresses * fix: correct comptroller address on arbitrum sepolia * refactor: move isWarm and isCold to SablierV2Lockup * refactor: use yarn instead of pnpm in local-environment * refactor: use new deployed addresses in guides * refactor: reference blob/release instead of tree/release * fix: correct arbitrum one dynamic address * refactor: remove proxy and permit paragraphs from frontend guide page --------- Co-authored-by: Paul Razvan Berg Co-authored-by: maxdesalle --- docs/apps/02-features.mdx | 16 +- docs/concepts/protocol/02-stream-types.mdx | 7 + docs/concepts/protocol/07-airstreams.mdx | 79 ++ docs/concepts/protocol/07-flash-loans.mdx | 55 -- docs/concepts/protocol/08-hooks.md | 18 +- docs/contracts/v2/01-overview.md | 4 +- docs/contracts/v2/02-deployments.md | 251 ++---- docs/contracts/v2/deployments/_category_.json | 5 + docs/contracts/v2/deployments/v2.0.md | 300 +++++++ .../v2/guides/01-local-environment.md | 10 +- docs/contracts/v2/guides/05-hooks.md | 14 - docs/contracts/v2/guides/06-flash-loans.md | 6 + docs/contracts/v2/guides/07-frontend.md | 16 - docs/contracts/v2/guides/09-etherscan.md | 2 +- .../01-batch-linear-streams.mdx | 132 +++ .../02-batch-dynamic-streams.mdx | 153 ++++ .../batch-create-streams/_category_.json | 8 + .../guides/create-stream/01-lockup-linear.mdx | 54 +- .../create-stream/02-lockup-dynamic.mdx | 62 +- .../guides/proxy-architecture/01-overview.mdx | 52 -- .../guides/proxy-architecture/02-deploy.mdx | 113 --- .../proxy-architecture/03-batch-stream.mdx | 288 ------- .../proxy-architecture/04-single-stream.mdx | 34 - .../guides/proxy-architecture/_category_.json | 5 - .../guides/stream-management/_category_.json | 2 +- docs/contracts/v2/reference/01-overview.md | 40 +- docs/contracts/v2/reference/02-diagrams.md | 140 +--- .../v2/reference/05-access-control.md | 27 +- .../core/abstracts/abstract.Adminable.md | 2 +- .../core/abstracts/abstract.NoDelegateCall.md | 8 +- .../core/abstracts/abstract.SablierV2Base.md | 2 +- .../abstracts/abstract.SablierV2FlashLoan.md | 2 +- .../abstracts/abstract.SablierV2Lockup.md | 107 ++- .../core/contract.SablierV2Comptroller.md | 2 +- .../core/contract.SablierV2LockupDynamic.md | 62 +- .../core/contract.SablierV2LockupLinear.md | 65 +- .../core/contract.SablierV2NFTDescriptor.md | 10 +- .../interface.IERC3156FlashBorrower.md | 2 +- .../erc3156/interface.IERC3156FlashLender.md | 2 +- .../interface.ISablierV2LockupRecipient.md | 2 +- .../hooks/interface.ISablierV2LockupSender.md | 37 - .../core/interfaces/interface.IAdminable.md | 2 +- .../interfaces/interface.ISablierV2Base.md | 2 +- .../interface.ISablierV2Comptroller.md | 2 +- .../interfaces/interface.ISablierV2Lockup.md | 30 +- .../interface.ISablierV2LockupDynamic.md | 1 + .../interface.ISablierV2LockupLinear.md | 1 + .../interface.ISablierV2NftDescriptor.md | 2 +- .../core/libraries/library.Errors.md | 10 +- .../core/libraries/library.Helpers.md | 2 +- .../core/libraries/library.NFTSVG.md | 12 +- .../core/libraries/library.SVGElements.md | 4 +- .../v2/reference/core/types/library.Lockup.md | 2 +- .../core/types/library.LockupDynamic.md | 64 +- .../core/types/library.LockupLinear.md | 47 +- .../v2/reference/core/types/struct.Broker.md | 2 +- .../abstracts/abstract.OnlyDelegateCall.md | 46 - .../abstract.SablierV2MerkleStreamer.md | 137 +++ .../periphery/contract.SablierV2Archive.md | 69 -- .../periphery/contract.SablierV2Batch.md | 161 ++++ ...contract.SablierV2MerkleStreamerFactory.md | 51 ++ .../contract.SablierV2MerkleStreamerLL.md | 78 ++ .../contract.SablierV2ProxyPlugin.md | 50 -- .../contract.SablierV2ProxyTarget.md | 790 ------------------ .../interfaces/interface.ISablierV2Archive.md | 73 -- .../interfaces/interface.ISablierV2Batch.md | 125 +++ .../interface.ISablierV2MerkleStreamer.md | 118 +++ ...terface.ISablierV2MerkleStreamerFactory.md | 70 ++ .../interface.ISablierV2MerkleStreamerLL.md | 58 ++ .../interface.ISablierV2ProxyPlugin.md | 18 - .../interface.ISablierV2ProxyTarget.md | 662 --------------- .../interface.IWrappedNativeAsset.md | 33 - .../periphery/libraries/library.Errors.md | 48 +- .../periphery/types/library.Batch.md | 16 +- .../periphery/types/struct.Permit2Params.md | 14 - docs/snippets/BatchCommonSteps.mdx | 39 + docs/snippets/ConstantsComment.mdx | 5 + docs/snippets/ERC20Steps.mdx | 16 - docs/snippets/ParamRecipient.mdx | 2 +- static/img/diagram-airstream-campaign.png | Bin 0 -> 151320 bytes 80 files changed, 2093 insertions(+), 2965 deletions(-) create mode 100644 docs/concepts/protocol/07-airstreams.mdx delete mode 100644 docs/concepts/protocol/07-flash-loans.mdx create mode 100644 docs/contracts/v2/deployments/_category_.json create mode 100644 docs/contracts/v2/deployments/v2.0.md create mode 100644 docs/contracts/v2/guides/batch-create-streams/01-batch-linear-streams.mdx create mode 100644 docs/contracts/v2/guides/batch-create-streams/02-batch-dynamic-streams.mdx create mode 100644 docs/contracts/v2/guides/batch-create-streams/_category_.json delete mode 100644 docs/contracts/v2/guides/proxy-architecture/01-overview.mdx delete mode 100644 docs/contracts/v2/guides/proxy-architecture/02-deploy.mdx delete mode 100644 docs/contracts/v2/guides/proxy-architecture/03-batch-stream.mdx delete mode 100644 docs/contracts/v2/guides/proxy-architecture/04-single-stream.mdx delete mode 100644 docs/contracts/v2/guides/proxy-architecture/_category_.json delete mode 100644 docs/contracts/v2/reference/core/interfaces/hooks/interface.ISablierV2LockupSender.md delete mode 100644 docs/contracts/v2/reference/periphery/abstracts/abstract.OnlyDelegateCall.md create mode 100644 docs/contracts/v2/reference/periphery/abstracts/abstract.SablierV2MerkleStreamer.md delete mode 100644 docs/contracts/v2/reference/periphery/contract.SablierV2Archive.md create mode 100644 docs/contracts/v2/reference/periphery/contract.SablierV2Batch.md create mode 100644 docs/contracts/v2/reference/periphery/contract.SablierV2MerkleStreamerFactory.md create mode 100644 docs/contracts/v2/reference/periphery/contract.SablierV2MerkleStreamerLL.md delete mode 100644 docs/contracts/v2/reference/periphery/contract.SablierV2ProxyPlugin.md delete mode 100644 docs/contracts/v2/reference/periphery/contract.SablierV2ProxyTarget.md delete mode 100644 docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2Archive.md create mode 100644 docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2Batch.md create mode 100644 docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2MerkleStreamer.md create mode 100644 docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2MerkleStreamerFactory.md create mode 100644 docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2MerkleStreamerLL.md delete mode 100644 docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2ProxyPlugin.md delete mode 100644 docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2ProxyTarget.md delete mode 100644 docs/contracts/v2/reference/periphery/interfaces/interface.IWrappedNativeAsset.md delete mode 100644 docs/contracts/v2/reference/periphery/types/struct.Permit2Params.md create mode 100644 docs/snippets/BatchCommonSteps.mdx create mode 100644 docs/snippets/ConstantsComment.mdx delete mode 100644 docs/snippets/ERC20Steps.mdx create mode 100644 static/img/diagram-airstream-campaign.png diff --git a/docs/apps/02-features.mdx b/docs/apps/02-features.mdx index 1ccb4a29..657acafa 100644 --- a/docs/apps/02-features.mdx +++ b/docs/apps/02-features.mdx @@ -96,14 +96,14 @@ on your payouts. Remember, you can always be both a sender and a recipient. We've mapped the most important utilities from the V2 contracts into in-app features. Interact with streams that reference you as a key participant (e.g. sender, recipient) directly from the interfaces. -| Feature | Recipient | Sender | -| ---------------------- | :-------: | :----: | -| Create Streams | ❌ | ✅ | -| Renounce Cancelability | ❌ | ✅ | -| Cancel | ✅ | ✅ | -| Cancel Multiple | ✅ | ✅ | -| Transfer | ✅ | ❌ | -| Withdraw | ✅ | ✅ | +| Feature | Sender | Recipient | +| ---------------------- | :----: | :-------: | +| Create Streams | ✅ | ❌ | +| Renounce Cancelability | ✅ | ❌ | +| Cancel | ✅ | ❌ | +| Cancel Multiple | ✅ | ❌ | +| Transfer | ❌ | ✅ | +| Withdraw | ✅ | ✅ | ## Stream from your Safe multisig diff --git a/docs/concepts/protocol/02-stream-types.mdx b/docs/concepts/protocol/02-stream-types.mdx index 54d8e163..bdacc974 100644 --- a/docs/concepts/protocol/02-stream-types.mdx +++ b/docs/concepts/protocol/02-stream-types.mdx @@ -72,6 +72,13 @@ These streams are powered by a number of user-provided "segments", which we will important to note here is that with Lockup Dynamic, Sablier has evolved into a universal streaming engine, capable of supporting any custom streaming curve. +:::note + +If you are interested in learning how to programmatically create the curves shown below in Solidity, check out the +[examples](https://github.com/sablier-labs/examples/blob/main/v2/core/LockupDynamicCurvesCreator.sol) repository. + +::: + ### Exponential A fantastic use case for Lockup Dynamic is Exponential streams, a streaming model under which the recipient receives diff --git a/docs/concepts/protocol/07-airstreams.mdx b/docs/concepts/protocol/07-airstreams.mdx new file mode 100644 index 00000000..3870fe19 --- /dev/null +++ b/docs/concepts/protocol/07-airstreams.mdx @@ -0,0 +1,79 @@ +--- +id: "airstreams" +sidebar_position: 7 +title: "Airstreams" +--- + +:::note + +Please refer to the [airdrop section](/concepts/use-cases#airdrops) of our use-cases page for an explanation regarding +why streaming an airdrop makes sense. + +::: + +## Why Airstreams? + +The gist of Airstreams is that instead of airdropping the entirety of the tokens all at once, airdrop recipients receive +a fraction of the tokens every second through a token stream. + +The beauty of it is that airdrop recipients are forced to think long-term and keep the project’s future as their first +and foremost priority. They are forced to, because instead of receiving all the tokens at once, they receive them over +time in the gorgeous Airstreams [interface](app.sablier.com/airstreams). + +:::info + +An airstream can have claim period of a few days, months, years or even no expiration, you get to chose. You could, for +example, configure the airdrop of your new token to vest over years, but the recipients will receive the streaming +tokens only if a claim is made in the interval set. + +::: + +Airstreams not only create the right incentives for token holders, but also prevents them from dumping their tokens on +day one, as has been the case for many airdrops in the past. + +With Airstreams, airdrop recipients only receive a fraction of the tokens every second, only after they claim a stream +and have to withdraw the tokens which are getting streamed. + +## How It Works + +Thanks to our battle-tested token streaming and distribution protocol, you can quickly create Airstreams for thousands +of recipients in a few clicks using our interface. Recipients and their respective airdrop allocations can be added by +uploading a simple CSV spreadsheet to the [interface](app.sablier.com/airstreams). + +Our spreadsheet feature is the perfect fit for airstreams: it allows you to upload a CSV file with tens of thousands of +recipients and the interface will automatically create a new airstream for each of these recipients. + +Another great advantage, is that creating an Airstream campaign with thousands of recipients won't ruin you in terms of +gas fees, on the contrary. When launching a campaign, a contract is deployed, and Airstreams are only created when they +are claimed by the Airstream recipients. This is made possible by a system called _Merkle Distributor_, which is backed +by a Merkle Tree, a data structure used for efficiently summarizing and verifying the integrity of large sets of data. + +:::note + +The contract that implements an Airstream Campaign is called `MerkleStreamer`. For those interested in the technical +part, [click here](/contracts/v2/reference/periphery/contract.SablierV2MerkleStreamerLL). + +::: + +When you create an Airstream campaign, all you are really doing is deploying a contract that allows for the recipients +you put in to prove that they are eligible, and create a stream if they are. That's all it is. + +Additionally, you don't have to immediately fund the Airstream contract. You can first create the contract, and at a +later date fund it with the tokens to be streamed over. + +**This has three great implications:** + +- **Recipients pay for the gas fees themselves to create the stream**, when they claim (the claim action creates the + stream). Creating a campaign with thousands of recipients would be incredibly costly if the campaign creator had to + pay for all the gas fees. +- **You keep full control over unclaimed Airstream token allocations**. If a recipient doesn't claim their Airstream, + it's not created, and you remain in full control over that token allocation. + - This is the case only if the campaign itself has an expiration date. If the campaign has no expiration date, the + recipients can claim their Airstreams at any time in the future. +- **You don't have to fund the campaign directly in order to deploy it**. You can deploy the campaign contract now, and + fund it at a later date when you feel comfortable doing so. There is no rush. + +Once the Airstream campaign is launched, recipients who have claimed can withdraw the tokens which have already been +streamed at any time using our interface. The deployment page, the last step when launching an Airstream campaign. + +![](/img/diagram-airstream-campaign.png) diff --git a/docs/concepts/protocol/07-flash-loans.mdx b/docs/concepts/protocol/07-flash-loans.mdx deleted file mode 100644 index d5812954..00000000 --- a/docs/concepts/protocol/07-flash-loans.mdx +++ /dev/null @@ -1,55 +0,0 @@ ---- -draft: true -id: "flash-loans" -sidebar_position: 7 -title: "Flash Loans" ---- - -import { links } from "@site/src/constants"; - -# Brief Primer - -Flash loans are a unique feature of blockchain systems that allows users to borrow a large amount of cryptocurrency -(typically ERC-20 assets) without providing any collateral. These loans are designed to be taken and repaid within the -same blockchain transaction. - -The concept behind flash loans is made possible by the atomic nature of blockchain transactions. In Ethereum, -transactions are either executed entirely or not at all. This allows users to borrow funds, perform actions (like -arbitrage, liquidations, or self-liquidations), and repay the loan along with a small fee, all in a single transaction. -If the transaction fails at any point or if the loan is not repaid, the entire transaction is reverted, ensuring that -the lending protocol doesn't lose any funds. - -Flash loans gained popularity with the emergence of DeFi platforms such as Aave and dYdX, which pioneered these -services. They have enabled developers and traders to get instant access to liquidity. - -## Flash loans in Sablier - -Sablier enables flash loans for specific ERC-20 assets approved by the [Protocol Admin](/concepts/governance). It's -important to note that no asset is available for flash loaning by default. - -A percentage fee, known as the flash fee, is applied to every flash loan involving any ERC-20 asset and is managed by -the Protocol Admin. The flash fee is currently set at 0.05%. - -Our flash loan implementation adheres to the [ERC-3156](https://eips.ethereum.org/EIPS/eip-3156) specification. Detailed -instructions on how to execute a flash loan can be found [here](/contracts/v2/guides/flash-loans). - -:::tip - -Interested in enabling your asset for flash loaning? Come say hi on Discord, or send an -email to [sales@sablier.com](mailto:sales@sablier.com). - -::: - -## Supported assets - -The following tables present the assets available for flash loaning. - -### Ethereum Mainnet - -| Asset | Address | -| ----- | ------------------------------------------ | -| DAI | 0x6b175474e89094c44da98b954eedeac495271d0f | -| LUSD | 0x5f98805A4E8be255a32880FDeC7F6728C6568bA0 | -| USDC | 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 | -| USDT | 0xdAC17F958D2ee523a2206206994597C13D831ec7 | -| WETH | 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 | diff --git a/docs/concepts/protocol/08-hooks.md b/docs/concepts/protocol/08-hooks.md index 4261c283..ce1fa600 100644 --- a/docs/concepts/protocol/08-hooks.md +++ b/docs/concepts/protocol/08-hooks.md @@ -5,8 +5,7 @@ title: "Hooks" --- In Sablier, hooks are arbitrary functions that get automatically executed by the protocol in response to specific -events. Their purpose is to programmatically notify the recipient when the sender interacts with a stream, and vice -versa. +events. Their purpose is to programmatically notify the recipient when the sender interacts with a stream. Hooks can only be written in smart contracts, so typical EOAs cannot implement them. However, they are entirely optional. You can interact with the Sablier Protocol without implementing any hooks. @@ -38,21 +37,6 @@ flowchart LR LL -- "onStreamWithdrawn" --> R ``` -### Recipient side - -If the sender is not a smart contract, the hook will not be run. - -```mermaid -flowchart LR - R((Recipient)) - subgraph Core - LL[LockupLinear] - end - S((Sender)) - R -- "cancel" --> LL - LL -- "onStreamCanceled" --> S -``` - ## Example scenario Suppose you have created an NFT lending marketplace and wish to integrate Sablier. As a first step, you would typically diff --git a/docs/contracts/v2/01-overview.md b/docs/contracts/v2/01-overview.md index 0b062491..c5dda88f 100644 --- a/docs/contracts/v2/01-overview.md +++ b/docs/contracts/v2/01-overview.md @@ -25,7 +25,7 @@ For a deeper dive, read through the [technical reference](./reference/overview). # Resources -- [V2 Core](https://github.com/sablier-labs/v2-core) -- [V2 Periphery](https://github.com/sablier-labs/v2-periphery) +- [V2 Core](https://github.com/sablier-labs/v2-core/tree/release) +- [V2 Periphery](https://github.com/sablier-labs/v2-periphery/tree/release) - [V2 Integration Template](https://github.com/sablier-labs/sablier-v2-integration-template) - [Foundry Book](https://book.getfoundry.sh/) diff --git a/docs/contracts/v2/02-deployments.md b/docs/contracts/v2/02-deployments.md index affb79a1..63131220 100644 --- a/docs/contracts/v2/02-deployments.md +++ b/docs/contracts/v2/02-deployments.md @@ -1,12 +1,12 @@ --- id: "deployments" -toc_max_heading_level: 2 sidebar_position: 2 title: "Deployment Addresses" +toc_max_heading_level: 2 --- -The latest versions of [@sablier/v2-core][v2-core] and [@sablier/v2-periphery][v2-periphery] are deployed at the -addresses listed below. +This document contains the deployment addresses for the V2.1 release of [@sablier/v2-core][v2-core] and +[@sablier/v2-periphery][v2-periphery]. [v2-core]: https://npmjs.com/package/@sablier/v2-core [v2-periphery]: https://npmjs.com/package/@sablier/v2-periphery @@ -17,11 +17,11 @@ A few noteworthy details about the deployments: - All LockupLinear and LockupDynamic contracts are non-upgradeable (see [Governance](/concepts/governance)) - The source code is verified on Etherscan across all chains -:::note +:::info -Came here looking for the V1 addresses? [Click here](/contracts/v1/deployments). +Came here looking for the V2.0 deployments? [Click here](/contracts/v2/deployments/v2.0). -Or maybe you're looking for the PRBProxy deployments? Check out [prbproxy.com](https://prbproxy.com) +Or maybe you're looking for V1? [Click here](/contracts/v1/deployments). ::: @@ -31,19 +31,17 @@ Or maybe you're looking for the PRBProxy deployments? Check out [prbproxy.com](h | Contract | Address | Deployment | | :--------------------- | :-------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------- | -| SablierV2LockupLinear | [0xB10daee1FCF62243aE27776D7a92D39dC8740f95](https://etherscan.io/address/0xB10daee1FCF62243aE27776D7a92D39dC8740f95) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | -| SablierV2LockupDynamic | [0x39EFdC3dbB57B2388CcC4bb40aC4CB1226Bc9E44](https://etherscan.io/address/0x39EFdC3dbB57B2388CcC4bb40aC4CB1226Bc9E44) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | -| SablierV2NFTDescriptor | [0x98F2196fECc01C240d1429B624d007Ca268EEA29](https://etherscan.io/address/0x98F2196fECc01C240d1429B624d007Ca268EEA29) | [core-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.1) | +| SablierV2LockupLinear | [0xAFb979d9afAd1aD27C5eFf4E27226E3AB9e5dCC9](https://etherscan.io/address/0xAFb979d9afAd1aD27C5eFf4E27226E3AB9e5dCC9) | [core-v1.1.2](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.1.2) | +| SablierV2LockupDynamic | [0x7CC7e125d83A581ff438608490Cc0f7bDff79127](https://etherscan.io/address/0x7CC7e125d83A581ff438608490Cc0f7bDff79127) | [core-v1.1.2](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.1.2) | +| SablierV2NFTDescriptor | [0x23eD5DA55AF4286c0dE55fAcb414dEE2e317F4CB](https://etherscan.io/address/0x23eD5DA55AF4286c0dE55fAcb414dEE2e317F4CB) | [core-v1.1.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.1.1) | | SablierV2Comptroller | [0xC3Be6BffAeab7B297c03383B4254aa3Af2b9a5BA](https://etherscan.io/address/0xC3Be6BffAeab7B297c03383B4254aa3Af2b9a5BA) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | ### Periphery -| Contract | Address | Deployment | -| :-------------------------- | :-------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | -| SablierV2Archive | [0x0Be20a8242B0781B6fd4d453e90DCC1CcF7DBcc6](https://etherscan.io/address/0x0Be20a8242B0781B6fd4d453e90DCC1CcF7DBcc6) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyPlugin | [0x9bdebF4F9adEB99387f46e4020FBf3dDa885D2b8](https://etherscan.io/address/0x9bdebF4F9adEB99387f46e4020FBf3dDa885D2b8) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyTargetPermit2 | [0x297b43aE44660cA7826ef92D8353324C018573Ef](https://etherscan.io/address/0x297b43aE44660cA7826ef92D8353324C018573Ef) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyTargetApprove | [0x638a7aC8315767cEAfc57a6f5e3559454347C3f6](https://etherscan.io/address/0x638a7aC8315767cEAfc57a6f5e3559454347C3f6) | [periphery-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.1) | +| Contract | Address | Deployment | +| :----------------------------- | :-------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | +| SablierV2Batch | [0xEa07DdBBeA804E7fe66b958329F8Fa5cDA95Bd55](https://etherscan.io/address/0xEa07DdBBeA804E7fe66b958329F8Fa5cDA95Bd55) | [periphery-v1.1.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.1.0) | +| SablierV2MerkleStreamerFactory | [0x1A272b596b10f02931480BC7a3617db4a8d154E3](https://etherscan.io/address/0x1A272b596b10f02931480BC7a3617db4a8d154E3) | [periphery-v1.1.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.1.0) | ## Arbitrum One @@ -51,58 +49,17 @@ Or maybe you're looking for the PRBProxy deployments? Check out [prbproxy.com](h | Contract | Address | Deployment | | :--------------------- | :------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------- | -| SablierV2LockupLinear | [0x197D655F3be03903fD25e7828c3534504bfe525e](https://arbiscan.io/address/0x197D655F3be03903fD25e7828c3534504bfe525e) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | -| SablierV2LockupDynamic | [0xA9EfBEf1A35fF80041F567391bdc9813b2D50197](https://arbiscan.io/address/0xA9EfBEf1A35fF80041F567391bdc9813b2D50197) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | -| SablierV2NFTDescriptor | [0xc245d6C9608769CeF91C3858e4d2a74802B9f1bB](https://arbiscan.io/address/0xc245d6C9608769CeF91C3858e4d2a74802B9f1bB) | [core-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.1) | +| SablierV2LockupLinear | [0xFDD9d122B451F549f48c4942c6fa6646D849e8C1](https://arbiscan.io/address/0xFDD9d122B451F549f48c4942c6fa6646D849e8C1) | [core-v1.1.2](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.1.2) | +| SablierV2LockupDynamic | [0xf390cE6f54e4dc7C5A5f7f8689062b7591F7111d](https://arbiscan.io/address/0xf390cE6f54e4dc7C5A5f7f8689062b7591F7111d) | [core-v1.1.2](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.1.2) | +| SablierV2NFTDescriptor | [0x2fb103fC853b2F5022a840091ab1cDf5172E7cfa](https://arbiscan.io/address/0x2fb103fC853b2F5022a840091ab1cDf5172E7cfa) | [core-v1.1.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.1.1) | | SablierV2Comptroller | [0x17Ec73692F0aDf7E7C554822FBEAACB4BE781762](https://arbiscan.io/address/0x17Ec73692F0aDf7E7C554822FBEAACB4BE781762) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | ### Periphery -| Contract | Address | Deployment | -| :-------------------------- | :------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | -| SablierV2Archive | [0xDFa4512d07AbD4eb8Be570Cd79e2e6Fe21ff15C9](https://arbiscan.io/address/0xDFa4512d07AbD4eb8Be570Cd79e2e6Fe21ff15C9) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyPlugin | [0x9aB73CA73c89AF0bdc69642aCeb23CC6A55A514C](https://arbiscan.io/address/0x9aB73CA73c89AF0bdc69642aCeb23CC6A55A514C) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyTargetPermit2 | [0xB7185AcAF42C4966fFA3c81486d9ED9633aa4c13](https://arbiscan.io/address/0xB7185AcAF42C4966fFA3c81486d9ED9633aa4c13) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyTargetApprove | [0x90cc23dc3e12e80f27c05b8137b5f0d2b1edfa20](https://arbiscan.io/address/0x90cc23dc3e12e80f27c05b8137b5f0d2b1edfa20) | [periphery-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.1) | - -## Arbitrum Nova - -### Core - -| Contract | Address | Deployment | -| :--------------------- | :------------------------------------------------------------------------------------------------------------------------ | :---------------------------------------------------------------------------------- | -| SablierV2LockupLinear | [0x18306C9550AbfE3F5900d1206FFdce9ce5763A89](https://nova.arbiscan.io/address/0x18306C9550AbfE3F5900d1206FFdce9ce5763A89) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | -| SablierV2LockupDynamic | [0xd6b66A8D797c1e83DdEcE8f483E7D1264B9DFDa6](https://nova.arbiscan.io/address/0xd6b66A8D797c1e83DdEcE8f483E7D1264B9DFDa6) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | -| SablierV2NFTDescriptor | [0xE88d26d1E8802be5cc023264b3FccF63Bc38C20c](https://nova.arbiscan.io/address/0xE88d26d1E8802be5cc023264b3FccF63Bc38C20c) | [core-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.1) | -| SablierV2Comptroller | [0x203f1722d4adb9b67bf652c878d0dc3cc8099113](https://nova.arbiscan.io/address/0x203f1722d4adb9b67bf652c878d0dc3cc8099113) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | - -### Periphery - -| Contract | Address | Deployment | -| :-------------------------- | :------------------------------------------------------------------------------------------------------------------------ | :-------------------------------------------------------------------------------------------- | -| SablierV2Archive | [0x17DE7707D0b25F878Ae4FaC03cdE2481CD616EDd](https://nova.arbiscan.io/address/0x17DE7707D0b25F878Ae4FaC03cdE2481CD616EDd) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyPlugin | [0x1f09ce4be5ad6e76cda6242af91921440df2306e](https://nova.arbiscan.io/address/0x1f09ce4be5ad6e76cda6242af91921440df2306e) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyTargetPermit2 | [0x4487F233bdf7d3C977F936891D5A0Ff1b275A2a8](https://nova.arbiscan.io/address/0x4487F233bdf7d3C977F936891D5A0Ff1b275A2a8) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | - -## Avalanche - -### Core - -| Contract | Address | Deployment | -| :--------------------- | :-------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------- | -| SablierV2LockupLinear | [0x610346E9088AFA70D6B03e96A800B3267E75cA19](https://snowtrace.io/address/0x610346E9088AFA70D6B03e96A800B3267E75cA19) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | -| SablierV2LockupDynamic | [0x665d1C8337F1035cfBe13DD94bB669110b975f5F](https://snowtrace.io/address/0x665d1C8337F1035cfBe13DD94bB669110b975f5F) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | -| SablierV2NFTDescriptor | [0xFd050AFA2e04aA0596947DaD3Ec5690162aDc77F](https://snowtrace.io/address/0xFd050AFA2e04aA0596947DaD3Ec5690162aDc77F) | [core-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.1) | -| SablierV2Comptroller | [0x66F5431B0765D984f82A4fc4551b2c9ccF7eAC9C](https://snowtrace.io/address/0x66F5431B0765D984f82A4fc4551b2c9ccF7eAC9C) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | - -### Periphery - -| Contract | Address | Deployment | -| :-------------------------- | :-------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | -| SablierV2Archive | [0x7b1ef644ce9a625537e9e0c3d7fef3be667e6159](https://snowtrace.io/address/0x7b1ef644ce9a625537e9e0c3d7fef3be667e6159) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyPlugin | [0x17167A7e2763121e263B4331B700a1BF9113b387](https://snowtrace.io/address/0x17167A7e2763121e263B4331B700a1BF9113b387) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyTargetPermit2 | [0x48B4889cf5d6f8360050f9d7606505F1433120BC](https://snowtrace.io/address/0x48B4889cf5d6f8360050f9d7606505F1433120BC) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyTargetApprove | [0x817fE1364A9d57d1fB951945B53942234163Ef10](https://snowtrace.io/address/0x817fE1364A9d57d1fB951945B53942234163Ef10) | [periphery-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.1) | +| Contract | Address | Deployment | +| :----------------------------- | :------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | +| SablierV2Batch | [0xAFd1434296e29a0711E24014656158055F00784c](https://arbiscan.io/address/0xAFd1434296e29a0711E24014656158055F00784c) | [periphery-v1.1.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.1.0) | +| SablierV2MerkleStreamerFactory | [0x237400eF5a41886a75B0e036228221Df075b3B80](https://arbiscan.io/address/0x237400eF5a41886a75B0e036228221Df075b3B80) | [periphery-v1.1.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.1.0) | ## Base @@ -110,19 +67,17 @@ Or maybe you're looking for the PRBProxy deployments? Check out [prbproxy.com](h | Contract | Address | Deployment | | :--------------------- | :-------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------- | -| SablierV2LockupLinear | [0x6b9a46C8377f21517E65fa3899b3A9Fab19D17f5](https://basescan.org/address/0x6b9a46C8377f21517E65fa3899b3A9Fab19D17f5) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | -| SablierV2LockupDynamic | [0x645B00960Dc352e699F89a81Fc845C0C645231cf](https://basescan.org/address/0x645B00960Dc352e699F89a81Fc845C0C645231cf) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | -| SablierV2NFTDescriptor | [0xEFc2896c29F70bc23e82892Df827d4e2259028Fd](https://basescan.org/address/0xEFc2896c29F70bc23e82892Df827d4e2259028Fd) | [core-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.1) | +| SablierV2LockupLinear | [0xFCF737582d167c7D20A336532eb8BCcA8CF8e350](https://basescan.org/address/0xFCF737582d167c7D20A336532eb8BCcA8CF8e350) | [core-v1.1.2](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.1.2) | +| SablierV2LockupDynamic | [0x461E13056a3a3265CEF4c593F01b2e960755dE91](https://basescan.org/address/0x461E13056a3a3265CEF4c593F01b2e960755dE91) | [core-v1.1.2](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.1.2) | +| SablierV2NFTDescriptor | [0x67e0a126b695DBA35128860cd61926B90C420Ceb](https://basescan.org/address/0x67e0a126b695DBA35128860cd61926B90C420Ceb) | [core-v1.1.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.1.1) | | SablierV2Comptroller | [0x7Faaedd40B1385C118cA7432952D9DC6b5CbC49e](https://basescan.org/address/0x7Faaedd40B1385C118cA7432952D9DC6b5CbC49e) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | ### Periphery -| Contract | Address | Deployment | -| :-------------------------- | :-------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | -| SablierV2Archive | [0x1C5Ac71dd48c7ff291743e5E6e3689ba92F73cC6](https://basescan.org/address/0x1C5Ac71dd48c7ff291743e5E6e3689ba92F73cC6) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyPlugin | [0x50E8B9dC7F28e5cA9253759455C1077e497c4232](https://basescan.org/address/0x50E8B9dC7F28e5cA9253759455C1077e497c4232) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyTargetPermit2 | [0x0648C80b969501c7778b6ff3ba47aBb78fEeDF39](https://basescan.org/address/0x0648C80b969501c7778b6ff3ba47aBb78fEeDF39) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyTargetApprove | [0xf19576Ab425753816eCbF98aca8132A0f693aEc5](https://basescan.org/address/0xf19576Ab425753816eCbF98aca8132A0f693aEc5) | [periphery-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.1) | +| Contract | Address | Deployment | +| :----------------------------- | :-------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | +| SablierV2Batch | [0x94E596EEd73b4e3171c067f05A87AB0268cA993c](https://basescan.org/address/0x94E596EEd73b4e3171c067f05A87AB0268cA993c) | [periphery-v1.1.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.1.0) | +| SablierV2MerkleStreamerFactory | [0x5545c8E7c3E1F74aDc98e518F2E8D23A002C4412](https://basescan.org/address/0x5545c8E7c3E1F74aDc98e518F2E8D23A002C4412) | [periphery-v1.1.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.1.0) | ## BNB Smart Chain @@ -130,19 +85,17 @@ Or maybe you're looking for the PRBProxy deployments? Check out [prbproxy.com](h | Contract | Address | Deployment | | :--------------------- | :------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------- | -| SablierV2LockupLinear | [0x3FE4333f62A75c2a85C8211c6AeFd1b9Bfde6e51](https://bscscan.com/address/0x3FE4333f62A75c2a85C8211c6AeFd1b9Bfde6e51) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | -| SablierV2LockupDynamic | [0xF2f3feF2454DcA59ECA929D2D8cD2a8669Cc6214](https://bscscan.com/address/0xF2f3feF2454DcA59ECA929D2D8cD2a8669Cc6214) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | -| SablierV2NFTDescriptor | [0x3daD1bF57edCFF979Fb68a802AC54c5AAfB78F4c](https://bscscan.com/address/0x3daD1bF57edCFF979Fb68a802AC54c5AAfB78F4c) | [core-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.1) | +| SablierV2LockupLinear | [0x14c35E126d75234a90c9fb185BF8ad3eDB6A90D2](https://bscscan.com/address/0x14c35E126d75234a90c9fb185BF8ad3eDB6A90D2) | [core-v1.1.2](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.1.2) | +| SablierV2LockupDynamic | [0xf900c5E3aA95B59Cc976e6bc9c0998618729a5fa](https://bscscan.com/address/0xf900c5E3aA95B59Cc976e6bc9c0998618729a5fa) | [core-v1.1.2](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.1.2) | +| SablierV2NFTDescriptor | [0xEcAfcF09c23057210cB6470eB5D0FD8Bafd1755F](https://bscscan.com/address/0xEcAfcF09c23057210cB6470eB5D0FD8Bafd1755F) | [core-v1.1.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.1.1) | | SablierV2Comptroller | [0x33511f69A784Fd958E6713aCaC7c9dCF1A5578E8](https://bscscan.com/address/0x33511f69A784Fd958E6713aCaC7c9dCF1A5578E8) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | ### Periphery -| Contract | Address | Deployment | -| :-------------------------- | :------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | -| SablierV2Archive | [0xeDe48EB173A869c0b27Cb98CC56d00BC391e5887](https://bscscan.com/address/0xeDe48EB173A869c0b27Cb98CC56d00BC391e5887) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyPlugin | [0xC43b2d8CedB71df30F45dFd9a21eC1E50A813bD6](https://bscscan.com/address/0xC43b2d8CedB71df30F45dFd9a21eC1E50A813bD6) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyTargetPermit2 | [0x135e78B8E17B1d189Af75FcfCC018ab2E6c7b879](https://bscscan.com/address/0x135e78B8E17B1d189Af75FcfCC018ab2E6c7b879) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyTargetApprove | [0xc9bf2A6bD467A813908d836c1506efE61E465761](https://bscscan.com/address/0xc9bf2A6bD467A813908d836c1506efE61E465761) | [periphery-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.1) | +| Contract | Address | Deployment | +| :----------------------------- | :------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | +| SablierV2Batch | [0x2E30a2ae6565Db78C06C28dE937F668597c80a1c](https://bscscan.com/address/0x2E30a2ae6565Db78C06C28dE937F668597c80a1c) | [periphery-v1.1.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.1.0) | +| SablierV2MerkleStreamerFactory | [0x434D73465aAc4125d204A6637eB6C579d8D69f48](https://bscscan.com/address/0x434D73465aAc4125d204A6637eB6C579d8D69f48) | [periphery-v1.1.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.1.0) | ## Gnosis @@ -150,19 +103,17 @@ Or maybe you're looking for the PRBProxy deployments? Check out [prbproxy.com](h | Contract | Address | Deployment | | :--------------------- | :--------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------- | -| SablierV2LockupLinear | [0x685E92c9cA2bB23f1B596d0a7D749c0603e88585](https://gnosisscan.io/address/0x685E92c9cA2bB23f1B596d0a7D749c0603e88585) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | -| SablierV2LockupDynamic | [0xeb148E4ec13aaA65328c0BA089a278138E9E53F9](https://gnosisscan.io/address/0xeb148E4ec13aaA65328c0BA089a278138E9E53F9) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | -| SablierV2NFTDescriptor | [0x8CE9Cd651e03325Cf6D4Ce9cfa74BE79CDf6d530](https://gnosisscan.io/address/0x8CE9Cd651e03325Cf6D4Ce9cfa74BE79CDf6d530) | [core-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.1) | +| SablierV2LockupLinear | [0xce49854a647a1723e8Fb7CC3D190CAB29A44aB48](https://gnosisscan.io/address/0xce49854a647a1723e8Fb7CC3D190CAB29A44aB48) | [core-v1.1.2](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.1.2) | +| SablierV2LockupDynamic | [0x1DF83C7682080B0f0c26a20C6C9CB8623e0Df24E](https://gnosisscan.io/address/0x1DF83C7682080B0f0c26a20C6C9CB8623e0Df24E) | [core-v1.1.2](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.1.2) | +| SablierV2NFTDescriptor | [0x01dbFE22205d8B109959e2Be02d0095379309eed](https://gnosisscan.io/address/0x01dbFE22205d8B109959e2Be02d0095379309eed) | [core-v1.1.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.1.1) | | SablierV2Comptroller | [0x73962c44c0fB4cC5e4545FB91732a5c5e87F55C2](https://gnosisscan.io/address/0x73962c44c0fB4cC5e4545FB91732a5c5e87F55C2) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | ### Periphery -| Contract | Address | Deployment | -| :-------------------------- | :--------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | -| SablierV2Archive | [0xF4A6F47Da7c6b26b6Dd774671aABA48fb4bFE309](https://gnosisscan.io/address/0xF4A6F47Da7c6b26b6Dd774671aABA48fb4bFE309) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyPlugin | [0xc84f0e95815A576171A19EB9E0fA55a217Ab1536](https://gnosisscan.io/address/0xc84f0e95815A576171A19EB9E0fA55a217Ab1536) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyTargetPermit2 | [0x5B144C3B9C8cfd48297Aeb59B90a024Ef3fCcE92](https://gnosisscan.io/address/0x5B144C3B9C8cfd48297Aeb59B90a024Ef3fCcE92) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyTargetApprove | [0x89AfE038714e547C29Fa881029DD4B5CFB008454](https://gnosisscan.io/address/0x89AfE038714e547C29Fa881029DD4B5CFB008454) | [periphery-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.1) | +| Contract | Address | Deployment | +| :----------------------------- | :--------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | +| SablierV2Batch | [0xBd9DDbC55B85FF6Dc0b76E9EFdCd2547Ab482501](https://gnosisscan.io/address/0xBd9DDbC55B85FF6Dc0b76E9EFdCd2547Ab482501) | [periphery-v1.1.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.1.0) | +| SablierV2MerkleStreamerFactory | [0x777F66477FF83aBabADf39a3F22A8CC3AEE43765](https://gnosisscan.io/address/0x777F66477FF83aBabADf39a3F22A8CC3AEE43765) | [periphery-v1.1.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.1.0) | ## Optimism @@ -170,19 +121,17 @@ Or maybe you're looking for the PRBProxy deployments? Check out [prbproxy.com](h | Contract | Address | Deployment | | :--------------------- | :------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------- | -| SablierV2LockupLinear | [0xB923aBdCA17Aed90EB5EC5E407bd37164f632bFD](https://optimistic.etherscan.io/address/0xB923aBdCA17Aed90EB5EC5E407bd37164f632bFD) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | -| SablierV2LockupDynamic | [0x6f68516c21E248cdDfaf4898e66b2b0Adee0e0d6](https://optimistic.etherscan.io/address/0x6f68516c21E248cdDfaf4898e66b2b0Adee0e0d6) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | -| SablierV2NFTDescriptor | [0xe0138C596939CC0D2382046795bC163ad5755e0E](https://optimistic.etherscan.io/address/0xe0138C596939CC0D2382046795bC163ad5755e0E) | [core-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.1) | +| SablierV2LockupLinear | [0x4b45090152a5731b5bc71b5baF71E60e05B33867](https://optimistic.etherscan.io/address/0x4b45090152a5731b5bc71b5baF71E60e05B33867) | [core-v1.1.2](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.1.2) | +| SablierV2LockupDynamic | [0xd6920c1094eABC4b71f3dC411A1566f64f4c206e](https://optimistic.etherscan.io/address/0xd6920c1094eABC4b71f3dC411A1566f64f4c206e) | [core-v1.1.2](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.1.2) | +| SablierV2NFTDescriptor | [0xF5050c04425E639C647F5ED632218b16ce96694d](https://optimistic.etherscan.io/address/0xF5050c04425E639C647F5ED632218b16ce96694d) | [core-v1.1.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.1.1) | | SablierV2Comptroller | [0x1EECb6e6EaE6a1eD1CCB4323F3a146A7C5443A10](https://optimistic.etherscan.io/address/0x1EECb6e6EaE6a1eD1CCB4323F3a146A7C5443A10) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | ### Periphery -| Contract | Address | Deployment | -| :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | -| SablierV2Archive | [0x9A09eC6f991386718854aDDCEe68647776Befd5b](https://optimistic.etherscan.io/address/0x9A09eC6f991386718854aDDCEe68647776Befd5b) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyPlugin | [0x77C8516B1F327890C956bb38F93Ac2d6B24795Ea](https://optimistic.etherscan.io/address/0x77C8516B1F327890C956bb38F93Ac2d6B24795Ea) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyTargetPermit2 | [0x194ed7D6005C8ba4084A948406545DF299ad37cD](https://optimistic.etherscan.io/address/0x194ed7D6005C8ba4084A948406545DF299ad37cD) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyTargetApprove | [0x8a6974c162fdc7Cb67996F7dB8bAAFb9a99566e0](https://optimistic.etherscan.io/address/0x8a6974c162fdc7Cb67996F7dB8bAAFb9a99566e0) | [periphery-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.1) | +| Contract | Address | Deployment | +| :----------------------------- | :------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | +| SablierV2Batch | [0x8145429538dDBdDc4099B2bAfd24DD8958fa03b8](https://optimistic.etherscan.io/address/0x8145429538dDBdDc4099B2bAfd24DD8958fa03b8) | [periphery-v1.1.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.1.0) | +| SablierV2MerkleStreamerFactory | [0x044EC80FbeC40f0eE7E7b3856828170971796C19](https://optimistic.etherscan.io/address/0x044EC80FbeC40f0eE7E7b3856828170971796C19) | [periphery-v1.1.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.1.0) | ## Polygon @@ -190,19 +139,17 @@ Or maybe you're looking for the PRBProxy deployments? Check out [prbproxy.com](h | Contract | Address | Deployment | | :--------------------- | :----------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------- | -| SablierV2LockupLinear | [0x67422C3E36A908D5C3237e9cFfEB40bDE7060f6E](https://polygonscan.com/address/0x67422C3E36A908D5C3237e9cFfEB40bDE7060f6E) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | -| SablierV2LockupDynamic | [0x7313AdDb53f96a4f710D3b91645c62B434190725](https://polygonscan.com/address/0x7313AdDb53f96a4f710D3b91645c62B434190725) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | -| SablierV2NFTDescriptor | [0xA820946EaAceB2a85aF123f706f23192c28bC6B9](https://polygonscan.com/address/0xA820946EaAceB2a85aF123f706f23192c28bC6B9) | [core-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.1) | +| SablierV2LockupLinear | [0x5f0e1dea4A635976ef51eC2a2ED41490d1eBa003](https://polygonscan.com/address/0x5f0e1dea4A635976ef51eC2a2ED41490d1eBa003) | [core-v1.1.2](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.1.2) | +| SablierV2LockupDynamic | [0xB194c7278C627D52E440316b74C5F24FC70c1565](https://polygonscan.com/address/0xB194c7278C627D52E440316b74C5F24FC70c1565) | [core-v1.1.2](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.1.2) | +| SablierV2NFTDescriptor | [0x8683da9DF8c5c3528e8251a5764EC7DAc7264795](https://polygonscan.com/address/0x8683da9DF8c5c3528e8251a5764EC7DAc7264795) | [core-v1.1.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.1.1) | | SablierV2Comptroller | [0x9761692EDf10F5F2A69f0150e2fd50dcecf05F2E](https://polygonscan.com/address/0x9761692EDf10F5F2A69f0150e2fd50dcecf05F2E) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | ### Periphery -| Contract | Address | Deployment | -| :-------------------------- | :----------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | -| SablierV2Archive | [0xA2f5B2e798e7ADd59d85d9b76645E6AC13fC4e1f](https://polygonscan.com/address/0xA2f5B2e798e7ADd59d85d9b76645E6AC13fC4e1f) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyPlugin | [0xBe4cad0e99865CC62787Ecf029aD9DD4815d3d2e](https://polygonscan.com/address/0xBe4cad0e99865CC62787Ecf029aD9DD4815d3d2e) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyTargetPermit2 | [0x576743075fc5F771bbC1376c3267A6185Af9D62B](https://polygonscan.com/address/0x576743075fc5F771bbC1376c3267A6185Af9D62B) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyTargetApprove | [0xccA6dd77bA2cfcccEdA01A82CB309e2A17901682](https://polygonscan.com/address/0xccA6dd77bA2cfcccEdA01A82CB309e2A17901682) | [periphery-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.1) | +| Contract | Address | Deployment | +| :----------------------------- | :----------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | +| SablierV2Batch | [0x5865C73789C4496665eDE1CAF018dc52ac248598](https://polygonscan.com/address/0x5865C73789C4496665eDE1CAF018dc52ac248598) | [periphery-v1.1.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.1.0) | +| SablierV2MerkleStreamerFactory | [0xF4906225e783fb8977410BDBFb960caBed6C2EF4](https://polygonscan.com/address/0xF4906225e783fb8977410BDBFb960caBed6C2EF4) | [periphery-v1.1.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.1.0) | ## Scroll @@ -210,38 +157,17 @@ Or maybe you're looking for the PRBProxy deployments? Check out [prbproxy.com](h | Contract | Address | Deployment | | :--------------------- | :---------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------- | -| SablierV2LockupLinear | [0x80640ca758615ee83801EC43452feEA09a202D33](https://scrollscan.com/address/0x80640ca758615ee83801EC43452feEA09a202D33) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | -| SablierV2LockupDynamic | [0xde6a30D851eFD0Fc2a9C922F294801Cfd5FCB3A1](https://scrollscan.com/address/0xde6a30D851eFD0Fc2a9C922F294801Cfd5FCB3A1) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | -| SablierV2NFTDescriptor | [0xC1fa624733203F2B7185c3724039C4D5E5234fE4](https://scrollscan.com/address/0xC1fa624733203F2B7185c3724039C4D5E5234fE4) | [core-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.1) | +| SablierV2LockupLinear | [0x57e14AB4DAd920548899d86B54AD47Ea27F00987](https://scrollscan.com/address/0x57e14AB4DAd920548899d86B54AD47Ea27F00987) | [core-v1.1.2](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.1.2) | +| SablierV2LockupDynamic | [0xAaff2D11f9e7Cd2A9cDC674931fAC0358a165995](https://scrollscan.com/address/0xAaff2D11f9e7Cd2A9cDC674931fAC0358a165995) | [core-v1.1.2](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.1.2) | +| SablierV2NFTDescriptor | [0xB71440B85172332E8B768e85EdBfdb34CB457c1c](https://scrollscan.com/address/0xB71440B85172332E8B768e85EdBfdb34CB457c1c) | [core-v1.1.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.1.1) | | SablierV2Comptroller | [0x859708495E3B3c61Bbe19e6E3E1F41dE3A5C5C5b](https://scrollscan.com/address/0x859708495E3B3c61Bbe19e6E3E1F41dE3A5C5C5b) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | ### Periphery -| Contract | Address | Deployment | -| :-------------------------- | :---------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | -| SablierV2Archive | [0x94A18AC6e4B7d97E31f1587f6a666Dc5503086c3](https://scrollscan.com/address/0x94A18AC6e4B7d97E31f1587f6a666Dc5503086c3) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyPlugin | [0xED1591BD6038032a74D786A452A23536b3201490](https://scrollscan.com/address/0xED1591BD6038032a74D786A452A23536b3201490) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyTargetPermit2 | [0x91154fc80933D25793E6B4D7CE19fb51dE6794B7](https://scrollscan.com/address/0x91154fc80933D25793E6B4D7CE19fb51dE6794B7) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyTargetApprove | [0x71CeA9c4d15fed2E58785cE0C05165CE34313A74](https://scrollscan.com/address/0x71CeA9c4d15fed2E58785cE0C05165CE34313A74) | [periphery-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.1) | - -## Arbitrum Goerli - -### Core - -| Contract | Address | Deployment | -| :--------------------- | :-------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------- | -| SablierV2LockupLinear | [0x323B629635b6cFfe2453Aa2869c5957AfF55F445](https://goerli.arbiscan.io/address/0x323B629635b6cFfe2453Aa2869c5957AfF55F445) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | -| SablierV2LockupDynamic | [0xdc0a619fF975de6a08c7615ea383533fd265f2e3](https://goerli.arbiscan.io/address/0xdc0a619fF975de6a08c7615ea383533fd265f2e3) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | -| SablierV2NFTDescriptor | [0x740509d893BC15a31EAE8542683Ed32085c559cB](https://goerli.arbiscan.io/address/0x740509d893BC15a31EAE8542683Ed32085c559cB) | [core-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.1) | -| SablierV2Comptroller | [0xECF737BDb9BB094489beCa39f0b9Ae66E0C14ba8](https://goerli.arbiscan.io/address/0xECF737BDb9BB094489beCa39f0b9Ae66E0C14ba8) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | - -### Periphery - -| Contract | Address | Deployment | -| :-------------------------- | :-------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | -| SablierV2Archive | [0x4371d767Cd7991248D20eD61d425e1e70c6CEEab](https://goerli.arbiscan.io/address/0x4371d767Cd7991248D20eD61d425e1e70c6CEEab) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyPlugin | [0xD37832B8993bEe6F41A8183967a7488C6e2a3551](https://goerli.arbiscan.io/address/0xD37832B8993bEe6F41A8183967a7488C6e2a3551) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyTargetPermit2 | [0x2Ebd987e12432Ee3a74Fe0A55Afe5D866096e354](https://goerli.arbiscan.io/address/0x2Ebd987e12432Ee3a74Fe0A55Afe5D866096e354) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| Contract | Address | Deployment | +| :----------------------------- | :---------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | +| SablierV2Batch | [0xD18faa233E02d41EDFFdb64f20281dE0592FA3b5](https://scrollscan.com/address/0xD18faa233E02d41EDFFdb64f20281dE0592FA3b5) | [periphery-v1.1.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.1.0) | +| SablierV2MerkleStreamerFactory | [0xb3ade5463000E6c0D376e7d7570f372eBf98BDAf](https://scrollscan.com/address/0xb3ade5463000E6c0D376e7d7570f372eBf98BDAf) | [periphery-v1.1.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.1.0) | ## Arbitrum Sepolia @@ -249,38 +175,17 @@ Or maybe you're looking for the PRBProxy deployments? Check out [prbproxy.com](h | Contract | Address | Deployment | | :--------------------- | :--------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------- | -| SablierV2LockupLinear | [0xa3e36b51B7A456812c92253780f4B15bad56e34c](https://sepolia.arbiscan.io/address/0xa3e36b51B7A456812c92253780f4B15bad56e34c) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | -| SablierV2LockupDynamic | [0x7938c18a59FaD2bA11426AcfBe8d74F0F598a4D2](https://sepolia.arbiscan.io/address/0x7938c18a59FaD2bA11426AcfBe8d74F0F598a4D2) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | -| SablierV2NFTDescriptor | [0xEe93BFf599C17C6fF8e31F2De6c3e40bd5e51312](https://sepolia.arbiscan.io/address/0xEe93BFf599C17C6fF8e31F2De6c3e40bd5e51312) | [core-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.1) | -| SablierV2Comptroller | [0xA6A0cfA3442053fbB516D55205A749Ef2D33aed9](https://sepolia.arbiscan.io/address/0xA6A0cfA3442053fbB516D55205A749Ef2D33aed9) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | - -### Periphery - -| Contract | Address | Deployment | -| :-------------------------- | :--------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | -| SablierV2Archive | [0x2C8fA48361C7D48Dc21b27a3D549402Cf8AE16B0](https://sepolia.arbiscan.io/address/0x2C8fA48361C7D48Dc21b27a3D549402Cf8AE16B0) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyPlugin | [0x7D310803c3824636bAff74e4f80e81ece167c440](https://sepolia.arbiscan.io/address/0x7D310803c3824636bAff74e4f80e81ece167c440) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyTargetPermit2 | [0x396A3a169918A4C0B339ECf86C583f46D696254E](https://sepolia.arbiscan.io/address/0x396A3a169918A4C0B339ECf86C583f46D696254E) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | - -## Goerli - -### Core - -| Contract | Address | Deployment | -| :--------------------- | :--------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------- | -| SablierV2LockupLinear | [0x6E3678c005815Ab34986D8d66A353Cd3699103DE](https://goerli.etherscan.io/address/0x6E3678c005815Ab34986D8d66A353Cd3699103DE) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | -| SablierV2LockupDynamic | [0x4BE70EDe968e9dBA12DB42b9869Bec66bEDC17d7](https://goerli.etherscan.io/address/0x4BE70EDe968e9dBA12DB42b9869Bec66bEDC17d7) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | -| SablierV2NFTDescriptor | [0x1D83CDd66BCf0ea8c99E745cC868478d6C3633f0](https://goerli.etherscan.io/address/0x1D83CDd66BCf0ea8c99E745cC868478d6C3633f0) | [core-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.1) | -| SablierV2Comptroller | [0x9B75F65bCCd05545C400145Cca29dA52DA57AC2b](https://goerli.etherscan.io/address/0x9B75F65bCCd05545C400145Cca29dA52DA57AC2b) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | +| SablierV2LockupLinear | [0x483bdd560dE53DC20f72dC66ACdB622C5075de34](https://sepolia.arbiscan.io/address/0x483bdd560dE53DC20f72dC66ACdB622C5075de34) | [core-v1.1.2](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.1.2) | +| SablierV2LockupDynamic | [0x8c8102b92B1f31cC304A085D490796f4DfdF7aF3](https://sepolia.arbiscan.io/address/0x8c8102b92B1f31cC304A085D490796f4DfdF7aF3) | [core-v1.1.2](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.1.2) | +| SablierV2NFTDescriptor | [0x593050f0360518C3A4F11c32Eb936146e1096FD1](https://sepolia.arbiscan.io/address/0x593050f0360518C3A4F11c32Eb936146e1096FD1) | [core-v1.1.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.1.1) | +| SablierV2Comptroller | [0xA6A0cfA3442053fbB516D55205A749Ef2D33aed9](https://sepolia.arbiscan.io/address/0xA6A0cfA3442053fbB516D55205A749Ef2D33aed9) | [core-v1.1.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | ### Periphery -| Contract | Address | Deployment | -| :-------------------------- | :--------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | -| SablierV2Archive | [0xFd14E62e6fe4d96F033cf972556ae56D09Bd49cA](https://goerli.etherscan.io/address/0xFd14E62e6fe4d96F033cf972556ae56D09Bd49cA) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyPlugin | [0x9CA1dFFC744318198bE9Cf92283A803CE16b698a](https://goerli.etherscan.io/address/0x9CA1dFFC744318198bE9Cf92283A803CE16b698a) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyTargetPermit2 | [0x0eE01680645c361B740ab4dCDdF238988eB20411](https://goerli.etherscan.io/address/0x0eE01680645c361B740ab4dCDdF238988eB20411) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyTargetApprove | [0x0e563B883dfe11469915194F8651a65212fdB96F](https://goerli.etherscan.io/address/0x0e563B883dfe11469915194F8651a65212fdB96F) | [periphery-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.1) | +| Contract | Address | Deployment | +| :----------------------------- | :--------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | +| SablierV2Batch | [0x72D921E579aB7FC5D19CD398B6be24d626Ccb6e7](https://sepolia.arbiscan.io/address/0x72D921E579aB7FC5D19CD398B6be24d626Ccb6e7) | [periphery-v1.1.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.1.0) | +| SablierV2MerkleStreamerFactory | [0xcc87b1A4de285832f226BD585bd54a2184D32105](https://sepolia.arbiscan.io/address/0xcc87b1A4de285832f226BD585bd54a2184D32105) | [periphery-v1.1.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.1.0) | ## Sepolia @@ -288,16 +193,14 @@ Or maybe you're looking for the PRBProxy deployments? Check out [prbproxy.com](h | Contract | Address | Deployment | | :--------------------- | :---------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------- | -| SablierV2LockupLinear | [0xd4300c5bc0b9e27c73ebabdc747ba990b1b570db](https://sepolia.etherscan.io/address/0xd4300c5bc0b9e27c73ebabdc747ba990b1b570db) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | -| SablierV2LockupDynamic | [0x421e1E7a53FF360f70A2D02037Ee394FA474e035](https://sepolia.etherscan.io/address/0x421e1E7a53FF360f70A2D02037Ee394FA474e035) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | -| SablierV2NFTDescriptor | [0x3cb51943ebcea05b23c35c50491b3d296ff675db](https://sepolia.etherscan.io/address/0x3cb51943ebcea05b23c35c50491b3d296ff675db) | [core-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.1) | +| SablierV2LockupLinear | [0x7a43F8a888fa15e68C103E18b0439Eb1e98E4301](https://sepolia.etherscan.io/address/0x7a43F8a888fa15e68C103E18b0439Eb1e98E4301) | [core-v1.1.2](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.1.2) | +| SablierV2LockupDynamic | [0xc9940AD8F43aAD8e8f33A4D5dbBf0a8F7FF4429A](https://sepolia.etherscan.io/address/0xc9940AD8F43aAD8e8f33A4D5dbBf0a8F7FF4429A) | [core-v1.1.2](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.1.2) | +| SablierV2NFTDescriptor | [0xE8fFEbA8963CD9302ffD39c704dc2c027128D36F](https://sepolia.etherscan.io/address/0xE8fFEbA8963CD9302ffD39c704dc2c027128D36F) | [core-v1.1.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.1.1) | | SablierV2Comptroller | [0x2006d43E65e66C5FF20254836E63947FA8bAaD68](https://sepolia.etherscan.io/address/0x2006d43E65e66C5FF20254836E63947FA8bAaD68) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | ### Periphery -| Contract | Address | Deployment | -| :-------------------------- | :---------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------- | -| SablierV2Archive | [0x83495d8DF6221f566232e1353a6e7231A86C61fF](https://sepolia.etherscan.io/address/0x83495d8DF6221f566232e1353a6e7231A86C61fF) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyPlugin | [0xa333c8233CfD04740E64AB4fd5447995E357561B](https://sepolia.etherscan.io/address/0xa333c8233CfD04740E64AB4fd5447995E357561B) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyTargetPermit2 | [0x5091900B7cF803a7407FCE6333A6bAE4aA779Fd4](https://sepolia.etherscan.io/address/0x5091900B7cF803a7407FCE6333A6bAE4aA779Fd4) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | -| SablierV2ProxyTargetApprove | [0x105E7728C5706Ad41d194EbDc7873B047352F3d2](https://sepolia.etherscan.io/address/0x105E7728C5706Ad41d194EbDc7873B047352F3d2) | [periphery-v1.0.1 ](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.1) | +| Contract | Address | Deployment | +| :----------------------------- | :---------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | +| SablierV2Batch | [0xd2569DC4A58dfE85d807Dffb976dbC0a3bf0B0Fb](https://sepolia.etherscan.io/address/0xd2569DC4A58dfE85d807Dffb976dbC0a3bf0B0Fb) | [periphery-v1.1.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.1.0) | +| SablierV2MerkleStreamerFactory | [0xBacC1d151A78eeD71D504f701c25E8739DC0262D](https://sepolia.etherscan.io/address/0xBacC1d151A78eeD71D504f701c25E8739DC0262D) | [periphery-v1.1.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.1.0) | diff --git a/docs/contracts/v2/deployments/_category_.json b/docs/contracts/v2/deployments/_category_.json new file mode 100644 index 00000000..62fb0cd6 --- /dev/null +++ b/docs/contracts/v2/deployments/_category_.json @@ -0,0 +1,5 @@ +{ + "collapsed": true, + "label": "Older Deployments", + "position": 6 +} diff --git a/docs/contracts/v2/deployments/v2.0.md b/docs/contracts/v2/deployments/v2.0.md new file mode 100644 index 00000000..3b760487 --- /dev/null +++ b/docs/contracts/v2/deployments/v2.0.md @@ -0,0 +1,300 @@ +--- +id: "v2.0" +title: "V2.0" +toc_max_heading_level: 2 +--- + +This document contains the deployment addresses for the V2.0 release of [@sablier/v2-core][v2-core] and +[@sablier/v2-periphery][v2-periphery]. + +[v2-core]: https://npmjs.com/package/@sablier/v2-core +[v2-periphery]: https://npmjs.com/package/@sablier/v2-periphery + +A few noteworthy details about the deployments: + +- The addresses are final +- All LockupLinear and LockupDynamic contracts are non-upgradeable (see [Governance](/concepts/governance)) +- The source code is verified on Etherscan across all chains + +:::info + +This is an outdated version of the Sablier protocol. See the latest version [here](/contracts/v2/deployments). + +::: + +## Mainnet + +### Core + +| Contract | Address | Deployment | +| :--------------------- | :-------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------- | +| SablierV2LockupLinear | [0xB10daee1FCF62243aE27776D7a92D39dC8740f95](https://etherscan.io/address/0xB10daee1FCF62243aE27776D7a92D39dC8740f95) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | +| SablierV2LockupDynamic | [0x39EFdC3dbB57B2388CcC4bb40aC4CB1226Bc9E44](https://etherscan.io/address/0x39EFdC3dbB57B2388CcC4bb40aC4CB1226Bc9E44) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | +| SablierV2NFTDescriptor | [0x98F2196fECc01C240d1429B624d007Ca268EEA29](https://etherscan.io/address/0x98F2196fECc01C240d1429B624d007Ca268EEA29) | [core-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.1) | +| SablierV2Comptroller | [0xC3Be6BffAeab7B297c03383B4254aa3Af2b9a5BA](https://etherscan.io/address/0xC3Be6BffAeab7B297c03383B4254aa3Af2b9a5BA) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | + +### Periphery + +| Contract | Address | Deployment | +| :-------------------------- | :-------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | +| SablierV2Archive | [0x0Be20a8242B0781B6fd4d453e90DCC1CcF7DBcc6](https://etherscan.io/address/0x0Be20a8242B0781B6fd4d453e90DCC1CcF7DBcc6) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyPlugin | [0x9bdebF4F9adEB99387f46e4020FBf3dDa885D2b8](https://etherscan.io/address/0x9bdebF4F9adEB99387f46e4020FBf3dDa885D2b8) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyTargetPermit2 | [0x297b43aE44660cA7826ef92D8353324C018573Ef](https://etherscan.io/address/0x297b43aE44660cA7826ef92D8353324C018573Ef) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyTargetApprove | [0x638a7aC8315767cEAfc57a6f5e3559454347C3f6](https://etherscan.io/address/0x638a7aC8315767cEAfc57a6f5e3559454347C3f6) | [periphery-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.1) | + +## Arbitrum One + +### Core + +| Contract | Address | Deployment | +| :--------------------- | :------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------- | +| SablierV2LockupLinear | [0x197D655F3be03903fD25e7828c3534504bfe525e](https://arbiscan.io/address/0x197D655F3be03903fD25e7828c3534504bfe525e) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | +| SablierV2LockupDynamic | [0xA9EfBEf1A35fF80041F567391bdc9813b2D50197](https://arbiscan.io/address/0xA9EfBEf1A35fF80041F567391bdc9813b2D50197) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | +| SablierV2NFTDescriptor | [0xc245d6C9608769CeF91C3858e4d2a74802B9f1bB](https://arbiscan.io/address/0xc245d6C9608769CeF91C3858e4d2a74802B9f1bB) | [core-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.1) | +| SablierV2Comptroller | [0x17Ec73692F0aDf7E7C554822FBEAACB4BE781762](https://arbiscan.io/address/0x17Ec73692F0aDf7E7C554822FBEAACB4BE781762) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | + +### Periphery + +| Contract | Address | Deployment | +| :-------------------------- | :------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | +| SablierV2Archive | [0xDFa4512d07AbD4eb8Be570Cd79e2e6Fe21ff15C9](https://arbiscan.io/address/0xDFa4512d07AbD4eb8Be570Cd79e2e6Fe21ff15C9) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyPlugin | [0x9aB73CA73c89AF0bdc69642aCeb23CC6A55A514C](https://arbiscan.io/address/0x9aB73CA73c89AF0bdc69642aCeb23CC6A55A514C) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyTargetPermit2 | [0xB7185AcAF42C4966fFA3c81486d9ED9633aa4c13](https://arbiscan.io/address/0xB7185AcAF42C4966fFA3c81486d9ED9633aa4c13) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyTargetApprove | [0x90cc23dc3e12e80f27c05b8137b5f0d2b1edfa20](https://arbiscan.io/address/0x90cc23dc3e12e80f27c05b8137b5f0d2b1edfa20) | [periphery-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.1) | + +## Arbitrum Nova + +### Core + +| Contract | Address | Deployment | +| :--------------------- | :------------------------------------------------------------------------------------------------------------------------ | :---------------------------------------------------------------------------------- | +| SablierV2LockupLinear | [0x18306C9550AbfE3F5900d1206FFdce9ce5763A89](https://nova.arbiscan.io/address/0x18306C9550AbfE3F5900d1206FFdce9ce5763A89) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | +| SablierV2LockupDynamic | [0xd6b66A8D797c1e83DdEcE8f483E7D1264B9DFDa6](https://nova.arbiscan.io/address/0xd6b66A8D797c1e83DdEcE8f483E7D1264B9DFDa6) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | +| SablierV2NFTDescriptor | [0xE88d26d1E8802be5cc023264b3FccF63Bc38C20c](https://nova.arbiscan.io/address/0xE88d26d1E8802be5cc023264b3FccF63Bc38C20c) | [core-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.1) | +| SablierV2Comptroller | [0x203f1722d4adb9b67bf652c878d0dc3cc8099113](https://nova.arbiscan.io/address/0x203f1722d4adb9b67bf652c878d0dc3cc8099113) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | + +### Periphery + +| Contract | Address | Deployment | +| :-------------------------- | :------------------------------------------------------------------------------------------------------------------------ | :-------------------------------------------------------------------------------------------- | +| SablierV2Archive | [0x17DE7707D0b25F878Ae4FaC03cdE2481CD616EDd](https://nova.arbiscan.io/address/0x17DE7707D0b25F878Ae4FaC03cdE2481CD616EDd) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyPlugin | [0x1f09ce4be5ad6e76cda6242af91921440df2306e](https://nova.arbiscan.io/address/0x1f09ce4be5ad6e76cda6242af91921440df2306e) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyTargetPermit2 | [0x4487F233bdf7d3C977F936891D5A0Ff1b275A2a8](https://nova.arbiscan.io/address/0x4487F233bdf7d3C977F936891D5A0Ff1b275A2a8) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | + +## Avalanche + +### Core + +| Contract | Address | Deployment | +| :--------------------- | :-------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------- | +| SablierV2LockupLinear | [0x610346E9088AFA70D6B03e96A800B3267E75cA19](https://snowtrace.io/address/0x610346E9088AFA70D6B03e96A800B3267E75cA19) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | +| SablierV2LockupDynamic | [0x665d1C8337F1035cfBe13DD94bB669110b975f5F](https://snowtrace.io/address/0x665d1C8337F1035cfBe13DD94bB669110b975f5F) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | +| SablierV2NFTDescriptor | [0xFd050AFA2e04aA0596947DaD3Ec5690162aDc77F](https://snowtrace.io/address/0xFd050AFA2e04aA0596947DaD3Ec5690162aDc77F) | [core-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.1) | +| SablierV2Comptroller | [0x66F5431B0765D984f82A4fc4551b2c9ccF7eAC9C](https://snowtrace.io/address/0x66F5431B0765D984f82A4fc4551b2c9ccF7eAC9C) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | + +### Periphery + +| Contract | Address | Deployment | +| :-------------------------- | :-------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | +| SablierV2Archive | [0x7b1ef644ce9a625537e9e0c3d7fef3be667e6159](https://snowtrace.io/address/0x7b1ef644ce9a625537e9e0c3d7fef3be667e6159) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyPlugin | [0x17167A7e2763121e263B4331B700a1BF9113b387](https://snowtrace.io/address/0x17167A7e2763121e263B4331B700a1BF9113b387) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyTargetPermit2 | [0x48B4889cf5d6f8360050f9d7606505F1433120BC](https://snowtrace.io/address/0x48B4889cf5d6f8360050f9d7606505F1433120BC) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyTargetApprove | [0x817fE1364A9d57d1fB951945B53942234163Ef10](https://snowtrace.io/address/0x817fE1364A9d57d1fB951945B53942234163Ef10) | [periphery-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.1) | + +## Base + +### Core + +| Contract | Address | Deployment | +| :--------------------- | :-------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------- | +| SablierV2LockupLinear | [0x6b9a46C8377f21517E65fa3899b3A9Fab19D17f5](https://basescan.org/address/0x6b9a46C8377f21517E65fa3899b3A9Fab19D17f5) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | +| SablierV2LockupDynamic | [0x645B00960Dc352e699F89a81Fc845C0C645231cf](https://basescan.org/address/0x645B00960Dc352e699F89a81Fc845C0C645231cf) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | +| SablierV2NFTDescriptor | [0xEFc2896c29F70bc23e82892Df827d4e2259028Fd](https://basescan.org/address/0xEFc2896c29F70bc23e82892Df827d4e2259028Fd) | [core-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.1) | +| SablierV2Comptroller | [0x7Faaedd40B1385C118cA7432952D9DC6b5CbC49e](https://basescan.org/address/0x7Faaedd40B1385C118cA7432952D9DC6b5CbC49e) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | + +### Periphery + +| Contract | Address | Deployment | +| :-------------------------- | :-------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | +| SablierV2Archive | [0x1C5Ac71dd48c7ff291743e5E6e3689ba92F73cC6](https://basescan.org/address/0x1C5Ac71dd48c7ff291743e5E6e3689ba92F73cC6) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyPlugin | [0x50E8B9dC7F28e5cA9253759455C1077e497c4232](https://basescan.org/address/0x50E8B9dC7F28e5cA9253759455C1077e497c4232) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyTargetPermit2 | [0x0648C80b969501c7778b6ff3ba47aBb78fEeDF39](https://basescan.org/address/0x0648C80b969501c7778b6ff3ba47aBb78fEeDF39) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyTargetApprove | [0xf19576Ab425753816eCbF98aca8132A0f693aEc5](https://basescan.org/address/0xf19576Ab425753816eCbF98aca8132A0f693aEc5) | [periphery-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.1) | + +## BNB Smart Chain + +### Core + +| Contract | Address | Deployment | +| :--------------------- | :------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------- | +| SablierV2LockupLinear | [0x3FE4333f62A75c2a85C8211c6AeFd1b9Bfde6e51](https://bscscan.com/address/0x3FE4333f62A75c2a85C8211c6AeFd1b9Bfde6e51) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | +| SablierV2LockupDynamic | [0xF2f3feF2454DcA59ECA929D2D8cD2a8669Cc6214](https://bscscan.com/address/0xF2f3feF2454DcA59ECA929D2D8cD2a8669Cc6214) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | +| SablierV2NFTDescriptor | [0x3daD1bF57edCFF979Fb68a802AC54c5AAfB78F4c](https://bscscan.com/address/0x3daD1bF57edCFF979Fb68a802AC54c5AAfB78F4c) | [core-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.1) | +| SablierV2Comptroller | [0x33511f69A784Fd958E6713aCaC7c9dCF1A5578E8](https://bscscan.com/address/0x33511f69A784Fd958E6713aCaC7c9dCF1A5578E8) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | + +### Periphery + +| Contract | Address | Deployment | +| :-------------------------- | :------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | +| SablierV2Archive | [0xeDe48EB173A869c0b27Cb98CC56d00BC391e5887](https://bscscan.com/address/0xeDe48EB173A869c0b27Cb98CC56d00BC391e5887) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyPlugin | [0xC43b2d8CedB71df30F45dFd9a21eC1E50A813bD6](https://bscscan.com/address/0xC43b2d8CedB71df30F45dFd9a21eC1E50A813bD6) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyTargetPermit2 | [0x135e78B8E17B1d189Af75FcfCC018ab2E6c7b879](https://bscscan.com/address/0x135e78B8E17B1d189Af75FcfCC018ab2E6c7b879) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyTargetApprove | [0xc9bf2A6bD467A813908d836c1506efE61E465761](https://bscscan.com/address/0xc9bf2A6bD467A813908d836c1506efE61E465761) | [periphery-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.1) | + +## Gnosis + +### Core + +| Contract | Address | Deployment | +| :--------------------- | :--------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------- | +| SablierV2LockupLinear | [0x685E92c9cA2bB23f1B596d0a7D749c0603e88585](https://gnosisscan.io/address/0x685E92c9cA2bB23f1B596d0a7D749c0603e88585) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | +| SablierV2LockupDynamic | [0xeb148E4ec13aaA65328c0BA089a278138E9E53F9](https://gnosisscan.io/address/0xeb148E4ec13aaA65328c0BA089a278138E9E53F9) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | +| SablierV2NFTDescriptor | [0x8CE9Cd651e03325Cf6D4Ce9cfa74BE79CDf6d530](https://gnosisscan.io/address/0x8CE9Cd651e03325Cf6D4Ce9cfa74BE79CDf6d530) | [core-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.1) | +| SablierV2Comptroller | [0x73962c44c0fB4cC5e4545FB91732a5c5e87F55C2](https://gnosisscan.io/address/0x73962c44c0fB4cC5e4545FB91732a5c5e87F55C2) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | + +### Periphery + +| Contract | Address | Deployment | +| :-------------------------- | :--------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | +| SablierV2Archive | [0xF4A6F47Da7c6b26b6Dd774671aABA48fb4bFE309](https://gnosisscan.io/address/0xF4A6F47Da7c6b26b6Dd774671aABA48fb4bFE309) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyPlugin | [0xc84f0e95815A576171A19EB9E0fA55a217Ab1536](https://gnosisscan.io/address/0xc84f0e95815A576171A19EB9E0fA55a217Ab1536) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyTargetPermit2 | [0x5B144C3B9C8cfd48297Aeb59B90a024Ef3fCcE92](https://gnosisscan.io/address/0x5B144C3B9C8cfd48297Aeb59B90a024Ef3fCcE92) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyTargetApprove | [0x89AfE038714e547C29Fa881029DD4B5CFB008454](https://gnosisscan.io/address/0x89AfE038714e547C29Fa881029DD4B5CFB008454) | [periphery-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.1) | + +## Optimism + +### Core + +| Contract | Address | Deployment | +| :--------------------- | :------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------- | +| SablierV2LockupLinear | [0xB923aBdCA17Aed90EB5EC5E407bd37164f632bFD](https://optimistic.etherscan.io/address/0xB923aBdCA17Aed90EB5EC5E407bd37164f632bFD) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | +| SablierV2LockupDynamic | [0x6f68516c21E248cdDfaf4898e66b2b0Adee0e0d6](https://optimistic.etherscan.io/address/0x6f68516c21E248cdDfaf4898e66b2b0Adee0e0d6) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | +| SablierV2NFTDescriptor | [0xe0138C596939CC0D2382046795bC163ad5755e0E](https://optimistic.etherscan.io/address/0xe0138C596939CC0D2382046795bC163ad5755e0E) | [core-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.1) | +| SablierV2Comptroller | [0x1EECb6e6EaE6a1eD1CCB4323F3a146A7C5443A10](https://optimistic.etherscan.io/address/0x1EECb6e6EaE6a1eD1CCB4323F3a146A7C5443A10) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | + +### Periphery + +| Contract | Address | Deployment | +| :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | +| SablierV2Archive | [0x9A09eC6f991386718854aDDCEe68647776Befd5b](https://optimistic.etherscan.io/address/0x9A09eC6f991386718854aDDCEe68647776Befd5b) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyPlugin | [0x77C8516B1F327890C956bb38F93Ac2d6B24795Ea](https://optimistic.etherscan.io/address/0x77C8516B1F327890C956bb38F93Ac2d6B24795Ea) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyTargetPermit2 | [0x194ed7D6005C8ba4084A948406545DF299ad37cD](https://optimistic.etherscan.io/address/0x194ed7D6005C8ba4084A948406545DF299ad37cD) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyTargetApprove | [0x8a6974c162fdc7Cb67996F7dB8bAAFb9a99566e0](https://optimistic.etherscan.io/address/0x8a6974c162fdc7Cb67996F7dB8bAAFb9a99566e0) | [periphery-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.1) | + +## Polygon + +### Core + +| Contract | Address | Deployment | +| :--------------------- | :----------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------- | +| SablierV2LockupLinear | [0x67422C3E36A908D5C3237e9cFfEB40bDE7060f6E](https://polygonscan.com/address/0x67422C3E36A908D5C3237e9cFfEB40bDE7060f6E) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | +| SablierV2LockupDynamic | [0x7313AdDb53f96a4f710D3b91645c62B434190725](https://polygonscan.com/address/0x7313AdDb53f96a4f710D3b91645c62B434190725) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | +| SablierV2NFTDescriptor | [0xA820946EaAceB2a85aF123f706f23192c28bC6B9](https://polygonscan.com/address/0xA820946EaAceB2a85aF123f706f23192c28bC6B9) | [core-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.1) | +| SablierV2Comptroller | [0x9761692EDf10F5F2A69f0150e2fd50dcecf05F2E](https://polygonscan.com/address/0x9761692EDf10F5F2A69f0150e2fd50dcecf05F2E) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | + +### Periphery + +| Contract | Address | Deployment | +| :-------------------------- | :----------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | +| SablierV2Archive | [0xA2f5B2e798e7ADd59d85d9b76645E6AC13fC4e1f](https://polygonscan.com/address/0xA2f5B2e798e7ADd59d85d9b76645E6AC13fC4e1f) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyPlugin | [0xBe4cad0e99865CC62787Ecf029aD9DD4815d3d2e](https://polygonscan.com/address/0xBe4cad0e99865CC62787Ecf029aD9DD4815d3d2e) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyTargetPermit2 | [0x576743075fc5F771bbC1376c3267A6185Af9D62B](https://polygonscan.com/address/0x576743075fc5F771bbC1376c3267A6185Af9D62B) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyTargetApprove | [0xccA6dd77bA2cfcccEdA01A82CB309e2A17901682](https://polygonscan.com/address/0xccA6dd77bA2cfcccEdA01A82CB309e2A17901682) | [periphery-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.1) | + +## Scroll + +### Core + +| Contract | Address | Deployment | +| :--------------------- | :---------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------- | +| SablierV2LockupLinear | [0x80640ca758615ee83801EC43452feEA09a202D33](https://scrollscan.com/address/0x80640ca758615ee83801EC43452feEA09a202D33) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | +| SablierV2LockupDynamic | [0xde6a30D851eFD0Fc2a9C922F294801Cfd5FCB3A1](https://scrollscan.com/address/0xde6a30D851eFD0Fc2a9C922F294801Cfd5FCB3A1) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | +| SablierV2NFTDescriptor | [0xC1fa624733203F2B7185c3724039C4D5E5234fE4](https://scrollscan.com/address/0xC1fa624733203F2B7185c3724039C4D5E5234fE4) | [core-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.1) | +| SablierV2Comptroller | [0x859708495E3B3c61Bbe19e6E3E1F41dE3A5C5C5b](https://scrollscan.com/address/0x859708495E3B3c61Bbe19e6E3E1F41dE3A5C5C5b) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | + +### Periphery + +| Contract | Address | Deployment | +| :-------------------------- | :---------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | +| SablierV2Archive | [0x94A18AC6e4B7d97E31f1587f6a666Dc5503086c3](https://scrollscan.com/address/0x94A18AC6e4B7d97E31f1587f6a666Dc5503086c3) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyPlugin | [0xED1591BD6038032a74D786A452A23536b3201490](https://scrollscan.com/address/0xED1591BD6038032a74D786A452A23536b3201490) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyTargetPermit2 | [0x91154fc80933D25793E6B4D7CE19fb51dE6794B7](https://scrollscan.com/address/0x91154fc80933D25793E6B4D7CE19fb51dE6794B7) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyTargetApprove | [0x71CeA9c4d15fed2E58785cE0C05165CE34313A74](https://scrollscan.com/address/0x71CeA9c4d15fed2E58785cE0C05165CE34313A74) | [periphery-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.1) | + +## Arbitrum Goerli + +### Core + +| Contract | Address | Deployment | +| :--------------------- | :-------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------- | +| SablierV2LockupLinear | [0x323B629635b6cFfe2453Aa2869c5957AfF55F445](https://goerli.arbiscan.io/address/0x323B629635b6cFfe2453Aa2869c5957AfF55F445) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | +| SablierV2LockupDynamic | [0xdc0a619fF975de6a08c7615ea383533fd265f2e3](https://goerli.arbiscan.io/address/0xdc0a619fF975de6a08c7615ea383533fd265f2e3) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | +| SablierV2NFTDescriptor | [0x740509d893BC15a31EAE8542683Ed32085c559cB](https://goerli.arbiscan.io/address/0x740509d893BC15a31EAE8542683Ed32085c559cB) | [core-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.1) | +| SablierV2Comptroller | [0xECF737BDb9BB094489beCa39f0b9Ae66E0C14ba8](https://goerli.arbiscan.io/address/0xECF737BDb9BB094489beCa39f0b9Ae66E0C14ba8) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | + +### Periphery + +| Contract | Address | Deployment | +| :-------------------------- | :-------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | +| SablierV2Archive | [0x4371d767Cd7991248D20eD61d425e1e70c6CEEab](https://goerli.arbiscan.io/address/0x4371d767Cd7991248D20eD61d425e1e70c6CEEab) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyPlugin | [0xD37832B8993bEe6F41A8183967a7488C6e2a3551](https://goerli.arbiscan.io/address/0xD37832B8993bEe6F41A8183967a7488C6e2a3551) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyTargetPermit2 | [0x2Ebd987e12432Ee3a74Fe0A55Afe5D866096e354](https://goerli.arbiscan.io/address/0x2Ebd987e12432Ee3a74Fe0A55Afe5D866096e354) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | + +## Arbitrum Sepolia + +### Core + +| Contract | Address | Deployment | +| :--------------------- | :--------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------- | +| SablierV2LockupLinear | [0xa3e36b51B7A456812c92253780f4B15bad56e34c](https://sepolia.arbiscan.io/address/0xa3e36b51B7A456812c92253780f4B15bad56e34c) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | +| SablierV2LockupDynamic | [0x7938c18a59FaD2bA11426AcfBe8d74F0F598a4D2](https://sepolia.arbiscan.io/address/0x7938c18a59FaD2bA11426AcfBe8d74F0F598a4D2) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | +| SablierV2NFTDescriptor | [0xEe93BFf599C17C6fF8e31F2De6c3e40bd5e51312](https://sepolia.arbiscan.io/address/0xEe93BFf599C17C6fF8e31F2De6c3e40bd5e51312) | [core-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.1) | +| SablierV2Comptroller | [0xA6A0cfA3442053fbB516D55205A749Ef2D33aed9](https://sepolia.arbiscan.io/address/0xA6A0cfA3442053fbB516D55205A749Ef2D33aed9) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | + +### Periphery + +| Contract | Address | Deployment | +| :-------------------------- | :--------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | +| SablierV2Archive | [0x2C8fA48361C7D48Dc21b27a3D549402Cf8AE16B0](https://sepolia.arbiscan.io/address/0x2C8fA48361C7D48Dc21b27a3D549402Cf8AE16B0) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyPlugin | [0x7D310803c3824636bAff74e4f80e81ece167c440](https://sepolia.arbiscan.io/address/0x7D310803c3824636bAff74e4f80e81ece167c440) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyTargetPermit2 | [0x396A3a169918A4C0B339ECf86C583f46D696254E](https://sepolia.arbiscan.io/address/0x396A3a169918A4C0B339ECf86C583f46D696254E) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | + +## Goerli + +### Core + +| Contract | Address | Deployment | +| :--------------------- | :--------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------- | +| SablierV2LockupLinear | [0x6E3678c005815Ab34986D8d66A353Cd3699103DE](https://goerli.etherscan.io/address/0x6E3678c005815Ab34986D8d66A353Cd3699103DE) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | +| SablierV2LockupDynamic | [0x4BE70EDe968e9dBA12DB42b9869Bec66bEDC17d7](https://goerli.etherscan.io/address/0x4BE70EDe968e9dBA12DB42b9869Bec66bEDC17d7) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | +| SablierV2NFTDescriptor | [0x1D83CDd66BCf0ea8c99E745cC868478d6C3633f0](https://goerli.etherscan.io/address/0x1D83CDd66BCf0ea8c99E745cC868478d6C3633f0) | [core-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.1) | +| SablierV2Comptroller | [0x9B75F65bCCd05545C400145Cca29dA52DA57AC2b](https://goerli.etherscan.io/address/0x9B75F65bCCd05545C400145Cca29dA52DA57AC2b) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | + +### Periphery + +| Contract | Address | Deployment | +| :-------------------------- | :--------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | +| SablierV2Archive | [0xFd14E62e6fe4d96F033cf972556ae56D09Bd49cA](https://goerli.etherscan.io/address/0xFd14E62e6fe4d96F033cf972556ae56D09Bd49cA) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyPlugin | [0x9CA1dFFC744318198bE9Cf92283A803CE16b698a](https://goerli.etherscan.io/address/0x9CA1dFFC744318198bE9Cf92283A803CE16b698a) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyTargetPermit2 | [0x0eE01680645c361B740ab4dCDdF238988eB20411](https://goerli.etherscan.io/address/0x0eE01680645c361B740ab4dCDdF238988eB20411) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyTargetApprove | [0x0e563B883dfe11469915194F8651a65212fdB96F](https://goerli.etherscan.io/address/0x0e563B883dfe11469915194F8651a65212fdB96F) | [periphery-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.1) | + +## Sepolia + +### Core + +| Contract | Address | Deployment | +| :--------------------- | :---------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------- | +| SablierV2LockupLinear | [0xd4300c5bc0b9e27c73ebabdc747ba990b1b570db](https://sepolia.etherscan.io/address/0xd4300c5bc0b9e27c73ebabdc747ba990b1b570db) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | +| SablierV2LockupDynamic | [0x421e1E7a53FF360f70A2D02037Ee394FA474e035](https://sepolia.etherscan.io/address/0x421e1E7a53FF360f70A2D02037Ee394FA474e035) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | +| SablierV2NFTDescriptor | [0x3cb51943ebcea05b23c35c50491b3d296ff675db](https://sepolia.etherscan.io/address/0x3cb51943ebcea05b23c35c50491b3d296ff675db) | [core-v1.0.1](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.1) | +| SablierV2Comptroller | [0x2006d43E65e66C5FF20254836E63947FA8bAaD68](https://sepolia.etherscan.io/address/0x2006d43E65e66C5FF20254836E63947FA8bAaD68) | [core-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/core/v1.0.0) | + +### Periphery + +| Contract | Address | Deployment | +| :-------------------------- | :---------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------- | +| SablierV2Archive | [0x83495d8DF6221f566232e1353a6e7231A86C61fF](https://sepolia.etherscan.io/address/0x83495d8DF6221f566232e1353a6e7231A86C61fF) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyPlugin | [0xa333c8233CfD04740E64AB4fd5447995E357561B](https://sepolia.etherscan.io/address/0xa333c8233CfD04740E64AB4fd5447995E357561B) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyTargetPermit2 | [0x5091900B7cF803a7407FCE6333A6bAE4aA779Fd4](https://sepolia.etherscan.io/address/0x5091900B7cF803a7407FCE6333A6bAE4aA779Fd4) | [periphery-v1.0.0](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.0) | +| SablierV2ProxyTargetApprove | [0x105E7728C5706Ad41d194EbDc7873B047352F3d2](https://sepolia.etherscan.io/address/0x105E7728C5706Ad41d194EbDc7873B047352F3d2) | [periphery-v1.0.1 ](https://github.com/sablier-labs/v2-deployments/tree/main/periphery/v1.0.1) | diff --git a/docs/contracts/v2/guides/01-local-environment.md b/docs/contracts/v2/guides/01-local-environment.md index 7c680a04..50c81104 100644 --- a/docs/contracts/v2/guides/01-local-environment.md +++ b/docs/contracts/v2/guides/01-local-environment.md @@ -17,7 +17,7 @@ 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) -- [Pnpm](https://pnpm.io) +- [Yarn](https://yarnpkg.com/) In addition, familiarity with [Ethereum](https://ethereum.org/) and [Solidity](https://soliditylang.org/) is requisite. @@ -38,7 +38,7 @@ To install the template, simply execute the following commands: $ mkdir sablier-v2-integration-template $ cd sablier-v2-integration-template $ forge init --template sablier-labs/sablier-v2-integration-template -$ pnpm install +$ yarn install ``` Then, hop to the `Run a Fork Test` section to complete your set up and start developing. @@ -79,13 +79,13 @@ The folder structure should be intuitive: - `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 -Let's install the Sablier Node.js packages using Pnpm: +Let's install the Sablier Node.js packages using Yarn: ```shell -$ pnpm install @sablier/v2-core @sablier/v2-periphery +$ yarn add @sablier/v2-core @sablier/v2-periphery ``` -Pnpm will download the Sablier contracts and put them in the `node_modules` directory. +Yarn will download the Sablier contracts, along with their dependencies, and put them in the `node_modules` directory. Let's remap the package names to point to the installed contracts. This step is required so that the Solidity compiler can find the Sablier contracts when you import them: diff --git a/docs/contracts/v2/guides/05-hooks.md b/docs/contracts/v2/guides/05-hooks.md index 79a39f0a..e14130a3 100644 --- a/docs/contracts/v2/guides/05-hooks.md +++ b/docs/contracts/v2/guides/05-hooks.md @@ -26,14 +26,6 @@ Doing so enables your contract to keep its internal accounting updated accuratel ::: -### Sender - -And these are the hooks that can be implemented by a sender contract: - -| Hook | Arguments | Description | -| ------------------ | ------------------------------------------------ | ------------------------------------------------- | -| `onStreamCanceled` | `(streamId,sender,senderAmount,recipientAmount)` | Called when the stream is canceled by the sender. | - ### Sample Implementations #### Recipient @@ -42,12 +34,6 @@ And these are the hooks that can be implemented by a sender contract: https://github.com/sablier-labs/examples/blob/main/v2/core/RecipientHooks.sol ``` -#### Sender - -```solidity reference title="Sablier Sender Hooks" -https://github.com/sablier-labs/examples/blob/main/v2/core/SenderHooks.sol -``` - ## Error Management :::danger diff --git a/docs/contracts/v2/guides/06-flash-loans.md b/docs/contracts/v2/guides/06-flash-loans.md index 638db073..cdd7964a 100644 --- a/docs/contracts/v2/guides/06-flash-loans.md +++ b/docs/contracts/v2/guides/06-flash-loans.md @@ -4,6 +4,12 @@ sidebar_position: 6 title: "Flash Loans" --- +:::note + +The actual Sablier contracts **do not** implement flash loans. This guide is just informative for how they could be. + +::: + Our flash loan implementation adheres to the [ERC-3156](https://eips.ethereum.org/EIPS/eip-3156) specification, which means that you can use any existing flash borrower implementation to flash loan ERC-20 assets from our contracts. diff --git a/docs/contracts/v2/guides/07-frontend.md b/docs/contracts/v2/guides/07-frontend.md index a4a2632b..aa032c02 100644 --- a/docs/contracts/v2/guides/07-frontend.md +++ b/docs/contracts/v2/guides/07-frontend.md @@ -24,19 +24,3 @@ Let's start by creating a new Next.js project: ```sh npx create-next-app my-project ``` - -## Proxy - -:::note - -This section is a stub. - -::: - -## Permit2 - -:::note - -This section is a stub. - -::: diff --git a/docs/contracts/v2/guides/09-etherscan.md b/docs/contracts/v2/guides/09-etherscan.md index 1ff6201a..e0d25e38 100644 --- a/docs/contracts/v2/guides/09-etherscan.md +++ b/docs/contracts/v2/guides/09-etherscan.md @@ -288,7 +288,7 @@ followed by 8 zeroes). The same logic applies to the [total amounts](#total-amou ```ts { - spender: "0xB10daee1FCF62243aE27776D7a92D39dC8740f95", + spender: "0xAFb979d9afAd1aD27C5eFf4E27226E3AB9e5dCC9", amount: 100000000000000000000 } ``` diff --git a/docs/contracts/v2/guides/batch-create-streams/01-batch-linear-streams.mdx b/docs/contracts/v2/guides/batch-create-streams/01-batch-linear-streams.mdx new file mode 100644 index 00000000..a2b0d84b --- /dev/null +++ b/docs/contracts/v2/guides/batch-create-streams/01-batch-linear-streams.mdx @@ -0,0 +1,132 @@ +--- +id: "batch-linear-streams" +sidebar_position: 1 +title: "Batch Linear Streams" +--- + +import AdmonitionSimpleCode from "@site/docs/snippets/AdmonitionSimpleCode.mdx"; +import BatchCommonSteps from "@site/docs/snippets/BatchCommonSteps.mdx"; +import ConstantsComment from "@site/docs/snippets/ConstantsComment.mdx"; + +# Create a Batch of Lockup Linear Streams + +In this guide, we will show you how to programmatically batch create linear streams via the Sablier's +[Batch](/contracts/v2/reference/periphery/contract.SablierV2Batch) contract. + +This guide assumes that you have already gone through the [Protocol Concepts](/concepts/protocol/streaming) section. + +:::note + +This guide interacts with the Periphery contract. + +::: + + + +## 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.19; +``` + +Now, import the relevant symbols from `@sablier/v2-core` and `@sablier/v2-periphery`: + +```solidity +import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; +import { Broker, LockupLinear } from "@sablier/v2-core/src/types/LockupLinear.sol"; +import { ud60x18 } from "@sablier/v2-core/src/types/Math.sol"; +import { IERC20 } from "@sablier/v2-core/src/types/Tokens.sol"; +import { ISablierV2Batch } from "@sablier/v2-periphery/src/interfaces/ISablierV2Batch.sol"; +import { Batch} from "@sablier/v2-periphery/src/types/DataTypes.sol"; +``` + +Create a contract called `BatchLockupLinearStreamCreator`, and declare a constant `DAI` of type `IERC20`, a constant +`LOCKUP_LINEAR` of type `ISablierV2LockupLinear`, and a constant `BATCH` of type `ISablierV2Batch`: + +```solidity +contract BatchLockupLinearStreamCreator { + IERC20 public constant DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); + ISablierV2LockupLinear public constant LOCKUP_LINEAR = + ISablierV2LockupLinear(0xAFb979d9afAd1aD27C5eFf4E27226E3AB9e5dCC9); + ISablierV2Batch public constant BATCH = ISablierV2Batch(0xEa07DdBBeA804E7fe66b958329F8Fa5cDA95Bd55); +} +``` + + + +## Batch create functions + +There are two batch create functions for the Lockup Linear contract: + +- [`createWithDurations`](/contracts/v2/reference/periphery/contract.SablierV2Batch#createwithdurations) +- [`createWithRange`](/contracts/v2/reference/periphery/contract.SablierV2Batch#createwithrange) + +Which one you choose depends upon your use case. In this guide, we will use `createWithDurations`. + + + +## Stream Parameters + +Given that we declared a `batchSize` of two, we need to define two +[Batch.CreateWithDurations](/contracts/v2/reference/periphery/types/library.Batch#createwithdurations) structs: + +```solidity +// Declare the first stream in the batch +Batch.CreateWithDurations memory stream0; +stream0.sender = address(0xABCD); // The sender to stream the assets, he will be able to cancel the stream +stream0.recipient = address(0xCAFE); // The recipient of the streamed assets +stream0.totalAmount = perStreamAmount; // The total amount of each stream, inclusive of all fees +stream0.cancelable = true; // Whether the stream will be cancelable or not +stream0.durations = LockupLinear.Durations({ + cliff: 4 weeks, // Assets will be unlocked only after 4 weeks + total: 52 weeks // Setting a total duration of ~1 year + }); +stream0.broker = Broker(address(0), ud60x18(0)); // Optional parameter left undefined +``` + +To add some variety, we will change the parameters of the second stream: + +```solidity +// Declare the second stream in the batch +Batch.CreateWithDurations memory stream1; +stream1.sender = address(0xABCD); // The sender to stream the assets, he will be able to cancel the stream +stream1.recipient = address(0xBEEF); // The recipient of the streamed assets +stream1.totalAmount = perStreamAmount; // The total amount of each stream, inclusive of all fees +stream1.cancelable = false; // Whether the stream will be cancelable or not +stream1.durations = LockupLinear.Durations({ + cliff: 1 weeks, // Assets will be unlocked only after 1 week + total: 26 weeks // Setting a total duration of ~6 months + }); +stream1.broker = Broker(address(0), ud60x18(0)); // Optional parameter left undefined +``` + +Once both structs are declared, the batch array has to be filled: + +```solidity +// Fill the batch param +Batch.CreateWithDurations[] memory batch = new Batch.CreateWithDurations[](batchSize); +batch[0] = stream0; +batch[1] = stream1; +``` + +## Invoke the batch create function + +With all parameters set, we can now call the `createWithDurations` function, and assign the ids of the newly created +streams to the array: + +```solidity +streamIds = BATCH.createWithDurations(LOCKUP_LINEAR, DAI, batch); +``` + +## The complete Batch Lockup Linear stream creator contract + +Below you can see the complete functioning code: a contract that batch creates Lockup Linear streams using Sablier's +`Batch` that start at `block.timestamp`. You can access the code on GitHub through this +[link](https://github.com/sablier-labs/examples/blob/main/v2/periphery/BatchLockupLinearStreamCreator.sol). + +```solidity reference title="Batch Lockup Linear stream creator" +https://github.com/sablier-labs/examples/blob/main/v2/periphery/BatchLockupLinearStreamCreator.sol +``` diff --git a/docs/contracts/v2/guides/batch-create-streams/02-batch-dynamic-streams.mdx b/docs/contracts/v2/guides/batch-create-streams/02-batch-dynamic-streams.mdx new file mode 100644 index 00000000..0f12dae0 --- /dev/null +++ b/docs/contracts/v2/guides/batch-create-streams/02-batch-dynamic-streams.mdx @@ -0,0 +1,153 @@ +--- +id: "batch-dynamic-streams" +sidebar_position: 2 +title: "Batch Dynamic Streams" +--- + +import AdmonitionSimpleCode from "@site/docs/snippets/AdmonitionSimpleCode.mdx"; +import BatchCommonSteps from "@site/docs/snippets/BatchCommonSteps.mdx"; +import ConstantsComment from "@site/docs/snippets/ConstantsComment.mdx"; + +# Create a Batch of Lockup Dynamic Streams + +In this guide, we will show you how to programmatically batch create dynamic streams via the Sablier's +[Batch](/contracts/v2/reference/periphery/contract.SablierV2Batch) contract. + +This guide assumes that you have already gone through the [Protocol Concepts](/concepts/protocol/streaming) section. + +:::note + +This guide interacts with the Periphery contract. + +::: + + + +## 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.19; +``` + +Now, import the relevant symbols from `@sablier/v2-core` and `@sablier/v2-periphery`: + +```solidity +import { ISablierV2LockupDynamic } from "@sablier/v2-core/src/interfaces/ISablierV2LockupDynamic.sol"; +import { Broker, LockupDynamic } from "@sablier/v2-core/src/types/LockupDynamic.sol"; +import { ud60x18 } from "@sablier/v2-core/src/types/Math.sol"; +import { IERC20 } from "@sablier/v2-core/src/types/Tokens.sol"; +import { ISablierV2Batch } from "@sablier/v2-periphery/src/interfaces/ISablierV2Batch.sol"; +import { Batch} from "@sablier/v2-periphery/src/types/DataTypes.sol"; +``` + +Create a contract called `BatchLockupDynamicStreamCreator`, and declare a constant `DAI` of type `IERC20`, a constant +`LOCKUP_DYNAMIC` of type `ISablierV2LockupDynamic`, and a constant `BATCH` of type `ISablierV2Batch`: + +```solidity +contract BatchLockupDynamicStreamCreator { + IERC20 public constant DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); + ISablierV2LockupDynamic public constant LOCKUP_DYNAMIC = + ISablierV2LockupDynamic(0x7CC7e125d83A581ff438608490Cc0f7bDff79127); + ISablierV2Batch public constant BATCH = ISablierV2Batch(0xEa07DdBBeA804E7fe66b958329F8Fa5cDA95Bd55); +} +``` + + + +## Batch create functions + +There are two batch create functions for the Lockup Dynamic contract: + +- [`createWithDeltas`](/contracts/v2/reference/periphery/contract.SablierV2Batch#createwithdeltas) +- [`createWithMilestones`](/contracts/v2/reference/periphery/contract.SablierV2Batch#createwithmilestones) + +Which one you choose depends upon your use case. In this guide, we will use `createWithMilestones`. + + + +## Stream Parameters + +Given that we declared a `batchSize` of two, we need to define two +[Batch.CreateWithMilestones](/contracts/v2/reference/periphery/types/library.Batch#createwithmilestones) structs: + +```solidity +// Declare the first stream in the batch +Batch.CreateWithMilestones memory stream0; +stream0.sender = address(0xABCD); // The sender to stream the assets, he will be able to cancel the stream +stream0.recipient = address(0xCAFE); // The recipient of the streamed assets +stream0.totalAmount = perStreamAmount; // The total amount of each stream, inclusive of all fees +stream0.cancelable = true; // Whether the stream will be cancelable or not +stream0.broker = Broker(address(0), ud60x18(0)); // Optional parameter left undefined + +// Declare some dummy segments +stream0.segments = new LockupDynamic.Segment[](2); +stream0.segments[0] = LockupDynamic.Segment({ + amount: uint128(perStreamAmount / 2), + exponent: ud2x18(0.25e18), + milestone: uint40(block.timestamp + 1 weeks) +}); +stream0.segments[1] = ( + LockupDynamic.Segment({ + amount: uint128(perStreamAmount - stream0.segments[0].amount), + exponent: ud2x18(2.71e18), + milestone: uint40(block.timestamp + 24 weeks) + }) +); +``` + +To add some variety, we will change the parameters of the second stream: + +```solidity +Batch.CreateWithMilestones memory stream1; +stream1.sender = address(0xABCD); // The sender to stream the assets, he will be able to cancel the stream +stream1.recipient = address(0xBEEF); // The recipient of the streamed assets +stream1.totalAmount = uint128(perStreamAmount); // The total amount of each stream, inclusive of all fees +stream1.cancelable = false; // Whether the stream will be cancelable or not +stream1.broker = Broker(address(0), ud60x18(0)); // Optional parameter left undefined + +// Declare some dummy segments +stream1.segments = new LockupDynamic.Segment[](2); +stream1.segments[0] = LockupDynamic.Segment({ + amount: uint128(perStreamAmount / 4), + exponent: ud2x18(1e18), + milestone: uint40(block.timestamp + 4 weeks) +}); +stream1.segments[1] = ( + LockupDynamic.Segment({ + amount: uint128(perStreamAmount - stream1.segments[0].amount), + exponent: ud2x18(3.14e18), + milestone: uint40(block.timestamp + 52 weeks) + }) +); +``` + +Once both structs are declared, the batch array has to be filled: + +```solidity +// Fill the batch array +Batch.CreateWithMilestones[] memory batch = new Batch.CreateWithMilestones[](batchSize); +batch[0] = stream0; +batch[1] = stream1; +``` + +## Invoke the batch create function + +With all parameters set, we can now call the `createWithMilestones` function, and assign the ids of the newly created +streams to the array: + +```solidity +streamIds = BATCH.createWithMilestones(LOCKUP_DYNAMIC, DAI, batch); +``` + +## The complete Batch Lockup Dynamic stream creator contract + +Below you can see the complete functioning code: a contract that batch creates Lockup Dynamic streams using Sablier's +`Batch` that start at `block.timestamp`. You can access the code on GitHub through this +[link](https://github.com/sablier-labs/examples/blob/main/v2/periphery/BatchLockupDynamicStreamCreator.sol). + +```solidity reference title="Batch Lockup Dynamic stream creator" +https://github.com/sablier-labs/examples/blob/main/v2/periphery/BatchLockupDynamicStreamCreator.sol +``` diff --git a/docs/contracts/v2/guides/batch-create-streams/_category_.json b/docs/contracts/v2/guides/batch-create-streams/_category_.json new file mode 100644 index 00000000..06815fa5 --- /dev/null +++ b/docs/contracts/v2/guides/batch-create-streams/_category_.json @@ -0,0 +1,8 @@ +{ + "collapsed": true, + "label": "Batch Create Streams", + "link": { + "id": "batch-create-streams" + }, + "position": 3 +} diff --git a/docs/contracts/v2/guides/create-stream/01-lockup-linear.mdx b/docs/contracts/v2/guides/create-stream/01-lockup-linear.mdx index 727f9069..bdb6282a 100644 --- a/docs/contracts/v2/guides/create-stream/01-lockup-linear.mdx +++ b/docs/contracts/v2/guides/create-stream/01-lockup-linear.mdx @@ -5,7 +5,7 @@ title: "Lockup Linear" --- import AdmonitionSimpleCode from "@site/docs/snippets/AdmonitionSimpleCode.mdx"; -import ERC20Steps from "@site/docs/snippets/ERC20Steps.mdx"; +import ConstantsComment from "@site/docs/snippets/ConstantsComment.mdx"; import ParamAsset from "@site/docs/snippets/ParamAsset.mdx"; import ParamBroker from "@site/docs/snippets/ParamBroker.mdx"; import ParamCancelable from "@site/docs/snippets/ParamCancelable.mdx"; @@ -22,9 +22,7 @@ This guide assumes that you have already gone through the [Protocol Concepts](/c :::note -This guide interacts with the Core contracts directly. To get access to the whole gamut of Sablier features, we -recommend creating streams via the Periphery contracts instead. See the -[Proxy Architecture](/contracts/v2/guides/proxy-architecture/overview) section. +This guide interacts with the Core contracts directly. ::: @@ -48,31 +46,18 @@ import { ud60x18 } from "@sablier/v2-core/src/types/Math.sol"; import { IERC20 } from "@sablier/v2-core/src/types/Tokens.sol"; ``` -Create a contract called `LockupLinearStreamCreator`, and declare a constant `DAI` of type `IERC20` and an immutable -variable `sablier` of type `ISablierV2LockupLinear`: +Create a contract called `LockupLinearStreamCreator`, and declare a constant `DAI` of type `IERC20` and a constant +`LOCKUP_LINEAR` of type `ISablierV2LockupLinear`: ```solidity contract LockupLinearStreamCreator { IERC20 public constant DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); - ISablierV2LockupLinear public immutable sablier; + ISablierV2LockupLinear public constant LOCKUP_LINEAR = + ISablierV2LockupLinear(0xAFb979d9afAd1aD27C5eFf4E27226E3AB9e5dCC9); } ``` -In the code above, the address of the [DAI](https://makerdao.com) stablecoin is hard-coded. However, in production, you -would likely use an input parameter to allow the contract to change the assets it interacts with on a per transaction -basis. - -## Initialization - -To initialize the Sablier contract, you first need to grab its address from the -[Deployment Addresses](/contracts/v2/deployments) page. Once you have obtained it, pass it to the constructor of the -creator contract: - -```solidity -constructor(ISablierV2LockupLinear sablier_) { - sablier = sablier_; -} -``` + ## Create functions @@ -87,16 +72,31 @@ Which one you choose depends upon your use case. In this guide, we will use `cre ## Function definition -Define a function called `createLockupLinearStream` which takes a single parameter `totalAmount`, and which returns the -id of the created stream: +Define a function called `createStream` which takes a single parameter `totalAmount`, and which returns the id of the +created stream: ```solidity -function createLockupLinearStream(uint256 totalAmount) public returns (uint256 streamId) { +function createStream(uint128 totalAmount) public returns (uint256 streamId) { // ... } ``` - +## ERC-20 steps + +To create a stream, the caller must approve the creator contract to pull the tokens from the calling address's account. +Then, we have to also approve the Sablier contract to pull the assets that the creator contract will be in possession of +after they are transferred from the calling address (you): + +```solidity +// Transfer the provided amount of DAI tokens to this contract +DAI.transferFrom(msg.sender, address(this), totalAmount); + +// Approve the Sablier contract to spend DAI +DAI.approve(address(LOCKUP_LINEAR), totalAmount); +``` + +For more guidance on how to approve and transfer ERC-20 assets, see +[this article](https://ethereum.org/en/developers/docs/standards/tokens/erc-20/) on the Ethereum website. ## Parameters @@ -152,5 +152,3 @@ Below you can see the complete functioning code: a contract that creates Lockup ```solidity reference title="Lockup Linear Stream Creator" https://github.com/sablier-labs/examples/blob/main/v2/core/LockupLinearStreamCreator.sol ``` - -[^1]: If creating streams on behalf of end users, they will have to approve your contract first. diff --git a/docs/contracts/v2/guides/create-stream/02-lockup-dynamic.mdx b/docs/contracts/v2/guides/create-stream/02-lockup-dynamic.mdx index 9f9beee6..3446160b 100644 --- a/docs/contracts/v2/guides/create-stream/02-lockup-dynamic.mdx +++ b/docs/contracts/v2/guides/create-stream/02-lockup-dynamic.mdx @@ -5,7 +5,7 @@ title: "Lockup Dynamic" --- import AdmonitionSimpleCode from "@site/docs/snippets/AdmonitionSimpleCode.mdx"; -import ERC20Steps from "@site/docs/snippets/ERC20Steps.mdx"; +import ConstantsComment from "@site/docs/snippets/ConstantsComment.mdx"; import ParamAsset from "@site/docs/snippets/ParamAsset.mdx"; import ParamBroker from "@site/docs/snippets/ParamBroker.mdx"; import ParamCancelable from "@site/docs/snippets/ParamCancelable.mdx"; @@ -22,9 +22,7 @@ This guide assumes that you have already gone through the [Protocol Concepts](/c :::note -This guide interacts with the Core contracts directly. To get access to the whole gamut of Sablier features, we -recommend creating streams via the Periphery contracts instead. See the -[Proxy Architecture](/contracts/v2/guides/proxy-architecture/overview) section. +This guide interacts with the Core contracts directly. ::: @@ -48,31 +46,18 @@ import { ud2x18, ud60x18 } from "@sablier/v2-core/src/types/Math.sol"; import { IERC20 } from "@sablier/v2-core/src/types/Tokens.sol"; ``` -Create a contract called `LockupDynamicStreamCreator`, and declare a constant `DAI` of type `IERC20` and an immutable -variable `sablier` of type `ISablierV2LockupDynamic`: +Create a contract called `LockupDynamicStreamCreator`, and declare a constant `DAI` of type `IERC20` and a constant +`LOCKUP_DYNAMIC` of type `ISablierV2LockupDynamic`: ```solidity contract LockupDynamicStreamCreator { IERC20 public constant DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); - ISablierV2LockupDynamic public immutable sablier; + ISablierV2LockupDynamic public constant LOCKUP_Dynamic = + ISablierV2LockupDynamic(0x7CC7e125d83A581ff438608490Cc0f7bDff79127); } ``` -In the code above, the address of the [DAI](https://makerdao.com) stablecoin is hard-coded. However, in production, you -would likely use an input parameter to allow the contract to change the assets it interacts with on a per transaction -basis. - -## Initialization - -To initialize the Sablier contract, you first need to grab its address from the -[Deployment Addresses](/contracts/v2/deployments) page. Once you have obtained it, pass it to the constructor of the -creator contract: - -```solidity -constructor(ISablierV2LockupDynamic sablier_) { - sablier = sablier_; -} -``` + ## Create functions @@ -87,11 +72,11 @@ Which one you choose depends upon your use case. In this guide, we will use `cre ## Function definition -Define a function called `createLockupDynamicStream` which takes two parameters, `amount0` and `amount1`, and which -returns the id of the created stream: +Define a function called `createStream` which takes two parameters, `amount0` and `amount1`, and which returns the id of +the created stream: ```solidity -function createLockupDynamicStream(uint256 amount0, uint256 amount1) public returns (uint256 streamId) { +function createStream(uint128 amount0, uint128 amount1) public returns (uint256 streamId) { // ... } ``` @@ -103,7 +88,22 @@ of the steps below: uint256 totalAmount = amount0 + amount1; ``` - +## ERC-20 steps + +To create a stream, the caller must approve the creator contract to pull the tokens from the calling address's account. +Then, we have to also approve the Sablier contract to pull the assets that the creator contract will be in possession of +after they are transferred from the calling address (you): + +```solidity +// Transfer the provided amount of DAI tokens to this contract +DAI.transferFrom(msg.sender, address(this), totalAmount); + +// Approve the Sablier contract to spend DAI +DAI.approve(address(LOCKUP_DYNAMIC), totalAmount); +``` + +For more guidance on how to approve and transfer ERC-20 assets, see +[this article](https://ethereum.org/en/developers/docs/standards/tokens/erc-20/) on the Ethereum website. ## Parameters @@ -154,13 +154,13 @@ Let's define two dummy segments: ```solidity params.segments = new LockupDynamic.Segment[](2); params.segments[0] = LockupDynamic.Segment({ - amount: uint128(amount0), + amount: amount0, exponent: ud2x18(1e18), milestone: uint40(block.timestamp + 4 weeks) }); params.segments[1] = ( LockupDynamic.Segment({ - amount: uint128(amount1), + amount: amount1, exponent: ud2x18(3.14e18), milestone: uint40(block.timestamp + 52 weeks) }) @@ -189,17 +189,15 @@ With all parameters set, we can now call the `createWithMilestones` function, an stream to a variable: ```solidity -streamId = lockupDynamic.createWithMilestones(params); +streamId = LOCKUP_DYNAMIC.createWithMilestones(params); ``` ## The complete Lockup Dynamic stream creator contract -Below you can see the complete functioning code: a contract that creates Lockup Dynamic streams with predefined segment +Below you can see the complete functioning code: a contract that creates a Lockup Dynamic stream with predefined segment milestones. You can access the code on GitHub through [this link](https://github.com/sablier-labs/examples/blob/main/v2/core/LockupDynamicStreamCreator.sol). ```solidity reference title="Lockup Dynamic Stream Creator" https://github.com/sablier-labs/examples/blob/main/v2/core/LockupDynamicStreamCreator.sol ``` - -[^1]: If creating streams on behalf of end users, they will have to approve your contract first. diff --git a/docs/contracts/v2/guides/proxy-architecture/01-overview.mdx b/docs/contracts/v2/guides/proxy-architecture/01-overview.mdx deleted file mode 100644 index 14418c79..00000000 --- a/docs/contracts/v2/guides/proxy-architecture/01-overview.mdx +++ /dev/null @@ -1,52 +0,0 @@ ---- -id: "overview" -sidebar_position: 1 -title: "Overview" ---- - -Sablier V2 uses a forwarding proxy architecture designed to make the protocol more modular and extensible without -introducing upgradeability. The particular forwarding proxy contract that we are using is -[PRBProxy](https://github.com/PaulRBerg/prb-proxy). You can think of this as an extension of externally-owned accounts -([EOAs](https://ethereum.org/en/developers/docs/accounts/#types-of-account) in short). - -When using the Sablier Interface, senders are required to deploy a proxy before being able to interact with the Sablier -Protocol. However, deploying the proxy is a one-time event. - -:::note - -**Do not** confuse this approach with upgradable proxies. Despite having a similar name, these are two different -concepts. PRBProxy is not upgradeable. - -::: - -:::info - -While reading the pages under this guide, it might be helpful to keep the following docs in separate tabs: -[Access Control](/contracts/v2/reference/access-control), -[Proxy Target](/contracts/v2/reference/periphery/contract.SablierV2ProxyTarget), and -[Technical Reference: Periphery](/contracts/v2/reference/overview#periphery). - -::: - -## Proxy is Sender - -When creating streams via the proxy contract, the Sablier Protocol will record the proxy as the stream's sender: - -```mermaid -flowchart LR; - PO((Proxy Owner))-- "execute" -->P; - P[Proxy]; - PT[Proxy Target] - P-- "create-delegatecall" -->PT; - PT-- "createStreamLogic" -->P - P-- "createStream" -->CC[Core Contract]; -``` - -## Permit2 - -In the [ProxyTarget](/contracts/v2/reference/periphery/contract.SablierV2ProxyTarget), we use -[Permit2](https://github.com/uniswap/permit2) for all interactions that involve transfers of -[ERC-20](https://ethereum.org/en/developers/docs/standards/tokens/erc-20/) tokens from the proxy owner to the proxy -contract. To be more specific, we are using the `AllowanceTransfer` contract instead of `SignatureTransfer` because it -offers a more user-friendly -[nonce schema](https://docs.uniswap.org/contracts/permit2/reference/allowance-transfer#nonce-schema). diff --git a/docs/contracts/v2/guides/proxy-architecture/02-deploy.mdx b/docs/contracts/v2/guides/proxy-architecture/02-deploy.mdx deleted file mode 100644 index a4fab1a7..00000000 --- a/docs/contracts/v2/guides/proxy-architecture/02-deploy.mdx +++ /dev/null @@ -1,113 +0,0 @@ ---- -id: "deploy" -sidebar_position: 2 -title: "Deploy Proxy" ---- - -# Deploy Proxy and Install Plugin - -import AdmonitionSimpleCode from "@site/docs/snippets/AdmonitionSimpleCode.mdx"; - -## Context - -In this guide, we will show you how to programmatically deploy a proxy and install the official Sablier plugin. - -While the proxy architecture offers many benefits, it presents us with a challenge in a particular circumstance. When a -recipient cancels a stream, the protocol transfers the sender's refund to the proxy contract. To improve the user -experience, we wrote a specialized plugin that can be installed on the proxy contract to auto-forward the refunded -assets back to the original user. - - - -## 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.19; -``` - -Import the relevant symbols from `@sablier/v2-periphery`: - -```solidity -import { IPRBProxy, IPRBProxyRegistry } from "@sablier/v2-periphery/src/types/Proxy.sol"; -import { ISablierV2ProxyPlugin } from "@sablier/v2-periphery/src/interfaces/ISablierV2ProxyPlugin.sol"; -``` - -Create a contract called `ProxyDeployerAndPluginInstaller`, and declare a constant `PROXY_REGISTRY` of type -`IPRBProxyRegistry` and an immutable variable `proxyPlugin` of type `ISablierV2ProxyPlugin`: - -```solidity -contract ProxyDeployerAndPluginInstaller { - IPRBProxyRegistry public constant PROXY_REGISTRY = IPRBProxyRegistry(0x584009E9eDe26e212182c9745F5c000191296a78); - ISablierV2ProxyPlugin public immutable proxyPlugin; -} -``` - -:::info - -The `PRBProxyRegistry` contract is deployed at the same address across all chains. You can see a list of all deployments -[here](https://prbproxy.com/deployments). - -::: - -## Initialization - -To initialize the address of the Sablier `ProxyPlugin` contract, you first need to grab its address from the -[Deployment Addresses](/contracts/v2/addresses) page. Once you have obtained it, pass it to the constructor of the -deployer contract: - -```solidity -constructor(ISablierV2ProxyPlugin proxyPlugin_) { - proxyPlugin = proxyPlugin_; -} -``` - -## Function definition - -Define a function called `deployProxyAndInstallPlugin` which returns the `proxy` of the contract itself: - -```solidity -function deployProxyAndInstallPlugin() public returns (IPRBProxy proxy) { - // ... -} -``` - -## Steps - -First, check if the proxy already exists: - -```solidity -// Get the proxy for this contract -proxy = PROXY_REGISTRY.getProxy({ user: address(this) }); -``` - -Second, deploy the proxy and install the plugin if the proxy doesn't exist, or otherwise just install the plugin: - -```solidity -if (address(proxy) == address(0)) { - // If a proxy doesn't exist, deploy one and install the plugin - proxy = PROXY_REGISTRY.deployAndInstallPlugin({ plugin: proxyPlugin }); -} else { - // If the proxy exists, then just install the plugin. - PROXY_REGISTRY.installPlugin({ plugin: proxyPlugin }); -} -``` - -:::note - -There can only be one PRBProxy contract per address. If you try to deploy a proxy for an address that already has one, -the transaction will revert. - -::: - -## The complete deployer contract - -Below you can see the complete functioning code: a contract that queries the registry, deploys the proxy if it doesn't -exist, and installs the plugin. You can access the code on GitHub through -[this link](https://github.com/sablier-labs/examples/blob/main/v2/periphery/DeployProxyAndInstallPlugin.sol). - -```solidity reference title="Proxy Deployer and Plugin Installer" -https://github.com/sablier-labs/examples/blob/main/v2/periphery/DeployProxyAndInstallPlugin.sol -``` diff --git a/docs/contracts/v2/guides/proxy-architecture/03-batch-stream.mdx b/docs/contracts/v2/guides/proxy-architecture/03-batch-stream.mdx deleted file mode 100644 index 249641d0..00000000 --- a/docs/contracts/v2/guides/proxy-architecture/03-batch-stream.mdx +++ /dev/null @@ -1,288 +0,0 @@ ---- -id: "batch-stream" -sidebar_position: 3 -title: "Batch stream" ---- - -import AdmonitionSimpleCode from "@site/docs/snippets/AdmonitionSimpleCode.mdx"; - -In this guide, we will show you how to programmatically batch create streams via the proxy and Sablier's `ProxyTarget`. -This is one of the coolest features enabled by the proxy design. - -Batch create means creating multiple streams within a transaction, which is done by integrating multiple components: - -- Proxy -- ProxyTarget -- Permit2 -- V2 Core - -:::note - -This guide assumes that you have already read the [Proxy Architecture](/contracts/v2/guides/proxy-architecture/overview) -overview. - -::: - - - -## Batch create functions - -There are four batch create functions in `ProxyTarget`, two for each Lockup contract: - -- [`batchCreateWithDurations`](/contracts/v2/reference/periphery/contract.SablierV2ProxyTarget#batchcreatewithdurations) -- [`batchCreateWithRange`](/contracts/v2/reference/periphery/contract.SablierV2ProxyTarget#batchcreatewithrange) -- [`batchCreateWithDeltas`](/contracts/v2/reference/periphery/contract.SablierV2ProxyTarget#batchcreatewithdeltas) -- [`batchCreateWithMilestones`](/contracts/v2/reference/periphery/contract.SablierV2ProxyTarget#batchcreatewithmilestones) - -We will show to use `batchCreateWithDurations` in this guide. - -## 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.19; -``` - -As mentioned in the [Overview](/contracts/v2/guides/proxy-architecture/overview), V2 Periphery uses `Permit2` for all -token interactions. Permit2 relies on signatures, but in the example below we will use a contract as a stream creator, -and contracts cannot sign standard messages. This limitation can be overcome by using -[EIP-1271](https://eips.ethereum.org/EIPS/eip-1271). - -Let's declare a dummy contract `ERC1271` that returns the "magic" EIP-1271 value (the function signature of -`isValidSignature`): - -```solidity -/// @notice Permit2 uses this ERC to validate contract signatures. -contract ERC1271 { - function isValidSignature(bytes32, /* hash */ bytes memory /* signature */ ) public pure returns (bytes4) { - return this.isValidSignature.selector; - } -} -``` - -Now, import the relevant symbols from `@sablier/v2-core` and `@sablier/v2-periphery`: - -```solidity -import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; -import { LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; -import { ud60x18 } from "@sablier/v2-core/src/types/Math.sol"; -import { IERC20 } from "@sablier/v2-core/src/types/Tokens.sol"; -import { ISablierV2ProxyTarget } from "@sablier/v2-periphery/src/interfaces/ISablierV2ProxyTarget.sol"; -import { Batch, Broker } from "@sablier/v2-periphery/src/types/DataTypes.sol"; -import { IAllowanceTransfer, Permit2Params } from "@sablier/v2-periphery/src/types/Permit2.sol"; -import { IPRBProxy, IPRBProxyRegistry } from "@sablier/v2-periphery/src/types/Proxy.sol"; -``` - -Create a contract called `BatchLockupLinearStreamCreator` that inherits `ERC1271`, and declare the following constants: - -```solidity -contract BatchLockupLinearStreamCreator is ERC1271 { - IERC20 public constant DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F); - IAllowanceTransfer public constant PERMIT2 = IAllowanceTransfer(0x000000000022D473030F116dDEE9F6B43aC78BA3); - IPRBProxyRegistry public constant PROXY_REGISTRY = IPRBProxyRegistry(0x584009E9eDe26e212182c9745F5c000191296a78); - ISablierV2LockupLinear public immutable lockupLinear; - ISablierV2ProxyTarget public immutable proxyTarget; -} -``` - -In the code above, the address of the [DAI](https://makerdao.com) stablecoin is hard-coded. However, in production, you -would likely use an input parameter to allow the contract to change the assets it interacts with on a per transaction -basis. - -:::info - -`Permit2` and `PRBProxyRegistry` are deployed at the same address across all chains. - -::: - -## Initialization - -To initialize the `LockupLinear` and the `ProxyTarget` contracts, you first need to grab their addresses from the -[Deployment Addresses](/contracts/v2/deployments) page. Once you have obtained them, pass them to the constructor of the -creator contract: - -```solidity -constructor(ISablierV2LockupLinear lockupLinear_, ISablierV2ProxyTarget proxyTarget_) { - lockupLinear = lockupLinear_; - proxyTarget = proxyTarget_; -} -``` - -## Function definition - -Define a function called `batchCreateLockupLinearStreams` that takes a parameter `perStreamAmount` and returns an array -of ids for the created streams: - -```solidity -function batchCreateLockupLinearStreams(uint256 perStreamAmount) public returns (uint256[] memory streamIds) { - // ... -} -``` - -## Proxy Steps - -Deploy a proxy instance for the creator contract if it doesn't exist: - -```solidity -// Get the proxy for this contract and deploy it if it doesn't exist -IPRBProxy proxy = PROXY_REGISTRY.getProxy({ user: address(this) }); -if (address(proxy) == address(0)) { - proxy = PROXY_REGISTRY.deployFor(address(this)); -} -``` - -## Batch size - -Next, declare a batch size, which is needed to calculate the transfer amount: - -```solidity -// Create a batch of two streams -uint256 batchSize = 2; - -// Calculate the combined amount of DAI assets to transfer to this contract -uint256 transferAmount = perStreamAmount * batchSize; -``` - -## ERC-20 Steps - -The caller must approve the creator contract to pull the tokens from the calling address's account. Also, if the -allowance is less than the transferred amount, we have to approve the Permit2 contract. To fully benefit from Permit2, -we perform a maximum approval: - -```solidity -// Transfer the provided amount of DAI tokens to this contract -DAI.transferFrom(msg.sender, address(this), totalAmount); - -// Approve the Permit2 contract to spend DAI -uint256 allowance = DAI.allowance(address(this), address(PERMIT2)); -if (allowance < totalAmount) { - DAI.approve({ spender: address(PERMIT2), amount: type(uint256).max }); -} -``` - -## Permit2 Steps - -We are going to declare the Permit2 variables needed in the -[Proxy Target](/contracts/v2/reference/periphery/types/struct.Permit2Params). - -```solidity -// Set up Permit2. See the full documentation at https://github.com/Uniswap/permit2 -IAllowanceTransfer.PermitDetails memory permitDetails; -permitDetails.token = address(DAI); -permitDetails.amount = uint160(totalAmount); -permitDetails.expiration = type(uint48).max; // maximum expiration possible -(,, permitDetails.nonce) = PERMIT2.allowance({ user: address(this), token: address(DAI), spender: address(proxy) }); - -IAllowanceTransfer.PermitSingle memory permitSingle; -permitSingle.details = permitDetails; -permitSingle.spender = address(proxy); // the proxy will be the spender -permitSingle.sigDeadline = type(uint48).max; // same deadline as expiration - -// Declare the Permit2 params needed by Sablier -Permit2Params memory permit2Params; -permit2Params.permitSingle = permitSingle; -permit2Params.signature = bytes(""); // dummy signature -``` - -Notice that for the signature we passed an empty `bytes`. This is because contracts can't sign transactions. If we used -an EOA instead, a signature would have been required. - -For more details on Permit2, see Uniswap's -[documentation](https://docs.uniswap.org/contracts/permit2/reference/allowance-transfer). - -## Stream Parameters - -Given that we declared a `batchSize` of two, we need to define two -[Batch.CreateWithDurations](/contracts/v2/reference/periphery/types/library.Batch#createwithdurations) structs: - -```solidity -// Declare the first stream in the batch -Batch.CreateWithDurations memory stream0; -stream0.sender = address(proxy); // The sender will be able to cancel the stream -stream0.recipient = address(0xcafe); // The recipient of the streamed assets -stream0.totalAmount = uint128(perStreamAmount); // The total amount of each stream, inclusive of all fees -stream0.cancelable = true; // Whether the stream will be cancelable or not -stream0.durations = LockupLinear.Durations({ - cliff: 4 weeks, // Assets will be unlocked only after 4 weeks - total: 52 weeks // Setting a total duration of ~1 year -}); -stream0.broker = Broker(address(0), ud60x18(0)); // Optional parameter left undefined -``` - -To add some variety, we will change the parameters of the second stream: - -```solidity -// Declare the second stream in the batch -Batch.CreateWithDurations memory stream1; -stream1.sender = address(proxy); // The sender will be able to cancel the stream -stream1.recipient = address(0xbeef); // The recipient of the streamed assets -stream1.totalAmount = uint128(perStreamAmount); // The total amount of each stream, inclusive of all fees -stream1.cancelable = false; // Whether the stream will be cancelable or not -stream1.durations = LockupLinear.Durations({ - cliff: 1 weeks, // Assets will be unlocked only after 1 week - total: 26 weeks // Setting a total duration of ~6 months -}); -stream1.broker = Broker(address(0), ud60x18(0)); // Optional parameter left undefined -``` - -Once both structs are declared, the batch array has to be filled: - -```solidity -// Fill the batch param -Batch.CreateWithDurations[] memory batch = new Batch.CreateWithDurations[](batchSize); -batch[0] = stream0; -batch[1] = stream1; -``` - -## Execution - -To send the transaction to the proxy, it's necessary to ABI encode the parameters: - -```solidity -bytes memory data = abi.encodeCall(proxyTarget.batchCreateWithDurations, (lockupLinear, DAI, batch, permit2Params)); -``` - -Once this set-up is complete, we can batch create streams via the proxy and Sablier's `ProxyTarget`: - -```solidity -bytes memory response = proxy.execute(address(proxyTarget), data); -``` - -To return the `streamIds`, we need to ABI decode the response to `uint256[]`: - -```solidity -streamIds = abi.decode(response, (uint256[])); -``` - -## The complete Batch Lockup Linear stream creator contract - -Below you can see the complete functioning code: a contract that batch creates Lockup Linear streams via the proxy and -Sablier's `ProxyTarget` that start at `block.timestamp`. You can access the code on GitHub through this -[link](https://github.com/sablier-labs/examples/blob/main/v2/periphery/BatchLockupLinearStreamCreator.sol). - -```solidity reference title="Batch Lockup Linear stream creator" -https://github.com/sablier-labs/examples/blob/main/v2/periphery/BatchLockupLinearStreamCreator.sol -``` - -## Lockup Dynamic - -To create a Lockup Dynamic stream, the same steps as above apply. The only difference is that we have to declare a -different parameter struct and define the segments: - -```solidity -// Declare the first stream in the batch -Batch.CreateWithMilestones memory stream0; -// ... -// Declare the second stream in the batch -Batch.CreateWithMilestones memory stream1; -// ... -// Fill the batch array -Batch.CreateWithMilestones[] memory batch = new Batch.CreateWithMilestones[](batchSize); -batch[0] = stream0; -batch[1] = stream1; -``` - -You can see a complete functioning example -[here](https://github.com/sablier-labs/examples/blob/main/v2/periphery/BatchLockupDynamicStreamCreator.sol). diff --git a/docs/contracts/v2/guides/proxy-architecture/04-single-stream.mdx b/docs/contracts/v2/guides/proxy-architecture/04-single-stream.mdx deleted file mode 100644 index 0af379c5..00000000 --- a/docs/contracts/v2/guides/proxy-architecture/04-single-stream.mdx +++ /dev/null @@ -1,34 +0,0 @@ ---- -id: "single-stream" -sidebar_position: 4 -title: "Single stream" ---- - -import AdmonitionSimpleCode from "@site/docs/snippets/AdmonitionSimpleCode.mdx"; - -In this guide, we will show you how to programmatically create a single stream via the proxy and Sablier's `ProxyTarget` - -Creating single streams via the proxy is similar to creating batches of streams. Refer to the -[previous guide](/contracts/v2/guides/proxy-architecture/batch-stream) for step-by-step instructions; for brevity, we -will not repeat them here. - - - -## Create single functions - -There are four create functions in the `ProxyTarget` contract, two for each Lockup contract: - -- [`createWithDurations`](/contracts/v2/reference/core/contract.SablierV2LockupLinear#createwithdurations) -- [`createWithRange`](/contracts/v2/reference/core/contract.SablierV2LockupLinear#createwithrange) -- [`createWithDeltas`](/contracts/v2/reference/core/contract.SablierV2LockupDynamic#createwithdeltas) -- [`createWithMilestones`](/contracts/v2/reference/core/contract.SablierV2LockupDynamic#createwithmilestones) - -## The complete Single Lockup Linear stream creator contract - -Below you can see a complete example of a contract that creates a single Lockup Linear stream via the proxy and -Sablier's `ProxyTarget` that starts at `block.timestamp`. You can access the code on GitHub through this -[link](https://github.com/sablier-labs/examples/blob/main/v2/periphery/SingleLockupLinearStreamCreator.sol). - -```solidity reference title="Single Lockup Linear stream creator" -https://github.com/sablier-labs/examples/blob/main/v2/periphery/SingleLockupLinearStreamCreator.sol -``` diff --git a/docs/contracts/v2/guides/proxy-architecture/_category_.json b/docs/contracts/v2/guides/proxy-architecture/_category_.json deleted file mode 100644 index 46f3813b..00000000 --- a/docs/contracts/v2/guides/proxy-architecture/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "collapsed": true, - "label": "Proxy Architecture", - "position": 4 -} diff --git a/docs/contracts/v2/guides/stream-management/_category_.json b/docs/contracts/v2/guides/stream-management/_category_.json index 90d841cf..d71e2d95 100644 --- a/docs/contracts/v2/guides/stream-management/_category_.json +++ b/docs/contracts/v2/guides/stream-management/_category_.json @@ -1,5 +1,5 @@ { "collapsed": true, - "position": 3, + "position": 4, "label": "Stream Management" } diff --git a/docs/contracts/v2/reference/01-overview.md b/docs/contracts/v2/reference/01-overview.md index ad29d2f8..9d8b4e8d 100644 --- a/docs/contracts/v2/reference/01-overview.md +++ b/docs/contracts/v2/reference/01-overview.md @@ -12,19 +12,15 @@ and Periphery. - **Periphery** contracts interact with one or more Core contracts but are not part of the Core. They are an abstraction layer that enhance the security and the extensibility of the protocol without introducing upgradeability. -In the Sablier Interface, the Periphery is used to handle stream sender interactions, such as creating, canceling, and -renouncing streams. The Core handles all stream recipient interactions, such as withdrawing from streams. - Given the permissionless nature of the Sablier Protocol, the Periphery has no special privileges and is only a fraction -of possible periphery-like contracts. Users maintain the liberty to establish streams via the Core directly, although -doing so renders those streams incompatible with the Sablier Interface. +of possible periphery-like contracts. Users maintain the liberty to establish streams via the Core directly. The design of the Sablier smart contracts draws inspiration from the architectural principles of [Uniswap](https://docs.uniswap.org/). ## Core -> [**Core Source Code**](https://github.com/sablier-labs/v2-core) +> [**Core Source Code**](https://github.com/sablier-labs/v2-core/tree/release) Core contains the streaming contracts LockupLinear and LockupDynamic, an NFT descriptor, and the Comptroller (an on-chain configuration module). @@ -55,33 +51,11 @@ This contract is in charge of the Sablier V2 protocol configuration, handling su ## Periphery -> [**Periphery Source Code**](https://github.com/sablier-labs/v2-periphery) - -The Periphery is a collection of contracts meant to make the Sablier Protocol more modular, more secure, and more -extensible without introducing upgradeability. The key to all this is to use a forwarding proxy like -[PRBProxy](https://github.com/PaulRBerg/prb-proxy). - -Although this design requires users to deploy a proxy before interacting with the Sablier Protocol, the benefits are -worth it. By bringing support for delegate calls to any Ethereum account, the proxy enables smooth migrations and opens -the door for the implementation of features in a permissionless and backwards-compatible way. - -### ProxyTarget - -> [**ProxyTarget Reference**](./periphery/contract.SablierV2ProxyTarget) - -Proxy target with stateless scripts for interacting with Sablier V2, designed to be used by stream senders. - -### ProxyPlugin - -> [**ProxyPlugin Reference**](./periphery/contract.SablierV2ProxyPlugin) - -Proxy plugin that forwards the refunded assets to the proxy owner when the recipient cancels a stream whose sender is -the proxy contract. - -Recall that cancelling a stream refunds the sender the unstreamed balance of the stream. +> [**Periphery Source Code**](https://github.com/sablier-labs/v2-periphery/tree/release) -### Archive +The Periphery is a collection of contracts meant to make the Sablier Protocol more modular while introducing +functionalities such as [Airstream Campaigns](/concepts/protocol/airstream-campaigns). -> [**Archive Reference**](./periphery/contract.SablierV2Archive) +## MerkleStreamer -An on-chain contract registry that keeps a record of all Sablier V2 contracts, including old deployments. +> [**MerkleStreamer Reference**](./periphery/contract.SablierV2MerkleStreamerLL) diff --git a/docs/contracts/v2/reference/02-diagrams.md b/docs/contracts/v2/reference/02-diagrams.md index 4d26ea7d..8e9a957c 100644 --- a/docs/contracts/v2/reference/02-diagrams.md +++ b/docs/contracts/v2/reference/02-diagrams.md @@ -78,138 +78,26 @@ flowchart LR; C --> S1; ``` -## Scenarios +### Airstream Campaign -A collection of scenarios to help you understand how the Sablier Protocol works from end to end. - -:::note - -In the diagrams below, [`LockupLinear`](/contracts/v2/reference/core/contract.SablierV2LockupLinear) is used as an -example. However, [`LockupDynamic`](/contracts/v2/reference/core/contract.SablierV2LockupLinear) could be used in its -place and the diagrams would still be valid. - -::: - -### Set up proxy - -This is the first action that the sender needs to take in order to create a stream via the Sablier Interface. It is a -one-time action that deploy a [PRBProxy](https://github.com/PaulRBerg/prb-proxy) contract for senders. - -```mermaid -flowchart LR - S((Sender)) - PR[PRBProxyRegistry] - P[PRBProxy] - PP[SablierV2ProxyPlugin] - S -- "deployAndInstallPlugin" --> PR - PR -- "deploy" --> P - PR -- "installPlugin" --> PP -``` - -### Create a stream - -```mermaid -flowchart LR - S((Sender)) - subgraph Periphery - P[Proxy] - PT[ProxyTarget] - P2[Permit2] - end - subgraph Core - LL[Lockup Linear] - end - - S -- "execute" --> P - P -- "delegatecall" --> PT - PT -- "create logic" --> P - P -- "permit" --> P2 - P -- "transferFrom" --> P2 - P -- "create" ---> LL -``` - -### Withdraw from a stream - -#### Sender withdraws - -```mermaid -flowchart LR - S((Sender)) - subgraph Periphery - P[Proxy] - PT[ProxyTarget] - end - subgraph Core - LL[Lockup Linear] - end - R((Recipient)) - - S -- "execute" --> P - P -- "delegatecall" --> PT - PT -- "withdraw logic" --> P - P -- "withdraw" --> LL - LL -- "transfer" ---> R -``` - -#### Recipient withdraws - -```mermaid -flowchart RL - LL[Lockup Linear] - R((Recipient)) - - R -- "withdraw" --> LL - LL -- "transfer" --> R -``` - -### Cancel a stream - -#### Sender cancels +An example of a user creating an Airstream campaign. ```mermaid flowchart LR - S((Sender)) + S((Airstream Creator)) subgraph Periphery - P[Proxy] - PT[ProxyTarget] - end - subgraph Core - LL[Lockup Linear] + MSF[MerkleStreamFactory] + MS[(MerkleStream)] end - PEnd[Proxy] - SEnd((Sender)) - - S -- "execute" --> P - P -- "delegatecall" --> PT - PT -- "cancel logic" --> P - P -- "cancel" --> LL - LL -- "transfer" --> PEnd - PEnd -- "transfer" --> SEnd -``` - -#### Recipient cancels - -When the recipient cancels a stream, the sender is automatically refunded the remaining balance. - -If the sender creates the stream via a proxy, the proxy plugin will be notified of the cancellation and will -auto-forward the refund to the sender. - -```mermaid -flowchart LR - R((Recipient)) subgraph Core - LL[Lockup Linear] - end - subgraph Periphery - P[Sender's Proxy] - PP[SablierV2ProxyPlugin] + LL[LockupLinear] end - S((Sender)) - - R -- "cancel" --> LL - LL -- "transfer" --> P - LL -- "onStreamCanceled" --> P - P -- "delegatecall" --> PP - PP -- "plugin logic" --> P - P -- "transfer" ---> S + R1((Recipient1)) + R2((Recipient2)) + S -- "createCampaign" --> MSF + MSF -- "deployCampaign" --> MS + R1 -- "claim" --> MS + R2 -- "claim" --> MS + MS -- "createStream" --> LL + MS -- "createStream" --> LL ``` diff --git a/docs/contracts/v2/reference/05-access-control.md b/docs/contracts/v2/reference/05-access-control.md index 31ead6c0..5363b5ef 100644 --- a/docs/contracts/v2/reference/05-access-control.md +++ b/docs/contracts/v2/reference/05-access-control.md @@ -24,12 +24,12 @@ The table below offers a quick overview of the access control for each action th | Action | Sender | Recipient | Operator(s) | | ----------------- | :----: | :-------: | :---------: | | Burn NFT | ❌ | ✅ | ✅ | -| Cancel | ✅ | ✅ | ❌ | -| Cancel Multiple | ✅ | ✅ | ❌ | +| Cancel | ✅ | ❌ | ❌ | +| Cancel Multiple | ✅ | ❌ | ❌ | | Renounce | ✅ | ❌ | ❌ | | Transfer NFT | ❌ | ✅ | ✅ | | Withdraw | ✅ | ✅ | ✅ | -| Withdraw Multiple | ❌ | ✅ | ✅ | +| Withdraw Multiple | ✅ | ✅ | ✅ | ## Burn NFT @@ -48,32 +48,25 @@ flowchart LR; ## Cancel -Either the sender or the recipient can cancel a stream. +Only the sender can cancel a stream. ```mermaid flowchart LR; sender((Sender)); - recipient((Recipient)); stream[(Stream)]; - sender -- cancel -->stream; - recipient -- cancel -->stream; ``` ## Cancel Multiple -Either the sender or the recipient can cancel multiple streams. - -- The caller must be either the sender or the recipient of each stream. +Only the sender can cancel multiple streams. ```mermaid flowchart LR; sender((Sender)); - recipient((Recipient)); streams[(Multiple Streams)]; sender -- cancelMultiple -->streams; - recipient -- cancelMultiple -->streams; ``` ## Renounce @@ -91,6 +84,8 @@ flowchart LR; Either the recipient or an approved operator can transfer the NFT associated with a stream. +- Only if the stream is transferable. + ```mermaid flowchart LR; recipient((Recipient)); @@ -124,17 +119,19 @@ flowchart LR; ## Withdraw Multiple -Either the recipient or an approved NFT operator can withdraw assets from multiple streams. +Either the recipient, an approved NFT operator, or the sender can withdraw assets from multiple streams. -- The caller has the option to specify a custom address to withdraw the assets to. -- The caller must be either the recipient or an approved NFT operator of each stream. +- Both the recipient and the NFT operator have the option to specify a custom address to withdraw the assets to. +- The sender, however, is limited to withdrawing assets directly to the recipient's address of each stream. ```mermaid flowchart LR; + sender((Sender)); recipient((Recipient)); operator((Operator)); streams[(Multiple Streams)]; + sender -- withdrawMultiple --->streams; recipient -- withdrawMultiple --->streams recipient -- approve -->operator; operator -- withdrawMultiple -->streams; diff --git a/docs/contracts/v2/reference/core/abstracts/abstract.Adminable.md b/docs/contracts/v2/reference/core/abstracts/abstract.Adminable.md index e41db9a6..9e4316b9 100644 --- a/docs/contracts/v2/reference/core/abstracts/abstract.Adminable.md +++ b/docs/contracts/v2/reference/core/abstracts/abstract.Adminable.md @@ -1,6 +1,6 @@ # Adminable -[Git Source](https://github.com/sablier-labs/v2-core/blob/bca1d9ea0485b065544486bb01f4148d44289644/docs/contracts/v2/reference/core/abstracts) +[Git Source](https://github.com/sablier-labs/v2-core/blob/release/src/abstracts/Adminable.sol) **Inherits:** [IAdminable](/docs/contracts/v2/reference/core/interfaces/interface.IAdminable.md) diff --git a/docs/contracts/v2/reference/core/abstracts/abstract.NoDelegateCall.md b/docs/contracts/v2/reference/core/abstracts/abstract.NoDelegateCall.md index f8af053f..84689227 100644 --- a/docs/contracts/v2/reference/core/abstracts/abstract.NoDelegateCall.md +++ b/docs/contracts/v2/reference/core/abstracts/abstract.NoDelegateCall.md @@ -1,17 +1,17 @@ # NoDelegateCall -[Git Source](https://github.com/sablier-labs/v2-core/blob/bca1d9ea0485b065544486bb01f4148d44289644/docs/contracts/v2/reference/core/abstracts) +[Git Source](https://github.com/sablier-labs/v2-core/blob/release/src/abstracts/NoDelegateCall.sol) This contract implements logic to prevent delegate calls. ## State Variables -### \_original +### ORIGINAL _The address of the original contract that was deployed._ ```solidity -address private immutable _original; +address private immutable ORIGINAL; ``` ## Functions @@ -37,7 +37,7 @@ modifier noDelegateCall(); 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 + 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. diff --git a/docs/contracts/v2/reference/core/abstracts/abstract.SablierV2Base.md b/docs/contracts/v2/reference/core/abstracts/abstract.SablierV2Base.md index 124c73cf..5c4f0cd0 100644 --- a/docs/contracts/v2/reference/core/abstracts/abstract.SablierV2Base.md +++ b/docs/contracts/v2/reference/core/abstracts/abstract.SablierV2Base.md @@ -1,6 +1,6 @@ # SablierV2Base -[Git Source](https://github.com/sablier-labs/v2-core/blob/bca1d9ea0485b065544486bb01f4148d44289644/docs/contracts/v2/reference/core/abstracts) +[Git Source](https://github.com/sablier-labs/v2-core/blob/release/src/abstracts/SablierV2Base.sol) **Inherits:** [NoDelegateCall](/docs/contracts/v2/reference/core/abstracts/abstract.NoDelegateCall.md), [ISablierV2Base](/docs/contracts/v2/reference/core/interfaces/interface.ISablierV2Base.md), diff --git a/docs/contracts/v2/reference/core/abstracts/abstract.SablierV2FlashLoan.md b/docs/contracts/v2/reference/core/abstracts/abstract.SablierV2FlashLoan.md index 1b767e10..df6e332b 100644 --- a/docs/contracts/v2/reference/core/abstracts/abstract.SablierV2FlashLoan.md +++ b/docs/contracts/v2/reference/core/abstracts/abstract.SablierV2FlashLoan.md @@ -1,6 +1,6 @@ # SablierV2FlashLoan -[Git Source](https://github.com/sablier-labs/v2-core/blob/bca1d9ea0485b065544486bb01f4148d44289644/docs/contracts/v2/reference/core/abstracts) +[Git Source](https://github.com/sablier-labs/v2-core/blob/release/src/abstracts/SablierV2FlashLoan.sol) **Inherits:** [IERC3156FlashLender](/docs/contracts/v2/reference/core/interfaces/erc3156/interface.IERC3156FlashLender.md), diff --git a/docs/contracts/v2/reference/core/abstracts/abstract.SablierV2Lockup.md b/docs/contracts/v2/reference/core/abstracts/abstract.SablierV2Lockup.md index c2f79d93..cba4e02e 100644 --- a/docs/contracts/v2/reference/core/abstracts/abstract.SablierV2Lockup.md +++ b/docs/contracts/v2/reference/core/abstracts/abstract.SablierV2Lockup.md @@ -1,9 +1,12 @@ # SablierV2Lockup -[Git Source](https://github.com/sablier-labs/v2-core/blob/bca1d9ea0485b065544486bb01f4148d44289644/docs/contracts/v2/reference/core/abstracts) +[Git Source](https://github.com/sablier-labs/v2-core/blob/release/src/abstracts/SablierV2Lockup.sol) -**Inherits:** IERC4906, [SablierV2Base](/docs/contracts/v2/reference/core/abstracts/abstract.SablierV2Base.md), -[ISablierV2Lockup](/docs/contracts/v2/reference/core/interfaces/interface.ISablierV2Lockup.md), ERC721 +**Inherits:** +[IERC4906](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/e50c24f5839db17f46991478384bfda14acfb830/contracts/interfaces/IERC4906.sol), +[SablierV2Base](/docs/contracts/v2/reference/core/abstracts/abstract.SablierV2Base.md), +[ISablierV2Lockup](/docs/contracts/v2/reference/core/interfaces/interface.ISablierV2Lockup.md), +[ERC721](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/e50c24f5839db17f46991478384bfda14acfb830/contracts/token/ERC721/ERC721.sol) See the documentation in [ISablierV2Lockup](/docs/contracts/v2/reference/core/interfaces/interface.ISablierV2Lockup.md). @@ -56,7 +59,7 @@ modifier notNull(uint256 streamId); ### updateMetadata -_Emits an ERC-4906 event to trigger an update of the NFT metadata._ +_Emits an `ERC-4906` event to trigger an update of the NFT metadata._ ```solidity modifier updateMetadata(uint256 streamId); @@ -78,6 +81,22 @@ function getRecipient(uint256 streamId) external view override returns (address | ---------- | --------- | ---------------------------- | | `streamId` | `uint256` | The stream id for the query. | +### isCold + +Retrieves a flag indicating whether the stream is cold, i.e. settled, canceled, or depleted. + +_Reverts if `streamId` references a null stream._ + +```solidity +function isCold(uint256 streamId) external view override notNull(streamId) returns (bool result); +``` + +**Parameters** + +| Name | Type | Description | +| ---------- | --------- | ---------------------------- | +| `streamId` | `uint256` | The stream id for the query. | + ### isDepleted Retrieves a flag indicating whether the stream is depleted. @@ -110,6 +129,22 @@ function isStream(uint256 streamId) public view virtual override returns (bool r | ---------- | --------- | ---------------------------- | | `streamId` | `uint256` | The stream id for the query. | +### isWarm + +Retrieves a flag indicating whether the stream is warm, i.e. either pending or streaming. + +_Reverts if `streamId` references a null stream._ + +```solidity +function isWarm(uint256 streamId) external view override notNull(streamId) returns (bool result); +``` + +**Parameters** + +| Name | Type | Description | +| ---------- | --------- | ---------------------------- | +| `streamId` | `uint256` | The stream id for the query. | + ### tokenURI ```solidity @@ -182,14 +217,13 @@ Emits a {Transfer}, {CancelLockupStream}, and {MetadataUpdate} event. Notes: - If there any assets left for the recipient to withdraw, the stream is marked as canceled. Otherwise, the stream is marked as depleted. -- This function attempts to invoke a hook on either the sender or the recipient, depending on who `msg.sender` is, and - if the resolved address is a contract. Requirements: +- This function attempts to invoke a hook on the recipient, if the resolved address is a contract. Requirements: - Must not be delegate called. - The stream must be warm and cancelable. -- `msg.sender` must be either the stream's sender or the stream's recipient (i.e. the NFT owner). +- `msg.sender` must be the stream's sender. ```solidity -function cancel(uint256 streamId) public override noDelegateCall updateMetadata(streamId); +function cancel(uint256 streamId) public override noDelegateCall; ``` **Parameters** @@ -377,6 +411,63 @@ function withdrawMultiple( | `to` | `address` | The address receiving the withdrawn assets. | | `amounts` | `uint128[]` | The amounts to withdraw, denoted in units of the asset's decimals. | +### \_afterTokenTransfer + +Overrides the internal `ERC-721` transfer function to emit an `ERC-4906` event upon transfer. The goal is to refresh the +NFT metadata on external platforms. This event is also emitted when the NFT is minted or burned. + +```solidity +function _afterTokenTransfer( + address, /* from */ + address, /* to */ + uint256 streamId, + uint256 /* batchSize */ +) + internal + override + updateMetadata(streamId) +{ } +``` + +**Parameters** + +| Name | Type | Description | +| ----------- | --------- | ---------------------------- | +| `from` | `address` | Ignored. | +| `to` | `address` | Ignored. | +| `streamId` | `uint256` | The stream id for the query. | +| `batchSize` | `uint256` | Ignored. | + +### \_beforeTokenTransfer + +Overrides the internal `ERC-721` transfer function to check that the stream is transferable. There are two cases when +the transferable flag is ignored: + +- If `from` is 0, then the transfer is a mint and is allowed. +- If `to` is 0, then the transfer is a burn and is also allowed. + +```solidity +function _beforeTokenTransfer( + address from, + address to, + uint256 streamId, + uint256 /* batchSize */ +) + internal + view + override +{ } +``` + +**Parameters** + +| Name | Type | Description | +| ----------- | --------- | ----------------------------- | +| `from` | `address` | The address to transfer from. | +| `to` | `address` | The address to transfer to. | +| `streamId` | `uint256` | The stream id for the query. | +| `batchSize` | `uint256` | Ignored. | + ### \_isCallerStreamRecipientOrApproved Checks whether `msg.sender` is the stream's recipient or an approved third party. diff --git a/docs/contracts/v2/reference/core/contract.SablierV2Comptroller.md b/docs/contracts/v2/reference/core/contract.SablierV2Comptroller.md index 1224e15c..f69c9b71 100644 --- a/docs/contracts/v2/reference/core/contract.SablierV2Comptroller.md +++ b/docs/contracts/v2/reference/core/contract.SablierV2Comptroller.md @@ -4,7 +4,7 @@ sidebar_position: 4 # SablierV2Comptroller -[Git Source](https://github.com/sablier-labs/v2-core/blob/bca1d9ea0485b065544486bb01f4148d44289644/docs/contracts/v2/reference/core) +[Git Source](https://github.com/sablier-labs/v2-core/blob/release/src/SablierV2Comptroller.sol) **Inherits:** [ISablierV2Comptroller](/docs/contracts/v2/reference/core/interfaces/interface.ISablierV2Comptroller.md), [Adminable](/docs/contracts/v2/reference/core/abstracts/abstract.Adminable.md) diff --git a/docs/contracts/v2/reference/core/contract.SablierV2LockupDynamic.md b/docs/contracts/v2/reference/core/contract.SablierV2LockupDynamic.md index 82240c9c..f596abaa 100644 --- a/docs/contracts/v2/reference/core/contract.SablierV2LockupDynamic.md +++ b/docs/contracts/v2/reference/core/contract.SablierV2LockupDynamic.md @@ -4,7 +4,7 @@ sidebar_position: 2 # SablierV2LockupDynamic -[Git Source](https://github.com/sablier-labs/v2-core/blob/bca1d9ea0485b065544486bb01f4148d44289644/docs/contracts/v2/reference/core) +[Git Source](https://github.com/sablier-labs/v2-core/blob/release/src/SablierV2LockupDynamic.sol) **Inherits:** [ISablierV2LockupDynamic](/docs/contracts/v2/reference/core/interfaces/interface.ISablierV2LockupDynamic.md), @@ -33,7 +33,9 @@ _Sablier V2 Lockup Dynamic streams mapped by unsigned integer ids._ mapping(uint256 id => LockupDynamic.Stream stream) private _streams; ``` -## Functions +See the struct `LockupDynamic.Stream` in [Types](/contracts/v2/reference/core/types/library.LockupDynamic#stream). + +## User Facing Functions ### constructor @@ -61,7 +63,7 @@ constructor( ### getAsset -Retrieves the address of the ERC-20 asset used for streaming. +Retrieves the address of the `ERC-20` asset used for streaming. _Reverts if `streamId` references a null stream._ @@ -267,22 +269,6 @@ function isCancelable(uint256 streamId) external view override notNull(streamId) | ---------- | --------- | ---------------------------- | | `streamId` | `uint256` | The stream id for the query. | -### isCold - -Retrieves a flag indicating whether the stream is cold, i.e. settled, canceled, or depleted. - -_Reverts if `streamId` references a null stream._ - -```solidity -function isCold(uint256 streamId) external view override notNull(streamId) returns (bool result); -``` - -**Parameters** - -| Name | Type | Description | -| ---------- | --------- | ---------------------------- | -| `streamId` | `uint256` | The stream id for the query. | - ### isDepleted Retrieves a flag indicating whether the stream is depleted. @@ -320,14 +306,14 @@ function isStream(uint256 streamId) public view override(ISablierV2Lockup, Sabli | ---------- | --------- | ---------------------------- | | `streamId` | `uint256` | The stream id for the query. | -### isWarm +### isTransferable -Retrieves a flag indicating whether the stream is warm, i.e. either pending or streaming. +Retrieves a flag indicating whether the stream NFT can be transferred. _Reverts if `streamId` references a null stream._ ```solidity -function isWarm(uint256 streamId) external view override notNull(streamId) returns (bool result); +function isTransferable(uint256 streamId) external view returns (bool result); ``` **Parameters** @@ -432,7 +418,7 @@ function wasCanceled(uint256 streamId) Creates a stream by setting the start time to `block.timestamp`, and the end time to the sum of `block.timestamp` and all specified time deltas. The segment milestones are derived from these deltas. The stream is funded by `msg.sender` -and is wrapped in an ERC-721 NFT. +and is wrapped in an `ERC-721` NFT. Emits a {Transfer} and {CreateLockupDynamicStream} event. Requirements: @@ -448,9 +434,9 @@ function createWithDeltas(LockupDynamic.CreateWithDeltas calldata params) **Parameters** -| Name | Type | Description | -| -------- | -------------------------------- | ---------------------------------------------------------------------------------- | -| `params` | `LockupDynamic.CreateWithDeltas` | Struct encapsulating the function parameters, which are documented in {DataTypes}. | +| Name | Type | Description | +| -------- | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `params` | `LockupDynamic.CreateWithDeltas` | Struct encapsulating the function parameters, which are documented in [Types](/contracts/v2/reference/core/types/library.LockupDynamic#createwithdeltas). | **Returns** @@ -461,7 +447,7 @@ function createWithDeltas(LockupDynamic.CreateWithDeltas calldata params) ### createWithMilestones Creates a stream with the provided segment milestones, implying the end time from the last milestone. The stream is -funded by `msg.sender` and is wrapped in an ERC-721 NFT. +funded by `msg.sender` and is wrapped in an `ERC-721` NFT. Emits a {Transfer} and {CreateLockupDynamicStream} event. Notes: @@ -488,9 +474,9 @@ function createWithMilestones(LockupDynamic.CreateWithMilestones calldata params **Parameters** -| Name | Type | Description | -| -------- | ------------------------------------ | ---------------------------------------------------------------------------------- | -| `params` | `LockupDynamic.CreateWithMilestones` | Struct encapsulating the function parameters, which are documented in {DataTypes}. | +| Name | Type | Description | +| -------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `params` | `LockupDynamic.CreateWithMilestones` | Struct encapsulating the function parameters, which are documented in [Types](/contracts/v2/reference/core/types/library.LockupDynamic#createwithmilestones). | **Returns** @@ -498,6 +484,8 @@ function createWithMilestones(LockupDynamic.CreateWithMilestones calldata params | ---------- | --------- | ----------------------------------- | | `streamId` | `uint256` | The id of the newly created stream. | +## Internal Functions + ### \_calculateStreamedAmount _Calculates the streamed amount without looking up the stream's status._ @@ -552,7 +540,8 @@ function _statusOf(uint256 streamId) internal view override returns (Lockup.Stat ### \_streamedAmountOf -_See the documentation for the user-facing functions that call this internal function._ +_Implements the internal logic for +[streamedAmountOf](/contracts/v2/reference/core/contract.SablierV2LockupLinear#streamedamountof)._ ```solidity function _streamedAmountOf(uint256 streamId) internal view returns (uint128); @@ -560,7 +549,8 @@ function _streamedAmountOf(uint256 streamId) internal view returns (uint128); ### \_withdrawableAmountOf -_See the documentation for the user-facing functions that call this internal function._ +_Implements the internal logic for +[withdrawableAmountOf](/contracts/v2/reference/core/abstracts/abstract.SablierV2Lockup#withdrawableamountof)._ ```solidity function _withdrawableAmountOf(uint256 streamId) internal view override returns (uint128); @@ -568,7 +558,7 @@ function _withdrawableAmountOf(uint256 streamId) internal view override returns ### \_cancel -_See the documentation for the user-facing functions that call this internal function._ +_Implements the internal logic for [cancel](/contracts/v2/reference/core/abstracts/abstract.SablierV2Lockup#cancel)._ ```solidity function _cancel(uint256 streamId) internal override; @@ -584,7 +574,8 @@ function _createWithMilestones(LockupDynamic.CreateWithMilestones memory params) ### \_renounce -_See the documentation for the user-facing functions that call this internal function._ +_Implements the internal logic for +[renounce](/contracts/v2/reference/core/abstracts/abstract.SablierV2Lockup#renounce)._ ```solidity function _renounce(uint256 streamId) internal override; @@ -592,7 +583,8 @@ function _renounce(uint256 streamId) internal override; ### \_withdraw -_See the documentation for the user-facing functions that call this internal function._ +_Implements the internal logic for +[\_withdraw](/contracts/v2/reference/core/abstracts/abstract.SablierV2Lockup#_withdraw)._ ```solidity function _withdraw(uint256 streamId, address to, uint128 amount) internal override; diff --git a/docs/contracts/v2/reference/core/contract.SablierV2LockupLinear.md b/docs/contracts/v2/reference/core/contract.SablierV2LockupLinear.md index 2f5439fa..9fad39ad 100644 --- a/docs/contracts/v2/reference/core/contract.SablierV2LockupLinear.md +++ b/docs/contracts/v2/reference/core/contract.SablierV2LockupLinear.md @@ -4,7 +4,7 @@ sidebar_position: 1 # SablierV2LockupLinear -[Git Source](https://github.com/sablier-labs/v2-core/blob/bca1d9ea0485b065544486bb01f4148d44289644/docs/contracts/v2/reference/core) +[Git Source](https://github.com/sablier-labs/v2-core/blob/release/src/SablierV2LockupLinear.sol) **Inherits:** [ISablierV2LockupLinear](/docs/contracts/v2/reference/core/interfaces/interface.ISablierV2LockupLinear.md), @@ -23,7 +23,9 @@ _Sablier V2 Lockup Linear streams mapped by unsigned integers._ mapping(uint256 id => LockupLinear.Stream stream) private _streams; ``` -## Functions +See the struct `LockupLinear.Stream` in [Types](/contracts/v2/reference/core/types/library.LockupLinear#stream). + +## User Facing Functions ### constructor @@ -49,7 +51,7 @@ constructor( ### getAsset -Retrieves the address of the ERC-20 asset used for streaming. +Retrieves the address of the `ERC-20` asset used for streaming. _Reverts if `streamId` references a null stream._ @@ -250,22 +252,6 @@ function isCancelable(uint256 streamId) external view override notNull(streamId) | ---------- | --------- | ---------------------------- | | `streamId` | `uint256` | The stream id for the query. | -### isCold - -Retrieves a flag indicating whether the stream is cold, i.e. settled, canceled, or depleted. - -_Reverts if `streamId` references a null stream._ - -```solidity -function isCold(uint256 streamId) external view override notNull(streamId) returns (bool result); -``` - -**Parameters** - -| Name | Type | Description | -| ---------- | --------- | ---------------------------- | -| `streamId` | `uint256` | The stream id for the query. | - ### isDepleted Retrieves a flag indicating whether the stream is depleted. @@ -303,14 +289,14 @@ function isStream(uint256 streamId) public view override(ISablierV2Lockup, Sabli | ---------- | --------- | ---------------------------- | | `streamId` | `uint256` | The stream id for the query. | -### isWarm +### isTransferable -Retrieves a flag indicating whether the stream is warm, i.e. either pending or streaming. +Retrieves a flag indicating whether the stream NFT can be transferred. _Reverts if `streamId` references a null stream._ ```solidity -function isWarm(uint256 streamId) external view override notNull(streamId) returns (bool result); +function isTransferable(uint256 streamId) external view returns (bool result); ``` **Parameters** @@ -413,7 +399,7 @@ function wasCanceled(uint256 streamId) ### createWithDurations Creates a stream by setting the start time to `block.timestamp`, and the end time to the sum of `block.timestamp` and -`params.durations.total. The stream is funded by `msg.sender` and is wrapped in an ERC-721 NFT. +`params.durations.total`. The stream is funded by `msg.sender` and is wrapped in an `ERC-721` NFT. Emits a {Transfer} and {CreateLockupLinearStream} event. Requirements: @@ -429,9 +415,9 @@ function createWithDurations(LockupLinear.CreateWithDurations calldata params) **Parameters** -| Name | Type | Description | -| -------- | ---------------------------------- | ---------------------------------------------------------------------------------- | -| `params` | `LockupLinear.CreateWithDurations` | Struct encapsulating the function parameters, which are documented in {DataTypes}. | +| Name | Type | Description | +| -------- | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `params` | `LockupLinear.CreateWithDurations` | Struct encapsulating the function parameters, which are documented in [Types](/contracts/v2/reference/core/types/library.LockupLinear#createwithdurations). | **Returns** @@ -442,7 +428,7 @@ function createWithDurations(LockupLinear.CreateWithDurations calldata params) ### createWithRange Creates a stream with the provided start time and end time as the range. The stream is funded by `msg.sender` and is -wrapped in an ERC-721 NFT. +wrapped in an `ERC-721` NFT. Emits a {Transfer} and {CreateLockupLinearStream} event. Notes: @@ -466,9 +452,9 @@ function createWithRange(LockupLinear.CreateWithRange calldata params) **Parameters** -| Name | Type | Description | -| -------- | ------------------------------ | ---------------------------------------------------------------------------------- | -| `params` | `LockupLinear.CreateWithRange` | Struct encapsulating the function parameters, which are documented in {DataTypes}. | +| Name | Type | Description | +| -------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `params` | `LockupLinear.CreateWithRange` | Struct encapsulating the function parameters, which are documented in [Types](/contracts/v2/reference/core/types/library.LockupLinear#createwithrange). | **Returns** @@ -476,6 +462,8 @@ function createWithRange(LockupLinear.CreateWithRange calldata params) | ---------- | --------- | ----------------------------------- | | `streamId` | `uint256` | The id of the newly created stream. | +## Internal Functions + ### \_calculateStreamedAmount _Calculates the streamed amount without looking up the stream's status._ @@ -508,7 +496,8 @@ function _statusOf(uint256 streamId) internal view override returns (Lockup.Stat ### \_streamedAmountOf -_See the documentation for the user-facing functions that call this internal function._ +_Implements the internal logic for +[streamedAmountOf](/contracts/v2/reference/core/contract.SablierV2LockupLinear#streamedamountof)._ ```solidity function _streamedAmountOf(uint256 streamId) internal view returns (uint128); @@ -516,7 +505,8 @@ function _streamedAmountOf(uint256 streamId) internal view returns (uint128); ### \_withdrawableAmountOf -_See the documentation for the user-facing functions that call this internal function._ +_Implements the internal logic for +[withdrawableAmountOf](/contracts/v2/reference/core/abstracts/abstract.SablierV2Lockup#withdrawableamountof)._ ```solidity function _withdrawableAmountOf(uint256 streamId) internal view override returns (uint128); @@ -524,7 +514,7 @@ function _withdrawableAmountOf(uint256 streamId) internal view override returns ### \_cancel -_See the documentation for the user-facing functions that call this internal function._ +_Implements the internal logic for [cancel](/contracts/v2/reference/core/abstracts/abstract.SablierV2Lockup#cancel)._ ```solidity function _cancel(uint256 streamId) internal override; @@ -532,7 +522,8 @@ function _cancel(uint256 streamId) internal override; ### \_createWithRange -_See the documentation for the user-facing functions that call this internal function._ +_Implements the internal logic for +[createWithRange](/contracts/v2/reference/core/contract.SablierV2LockupLinear#createwithrange)._ ```solidity function _createWithRange(LockupLinear.CreateWithRange memory params) internal returns (uint256 streamId); @@ -540,7 +531,8 @@ function _createWithRange(LockupLinear.CreateWithRange memory params) internal r ### \_renounce -_See the documentation for the user-facing functions that call this internal function._ +_Implements the internal logic for +[renounce](/contracts/v2/reference/core/abstracts/abstract.SablierV2Lockup#renounce)._ ```solidity function _renounce(uint256 streamId) internal override; @@ -548,7 +540,8 @@ function _renounce(uint256 streamId) internal override; ### \_withdraw -_See the documentation for the user-facing functions that call this internal function._ +_Implements the internal logic for +[\_withdraw](/contracts/v2/reference/core/abstracts/abstract.SablierV2Lockup#_withdraw)._ ```solidity function _withdraw(uint256 streamId, address to, uint128 amount) internal override; diff --git a/docs/contracts/v2/reference/core/contract.SablierV2NFTDescriptor.md b/docs/contracts/v2/reference/core/contract.SablierV2NFTDescriptor.md index 3ca60ac7..54a9f72e 100644 --- a/docs/contracts/v2/reference/core/contract.SablierV2NFTDescriptor.md +++ b/docs/contracts/v2/reference/core/contract.SablierV2NFTDescriptor.md @@ -4,7 +4,7 @@ sidebar_position: 3 # SablierV2NFTDescriptor -[Git Source](https://github.com/sablier-labs/v2-core/blob/bca1d9ea0485b065544486bb01f4148d44289644/docs/contracts/v2/reference/core) +[Git Source](https://github.com/sablier-labs/v2-core/blob/release/src/SablierV2NFTDescriptor.sol) **Inherits:** [ISablierV2NFTDescriptor](/docs/contracts/v2/reference/core/interfaces/interface.ISablierV2NFTDescriptor.md) @@ -61,9 +61,9 @@ function abbreviateAmount(uint256 amount, uint256 decimals) internal pure return **Returns** -| Name | Type | Description | -| -------- | -------- | -------------------------------------------------------------------------------- | -| `` | `string` | abbreviation The abbreviated representation of the provided amount, as a string. | +| Name | Type | Description | +| -------- | -------- | ------------------------------------------------------------------- | +| `` | `string` | The abbreviated representation of the provided amount, as a string. | ### calculateDurationInDays @@ -215,12 +215,12 @@ _Needed to avoid Stack Too Deep._ struct TokenURIVars { address asset; string assetSymbol; + uint128 depositedAmount; string json; ISablierV2Lockup sablier; string sablierAddress; string status; string svg; - uint128 streamedAmount; uint256 streamedPercentage; string streamingModel; } diff --git a/docs/contracts/v2/reference/core/interfaces/erc3156/interface.IERC3156FlashBorrower.md b/docs/contracts/v2/reference/core/interfaces/erc3156/interface.IERC3156FlashBorrower.md index 7b428009..cf6a37c6 100644 --- a/docs/contracts/v2/reference/core/interfaces/erc3156/interface.IERC3156FlashBorrower.md +++ b/docs/contracts/v2/reference/core/interfaces/erc3156/interface.IERC3156FlashBorrower.md @@ -1,6 +1,6 @@ # IERC3156FlashBorrower -[Git Source](https://github.com/sablier-labs/v2-core/blob/bca1d9ea0485b065544486bb01f4148d44289644/docs/contracts/v2/reference/core/interfaces/erc3156) +[Git Source](https://github.com/sablier-labs/v2-core/blob/release/src/interfaces/erc3156/IERC3156FlashBorrower.sol) Interface for ERC-3156 flash borrowers. diff --git a/docs/contracts/v2/reference/core/interfaces/erc3156/interface.IERC3156FlashLender.md b/docs/contracts/v2/reference/core/interfaces/erc3156/interface.IERC3156FlashLender.md index 72bde406..bff79c80 100644 --- a/docs/contracts/v2/reference/core/interfaces/erc3156/interface.IERC3156FlashLender.md +++ b/docs/contracts/v2/reference/core/interfaces/erc3156/interface.IERC3156FlashLender.md @@ -1,6 +1,6 @@ # IERC3156FlashLender -[Git Source](https://github.com/sablier-labs/v2-core/blob/bca1d9ea0485b065544486bb01f4148d44289644/docs/contracts/v2/reference/core/interfaces/erc3156) +[Git Source](https://github.com/sablier-labs/v2-core/blob/release/src/interfaces/erc3156/IERC3156FlashLender.sol) Interface for ERC-3156 flash lenders. diff --git a/docs/contracts/v2/reference/core/interfaces/hooks/interface.ISablierV2LockupRecipient.md b/docs/contracts/v2/reference/core/interfaces/hooks/interface.ISablierV2LockupRecipient.md index f1eb2910..19eb8bc9 100644 --- a/docs/contracts/v2/reference/core/interfaces/hooks/interface.ISablierV2LockupRecipient.md +++ b/docs/contracts/v2/reference/core/interfaces/hooks/interface.ISablierV2LockupRecipient.md @@ -1,6 +1,6 @@ # ISablierV2LockupRecipient -[Git Source](https://github.com/sablier-labs/v2-core/blob/bca1d9ea0485b065544486bb01f4148d44289644/src/interfaces/hooks/ISablierV2LockupRecipient.sol) +[Git Source](https://github.com/sablier-labs/v2-core/blob/release/src/interfaces/hooks/ISablierV2LockupRecipient.sol) Interface for recipient contracts capable of reacting to cancellations, renouncements, and withdrawals. diff --git a/docs/contracts/v2/reference/core/interfaces/hooks/interface.ISablierV2LockupSender.md b/docs/contracts/v2/reference/core/interfaces/hooks/interface.ISablierV2LockupSender.md deleted file mode 100644 index 3705a53d..00000000 --- a/docs/contracts/v2/reference/core/interfaces/hooks/interface.ISablierV2LockupSender.md +++ /dev/null @@ -1,37 +0,0 @@ -# ISablierV2LockupSender - -[Git Source](https://github.com/sablier-labs/v2-core/blob/bca1d9ea0485b065544486bb01f4148d44289644/src/interfaces/hooks/ISablierV2LockupSender.sol) - -Interface for sender contracts capable of reacting to cancellations. - -_Implementation of this interface is optional. If a sender contract doesn't implement this interface, function execution -will not revert._ - -## Functions - -### onStreamCanceled - -Responds to recipient-triggered cancellations. - -Notes: - -- This function may revert, but the Sablier contract will ignore the revert. - -```solidity -function onStreamCanceled( - uint256 streamId, - address recipient, - uint128 senderAmount, - uint128 recipientAmount -) - external; -``` - -**Parameters** - -| Name | Type | Description | -| ----------------- | --------- | ----------------------------------------------------------------------------------------------------------- | -| `streamId` | `uint256` | The id of the canceled stream. | -| `recipient` | `address` | The stream's recipient, who canceled the stream. | -| `senderAmount` | `uint128` | The amount of assets refunded to the stream's sender, denoted in units of the asset's decimals. | -| `recipientAmount` | `uint128` | The amount of assets left for the stream's recipient to withdraw, denoted in units of the asset's decimals. | diff --git a/docs/contracts/v2/reference/core/interfaces/interface.IAdminable.md b/docs/contracts/v2/reference/core/interfaces/interface.IAdminable.md index 5ffb7e81..31197634 100644 --- a/docs/contracts/v2/reference/core/interfaces/interface.IAdminable.md +++ b/docs/contracts/v2/reference/core/interfaces/interface.IAdminable.md @@ -1,6 +1,6 @@ # IAdminable -[Git Source](https://github.com/sablier-labs/v2-core/blob/bca1d9ea0485b065544486bb01f4148d44289644/docs/contracts/v2/reference/core/interfaces) +[Git Source](https://github.com/sablier-labs/v2-core/blob/release/src/interfaces/IAdminable.sol) Contract module that provides a basic access control mechanism, with an admin that can be granted exclusive access to specific functions. The inheriting contract must set the initial admin in the constructor. diff --git a/docs/contracts/v2/reference/core/interfaces/interface.ISablierV2Base.md b/docs/contracts/v2/reference/core/interfaces/interface.ISablierV2Base.md index 81bad032..b879511b 100644 --- a/docs/contracts/v2/reference/core/interfaces/interface.ISablierV2Base.md +++ b/docs/contracts/v2/reference/core/interfaces/interface.ISablierV2Base.md @@ -1,6 +1,6 @@ # ISablierV2Base -[Git Source](https://github.com/sablier-labs/v2-core/blob/bca1d9ea0485b065544486bb01f4148d44289644/docs/contracts/v2/reference/core/interfaces) +[Git Source](https://github.com/sablier-labs/v2-core/blob/release/src/interfaces/ISablierV2Base.sol) **Inherits:** [IAdminable](/docs/contracts/v2/reference/core/interfaces/interface.IAdminable.md) diff --git a/docs/contracts/v2/reference/core/interfaces/interface.ISablierV2Comptroller.md b/docs/contracts/v2/reference/core/interfaces/interface.ISablierV2Comptroller.md index 78dfa024..a4623732 100644 --- a/docs/contracts/v2/reference/core/interfaces/interface.ISablierV2Comptroller.md +++ b/docs/contracts/v2/reference/core/interfaces/interface.ISablierV2Comptroller.md @@ -1,6 +1,6 @@ # ISablierV2Comptroller -[Git Source](https://github.com/sablier-labs/v2-core/blob/bca1d9ea0485b065544486bb01f4148d44289644/docs/contracts/v2/reference/core/interfaces) +[Git Source](https://github.com/sablier-labs/v2-core/blob/release/src/interfaces/ISablierV2Comptroller.sol) **Inherits:** [IAdminable](/docs/contracts/v2/reference/core/interfaces/interface.IAdminable.md) diff --git a/docs/contracts/v2/reference/core/interfaces/interface.ISablierV2Lockup.md b/docs/contracts/v2/reference/core/interfaces/interface.ISablierV2Lockup.md index e8d4ee80..760a45e1 100644 --- a/docs/contracts/v2/reference/core/interfaces/interface.ISablierV2Lockup.md +++ b/docs/contracts/v2/reference/core/interfaces/interface.ISablierV2Lockup.md @@ -1,9 +1,9 @@ # ISablierV2Lockup -[Git Source](https://github.com/sablier-labs/v2-core/blob/bca1d9ea0485b065544486bb01f4148d44289644/docs/contracts/v2/reference/core/interfaces) +[Git Source](https://github.com/sablier-labs/v2-core/blob/release/src/interfaces/ISablierV2Lockup.sol) **Inherits:** [ISablierV2Base](/docs/contracts/v2/reference/core/interfaces/interface.ISablierV2Base.md), -IERC721Metadata +[IERC721Metadata](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/e50c24f5839db17f46991478384bfda14acfb830/contracts/token/ERC721/extensions/IERC721Metadata.sol) Common logic between all Sablier V2 lockup streaming contracts. @@ -202,6 +202,22 @@ function isStream(uint256 streamId) external view returns (bool result); | ---------- | --------- | ---------------------------- | | `streamId` | `uint256` | The stream id for the query. | +### isTransferable + +Retrieves a flag indicating whether the stream NFT can be transferred. + +_Reverts if `streamId` references a null stream._ + +```solidity +function isTransferable(uint256 streamId) external view returns (bool result); +``` + +**Parameters** + +| Name | Type | Description | +| ---------- | --------- | ---------------------------- | +| `streamId` | `uint256` | The stream id for the query. | + ### isWarm Retrieves a flag indicating whether the stream is warm, i.e. either pending or streaming. @@ -334,11 +350,10 @@ Emits a {Transfer}, {CancelLockupStream}, and {MetadataUpdate} event. Notes: - If there any assets left for the recipient to withdraw, the stream is marked as canceled. Otherwise, the stream is marked as depleted. -- This function attempts to invoke a hook on either the sender or the recipient, depending on who `msg.sender` is, and - if the resolved address is a contract. Requirements: +- This function attempts to invoke a hook on the recipient, if the resolved address is a contract. Requirements: - Must not be delegate called. - The stream must be warm and cancelable. -- `msg.sender` must be either the stream's sender or the stream's recipient (i.e. the NFT owner). +- `msg.sender` must be the stream's sender. ```solidity function cancel(uint256 streamId) external; @@ -514,9 +529,10 @@ Emitted when a stream is canceled. ```solidity event CancelLockupStream( - uint256 indexed streamId, + uint256 streamId, address indexed sender, address indexed recipient, + address indexed asset, uint128 senderAmount, uint128 recipientAmount ); @@ -545,5 +561,5 @@ event SetNFTDescriptor( Emitted when assets are withdrawn from a stream. ```solidity -event WithdrawFromLockupStream(uint256 indexed streamId, address indexed to, uint128 amount); +event WithdrawFromLockupStream(uint256 indexed streamId, address indexed to, address indexed asset, uint128 amount); ``` diff --git a/docs/contracts/v2/reference/core/interfaces/interface.ISablierV2LockupDynamic.md b/docs/contracts/v2/reference/core/interfaces/interface.ISablierV2LockupDynamic.md index 282a0b16..335fcc40 100644 --- a/docs/contracts/v2/reference/core/interfaces/interface.ISablierV2LockupDynamic.md +++ b/docs/contracts/v2/reference/core/interfaces/interface.ISablierV2LockupDynamic.md @@ -174,6 +174,7 @@ event CreateLockupDynamicStream( Lockup.CreateAmounts amounts, IERC20 indexed asset, bool cancelable, + bool transferable, LockupDynamic.Segment[] segments, LockupDynamic.Range range, address broker diff --git a/docs/contracts/v2/reference/core/interfaces/interface.ISablierV2LockupLinear.md b/docs/contracts/v2/reference/core/interfaces/interface.ISablierV2LockupLinear.md index 5ef4b2d8..82d6cc8f 100644 --- a/docs/contracts/v2/reference/core/interfaces/interface.ISablierV2LockupLinear.md +++ b/docs/contracts/v2/reference/core/interfaces/interface.ISablierV2LockupLinear.md @@ -159,6 +159,7 @@ event CreateLockupLinearStream( Lockup.CreateAmounts amounts, IERC20 indexed asset, bool cancelable, + bool transferable, LockupLinear.Range range, address broker ); diff --git a/docs/contracts/v2/reference/core/interfaces/interface.ISablierV2NftDescriptor.md b/docs/contracts/v2/reference/core/interfaces/interface.ISablierV2NftDescriptor.md index a82f3290..f9430e18 100644 --- a/docs/contracts/v2/reference/core/interfaces/interface.ISablierV2NftDescriptor.md +++ b/docs/contracts/v2/reference/core/interfaces/interface.ISablierV2NftDescriptor.md @@ -1,6 +1,6 @@ # ISablierV2NFTDescriptor -[Git Source](https://github.com/sablier-labs/v2-core/blob/bca1d9ea0485b065544486bb01f4148d44289644/docs/contracts/v2/reference/core/interfaces) +[Git Source](https://github.com/sablier-labs/v2-core/blob/release/src/interfaces/ISablierV2NFTDescriptor.sol) This contract generates the URI describing the Sablier V2 stream NFTs. diff --git a/docs/contracts/v2/reference/core/libraries/library.Errors.md b/docs/contracts/v2/reference/core/libraries/library.Errors.md index c4a52fac..c4ed9422 100644 --- a/docs/contracts/v2/reference/core/libraries/library.Errors.md +++ b/docs/contracts/v2/reference/core/libraries/library.Errors.md @@ -1,6 +1,6 @@ # Errors -[Git Source](https://github.com/sablier-labs/v2-core/blob/bca1d9ea0485b065544486bb01f4148d44289644/src/libraries/Errors.sol) +[Git Source](https://github.com/sablier-labs/v2-core/blob/release/src/libraries/Errors.sol) Library containing all custom errors the protocol may revert with. @@ -94,6 +94,14 @@ Thrown when the stream's sender tries to withdraw to an address other than the r error SablierV2Lockup_InvalidSenderWithdrawal(uint256 streamId, address sender, address to); ``` +### SablierV2Lockup_NotTransferrable + +Thrown when trying to transfer Stream NFT when transferability is disabled. + +```solidity +error SablierV2Lockup_NotTransferrable(uint256 tokenId); +``` + ### SablierV2Lockup_Null Thrown when the id references a null stream. diff --git a/docs/contracts/v2/reference/core/libraries/library.Helpers.md b/docs/contracts/v2/reference/core/libraries/library.Helpers.md index 15829e1e..24f05de5 100644 --- a/docs/contracts/v2/reference/core/libraries/library.Helpers.md +++ b/docs/contracts/v2/reference/core/libraries/library.Helpers.md @@ -1,6 +1,6 @@ # Helpers -[Git Source](https://github.com/sablier-labs/v2-core/blob/bca1d9ea0485b065544486bb01f4148d44289644/src/libraries/Helpers.sol) +[Git Source](https://github.com/sablier-labs/v2-core/blob/release/src/libraries/Helpers.sol) Library with helper functions needed across the Sablier V2 contracts. diff --git a/docs/contracts/v2/reference/core/libraries/library.NFTSVG.md b/docs/contracts/v2/reference/core/libraries/library.NFTSVG.md index d16aa87e..326e2fd8 100644 --- a/docs/contracts/v2/reference/core/libraries/library.NFTSVG.md +++ b/docs/contracts/v2/reference/core/libraries/library.NFTSVG.md @@ -1,6 +1,6 @@ # NFTSVG -[Git Source](https://github.com/sablier-labs/v2-core/blob/bca1d9ea0485b065544486bb01f4148d44289644/src/libraries/NFTSVG.sol) +[Git Source](https://github.com/sablier-labs/v2-core/blob/release/src/libraries/NFTSVG.sol) ## State Variables @@ -51,7 +51,7 @@ function generateFloatingText( function generateHrefs( uint256 progressXPosition, uint256 statusXPosition, - uint256 streamedXPosition, + uint256 amountXPosition, uint256 durationXPosition ) internal @@ -66,6 +66,7 @@ function generateHrefs( ```solidity struct SVGParams { string accentColor; + string amount; string assetAddress; string assetSymbol; string duration; @@ -73,7 +74,6 @@ struct SVGParams { uint256 progressNumerical; string sablierAddress; string status; - string streamed; string streamingModel; } ``` @@ -82,6 +82,9 @@ struct SVGParams { ```solidity struct SVGVars { + string amountCard; + uint256 amountWidth; + uint256 amountXPosition; string cards; uint256 cardsWidth; string durationCard; @@ -93,8 +96,5 @@ struct SVGVars { string statusCard; uint256 statusWidth; uint256 statusXPosition; - string streamedCard; - uint256 streamedWidth; - uint256 streamedXPosition; } ``` diff --git a/docs/contracts/v2/reference/core/libraries/library.SVGElements.md b/docs/contracts/v2/reference/core/libraries/library.SVGElements.md index 82cec524..4544a659 100644 --- a/docs/contracts/v2/reference/core/libraries/library.SVGElements.md +++ b/docs/contracts/v2/reference/core/libraries/library.SVGElements.md @@ -1,6 +1,6 @@ # SVGElements -[Git Source](https://github.com/sablier-labs/v2-core/blob/bca1d9ea0485b065544486bb01f4148d44289644/src/libraries/SVGElements.sol) +[Git Source](https://github.com/sablier-labs/v2-core/blob/release/src/libraries/SVGElements.sol) ## State Variables @@ -186,7 +186,7 @@ function stringifyCardType(CardType cardType) internal pure returns (string memo enum CardType { PROGRESS, STATUS, - STREAMED, + AMOUNT, DURATION } ``` diff --git a/docs/contracts/v2/reference/core/types/library.Lockup.md b/docs/contracts/v2/reference/core/types/library.Lockup.md index 9a3bbda6..511f103b 100644 --- a/docs/contracts/v2/reference/core/types/library.Lockup.md +++ b/docs/contracts/v2/reference/core/types/library.Lockup.md @@ -1,6 +1,6 @@ # Lockup -[Git Source](https://github.com/sablier-labs/v2-core/blob/bca1d9ea0485b065544486bb01f4148d44289644/src/types/DataTypes.sol) +[Git Source](https://github.com/sablier-labs/v2-core/blob/release/src/types/DataTypes.sol) Namespace for the structs used in both [SablierV2LockupLinear](docs/contracts/v2/reference/core/contract.SablierV2LockupLinear.md) and diff --git a/docs/contracts/v2/reference/core/types/library.LockupDynamic.md b/docs/contracts/v2/reference/core/types/library.LockupDynamic.md index 33335f90..c884bf1d 100644 --- a/docs/contracts/v2/reference/core/types/library.LockupDynamic.md +++ b/docs/contracts/v2/reference/core/types/library.LockupDynamic.md @@ -1,6 +1,6 @@ # LockupDynamic -[Git Source](https://github.com/sablier-labs/v2-core/blob/bca1d9ea0485b065544486bb01f4148d44289644/src/types/DataTypes.sol) +[Git Source](https://github.com/sablier-labs/v2-core/blob/release/src/types/DataTypes.sol) Namespace for the structs used in [SablierV2LockupDynamic](docs/contracts/v2/reference/core/contract.SablierV2LockupDynamic.md). @@ -15,6 +15,7 @@ Struct encapsulating the parameters for the {SablierV2LockupDynamic.createWithDe struct CreateWithDeltas { address sender; bool cancelable; + bool transferable; address recipient; uint128 totalAmount; IERC20 asset; @@ -23,6 +24,19 @@ struct CreateWithDeltas { } ``` +**Parameters** + +| Name | Type | Description | +| -------------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `sender` | `address` | The address streaming the assets, with the ability to cancel the stream. It doesn't have to be the same as `msg.sender`. | +| `cancelable` | `bool` | Indicates if the stream is cancelable. | +| `transferable` | `bool` | Indicates if the stream NFT is transferable. | +| `recipient` | `address` | The address receiving the assets. | +| `totalAmount` | `uint128` | The total amount of `ERC-20` assets to be paid, including the stream deposit and any potential fees, all denoted in units of the asset's decimals. | +| `asset` | `IERC20` | The contract address of the `ERC-20` asset used for streaming. | +| `broker` | `Broker` | Struct containing (i) the address of the broker assisting in creating the stream, and (ii) the percentage fee paid to the broker from `totalAmount`, denoted as a fixed-point number. Both can be set to zero. | +| `segments` | `SegmentWithDelta[]` | Segments with deltas used to compose the custom streaming curve. Milestones are calculated by starting from `block.timestamp` and adding each delta to the previous milestone. | + ### CreateWithMilestones Struct encapsulating the parameters for the {SablierV2LockupDynamic.createWithMilestones} function. @@ -32,6 +46,7 @@ struct CreateWithMilestones { address sender; uint40 startTime; bool cancelable; + bool transferable; address recipient; uint128 totalAmount; IERC20 asset; @@ -40,6 +55,20 @@ struct CreateWithMilestones { } ``` +**Parameters** + +| Name | Type | Description | +| -------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `sender` | `address` | The address streaming the assets, with the ability to cancel the stream. It doesn't have to be the same as `msg.sender`. | +| `startTime` | `uint40` | The Unix timestamp indicating the stream's start. | +| `cancelable` | `bool` | Indicates if the stream is cancelable. | +| `transferable` | `bool` | Indicates if the stream NFT is transferable. | +| `recipient` | `address` | The address receiving the assets. | +| `totalAmount` | `uint128` | The total amount of `ERC-20` assets to be paid, including the stream deposit and any potential fees, all denoted in units of the asset's decimals. | +| `asset` | `IERC20` | The contract address of the `ERC-20` asset used for streaming. | +| `broker` | `Broker` | Struct containing (i) the address of the broker assisting in creating the stream, and (ii) the percentage fee paid to the broker from `totalAmount`, denoted as a fixed-point number. Both can be set to zero. | +| `segments` | `Segment[]` | Segments used to compose the custom streaming curve. | + ### Range Struct encapsulating the time range. @@ -63,6 +92,14 @@ struct Segment { } ``` +**Parameters** + +| Name | Type | Description | +| ----------- | --------- | ---------------------------------------------------------------------------------------------- | +| `amount` | `uint128` | The amount of assets to be streamed in this segment, denoted in units of the asset's decimals. | +| `exponent` | `UD2x18` | The exponent of this segment, denoted as a fixed-point number. | +| `milestone` | `uint40` | The Unix timestamp indicating this segment's end. | + ### SegmentWithDelta Segment struct used at runtime in {SablierV2LockupDynamic.createWithDeltas}. @@ -75,6 +112,14 @@ struct SegmentWithDelta { } ``` +**Parameters** + +| Name | Type | Description | +| ----------- | --------- | ---------------------------------------------------------------------------------------------- | +| `amount` | `uint128` | The amount of assets to be streamed in this segment, denoted in units of the asset's decimals. | +| `exponent` | `UD2x18` | The exponent of this segment, denoted as a fixed-point number. | +| `milestone` | `uint40` | The time difference in seconds between this segment and the previous one. | + ### Stream Lockup Dynamic stream. @@ -91,7 +136,24 @@ struct Stream { IERC20 asset; bool isDepleted; bool isStream; + bool isTransferable; Lockup.Amounts amounts; Segment[] segments; } ``` + +**Parameters** + +| Name | Type | Description | +| ---------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------ | +| `sender` | `address` | The address streaming the assets, with the ability to cancel the stream. It doesn't have to be the same as `msg.sender`. | +| `startTime` | `uint40` | The Unix timestamp indicating the stream's start. | +| `endTime` | `uint40` | The Unix timestamp indicating the stream's end. | +| `isCancelable` | `bool` | Boolean indicating if the stream is cancelable. | +| `wasCanceled` | `bool` | Boolean indicating if the stream was canceled. | +| `asset` | `IERC20` | The contract address of the `ERC-20` asset used for streaming. | +| `isDepleted` | `bool` | Boolean indicating if the stream is depleted. | +| `isStream` | `bool` | Boolean indicating if the struct entity exists. | +| `isTransferable` | `bool` | Boolean indicating if the stream NFT is transferable. | +| `amounts` | `Lockup.Amounts` | Struct containing the deposit, withdrawn, and refunded amounts, all denoted in units of the asset's decimals. | +| `segments` | `Segment[]` | Segments used to compose the custom streaming curve. | diff --git a/docs/contracts/v2/reference/core/types/library.LockupLinear.md b/docs/contracts/v2/reference/core/types/library.LockupLinear.md index a1d0aee7..3c789b8c 100644 --- a/docs/contracts/v2/reference/core/types/library.LockupLinear.md +++ b/docs/contracts/v2/reference/core/types/library.LockupLinear.md @@ -1,6 +1,6 @@ # LockupLinear -[Git Source](https://github.com/sablier-labs/v2-core/blob/bca1d9ea0485b065544486bb01f4148d44289644/src/types/DataTypes.sol) +[Git Source](https://github.com/sablier-labs/v2-core/blob/release/src/types/DataTypes.sol) Namespace for the structs used in [SablierV2LockupLinear](docs/contracts/v2/reference/core/contract.SablierV2LockupLinear.md). @@ -18,11 +18,25 @@ struct CreateWithDurations { uint128 totalAmount; IERC20 asset; bool cancelable; + bool transferable; Durations durations; Broker broker; } ``` +**Parameters** + +| Name | Type | Description | +| -------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `sender` | `address` | The address streaming the assets, with the ability to cancel the stream. It doesn't have to be the same as `msg.sender`. | +| `recipient` | `address` | The address receiving the assets. | +| `totalAmount` | `uint128` | The total amount of `ERC-20` assets to be paid, including the stream deposit and any potential fees, all denoted in units of the asset's decimals. | +| `asset` | `IERC20` | The contract address of the `ERC-20` asset used for streaming. | +| `cancelable` | `bool` | Indicates if the stream is cancelable. | +| `transferable` | `bool` | Indicates if the stream NFT is transferable. | +| `durations` | `Durations` | Struct containing (i) cliff period duration and (ii) total stream duration, both in seconds. | +| `broker` | `Broker` | Struct containing (i) the address of the broker assisting in creating the stream, and (ii) the percentage fee paid to the broker from `totalAmount`, denoted as a fixed-point number. Both can be set to zero. | + ### CreateWithRange Struct encapsulating the parameters for the {SablierV2LockupLinear.createWithRange} function. @@ -34,11 +48,25 @@ struct CreateWithRange { uint128 totalAmount; IERC20 asset; bool cancelable; + bool transferable; Range range; Broker broker; } ``` +**Parameters** + +| Name | Type | Description | +| -------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `sender` | `address` | The address streaming the assets, with the ability to cancel the stream. It doesn't have to be the same as `msg.sender`. | +| `recipient` | `address` | The address receiving the assets. | +| `totalAmount` | `uint128` | The total amount of `ERC-20` assets to be paid, including the stream deposit and any potential fees, all denoted in units of the asset's decimals. | +| `asset` | `IERC20` | The contract address of the `ERC-20` asset used for streaming. | +| `cancelable` | `bool` | Indicates if the stream is cancelable. | +| `transferable` | `bool` | Indicates if the stream NFT is transferable. | +| `range` | `Range` | Struct containing (i) the stream's start time, (ii) cliff time, and (iii) end time, all as Unix timestamps. | +| `broker` | `Broker` | Struct containing (i) the address of the broker assisting in creating the stream, and (ii) the percentage fee paid to the broker from `totalAmount`, denoted as a fixed-point number. Both can be set to zero. | + ### Durations Struct encapsulating the cliff duration and the total duration. @@ -79,6 +107,23 @@ struct Stream { uint40 endTime; bool isDepleted; bool isStream; + bool isTransferable; Lockup.Amounts amounts; } ``` + +**Parameters** + +| Name | Type | Description | +| ---------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------ | +| `sender` | `address` | The address streaming the assets, with the ability to cancel the stream. It doesn't have to be the same as `msg.sender`. | +| `startTime` | `uint40` | The Unix timestamp indicating the stream's start. | +| `cliffTime` | `uint40` | The Unix timestamp indicating the cliff period's end. | +| `isCancelable` | `bool` | Boolean indicating if the stream is cancelable. | +| `wasCanceled` | `bool` | Boolean indicating if the stream was canceled. | +| `asset` | `IERC20` | The contract address of the `ERC-20` asset used for streaming. | +| `endTime` | `uint40` | The Unix timestamp indicating the stream's end. | +| `isDepleted` | `bool` | Boolean indicating if the stream is depleted. | +| `isStream` | `bool` | Boolean indicating if the struct entity exists. | +| `isTransferable` | `bool` | Boolean indicating if the stream NFT is transferable. | +| `amounts` | `Lockup.Amounts` | Struct containing the deposit, withdrawn, and refunded amounts, all denoted in units of the asset's decimals. | diff --git a/docs/contracts/v2/reference/core/types/struct.Broker.md b/docs/contracts/v2/reference/core/types/struct.Broker.md index 954801a9..fffbfbe8 100644 --- a/docs/contracts/v2/reference/core/types/struct.Broker.md +++ b/docs/contracts/v2/reference/core/types/struct.Broker.md @@ -1,6 +1,6 @@ # Broker -[Git Source](https://github.com/sablier-labs/v2-core/blob/bca1d9ea0485b065544486bb01f4148d44289644/src/types/DataTypes.sol) +[Git Source](https://github.com/sablier-labs/v2-core/blob/release/src/types/DataTypes.sol) Struct encapsulating the broker parameters passed to the create functions. Both can be set to zero. diff --git a/docs/contracts/v2/reference/periphery/abstracts/abstract.OnlyDelegateCall.md b/docs/contracts/v2/reference/periphery/abstracts/abstract.OnlyDelegateCall.md deleted file mode 100644 index 12cb8b39..00000000 --- a/docs/contracts/v2/reference/periphery/abstracts/abstract.OnlyDelegateCall.md +++ /dev/null @@ -1,46 +0,0 @@ -# OnlyDelegateCall - -[Git Source](https://github.com/sablier-labs/v2-periphery/blob/05c331e79e05886c7837dfda1bc21197c1c3c748/docs/contracts/v2/reference/periphery/abstracts) - -This contract implements logic to allow only delegate calls. - -## State Variables - -### \_original - -_The address of the original contract that was deployed._ - -```solidity -address private immutable _original; -``` - -## Functions - -### constructor - -_Sets the original contract address._ - -```solidity -constructor(); -``` - -### onlyDelegateCall - -Allows only delegate calls. - -```solidity -modifier onlyDelegateCall(); -``` - -### \_allowOnlyDelegateCall - -This function checks whether the current call is a delegate call, and reverts if it is not. - -- 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 _allowOnlyDelegateCall() private view; -``` diff --git a/docs/contracts/v2/reference/periphery/abstracts/abstract.SablierV2MerkleStreamer.md b/docs/contracts/v2/reference/periphery/abstracts/abstract.SablierV2MerkleStreamer.md new file mode 100644 index 00000000..804eb8b1 --- /dev/null +++ b/docs/contracts/v2/reference/periphery/abstracts/abstract.SablierV2MerkleStreamer.md @@ -0,0 +1,137 @@ +# SablierV2MerkleStreamer + +[Git Source](https://github.com/sablier-labs/v2-periphery/blob/release/src/abstracts/SablierV2MerkleStreamer.sol) + +**Inherits:** +[ISablierV2MerkleStreamer](/docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2MerkleStreamer.md) +[Adminable](/docs/contracts/v2/reference/core/abstracts/abstract.Adminable.md) + +## State Variables + +### ASSET + +_The streamed ERC-20 asset._ + +```solidity +IERC20 public immutable override ASSET; +``` + +### CANCELABLE + +_A flag indicating whether the streams can be canceled._ + +```solidity +bool public immutable override CANCELABLE; +``` + +### EXPIRATION + +_The cut-off point for the Merkle streamer, as a Unix timestamp. A value of zero means there is no expiration._ + +```solidity +uint40 public immutable override EXPIRATION; +``` + +### LOCKUP + +_The address of the {SablierV2Lockup} contract._ + +```solidity +ISablierV2Lockup public immutable override LOCKUP; +``` + +### MERKLE_ROOT + +_The root of the Merkle tree used to validate the claims._ + +```solidity +bytes32 public immutable override MERKLE_ROOT; +``` + +### TRANSFERABLE + +_A flag indicating whether the stream NFTs are transferable._ + +```solidity +bool public immutable override TRANSFERABLE; +``` + +### \_claimedBitMap; + +_Packed booleans that record the history of claims._ + +```solidity +BitMaps.BitMap internal _claimedBitMap; +``` + +We are using +[BitMaps](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/e50c24f5839db17f46991478384bfda14acfb830/contracts/utils/structs/BitMaps.sol) +OpenZeppelin's library. + +## User Facing Functions + +### constructor + +_Sets the immutable state variables._ + +```solidity +constructor( + address initialAdmin, + IERC20 asset, + ISablierV2Lockup lockup, + bytes32 merkleRoot, + uint40 expiration, + bool cancelable, + bool transferable +); +``` + +### hasClaimed + +Returns a flag indicating whether a claim has been made for a given index. Uses a bitmap to save gas. + +```solidity +function hasClaimed(uint256 index) external returns (bool); +``` + +**Parameters** + +| Name | Type | Description | +| ------- | --------- | ------------------------------------ | +| `index` | `uint256` | The index of the recipient to check. | + +### hasExpired + +Returns a flag indicating whether the Merkle streamer has expired. + +```solidity +function hasExpired() external returns (bool); +``` + +### clawback + +Claws back the unclaimed tokens from the Merkle streamer. + +Emits a {Clawback} event. Requirements: + +- `msg.sender` must be the contract admin. +- The Merkle streamer must have expired. + +```solidity +function clawback(address to, uint128 amount) external; +``` + +**Parameters** + +| Name | Type | Description | +| -------- | --------- | ---------------------------------- | +| `to` | `address` | The address to receive the tokens. | +| `amount` | `uint128` | The amount of tokens to claw back. | + +## Internal Functions + +Validates the parameters of the `claim` function, which is implemented by child contracts. + +```solidity +function _checkClaim(uint256 index, bytes32 leaf, bytes32[] calldata merkleProof) internal view; +``` diff --git a/docs/contracts/v2/reference/periphery/contract.SablierV2Archive.md b/docs/contracts/v2/reference/periphery/contract.SablierV2Archive.md deleted file mode 100644 index 0ce79418..00000000 --- a/docs/contracts/v2/reference/periphery/contract.SablierV2Archive.md +++ /dev/null @@ -1,69 +0,0 @@ ---- -sidebar_position: 1 ---- - -# SablierV2Archive - -[Git Source](https://github.com/sablier-labs/v2-periphery/blob/05c331e79e05886c7837dfda1bc21197c1c3c748/docs/contracts/v2/reference/periphery) - -**Inherits:** [ISablierV2Archive](/docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2Archive.md), -Adminable - -_See the documentation in -[ISablierV2Archive](/docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2Archive.md)._ - -## State Variables - -### isListed - -A boolean flag that indicates whether the provided address is part of the archive. - -```solidity -mapping(address addr => bool listed) public override isListed; -``` - -## Functions - -### constructor - -```solidity -constructor(address initialAdmin); -``` - -### list - -Lists an address in the archive. - -Emits a {List} event. Notes: - -- It is not an error to list an address that is already listed. Requirements: -- The caller must be the admin. - -```solidity -function list(address addr) external onlyAdmin; -``` - -**Parameters** - -| Name | Type | Description | -| ------ | --------- | --------------------------------------------------------------- | -| `addr` | `address` | The address to list in the archive, which should be a contract. | - -### unlist - -Unlists an address from the archive. - -Emits an {Unlist} event. Notes: - -- It is not an error to unlist an address that is not already listed. Requirements: -- The caller must be the admin. - -```solidity -function unlist(address addr) external onlyAdmin; -``` - -**Parameters** - -| Name | Type | Description | -| ------ | --------- | ---------------------------------------------------------------------------- | -| `addr` | `address` | The address to unlist from the archive, which is usually a contract address. | diff --git a/docs/contracts/v2/reference/periphery/contract.SablierV2Batch.md b/docs/contracts/v2/reference/periphery/contract.SablierV2Batch.md new file mode 100644 index 00000000..ff5a6164 --- /dev/null +++ b/docs/contracts/v2/reference/periphery/contract.SablierV2Batch.md @@ -0,0 +1,161 @@ +# SablierV2Batch + +[Git Source](https://github.com/sablier-labs/v2-periphery/blob/release/src/SablierV2Batch.sol) + +**Inherits:** [ISablierV2Batch](/contracts/v2/reference/periphery/interfaces/interface.ISablierV2Batch) + +_See the documentation in [ISablierV2Batch](/contracts/v2/reference/periphery/interfaces/interface.ISablierV2Batch)._ + +## User Facing Functions + +### createMerkleStreamerLL + +Creates a batch of Lockup Linear streams using `createWithDurations`. + +Requirements: + +- There must be at least one element in `batch`. +- All requirements from + [ISablierV2LockupLinear.createWithDurations](/contracts/v2/reference/core/interfaces/interface.ISablierV2LockupLinear#createwithdurations) + must be met for each stream. + +```solidity +function createWithDurations( + ISablierV2LockupLinear lockupLinear, + IERC20 asset, + Batch.CreateWithDurations[] calldata batch +) + external + returns (uint256[] memory streamIds); +``` + +**Parameters** + +| Name | Type | Description | +| -------------- | ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `lockupLinear` | `ISablierV2LockupLinear` | The address of the {SablierV2LockupLinear} contract. | +| `asset` | `IERC20` | The address of the `ERC-20` asset. | +| `batch` | `Batch.CreateWithDurations[]` | An array of structs, each encapsulating a subset of the parameters of [SablierV2LockupLinear.createWithDurations](/contracts/v2/reference/core/types/library.LockupLinear#createwithdurations). | + +### createWithRange + +Creates a batch of Lockup Linear streams using `createWithRange`. + +Requirements: + +- There must be at least one element in `batch`. +- All requirements from + [ISablierV2LockupLinear.createWithRange](/contracts/v2/reference/core/interfaces/interface.ISablierV2LockupLinear#createwithrange) + must be met for each stream. + +```solidity +function createWithRange( + ISablierV2LockupLinear lockupLinear, + IERC20 asset, + Batch.createWithRange[] calldata batch +) + external + returns (uint256[] memory streamIds); +``` + +**Parameters** + +| Name | Type | Description | +| -------------- | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `lockupLinear` | `ISablierV2LockupLinear` | The address of the {SablierV2LockupLinear} contract. | +| `asset` | `IERC20` | The address of the `ERC-20` asset. | +| `batch` | `Batch.CreateWithRange[]` | An array of structs, each encapsulating a subset of the parameters of [SablierV2LockupLinear.createWithRange](/contracts/v2/reference/core/types/library.LockupLinear#createwithrange). | + +### createWithDeltas + +Creates a batch of Lockup Dynamic streams using `createWithDeltas`. + +Requirements: + +- There must be at least one element in `batch`. +- All requirements from + [ISablierV2LockupDynamic.createWithDeltas](/contracts/v2/reference/core/interfaces/interface.ISablierV2LockupDynamic#createwithdeltas) + must be met for each stream. + +```solidity +function createWithDeltas( + ISablierV2LockupDynamic lockupDynamic, + IERC20 asset, + Batch.CreateWithDeltas[] calldata batch +) + external + returns (uint256[] memory streamIds); +``` + +**Parameters** + +| Name | Type | Description | +| --------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `lockupDynamic` | `ISablierV2LockupDynamic` | The address of the {SablierV2LockupDynamic} contract. | +| `asset` | `IERC20` | The address of the `ERC-20` asset. | +| `batch` | `Batch.CreateWithDeltas[]` | An array of structs, each encapsulating a subset of the parameters of [SablierV2LockupDynamic.createWithDeltas](/contracts/v2/reference/core/types/library.LockupDynamic#createwithdeltas). | + +### createWithMilestones + +Creates a batch of Lockup Dynamic streams using `createWithMilestones`. + +Requirements: + +- There must be at least one element in `batch`. +- All requirements from + [ISablierV2LockupDynamic.createWithMilestones](/contracts/v2/reference/core/interfaces/interface.ISablierV2LockupDynamic#createwithmilestones) + must be met for each stream. + +```solidity +function createWithMilestones( + ISablierV2LockupDynamic lockupDynamic, + IERC20 asset, + Batch.CreateWithMilestones[] calldata batch +) + external + returns (uint256[] memory streamIds); +``` + +**Parameters** + +| Name | Type | Description | +| --------------- | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `lockupDynamic` | `ISablierV2LockupDynamic` | The address of the {SablierV2LockupDynamic} contract. | +| `asset` | `IERC20` | The address of the `ERC-20` asset. | +| `batch` | `Batch.CreateWithMilestones[]` | An array of structs, each encapsulating a subset of the parameters of [SablierV2LockupDynamic.createWithMilestones](/contracts/v2/reference/core/types/library.LockupDynamic#createwithmilestones). | + +## Internal Functions + +### \_approve + +Helper function to approve a Sablier contract to spend funds from the batch. If the current allowance is insufficient, +this function approves Sablier to spend the exact `amount`. The {SafeERC20.forceApprove} function is used to handle +special `ERC-20 assets` (e.g. USDT) that require the current allowance to be zero before setting it to a non-zero value. + +```solidity +function _approve(address sablierContract, IERC20 asset, uint256 amount) internal; +``` + +**Parameters** + +| Name | Type | Description | +| ----------------- | --------- | ----------------------------------------------- | +| `sablierContract` | `address` | The address of the Sablier contract to approve. | +| `asset` | `IERC20` | The address of the `ERC-20` asset. | +| `amount` | `uint256` | The amount of tokens to approve. | + +### \_handleTransfer + +Helper function to transfer assets from the caller to the batch contract and approve the Sablier contract. + +```solidity +function _handleTransfer(address sablierContract, IERC20 asset, uint256 amount) internal; +``` + +**Parameters** + +| Name | Type | Description | +| ----------------- | --------- | ----------------------------------------------------- | +| `sablierContract` | `address` | The address of the Sablier contract to transfer from. | +| `asset` | `IERC20` | The address of the `ERC-20` asset. | +| `amount` | `uint256` | The amount of tokens to transfer. | diff --git a/docs/contracts/v2/reference/periphery/contract.SablierV2MerkleStreamerFactory.md b/docs/contracts/v2/reference/periphery/contract.SablierV2MerkleStreamerFactory.md new file mode 100644 index 00000000..4adadce1 --- /dev/null +++ b/docs/contracts/v2/reference/periphery/contract.SablierV2MerkleStreamerFactory.md @@ -0,0 +1,51 @@ +# SablierV2MerkleStreamerFactory + +[Git Source](https://github.com/sablier-labs/v2-periphery/blob/release/src/SablierV2MerkleStreamerFactory.sol) + +**Inherits:** +[ISablierV2MerkleStreamerFactory](/contracts/v2/reference/periphery/interfaces/interface.ISablierV2MerkleStreamerFactory) + +_See the documentation in +[ISablierV2MerkleStreamerFactory](/contracts/v2/reference/periphery/interfaces/interface.ISablierV2MerkleStreamerFactory)._ + +## User Facing Functions + +### createMerkleStreamerLL + +Creates a new Merkle streamer that uses Lockup Linear. + +Emits a {CreateMerkleStreamerLL} event. + +```solidity +function createMerkleStreamerLL( + address initialAdmin, + ISablierV2LockupLinear lockupLinear, + IERC20 asset, + bytes32 merkleRoot, + uint40 expiration, + LockupLinear.Durations memory streamDurations, + bool cancelable, + bool transferable, + string memory ipfsCID, + uint256 aggregateAmount, + uint256 recipientsCount + ) + external + returns (ISablierV2MerkleStreamerLL merkleStreamerLL); +``` + +**Parameters** + +| Name | Type | Description | +| ----------------- | ------------------------ | ----------------------------------------------------------------- | +| `initialAdmin` | `address` | The initial admin of the Merkle streamer contract. | +| `lockupLinear` | `ISablierV2LockupLinear` | The address of the {SablierV2LockupLinear} contract. | +| `asset` | `IERC20` | The address of the `ERC-20` asset. | +| `merkleRoot` | `bytes32` | The Merkle root of the claim data. | +| `expiration` | `uint40` | The expiration of the streaming campaign, as a Unix timestamp. | +| `streamDurations` | `LockupLinear.Durations` | The durations for each stream due to the recipient. | +| `cancelable` | `bool` | Indicates if each stream will be cancelable. | +| `transferable` | `bool` | Indicates if each stream NFT will be transferable. | +| `ipfsCID` | `string` | Metadata parameter emitted for indexing purposes. | +| `aggregateAmount` | `uint256` | Total amount of `ERC-20` assets to be streamed to all recipients. | +| `recipientsCount` | `uint256` | Total number of recipients eligible to claim. | diff --git a/docs/contracts/v2/reference/periphery/contract.SablierV2MerkleStreamerLL.md b/docs/contracts/v2/reference/periphery/contract.SablierV2MerkleStreamerLL.md new file mode 100644 index 00000000..f5c31566 --- /dev/null +++ b/docs/contracts/v2/reference/periphery/contract.SablierV2MerkleStreamerLL.md @@ -0,0 +1,78 @@ +# SablierV2MerkleStreamerLL + +[Git Source](https://github.com/sablier-labs/v2-periphery/blob/release/src/SablierV2MerkleStreamerLL.sol) + +**Inherits:** +[ISablierV2MerkleStreamerLL](/contracts/v2/reference/periphery/interfaces/interface.ISablierV2MerkleStreamerLL) +[SablierV2MerkleStreamer](/contracts/v2/reference/periphery/abstracts/abstract.SablierV2MerkleStreamer) + +_See the documentation in +[ISablierV2MerkleStreamerLL](/contracts/v2/reference/periphery/interfaces/interface.ISablierV2MerkleStreamerLL)._ + +## State Variables + +### LOCKUP_LINEAR + +The address of the {SablierV2LockupLinear} contract. + +```solidity +ISablierV2LockupLinear public immutable override LOCKUP_LINEAR; +``` + +### streamDurations + +The total streaming duration of each stream, after claim. + +```solidity +LockupLinear.Durations public override streamDurations; +``` + +## User Facing Functions + +### constructor + +Constructs the contract by initializing the immutable state variables, and max approving the Sablier contract. + +```solidity +constructor( + address initialAdmin, + ISablierV2LockupLinear lockupLinear, + IERC20 asset, + bytes32 merkleRoot, + uint40 expiration, + LockupLinear.Durations memory streamDurations_, + bool cancelable, + bool transferable +) + SablierV2MerkleStreamer(initialAdmin, asset, lockupLinear, merkleRoot, expiration, cancelable, transferable) +{} +``` + +### claim + +Makes the claim by creating a Lockup Linear stream to the recipient. + +Emits a {Claim} event. Requirements: + +- The protocol fee must be zero. +- The campaign must not have expired. +- The stream must not have been claimed already. +- The Merkle proof must be valid. + +```solidity +function claim( + uint256 index, + address recipient, + uint128 amount, + bytes32[] calldata merkleProof +) + external + returns (uint256 streamId); +``` + +| Name | Type | Description | +| --------- | ------------- | ---------------------------------------------- | +| `index` | `uint256` | The index of the recipient in the Merkle tree. | +| `address` | `recipient` | The address of the stream holder. | +| `amount` | `uint128` | The amount of tokens to be streamed. | +| `bytes32` | `merkleProof` | The Merkle proof of inclusion in the stream. | diff --git a/docs/contracts/v2/reference/periphery/contract.SablierV2ProxyPlugin.md b/docs/contracts/v2/reference/periphery/contract.SablierV2ProxyPlugin.md deleted file mode 100644 index aa788659..00000000 --- a/docs/contracts/v2/reference/periphery/contract.SablierV2ProxyPlugin.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -sidebar_position: 2 ---- - -# SablierV2ProxyPlugin - -[Git Source](https://github.com/sablier-labs/v2-periphery/blob/05c331e79e05886c7837dfda1bc21197c1c3c748/docs/contracts/v2/reference/periphery) - -**Inherits:** [OnlyDelegateCall](/docs/contracts/v2/reference/periphery/abstracts/abstract.OnlyDelegateCall.md), -[ISablierV2ProxyPlugin](/docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2ProxyPlugin.md) - -See the documentation in -[ISablierV2ProxyPlugin](/docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2ProxyPlugin.md). - -## State Variables - -### archive - -Retrieves the address of the archive contract. - -```solidity -ISablierV2Archive public immutable override archive; -``` - -## Functions - -### constructor - -```solidity -constructor(ISablierV2Archive archive_); -``` - -### getMethods - -```solidity -function getMethods() external pure returns (bytes4[] memory methods); -``` - -### onStreamCanceled - -Forwards the refunded assets to the proxy owner when the recipient cancels a stream whose sender is the proxy contract. - -Requirements: - -- Must be delegate called. -- The caller must be an address listed in the archive. - -```solidity -function onStreamCanceled(uint256 streamId, address, uint128 senderAmount, uint128) external onlyDelegateCall; -``` diff --git a/docs/contracts/v2/reference/periphery/contract.SablierV2ProxyTarget.md b/docs/contracts/v2/reference/periphery/contract.SablierV2ProxyTarget.md deleted file mode 100644 index 80eb45f4..00000000 --- a/docs/contracts/v2/reference/periphery/contract.SablierV2ProxyTarget.md +++ /dev/null @@ -1,790 +0,0 @@ ---- -sidebar_position: 3 ---- - -# SablierV2ProxyTarget - -[Git Source](https://github.com/sablier-labs/v2-periphery/blob/05c331e79e05886c7837dfda1bc21197c1c3c748/docs/contracts/v2/reference/periphery) - -**Inherits:** -[ISablierV2ProxyTarget](/docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2ProxyTarget.md), -[OnlyDelegateCall](/docs/contracts/v2/reference/periphery/abstracts/abstract.OnlyDelegateCall.md) - -See the documentation in -[ISablierV2ProxyTarget](/docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2ProxyTarget.md). - -## State Variables - -### PERMIT2 - -```solidity -IAllowanceTransfer internal immutable PERMIT2; -``` - -## Functions - -### constructor - -```solidity -constructor(IAllowanceTransfer permit2); -``` - -### batchCancelMultiple - -Cancels multiple streams across different lockup contracts. - -Notes: - -- All refunded assets are forwarded to the proxy owner. -- It is assumed that `assets` includes only the assets associated with the stream ids in `batch`. If any asset is - missing, the refunded amount will be left in the proxy. Requirements: -- Must be delegate called. -- There must be at least one element in `batch`. - -```solidity -function batchCancelMultiple( - Batch.CancelMultiple[] calldata batch, - IERC20[] calldata assets -) - external - onlyDelegateCall; -``` - -**Parameters** - -| Name | Type | Description | -| -------- | ------------------------ | -------------------------------------------------------------------------------------------------- | -| `batch` | `Batch.CancelMultiple[]` | An array of structs, each encapsulating the lockup contract's address and the stream id to cancel. | -| `assets` | `IERC20[]` | The contract addresses of the ERC-20 assets used for streaming. | - -### burn - -Mirror for {ISablierV2Lockup.burn}. - -_Must be delegate called._ - -```solidity -function burn(ISablierV2Lockup lockup, uint256 streamId) external onlyDelegateCall; -``` - -### cancel - -Mirror for {ISablierV2Lockup.cancel}. - -Notes: - -- All refunded assets are forwarded to the proxy owner. Requirements: -- Must be delegate called. - -```solidity -function cancel(ISablierV2Lockup lockup, uint256 streamId) public onlyDelegateCall; -``` - -### cancelMultiple - -Mirror for {ISablierV2Lockup.cancelMultiple}. - -Notes: - -- All refunded assets are forwarded to the proxy owner. -- It is assumed that `assets` includes only the assets associated with `streamIds`. If any asset is missing, the - refunded amount will be left in the proxy. Requirements: -- Must be delegate called. - -```solidity -function cancelMultiple( - ISablierV2Lockup lockup, - IERC20[] calldata assets, - uint256[] calldata streamIds -) - external - onlyDelegateCall; -``` - -**Parameters** - -| Name | Type | Description | -| ----------- | ------------------ | --------------------------------------------------------------- | -| `lockup` | `ISablierV2Lockup` | The address of the Lockup streaming contract. | -| `assets` | `IERC20[]` | The contract addresses of the ERC-20 assets used for streaming. | -| `streamIds` | `uint256[]` | The stream ids to cancel. | - -### renounce - -Mirror for {ISablierV2Lockup.renounce}. - -_Must be delegate called._ - -```solidity -function renounce(ISablierV2Lockup lockup, uint256 streamId) external onlyDelegateCall; -``` - -### withdraw - -Mirror for {ISablierV2Lockup.withdraw}. - -_Must be delegate called._ - -```solidity -function withdraw(ISablierV2Lockup lockup, uint256 streamId, address to, uint128 amount) external onlyDelegateCall; -``` - -### withdrawMax - -Mirror for {ISablierV2Lockup.withdrawMax}. - -_Must be delegate called._ - -```solidity -function withdrawMax(ISablierV2Lockup lockup, uint256 streamId, address to) external onlyDelegateCall; -``` - -### withdrawMaxAndTransfer - -Mirror for {ISablierV2Lockup.withdrawMaxAndTransfer}. - -_Must be delegate called._ - -```solidity -function withdrawMaxAndTransfer( - ISablierV2Lockup lockup, - uint256 streamId, - address newRecipient -) - external - onlyDelegateCall; -``` - -### batchCreateWithDurations - -Creates a batch of Lockup Linear streams using `createWithDurations`. Assets are transferred to the proxy via Permit2. - -Requirements: - -- Must be delegate called. -- There must be at least one element in `batch`. -- All requirements from {ISablierV2LockupLinear.createWithDurations} must be met for each stream. - -```solidity -function batchCreateWithDurations( - ISablierV2LockupLinear lockupLinear, - IERC20 asset, - Batch.CreateWithDurations[] calldata batch, - Permit2Params calldata permit2Params -) - external - override - onlyDelegateCall - returns (uint256[] memory streamIds); -``` - -**Parameters** - -| Name | Type | Description | -| --------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------ | -| `lockupLinear` | `ISablierV2LockupLinear` | The address of the [SablierV2LockupLinear](docs/contracts/v2/reference/core/contract.SablierV2LockupLinear.md) contract. | -| `asset` | `IERC20` | The contract address of the ERC-20 asset used for streaming. | -| `batch` | `Batch.CreateWithDurations[]` | An array of structs, each encapsulating a subset of the parameters of {SablierV2LockupLinear.createWithDurations}. | -| `permit2Params` | `Permit2Params` | A struct encapsulating the parameters needed for Permit2, most importantly the signature. | - -**Returns** - -| Name | Type | Description | -| ----------- | ----------- | ------------------------------------- | -| `streamIds` | `uint256[]` | The ids of the newly created streams. | - -### batchCreateWithRange - -Creates a batch of Lockup Linear streams using `createWithRange`. Assets are transferred to the proxy via Permit2. - -Requirements: - -- Must be delegate called. -- There must be at least one element in `batch`. -- All requirements from {ISablierV2LockupLinear.createWithRange} must be met for each stream. - -```solidity -function batchCreateWithRange( - ISablierV2LockupLinear lockupLinear, - IERC20 asset, - Batch.CreateWithRange[] calldata batch, - Permit2Params calldata permit2Params -) - external - override - onlyDelegateCall - returns (uint256[] memory streamIds); -``` - -**Parameters** - -| Name | Type | Description | -| --------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------ | -| `lockupLinear` | `ISablierV2LockupLinear` | The address of the [SablierV2LockupLinear](docs/contracts/v2/reference/core/contract.SablierV2LockupLinear.md) contract. | -| `asset` | `IERC20` | The contract address of the ERC-20 asset used for streaming. | -| `batch` | `Batch.CreateWithRange[]` | An array of structs, each encapsulating a subset of the parameters of {SablierV2LockupLinear.createWithRange}. | -| `permit2Params` | `Permit2Params` | A struct encapsulating the parameters needed for Permit2, most importantly the signature. | - -**Returns** - -| Name | Type | Description | -| ----------- | ----------- | ------------------------------------- | -| `streamIds` | `uint256[]` | The ids of the newly created streams. | - -### cancelAndCreateWithDurations - -Cancels a Lockup stream and creates a new Lockup Linear stream using `createWithDurations`. Assets are transferred to -the proxy via Permit2. - -Notes: - -- `streamId` can reference either a Lockup Linear or a Lockup Dynamic stream. -- See {ISablierV2Lockup.cancel} and {ISablierV2LockupLinear.createWithDurations} for full documentation. Requirements: -- Must be delegate called. - -```solidity -function cancelAndCreateWithDurations( - ISablierV2Lockup lockup, - uint256 streamId, - ISablierV2LockupLinear lockupLinear, - LockupLinear.CreateWithDurations calldata createParams, - Permit2Params calldata permit2Params -) - external - override - onlyDelegateCall - returns (uint256 newStreamId); -``` - -**Parameters** - -| Name | Type | Description | -| --------------- | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `lockup` | `ISablierV2Lockup` | The address of the Lockup streaming contract where the stream to cancel is. | -| `streamId` | `uint256` | The id of the stream to cancel. | -| `lockupLinear` | `ISablierV2LockupLinear` | The address of the [SablierV2LockupLinear](docs/contracts/v2/reference/core/contract.SablierV2LockupLinear.md) contract to use for creating the new stream. | -| `createParams` | `LockupLinear.CreateWithDurations` | | -| `permit2Params` | `Permit2Params` | A struct encapsulating the parameters needed for Permit2, most importantly the signature. | - -**Returns** - -| Name | Type | Description | -| ------------- | --------- | ----------------------------------- | -| `newStreamId` | `uint256` | The id of the newly created stream. | - -### cancelAndCreateWithRange - -Cancels a Lockup stream and creates a new Lockup Linear stream using `createWithRange`. Assets are transferred to the -proxy via Permit2. - -Notes: - -- `streamId` can reference either a Lockup Linear or a Lockup Dynamic stream. -- See {ISablierV2Lockup.cancel} and {ISablierV2LockupLinear.createWithRange} for full documentation. Requirements: -- Must be delegate called. - -```solidity -function cancelAndCreateWithRange( - ISablierV2Lockup lockup, - uint256 streamId, - ISablierV2LockupLinear lockupLinear, - LockupLinear.CreateWithRange calldata createParams, - Permit2Params calldata permit2Params -) - external - override - onlyDelegateCall - returns (uint256 newStreamId); -``` - -**Parameters** - -| Name | Type | Description | -| --------------- | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `lockup` | `ISablierV2Lockup` | The address of the Lockup streaming contract where the stream to cancel is. | -| `streamId` | `uint256` | The id of the stream to cancel. | -| `lockupLinear` | `ISablierV2LockupLinear` | The address of the [SablierV2LockupLinear](docs/contracts/v2/reference/core/contract.SablierV2LockupLinear.md) contract to use for creating the new stream. | -| `createParams` | `LockupLinear.CreateWithRange` | | -| `permit2Params` | `Permit2Params` | A struct encapsulating the parameters needed for Permit2, most importantly the signature. | - -**Returns** - -| Name | Type | Description | -| ------------- | --------- | ----------------------------------- | -| `newStreamId` | `uint256` | The id of the newly created stream. | - -### createWithDurations - -Mirror for {SablierV2LockupLinear.createWithDurations}. Assets are transferred to the proxy via Permit2. - -_Must be delegate called._ - -```solidity -function createWithDurations( - ISablierV2LockupLinear lockupLinear, - LockupLinear.CreateWithDurations calldata createParams, - Permit2Params calldata permit2Params -) - public - override - onlyDelegateCall - returns (uint256 streamId); -``` - -**Parameters** - -| Name | Type | Description | -| --------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | -| `lockupLinear` | `ISablierV2LockupLinear` | The address of the [SablierV2LockupLinear](docs/contracts/v2/reference/core/contract.SablierV2LockupLinear.md) contract. | -| `createParams` | `LockupLinear.CreateWithDurations` | Struct encapsulating the function parameters, which are documented in V2 Core. | -| `permit2Params` | `Permit2Params` | A struct encapsulating the parameters needed for Permit2, most importantly the signature. | - -**Returns** - -| Name | Type | Description | -| ---------- | --------- | ----------------------------------- | -| `streamId` | `uint256` | The id of the newly created stream. | - -### createWithRange - -Mirror for {SablierV2LockupLinear.createWithRange}. Assets are transferred to the proxy via Permit2. - -_Must be delegate called._ - -```solidity -function createWithRange( - ISablierV2LockupLinear lockupLinear, - LockupLinear.CreateWithRange calldata createParams, - Permit2Params calldata permit2Params -) - public - override - onlyDelegateCall - returns (uint256 streamId); -``` - -**Parameters** - -| Name | Type | Description | -| --------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------ | -| `lockupLinear` | `ISablierV2LockupLinear` | The address of the [SablierV2LockupLinear](docs/contracts/v2/reference/core/contract.SablierV2LockupLinear.md) contract. | -| `createParams` | `LockupLinear.CreateWithRange` | Struct encapsulating the function parameters, which are documented in V2 Core. | -| `permit2Params` | `Permit2Params` | A struct encapsulating the parameters needed for Permit2, most importantly the signature. | - -**Returns** - -| Name | Type | Description | -| ---------- | --------- | ----------------------------------- | -| `streamId` | `uint256` | The id of the newly created stream. | - -### wrapAndCreateWithDurations - -Wraps the native asset payment in ERC-20 form and creates a Lockup Linear stream using `createWithDurations`. - -Notes: - -- `createParams.totalAmount` is overwritten with `msg.value`. -- See {ISablierV2LockupLinear.createWithDurations} for full documentation. Requirements: -- Must be delegate called. -- The ERC-20 amount credited by the wrapper contract must be equal to `msg.value`. - -```solidity -function wrapAndCreateWithDurations( - ISablierV2LockupLinear lockupLinear, - LockupLinear.CreateWithDurations memory createParams -) - external - payable - override - onlyDelegateCall - returns (uint256 streamId); -``` - -**Parameters** - -| Name | Type | Description | -| -------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | -| `lockupLinear` | `ISablierV2LockupLinear` | The address of the [SablierV2LockupLinear](docs/contracts/v2/reference/core/contract.SablierV2LockupLinear.md) contract. | -| `createParams` | `LockupLinear.CreateWithDurations` | Struct encapsulating the function parameters, which are documented in V2 Core. | - -### wrapAndCreateWithRange - -Wraps the native asset payment in ERC-20 form and creates a Lockup Linear stream using `createWithRange`. - -Notes: - -- `createParams.totalAmount` is overwritten with `msg.value`. -- See {ISablierV2LockupLinear.createWithRange} for full documentation. Requirements: -- Must be delegate called. -- The ERC-20 amount credited by the wrapper contract must be equal to `msg.value`. - -```solidity -function wrapAndCreateWithRange( - ISablierV2LockupLinear lockupLinear, - LockupLinear.CreateWithRange memory createParams -) - external - payable - override - onlyDelegateCall - returns (uint256 streamId); -``` - -**Parameters** - -| Name | Type | Description | -| -------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------ | -| `lockupLinear` | `ISablierV2LockupLinear` | The address of the [SablierV2LockupLinear](docs/contracts/v2/reference/core/contract.SablierV2LockupLinear.md) contract. | -| `createParams` | `LockupLinear.CreateWithRange` | Struct encapsulating the function parameters, which are documented in V2 Core. | - -### batchCreateWithDeltas - -Creates a batch of Lockup Dynamic streams using `createWithDeltas`. Assets are transferred to the proxy via Permit2. - -Requirements: - -- Must be delegate called. -- There must be at least one element in `batch`. -- All requirements from {ISablierV2LockupDynamic.createWithDeltas} must be met for each stream. - -```solidity -function batchCreateWithDeltas( - ISablierV2LockupDynamic dynamic, - IERC20 asset, - Batch.CreateWithDeltas[] calldata batch, - Permit2Params calldata permit2Params -) - external - override - onlyDelegateCall - returns (uint256[] memory streamIds); -``` - -**Parameters** - -| Name | Type | Description | -| --------------- | -------------------------- | ---------------------------------------------------------------------------------------------------------------- | -| `dynamic` | `ISablierV2LockupDynamic` | | -| `asset` | `IERC20` | The contract address of the ERC-20 asset used for streaming. | -| `batch` | `Batch.CreateWithDeltas[]` | An array of structs, each encapsulating a subset of the parameters of {SablierV2LockupDynamic.createWithDeltas}. | -| `permit2Params` | `Permit2Params` | A struct encapsulating the parameters needed for Permit2, most importantly the signature. | - -**Returns** - -| Name | Type | Description | -| ----------- | ----------- | ------------------------------------- | -| `streamIds` | `uint256[]` | The ids of the newly created streams. | - -### batchCreateWithMilestones - -Creates a batch of Lockup Dynamic streams using `createWithMilestones`. Assets are transferred to the proxy via Permit2. - -Requirements: - -- Must be delegate called. -- There must be at least one element in `batch`. -- All requirements from {ISablierV2LockupDynamic.createWithMilestones} must be met for each stream. - -```solidity -function batchCreateWithMilestones( - ISablierV2LockupDynamic dynamic, - IERC20 asset, - Batch.CreateWithMilestones[] calldata batch, - Permit2Params calldata permit2Params -) - external - override - onlyDelegateCall - returns (uint256[] memory streamIds); -``` - -**Parameters** - -| Name | Type | Description | -| --------------- | ------------------------------ | -------------------------------------------------------------------------------------------------------------------- | -| `dynamic` | `ISablierV2LockupDynamic` | | -| `asset` | `IERC20` | The contract address of the ERC-20 asset used for streaming. | -| `batch` | `Batch.CreateWithMilestones[]` | An array of structs, each encapsulating a subset of the parameters of {SablierV2LockupDynamic.createWithMilestones}. | -| `permit2Params` | `Permit2Params` | A struct encapsulating the parameters needed for Permit2, most importantly the signature. | - -**Returns** - -| Name | Type | Description | -| ----------- | ----------- | ------------------------------------- | -| `streamIds` | `uint256[]` | The ids of the newly created streams. | - -### cancelAndCreateWithDeltas - -Cancels a Lockup stream and creates a new Lockup Dynamic stream using `createWithDeltas`. Assets are transferred to the -proxy via Permit2. - -Notes: - -- `streamId` can reference either a Lockup Linear or a Lockup Dynamic stream. -- See {ISablierV2Lockup.cancel} and {ISablierV2LockupDynamic.createWithDeltas} for full documentation. Requirements: -- Must be delegate called. - -```solidity -function cancelAndCreateWithDeltas( - ISablierV2Lockup lockup, - uint256 streamId, - ISablierV2LockupDynamic dynamic, - LockupDynamic.CreateWithDeltas calldata createParams, - Permit2Params calldata permit2Params -) - external - override - onlyDelegateCall - returns (uint256 newStreamId); -``` - -**Parameters** - -| Name | Type | Description | -| --------------- | -------------------------------- | ----------------------------------------------------------------------------------------- | -| `lockup` | `ISablierV2Lockup` | The address of the Lockup streaming contract where the stream to cancel is. | -| `streamId` | `uint256` | The id of the stream to cancel. | -| `dynamic` | `ISablierV2LockupDynamic` | | -| `createParams` | `LockupDynamic.CreateWithDeltas` | A struct encapsulating the create function parameters, which are documented in V2 Core. | -| `permit2Params` | `Permit2Params` | A struct encapsulating the parameters needed for Permit2, most importantly the signature. | - -**Returns** - -| Name | Type | Description | -| ------------- | --------- | ----------------------------------- | -| `newStreamId` | `uint256` | The id of the newly created stream. | - -### cancelAndCreateWithMilestones - -Cancels a Lockup stream and creates a new Lockup Dynamic stream using `createWithMilestones`. Assets are transferred to -the proxy via Permit2. - -Notes: - -- `streamId` can reference either a Lockup Linear or a Lockup Dynamic stream. -- See {ISablierV2Lockup.cancel} and {ISablierV2LockupDynamic.createWithMilestones} for full documentation. Requirements: -- Must be delegate called. - -```solidity -function cancelAndCreateWithMilestones( - ISablierV2Lockup lockup, - uint256 streamId, - ISablierV2LockupDynamic dynamic, - LockupDynamic.CreateWithMilestones calldata createParams, - Permit2Params calldata permit2Params -) - external - override - onlyDelegateCall - returns (uint256 newStreamId); -``` - -**Parameters** - -| Name | Type | Description | -| --------------- | ------------------------------------ | ----------------------------------------------------------------------------------------- | -| `lockup` | `ISablierV2Lockup` | The address of the Lockup streaming contract where the stream to cancel is. | -| `streamId` | `uint256` | The id of the stream to cancel. | -| `dynamic` | `ISablierV2LockupDynamic` | | -| `createParams` | `LockupDynamic.CreateWithMilestones` | A struct encapsulating the create function parameters, which are documented in V2 Core. | -| `permit2Params` | `Permit2Params` | A struct encapsulating the parameters needed for Permit2, most importantly the signature. | - -**Returns** - -| Name | Type | Description | -| ------------- | --------- | ----------------------------------- | -| `newStreamId` | `uint256` | The id of the newly created stream. | - -### createWithDeltas - -Mirror for {SablierV2LockupDynamic.createWithDeltas}. Assets are transferred to the proxy via Permit2. - -_Must be delegate called._ - -```solidity -function createWithDeltas( - ISablierV2LockupDynamic dynamic, - LockupDynamic.CreateWithDeltas calldata createParams, - Permit2Params calldata permit2Params -) - public - override - onlyDelegateCall - returns (uint256 streamId); -``` - -**Parameters** - -| Name | Type | Description | -| --------------- | -------------------------------- | ----------------------------------------------------------------------------------------- | -| `dynamic` | `ISablierV2LockupDynamic` | | -| `createParams` | `LockupDynamic.CreateWithDeltas` | A struct encapsulating the create function parameters, which are documented in V2 Core. | -| `permit2Params` | `Permit2Params` | A struct encapsulating the parameters needed for Permit2, most importantly the signature. | - -**Returns** - -| Name | Type | Description | -| ---------- | --------- | ----------------------------------- | -| `streamId` | `uint256` | The id of the newly created stream. | - -### createWithMilestones - -Mirror for {SablierV2LockupDynamic.createWithMilestones}. Assets are transferred to the proxy via Permit2. - -_Must be delegate called._ - -```solidity -function createWithMilestones( - ISablierV2LockupDynamic dynamic, - LockupDynamic.CreateWithMilestones calldata createParams, - Permit2Params calldata permit2Params -) - public - override - onlyDelegateCall - returns (uint256 streamId); -``` - -**Parameters** - -| Name | Type | Description | -| --------------- | ------------------------------------ | ----------------------------------------------------------------------------------------- | -| `dynamic` | `ISablierV2LockupDynamic` | | -| `createParams` | `LockupDynamic.CreateWithMilestones` | Struct encapsulating the function parameters, which are documented in V2 Core. | -| `permit2Params` | `Permit2Params` | A struct encapsulating the parameters needed for Permit2, most importantly the signature. | - -**Returns** - -| Name | Type | Description | -| ---------- | --------- | ----------------------------------- | -| `streamId` | `uint256` | The id of the newly created stream. | - -### wrapAndCreateWithDeltas - -Wraps the native asset payment in ERC-20 form and creates a Lockup Dynamic stream using `createWithDeltas`. - -Notes: - -- `createParams.totalAmount` is overwritten with `msg.value`. -- See {ISablierV2LockupDynamic.createWithDeltas} for full documentation. Requirements: -- Must be delegate called. -- The ERC-20 amount credited by the wrapper contract must be equal to `msg.value`. - -```solidity -function wrapAndCreateWithDeltas( - ISablierV2LockupDynamic dynamic, - LockupDynamic.CreateWithDeltas memory createParams -) - external - payable - override - onlyDelegateCall - returns (uint256 streamId); -``` - -**Parameters** - -| Name | Type | Description | -| -------------- | -------------------------------- | ------------------------------------------------------------------------------ | -| `dynamic` | `ISablierV2LockupDynamic` | | -| `createParams` | `LockupDynamic.CreateWithDeltas` | Struct encapsulating the function parameters, which are documented in V2 Core. | - -**Returns** - -| Name | Type | Description | -| ---------- | --------- | ----------------------------------- | -| `streamId` | `uint256` | The id of the newly created stream. | - -### wrapAndCreateWithMilestones - -Wraps the native asset payment in ERC-20 form and creates a Lockup Dynamic stream using `createWithMilestones`. - -Notes: - -- `createParams.totalAmount` is overwritten with `msg.value`. -- See {ISablierV2LockupDynamic.createWithMilestones} for full documentation. Requirements: -- Must be delegate called. -- The ERC-20 amount credited by the wrapper contract must be equal to `msg.value`. - -```solidity -function wrapAndCreateWithMilestones( - ISablierV2LockupDynamic dynamic, - LockupDynamic.CreateWithMilestones memory createParams -) - external - payable - override - onlyDelegateCall - returns (uint256 streamId); -``` - -**Parameters** - -| Name | Type | Description | -| -------------- | ------------------------------------ | ------------------------------------------------------------------------------ | -| `dynamic` | `ISablierV2LockupDynamic` | | -| `createParams` | `LockupDynamic.CreateWithMilestones` | Struct encapsulating the function parameters, which are documented in V2 Core. | - -**Returns** - -| Name | Type | Description | -| ---------- | --------- | ----------------------------------- | -| `streamId` | `uint256` | The id of the newly created stream. | - -### \_approve - -_Helper function to approve a Sablier contract to spend funds from the proxy. If the current allowance is insufficient, -this function approves Sablier to spend the exact `amount`. The {SafeERC20.forceApprove} function is used to handle -special ERC-20 assets (e.g. USDT) that require the current allowance to be zero before setting it to a non-zero value._ - -```solidity -function _approve(address sablierContract, IERC20 asset, uint256 amount) internal; -``` - -### \_getBalances - -_Helper function to retrieve the proxy's balance for the provided assets._ - -```solidity -function _getBalances(IERC20[] calldata assets) internal view returns (uint256[] memory initialBalances); -``` - -### \_getOwner - -_Helper function to retrieve the proxy's owner, which is stored as an immutable variable in the proxy._ - -```solidity -function _getOwner() internal view returns (address); -``` - -### \_postMultipleCancellations - -_Shared logic between {cancelMultiple} and {batchCancelMultiple}._ - -```solidity -function _postMultipleCancellations(uint256[] memory initialBalances, IERC20[] calldata assets) internal; -``` - -### \_safeWrap - -_Safely wraps the native asset payment in ERC-20 form, checking that the credit amount is greater than or equal to -`msg.value`._ - -```solidity -function _safeWrap(IERC20 asset) internal; -``` - -### \_transferAndApprove - -_Helper function to transfer funds from the proxy owner to the proxy using Permit2 and, if needed, approve the Sablier -contract to spend funds from the proxy._ - -```solidity -function _transferAndApprove( - address sablierContract, - IERC20 asset, - uint160 amount, - Permit2Params calldata permit2Params -) - internal; -``` diff --git a/docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2Archive.md b/docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2Archive.md deleted file mode 100644 index 5d6675d3..00000000 --- a/docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2Archive.md +++ /dev/null @@ -1,73 +0,0 @@ -# ISablierV2Archive - -[Git Source](https://github.com/sablier-labs/v2-periphery/blob/05c331e79e05886c7837dfda1bc21197c1c3c748/docs/contracts/v2/reference/periphery/interfaces) - -**Inherits:** IAdminable - -An on-chain contract registry that keeps a record of all Sablier V2 contracts, including old deployments. - -## Functions - -### isListed - -A boolean flag that indicates whether the provided address is part of the archive. - -```solidity -function isListed(address addr) external returns (bool result); -``` - -### list - -Lists an address in the archive. - -Emits a {List} event. Notes: - -- It is not an error to list an address that is already listed. Requirements: -- The caller must be the admin. - -```solidity -function list(address addr) external; -``` - -**Parameters** - -| Name | Type | Description | -| ------ | --------- | --------------------------------------------------------------- | -| `addr` | `address` | The address to list in the archive, which should be a contract. | - -### unlist - -Unlists an address from the archive. - -Emits an {Unlist} event. Notes: - -- It is not an error to unlist an address that is not already listed. Requirements: -- The caller must be the admin. - -```solidity -function unlist(address addr) external; -``` - -**Parameters** - -| Name | Type | Description | -| ------ | --------- | ---------------------------------------------------------------------------- | -| `addr` | `address` | The address to unlist from the archive, which is usually a contract address. | - -## Events - -### List - -Emitted when an address is listed in the archive. - -```solidity -event List(address indexed admin, address indexed addr); -``` - -### Unlist - -Emitted when an address is unlisted from the archive. - -```solidity -event Unlist(address indexed admin, address indexed addr); -``` diff --git a/docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2Batch.md b/docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2Batch.md new file mode 100644 index 00000000..d5352724 --- /dev/null +++ b/docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2Batch.md @@ -0,0 +1,125 @@ +# ISablierV2Batch + +[Git Source](https://github.com/sablier-labs/v2-periphery/blob/release/src/interfaces/ISablierV2Batch.sol) + +**Inherits:** [IAdminable](/docs/contracts/v2/reference/core/interfaces/interface.IAdminable.md) + +_Helper to batch create Sablier V2 Lockup streams._ + +## Functions + +### createWithDurations + +Creates a batch of Lockup Linear streams using `createWithDurations`. + +Requirements: + +- There must be at least one element in `batch`. +- All requirements from + [ISablierV2LockupLinear.createWithDurations](/contracts/v2/reference/core/interfaces/interface.ISablierV2LockupLinear#createwithdurations) + must be met for each stream. + +```solidity +function createWithDurations( + ISablierV2LockupLinear lockupLinear, + IERC20 asset, + Batch.CreateWithDurations[] calldata batch +) + external + returns (uint256[] memory streamIds); +``` + +**Parameters** + +| Name | Type | Description | +| -------------- | ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `lockupLinear` | `ISablierV2LockupLinear` | The address of the {SablierV2LockupLinear} contract. | +| `asset` | `IERC20` | The address of the `ERC-20` asset. | +| `batch` | `Batch.CreateWithDurations[]` | An array of structs, each encapsulating a subset of the parameters of [SablierV2LockupLinear.createWithDurations](/contracts/v2/reference/core/types/library.LockupLinear#createwithdurations). | + +### createWithRange + +Creates a batch of Lockup Linear streams using `createWithRange`. + +Requirements: + +- There must be at least one element in `batch`. +- All requirements from + [ISablierV2LockupLinear.createWithRange](/contracts/v2/reference/core/interfaces/interface.ISablierV2LockupLinear#createwithrange) + must be met for each stream. + +```solidity +function createWithRange( + ISablierV2LockupLinear lockupLinear, + IERC20 asset, + Batch.createWithRange[] calldata batch +) + external + returns (uint256[] memory streamIds); +``` + +**Parameters** + +| Name | Type | Description | +| -------------- | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `lockupLinear` | `ISablierV2LockupLinear` | The address of the {SablierV2LockupLinear} contract. | +| `asset` | `IERC20` | The address of the `ERC-20` asset. | +| `batch` | `Batch.CreateWithRange[]` | An array of structs, each encapsulating a subset of the parameters of [SablierV2LockupLinear.createWithRange](/contracts/v2/reference/core/types/library.LockupLinear#createwithrange). | + +### createWithDeltas + +Creates a batch of Lockup Dynamic streams using `createWithDeltas`. + +Requirements: + +- There must be at least one element in `batch`. +- All requirements from + [ISablierV2LockupDynamic.createWithDeltas](/contracts/v2/reference/core/interfaces/interface.ISablierV2LockupDynamic#createwithdeltas) + must be met for each stream. + +```solidity +function createWithDeltas( + ISablierV2LockupDynamic lockupDynamic, + IERC20 asset, + Batch.CreateWithDeltas[] calldata batch +) + external + returns (uint256[] memory streamIds); +``` + +**Parameters** + +| Name | Type | Description | +| --------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `lockupDynamic` | `ISablierV2LockupDynamic` | The address of the {SablierV2LockupDynamic} contract. | +| `asset` | `IERC20` | The address of the `ERC-20` asset. | +| `batch` | `Batch.CreateWithDeltas[]` | An array of structs, each encapsulating a subset of the parameters of [SablierV2LockupDynamic.createWithDeltas](/contracts/v2/reference/core/types/library.LockupDynamic#createwithdeltas). | + +### createWithMilestones + +Creates a batch of Lockup Dynamic streams using `createWithMilestones`. + +Requirements: + +- There must be at least one element in `batch`. +- All requirements from + [ISablierV2LockupDynamic.createWithMilestones](/contracts/v2/reference/core/interfaces/interface.ISablierV2LockupDynamic#createwithmilestones) + must be met for each stream. + +```solidity +function createWithMilestones( + ISablierV2LockupDynamic lockupDynamic, + IERC20 asset, + Batch.CreateWithMilestones[] calldata batch +) + external + returns (uint256[] memory streamIds); +``` + +**Parameters** + +| Name | Type | Description | +| --------------- | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `lockupDynamic` | `ISablierV2LockupDynamic` | The address of the {SablierV2LockupDynamic} contract. | +| `asset` | `IERC20` | The address of the `ERC-20` asset. | +| `batch` | `Batch.CreateWithMilestones[]` | An array of structs, each encapsulating a subset of the parameters of [SablierV2LockupDynamic.createWithMilestones](/contracts/v2/reference/core/types/library.LockupDynamic#createwithmilestones). | diff --git a/docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2MerkleStreamer.md b/docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2MerkleStreamer.md new file mode 100644 index 00000000..2450cac2 --- /dev/null +++ b/docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2MerkleStreamer.md @@ -0,0 +1,118 @@ +# ISablierV2MerkleStreamer + +[Git Source](https://github.com/sablier-labs/v2-periphery/blob/release/src/interfaces/ISablierV2MerkleStreamer.sol) + +**Inherits:** [IAdminable](/docs/contracts/v2/reference/core/interfaces/interface.IAdminable.md) + +_A contract that lets user claim Sablier streams using Merkle proofs. An interesting use case for MerkleStream is +airstreams, which is a portmanteau of "airdrop" and "stream". This is an airdrop model where the tokens are distributed +over time, as opposed to all at once. This is the base interface for MerkleStreamer contracts._ + +## Functions + +### ASSET + +The streamed ERC-20 asset. + +```solidity +function ASSET() external returns (IERC20); +``` + +### CANCELABLE + +A flag indicating whether the streams can be canceled. + +```solidity +function CANCELABLE() external returns (bool); +``` + +### EXPIRATION + +The cut-off point for the Merkle streamer, as a Unix timestamp. A value of zero means there is no expiration. + +```solidity +function EXPIRATION() external returns (uint40); +``` + +### LOCKUP + +_The address of the {SablierV2Lockup} contract._ + +```solidity +function LOCKUP() external returns (ISablierV2Lockup); +``` + +### hasClaimed + +Returns a flag indicating whether a claim has been made for a given index. + +```solidity +function hasClaimed(uint256 index) external returns (bool); +``` + +**Parameters** + +| Name | Type | Description | +| ------- | --------- | ------------------------------------ | +| `index` | `uint256` | The index of the recipient to check. | + +### hasExpired + +Returns a flag indicating whether the Merkle streamer has expired. + +```solidity +function hasExpired() external view returns (bool); +``` + +### MERKLE_ROOT + +The root of the Merkle tree used to validate the claims. + +```solidity +function MERKLE_ROOT() external returns (bytes32); +``` + +### TRANSFERABLE + +A flag indicating whether the stream NFTs are transferable. + +```solidity +function TRANSFERABLE() external returns (bool); +``` + +### clawback + +Claws back the unclaimed tokens from the Merkle streamer. + +Emits a {Clawback} event. Requirements: + +- `msg.sender` must be the contract admin. +- The Merkle streamer must have expired. + +```solidity +function clawback(address to, uint128 amount) external; +``` + +| Name | Type | Description | +| -------- | --------- | ---------------------------------- | +| `to` | `address` | The address to receive the tokens. | +| `amount` | `uint128` | The amount of tokens to claw back. | + +## Events + +### Claim + +Emitted when a recipient claims a stream. + +```solidity +event Claim(uint256 index, address indexed recipient, uint128 amount, uint256 +indexed streamId); +``` + +### Clawback + +Emitted when the admin claws back the unclaimed tokens. + +```solidity +event Clawback(address indexed admin, address indexed to, uint128 amount); +``` diff --git a/docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2MerkleStreamerFactory.md b/docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2MerkleStreamerFactory.md new file mode 100644 index 00000000..629f046b --- /dev/null +++ b/docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2MerkleStreamerFactory.md @@ -0,0 +1,70 @@ +# ISablierV2MerkleStreamerFactory + +[Git Source](https://github.com/sablier-labs/v2-periphery/blob/release/src/interfaces/ISablierV2MerkleStreamerFactory.sol) + +_Deploys new Lockup Linear Merkle streamers via CREATE2._ + +## Functions + +### createMerkleStreamerLL + +Creates a new Merkle streamer that uses Lockup Linear. + +Emits a {CreateMerkleStreamerLL} event. + +```solidity +function createMerkleStreamerLL( + address initialAdmin, + ISablierV2LockupLinear lockupLinear, + IERC20 asset, + bytes32 merkleRoot, + uint40 expiration, + LockupLinear.Durations memory streamDurations, + bool cancelable, + bool transferable, + string memory ipfsCID, + uint256 aggregateAmount, + uint256 recipientsCount + ) + external + returns (ISablierV2MerkleStreamerLL merkleStreamerLL); +``` + +**Parameters** + +| Name | Type | Description | +| ----------------- | ------------------------ | --------------------------------------------------------------- | +| `initialAdmin` | `address` | The initial admin of the Merkle streamer contract. | +| `lockupLinear` | `ISablierV2LockupLinear` | The address of the {SablierV2LockupLinear} contract. | +| `asset` | `IERC20` | The address of the `ERC-20` asset. | +| `merkleRoot` | `bytes32` | The Merkle root of the claim data. | +| `expiration` | `uint40` | The expiration of the streaming campaign, as a Unix timestamp. | +| `streamDurations` | `LockupLinear.Durations` | The durations for each stream due to the recipient. | +| `cancelable` | `bool` | Indicates if each stream will be cancelable. | +| `transferable` | `bool` | Indicates if each stream NFT will be transferable. | +| `ipfsCID` | `string` | Metadata parameter emitted for indexing purposes. | +| `aggregateAmount` | `uint256` | Total amount of ERC-20 assets to be streamed to all recipients. | +| `recipientsCount` | `uint256` | Total number of recipients eligible to claim. | + +## Events + +### CreateMerkleStreamerLL + +Emitted when a Sablier V2 Lockup Linear Merkle streamer is created. + +```solidity +event CreateMerkleStreamerLL( + ISablierV2MerkleStreamerLL merkleStreamer, + address indexed admin, + ISablierV2LockupLinear indexed lockupLinear, + IERC20 indexed asset, + bytes32 merkleRoot, + uint40 expiration, + LockupLinear.Durations streamDurations, + bool cancelable, + bool transferable, + string ipfsCID, + uint256 aggregateAmount, + uint256 recipientsCount +); +``` diff --git a/docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2MerkleStreamerLL.md b/docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2MerkleStreamerLL.md new file mode 100644 index 00000000..239c00bb --- /dev/null +++ b/docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2MerkleStreamerLL.md @@ -0,0 +1,58 @@ +# ISablierV2MerkleStreamerLL + +[Git Source](https://github.com/sablier-labs/v2-periphery/blob/release/src/interfaces/ISablierV2MerkleStreamerLL.sol) + +**Inherits:** +[ISablierV2MerkleStreamer](/contracts/v2/reference/periphery/interfaces/interface.ISablierV2MerkleStreamer) + +_See the documentation in +[ISablierV2MerkleStreamer](/contracts/v2/reference/periphery/interfaces/interface.ISablierV2MerkleStreamer)._ + +_Merkle streamer that creates Lockup Linear streams._ + +## Functions + +### LOCKUP_LINEAR + +The address of the {SablierV2LockupLinear} contract. + +```solidity +function LOCKUP_LINEAR() external returns (ISablierV2LockupLinear); +``` + +### streamDurations + +The total streaming duration of each stream, after claim. + +```solidity +function streamDurations() external returns (uint40 cliff, uint40 duration); +``` + +### claim + +Makes the claim by creating a Lockup Linear stream to the recipient. + +Emits a {Claim} event. Requirements: + +- The protocol fee must be zero. +- The campaign must not have expired. +- The stream must not have been claimed already. +- The Merkle proof must be valid. + +```solidity +function claim( + uint256 index, + address recipient, + uint128 amount, + bytes32[] calldata merkleProof +) + external + returns (uint256 streamId); +``` + +| Name | Type | Description | +| --------- | ------------- | ---------------------------------------------- | +| `index` | `uint256` | The index of the recipient in the Merkle tree. | +| `address` | `recipient` | The address of the stream holder. | +| `amount` | `uint128` | The amount of tokens to be streamed. | +| `bytes32` | `merkleProof` | The Merkle proof of inclusion in the stream. | diff --git a/docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2ProxyPlugin.md b/docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2ProxyPlugin.md deleted file mode 100644 index 9c209005..00000000 --- a/docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2ProxyPlugin.md +++ /dev/null @@ -1,18 +0,0 @@ -# ISablierV2ProxyPlugin - -[Git Source](https://github.com/sablier-labs/v2-periphery/blob/05c331e79e05886c7837dfda1bc21197c1c3c748/docs/contracts/v2/reference/periphery/interfaces) - -**Inherits:** ISablierV2LockupSender, IPRBProxyPlugin - -Proxy plugin that forwards the refunded assets to the proxy owner when the recipient cancels a stream whose sender is -the proxy contract. The plugin works by implementing the hook interface defined in V2 Core. - -## Functions - -### archive - -Retrieves the address of the archive contract. - -```solidity -function archive() external view returns (ISablierV2Archive); -``` diff --git a/docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2ProxyTarget.md b/docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2ProxyTarget.md deleted file mode 100644 index d5dee888..00000000 --- a/docs/contracts/v2/reference/periphery/interfaces/interface.ISablierV2ProxyTarget.md +++ /dev/null @@ -1,662 +0,0 @@ -# ISablierV2ProxyTarget - -[Git Source](https://github.com/sablier-labs/v2-periphery/blob/05c331e79e05886c7837dfda1bc21197c1c3c748/docs/contracts/v2/reference/periphery/interfaces) - -Proxy target with stateless scripts for interacting with Sablier V2, designed to be used by stream senders. - -_Intended for use with an instance of PRBProxy through delegate calls. Any standard calls will be reverted._ - -## Functions - -### batchCancelMultiple - -Cancels multiple streams across different lockup contracts. - -Notes: - -- All refunded assets are forwarded to the proxy owner. -- It is assumed that `assets` includes only the assets associated with the stream ids in `batch`. If any asset is - missing, the refunded amount will be left in the proxy. Requirements: -- Must be delegate called. -- There must be at least one element in `batch`. - -```solidity -function batchCancelMultiple(Batch.CancelMultiple[] calldata batch, IERC20[] calldata assets) external; -``` - -**Parameters** - -| Name | Type | Description | -| -------- | ------------------------ | -------------------------------------------------------------------------------------------------- | -| `batch` | `Batch.CancelMultiple[]` | An array of structs, each encapsulating the lockup contract's address and the stream id to cancel. | -| `assets` | `IERC20[]` | The contract addresses of the ERC-20 assets used for streaming. | - -### burn - -Mirror for {ISablierV2Lockup.burn}. - -_Must be delegate called._ - -```solidity -function burn(ISablierV2Lockup lockup, uint256 streamId) external; -``` - -### cancel - -Mirror for {ISablierV2Lockup.cancel}. - -Notes: - -- All refunded assets are forwarded to the proxy owner. Requirements: -- Must be delegate called. - -```solidity -function cancel(ISablierV2Lockup lockup, uint256 streamId) external; -``` - -### cancelMultiple - -Mirror for {ISablierV2Lockup.cancelMultiple}. - -Notes: - -- All refunded assets are forwarded to the proxy owner. -- It is assumed that `assets` includes only the assets associated with `streamIds`. If any asset is missing, the - refunded amount will be left in the proxy. Requirements: -- Must be delegate called. - -```solidity -function cancelMultiple(ISablierV2Lockup lockup, IERC20[] calldata assets, uint256[] calldata streamIds) external; -``` - -**Parameters** - -| Name | Type | Description | -| ----------- | ------------------ | --------------------------------------------------------------- | -| `lockup` | `ISablierV2Lockup` | The address of the Lockup streaming contract. | -| `assets` | `IERC20[]` | The contract addresses of the ERC-20 assets used for streaming. | -| `streamIds` | `uint256[]` | The stream ids to cancel. | - -### renounce - -Mirror for {ISablierV2Lockup.renounce}. - -_Must be delegate called._ - -```solidity -function renounce(ISablierV2Lockup lockup, uint256 streamId) external; -``` - -### withdraw - -Mirror for {ISablierV2Lockup.withdraw}. - -_Must be delegate called._ - -```solidity -function withdraw(ISablierV2Lockup lockup, uint256 streamId, address to, uint128 amount) external; -``` - -### withdrawMax - -Mirror for {ISablierV2Lockup.withdrawMax}. - -_Must be delegate called._ - -```solidity -function withdrawMax(ISablierV2Lockup lockup, uint256 streamId, address to) external; -``` - -### withdrawMaxAndTransfer - -Mirror for {ISablierV2Lockup.withdrawMaxAndTransfer}. - -_Must be delegate called._ - -```solidity -function withdrawMaxAndTransfer(ISablierV2Lockup lockup, uint256 streamId, address newRecipient) external; -``` - -### batchCreateWithDurations - -Creates a batch of Lockup Linear streams using `createWithDurations`. Assets are transferred to the proxy via Permit2. - -Requirements: - -- Must be delegate called. -- There must be at least one element in `batch`. -- All requirements from {ISablierV2LockupLinear.createWithDurations} must be met for each stream. - -```solidity -function batchCreateWithDurations( - ISablierV2LockupLinear lockupLinear, - IERC20 asset, - Batch.CreateWithDurations[] calldata batch, - Permit2Params calldata permit2Params -) - external - returns (uint256[] memory streamIds); -``` - -**Parameters** - -| Name | Type | Description | -| --------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------ | -| `lockupLinear` | `ISablierV2LockupLinear` | The address of the [SablierV2LockupLinear](docs/contracts/v2/reference/core/contract.SablierV2LockupLinear.md) contract. | -| `asset` | `IERC20` | The contract address of the ERC-20 asset used for streaming. | -| `batch` | `Batch.CreateWithDurations[]` | An array of structs, each encapsulating a subset of the parameters of {SablierV2LockupLinear.createWithDurations}. | -| `permit2Params` | `Permit2Params` | A struct encapsulating the parameters needed for Permit2, most importantly the signature. | - -**Returns** - -| Name | Type | Description | -| ----------- | ----------- | ------------------------------------- | -| `streamIds` | `uint256[]` | The ids of the newly created streams. | - -### batchCreateWithRange - -Creates a batch of Lockup Linear streams using `createWithRange`. Assets are transferred to the proxy via Permit2. - -Requirements: - -- Must be delegate called. -- There must be at least one element in `batch`. -- All requirements from {ISablierV2LockupLinear.createWithRange} must be met for each stream. - -```solidity -function batchCreateWithRange( - ISablierV2LockupLinear lockupLinear, - IERC20 asset, - Batch.CreateWithRange[] calldata batch, - Permit2Params calldata permit2Params -) - external - returns (uint256[] memory streamIds); -``` - -**Parameters** - -| Name | Type | Description | -| --------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------ | -| `lockupLinear` | `ISablierV2LockupLinear` | The address of the [SablierV2LockupLinear](docs/contracts/v2/reference/core/contract.SablierV2LockupLinear.md) contract. | -| `asset` | `IERC20` | The contract address of the ERC-20 asset used for streaming. | -| `batch` | `Batch.CreateWithRange[]` | An array of structs, each encapsulating a subset of the parameters of {SablierV2LockupLinear.createWithRange}. | -| `permit2Params` | `Permit2Params` | A struct encapsulating the parameters needed for Permit2, most importantly the signature. | - -**Returns** - -| Name | Type | Description | -| ----------- | ----------- | ------------------------------------- | -| `streamIds` | `uint256[]` | The ids of the newly created streams. | - -### cancelAndCreateWithDurations - -Cancels a Lockup stream and creates a new Lockup Linear stream using `createWithDurations`. Assets are transferred to -the proxy via Permit2. - -Notes: - -- `streamId` can reference either a Lockup Linear or a Lockup Dynamic stream. -- See {ISablierV2Lockup.cancel} and {ISablierV2LockupLinear.createWithDurations} for full documentation. Requirements: -- Must be delegate called. - -```solidity -function cancelAndCreateWithDurations( - ISablierV2Lockup lockup, - uint256 streamId, - ISablierV2LockupLinear lockupLinear, - LockupLinear.CreateWithDurations calldata createParams, - Permit2Params calldata permit2Params -) - external - returns (uint256 newStreamId); -``` - -**Parameters** - -| Name | Type | Description | -| --------------- | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `lockup` | `ISablierV2Lockup` | The address of the Lockup streaming contract where the stream to cancel is. | -| `streamId` | `uint256` | The id of the stream to cancel. | -| `lockupLinear` | `ISablierV2LockupLinear` | The address of the [SablierV2LockupLinear](docs/contracts/v2/reference/core/contract.SablierV2LockupLinear.md) contract to use for creating the new stream. | -| `createParams` | `LockupLinear.CreateWithDurations` | | -| `permit2Params` | `Permit2Params` | A struct encapsulating the parameters needed for Permit2, most importantly the signature. | - -**Returns** - -| Name | Type | Description | -| ------------- | --------- | ----------------------------------- | -| `newStreamId` | `uint256` | The id of the newly created stream. | - -### cancelAndCreateWithRange - -Cancels a Lockup stream and creates a new Lockup Linear stream using `createWithRange`. Assets are transferred to the -proxy via Permit2. - -Notes: - -- `streamId` can reference either a Lockup Linear or a Lockup Dynamic stream. -- See {ISablierV2Lockup.cancel} and {ISablierV2LockupLinear.createWithRange} for full documentation. Requirements: -- Must be delegate called. - -```solidity -function cancelAndCreateWithRange( - ISablierV2Lockup lockup, - uint256 streamId, - ISablierV2LockupLinear lockupLinear, - LockupLinear.CreateWithRange calldata createParams, - Permit2Params calldata permit2Params -) - external - returns (uint256 newStreamId); -``` - -**Parameters** - -| Name | Type | Description | -| --------------- | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `lockup` | `ISablierV2Lockup` | The address of the Lockup streaming contract where the stream to cancel is. | -| `streamId` | `uint256` | The id of the stream to cancel. | -| `lockupLinear` | `ISablierV2LockupLinear` | The address of the [SablierV2LockupLinear](docs/contracts/v2/reference/core/contract.SablierV2LockupLinear.md) contract to use for creating the new stream. | -| `createParams` | `LockupLinear.CreateWithRange` | | -| `permit2Params` | `Permit2Params` | A struct encapsulating the parameters needed for Permit2, most importantly the signature. | - -**Returns** - -| Name | Type | Description | -| ------------- | --------- | ----------------------------------- | -| `newStreamId` | `uint256` | The id of the newly created stream. | - -### createWithDurations - -Mirror for {SablierV2LockupLinear.createWithDurations}. Assets are transferred to the proxy via Permit2. - -_Must be delegate called._ - -```solidity -function createWithDurations( - ISablierV2LockupLinear lockupLinear, - LockupLinear.CreateWithDurations calldata createParams, - Permit2Params calldata permit2Params -) - external - returns (uint256 streamId); -``` - -**Parameters** - -| Name | Type | Description | -| --------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | -| `lockupLinear` | `ISablierV2LockupLinear` | The address of the [SablierV2LockupLinear](docs/contracts/v2/reference/core/contract.SablierV2LockupLinear.md) contract. | -| `createParams` | `LockupLinear.CreateWithDurations` | Struct encapsulating the function parameters, which are documented in V2 Core. | -| `permit2Params` | `Permit2Params` | A struct encapsulating the parameters needed for Permit2, most importantly the signature. | - -**Returns** - -| Name | Type | Description | -| ---------- | --------- | ----------------------------------- | -| `streamId` | `uint256` | The id of the newly created stream. | - -### createWithRange - -Mirror for {SablierV2LockupLinear.createWithRange}. Assets are transferred to the proxy via Permit2. - -_Must be delegate called._ - -```solidity -function createWithRange( - ISablierV2LockupLinear lockupLinear, - LockupLinear.CreateWithRange calldata createParams, - Permit2Params calldata permit2Params -) - external - returns (uint256 streamId); -``` - -**Parameters** - -| Name | Type | Description | -| --------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------ | -| `lockupLinear` | `ISablierV2LockupLinear` | The address of the [SablierV2LockupLinear](docs/contracts/v2/reference/core/contract.SablierV2LockupLinear.md) contract. | -| `createParams` | `LockupLinear.CreateWithRange` | Struct encapsulating the function parameters, which are documented in V2 Core. | -| `permit2Params` | `Permit2Params` | A struct encapsulating the parameters needed for Permit2, most importantly the signature. | - -**Returns** - -| Name | Type | Description | -| ---------- | --------- | ----------------------------------- | -| `streamId` | `uint256` | The id of the newly created stream. | - -### wrapAndCreateWithDurations - -Wraps the native asset payment in ERC-20 form and creates a Lockup Linear stream using `createWithDurations`. - -Notes: - -- `createParams.totalAmount` is overwritten with `msg.value`. -- See {ISablierV2LockupLinear.createWithDurations} for full documentation. Requirements: -- Must be delegate called. -- The ERC-20 amount credited by the wrapper contract must be equal to `msg.value`. - -```solidity -function wrapAndCreateWithDurations( - ISablierV2LockupLinear lockupLinear, - LockupLinear.CreateWithDurations memory createParams -) - external - payable - returns (uint256 streamId); -``` - -**Parameters** - -| Name | Type | Description | -| -------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | -| `lockupLinear` | `ISablierV2LockupLinear` | The address of the [SablierV2LockupLinear](docs/contracts/v2/reference/core/contract.SablierV2LockupLinear.md) contract. | -| `createParams` | `LockupLinear.CreateWithDurations` | Struct encapsulating the function parameters, which are documented in V2 Core. | - -### wrapAndCreateWithRange - -Wraps the native asset payment in ERC-20 form and creates a Lockup Linear stream using `createWithRange`. - -Notes: - -- `createParams.totalAmount` is overwritten with `msg.value`. -- See {ISablierV2LockupLinear.createWithRange} for full documentation. Requirements: -- Must be delegate called. -- The ERC-20 amount credited by the wrapper contract must be equal to `msg.value`. - -```solidity -function wrapAndCreateWithRange( - ISablierV2LockupLinear lockupLinear, - LockupLinear.CreateWithRange memory createParams -) - external - payable - returns (uint256 streamId); -``` - -**Parameters** - -| Name | Type | Description | -| -------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------ | -| `lockupLinear` | `ISablierV2LockupLinear` | The address of the [SablierV2LockupLinear](docs/contracts/v2/reference/core/contract.SablierV2LockupLinear.md) contract. | -| `createParams` | `LockupLinear.CreateWithRange` | Struct encapsulating the function parameters, which are documented in V2 Core. | - -### batchCreateWithDeltas - -Creates a batch of Lockup Dynamic streams using `createWithDeltas`. Assets are transferred to the proxy via Permit2. - -Requirements: - -- Must be delegate called. -- There must be at least one element in `batch`. -- All requirements from {ISablierV2LockupDynamic.createWithDeltas} must be met for each stream. - -```solidity -function batchCreateWithDeltas( - ISablierV2LockupDynamic lockupDynamic, - IERC20 asset, - Batch.CreateWithDeltas[] calldata batch, - Permit2Params calldata permit2Params -) - external - returns (uint256[] memory streamIds); -``` - -**Parameters** - -| Name | Type | Description | -| --------------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------- | -| `lockupDynamic` | `ISablierV2LockupDynamic` | The address of the [SablierV2LockupDynamic](docs/contracts/v2/reference/core/contract.SablierV2LockupDynamic.md) contract. | -| `asset` | `IERC20` | The contract address of the ERC-20 asset used for streaming. | -| `batch` | `Batch.CreateWithDeltas[]` | An array of structs, each encapsulating a subset of the parameters of {SablierV2LockupDynamic.createWithDeltas}. | -| `permit2Params` | `Permit2Params` | A struct encapsulating the parameters needed for Permit2, most importantly the signature. | - -**Returns** - -| Name | Type | Description | -| ----------- | ----------- | ------------------------------------- | -| `streamIds` | `uint256[]` | The ids of the newly created streams. | - -### batchCreateWithMilestones - -Creates a batch of Lockup Dynamic streams using `createWithMilestones`. Assets are transferred to the proxy via Permit2. - -Requirements: - -- Must be delegate called. -- There must be at least one element in `batch`. -- All requirements from {ISablierV2LockupDynamic.createWithMilestones} must be met for each stream. - -```solidity -function batchCreateWithMilestones( - ISablierV2LockupDynamic lockupDynamic, - IERC20 asset, - Batch.CreateWithMilestones[] calldata batch, - Permit2Params calldata permit2Params -) - external - returns (uint256[] memory streamIds); -``` - -**Parameters** - -| Name | Type | Description | -| --------------- | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------- | -| `lockupDynamic` | `ISablierV2LockupDynamic` | The address of the [SablierV2LockupDynamic](docs/contracts/v2/reference/core/contract.SablierV2LockupDynamic.md) contract. | -| `asset` | `IERC20` | The contract address of the ERC-20 asset used for streaming. | -| `batch` | `Batch.CreateWithMilestones[]` | An array of structs, each encapsulating a subset of the parameters of {SablierV2LockupDynamic.createWithMilestones}. | -| `permit2Params` | `Permit2Params` | A struct encapsulating the parameters needed for Permit2, most importantly the signature. | - -**Returns** - -| Name | Type | Description | -| ----------- | ----------- | ------------------------------------- | -| `streamIds` | `uint256[]` | The ids of the newly created streams. | - -### cancelAndCreateWithDeltas - -Cancels a Lockup stream and creates a new Lockup Dynamic stream using `createWithDeltas`. Assets are transferred to the -proxy via Permit2. - -Notes: - -- `streamId` can reference either a Lockup Linear or a Lockup Dynamic stream. -- See {ISablierV2Lockup.cancel} and {ISablierV2LockupDynamic.createWithDeltas} for full documentation. Requirements: -- Must be delegate called. - -```solidity -function cancelAndCreateWithDeltas( - ISablierV2Lockup lockup, - uint256 streamId, - ISablierV2LockupDynamic lockupDynamic, - LockupDynamic.CreateWithDeltas calldata createParams, - Permit2Params calldata permit2Params -) - external - returns (uint256 newStreamId); -``` - -**Parameters** - -| Name | Type | Description | -| --------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `lockup` | `ISablierV2Lockup` | The address of the Lockup streaming contract where the stream to cancel is. | -| `streamId` | `uint256` | The id of the stream to cancel. | -| `lockupDynamic` | `ISablierV2LockupDynamic` | The address of the [SablierV2LockupDynamic](docs/contracts/v2/reference/core/contract.SablierV2LockupDynamic.md) contract to use for creating the new stream. | -| `createParams` | `LockupDynamic.CreateWithDeltas` | A struct encapsulating the create function parameters, which are documented in V2 Core. | -| `permit2Params` | `Permit2Params` | A struct encapsulating the parameters needed for Permit2, most importantly the signature. | - -**Returns** - -| Name | Type | Description | -| ------------- | --------- | ----------------------------------- | -| `newStreamId` | `uint256` | The id of the newly created stream. | - -### cancelAndCreateWithMilestones - -Cancels a Lockup stream and creates a new Lockup Dynamic stream using `createWithMilestones`. Assets are transferred to -the proxy via Permit2. - -Notes: - -- `streamId` can reference either a Lockup Linear or a Lockup Dynamic stream. -- See {ISablierV2Lockup.cancel} and {ISablierV2LockupDynamic.createWithMilestones} for full documentation. Requirements: -- Must be delegate called. - -```solidity -function cancelAndCreateWithMilestones( - ISablierV2Lockup lockup, - uint256 streamId, - ISablierV2LockupDynamic lockupDynamic, - LockupDynamic.CreateWithMilestones calldata createParams, - Permit2Params calldata permit2Params -) - external - returns (uint256 newStreamId); -``` - -**Parameters** - -| Name | Type | Description | -| --------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `lockup` | `ISablierV2Lockup` | The address of the Lockup streaming contract where the stream to cancel is. | -| `streamId` | `uint256` | The id of the stream to cancel. | -| `lockupDynamic` | `ISablierV2LockupDynamic` | The address of the [SablierV2LockupDynamic](docs/contracts/v2/reference/core/contract.SablierV2LockupDynamic.md) contract to use for creating the new stream. | -| `createParams` | `LockupDynamic.CreateWithMilestones` | A struct encapsulating the create function parameters, which are documented in V2 Core. | -| `permit2Params` | `Permit2Params` | A struct encapsulating the parameters needed for Permit2, most importantly the signature. | - -**Returns** - -| Name | Type | Description | -| ------------- | --------- | ----------------------------------- | -| `newStreamId` | `uint256` | The id of the newly created stream. | - -### createWithDeltas - -Mirror for {SablierV2LockupDynamic.createWithDeltas}. Assets are transferred to the proxy via Permit2. - -_Must be delegate called._ - -```solidity -function createWithDeltas( - ISablierV2LockupDynamic lockupDynamic, - LockupDynamic.CreateWithDeltas calldata createParams, - Permit2Params calldata permit2Params -) - external - returns (uint256 streamId); -``` - -**Parameters** - -| Name | Type | Description | -| --------------- | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | -| `lockupDynamic` | `ISablierV2LockupDynamic` | The address of the [SablierV2LockupDynamic](docs/contracts/v2/reference/core/contract.SablierV2LockupDynamic.md) contract. | -| `createParams` | `LockupDynamic.CreateWithDeltas` | A struct encapsulating the create function parameters, which are documented in V2 Core. | -| `permit2Params` | `Permit2Params` | A struct encapsulating the parameters needed for Permit2, most importantly the signature. | - -**Returns** - -| Name | Type | Description | -| ---------- | --------- | ----------------------------------- | -| `streamId` | `uint256` | The id of the newly created stream. | - -### createWithMilestones - -Mirror for {SablierV2LockupDynamic.createWithMilestones}. Assets are transferred to the proxy via Permit2. - -_Must be delegate called._ - -```solidity -function createWithMilestones( - ISablierV2LockupDynamic lockupDynamic, - LockupDynamic.CreateWithMilestones calldata createParams, - Permit2Params calldata permit2Params -) - external - returns (uint256 streamId); -``` - -**Parameters** - -| Name | Type | Description | -| --------------- | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- | -| `lockupDynamic` | `ISablierV2LockupDynamic` | The address of the [SablierV2LockupDynamic](docs/contracts/v2/reference/core/contract.SablierV2LockupDynamic.md) contract. | -| `createParams` | `LockupDynamic.CreateWithMilestones` | Struct encapsulating the function parameters, which are documented in V2 Core. | -| `permit2Params` | `Permit2Params` | A struct encapsulating the parameters needed for Permit2, most importantly the signature. | - -**Returns** - -| Name | Type | Description | -| ---------- | --------- | ----------------------------------- | -| `streamId` | `uint256` | The id of the newly created stream. | - -### wrapAndCreateWithDeltas - -Wraps the native asset payment in ERC-20 form and creates a Lockup Dynamic stream using `createWithDeltas`. - -Notes: - -- `createParams.totalAmount` is overwritten with `msg.value`. -- See {ISablierV2LockupDynamic.createWithDeltas} for full documentation. Requirements: -- Must be delegate called. -- The ERC-20 amount credited by the wrapper contract must be equal to `msg.value`. - -```solidity -function wrapAndCreateWithDeltas( - ISablierV2LockupDynamic lockupDynamic, - LockupDynamic.CreateWithDeltas memory createParams -) - external - payable - returns (uint256 streamId); -``` - -**Parameters** - -| Name | Type | Description | -| --------------- | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | -| `lockupDynamic` | `ISablierV2LockupDynamic` | The address of the [SablierV2LockupDynamic](docs/contracts/v2/reference/core/contract.SablierV2LockupDynamic.md) contract. | -| `createParams` | `LockupDynamic.CreateWithDeltas` | Struct encapsulating the function parameters, which are documented in V2 Core. | - -**Returns** - -| Name | Type | Description | -| ---------- | --------- | ----------------------------------- | -| `streamId` | `uint256` | The id of the newly created stream. | - -### wrapAndCreateWithMilestones - -Wraps the native asset payment in ERC-20 form and creates a Lockup Dynamic stream using `createWithMilestones`. - -Notes: - -- `createParams.totalAmount` is overwritten with `msg.value`. -- See {ISablierV2LockupDynamic.createWithMilestones} for full documentation. Requirements: -- Must be delegate called. -- The ERC-20 amount credited by the wrapper contract must be equal to `msg.value`. - -```solidity -function wrapAndCreateWithMilestones( - ISablierV2LockupDynamic lockupDynamic, - LockupDynamic.CreateWithMilestones memory createParams -) - external - payable - returns (uint256 streamId); -``` - -**Parameters** - -| Name | Type | Description | -| --------------- | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- | -| `lockupDynamic` | `ISablierV2LockupDynamic` | The address of the [SablierV2LockupDynamic](docs/contracts/v2/reference/core/contract.SablierV2LockupDynamic.md) contract. | -| `createParams` | `LockupDynamic.CreateWithMilestones` | Struct encapsulating the function parameters, which are documented in V2 Core. | - -**Returns** - -| Name | Type | Description | -| ---------- | --------- | ----------------------------------- | -| `streamId` | `uint256` | The id of the newly created stream. | diff --git a/docs/contracts/v2/reference/periphery/interfaces/interface.IWrappedNativeAsset.md b/docs/contracts/v2/reference/periphery/interfaces/interface.IWrappedNativeAsset.md deleted file mode 100644 index d9eda7b6..00000000 --- a/docs/contracts/v2/reference/periphery/interfaces/interface.IWrappedNativeAsset.md +++ /dev/null @@ -1,33 +0,0 @@ -# IWrappedNativeAsset - -[Git Source](https://github.com/sablier-labs/v2-periphery/blob/05c331e79e05886c7837dfda1bc21197c1c3c748/docs/contracts/v2/reference/periphery/interfaces) - -**Inherits:** IERC20 - -An interface for contracts that wrap native assets in ERC-20 form, such as WETH. - -_A generic name is used instead of "WETH" to accommodate chains with different native assets._ - -## Functions - -### deposit - -Deposits native assets to receive ERC-20 wrapped assets. - -```solidity -function deposit() external payable; -``` - -### withdraw - -Withdraws ERC-20 wrapped assets to obtain native assets. - -```solidity -function withdraw(uint256 amount) external; -``` - -**Parameters** - -| Name | Type | Description | -| -------- | --------- | ------------------------------------------------ | -| `amount` | `uint256` | The amount of ERC-20 wrapped assets to withdraw. | diff --git a/docs/contracts/v2/reference/periphery/libraries/library.Errors.md b/docs/contracts/v2/reference/periphery/libraries/library.Errors.md index db3e3813..6d8c8480 100644 --- a/docs/contracts/v2/reference/periphery/libraries/library.Errors.md +++ b/docs/contracts/v2/reference/periphery/libraries/library.Errors.md @@ -1,39 +1,61 @@ # Errors -[Git Source](https://github.com/sablier-labs/v2-periphery/blob/05c331e79e05886c7837dfda1bc21197c1c3c748/src/libraries/Errors.sol) +[Git Source](https://github.com/sablier-labs/v2-periphery/blob/release/src/libraries/Errors.sol) Library containing all custom errors the protocol may revert with. ## Errors -### CallNotDelegateCall +### SablierV2Batch_BatchSizeZero -Thrown when trying to perform a standard call to a function that allows only delegate calls. +Thrown when trying to create a batch with zero elements. ```solidity -error CallNotDelegateCall(); +error SablierV2Batch_BatchSizeZero(); ``` -### SablierV2ProxyPlugin_UnknownCaller +### SablierV2MerkleStreamer_CampaignExpired -Thrown when the caller is an unknown address, which is not listed in the archive. +Thrown when trying to claim after the campaign has expired. ```solidity -error SablierV2ProxyPlugin_UnknownCaller(address caller); +error SablierV2MerkleStreamer_CampaignExpired(uint256 currentTime, uint40 expiration); ``` -### SablierV2ProxyTarget_BatchSizeZero +### SablierV2MerkleStreamer_CampaignNotExpired -Thrown when trying to perform an action that requires the batch size to not be zero. +Thrown when trying to claim when Merkle streamer has not expired. ```solidity -error SablierV2ProxyTarget_BatchSizeZero(); +error SablierV2MerkleStreamer_CampaignNotExpired(uint256 currentTime, uint40 expiration); ``` -### SablierV2ProxyTarget_CreditAmountMismatch +### SablierV2MerkleStreamer_InvalidProof -Thrown when trying to wrap and create a stream and the credit amount is not equal to `msg.value`. +Thrown when trying to claim with an invalid Merkle proof. ```solidity -error SablierV2ProxyTarget_CreditAmountMismatch(uint256 msgValue, uint256 creditAmount); +error SablierV2MerkleStreamer_InvalidProof(); +``` + +Thrown when trying to clawback when the protocol fee is zero. + +```solidity +error SablierV2MerkleStreamer_ProtocolFeeZero(); +``` + +### SablierV2MerkleStreamer_ProtocolFeeNotZero + +Thrown when trying to claim when the protocol fee is not zero. + +```solidity +error SablierV2MerkleStreamer_ProtocolFeeNotZero(); +``` + +### SablierV2MerkleStreamer_StreamClaimed + +Thrown when trying to claim the same stream more than once. + +```solidity +error SablierV2MerkleStreamer_StreamClaimed(uint256 index); ``` diff --git a/docs/contracts/v2/reference/periphery/types/library.Batch.md b/docs/contracts/v2/reference/periphery/types/library.Batch.md index 49c55885..22a480c5 100644 --- a/docs/contracts/v2/reference/periphery/types/library.Batch.md +++ b/docs/contracts/v2/reference/periphery/types/library.Batch.md @@ -17,7 +17,9 @@ struct CancelMultiple { ### CreateWithDeltas -A struct encapsulating all parameters of {SablierV2LockupDynamic.createWithDelta} except for the asset. +A struct encapsulating all parameters of +[SablierV2LockupDynamic.createWithDelta](/contracts/v2/reference/core/types/library.LockupDynamic#createwithdeltas) +except for the asset. ```solidity struct CreateWithDeltas { @@ -32,7 +34,9 @@ struct CreateWithDeltas { ### CreateWithDurations -A struct encapsulating all parameters of {SablierV2LockupLinear.createWithDurations} except for the asset. +A struct encapsulating all parameters of +[SablierV2LockupLinear.createWithDurations](/contracts/v2/reference/core/types/library.LockupLinear#createwithdurations) +except for the asset. ```solidity struct CreateWithDurations { @@ -47,7 +51,9 @@ struct CreateWithDurations { ### CreateWithMilestones -A struct encapsulating all parameters of {SablierV2LockupDynamic.createWithMilestones} except for the asset. +A struct encapsulating all parameters of +[SablierV2LockupDynamic.createWithMilestones](/contracts/v2/reference/core/types/library.LockupDynamic#createwithmilestones) +except for the asset. ```solidity struct CreateWithMilestones { @@ -63,7 +69,9 @@ struct CreateWithMilestones { ### CreateWithRange -A struct encapsulating all parameters of {SablierV2LockupLinear.createWithRange} except for the asset. +A struct encapsulating all parameters of +[SablierV2LockupLinear.createWithRange](/contracts/v2/reference/core/types/library.LockupLinear#createwithrange)) except +for the asset. ```solidity struct CreateWithRange { diff --git a/docs/contracts/v2/reference/periphery/types/struct.Permit2Params.md b/docs/contracts/v2/reference/periphery/types/struct.Permit2Params.md deleted file mode 100644 index 39e6e225..00000000 --- a/docs/contracts/v2/reference/periphery/types/struct.Permit2Params.md +++ /dev/null @@ -1,14 +0,0 @@ -# Permit2Params - -[Git Source](https://github.com/sablier-labs/v2-periphery/blob/05c331e79e05886c7837dfda1bc21197c1c3c748/src/types/Permit2.sol) - -A struct encapsulating the parameters needed for Permit2. - -_See the full documentation at https://github.com/Uniswap/permit2._ - -```solidity -struct Permit2Params { - IAllowanceTransfer.PermitSingle permitSingle; - bytes signature; -} -``` diff --git a/docs/snippets/BatchCommonSteps.mdx b/docs/snippets/BatchCommonSteps.mdx new file mode 100644 index 00000000..009d08ef --- /dev/null +++ b/docs/snippets/BatchCommonSteps.mdx @@ -0,0 +1,39 @@ +## Function definition + +Define a function called `batchCreateStreams` that takes a parameter `perStreamAmount` and returns an array of ids for +the created streams: + +```solidity +function batchCreateStreams(uint128 perStreamAmount) public returns (uint256[] memory streamIds) { + // ... +} +``` + +## Batch size + +Next, declare a batch size, which is needed to calculate the transfer amount: + +```solidity +// Create a batch of two streams +uint256 batchSize = 2; + +// Calculate the combined amount of DAI assets to transfer to this contract +uint256 transferAmount = perStreamAmount * batchSize; +``` + +## ERC-20 steps + +To create a stream, the caller must approve the creator contract to pull the tokens from the calling address's account. +Then, we have to also approve the `Batch` contract to pull the assets that the creator contract will be in possession of +after they are transferred from the calling address (you): + +```solidity +// Transfer the provided amount of DAI tokens to this contract +DAI.transferFrom(msg.sender, address(this), transferAmount); + +// Approve the Batch contract to spend DAI +DAI.approve(address(BATCH), transferAmount); +``` + +For more guidance on how to approve and transfer ERC-20 assets, see +[this article](https://ethereum.org/en/developers/docs/standards/tokens/erc-20/) on the Ethereum website. diff --git a/docs/snippets/ConstantsComment.mdx b/docs/snippets/ConstantsComment.mdx new file mode 100644 index 00000000..a229dd17 --- /dev/null +++ b/docs/snippets/ConstantsComment.mdx @@ -0,0 +1,5 @@ +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 Mainnet. If you need to work with a different chain, the Sablier addresses can be +obtained from the [Deployment Addresses](/contracts/v2/deployments) page. diff --git a/docs/snippets/ERC20Steps.mdx b/docs/snippets/ERC20Steps.mdx deleted file mode 100644 index b51b6d97..00000000 --- a/docs/snippets/ERC20Steps.mdx +++ /dev/null @@ -1,16 +0,0 @@ -## ERC-20 steps - -To create a stream, the caller must approve the creator contract to pull the tokens from the calling address's account. -Then, we have to also approve the Sablier contract to pull the assets that the creator contract will be in possession of -after they are transferred from the calling address (you): - -```solidity -// Transfer the provided amount of DAI tokens to this contract -DAI.transferFrom(msg.sender, address(this), totalAmount); - -// Approve the Sablier contract to spend DAI -DAI.approve(address(sablier), totalAmount); -``` - -For more guidance on how to approve and transfer ERC-20 assets, see -[this article](https://ethereum.org/en/developers/docs/standards/tokens/erc-20/) on the Ethereum website. diff --git a/docs/snippets/ParamRecipient.mdx b/docs/snippets/ParamRecipient.mdx index d551d597..b78eb604 100644 --- a/docs/snippets/ParamRecipient.mdx +++ b/docs/snippets/ParamRecipient.mdx @@ -3,5 +3,5 @@ The address receiving the assets: ```solidity -params.recipient = recipient; +params.recipient = address(0xCAFE); ``` diff --git a/static/img/diagram-airstream-campaign.png b/static/img/diagram-airstream-campaign.png new file mode 100644 index 0000000000000000000000000000000000000000..2b3f0e9fab1a0f3f70ee326bad4cc4fb010f2ed3 GIT binary patch literal 151320 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

fXd}fIOO`_a23Bv|4Ja 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