XSC003 Streaming Payments
- Inspired by protocols like Superfluid which solve the problem of recurring payments using cryptocurrencies.
- Allows a user to create a stream of payments to a receiver, with a rate and a time window.
- Supports opening a payment stream using a
XSC002 permit
. - Xian can recommend this feature as the base token standard, enabling subscriptions & recurring payments as standard on all tokens.
How it works :
The provided methods facilitate the creation and management of payment streams within a smart contract. These streams allow for continuous, time-based payments between parties, defined by a rate and a specific time window. The functionality supports both direct transactions by the sender and transactions authorized through cryptographic permits.
User Stories :
As a sender I want to be able to:
- open a stream to a receiver with a rate and a time window
- close a stream to a receiver, fulfilling outstanding claims to the receiver
- update a stream to a receiver.
- open multiple streams to a receiver
- open multiple streams to multiple receivers
As a receiver I want to be able to:
- claim funds due to me from a sender
- finalize a stream from a sender
- forfeit a stream from a sender
Note on time arguments (begins, closes, deadline, etc) :
All time arguments must be in the format of %Y-%m-%d %H:%M:%S
e.g 2023-01-01 10:00:00
Method: create_stream
create_stream(receiver: str, rate: float, begins: str, closes: str)
Overview
The create_stream
method facilitates the creation of a new payment stream from the sender to a receiver. This method allows for flexible scheduling of the stream, which can be set to start at any point in the past, present, or future.
Functionality
This method performs the following operations to establish a new payment stream:
- Sender Identification:
- The sender of the stream is automatically identified as the caller of the function (ctx.caller).
- Stream Creation:
- The method calls
perform_create_stream
, an internal function that handles the logic for setting up the stream. This includes generating a unique stream ID and validating the parameters such as the start and end times, ensuring the rate is positive, and checking that the stream does not already exist.
- The method calls
- Return Value:
- The unique stream ID is returned, providing a reference to the newly created stream. This method simplifies the process of initiating a payment stream, making it accessible for users to set up scheduled payments to other parties within the smart contract environment.
Method : create_stream_from_permit
create_stream_from_permit(sender: str, receiver: str, rate: float, begins: str, closes: str, deadline: str, signature: str)
Overview
The create_stream_from_permit method enables the creation of a payment stream based on a cryptographic permit, which includes a signature that must be verified before the stream can be established. This method allows for secure and verified transactions, ensuring that the stream is initiated based on pre-approved permissions.
Functionality
This method performs the following operations to establish a new payment stream using a cryptographic permit:
- Checks the the deadline has not passed.
- Permit Message Construction:
- Constructs a permit message using the sender, receiver, rate, and the specified time window (begins and closes). This message is then hashed to create a unique identifier for the permit.
- Permit Verification:
- Uniqueness Check: Ensures that the permit has not been used before by checking the hashed permit message against a record of used permits.
- Signature Verification:
- Validates the signature using cryptographic methods to ensure that it was indeed signed by the sender, confirming their intent to create the stream.
- Permit Registration:
- Marks the permit as used in the system to prevent reuse, ensuring the integrity of the permit system.
- Stream Creation:
- Calls perform_create_stream to handle the actual creation of the stream using the validated parameters. This internal function ensures that the stream is set up correctly with all necessary validations.
- Return Value:
- Returns the unique stream ID generated during the stream creation process, providing a reference to the newly established stream.
Method : balance_stream
balance_stream(stream_id: str)
Overview
The balance_stream method is designed to facilitate the transfer of funds between a sender and a receiver based on the terms of an active payment stream. This method can be invoked by either the sender or the receiver to calculate and transfer the amount due at the time of the call.
Functionality
This method ensures that payment streams are managed fairly and transparently, allowing parties to claim due funds based on the agreed-upon terms of the stream.
- Existence and Status Check: It first verifies that the stream identified by stream_id exists and is currently active. If the stream does not exist or is not active, the method will raise an assertion error.
- Time Check: It checks that the current time (now) is after the stream's start time (BEGIN_KEY). This ensures that no funds are transferred before the stream is supposed to begin.
- Caller Validation: Only the sender or the receiver associated with the stream can call this method to balance the stream. This is enforced by checking if the caller (ctx.caller) is either the sender or the receiver.
- Calculation of Amount Due: The method calculates the outstanding balance that can be claimed at the time of the call. This is done by determining the amount due from the start of the stream or the last claim, up to the current time or the end of the stream, whichever is earlier.
- Claimable Amount: It then calculates the actual amount that can be claimed, which is the lesser of the outstanding balance or the sender's current balance. This prevents attempting to claim more than the sender has.
- Transfer of Funds: The calculated claimable amount is then transferred from the sender's balance to the receiver's balance. This updates the balances of both parties.
- Update Claimed Amount: The amount claimed is recorded in the stream's data under CLAIMED_KEY to keep track of the total amount that has been transferred over the life of the stream.
- Return Statement: Finally, the method returns a message indicating the amount of tokens claimed from the stream, providing a clear confirmation of the transaction.
Method : change_close_time
change_close_time(stream_id: str, new_close_time: str)
Overview
The change_close_time method allows the sender of a payment stream to adjust the closing time of an active stream. This functionality is crucial for extending or shortening the duration of a payment stream based on new agreements or circumstances.
Functionality
This method performs several operations to ensure the proper management of the stream's lifecycle:
- Stream Existence and Status Check:
- It first checks that the stream identified by
stream_id
exists and is active. If the stream does not exist or is not active, an assertion error is raised.
- It first checks that the stream identified by
- Sender Authorization:
- The method ensures that only the sender of the stream can change its closing time. This is enforced by verifying that the caller (
ctx.caller
) is the sender recorded in the stream's data.
- The method ensures that only the sender of the stream can change its closing time. This is enforced by verifying that the caller (
- Adjusting Close Time:
- If the new closing time is before the stream's start time and the current time is also before the stream's start time, the closing time is set to the start time. This effectively invalidates the stream by making it non-operational from the start.
- If the new closing time is in the past or equal to the current time, the stream is closed immediately by setting the closing time to the current time (
now
). - Otherwise, the closing time is updated to the new specified time. This allows for the extension or reduction of the stream's duration based on the new closing time.
- Return Statement:
- The method returns a message indicating the new closing time of the stream, providing clear feedback on the operation performed.
Method : finalize_stream
finalize_stream(stream_id: str)
Overview
The finalize_stream method is designed to formally close a payment stream between a sender and a receiver. This method ensures that all due balances are settled and that the stream is properly closed without any pending obligations.
Functionality
This method performs several critical checks and operations to ensure the stream is correctly finalized:
- Stream Existence and Status Check:
- It verifies that the stream identified by
stream_id
exists and is currently active. If the stream does not exist or is not active, an assertion error is raised.
- It verifies that the stream identified by
- Caller Authorization:
- The method ensures that only the sender or the receiver associated with the stream can finalize it. This is enforced by checking if the caller (
ctx.caller
) is either the sender or the receiver.
- The method ensures that only the sender or the receiver associated with the stream can finalize it. This is enforced by checking if the caller (
- Time Check:
- It checks that the current time (
now
) is after or exactly at the stream's closing time (CLOSE_KEY
). This ensures that the stream is not finalized prematurely.
- It checks that the current time (
- Balance Settlement:
- Before finalizing, the method calculates the outstanding balance using
calc_outstanding_balance
, which considers the stream's start time, closing time, rate, and already claimed amount. It asserts that the outstanding balance must be zero, indicating that all due payments have been settled and claimed. This prevents the finalization of streams that still have pending payments.
- Before finalizing, the method calculates the outstanding balance using
- Stream Status Update:
- Once all checks are passed, the stream's status is updated to
STREAM_FINALIZED
, indicating that it is officially closed and cannot be reactivated.
- Once all checks are passed, the stream's status is updated to
- Return Statement:
- The method returns a message confirming the finalization of the stream, providing clear feedback on the operation performed.
Method : forfeit_stream
forfeit_stream(stream_id: str)
Overview
The forfeit_stream method allows a receiver to voluntarily forfeit an active payment stream. This action effectively terminates the stream, marking it as forfeited and relinquishing any claims the receiver might have had.
Functionality
This method performs several operations to ensure the stream is correctly forfeited:
- Stream Existence and Status Check: -It first verifies that the stream identified by
stream_id
exists and is currently active. If the stream does not exist or is not active, an assertion error is raised. - Receiver Authorization:
- The method ensures that only the receiver associated with the stream can forfeit it. This is enforced by checking if the caller (
ctx.caller
) is the receiver recorded in the stream's data.
- The method ensures that only the receiver associated with the stream can forfeit it. This is enforced by checking if the caller (
- Stream Status Update:
- The stream's status is updated to
STREAM_FORFEIT
, indicating that it has been voluntarily terminated by the receiver. Additionally, the closing time of the stream is set to the current time (now
), marking the exact moment the stream was forfeited.
- The stream's status is updated to
- Return Statement:
- The method returns a message confirming that the stream has been forfeited, providing clear feedback on the operation performed.
How to test :
- Setup testing harness by following the instructions in the contract dev environment
- Clone this repo to
contracts
- Run
pytest
in the root directory of the repo