Cosmic Bull

revshare

gno.land/r/g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39/revshare on gno.land mainnet

realmfactory-applicationrevenuelive on mainnet

Share subscription revenue among a team by weight.

Collects what a team earns as a subscription provider and splits it into balances each member claims for themselves.

In detail curated

Team revenue realm that IS a subscription provider: creates plans on the live subscriptions realm as the provider (admin-gated), pulls accrued revenue across the realm boundary via permissionless Pull (ClaimAll cross-call, measured held-delta), and splits it among weighted members (<=20, weights 1..10000) into pull-claimable feeledger balances, rounding dust to the highest-weight member. Conservation Held == UsersTotal + S; the downstream receivable is accounted, not custodied. Factory benchmark #7 (economic composition).

Use it

A realm holds state. Reading it is a free query; changing it is a transaction you sign yourself.

Read it — no key, no account, no GNOT

gnokey query vm/qeval \
  --data 'gno.land/r/g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39/revshare.Held()' \
  --remote https://rpc.gno.land:443

Held is read-only, so this is a query rather than a transaction: it costs nothing, signs nothing and needs no funded account. Every exported function is listed under the API below; swap the name and the arguments to read any of the others.

Change its state

Every state change is a transaction you sign with your own key — this site never asks for one and holds none. You do not have to hand-write the transaction: gnoweb generates the exact gnokey maketx call command for any of this realm's 11 crossing functions, with the argument names filled in.

The gno.land mainnet builder spends real GNOT. Start with the read above, and size -gas-fee from a simulation rather than from any example.

Read the known limitations further down this page first. They record what was not verified on chain, which is usually the part that matters.

Import it from another realm

import "gno.land/r/g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39/revshare"

The path is the address: there is no package manager, nothing to add to a manifest and no version to pin, so the package you compile against is the one the chain runs. It differs per chain because the namespace in it is the deploying account's.

Deployments

gno.land mainnet gnoland-1

RenderedSourceCall builderState

chain-attested
Import pathgno.land/r/g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39/revshare
Namespaceg1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39
Deployed at height337,132
Deploy transaction7fbaaf1b7121b4badda4a1bb65932bdb80ed32551466a80c618ce1953a62b1fb look it up on the RPC
Deployerg1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39
Gas used46,434,354
Storagenot attested per package on this chain — see the limitations below
Files on chaingnomod.toml revshare.gno revshare_test.gno
Deployed bytesrevshare.gno — 16,794 bytes
sha256659511153647b8892efbdb01f1e9954d1264d900d2d92edfe2cbe016b88e440f
Realm addressg1xn5xtquhks33g9y6ac0wlsegwwygzetqfd54r4 derived, never confirmed against the realm

Do not take the hash above on trust. $download returns the bytes gnoland-1 is actually running; this command fetches them and prints their digest, which should equal the one in the table:

curl -sS 'https://gno.land/r/g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39/revshare$download&file=revshare.gno' | shasum -a 256

Expected: 659511153647b8892efbdb01f1e9954d1264d900d2d92edfe2cbe016b88e440f — 16,794 bytes. Use curl: the gno.land edges answer Python's default user-agent with HTTP 403.

API

chain-derived 22 exported functions.

Every function below deep-links to gnoweb's call builder, which generates a ready-to-run gnokey maketx call for it:

AcceptAdminAddMemberBalanceOfClaimClaimAllCreatePlanHeldMemberCountMemberWeightNumPlansPlanIDPullPulledReceivableRemoveMemberRenderRetirePlanSetWeightSweepDenomTotalWeightTransferAdminUsersTotal

Overview

Package revshare is a team revenue realm that IS a subscription provider. The live subscriptions realm supports realm providers but warns, in its own frozen header, that a realm provider "must expose its own crossing path to Claim, or what it earns is stranded." revshare is that crossing path, plus the one thing a team needs on top of it: pulled revenue is split among weighted members into pull-claimable balances.

THE ECONOMIC COMPOSITION, PRECISELY:

components : subscriptions (live realm — the revenue machine and the upstream custodian), feeledger (member balance accounting, fee cap 0), coinio (payouts, held-balance reads, reserve-protected sweep). value boundaries : B1 subscriber EOA -> subscriptions (plan price, origin envelope; governed by subscriptions' own H == U + F). B2 subscriptions -> revshare (banker send: ClaimAll pays the caller, and the caller is this realm). B3 revshare -> member EOA (coinio.Payout on Claim/ClaimAll). who owns which state : subscriptions owns plans, subs and the provider's claimable balance (revshare's RECEIVABLE); revshare owns the member table, weights, and the member balances in its own ledger. No state is shared; the only coupling is the crossing calls and the coins that move over B2. who controls funds : upstream, only a claim by this realm can move its receivable (provider-keyed ledger). Here, member balances move only to their owner (pull claims); the admin can NEVER touch earned balances — sweep reserves them, weight changes affect only FUTURE distributions. identity propagation : downstream sees cur.Previous() = THIS realm on every crossing call, so the plan provider and the claim beneficiary are the realm address by construction — no admin or member identity ever reaches the downstream realm. conservation : Held == UsersTotal + S (surplus above the Liabilities() reserve, recoverable only by SweepDenom). Distribution is exact: shares are floor(amountw/W) via the overflow-free split (A/W)w + ((A%W)*w)/W, and the remainder goes to the highest-weight member (ties: lowest address) — fee_split's deterministic dust policy, so no residual pool exists. Cross-boundary: lifetime Pulled equals the sum of all distributions, and the downstream receivable is NOT part of Held. ordering : Pull refuses BEFORE the downstream call (no members configured = refuse), claims downstream, MEASURES the arrival as a held-balance delta, then distributes exactly what arrived. State-after-call: the only local mutations happen after the boundary, on measured coins. downstream abort : "nothing to claim" (or any downstream panic) aborts the whole Pull — no local state exists yet to corrupt, by ordering AND by VM atomicity. There is no recover anywhere in this realm, and none may be added. replay : a second Pull finds a zero downstream balance and aborts there. Distribution credits are driven by the measured delta, so a replayed Pull cannot double-count even in principle. trust : revshare does not trust the downstream reply beyond "it did not abort" — it distributes the measured balance delta, not a reported amount. Downstream validates nothing about this caller; the provider ledger is keyed by address. terminal states : plans retire downstream (provider-only, exposed here admin-gated); members can be removed (earned balances survive removal and stay claimable); the realm itself has no terminal state — a team that walks away leaves only pull-claimable balances. adversarial callers : Pull is permissionless — it can only move the receivable into member balances at the fixed weights, so a stranger's Pull is a free favor. Plan creation/retirement is admin-gated (the downstream per-provider plan quota is a griefable resource). Members trust the admin for FUTURE weights only, never for earned balances. All entrypoints refuse coin-carrying transactions (the downstream assertNoSend reads the origin envelope; ours matches).

One team per deploy: subscriptions keeps ONE claimable balance per provider address, so a multi-team router behind one realm address could not attribute revenue at the boundary. This is a measured constraint of the downstream API, not a choice.

Imports

Constants and variables

MaxMembers bounds the distribution loop; MaxWeight bounds a single weight. Together they cap totalWeight at 200,000, which makes the remainder step of the split ((A%W)*w) provably overflow-free for any int64 coin amount — the conservation math cannot trap.

const (
	MaxMembers = 20
	MaxWeight  = int64(10000)
)

Denom is the only asset this realm accounts.

const Denom = "ugnot"

Functions

AcceptAdmin

func AcceptAdmin(cur realm)

Crossing function. Callable from a transaction via MsgCall, and from another realm as AcceptAdmin(cross(cur), ...).

AcceptAdmin completes the handover; only the staged successor may. The sweeper role moves with the admin.

AddMember

func AddMember(cur realm, a address, weight int64)

Crossing function. Callable from a transaction via MsgCall, and from another realm as AddMember(cross(cur), ...).

AddMember adds a weighted member. Weight changes never touch earned balances; they shape future pulls only.

BalanceOf

func BalanceOf(a address) int64

Claim

func Claim(cur realm, amount int64)

Crossing function. Callable from a transaction via MsgCall, and from another realm as Claim(cross(cur), ...).

Claim sends amount ugnot of the caller's earned balance to the caller.

ClaimAll

func ClaimAll(cur realm)

Crossing function. Callable from a transaction via MsgCall, and from another realm as ClaimAll(cross(cur), ...).

ClaimAll sends the caller's entire earned balance to the caller.

CreatePlan

func CreatePlan(cur realm, title, description string, price, periodBlocks, maxFeeBps int64) int64

Crossing function. Callable from a transaction via MsgCall, and from another realm as CreatePlan(cross(cur), ...).

CreatePlan creates a subscription plan THROUGH this realm, making the realm the plan's provider downstream. Admin only: the per-provider plan quota downstream is a griefable resource.

Held

func Held() int64

MemberCount

func MemberCount() int64

MemberWeight

func MemberWeight(a address) int64

NumPlans

func NumPlans() int64

PlanID

func PlanID(i int64) int64

Pull

func Pull(cur realm) int64

Crossing function. Callable from a transaction via MsgCall, and from another realm as Pull(cross(cur), ...).

Pull claims this realm's entire accrued provider balance from the subscriptions realm and distributes it to the members by weight. Permissionless: pulling can only move the receivable into member balances at the fixed weights, so anyone may crank it. Refuses BEFORE the downstream call when no member could receive the funds.

Pulled

func Pulled() int64

Receivable

func Receivable() int64

Receivable reads this realm's accrued, not-yet-pulled provider balance inside the subscriptions realm — the other side of value boundary B2. It is deliberately NOT part of Held or of the local conservation equation.

RemoveMember

func RemoveMember(cur realm, a address)

Crossing function. Callable from a transaction via MsgCall, and from another realm as RemoveMember(cross(cur), ...).

RemoveMember removes a member from future distributions. The member's earned balance is untouched and stays claimable forever.

Render

func Render(path string) string

RetirePlan

func RetirePlan(cur realm, planID int64)

Crossing function. Callable from a transaction via MsgCall, and from another realm as RetirePlan(cross(cur), ...).

RetirePlan retires one of this realm's plans downstream. Admin only.

SetWeight

func SetWeight(cur realm, a address, weight int64)

Crossing function. Callable from a transaction via MsgCall, and from another realm as SetWeight(cross(cur), ...).

SetWeight changes a member's weight for future distributions.

SweepDenom

func SweepDenom(cur realm, denom string)

Crossing function. Callable from a transaction via MsgCall, and from another realm as SweepDenom(cross(cur), ...).

SweepDenom recovers out-of-band coins to the sweeper. For the accounting denom the reserve is the full ledger liability — member balances are structurally unsweepable. Sweeper only.

TotalWeight

func TotalWeight() int64

TransferAdmin

func TransferAdmin(cur realm, successor address)

Crossing function. Callable from a transaction via MsgCall, and from another realm as TransferAdmin(cross(cur), ...).

TransferAdmin stages a two-step admin handover.

UsersTotal

func UsersTotal() int64

Doc text is reproduced as vm/qdoc returns it. The node markdown-escapes doc comments, so a bracket or angle bracket may carry a backslash the committed source does not have. The source itself is at source and in this repository.

Dependencies

chain-attested
Importschain, chain/runtime/unsafe, gno.land/p/g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39/coinio, gno.land/p/g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39/feeledger, gno.land/r/g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39/subscriptions, strconv
First-party dependenciesgno.land/p/g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39/coinio, gno.land/p/g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39/feeledger, gno.land/r/g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39/subscriptions

Known limitations

Recorded by the people who built and deployed it. This list is deliberately not empty where honesty costs something.

curated

Source

Source filemainnet/r/revshare/revshare.gno at commit c2e10d17ffbf in the project repository (not public — the digest command above is the check that needs no repository)
Matches the deployed bytesyes — byte-identical