diff --git a/src/modules/invoice-module/InvoiceModule.sol b/src/modules/invoice-module/InvoiceModule.sol index 6b8af2a..0cef083 100644 --- a/src/modules/invoice-module/InvoiceModule.sol +++ b/src/modules/invoice-module/InvoiceModule.sol @@ -213,33 +213,27 @@ contract InvoiceModule is IInvoiceModule, StreamManager { if (invoice.status == Types.Status.Paid) { revert Errors.CannotCancelPaidInvoice(); } else if (invoice.status == Types.Status.Canceled) { - revert Errors.InvoiceAlreadyCanceled(); + revert Errors.CannotCancelCanceledInvoice(); } // Checks: the `msg.sender` is the creator if dealing with a transfer-based invoice - // or a linear/tranched stream-based invoice which was not paid yet (not streaming) // // Notes: - // - Once a linear or tranched stream is created, the `msg.sender` is checked in the + // - for a linear or tranched stream-based invoice, the `msg.sender` is checked in the // {SablierV2Lockup} `cancel` method - if (invoice.payment.method == Types.Method.Transfer || invoice.status == Types.Status.Pending) { + if (invoice.payment.method == Types.Method.Transfer) { if (invoice.recipient != msg.sender) { revert Errors.InvoiceOwnerUnauthorized(); } } + // Effects: cancel the stream accordingly depending on its type - // - // Notes: - // - A transfer-based invoice can be canceled directly - // - A linear or tranched stream MUST be canceled by calling the `cancel` method on the according - // {ISablierV2Lockup} contract - else if (invoice.status == Types.Status.Ongoing) { - if (invoice.payment.method == Types.Method.LinearStream) { - cancelLinearStream({ streamId: invoice.payment.streamId }); - } else if (invoice.payment.method == Types.Method.TranchedStream) { - cancelTranchedStream({ streamId: invoice.payment.streamId }); - } + if (invoice.payment.method == Types.Method.LinearStream) { + cancelLinearStream({ streamId: invoice.payment.streamId }); + } else if (invoice.payment.method == Types.Method.TranchedStream) { + cancelTranchedStream({ streamId: invoice.payment.streamId }); } + // Effects: mark the invoice as canceled _invoices[id].status = Types.Status.Canceled; diff --git a/src/modules/invoice-module/interfaces/IInvoiceModule.sol b/src/modules/invoice-module/interfaces/IInvoiceModule.sol index ebb9a42..00e6c18 100644 --- a/src/modules/invoice-module/interfaces/IInvoiceModule.sol +++ b/src/modules/invoice-module/interfaces/IInvoiceModule.sol @@ -72,9 +72,6 @@ interface IInvoiceModule { /// @notice Cancels the `id` invoice /// /// Notes: - /// - A transfer-based invoice can be canceled only by its creator (recipient) - /// - A linear/tranched stream-based invoice can be canceled by its creator only if its - /// status is `Pending`; otherwise only the stream sender can cancel it /// - if the invoice has a linear or tranched stream payment method, the streaming flow will be /// stopped and the remaining funds will be refunded to the stream payer /// diff --git a/src/modules/invoice-module/libraries/Errors.sol b/src/modules/invoice-module/libraries/Errors.sol index b5d1523..2275bf7 100644 --- a/src/modules/invoice-module/libraries/Errors.sol +++ b/src/modules/invoice-module/libraries/Errors.sol @@ -55,10 +55,7 @@ library Errors { error CannotCancelPaidInvoice(); /// @notice Thrown when an attempt is made to cancel an already canceled invoice - error InvoiceAlreadyCanceled(); - - /// @notice Thrown when the caller is not the initial stream sender - error OnlyInitialStreamSender(address initialSender); + error CannotCancelCanceledInvoice(); /*////////////////////////////////////////////////////////////////////////// STREAM-MANAGER diff --git a/src/modules/invoice-module/sablier-v2/StreamManager.sol b/src/modules/invoice-module/sablier-v2/StreamManager.sol index a890638..7dc908b 100644 --- a/src/modules/invoice-module/sablier-v2/StreamManager.sol +++ b/src/modules/invoice-module/sablier-v2/StreamManager.sol @@ -35,13 +35,6 @@ contract StreamManager is IStreamManager { /// @inheritdoc IStreamManager UD60x18 public override brokerFee; - /*////////////////////////////////////////////////////////////////////////// - PRIVATE STORAGE - //////////////////////////////////////////////////////////////////////////*/ - - /// @dev Stores the initial address of the account that started the stream - mapping(uint256 streamId => address initialSender) private _initialStreamSender; - /*////////////////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////////////////*/ @@ -68,13 +61,6 @@ contract StreamManager is IStreamManager { _; } - /// @notice Reverts if the `msg.sender` is not the initial stream sender (creator of the stream) - modifier onlyInitialStreamSender(uint256 streamId) { - address initialSender = _initialStreamSender[streamId]; - if (msg.sender != initialSender) revert Errors.OnlyInitialStreamSender(initialSender); - _; - } - /*////////////////////////////////////////////////////////////////////////// NON-CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ @@ -92,9 +78,6 @@ contract StreamManager is IStreamManager { // Create the Lockup Linear stream streamId = _createLinearStream(asset, totalAmount, startTime, endTime, recipient); - - // Set `msg.sender` as the initial stream sender to allow secure stream management - _initialStreamSender[streamId] = msg.sender; } /// @inheritdoc IStreamManager @@ -111,9 +94,6 @@ contract StreamManager is IStreamManager { // Create the Lockup Linear stream streamId = _createTranchedStream(asset, totalAmount, startTime, recipient, numberOfTranches, recurrence); - - // Set `msg.sender` as the initial stream sender to allow secure stream management - _initialStreamSender[streamId] = msg.sender; } /// @inheritdoc IStreamManager @@ -185,7 +165,7 @@ contract StreamManager is IStreamManager { LockupLinear.CreateWithTimestamps memory params; // Declare the function parameters - params.sender = address(this); // The sender will be able to cancel the stream + params.sender = msg.sender; // The sender will be able to cancel the stream params.recipient = recipient; // The recipient of the streamed assets params.totalAmount = totalAmount; // Total amount is the amount inclusive of all fees params.asset = asset; // The streaming asset @@ -213,7 +193,7 @@ contract StreamManager is IStreamManager { LockupTranched.CreateWithTimestamps memory params; // Declare the function parameters - params.sender = address(this); // The sender will be able to cancel the stream + params.sender = msg.sender; // The sender will be able to cancel the stream params.recipient = recipient; // The recipient of the streamed assets params.totalAmount = totalAmount; // Total amount is the amount inclusive of all fees params.asset = asset; // The streaming asset @@ -247,17 +227,12 @@ contract StreamManager is IStreamManager { } /// @dev Withdraws from either a linear or tranched stream - function _withdrawStream( - ISablierV2Lockup sablier, - uint256 streamId, - address to, - uint128 amount - ) internal onlyInitialStreamSender(streamId) { + function _withdrawStream(ISablierV2Lockup sablier, uint256 streamId, address to, uint128 amount) internal { sablier.withdraw(streamId, to, amount); } /// @dev Cancels the `streamId` stream - function _cancelStream(ISablierV2Lockup sablier, uint256 streamId) internal onlyInitialStreamSender(streamId) { + function _cancelStream(ISablierV2Lockup sablier, uint256 streamId) internal { sablier.cancel(streamId); } diff --git a/test/integration/concrete/invoice-module/cancel-invoice/cancelInvoice.t.sol b/test/integration/concrete/invoice-module/cancel-invoice/cancelInvoice.t.sol deleted file mode 100644 index 77487ee..0000000 --- a/test/integration/concrete/invoice-module/cancel-invoice/cancelInvoice.t.sol +++ /dev/null @@ -1,312 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.26; - -import { CancelInvoice_Integration_Shared_Test } from "../../../shared/cancelInvoice.t.sol"; -import { Types } from "./../../../../../src/modules/invoice-module/libraries/Types.sol"; -import { Events } from "../../../../utils/Events.sol"; -import { Errors } from "../../../../utils/Errors.sol"; - -contract CancelInvoice_Integration_Concret_Test is CancelInvoice_Integration_Shared_Test { - function setUp() public virtual override { - CancelInvoice_Integration_Shared_Test.setUp(); - } - - function test_RevertWhen_InvoiceIsPaid() external { - // Set the one-off ETH transfer invoice as current one - uint256 invoiceId = 1; - - // Make Bob the payer for the default invoice - vm.startPrank({ msgSender: users.bob }); - - // Pay the invoice first - invoiceModule.payInvoice{ value: invoices[invoiceId].payment.amount }({ id: invoiceId }); - - // Make Eve the caller who is the recipient of the invoice - vm.startPrank({ msgSender: users.eve }); - - // Expect the call to revert with the {CannotCancelPaidInvoice} error - vm.expectRevert(Errors.CannotCancelPaidInvoice.selector); - - // Run the test - invoiceModule.cancelInvoice({ id: invoiceId }); - } - - function test_RevertWhen_InvoiceIsCanceled() external whenInvoiceNotAlreadyPaid { - // Set the one-off ETH transfer invoice as current one - uint256 invoiceId = 1; - - // Make Eve the caller who is the recipient of the invoice - vm.startPrank({ msgSender: users.eve }); - - // Cancel the invoice first - invoiceModule.cancelInvoice({ id: invoiceId }); - - // Expect the call to revert with the {InvoiceAlreadyCanceled} error - vm.expectRevert(Errors.InvoiceAlreadyCanceled.selector); - - // Run the test - invoiceModule.cancelInvoice({ id: invoiceId }); - } - - function test_RevertWhen_PaymentMethodTransfer_SenderNotInvoiceRecipient() - external - whenInvoiceNotAlreadyPaid - whenInvoiceNotCanceled - givenPaymentMethodTransfer - { - // Set the one-off ETH transfer invoice as current one - uint256 invoiceId = 1; - - // Make Bob the caller who IS NOT the recipient of the invoice - vm.startPrank({ msgSender: users.bob }); - - // Expect the call to revert with the {InvoiceOwnerUnauthorized} error - vm.expectRevert(Errors.InvoiceOwnerUnauthorized.selector); - - // Run the test - invoiceModule.cancelInvoice({ id: invoiceId }); - } - - function test_CancelInvoice_PaymentMethodTransfer() - external - whenInvoiceNotAlreadyPaid - whenInvoiceNotCanceled - givenPaymentMethodTransfer - whenSenderInvoiceRecipient - { - // Set the one-off ETH transfer invoice as current one - uint256 invoiceId = 1; - - // Make Eve the caller who is the recipient of the invoice - vm.startPrank({ msgSender: users.eve }); - - // Expect the {InvoiceCanceled} event to be emitted - vm.expectEmit(); - emit Events.InvoiceCanceled({ id: invoiceId }); - - // Run the test - invoiceModule.cancelInvoice({ id: invoiceId }); - - // Assert the actual and expected invoice status - Types.Invoice memory invoice = invoiceModule.getInvoice({ id: invoiceId }); - assertEq(uint8(invoice.status), uint8(Types.Status.Canceled)); - } - - function test_RevertWhen_PaymentMethodLinearStream_StatusPending_SenderNotInvoiceRecipient() - external - whenInvoiceNotAlreadyPaid - whenInvoiceNotCanceled - givenPaymentMethodLinearStream - givenInvoiceStatusPending - { - // Set current invoice as a linear stream-based one - uint256 invoiceId = 3; - - // Make Bob the caller who IS NOT the recipient of the invoice - vm.startPrank({ msgSender: users.bob }); - - // Expect the call to revert with the {InvoiceOwnerUnauthorized} error - vm.expectRevert(Errors.InvoiceOwnerUnauthorized.selector); - - // Run the test - invoiceModule.cancelInvoice({ id: invoiceId }); - } - - function test_CancelInvoice_PaymentMethodLinearStream_StatusPending() - external - whenInvoiceNotAlreadyPaid - whenInvoiceNotCanceled - givenPaymentMethodLinearStream - givenInvoiceStatusPending - whenSenderInvoiceRecipient - { - // Set current invoice as a linear stream-based one - uint256 invoiceId = 3; - - // Make Eve the caller who is the recipient of the invoice - vm.startPrank({ msgSender: users.eve }); - - // Expect the {InvoiceCanceled} event to be emitted - vm.expectEmit(); - emit Events.InvoiceCanceled({ id: invoiceId }); - - // Run the test - invoiceModule.cancelInvoice({ id: invoiceId }); - - // Assert the actual and expected invoice status - Types.Invoice memory invoice = invoiceModule.getInvoice({ id: invoiceId }); - assertEq(uint8(invoice.status), uint8(Types.Status.Canceled)); - } - - function test_RevertWhen_PaymentMethodLinearStream_StatusOngoing_SenderNoInitialtStreamSender() - external - whenInvoiceNotAlreadyPaid - whenInvoiceNotCanceled - givenPaymentMethodLinearStream - givenInvoiceStatusOngoing - { - // Set current invoice as a linear stream-based one - uint256 invoiceId = 3; - - // The invoice must be paid for its status to be updated to `Ongoing` - // Make Bob the payer of the invoice (also Bob will be the stream sender) - vm.startPrank({ msgSender: users.bob }); - - // Approve the {InvoiceModule} to transfer the USDT tokens on Bob's behalf - usdt.approve({ spender: address(invoiceModule), amount: invoices[invoiceId].payment.amount }); - - // Pay the invoice first (status will be updated to `Ongoing`) - invoiceModule.payInvoice{ value: invoices[invoiceId].payment.amount }({ id: invoiceId }); - - // Make Eve the caller who IS NOT the initial stream sender but rather the recipient - vm.startPrank({ msgSender: users.eve }); - - // Expect the call to revert with the {OnlyInitialStreamSender} error - vm.expectRevert(abi.encodeWithSelector(Errors.OnlyInitialStreamSender.selector, users.bob)); - - // Run the test - invoiceModule.cancelInvoice({ id: invoiceId }); - } - - function test_CancelInvoice_PaymentMethodLinearStream_StatusOngoing() - external - whenInvoiceNotAlreadyPaid - whenInvoiceNotCanceled - givenPaymentMethodLinearStream - givenInvoiceStatusOngoing - whenSenderInitialStreamSender - { - // Set current invoice as a linear stream-based one - uint256 invoiceId = 3; - - // The invoice must be paid for its status to be updated to `Ongoing` - // Make Bob the payer of the invoice (also Bob will be the initial stream sender) - vm.startPrank({ msgSender: users.bob }); - - // Approve the {InvoiceModule} to transfer the USDT tokens on Bob's behalf - usdt.approve({ spender: address(invoiceModule), amount: invoices[invoiceId].payment.amount }); - - // Pay the invoice first (status will be updated to `Ongoing`) - invoiceModule.payInvoice{ value: invoices[invoiceId].payment.amount }({ id: invoiceId }); - - // Expect the {InvoiceCanceled} event to be emitted - vm.expectEmit(); - emit Events.InvoiceCanceled({ id: invoiceId }); - - // Run the test - invoiceModule.cancelInvoice({ id: invoiceId }); - - // Assert the actual and expected invoice status - Types.Invoice memory invoice = invoiceModule.getInvoice({ id: invoiceId }); - assertEq(uint8(invoice.status), uint8(Types.Status.Canceled)); - } - - function test_RevertWhen_PaymentMethodTranchedStream_StatusPending_SenderNotInvoiceRecipient() - external - whenInvoiceNotAlreadyPaid - whenInvoiceNotCanceled - givenPaymentMethodTranchedStream - givenInvoiceStatusPending - { - // Set current invoice as a tranched stream-based one - uint256 invoiceId = 4; - - // Make Bob the caller who IS NOT the recipient of the invoice - vm.startPrank({ msgSender: users.bob }); - - // Expect the call to revert with the {InvoiceOwnerUnauthorized} error - vm.expectRevert(Errors.InvoiceOwnerUnauthorized.selector); - - // Run the test - invoiceModule.cancelInvoice({ id: invoiceId }); - } - - function test_CancelInvoice_PaymentMethodTranchedStream_StatusPending() - external - whenInvoiceNotAlreadyPaid - whenInvoiceNotCanceled - givenPaymentMethodTranchedStream - givenInvoiceStatusPending - whenSenderInvoiceRecipient - { - // Set current invoice as a tranched stream-based one - uint256 invoiceId = 4; - - // Make Eve the caller who is the recipient of the invoice - vm.startPrank({ msgSender: users.eve }); - - // Expect the {InvoiceCanceled} event to be emitted - vm.expectEmit(); - emit Events.InvoiceCanceled({ id: invoiceId }); - - // Run the test - invoiceModule.cancelInvoice({ id: invoiceId }); - - // Assert the actual and expected invoice status - Types.Invoice memory invoice = invoiceModule.getInvoice({ id: invoiceId }); - assertEq(uint8(invoice.status), uint8(Types.Status.Canceled)); - } - - function test_RevertWhen_PaymentMethodTranchedStream_StatusOngoing_SenderNoInitialtStreamSender() - external - whenInvoiceNotAlreadyPaid - whenInvoiceNotCanceled - givenPaymentMethodTranchedStream - givenInvoiceStatusOngoing - { - // Set current invoice as a tranched stream-based one - uint256 invoiceId = 4; - - // The invoice must be paid for its status to be updated to `Ongoing` - // Make Bob the payer of the invoice (also Bob will be the stream sender) - vm.startPrank({ msgSender: users.bob }); - - // Approve the {InvoiceModule} to transfer the USDT tokens on Bob's behalf - usdt.approve({ spender: address(invoiceModule), amount: invoices[invoiceId].payment.amount }); - - // Pay the invoice first (status will be updated to `Ongoing`) - invoiceModule.payInvoice{ value: invoices[invoiceId].payment.amount }({ id: invoiceId }); - - // Make Eve the caller who IS NOT the initial stream sender but rather the recipient - vm.startPrank({ msgSender: users.eve }); - - // Expect the call to revert with the {OnlyInitialStreamSender} error - vm.expectRevert(abi.encodeWithSelector(Errors.OnlyInitialStreamSender.selector, users.bob)); - - // Run the test - invoiceModule.cancelInvoice({ id: invoiceId }); - } - - function test_CancelInvoice_PaymentMethodTranchedStream_StatusOngoing() - external - whenInvoiceNotAlreadyPaid - whenInvoiceNotCanceled - givenPaymentMethodTranchedStream - givenInvoiceStatusOngoing - whenSenderInitialStreamSender - { - // Set current invoice as a tranched stream-based one - uint256 invoiceId = 4; - - // The invoice must be paid for its status to be updated to `Ongoing` - // Make Bob the payer of the invoice (also Bob will be the initial stream sender) - vm.startPrank({ msgSender: users.bob }); - - // Approve the {InvoiceModule} to transfer the USDT tokens on Bob's behalf - usdt.approve({ spender: address(invoiceModule), amount: invoices[invoiceId].payment.amount }); - - // Pay the invoice first (status will be updated to `Ongoing`) - invoiceModule.payInvoice{ value: invoices[invoiceId].payment.amount }({ id: invoiceId }); - - // Expect the {InvoiceCanceled} event to be emitted - vm.expectEmit(); - emit Events.InvoiceCanceled({ id: invoiceId }); - - // Run the test - invoiceModule.cancelInvoice({ id: invoiceId }); - - // Assert the actual and expected invoice status - Types.Invoice memory invoice = invoiceModule.getInvoice({ id: invoiceId }); - assertEq(uint8(invoice.status), uint8(Types.Status.Canceled)); - } -} diff --git a/test/integration/concrete/invoice-module/cancel-invoice/cancelInvoice.tree b/test/integration/concrete/invoice-module/cancel-invoice/cancelInvoice.tree deleted file mode 100644 index 58202e3..0000000 --- a/test/integration/concrete/invoice-module/cancel-invoice/cancelInvoice.tree +++ /dev/null @@ -1,40 +0,0 @@ -cancelInvoice.t.sol -├── when the invoice status IS Paid -│ └── it should revert with the {CannotCancelPaidInvoice} error -└── when the invoice status IS NOT Paid - ├── when the invoice status IS Canceled - │ └── it should revert with the {InvoiceAlreadyCanceled} error - └── when the invoice status IS NOT Canceled - ├── given the payment method is transfer - │ ├── when the sender IS NOT the invoice recipient - │ │ └── it should revert with the {InvoiceOwnerUnauthorized} - │ └── when the sender IS the invoice recipient - │ ├── it should mark the invoice as Canceled - │ └── it should emit an {InvoiceCanceled} event - ├── given the payment method is linear stream-based - │ ├── given the invoice status is Pending - │ │ ├── when the sender IS NOT the invoice recipient - │ │ │ └── it should revert with the {InvoiceOwnerUnauthorized} - │ │ └── when the sender IS the invoice recipient - │ │ ├── it should mark the invoice as Canceled - │ │ └── it should emit an {InvoiceCanceled} event - │ └── given the invoice status is Ongoing - │ ├── when the sender IS NOT the initial stream sender - │ │ └── it should revert with the {OnlyInitialStreamSender} error - │ └── when the sender IS the initial stream sender - │ ├── it should mark the invoice as Canceled - │ └── it should emit an {InvoiceCanceled} event - └── given the payment method is tranched stream-based - ├── given the invoice status is Pending - │ ├── when the sender IS NOT the invoice recipient - │ │ └── it should revert with the {InvoiceOwnerUnauthorized} - │ └── when the sender IS the invoice recipient - │ ├── it should mark the invoice as Canceled - │ └── it should emit an {InvoiceCanceled} event - └── given the invoice status is Ongoing - ├── when the sender IS NOT the initial stream sender - │ └──it should revert with the {OnlyInitialStreamSender} error - └── when the sender IS the initial stream sender - ├── it should mark the invoice as Canceled - └── it should emit an {InvoiceCanceled} event - diff --git a/test/integration/concrete/invoice-module/pay-invoice/payInvoice.t.sol b/test/integration/concrete/invoice-module/pay-invoice/payInvoice.t.sol index 45ca0e6..996b248 100644 --- a/test/integration/concrete/invoice-module/pay-invoice/payInvoice.t.sol +++ b/test/integration/concrete/invoice-module/pay-invoice/payInvoice.t.sol @@ -264,7 +264,7 @@ contract PayInvoice_Integration_Concret_Test is PayInvoice_Integration_Shared_Te // Assert the actual and the expected state of the Sablier v2 linear stream LockupLinear.StreamLL memory stream = invoiceModule.getLinearStream({ streamId: 1 }); - assertEq(stream.sender, address(invoiceModule)); + assertEq(stream.sender, users.bob); assertEq(stream.recipient, users.eve); assertEq(address(stream.asset), address(usdt)); assertEq(stream.startTime, invoice.startTime); @@ -316,7 +316,7 @@ contract PayInvoice_Integration_Concret_Test is PayInvoice_Integration_Shared_Te // Assert the actual and the expected state of the Sablier v2 tranched stream LockupTranched.StreamLT memory stream = invoiceModule.getTranchedStream({ streamId: 1 }); - assertEq(stream.sender, address(invoiceModule)); + assertEq(stream.sender, users.bob); assertEq(stream.recipient, users.eve); assertEq(address(stream.asset), address(usdt)); assertEq(stream.startTime, invoice.startTime); diff --git a/test/integration/shared/cancelInvoice.t.sol b/test/integration/shared/cancelInvoice.t.sol deleted file mode 100644 index e8cfcf2..0000000 --- a/test/integration/shared/cancelInvoice.t.sol +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.26; - -import { Integration_Test } from "../Integration.t.sol"; -import { PayInvoice_Integration_Shared_Test } from "./payInvoice.t.sol"; - -abstract contract CancelInvoice_Integration_Shared_Test is Integration_Test, PayInvoice_Integration_Shared_Test { - function setUp() public virtual override(Integration_Test, PayInvoice_Integration_Shared_Test) { - PayInvoice_Integration_Shared_Test.setUp(); - } - - modifier whenInvoiceStatusNotPaid() { - _; - } - - modifier whenInvoiceStatusNotCanceled() { - _; - } - - modifier whenSenderInvoiceRecipient() { - _; - } - - modifier givenInvoiceStatusPending() { - _; - } - - modifier givenInvoiceStatusOngoing() { - _; - } - - modifier whenSenderInitialStreamSender() { - _; - } -} diff --git a/test/utils/Errors.sol b/test/utils/Errors.sol index 697f76d..13ce956 100644 --- a/test/utils/Errors.sol +++ b/test/utils/Errors.sol @@ -83,22 +83,10 @@ library Errors { /// @notice Thrown when a tranched stream has a one-off recurrence type error TranchedStreamInvalidOneOffRecurence(); - /// @notice Thrown when an attempt is made to cancel an already paid invoice - error CannotCancelPaidInvoice(); - - /// @notice Thrown when an attempt is made to cancel an already canceled invoice - error InvoiceAlreadyCanceled(); - - /// @notice Thrown when the caller is not the initial stream sender - error OnlyInitialStreamSender(address initialSender); - /*////////////////////////////////////////////////////////////////////////// STREAM-MANAGER //////////////////////////////////////////////////////////////////////////*/ /// @notice Thrown when the caller is not the broker admin error OnlyBrokerAdmin(); - - /// @notice Thrown when `msg.sender` is not the stream's sender - error SablierV2Lockup_Unauthorized(uint256 streamId, address caller); }