Cosmic Bull

Realm on pearl-1

fee_split

gno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/fee_split

realmpipeline-applicationrevenue-and-value-flow

RenderedSourceCall builderState

curated

Weighted revenue splitting among payees with pull-based withdrawal. GitHub-pipeline application #1.

Identity

Import pathgno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/fee_split
Kindrealm (/r/)
Chainpearl-1
Namespaceg1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3
Realm addressg12v97h3jexeacya6r4pkrm722axgahet25e6lms derived, never confirmed against the realm

Provenance

chain-attested
Deployed at height596,803
Deploy transaction061c89b1fd34f696396a2ee4e0b39dc7a808cdb0e5916763d39a7afe80c43c16 look it up on the RPC
Deployerg1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3
Gas used30,604,157
Storage28,514 bytes, deposit 2851400ugnot
Files on chainfee_split.gno gnomod.toml
Deployed bytesfee_split.gno — 19,044 bytes
sha256e2198ebbd047ea118236208dfdcdf7670abd40bd74f7fd4899b3b57d540dd883

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

curl -sS 'https://pearl.testnets.gno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/fee_split$download&file=fee_split.gno' | shasum -a 256

Expected: e2198ebbd047ea118236208dfdcdf7670abd40bd74f7fd4899b3b57d540dd883 — 19,044 bytes. This was checked for all 21 packages while building this site's architecture record; every one matched. Use curl: pearl's edge answers Python's default user-agent with HTTP 403.

API

chain-derived 15 exported functions, 1 type.

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

AcceptFeeAdminArchiveClaimClaimFeesCreateSplitDepositFreezeGetClaimableGetFeeInfoGetSplitInfoNominateFeeAdminRenderSetFeeTransferOwnershipUpdateShares

Overview

This package carries no package doc comment on chain, so there is nothing for vm/qdoc to return and gnoweb's $help Overview is empty. Deployed bytes are immutable, so this cannot be repaired in place — see catalog/DISCOVERY_APIDOCS.md §2.4.

Imports

Constants and variables

const (
	MaxRecipients     = 20
	MaxSplitsPerOwner = 10
	// Quotas are PER-OWNER only (round-4 audit): a global cap is a shared
	// resource 50 sybil accounts could fill forever — and seeding grief
	// splits with balances to keyless recipients made the fill
	// unrecoverable even by the sybils. Per-owner quotas mean an attacker
	// consumes only their own budget; state growth is gas-priced.
	// Render is bounded separately (MaxRenderSplits).
	MaxRenderSplits = 100
	// MaxFeeBps is an IMMUTABLE ceiling on the protocol fee (1%). The
	// admin can set any fee from 0 up to this cap, never above it — the
	// cap, not the current setting, is what users must trust.
	MaxFeeBps = int64(100)

	// Pre-parse input bounds (round-4 audit): caps were enforced only
	// AFTER full parsing, so a 1MB recipient list burned ~11B gas before
	// refusal. 20 bech32 addresses + separators fit well within these.
	MaxRecipientListLen = 1024
	MaxShareListLen     = 128

	// Denomination handled by this realm. Deposits must be exactly one
	// coin of this denom; claims pay out in it.
	Denom = "ugnot"

	// Largest single deposit for which share math (amount * share,
	// share <= 10000) cannot overflow int64.
	MaxDepositAmount = int64(9223372036854775807) / 10000
)

Types

type Split

type Split struct {
	Owner          address
	Recipients     []address
	Shares         []int64 // basis points, must sum to 10000
	Balances       map[address]int64
	TotalDeposited int64
	TotalClaimed   int64
	Frozen         bool
	Archived       bool
}

Split holds a fee-splitting configuration with percentage-based shares denominated in basis points (1 bp = 0.01%, 10000 bp = 100%).

Exported fieldTypeDoc
Owneraddress
Recipients[]address
Shares[]int64basis points, must sum to 10000
Balancesmap[address]int64
TotalDepositedint64
TotalClaimedint64
Frozenbool
Archivedbool

Functions

AcceptFeeAdmin

func AcceptFeeAdmin(_ realm)

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

AcceptFeeAdmin completes the handover; only the nominee can accept.

Archive

func Archive(_ realm, splitID string)

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

Archive marks a fully-claimed split as archived. Only the owner can archive, and only if EVERY balance — including balances held by ex-recipients removed in a share update — is zero, since archiving blocks all further claims. Cannot be undone.

Claim

func Claim(cur realm, splitID string) int64

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

Claim withdraws the caller's accumulated balance and SENDS the coins to the caller's address. Balance is zeroed before the transfer. Claims remain possible on frozen splits, and by ex-recipients whose accrued balance predates a share update.

ClaimFees

func ClaimFees(cur realm) int64

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

ClaimFees sends all accrued protocol fees to the fee admin.

CreateSplit

func CreateSplit(_ realm, recipientList, shareList string) string

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

CreateSplit registers a new split. The caller becomes the owner. Recipients and shares are comma-separated; shares are in basis points summing to 10000.

Deposit

func Deposit(_ realm, splitID string)

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

Deposit distributes the coins sent with the call across recipients proportionally.

DEPLOYMENT PRECONDITION (round-4 audit): on a network with restricted/token-locked ugnot transfers, the bank gate is SENDER-whitelist-based — a whitelisted user's Deposit succeeds but Claim sends FROM this realm's (non-whitelisted) address and reverts. Funds would flow in and not out until the restriction lifts. Deploy only to networks with unrestricted ugnot, or have governance whitelist this realm's address first.

LIMITATION (round-3 audit, documented): only direct user calls can deposit. A DAO/realm treasury has NO deposit path — a realm-routed call is refused, and a bare banker send to this realm's address is an unrecoverable donation. Realm treasuries must route deposits through a user account. The deposit is the ACTUAL attached send — exactly one coin of Denom — so balances are always backed by funds this realm holds. Direct user calls only: a deposit routed through an intermediary realm would deliver its coins to that realm, not here, and must be rejected. Rounding dust goes to the highest-share recipient (deterministic, not order-dependent).

Freeze

func Freeze(_ realm, splitID string)

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

Freeze permanently locks shares and stops further deposits. One-way, cannot be undone. Claims remain possible.

GetClaimable

func GetClaimable(splitID string, addr address) int64

GetClaimable returns claimable balance for an address.

GetFeeInfo

func GetFeeInfo() string

GetFeeInfo returns the current fee configuration and accrued total.

GetSplitInfo

func GetSplitInfo(splitID string) string

GetSplitInfo returns a human-readable summary.

NominateFeeAdmin

func NominateFeeAdmin(_ realm, nominee address)

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

NominateFeeAdmin begins a two-step admin handover; the nominee must AcceptFeeAdmin. Pass "" to clear a pending nomination. Two-step because the admin address is a funds destination: a typo'd one-step transfer would strand all future fees.

Render

func Render(path string) string

Render returns a markdown overview. Never panics.

SetFee

func SetFee(_ realm, bps int64)

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

SetFee sets the protocol fee in basis points, admin only, hard-capped at MaxFeeBps. Applies to FUTURE deposits only.

TransferOwnership

func TransferOwnership(_ realm, splitID string, newOwner address)

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

TransferOwnership hands control to a new owner. The per-owner split slot moves with it: the old owner's count is freed and the new owner's is consumed (and must be under the limit).

UpdateShares

func UpdateShares(_ realm, splitID, recipientList, shareList string)

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

UpdateShares replaces recipients and shares. Owner only. Not if frozen. Removed recipients keep any accrued balance and can still Claim it.


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/banker, chain/runtime/unsafe, sort, strconv, strings
First-party dependenciesnone
Used bynone

Known limitations

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

curated

Source and records

Source filepearl/r/feesplit/fee_split.gno at commit 6a510c665a53 in the project repository (not public — the digest command above is the check that needs no repository)
Matches the deployed bytesno — deliberate divergence
WhyLocal pearl/r/feesplit/fee_split.gno carries the undeployed yellow-remediation. fee_split v1 is frozen and no fee_split_v2 is authorized. Do not reconcile by redeploying.
Recorded inpearl/INFRASTRUCTURE.md
Upstream repositoryhttps://github.com/SillyZir/fee_split
Deployed from commit819239ad5ddc46cf685f5ff363cabb7290848c1f
Recordscatalog/applications.md#fee_split
pearl/DEPLOYMENT.md