Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

subscriptions source realm

Package subscriptions is a multi-provider subscription hub: providers publish plans priced per billing period; subscr...

Overview

Package subscriptions is a multi-provider subscription hub: providers publish plans priced per billing period; subscribers pay per period, with deterministic renewal, grace, expiration and cancellation rules; other realms and off-chain services gate access on Entitled / EntitledFor, which is the integration surface this realm exists to provide.

THE BILLING MODEL, COMPLETELY:

Example
 1Subscribe (payable, exact price)  : creates the subscription, pays
 2                                    period 1. paidThrough = height
 3                                    + periodBlocks.
 4Renew (payable, exact price)      : extends paidThrough by exactly
 5                                    one periodBlocks, FROM
 6                                    paidThrough — never from the
 7                                    current height — so period
 8                                    boundaries are fixed at
 9                                    Subscribe time and never drift.
10Entitlement                       : height < paidThrough. Nothing
11                                    else. Status does not enter
12                                    into it: a cancelled
13                                    subscription stays entitled to
14                                    what it already paid for.
15Renewal window                    : a renewal is accepted iff BOTH
16                    paidThrough - height <= periodBlocks   (early bound)
17                    height < paidThrough + periodBlocks    (late bound)
18                                    The early bound caps prepayment
19                                    at one full unstarted period —
20                                    a second Renew straight after a
21                                    first is refused, which is what
22                                    makes an accidental duplicate
23                                    payment structurally impossible
24                                    rather than merely unlikely.
25                                    The late bound is the grace
26                                    window: renewing after lapse
27                                    extends from paidThrough, so it
28                                    back-pays the lapsed span to
29                                    keep the original schedule and
30                                    buys paidThrough + periodBlocks
31                                    - height further blocks — always
32                                    at least one, because the bound
33                                    is exclusive (audit finding Y2).
34                                    A lapsed subscriber who prefers
35                                    a fresh full period may Cancel
36                                    and Subscribe again at the same
37                                    total price; Subscribe's refusal
38                                    message states both options.
39Expire (permissionless valve)     : once height >= paidThrough +
40                                    periodBlocks, anyone may mark
41                                    the subscription Expired. No
42                                    funds move — every payment
43                                    settled when it was made. The
44                                    valve exists so the
45                                    plan|subscriber slot frees
46                                    without depending on either
47                                    party, and Subscribe itself
48                                    collapses an expired incumbent,
49                                    so a fresh start never depends
50                                    on housekeeping having run. The
51                                    renewable and expirable height
52                                    sets partition exactly: no
53                                    height is in both or neither.
54Cancel (subscriber only)          : Active -> Cancelled. Terminal.
55                                    No refund — payments settle to
56                                    the provider at payment time,
57                                    and what was bought (entitlement
58                                    through paidThrough) stays
59                                    bought. What cancellation ends
60                                    is the OBLIGATION: a Cancelled
61                                    subscription can never be
62                                    renewed, by the subscriber or
63                                    anyone else.
64RetirePlan (provider only)        : no new Subscribes, no renewals.
65                                    Existing entitlements run to
66                                    paidThrough untouched. Refusing
67                                    renewals on a retired plan is
68                                    subscriber protection: nobody
69                                    can keep paying for a service
70                                    whose provider announced its
71                                    end.

The obligation is therefore explicit on chain at every moment: a subscription owes nothing (there is no pull payment and no debt — a lapse simply ends entitlement), and the realm owes the subscriber exactly `paidThrough - height` blocks of entitlement, queryable by anyone via PaidThrough / Entitled / EntitledFor.

WHO PAYS WHOM. Payments settle immediately: price - fee is credited to the provider's claimable balance, fee to the protocol pot, both inside the same feeledger the sibling realms use. There is no escrow: H == U + F at all times (plus out-of-band surplus, recoverable by SweepDenom above the Liabilities reserve). The renewal caller must be the subscriber — a third party cannot extend someone else's subscription, which closes both a consent problem (an unwanted gift re-arms a lapsing obligation) and a griefing edge (spending pennies to keep a victim's slot occupied).

FEES follow the house pattern exactly: a compile-time MaxFeeBps ceiling, the current fee snapshotted into the PLAN at CreatePlan (provider consents via its own maxFeeBps argument), copied into the subscription at Subscribe, and charged at every payment from the PROVIDER's side. A later SetFeeBps touches only plans created afterwards; no existing plan or subscription can have its fee moved by anyone.

REALM-CALLER CAVEAT, inherited from the siblings verbatim: coinio's receipt guard admits only EOA payers, so subscribers are EOAs; assertNoSend reads the ORIGIN envelope, so every non-payable function refuses any transaction that attached coins anywhere. Providers may be EOAs or realms, but a realm provider must expose its own crossing path to Claim, or what it earns is stranded (see RegisterService's caveat in service_market — the same three obligations apply).

Constants 5

const MaxTitleLen, MaxDescLen, MinPrice, MinPeriodBlocks, MaxPeriodBlocks, MaxPlansPerProvider, MaxSubsPerSubscriber, RenderLimit

 1const (
 2	MaxTitleLen = 80
 3	MaxDescLen  = 2000
 4	MinPrice    = int64(1)
 5
 6	// MinPeriodBlocks/MaxPeriodBlocks bound a plan's billing period:
 7	// ~40 seconds to ~1 year at pearl's observed ~4.2s blocks. The
 8	// floor keeps a hostile plan from turning renewal into a
 9	// per-block treadmill; the ceiling keeps paidThrough arithmetic
10	// far from overflow even at maximum prepayment.
11	MinPeriodBlocks = int64(10)
12	MaxPeriodBlocks = int64(7500000)
13
14	// MaxPlansPerProvider bounds catalog monopolization by a single
15	// address — the finding that was RED in permission_registry and
16	// service_registry, carried from the start here.
17	MaxPlansPerProvider = 20
18
19	// MaxSubsPerSubscriber bounds one account's open-subscription
20	// state. Terminal subscriptions free their slot.
21	MaxSubsPerSubscriber = 100
22
23	// RenderLimit bounds every rendered list — an unbounded Render
24	// was YELLOW in three prior audits.
25	RenderLimit = 20
26)
source

Input bounds.

const Denom

1const Denom = "ugnot"
source

Denom is the only asset this realm accepts.

const MaxFeeBps

1const MaxFeeBps = int64(1000)
source

MaxFeeBps is the hard protocol-fee cap: 1000 bps = 10%.

Functions 28

func AcceptAdmin

crossing Action
1func AcceptAdmin(cur realm)
source

AcceptAdmin completes the handover; only the staged successor may.

func ActiveSubID

Action
1func ActiveSubID(planID int64, subscriber address) (int64, bool)
source

ActiveSubID returns the caller-facing id of subscriber's live subscription to planID, or (0, false) if none is Active.

func Cancel

crossing Action
1func Cancel(cur realm, subID int64)
source

Cancel ends the caller's own subscription. Terminal: it can never be renewed afterwards, by anyone. No refund and no funds move — every payment settled when it was made, and the entitlement already bought (height < paidThrough) remains until it runs out. The plan slot and the subscriber's quota slot free immediately.

func Claim

crossing Action
1func Claim(cur realm, amount int64)
source

Claim sends amount ugnot of the caller's claimable balance back to the caller. Providers earn into this balance at every payment.

func ClaimAll

crossing Action
1func ClaimAll(cur realm)
source

ClaimAll sends the caller's entire claimable balance back to the caller. Fails if there is nothing to claim.

func CreatePlan

crossing Action
1func CreatePlan(cur realm, title, description string, price, periodBlocks, maxFeeBps int64) int64
source

CreatePlan publishes a subscription plan and returns its id. No coins are accepted; the storage deposit the caller pays is the anti-spam. The current protocol fee is snapshotted into the plan and must not exceed maxFeeBps, the ceiling the provider signed for — pass MaxFeeBps to accept any legal fee. Plans are immutable once created: price and period changes are a new plan, so nothing a subscriber agreed to can move underneath them.

func Entitled

Action
1func Entitled(subID int64) bool
source

Entitled reports whether the subscription's paid entitlement covers the current block: height < paidThrough. Status deliberately does not enter into it — a cancelled subscriber keeps what they paid for, and an expirable-but-unexpired one has already lapsed here.

func EntitledFor

Action
1func EntitledFor(planID int64, subscriber address) bool
source

EntitledFor reports whether subscriber currently holds paid entitlement to planID, through their MOST RECENT subscription to it. This is the one-call integration surface for other realms and services, and it honors the entitlement contract across status: a cancelled subscription keeps answering true until its paidThrough — what was bought stays bought (audit finding Y1). One self-inflicted edge is fail-closed: cancelling a prepaid subscription and re-subscribing at once points this surface at the NEW, earlier paidThrough; the old subscription's remaining span stays queryable per-id via Entitled.

func Expire

crossing Action
1func Expire(cur realm, subID int64)
source

Expire marks a lapsed subscription Expired once its grace window is over: height >= paidThrough + periodBlocks. Permissionless by design — like the sibling realms' valves, no slot's liveness may depend on either party showing up. No funds move.

func PaidThrough

Action
1func PaidThrough(subID int64) int64
source

PaidThrough returns the absolute height a subscription is paid to.

func Renew

crossing Action
1func Renew(cur realm, subID int64)
source

Renew pays for the next billing period of the caller's own subscription. The transaction must attach exactly the subscription's price. The renewal window is deterministic and stated in the header: accepted iff paidThrough - height <= periodBlocks (at most one full unstarted period prepaid — the duplicate-payment bound) and height < paidThrough + periodBlocks (the grace bound, exclusive — a renewal always buys at least one block). Extension is always FROM paidThrough, so period boundaries never drift, and a renewal inside grace covers the lapsed span — that is the price of keeping the original schedule, and it is the documented, deterministic choice.

func RenewableFrom

Action
1func RenewableFrom(subID int64) (from, until int64)
source

RenewableFrom returns the earliest height at which Renew will accept a payment for this subscription, and the last height at which it still will (inclusive) — the deterministic window, precomputed for integrators. From until+1 the subscription is expirable instead; the two sets partition exactly.

func RetirePlan

crossing Action
1func RetirePlan(cur realm, planID int64)
source

RetirePlan takes a plan off the market: no new subscriptions and no renewals. Provider only. Existing entitlements run to their paidThrough untouched; refusing renewals is subscriber protection — nobody keeps paying for a service whose provider announced its end. The provider's plan-quota slot frees.

func SetFeeBps

crossing Action
1func SetFeeBps(cur realm, bps int64)
source

SetFeeBps sets the protocol fee snapshotted into FUTURE plans. Bounded by MaxFeeBps; existing plans and subscriptions are untouched — their fee was fixed the moment the provider consented to it.

func SetFeeRecipient

crossing Action
1func SetFeeRecipient(cur realm, recipient address)
source

SetFeeRecipient points future fee withdrawals and sweeps at a new address. Admin only. The zero address is refused — it would strand the pot.

func Subscribe

crossing Action
1func Subscribe(cur realm, planID int64) int64
source

Subscribe pays for the first billing period of a plan and returns the new subscription id. The transaction must attach EXACTLY the plan's price in ugnot — over- and underpayment are both refused, so a mistaken double-attach cannot silently become a donation. One subscriber holds at most one live subscription per plan: if an Active one exists the call is refused (the renewal path is Renew, never a second Subscribe — that is the duplicate-payment guard at the identity level); an incumbent past its grace window is collapsed to Expired in place, so a fresh start never waits on housekeeping.

The payment settles immediately: price minus the plan's snapshotted fee to the provider's claimable balance, fee to the protocol pot. Entitlement runs from this block: paidThrough = height + periodBlocks.

func SweepDenom

crossing Action
1func SweepDenom(cur realm, denom string)
source

SweepDenom recovers out-of-band coins (sent by raw bank transfer, outside any entrypoint) to the fee recipient. For the ledger denom the reserve is Liabilities() — user balances and the fee pot are structurally unreachable. Fee recipient only.

func TransferAdmin

crossing Action
1func TransferAdmin(cur realm, successor address)
source

TransferAdmin stages a two-step admin handover. The successor holds nothing until AcceptAdmin.

func WithdrawFees

crossing Action
1func WithdrawFees(cur realm)
source

WithdrawFees sends the accrued fee pot to the fee recipient. Only the fee recipient may call it, and only the pot moves.

Imports 8

Source Files 2