Appearance
v1.8 Advanced Billing Features (Active)
Started: 2026-04-15 | Phases: 3 (45–47) | Plans: 47 | Status: Phase 46 complete, Phase 47 in progress
The most ambitious milestone by plans per phase (47 across 3 phases). v1.8 builds the three major billing features that v1.7's infrastructure was designed to support: streaming payments, plan upgrades with multi-token, and a webhook SDK with staging acceptance testing.
Scope
Phase 45: Streaming Engine (COMPLETE)
The first net-new billing primitive since v1.0's periodic pulls.
- StreamMandate PDA — A new mandate type for continuous payment streams
authorized_max_rate— Rate-per-second authorization (not a fixed amount)- 56-byte reserved tail for future extensions
- 6 streaming instructions:
create_stream— Establish a payment stream between payer and payeeexecute_stream— Pull-based settlement at the authorized ratepause_stream/resume_stream— Temporary stream suspensionupdate_stream_rate— Change the authorized rate on an active streamcancel_stream— Permanently end a stream with final settlement
- Settle-then-mutate invariant — All stream operations settle accrued amounts before modifying state, enforced by a
pub(crate)helper - Transfer hook streaming branch — Slot 3 in the ExtraAccountMetaList stays PullApproval-compatible; the hook dispatches from the owner account discriminator
- SDK builders — TypeScript instruction builders for all streaming operations
- Error codes 6700–6711 — Reserved for stream-specific validation and authorization failures
- 128-case proptest suite — Math property testing for stream calculations
- LiteSVM edge-case suite — Full E2E testing including clock regression
Phase 46: Plan Upgrades + Multi-Token Activation (COMPLETE)
Plan modification and multi-currency billing.
- Proration math — Calculate prorated amounts when subscribers switch plans mid-cycle
- Mandate v3 inline pending_upgrade — Upgrade state lives inline on the mandate using v1.7's reserved space (no separate UpgradeIntent PDA)
- TokenConfig end-to-end activation — Multi-token billing fully wired from Phase 41's TokenConfig registry
- Per-mint billing rails
- Decimal handling across different tokens
- Oracle configuration for USD display
- Browser-safe SDK boundary — SDK works in browser environments for plan upgrade flows
- 16 plans executed, 24 requirements covered
Phase 47: Webhook SDK + Staging Closure (IN PROGRESS)
Typed webhook events and staging environment acceptance.
@vela/webhookpackage — Workspace package inside vela-sdk monorepo (NOT a separate repo)- Typed wrappers around raw Helius webhook payloads
- NOT a new Vela-emitted event feed — wraps existing events
- HMAC verifier — Cryptographic verification of webhook authenticity
- DLQ (Dead Letter Queue) — Reliable handling of failed webhook deliveries
- Staging E2E acceptance — Closing v1.5 tech debt:
- Real Resend delivery verification
- Hosted invoice visual QA
- Queue/DLQ timing measurement
- Live wallet acceptance testing
- Pricing table accessibility sweep
- 18 plans scoped
Key Design Decisions
Streaming Is a Distinct Primitive
Streaming is NOT "periodic billing with period=1 second." It's a fundamentally different billing model:
- Pull-based — The payee pulls at the authorized rate, not the payer pushes
- Rate-per-second authorization — Not a fixed amount per period
- Continuous accrual — Amount owed grows continuously, settled on pull
- Settle-then-mutate — Any state change (pause, rate change, cancel) settles accrued amount first
This distinction matters for regulatory compliance (streaming vs periodic), accounting treatment, and the transfer hook validation path.
Pull-Based Streaming with Rate-per-Second Authorization
The stream authorizes a rate (tokens per second), not an amount. The keeper pulls whenever it wants, and the settled amount is calculated from rate × elapsed_time. This means:
- No fixed billing intervals — Settlement can happen at any cadence
- Rate changes mid-stream — Update the rate without restarting the stream
- Graceful pauses — Pause stops accrual, resume continues from zero accrued
Settle-Then-Mutate Invariant
Every stream operation that modifies state must first settle any accrued amount:
fn settle_accrued_in_place(stream: &mut StreamMandate, clock: &Clock) -> u64 {
let accrued = stream.authorized_max_rate * (clock.slot - stream.last_settlement_slot);
stream.accrued += accrued;
stream.last_settlement_slot = clock.slot;
accrued
}This invariant is enforced by a pub(crate) helper that all mutating instructions call. It's kept crate-private so future handlers share one trust boundary.
Upgrade State Inline on Mandate v3
Plan upgrades use v1.7's reserved space on the Mandate v3 account:
pending_upgradestored in the 64-byte reserved area- No separate UpgradeIntent PDA needed
- Upgrade state is co-located with the mandate, reducing account lookups
Multi-Token from Day One
Multi-token support was designed into v1.8 from the start, not retrofitted:
- Phase 45 streaming is multi-token-aware (rate denominated in the mandate's mint)
- Phase 46 upgrade proration handles multi-token decimals
- TokenConfig registry from v1.7 Phase 41 is fully activated
@vela/webhook as Workspace Package
The webhook SDK is a workspace package inside the vela-sdk monorepo, not a separate repository:
- Shares types with the SDK
- Same build and publish pipeline
- Wraps raw Helius payloads — not a new Vela-emitted event feed
Oracle Lookup Is Client-Side
USD display values (for plans denominated in various tokens) use a client-side SDK helper with 60-second TTL cache. Plans are token-denominated on-chain. The oracle is a display concern, not a billing concern.
Sequencing Rationale
The three phases were sequenced A → B+C (merged) → D+E (merged):
- Streaming first — The only net-new primitive and highest review risk. Needed to land before anything depends on it.
- Upgrades + Multi-Token merged — Both touch SDK builders, dashboard UI, and webhook event tagging. Upgrade proration already must handle multi-token decimals, so they share work.
- Webhook SDK + Staging merged — Webhook SDK consumes stabilized events from Phases 45+46. Staging E2E (Playwright + Mailpit) naturally validates webhook delivery. Cross-phase dependencies resolve since 47 is last.
The original plan was 5 phases (A→B→C→D with E parallel). The 3-phase aggressive merge reduced overhead while maintaining strict serial execution.
Phase 45 Design Decisions (Detailed)
The streaming engine required several lockable decisions during execution:
- Freeze the canonical stream schema at
state/stream_mandate.rswithauthorized_max_rateand 56-byte reserved tail - Retire the Plan 45-01 stream-proto path so downstream plans only target the final schema
- Error codes 6700–6711 reserved for stream-specific failures
settle_accrued_in_placekeptpub(crate)and re-exported frominstructions/mod.rs- Fail closed on non-stream discriminators with
WrongAccountTypewhile reusing existingIncorrectProgramIdowner check - Source-module test harness for direct helper testing (Rust integration tests can't call
pub(crate)) execute_streamon the approved GO path — slot 3 stays PullApproval-compatible- Short-circuit zero-amount transfers — No
transfer_checkedCPI whensettle_accrued_in_placereturns zero - Either keeper or merchant can settle — Without reintroducing plan-account dependencies
- Reuse execute_stream transfer path for pause, cancel, and rate updates (no duplicated CPI hook plumbing)
resume_streamfree of settlement references — So no-back-accrual invariant stays grep-checkable- 128 proptest cases for math properties (clears under 60-second verification budget)
- Concurrent settle+update modeled as single transaction, accepting only old-rate linearization or full rejection
Status
| Phase | Plans | Status |
|---|---|---|
| 45 Streaming Engine | 12 | COMPLETE |
| 46 Plan Upgrades + Multi-Token | 16 | COMPLETE |
| 47 Webhook SDK + Staging Closure | 18 | IN PROGRESS |
v1.8 progress: 28/47+ plans complete (Phases 45–46 done, Phase 47 executing)