How it Works
Step-by-step lifecycle from pool creation to atomic settlement on Soroban.
The SplitPay protocol follows a deterministic lifecycle designed to guarantee that no payment can be settled unless the pool configuration is strictly valid.
Protocol Lifecycle Overview
Step 1: Pool Creation
The creator calls create_pool(pool_id, owner, asset). The contract verifies that the caller authorized the transaction and that the pool ID does not already exist. The pool is initialized in the Active status with an empty member roster.
let pool = Pool { id: pool_id, owner: owner.clone(), asset: asset.clone(), status: PoolStatus::Active, created_at: env.ledger().timestamp(),};set_pool(&env, &pool);events::pool_created(&env, pool_id, &owner, &asset, created_at);Step 2: Member & Share Configuration
The pool owner registers member addresses and their percentage shares in basis points using add_member(pool_id, address, share_bps).
Step 3: Payment Creation
A payer initiates a payment targeting the pool using create_payment(payment_id, pool_id, payer, amount). The contract asserts:
- Payer authorized the call (
payer.require_auth()). - Amount is strictly greater than zero (
amount > 0). - The pool status is
PoolStatus::Active. - Total configured member shares currently equal 10,000 BPS.
Step 4: Atomic Settlement & Snapshot
Calling settle_payment(payment_id) triggers the settlement engine:
- Validates that the payment has not already been settled (
Error::PaymentAlreadySettled). - Snapshots the member shares at that exact ledger timestamp.
- Computes integer allocations using checked arithmetic and allocates any remainder stroops to member index 0.
- Invokes the Stellar Asset Contract to pull total funds from the payer to the contract address.
- Dispatches individual token transfers to each member address.
- Persists a
Distributionrecord for each recipient and marks the paymentSettled.
Step 5: Querying Historical Distributions
Anyone can query historical distributions using get_distributions(payment_id) or inspect individual allocations with get_distribution(payment_id, recipient). Even if the pool configuration is modified later, historical distributions remain completely unchanged.