11# Streaming Payments Contract
22
3- A private streaming payments contract for Aztec that enables:
3+ A streaming payments contract for Aztec that enables:
44- Salary streaming
55- Token vesting
66- Subscription payments
77
8- All with full privacy - amounts, schedules, and participant identities remain hidden .
8+ Token balances remain private while stream metadata is stored publicly for both parties to access .
99
1010## Features
1111
1212- ** Linear Vesting** : Tokens unlock linearly from start to end time
1313- ** Cliff Period** : Optional cliff before which no tokens can be withdrawn
14- - ** Private Streams** : Stream details stored in private notes
14+ - ** Public Stream Registry** : Stream parameters stored publicly so both sender and recipient can interact
15+ - ** Private Token Balances** : Actual token amounts remain in private balances
1516- ** Cancellation** : Sender can cancel and reclaim unvested tokens
1617- ** Partial Withdrawals** : Recipient can withdraw any unlocked amount
1718- ** Full Token Integration** : Uses the defi-wonderland/aztec-standards Token contract
@@ -22,24 +23,24 @@ All with full privacy - amounts, schedules, and participant identities remain hi
2223
2324```
2425Storage:
25- ├── token: PublicImmutable<AztecAddress> # Token contract address
26- └── streams: Map<Field, Map<AztecAddress, Owned<PrivateMutable<StreamNote>>>>
26+ ├── token: PublicImmutable<AztecAddress> # Token contract address
27+ └── streams: Map<Field, PublicMutable<StreamData>> # Public stream registry
2728```
2829
29- ### StreamNote
30+ ### StreamData
3031
31- Each stream is represented as a private note containing :
32+ Each stream is stored publicly with the following fields :
3233
3334| Field | Type | Description |
3435| -------| ------| -------------|
35- | stream_id | Field | Unique identifier |
36- | sender | AztecAddress | Stream creator |
36+ | sender | AztecAddress | Stream creator (can cancel) |
37+ | recipient | AztecAddress | Token recipient |
3738| total_amount | u128 | Total tokens to stream |
3839| start_time | u64 | When streaming begins |
3940| end_time | u64 | When fully vested |
4041| cliff_time | u64 | No withdrawals before this |
4142| claimed_amount | u128 | Already withdrawn |
42- | owner | AztecAddress | Recipient (note owner) |
43+ | cancelled | bool | Whether stream was cancelled |
4344
4445### Key Functions
4546
@@ -48,22 +49,30 @@ Each stream is represented as a private note containing:
4849| ` constructor(token) ` | public | Initialize with token address |
4950| ` create_stream(...) ` | private | Create a new stream (requires authwit) |
5051| ` withdraw(stream_id, amount) ` | private | Withdraw unlocked tokens |
51- | ` cancel_stream(stream_id, recipient, unvested ) ` | private | Cancel and reclaim unvested |
52+ | ` cancel_stream(stream_id, unvested_amount ) ` | private | Cancel and reclaim unvested |
5253| ` get_stream_info(...) ` | utility | View stream details |
5354| ` get_withdrawable(...) ` | utility | Calculate withdrawable amount |
5455| ` get_unvested(...) ` | utility | Calculate unvested amount |
5556
56- ## Privacy Properties
57+ ## Privacy Model
5758
5859** Private** (hidden from observers):
59- - Total stream amounts
60- - Vesting schedules
61- - Sender and recipient identities
62- - Withdrawal amounts and timing
60+ - Token balances (sender's source, recipient's destination)
61+ - Individual withdrawal/cancellation amounts going to private balances
6362
6463** Public** (visible on-chain):
65- - Stream existence (via nullifiers)
66- - Token contract address
64+ - Stream existence and parameters
65+ - Sender and recipient addresses
66+ - Vesting schedule (start, end, cliff times)
67+ - Total stream amount
68+ - Claimed amount and cancellation status
69+
70+ ### Design Rationale
71+
72+ The public stream registry approach was chosen because:
73+ 1 . ** Both parties need access** : The sender needs to cancel, the recipient needs to withdraw
74+ 2 . ** Note ownership limitation** : In Aztec, only the note owner can nullify their notes
75+ 3 . ** Practical privacy** : For most use cases (payroll, vesting), stream existence isn't secret - what matters is keeping actual balances private
6776
6877## Usage
6978
@@ -148,7 +157,9 @@ npm test
148157
1491587 . ** Cancel** (optional): Sender cancels and reclaims unvested tokens
150159 ```
151- cancel_stream(stream_id, recipient, unvested_amount)
160+ // First query unvested amount
161+ let unvested = get_unvested(stream_id, current_time)
162+ cancel_stream(stream_id, unvested)
152163 ```
153164
154165## Linear Vesting Formula
@@ -200,8 +211,8 @@ streaming-payments/
200211│ ├── Nargo.toml # Contract dependencies
201212│ └── src/
202213│ ├── main.nr # Main contract
203- │ ├── stream_note .nr # StreamNote type
204- │ └── lib .nr # Pure functions + tests
214+ │ ├── lib .nr # StreamData type + pure functions + tests
215+ │ └── stream_note .nr # Legacy StreamNote type (unused)
205216├── scripts/
206217│ └── setup-token.sh # Setup script
207218├── tests/
@@ -221,4 +232,4 @@ streaming-payments/
221232
222233- ** Token Contract** : For actual token transfers
223234- ** Crowdfunding** : Similar time-based private payments
224- - ** Private Voting** : Uses similar note replacement patterns
235+ - ** Private Voting** : Uses similar public/private hybrid patterns
0 commit comments