Skip to content

Commit

Permalink
refactor: extract payment logic from 'InvoiceModule' to 'PaymentModule'
Browse files Browse the repository at this point in the history
  • Loading branch information
gabrielstoica committed Nov 7, 2024
1 parent d05a473 commit 8b1dfa4
Show file tree
Hide file tree
Showing 10 changed files with 578 additions and 507 deletions.
383 changes: 46 additions & 337 deletions src/modules/invoice-module/InvoiceModule.sol

Large diffs are not rendered by default.

89 changes: 4 additions & 85 deletions src/modules/invoice-module/interfaces/IInvoiceModule.sol
Original file line number Diff line number Diff line change
@@ -1,96 +1,15 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.26;

import { Types } from "./../libraries/Types.sol";

/// @title IInvoiceModule
/// @notice Contract module that provides functionalities to issue and pay an on-chain invoice
interface IInvoiceModule {
/*//////////////////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Emitted when an invoice is created
/// @param id The ID of the invoice
/// @param recipient The address receiving the payment
/// @param status The status of the invoice
/// @param startTime The timestamp when the invoice takes effect
/// @param endTime The timestamp by which the invoice must be paid
/// @param payment Struct representing the payment details associated with the invoice
event InvoiceCreated(
uint256 id,
address indexed recipient,
Types.Status status,
uint40 startTime,
uint40 endTime,
Types.Payment payment
);

/// @notice Emitted when an invoice is paid
/// @param id The ID of the invoice
/// @param payer The address of the payer
/// @param status The status of the invoice
/// @param payment Struct representing the payment details associated with the invoice
event InvoicePaid(uint256 indexed id, address indexed payer, Types.Status status, Types.Payment payment);

/// @notice Emitted when an invoice is canceled
/// @param id The ID of the invoice
event InvoiceCanceled(uint256 indexed id);

/*//////////////////////////////////////////////////////////////////////////
CONSTANT FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Retrieves the details of the `id` invoice
/// @param id The ID of the invoice for which to get the details
function getInvoice(uint256 id) external view returns (Types.Invoice memory invoice);

/*//////////////////////////////////////////////////////////////////////////
NON-CONSTANT FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Creates a new invoice
///
/// Requirements:
/// - `msg.sender` must be a contract implementing the {ISpace} interface
///
/// Notes:
/// - `recipient` is not checked because the call is enforced to be made through a {Space} contract
///
/// @param invoice The details of the invoice following the {Invoice} struct format
/// @return id The on-chain ID of the invoice
function createInvoice(Types.Invoice calldata invoice) external returns (uint256 id);

/// @notice Pays a transfer-based invoice
///
/// Notes:
/// - `msg.sender` is enforced to be a specific payer address
///
/// @param id The ID of the invoice to pay
function payInvoice(uint256 id) external payable;

/// @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
///
/// Important:
/// - if the invoice has a linear or tranched stream payment method, the portion that has already
/// been streamed is NOT automatically transferred
///
/// @param id The ID of the invoice
function cancelInvoice(uint256 id) external;

/// @notice Withdraws the maximum withdrawable amount from the stream associated with the `id` invoice
///
/// Notes:
/// - reverts if `msg.sender` is not the stream recipient
/// - reverts if the payment method of the `id` invoice is not linear or tranched stream based
///
/// @param id The ID of the invoice
function withdrawInvoiceStream(uint256 id) external returns (uint128 withdrawnAmount);
/// @notice Creates an on-chain representation of an off-chain invoice by minting an ERC-721 token
/// @param to The address to which the NFT will be minted
/// @param paymentRequestId The ID of the payment request to which this invoice belongs
function mint(address to, uint256 paymentRequestId) external;
}
65 changes: 3 additions & 62 deletions src/modules/invoice-module/libraries/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,71 +2,12 @@
pragma solidity ^0.8.26;

/// @title Errors
/// @notice Library containing all custom errors the {InvoiceModule} and {StreamManager} may revert with
/// @notice Library containing all custom errors the {InvoiceModule} may revert with
library Errors {
/*//////////////////////////////////////////////////////////////////////////
INVOICE-MODULE
//////////////////////////////////////////////////////////////////////////*/

/// @notice Thrown when the caller is an invalid zero code contract or EOA
error SpaceZeroCodeSize();

/// @notice Thrown when the caller is a contract that does not implement the {ISpace} interface
error SpaceUnsupportedInterface();

/// @notice Thrown when the end time of an invoice is in the past
error EndTimeInThePast();

/// @notice Thrown when the start time is later than the end time
error StartTimeGreaterThanEndTime();

/// @notice Thrown when the payment amount set for a new invoice is zero
error ZeroPaymentAmount();

/// @notice Thrown when the payment amount is less than the invoice value
error PaymentAmountLessThanInvoiceValue(uint256 amount);

/// @notice Thrown when a payment in the native token (ETH) fails
error NativeTokenPaymentFailed();

/// @notice Thrown when a linear or tranched stream is created with the native token as the payment asset
error OnlyERC20StreamsAllowed();

/// @notice Thrown when a payer attempts to pay an invoice that has already been paid
error InvoiceAlreadyPaid();

/// @notice Thrown when a payer attempts to pay a canceled invoice
error InvoiceCanceled();

/// @notice Thrown when the invoice ID references a null invoice
error InvoiceNull();

/// @notice Thrown when `msg.sender` attempts to withdraw from an invoice that is not stream-based
error InvoiceNotStreamBased();

/// @notice Thrown when `msg.sender` is not the invoice recipient
error OnlyInvoiceRecipient();

/// @notice Thrown when the payment interval (endTime - startTime) is too short for the selected recurrence
/// i.e. recurrence is set to weekly but interval is shorter than 1 week
error PaymentIntervalTooShortForSelectedRecurrence();

/// @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 the caller is unathorized to execute a call
error Unathorized();
}
Loading

0 comments on commit 8b1dfa4

Please sign in to comment.