Realm on pearl-1
fee_split
gno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/fee_split
realmpipeline-applicationrevenue-and-value-flow
Weighted revenue splitting among payees with pull-based withdrawal. GitHub-pipeline application #1.
Identity
| Import path | gno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/fee_split |
|---|---|
| Kind | realm (/r/) |
| Chain | pearl-1 |
| Namespace | g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3 |
| Realm address | g12v97h3jexeacya6r4pkrm722axgahet25e6lms derived, never confirmed against the realm |
Provenance
chain-attested| Deployed at height | 596,803 |
|---|---|
| Deploy transaction | 061c89b1fd34f696396a2ee4e0b39dc7a808cdb0e5916763d39a7afe80c43c16 look it up on the RPC |
| Deployer | g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3 |
| Gas used | 30,604,157 |
| Storage | 28,514 bytes, deposit 2851400ugnot |
| Files on chain | fee_split.gno gnomod.toml |
| Deployed bytes | fee_split.gno — 19,044 bytes |
| sha256 | e2198ebbd047ea118236208dfdcdf7670abd40bd74f7fd4899b3b57d540dd883 |
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 256Expected: 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
chainchain/bankerchain/runtime/unsafesortstrconvstrings
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 field | Type | Doc |
|---|---|---|
Owner | address | |
Recipients | []address | |
Shares | []int64 | basis points, must sum to 10000 |
Balances | map[address]int64 | |
TotalDeposited | int64 | |
TotalClaimed | int64 | |
Frozen | bool | |
Archived | bool |
Functions
AcceptFeeAdmin
func AcceptFeeAdmin(_ realm)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asAcceptFeeAdmin(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 asArchive(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 asClaim(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 asClaimFees(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 asCreateSplit(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 asDeposit(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 asFreeze(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 asNominateFeeAdmin(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 asSetFee(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 asTransferOwnership(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 asUpdateShares(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| Imports | chain, chain/banker, chain/runtime/unsafe, sort, strconv, strings |
|---|---|
| First-party dependencies | none |
| Used by | none |
Known limitations
Recorded by the people who built and deployed it. This list is deliberately not empty where honesty costs something.
curated- 3 YELLOW findings resolved in the repository after deployment; live v1 is frozen at the deployed bytes and no fee_split_v2 is authorized.
Source and records
| Source file | pearl/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 bytes | no — deliberate divergence |
| Why | Local 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 in | pearl/INFRASTRUCTURE.md |
| Upstream repository | https://github.com/SillyZir/fee_split |
| Deployed from commit | 819239ad5ddc46cf685f5ff363cabb7290848c1f |
| Records | catalog/applications.md#fee_splitpearl/DEPLOYMENT.md |