Skip to content

Latest commit

 

History

History
411 lines (321 loc) · 9.88 KB

File metadata and controls

411 lines (321 loc) · 9.88 KB

StreamPay API v0.2.0 - Quick Reference

Core Operations

create_stream

Create a new payment stream with optional time limit.

pub fn create_stream(
    env: Env,
    payer: Address,
    recipient: Address,
    rate_per_second: i128,
    initial_balance: i128,
    end_time: u64  // 0 = unlimited; > 0 = auto-terminate timestamp
) -> u32

Requires: Payer authentication Returns: Stream ID (u32) Errors: Panics if rate or balance is non-positive

Example:

// Unlimited stream
let stream_id = client.create_stream(
    &payer, &recipient, &100, &10_000, &0
);

// Time-limited (7 day subscription)
let end_time = current_time + 7 * 24 * 3600;
let stream_id = client.create_stream(
    &payer, &recipient, &100, &10_000, &end_time
);

start_stream

Start an inactive stream.

pub fn start_stream(env: Env, stream_id: u32)

Requires: Payer authentication Returns: Nothing Errors:

  • "stream already active" - Cannot start active stream
  • "end_time must be in the future" - end_time in past

Behavior:

  • Sets start_time = now
  • Sets is_active = true
  • Clears paused_at (if paused)
  • Validates end_time > now if set

Example:

client.start_stream(&stream_id);  // Begin accrual

settle_stream

Calculate and deduct accrued amounts (internal accounting).

pub fn settle_stream(env: Env, stream_id: u32) -> i128

Requires: No authorization Returns: Amount settled (i128) Errors: Panics if stream not found

Behavior:

  • Computes accrued: (time_elapsed) * rate_per_second
  • Caps at balance (no overpayment)
  • Deducts from balance
  • Respects paused_at (uses pause time if paused)
  • Respects end_time (caps accrual, auto-deactivates at end)

Example:

let accrued = client.settle_stream(&stream_id);
println!("Amount settled: {}", accrued);  // In base units

Paused Behavior:

client.pause_stream(&stream_id);
env.advance_time(1000);  // Pass time
let accrued = client.settle_stream(&stream_id);
assert_eq!(accrued, 0);  // No new accrual while paused

New: Cancel Stream (Issue #4)

cancel_stream

Cancel active stream and settle accrued amounts immediately.

pub fn cancel_stream(env: Env, stream_id: u32)

Requires: Payer authentication Returns: Nothing
Errors: "cannot cancel inactive stream"

Behavior:

  • Immediately settles accrued amounts
  • Recipient receives accrued amount
  • Payer retains remaining balance
  • Stream marked inactive
  • Atomic operation (prevents races)

Example:

// 10s into a 100/s stream
client.cancel_stream(&stream_id);  // Settles 1000, leaves 9000
let info = client.get_stream_info(&stream_id);
assert!(!info.is_active);
assert_eq!(info.balance, 9000);

New: Pause/Resume

pause_stream

Freeze accrual without terminating the stream.

pub fn pause_stream(env: Env, stream_id: u32)

Requires: Payer authentication Returns: Nothing Errors:

  • "cannot pause inactive stream"
  • "stream already paused"

Behavior:

  • Settles accrued amount to pause point
  • Sets paused_at = now
  • Keeps is_active = true
  • Accrual stops until resumed

Example:

client.start_stream(&stream_id);
env.advance_time(5);
client.pause_stream(&stream_id);  // Settles 500

// 1 hour passes, stream doesn't accrue
env.advance_time(3600);

client.resume_stream(&stream_id);
env.advance_time(2);
let accrued = client.settle_stream(&stream_id);  // Only 200

resume_stream

Restart accrual from a paused stream.

pub fn resume_stream(env: Env, stream_id: u32)

Requires: Payer authentication Returns: Nothing Errors:

  • "cannot resume inactive stream"
  • "stream is not paused"

Behavior:

  • Clears paused_at
  • Resets start_time = now (for future accrual calculation)
  • Keeps is_active = true
  • Accrual resumes

Example:

// After pause
client.resume_stream(&stream_id);
env.advance_time(3);
let accrued = client.settle_stream(&stream_id);  // 300 accrued

Existing Operations (Enhanced)

stop_stream

Permanently stop an active stream.

pub fn stop_stream(env: Env, stream_id: u32)

Enhanced in v0.2.0:

  • Now clears paused_at when stopping

Example:

client.stop_stream(&stream_id);  // Final stop
let info = client.get_stream_info(&stream_id);
assert!(!info.is_active);

get_stream_info

Retrieve stream details (read-only).

pub fn get_stream_info(env: Env, stream_id: u32) -> StreamInfo

Returns: StreamInfo struct

StreamInfo Fields:

pub struct StreamInfo {
    pub payer: Address,              // Payer address
    pub recipient: Address,          // Recipient address
    pub rate_per_second: i128,       // Payment rate (base units/s)
    pub balance: i128,               // Remaining balance
    pub start_time: u64,             // Stream start or resume timestamp
    pub end_time: u64,               // Auto-terminate time (0 = unlimited)
    pub is_active: bool,             // Active vs. stopped
    pub paused_at: u64,              // Pause timestamp (0 = not paused)
}

Example:

let info = client.get_stream_info(&stream_id);
if info.paused_at > 0 {
    println!("Stream paused at {}", info.paused_at);
} else if info.is_active {
    println!("Stream active, balance: {}", info.balance);
} else {
    println!("Stream stopped");
}

archive_stream

Remove a fully-settled, inactive stream from storage.

pub fn archive_stream(env: Env, stream_id: u32)

Requires: Payer authentication
Returns: Nothing Errors:

  • "cannot archive active stream"
  • "cannot archive stream with unsettled balance"

Behavior:

  • Removes stream from persistent storage
  • Must be stopped and fully settled (balance = 0)
  • Protects recipient entitlements

Example:

client.stop_stream(&stream_id);
let accrued = client.settle_stream(&stream_id);  // Drain balance
client.archive_stream(&stream_id);  // Remove from storage

// get_stream_info now panics (stream not found)

version

Get contract version.

pub fn version(_env: Env) -> u32

Returns: Version as u32 (major1M + minor1k + patch) Current: 2_000 (v0.2.0)

Example:

let v = client.version();
assert_eq!(v, 2_000);  // v0.2.0

State Transitions

Valid Transitions

[Created]
    ↓ start_stream
[Active]
    ├─→ pause_stream → [Active, Paused]
    │                      ↓ resume_stream → [Active]
    ├─→ settle_stream → [Active or Inactive] (depending on balance/end_time)
    ├─→ cancel_stream → [Cancelled/Inactive]
    └─→ stop_stream → [Inactive]

[Inactive]
    ├─→ start_stream → [Active]  (if not created yet)
    └─→ archive_stream → [Archived/Removed]

Error Guide

Error Message Function Cause Fix
"rate and balance must be positive" create_stream Non-positive rate/balance Use positive values
"stream already active" start_stream Stream already started Call start_stream once
"end_time must be in the future" start_stream end_time ≤ now Use future timestamp
"cannot cancel inactive stream" cancel_stream Stream not active Start stream first
"cannot pause inactive stream" pause_stream Stream not active Start stream first
"stream already paused" pause_stream Already paused Resume first
"cannot resume inactive stream" resume_stream Stream not active Start stream first
"stream is not paused" resume_stream Not paused Pause first
"stream not active" stop_stream Stream already stopped Call once
"cannot archive active stream" archive_stream Stream still running Stop first
"cannot archive stream with unsettled balance" archive_stream Balance not zero Settle first
"stream not found" Any Stream ID invalid Check ID

Common Patterns

Pattern 1: Simple Unlimited Stream

let stream_id = client.create_stream(&payer, &recipient, &100, &1000, &0);
client.start_stream(&stream_id);
env.advance_time(5);
let accrued = client.settle_stream(&stream_id);  // 500
client.stop_stream(&stream_id);
client.archive_stream(&stream_id);

Pattern 2: Pause/Resume Workflow

// Setup
let stream_id = client.create_stream(&payer, &recipient, &50, &500, &0);
client.start_stream(&stream_id);

// Pause during off-hours
client.pause_stream(&stream_id);

// Hours later...
client.resume_stream(&stream_id);

// Cleanup
client.stop_stream(&stream_id);
client.settle_stream(&stream_id);
client.archive_stream(&stream_id);

Pattern 3: Time-Limited Subscription

let end_time = now + 30 * 24 * 3600;  // 30 days
let stream_id = client.create_stream(&payer, &recipient, &3_33, &3_000, &end_time);
client.start_stream(&stream_id);

// Settles over 30 days
// After 30 days, auto-deactivates
env.advance_time(30 * 24 * 3600);
let accrued = client.settle_stream(&stream_id);  // ~3000
let info = client.get_stream_info(&stream_id);
assert!(!info.is_active);  // Auto-deactivated

Pattern 4: Early Cancellation with Refund

let stream_id = client.create_stream(&payer, &recipient, &100, &1000, &0);
client.start_stream(&stream_id);
env.advance_time(3);
let balance_before = client.get_stream_info(&stream_id).balance;

client.cancel_stream(&stream_id);  // Atomic: settle + deactivate

let info = client.get_stream_info(&stream_id);
assert_eq!(info.balance, balance_before - 300);  // Recipient got 300
assert!(!info.is_active);

Version History

Version Features Breaking Changes
0.1.0 Basic streaming None (initial)
0.2.0 Cancel, Pause/Resume, End Time create_stream signature

Documentation: See docs/ for detailed specifications Tests: Run cargo test --lib for 27 comprehensive tests