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:443Held 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 path | gno.land/r/g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39/revshare |
|---|---|
| Namespace | g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39 |
| Deployed at height | 337,132 |
| Deploy transaction | 7fbaaf1b7121b4badda4a1bb65932bdb80ed32551466a80c618ce1953a62b1fb look it up on the RPC |
| Deployer | g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39 |
| Gas used | 46,434,354 |
| Storage | not attested per package on this chain — see the limitations below |
| Files on chain | gnomod.toml revshare.gno revshare_test.gno |
| Deployed bytes | revshare.gno — 16,794 bytes |
| sha256 | 659511153647b8892efbdb01f1e9954d1264d900d2d92edfe2cbe016b88e440f |
| Realm address | g1xn5xtquhks33g9y6ac0wlsegwwygzetqfd54r4 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 256Expected: 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
chainchain/runtime/unsafegno.land/p/g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39/coiniogno.land/p/g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39/feeledgergno.land/r/g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39/subscriptionsstrconv
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 asAcceptAdmin(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 asAddMember(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 asClaim(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 asClaimAll(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 asCreatePlan(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 asPull(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 asRemoveMember(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 asRetirePlan(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 asSetWeight(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 asSweepDenom(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 asTransferAdmin(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| Imports | chain, chain/runtime/unsafe, gno.land/p/g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39/coinio, gno.land/p/g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39/feeledger, gno.land/r/g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39/subscriptions, strconv |
|---|---|
| First-party dependencies | gno.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- No state-changing call was made against this realm on gnoland-1. The live evidence is byte custody plus a successful vm/qrender; every state transition rests on the committed test suite and the pearl-1 audit, not on mainnet behaviour ([an unpublished record], Live verification).
- storage_bytes and storage_deposit are null because gnoland-1 emits no /tm.StorageDepositEvent ON ADDPKG -- a scope that is load-bearing, because it DOES emit one on MsgCall (measured 2026-10-17, AL62); see the chain's storage_deposit_events field. The deposit actually charged was 3,220,700 ugnot, measured from the signer's balance and recorded in [an unpublished record], Cost; it is not chain-attested per package by this manifest.
- The deployed file set includes revshare_test.gno. The pearl-1 deployment of the same realm carried only the source and gnomod.toml, so the mainnet package is three files where pearl's is two -- the test file is stored on chain and paid for in the deposit. It declares no crossing function and so adds no callable surface, but it is part of the immutable package.
- Admin two-step handover, SweepDenom success legs, SetWeight/RemoveMember and the 20-member cap are unit-tested but were not exercised live; each is correlated to its committed test in DEPLOYMENT.md section 19.
- One team per deploy: subscriptions keeps one claimable balance per provider address, so multi-team routing behind one realm address is unaccountable at the boundary (measured downstream constraint).
- Pull-time weights govern accrued-but-unpulled receivable (documented trust model; permissionless Pull is the member-side self-protection); admin loss with an empty member table strands the receivable (operating note: keep at least one member configured while plans are live).
- Pull gas (~30,000ugnot at padded gas_wanted) exceeded the demo pull amounts; the crank is amount-independent so batching amortizes it (3% of a 1M pull) -- demo economics intentionally experimental, stated in the record.
- Members should be EOAs; a realm member strands its own claims (same caveat as sibling realms).
Source
| Source file | mainnet/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 bytes | yes — byte-identical |