PathrockNetwork Gno Explorer
HomeBlocksTransactionsTokensRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/r/g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39/fee_split/v2

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
v2
Namespace
g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39 / fee_split
Files
3 (gnomod.toml)
Exported functions
17
Module
gno.land/r/g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39/fee_split/v2
gno
0.9

Files (3)

  • gnomod.tomltoml
  • fee_split.gnogno
  • fee_split_test.gnogno
fee_split_test.gnogno
1package fee_split23import (4	"chain"5	"chain/banker"6	"strconv"7	"strings"8	"testing"910	"gno.land/p/nt/testutils/v0"11	"gno.land/p/nt/uassert/v0"12)1314var (15	owner    = testutils.TestAddress("owner")16	alice    = testutils.TestAddress("alice")17	bob      = testutils.TestAddress("bob")18	carol    = testutils.TestAddress("carol")19	dave     = testutils.TestAddress("dave")20	attacker = testutils.TestAddress("attacker")21)2223// resetState resets all package-level state between tests.24// Required because Gno test functions share the same VM context.25func resetState() {26	splits = make(map[string]*Split)27	splitIDs = []string{}28	ownerSplits = make(map[address]int)29	nextID = 130	feeBps = 031	feeAdmin = feeAdminAddr32	pendingFeeAdmin = ""33	feesAccrued = 034	feesClaimed = 035}3637var feeAdminAddr = testutils.TestAddress("feeadmin")3839func addrList(addrs ...address) string {40	parts := make([]string, len(addrs))41	for i, a := range addrs {42		parts[i] = string(a)43	}44	return strings.Join(parts, ",")45}4647// fund gives the realm's package address coins so Claim payouts succeed,48// mirroring the deposits the accounting records.49func fund(amount int64) {50	pkgAddr := chain.PackageAddress("gno.land/r/g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39/fee_split/v2")51	testing.IssueCoins(pkgAddr, chain.Coins{{Denom: Denom, Amount: amount}})52}5354// deposit performs a coin-backed deposit as addr.55func deposit(cur realm, addr address, splitID string, amount int64) {56	testing.SetRealm(testing.NewUserRealm(addr))57	testing.SetOriginSend(chain.Coins{{Denom: Denom, Amount: amount}})58	fund(amount)59	Deposit(cross(cur), splitID)60	testing.SetOriginSend(nil)61}6263// ---------- CreateSplit ----------6465func TestCreateSplit_Basic(cur realm, t *testing.T) {66	resetState()67	testing.SetRealm(testing.NewUserRealm(owner))6869	id := CreateSplit(cross(cur), addrList(alice, bob), "6000,4000")70	uassert.Equal(t, "split_1", id)7172	s := splits["split_1"]73	uassert.Equal(t, string(owner), string(s.Owner))74	uassert.Equal(t, 2, len(s.Recipients))75	uassert.Equal(t, string(alice), string(s.Recipients[0]))76	uassert.Equal(t, string(bob), string(s.Recipients[1]))77	uassert.Equal(t, int64(6000), s.Shares[0])78	uassert.Equal(t, int64(4000), s.Shares[1])79	uassert.False(t, s.Frozen, "new split must not be frozen")80	uassert.Equal(t, int64(0), s.TotalDeposited)81	uassert.Equal(t, int64(0), s.TotalClaimed)82}8384func TestCreateSplit_IDsIncrement(cur realm, t *testing.T) {85	resetState()86	testing.SetRealm(testing.NewUserRealm(owner))8788	id1 := CreateSplit(cross(cur), addrList(alice), "10000")89	id2 := CreateSplit(cross(cur), addrList(bob), "10000")90	uassert.Equal(t, "split_1", id1)91	uassert.Equal(t, "split_2", id2)92}9394func TestCreateSplit_DuplicateRecipient(cur realm, t *testing.T) {95	resetState()96	testing.SetRealm(testing.NewUserRealm(owner))9798	uassert.AbortsWithMessage(t, cur,99		"duplicate recipient: "+string(alice),100		func() { CreateSplit(cross(cur), addrList(alice, alice), "5000,5000") },101	)102}103104func TestCreateSplit_SharesDontSum(cur realm, t *testing.T) {105	resetState()106	testing.SetRealm(testing.NewUserRealm(owner))107108	uassert.AbortsWithMessage(t, cur,109		"shares must sum to 10000 basis points, got 5000",110		func() { CreateSplit(cross(cur), addrList(alice), "5000") },111	)112}113114func TestCreateSplit_LengthMismatch(cur realm, t *testing.T) {115	resetState()116	testing.SetRealm(testing.NewUserRealm(owner))117118	uassert.AbortsWithMessage(t, cur,119		"recipients and shares must have the same length",120		func() { CreateSplit(cross(cur), addrList(alice, bob), "10000") },121	)122}123124func TestCreateSplit_InvalidAddressRejected(cur realm, t *testing.T) {125	resetState()126	testing.SetRealm(testing.NewUserRealm(owner))127128	uassert.AbortsWithMessage(t, cur,129		"invalid recipient address: not_an_address",130		func() { CreateSplit(cross(cur), "not_an_address", "10000") },131	)132}133134func TestCreateSplit_PerOwnerLimit(cur realm, t *testing.T) {135	resetState()136	testing.SetRealm(testing.NewUserRealm(owner))137138	for i := 0; i < MaxSplitsPerOwner; i++ {139		CreateSplit(cross(cur), addrList(alice), "10000")140	}141	uassert.AbortsWithMessage(t, cur,142		"per-owner split limit reached",143		func() { CreateSplit(cross(cur), addrList(alice), "10000") },144	)145}146147// ---------- Deposit (coin-backed) ----------148149func TestDeposit_UsesSentCoins(cur realm, t *testing.T) {150	resetState()151	testing.SetRealm(testing.NewUserRealm(owner))152	id := CreateSplit(cross(cur), addrList(alice, bob), "5000,5000")153154	deposit(cur, owner, id, 1000)155156	s := splits[id]157	uassert.Equal(t, int64(1000), s.TotalDeposited)158	uassert.Equal(t, int64(500), s.Balances[alice])159	uassert.Equal(t, int64(500), s.Balances[bob])160}161162func TestDeposit_NoCoinsPanics(cur realm, t *testing.T) {163	resetState()164	testing.SetRealm(testing.NewUserRealm(owner))165	id := CreateSplit(cross(cur), addrList(alice), "10000")166167	// no SetOriginSend: nothing attached to the call168	testing.SetOriginSend(nil)169	uassert.AbortsWithMessage(t, cur,170		"deposit must send exactly one coin of "+Denom,171		func() { Deposit(cross(cur), id) },172	)173	uassert.Equal(t, int64(0), splits[id].TotalDeposited)174}175176func TestDeposit_WrongDenomPanics(cur realm, t *testing.T) {177	resetState()178	testing.SetRealm(testing.NewUserRealm(owner))179	id := CreateSplit(cross(cur), addrList(alice), "10000")180181	testing.SetOriginSend(chain.Coins{{Denom: "ufoo", Amount: 100}})182	uassert.AbortsWithMessage(t, cur,183		"deposit must send exactly one coin of "+Denom,184		func() { Deposit(cross(cur), id) },185	)186	testing.SetOriginSend(nil)187}188189func TestDeposit_RoundingDustGoesToHighestShare(cur realm, t *testing.T) {190	resetState()191	testing.SetRealm(testing.NewUserRealm(owner))192	id := CreateSplit(cross(cur), addrList(alice, bob, carol), "3334,3333,3333")193194	deposit(cur, owner, id, 10)195196	s := splits[id]197	uassert.Equal(t, int64(10), s.TotalDeposited)198	// alice: (10*3334)/10000 = 3, +1 dust (highest share)199	uassert.Equal(t, int64(4), s.Balances[alice])200	uassert.Equal(t, int64(3), s.Balances[bob])201	uassert.Equal(t, int64(3), s.Balances[carol])202}203204func TestDeposit_AccumulatesAcrossMultipleDeposits(cur realm, t *testing.T) {205	resetState()206	testing.SetRealm(testing.NewUserRealm(owner))207	id := CreateSplit(cross(cur), addrList(alice, bob), "5000,5000")208209	deposit(cur, owner, id, 100)210	deposit(cur, owner, id, 200)211212	s := splits[id]213	uassert.Equal(t, int64(300), s.TotalDeposited)214	uassert.Equal(t, int64(150), s.Balances[alice])215	uassert.Equal(t, int64(150), s.Balances[bob])216}217218func TestDeposit_OverflowGuard(cur realm, t *testing.T) {219	resetState()220	testing.SetRealm(testing.NewUserRealm(owner))221	id := CreateSplit(cross(cur), addrList(alice), "10000")222223	testing.SetOriginSend(chain.Coins{{Denom: Denom, Amount: MaxDepositAmount + 1}})224	uassert.AbortsWithMessage(t, cur,225		"deposit exceeds maximum supported amount",226		func() { Deposit(cross(cur), id) },227	)228	testing.SetOriginSend(nil)229	uassert.Equal(t, int64(0), splits[id].TotalDeposited)230}231232func TestDeposit_FrozenSplitPanics(cur realm, t *testing.T) {233	resetState()234	testing.SetRealm(testing.NewUserRealm(owner))235	id := CreateSplit(cross(cur), addrList(alice), "10000")236	Freeze(cross(cur), id)237238	testing.SetOriginSend(chain.Coins{{Denom: Denom, Amount: 100}})239	uassert.AbortsWithMessage(t, cur,240		"split is frozen",241		func() { Deposit(cross(cur), id) },242	)243	testing.SetOriginSend(nil)244}245246func TestDeposit_UnknownSplitPanics(cur realm, t *testing.T) {247	resetState()248	testing.SetRealm(testing.NewUserRealm(owner))249250	uassert.AbortsWithMessage(t, cur,251		"split not found: split_99",252		func() { Deposit(cross(cur), "split_99") },253	)254}255256// ---------- Claim (pays out coins) ----------257258func TestClaim_SendsCoins(cur realm, t *testing.T) {259	resetState()260	testing.SetRealm(testing.NewUserRealm(owner))261	id := CreateSplit(cross(cur), addrList(alice, bob), "7000,3000")262	deposit(cur, owner, id, 10000)263264	before := banker.NewReadonlyBanker().GetCoins(alice).AmountOf(Denom)265266	testing.SetRealm(testing.NewUserRealm(alice))267	claimed := Claim(cross(cur), id)268	uassert.Equal(t, int64(7000), claimed)269270	after := banker.NewReadonlyBanker().GetCoins(alice).AmountOf(Denom)271	uassert.Equal(t, int64(7000), after-before)272273	s := splits[id]274	uassert.Equal(t, int64(0), s.Balances[alice])275	uassert.Equal(t, int64(7000), s.TotalClaimed)276}277278func TestClaim_CallerIdentityIsEnforced(cur realm, t *testing.T) {279	resetState()280	testing.SetRealm(testing.NewUserRealm(owner))281	id := CreateSplit(cross(cur), addrList(alice), "10000")282	deposit(cur, owner, id, 100)283284	// the attacker cannot claim alice's balance: identity comes from the285	// chain, not from an argument286	testing.SetRealm(testing.NewUserRealm(attacker))287	uassert.AbortsWithMessage(t, cur,288		"not a recipient of this split",289		func() { Claim(cross(cur), id) },290	)291	uassert.Equal(t, int64(100), splits[id].Balances[alice])292}293294func TestClaim_NothingToClaim(cur realm, t *testing.T) {295	resetState()296	testing.SetRealm(testing.NewUserRealm(owner))297	id := CreateSplit(cross(cur), addrList(alice, bob), "5000,5000")298299	testing.SetRealm(testing.NewUserRealm(alice))300	uassert.AbortsWithMessage(t, cur,301		"nothing to claim",302		func() { Claim(cross(cur), id) },303	)304}305306func TestClaim_CannotClaimTwice(cur realm, t *testing.T) {307	resetState()308	testing.SetRealm(testing.NewUserRealm(owner))309	id := CreateSplit(cross(cur), addrList(alice), "10000")310	deposit(cur, owner, id, 100)311312	testing.SetRealm(testing.NewUserRealm(alice))313	Claim(cross(cur), id)314	uassert.AbortsWithMessage(t, cur,315		"nothing to claim",316		func() { Claim(cross(cur), id) },317	)318}319320func TestClaim_ExRecipientKeepsAccruedBalance(cur realm, t *testing.T) {321	resetState()322	testing.SetRealm(testing.NewUserRealm(owner))323	id := CreateSplit(cross(cur), addrList(alice, bob), "5000,5000")324	deposit(cur, owner, id, 1000)325326	testing.SetRealm(testing.NewUserRealm(owner))327	UpdateShares(cross(cur), id, addrList(carol, dave), "6000,4000")328329	// bob was removed but keeps — and can claim — his accrued 500330	testing.SetRealm(testing.NewUserRealm(bob))331	claimed := Claim(cross(cur), id)332	uassert.Equal(t, int64(500), claimed)333}334335// ---------- UpdateShares ----------336337func TestUpdateShares_Basic(cur realm, t *testing.T) {338	resetState()339	testing.SetRealm(testing.NewUserRealm(owner))340	id := CreateSplit(cross(cur), addrList(alice, bob), "5000,5000")341342	UpdateShares(cross(cur), id, addrList(alice, carol), "7000,3000")343344	s := splits[id]345	uassert.Equal(t, 2, len(s.Recipients))346	uassert.Equal(t, string(alice), string(s.Recipients[0]))347	uassert.Equal(t, string(carol), string(s.Recipients[1]))348	uassert.Equal(t, int64(7000), s.Shares[0])349	uassert.Equal(t, int64(3000), s.Shares[1])350}351352func TestUpdateShares_PreservesUnclaimedBalances(cur realm, t *testing.T) {353	resetState()354	testing.SetRealm(testing.NewUserRealm(owner))355	id := CreateSplit(cross(cur), addrList(alice, bob), "5000,5000")356	deposit(cur, owner, id, 1000)357358	testing.SetRealm(testing.NewUserRealm(owner))359	UpdateShares(cross(cur), id, addrList(carol, dave), "6000,4000")360361	s := splits[id]362	uassert.Equal(t, int64(500), s.Balances[alice])363	uassert.Equal(t, int64(500), s.Balances[bob])364}365366func TestUpdateShares_WrongOwner(cur realm, t *testing.T) {367	resetState()368	testing.SetRealm(testing.NewUserRealm(owner))369	id := CreateSplit(cross(cur), addrList(alice), "10000")370371	testing.SetRealm(testing.NewUserRealm(attacker))372	uassert.AbortsWithMessage(t, cur,373		"only the owner can update shares",374		func() { UpdateShares(cross(cur), id, addrList(alice), "10000") },375	)376}377378func TestUpdateShares_FrozenSplitPanics(cur realm, t *testing.T) {379	resetState()380	testing.SetRealm(testing.NewUserRealm(owner))381	id := CreateSplit(cross(cur), addrList(alice), "10000")382	Freeze(cross(cur), id)383384	uassert.AbortsWithMessage(t, cur,385		"split is frozen",386		func() { UpdateShares(cross(cur), id, addrList(alice, bob), "5000,5000") },387	)388}389390// ---------- TransferOwnership / AcceptOwnership (two-step, v2 Y1) ----------391392func TestTransferOwnership_TwoStep(cur realm, t *testing.T) {393	resetState()394	testing.SetRealm(testing.NewUserRealm(owner))395	id := CreateSplit(cross(cur), addrList(alice), "10000")396397	TransferOwnership(cross(cur), id, bob)398	// staging moves NOTHING: owner, both quotas unchanged399	uassert.Equal(t, string(owner), string(splits[id].Owner))400	uassert.Equal(t, string(bob), string(splits[id].PendingOwner))401	uassert.Equal(t, 1, ownerSplits[owner])402	uassert.Equal(t, 0, ownerSplits[bob])403404	testing.SetRealm(testing.NewUserRealm(bob))405	AcceptOwnership(cross(cur), id)406	uassert.Equal(t, string(bob), string(splits[id].Owner))407	uassert.Equal(t, "", string(splits[id].PendingOwner))408}409410func TestTransferOwnership_WrongOwner(cur realm, t *testing.T) {411	resetState()412	testing.SetRealm(testing.NewUserRealm(owner))413	id := CreateSplit(cross(cur), addrList(alice), "10000")414415	testing.SetRealm(testing.NewUserRealm(attacker))416	uassert.AbortsWithMessage(t, cur,417		"only the owner can transfer ownership",418		func() { TransferOwnership(cross(cur), id, attacker) },419	)420}421422func TestAcceptOwnership_OnlyNominee(cur realm, t *testing.T) {423	resetState()424	testing.SetRealm(testing.NewUserRealm(owner))425	id := CreateSplit(cross(cur), addrList(alice), "10000")426	TransferOwnership(cross(cur), id, bob)427428	// a non-nominee cannot accept — including with no nomination staged429	testing.SetRealm(testing.NewUserRealm(attacker))430	uassert.AbortsWithMessage(t, cur,431		"caller is not the pending owner",432		func() { AcceptOwnership(cross(cur), id) },433	)434	// the owner clears the nomination; the ex-nominee can no longer accept435	testing.SetRealm(testing.NewUserRealm(owner))436	TransferOwnership(cross(cur), id, "")437	testing.SetRealm(testing.NewUserRealm(bob))438	uassert.AbortsWithMessage(t, cur,439		"caller is not the pending owner",440		func() { AcceptOwnership(cross(cur), id) },441	)442}443444func TestAcceptOwnership_MovesOwnerSlotAtConsent(cur realm, t *testing.T) {445	resetState()446	testing.SetRealm(testing.NewUserRealm(owner))447	id := CreateSplit(cross(cur), addrList(alice), "10000")448	uassert.Equal(t, 1, ownerSplits[owner])449	TransferOwnership(cross(cur), id, bob)450451	testing.SetRealm(testing.NewUserRealm(bob))452	AcceptOwnership(cross(cur), id)453454	// the creator's slot is freed; the acceptor's is consumed455	uassert.Equal(t, 0, ownerSplits[owner])456	uassert.Equal(t, 1, ownerSplits[bob])457458	// and the creator can fill the freed slot again459	testing.SetRealm(testing.NewUserRealm(owner))460	for i := 0; i < MaxSplitsPerOwner; i++ {461		CreateSplit(cross(cur), addrList(alice), "10000")462	}463}464465func TestAcceptOwnership_AcceptorAtLimitBlocked(cur realm, t *testing.T) {466	resetState()467	testing.SetRealm(testing.NewUserRealm(bob))468	for i := 0; i < MaxSplitsPerOwner; i++ {469		CreateSplit(cross(cur), addrList(alice), "10000")470	}471472	testing.SetRealm(testing.NewUserRealm(owner))473	id := CreateSplit(cross(cur), addrList(alice), "10000")474	TransferOwnership(cross(cur), id, bob)475476	// the quota gate fires at ACCEPT, against the acceptor's own count477	testing.SetRealm(testing.NewUserRealm(bob))478	uassert.AbortsWithMessage(t, cur,479		"new owner is at the per-owner split limit",480		func() { AcceptOwnership(cross(cur), id) },481	)482}483484// The v2 audit Y1 griefing scenario is dead: staging a transfer at a485// victim consumes none of the victim's quota and changes nothing the486// victim must act on.487func TestTransferOwnership_UnconsentedGriefDefused(cur realm, t *testing.T) {488	resetState()489	testing.SetRealm(testing.NewUserRealm(attacker))490	for i := 0; i < MaxSplitsPerOwner; i++ {491		id := CreateSplit(cross(cur), addrList(alice), "10000")492		TransferOwnership(cross(cur), id, bob) // staged at the victim, never accepted493	}494	// victim quota untouched; victim can still create a full quota of splits495	uassert.Equal(t, 0, ownerSplits[bob])496	testing.SetRealm(testing.NewUserRealm(bob))497	for i := 0; i < MaxSplitsPerOwner; i++ {498		CreateSplit(cross(cur), addrList(alice), "10000")499	}500	// while the attacker's own budget stays consumed — as documented501	uassert.Equal(t, MaxSplitsPerOwner, ownerSplits[attacker])502}503504// Self-transfer is refused at STAGE time. This guard is what makes505// self-accept unreachable, and so what keeps AcceptOwnership's -1/+1506// quota pair from collapsing onto one address (v2 audit G-7.1).507func TestTransferOwnership_SelfRefused(cur realm, t *testing.T) {508	resetState()509	testing.SetRealm(testing.NewUserRealm(owner))510	id := CreateSplit(cross(cur), addrList(alice), "10000")511512	uassert.AbortsWithMessage(t, cur,513		"new owner is already the owner",514		func() { TransferOwnership(cross(cur), id, owner) },515	)516	uassert.Equal(t, 1, ownerSplits[owner])517}518519// Re-staging overwrites the prior nomination; only the latest nominee520// can accept (v2 audit G-7.4).521func TestTransferOwnership_RestageOverwrites(cur realm, t *testing.T) {522	resetState()523	testing.SetRealm(testing.NewUserRealm(owner))524	id := CreateSplit(cross(cur), addrList(alice), "10000")525	TransferOwnership(cross(cur), id, bob)526	TransferOwnership(cross(cur), id, alice)527	uassert.Equal(t, string(alice), GetPendingOwner(id))528529	testing.SetRealm(testing.NewUserRealm(bob))530	uassert.AbortsWithMessage(t, cur,531		"caller is not the pending owner",532		func() { AcceptOwnership(cross(cur), id) },533	)534	testing.SetRealm(testing.NewUserRealm(alice))535	AcceptOwnership(cross(cur), id)536	uassert.Equal(t, string(alice), string(splits[id].Owner))537}538539// A nomination on a split that is later archived is inert — the doc540// comment on AcceptOwnership claims it, this pins it (v2 audit G-7.2).541func TestAcceptOwnership_ArchivedRefused(cur realm, t *testing.T) {542	resetState()543	testing.SetRealm(testing.NewUserRealm(owner))544	id := CreateSplit(cross(cur), addrList(alice), "10000")545	TransferOwnership(cross(cur), id, bob)546	Archive(cross(cur), id)547548	testing.SetRealm(testing.NewUserRealm(bob))549	uassert.AbortsWithMessage(t, cur, "split is archived: "+id,550		func() { AcceptOwnership(cross(cur), id) },551	)552	// the archived split's slot stays returned to its creator, not the nominee553	uassert.Equal(t, 0, ownerSplits[owner])554	uassert.Equal(t, 0, ownerSplits[bob])555}556557// Freezing locks shares and deposits, not custody: a frozen split stays558// transferable so its owner can hand off claim-servicing duty559// (v2 audit G-7.3).560func TestAcceptOwnership_FrozenAllowed(cur realm, t *testing.T) {561	resetState()562	testing.SetRealm(testing.NewUserRealm(owner))563	id := CreateSplit(cross(cur), addrList(alice), "10000")564	TransferOwnership(cross(cur), id, bob)565	Freeze(cross(cur), id)566567	testing.SetRealm(testing.NewUserRealm(bob))568	AcceptOwnership(cross(cur), id)569	uassert.Equal(t, string(bob), string(splits[id].Owner))570	uassert.True(t, splits[id].Frozen)571}572573// The nominee can read the offer without paying gas to probe it, and the574// pending line disappears once consumed or cleared (v2 audit Y1-obs).575func TestPendingOwner_ReadSurface(cur realm, t *testing.T) {576	resetState()577	testing.SetRealm(testing.NewUserRealm(owner))578	id := CreateSplit(cross(cur), addrList(alice), "10000")579	uassert.Equal(t, "", GetPendingOwner(id))580	uassert.False(t, strings.Contains(GetSplitInfo(id), "Pending owner:"))581582	TransferOwnership(cross(cur), id, bob)583	uassert.Equal(t, string(bob), GetPendingOwner(id))584	uassert.True(t, strings.Contains(GetSplitInfo(id), "Pending owner: "+string(bob)))585	uassert.True(t, strings.Contains(Render(""), "Pending owner: "+string(bob)))586587	testing.SetRealm(testing.NewUserRealm(bob))588	AcceptOwnership(cross(cur), id)589	uassert.Equal(t, "", GetPendingOwner(id))590	uassert.False(t, strings.Contains(GetSplitInfo(id), "Pending owner:"))591}592593// ---------- Freeze ----------594595func TestFreeze_Basic(cur realm, t *testing.T) {596	resetState()597	testing.SetRealm(testing.NewUserRealm(owner))598	id := CreateSplit(cross(cur), addrList(alice), "10000")599600	Freeze(cross(cur), id)601	uassert.True(t, splits[id].Frozen, "split must be frozen after Freeze()")602}603604func TestFreeze_WrongOwner(cur realm, t *testing.T) {605	resetState()606	testing.SetRealm(testing.NewUserRealm(owner))607	id := CreateSplit(cross(cur), addrList(alice), "10000")608609	testing.SetRealm(testing.NewUserRealm(attacker))610	uassert.AbortsWithMessage(t, cur,611		"only the owner can freeze",612		func() { Freeze(cross(cur), id) },613	)614}615616func TestFreeze_ClaimsStillWork(cur realm, t *testing.T) {617	resetState()618	testing.SetRealm(testing.NewUserRealm(owner))619	id := CreateSplit(cross(cur), addrList(alice), "10000")620	deposit(cur, owner, id, 100)621622	testing.SetRealm(testing.NewUserRealm(owner))623	Freeze(cross(cur), id)624625	testing.SetRealm(testing.NewUserRealm(alice))626	uassert.Equal(t, int64(100), Claim(cross(cur), id))627}628629// ---------- Archive ----------630631func TestArchive_BlocksWithOutstandingBalance(cur realm, t *testing.T) {632	resetState()633	testing.SetRealm(testing.NewUserRealm(owner))634	id := CreateSplit(cross(cur), addrList(alice), "10000")635	deposit(cur, owner, id, 100)636637	testing.SetRealm(testing.NewUserRealm(owner))638	uassert.AbortsWithMessage(t, cur,639		"cannot archive: outstanding balance for "+string(alice),640		func() { Archive(cross(cur), id) },641	)642}643644func TestArchive_BlocksOnExRecipientBalance(cur realm, t *testing.T) {645	resetState()646	testing.SetRealm(testing.NewUserRealm(owner))647	id := CreateSplit(cross(cur), addrList(alice, bob), "5000,5000")648	deposit(cur, owner, id, 1000)649650	testing.SetRealm(testing.NewUserRealm(owner))651	// remove bob; his 500 stays claimable652	UpdateShares(cross(cur), id, addrList(alice), "10000")653	testing.SetRealm(testing.NewUserRealm(alice))654	Claim(cross(cur), id)655656	// archive must refuse while the EX-recipient still has a balance —657	// archiving would lock his claim forever658	testing.SetRealm(testing.NewUserRealm(owner))659	uassert.AbortsWithMessage(t, cur,660		"cannot archive: outstanding balance for "+string(bob),661		func() { Archive(cross(cur), id) },662	)663664	// once bob claims, archive succeeds665	testing.SetRealm(testing.NewUserRealm(bob))666	Claim(cross(cur), id)667	testing.SetRealm(testing.NewUserRealm(owner))668	Archive(cross(cur), id)669	uassert.True(t, splits[id].Archived, "split must be archived")670}671672// ---------- queries ----------673674func TestGetClaimable(cur realm, t *testing.T) {675	resetState()676	testing.SetRealm(testing.NewUserRealm(owner))677	id := CreateSplit(cross(cur), addrList(alice, bob), "8000,2000")678	deposit(cur, owner, id, 5000)679680	uassert.Equal(t, int64(4000), GetClaimable(id, alice))681	uassert.Equal(t, int64(1000), GetClaimable(id, bob))682	uassert.Equal(t, int64(0), GetClaimable(id, attacker))683}684685func TestGetSplitInfo_ContainsExpectedFields(cur realm, t *testing.T) {686	resetState()687	testing.SetRealm(testing.NewUserRealm(owner))688	id := CreateSplit(cross(cur), addrList(alice, bob), "5000,5000")689	deposit(cur, owner, id, 200)690691	info := GetSplitInfo(id)692	uassert.True(t, strings.Contains(info, "Owner"), "GetSplitInfo output must contain 'Owner'")693	uassert.True(t, strings.Contains(info, string(owner)), "GetSplitInfo output must contain owner address")694	uassert.True(t, strings.Contains(info, string(alice)), "GetSplitInfo output must contain recipient")695}696697// ---------- render ----------698699func TestRender_EmptyState(cur realm, t *testing.T) {700	resetState()701702	out := Render("")703	uassert.Equal(t, "# Fee Split\n\nNo active splits.\n", out)704}705706func TestRender_WithSplits(cur realm, t *testing.T) {707	resetState()708	testing.SetRealm(testing.NewUserRealm(owner))709	CreateSplit(cross(cur), addrList(alice, bob), "6000,4000")710	deposit(cur, owner, "split_1", 1000)711712	out := Render("")713	uassert.True(t, strings.Contains(out, "split_1"), "Render output must reference split_1")714	uassert.True(t, strings.Contains(out, string(owner)), "Render output must include owner")715}716717// ---------- re-audit regressions (2026-09-02) ----------718719func TestCreateSplit_ShareSumWraparoundRejected(cur realm, t *testing.T) {720	resetState()721	testing.SetRealm(testing.NewUserRealm(owner))722723	// re-audit P1: these shares sum past 2^64 back to exactly 10000; the724	// per-share <=10000 bound must refuse them before the sum can wrap725	uassert.AbortsWithMessage(t, cur,726		"invalid share value: 1152921505606846976",727		func() {728			CreateSplit(cross(cur), addrList(alice, bob, carol),729				"1152921505606846976,8646911284051357320,8646911284051357320")730		},731	)732	uassert.AbortsWithMessage(t, cur,733		"invalid share value: 10001",734		func() { CreateSplit(cross(cur), addrList(alice, bob), "10001,10000") },735	)736}737738func TestArchiveFreesActiveListAndOwnerSlot(cur realm, t *testing.T) {739	resetState()740	testing.SetRealm(testing.NewUserRealm(owner))741742	id := CreateSplit(cross(cur), addrList(alice), "10000")743	Archive(cross(cur), id)744	uassert.Equal(t, 1, len(splits), "archived entry stays for audit")745	uassert.Equal(t, 0, len(splitIDs), "archived entry frees the active list")746	CreateSplit(cross(cur), addrList(alice), "10000") // must not abort747}748749func TestNoGlobalCap_SybilsCannotBlockOthers(cur realm, t *testing.T) {750	resetState()751752	// round-4 P2: with per-owner quotas only, other owners filling their753	// own budgets never block a legitimate creator754	for i := 0; i < 30; i++ {755		syb := testutils.TestAddress("sybil_" + strconv.Itoa(i))756		testing.SetRealm(testing.NewUserRealm(syb))757		for j := 0; j < MaxSplitsPerOwner; j++ {758			CreateSplit(cross(cur), addrList(alice), "10000")759		}760	}761	uassert.Equal(t, 300, len(splitIDs))762763	testing.SetRealm(testing.NewUserRealm(owner))764	CreateSplit(cross(cur), addrList(alice), "10000") // must not abort765}766767func TestPreParseInputBounds(cur realm, t *testing.T) {768	resetState()769	testing.SetRealm(testing.NewUserRealm(owner))770771	// round-4 P2: oversized inputs must be refused BEFORE parsing work772	uassert.AbortsWithMessage(t, cur,773		"recipient list too long",774		func() { CreateSplit(cross(cur), strings.Repeat(",", MaxRecipientListLen+1), "10000") },775	)776	uassert.AbortsWithMessage(t, cur,777		"share list too long",778		func() { CreateSplit(cross(cur), addrList(alice), strings.Repeat("1,", MaxShareListLen)) },779	)780}781782func TestStraySendRejected(cur realm, t *testing.T) {783	resetState()784	testing.SetRealm(testing.NewUserRealm(owner))785	id := CreateSplit(cross(cur), addrList(alice), "10000")786	deposit(cur, owner, id, 100)787788	// re-audit P3: coins attached to a non-Deposit call must abort (and789	// therefore revert to the sender), never strand on the realm address790	testing.SetRealm(testing.NewUserRealm(alice))791	testing.SetOriginSend(chain.Coins{{Denom: Denom, Amount: 50}})792	uassert.AbortsWithMessage(t, cur,793		"this function does not accept coins; attach coins to Deposit only",794		func() { Claim(cross(cur), id) },795	)796	testing.SetOriginSend(nil)797798	// without coins attached the same claim succeeds799	uassert.Equal(t, int64(100), Claim(cross(cur), id))800}801802func TestDeposit_CodeRealmRefused(cur realm, t *testing.T) {803	resetState()804	testing.SetRealm(testing.NewUserRealm(owner))805	id := CreateSplit(cross(cur), addrList(alice), "10000")806807	// re-audit P1 companion: only a direct user MsgCall may deposit — a808	// code realm as previous realm must be refused (its send would not809	// have landed here). The MsgRun/ephemeral shape (the other half of810	// the IsUserCall-not-IsUser rationale) cannot be constructed in this811	// driver: SetRealm accepts only user realms and /r/ code realms812	// (NewCodeRealm rejects /e/ paths), so the predicate choice is813	// pinned here via the code-realm branch and verified live on-chain.814	testing.SetRealm(testing.NewCodeRealm("gno.land/r/demo/intermediary"))815	testing.SetOriginSend(chain.Coins{{Denom: Denom, Amount: 100}})816	uassert.AbortsContains(t, cur,817		"deposits must be sent by direct call",818		func() { Deposit(cross(cur), id) },819	)820	testing.SetOriginSend(nil)821}822823// ---------- round-3 audit regressions (2026-09-02) ----------824825func TestUppercaseAddressesRejected(cur realm, t *testing.T) {826	resetState()827	testing.SetRealm(testing.NewUserRealm(owner))828829	// round-3 P2: all-uppercase bech32 passes IsValid but can never830	// match caller(): as a recipient it locks funds and blocks Archive;831	// as upper/lower twins it bypasses the duplicate check; as an owner832	// it orphans the split833	upper := strings.ToUpper(string(alice))834	uassert.AbortsWithMessage(t, cur,835		"invalid recipient address: "+upper,836		func() { CreateSplit(cross(cur), string(alice)+","+upper, "5000,5000") },837	)838839	id := CreateSplit(cross(cur), addrList(alice), "10000")840	uassert.AbortsWithMessage(t, cur,841		"invalid new owner address: "+upper,842		func() { TransferOwnership(cross(cur), id, address(upper)) },843	)844}845846func TestRender_OnlyArchivedSplits(cur realm, t *testing.T) {847	resetState()848	testing.SetRealm(testing.NewUserRealm(owner))849	id := CreateSplit(cross(cur), addrList(alice), "10000")850	Archive(cross(cur), id)851852	uassert.Equal(t, "# Fee Split\n\nNo active splits.\n", Render(""),853		"archived-only state must render as no active splits")854}855856// ---------- protocol fee (2026-09-03) ----------857858func TestFee_DefaultZeroAndCap(cur realm, t *testing.T) {859	resetState()860	uassert.Equal(t, int64(0), feeBps)861862	testing.SetRealm(testing.NewUserRealm(feeAdminAddr))863	uassert.AbortsContains(t, cur, "fee must be between 0 and",864		func() { SetFee(cross(cur), MaxFeeBps+1) })865	uassert.AbortsContains(t, cur, "fee must be between 0 and",866		func() { SetFee(cross(cur), -1) })867	SetFee(cross(cur), MaxFeeBps) // exactly the cap is allowed868	uassert.Equal(t, MaxFeeBps, feeBps)869870	testing.SetRealm(testing.NewUserRealm(attacker))871	uassert.AbortsWithMessage(t, cur, "only the fee admin can set the fee",872		func() { SetFee(cross(cur), 1) })873}874875func TestFee_TakenFromDepositNetAccounting(cur realm, t *testing.T) {876	resetState()877	testing.SetRealm(testing.NewUserRealm(feeAdminAddr))878	SetFee(cross(cur), 50) // 0.5%879880	testing.SetRealm(testing.NewUserRealm(owner))881	id := CreateSplit(cross(cur), addrList(alice, bob), "5000,5000")882	deposit(cur, owner, id, 10000)883884	s := splits[id]885	// fee 50 off the top; 9950 net distributed (4975 + 4975)886	uassert.Equal(t, int64(50), feesAccrued)887	uassert.Equal(t, int64(9950), s.TotalDeposited)888	uassert.Equal(t, int64(4975), s.Balances[alice])889	uassert.Equal(t, int64(4975), s.Balances[bob])890	// conservation on net amounts holds untouched891	uassert.Equal(t, s.TotalDeposited, s.Balances[alice]+s.Balances[bob]+s.TotalClaimed)892}893894func TestFee_NeverRetroactive(cur realm, t *testing.T) {895	resetState()896	testing.SetRealm(testing.NewUserRealm(owner))897	id := CreateSplit(cross(cur), addrList(alice), "10000")898	deposit(cur, owner, id, 10000) // fee 0 at deposit time899900	testing.SetRealm(testing.NewUserRealm(feeAdminAddr))901	SetFee(cross(cur), MaxFeeBps)902903	// the previously credited balance is untouched by the fee change904	uassert.Equal(t, int64(10000), splits[id].Balances[alice])905	testing.SetRealm(testing.NewUserRealm(alice))906	uassert.Equal(t, int64(10000), Claim(cross(cur), id))907}908909func TestFee_ClaimFeesSendsCoins(cur realm, t *testing.T) {910	resetState()911	testing.SetRealm(testing.NewUserRealm(feeAdminAddr))912	SetFee(cross(cur), 100) // 1%913914	testing.SetRealm(testing.NewUserRealm(owner))915	id := CreateSplit(cross(cur), addrList(alice), "10000")916	deposit(cur, owner, id, 50000) // fee 500917918	testing.SetRealm(testing.NewUserRealm(attacker))919	uassert.AbortsWithMessage(t, cur, "only the fee admin can claim fees",920		func() { ClaimFees(cross(cur)) })921922	before := banker.NewReadonlyBanker().GetCoins(feeAdminAddr).AmountOf(Denom)923	testing.SetRealm(testing.NewUserRealm(feeAdminAddr))924	uassert.Equal(t, int64(500), ClaimFees(cross(cur)))925	after := banker.NewReadonlyBanker().GetCoins(feeAdminAddr).AmountOf(Denom)926	uassert.Equal(t, int64(500), after-before)927	uassert.Equal(t, int64(0), feesAccrued)928	uassert.Equal(t, int64(500), feesClaimed)929930	uassert.AbortsWithMessage(t, cur, "no fees accrued", func() { ClaimFees(cross(cur)) })931}932933func TestFee_TinyDepositFullyConsumedRefused(cur realm, t *testing.T) {934	resetState()935	testing.SetRealm(testing.NewUserRealm(feeAdminAddr))936	SetFee(cross(cur), MaxFeeBps) // 1%937938	testing.SetRealm(testing.NewUserRealm(owner))939	id := CreateSplit(cross(cur), addrList(alice), "10000")940	// amounts < 100 at 1% round the fee to 0 — deposit passes whole941	deposit(cur, owner, id, 99)942	uassert.Equal(t, int64(0), feesAccrued)943	uassert.Equal(t, int64(99), splits[id].Balances[alice])944}945946func TestFee_TwoStepAdminHandover(cur realm, t *testing.T) {947	resetState()948	newAdmin := testutils.TestAddress("newfeeadmin")949950	testing.SetRealm(testing.NewUserRealm(attacker))951	uassert.AbortsWithMessage(t, cur, "only the fee admin can nominate a successor",952		func() { NominateFeeAdmin(cross(cur), attacker) })953954	testing.SetRealm(testing.NewUserRealm(feeAdminAddr))955	uassert.AbortsContains(t, cur, "invalid nominee address",956		func() { NominateFeeAdmin(cross(cur), address("JUNK")) })957	NominateFeeAdmin(cross(cur), newAdmin)958	// nomination alone changes nothing959	uassert.Equal(t, string(feeAdminAddr), string(feeAdmin))960961	testing.SetRealm(testing.NewUserRealm(attacker))962	uassert.AbortsWithMessage(t, cur, "caller is not the pending fee admin",963		func() { AcceptFeeAdmin(cross(cur)) })964965	testing.SetRealm(testing.NewUserRealm(newAdmin))966	AcceptFeeAdmin(cross(cur))967	uassert.Equal(t, string(newAdmin), string(feeAdmin))968	uassert.Equal(t, "", string(pendingFeeAdmin))969970	// clearing a nomination works971	testing.SetRealm(testing.NewUserRealm(newAdmin))972	NominateFeeAdmin(cross(cur), feeAdminAddr)973	NominateFeeAdmin(cross(cur), "")974	testing.SetRealm(testing.NewUserRealm(feeAdminAddr))975	uassert.AbortsWithMessage(t, cur, "caller is not the pending fee admin",976		func() { AcceptFeeAdmin(cross(cur)) })977}978979func TestFee_StraySendRejectedOnFeeFunctions(cur realm, t *testing.T) {980	resetState()981	testing.SetRealm(testing.NewUserRealm(feeAdminAddr))982	testing.SetOriginSend(chain.Coins{{Denom: Denom, Amount: 5}})983	uassert.AbortsWithMessage(t, cur,984		"this function does not accept coins; attach coins to Deposit only",985		func() { SetFee(cross(cur), 10) },986	)987	testing.SetOriginSend(nil)988}989990// ---------- round-5 audit regressions (fee path, 2026-09-03) ----------991992// Pins the benign-degradation safety property: an empty fee admin (the993// gno-test-VM init state, and any hypothetical empty-origin deploy)994// leaves the fee permanently disabled and zero funds at risk — SetFee995// can never be authorized, so feeBps stays 0 and nothing accrues.996func TestFee_EmptyAdminDegradesToDisabled(cur realm, t *testing.T) {997	resetState()998	feeAdmin = ""999	feeBps = 010001001	for _, who := range []address{owner, alice, feeAdminAddr, attacker} {1002		testing.SetRealm(testing.NewUserRealm(who))1003		uassert.AbortsWithMessage(t, cur, "only the fee admin can set the fee",1004			func() { SetFee(cross(cur), 100) })1005	}1006	uassert.Equal(t, int64(0), feeBps, "fee must remain 0 with no reachable admin")10071008	// a deposit accrues nothing1009	testing.SetRealm(testing.NewUserRealm(owner))1010	id := CreateSplit(cross(cur), addrList(alice), "10000")1011	deposit(cur, owner, id, 10000)1012	uassert.Equal(t, int64(0), feesAccrued)1013	testing.SetRealm(testing.NewUserRealm(alice))1014	uassert.Equal(t, int64(10000), Claim(cross(cur), id)) // full amount, no fee1015}10161017// Fees accrued before a handover are claimable by the NEW admin1018// (documented semantics: claim before transferring if that matters).1019func TestFee_HandoverTransfersAccruedClaimRight(cur realm, t *testing.T) {1020	resetState()1021	newFa := testutils.TestAddress("newfa")1022	testing.SetRealm(testing.NewUserRealm(feeAdminAddr))1023	SetFee(cross(cur), 100)1024	testing.SetRealm(testing.NewUserRealm(owner))1025	id := CreateSplit(cross(cur), addrList(alice), "10000")1026	deposit(cur, owner, id, 100000) // 1000 accrued under the original admin10271028	testing.SetRealm(testing.NewUserRealm(feeAdminAddr))1029	NominateFeeAdmin(cross(cur), newFa)1030	testing.SetRealm(testing.NewUserRealm(newFa))1031	AcceptFeeAdmin(cross(cur))1032	uassert.Equal(t, int64(1000), ClaimFees(cross(cur)), "new admin claims prior-regime fees")10331034	// the old admin can no longer claim — fails the authorization check1035	testing.SetRealm(testing.NewUserRealm(feeAdminAddr))1036	uassert.AbortsWithMessage(t, cur, "only the fee admin can claim fees", func() { ClaimFees(cross(cur)) })1037}10381039// Every fee function rejects an attached send (only SetFee was pinned).1040func TestFee_AllFunctionsRejectStraySend(cur realm, t *testing.T) {1041	resetState()1042	testing.SetRealm(testing.NewUserRealm(feeAdminAddr))1043	send := chain.Coins{{Denom: Denom, Amount: 3}}1044	msg := "this function does not accept coins; attach coins to Deposit only"10451046	testing.SetOriginSend(send)1047	uassert.AbortsWithMessage(t, cur, msg, func() { SetFee(cross(cur), 10) })1048	uassert.AbortsWithMessage(t, cur, msg, func() { NominateFeeAdmin(cross(cur), attacker) })1049	uassert.AbortsWithMessage(t, cur, msg, func() { ClaimFees(cross(cur)) })1050	testing.SetOriginSend(nil)10511052	// AcceptFeeAdmin from the pending side, also with a send1053	NominateFeeAdmin(cross(cur), attacker)1054	testing.SetRealm(testing.NewUserRealm(attacker))1055	testing.SetOriginSend(send)1056	uassert.AbortsWithMessage(t, cur, msg, func() { AcceptFeeAdmin(cross(cur)) })1057	testing.SetOriginSend(nil)1058}10591060// ---------- audit round-1 gap closures (2026-09-21) ----------10611062func TestStraySendRejectedOnAllSplitFunctions(cur realm, t *testing.T) {1063	resetState()1064	testing.SetRealm(testing.NewUserRealm(owner))1065	id := CreateSplit(cross(cur), addrList(alice, bob), "5000,5000")10661067	// audit Y2 gap 2: only Claim and the fee functions were pinned; the1068	// five split-management functions call the same guard — prove it1069	msg := "this function does not accept coins; attach coins to Deposit only"1070	testing.SetOriginSend(chain.Coins{{Denom: Denom, Amount: 7}})1071	uassert.AbortsWithMessage(t, cur, msg, func() { CreateSplit(cross(cur), addrList(carol), "10000") })1072	uassert.AbortsWithMessage(t, cur, msg, func() { UpdateShares(cross(cur), id, addrList(alice), "10000") })1073	uassert.AbortsWithMessage(t, cur, msg, func() { TransferOwnership(cross(cur), id, carol) })1074	uassert.AbortsWithMessage(t, cur, msg, func() { AcceptOwnership(cross(cur), id) })1075	uassert.AbortsWithMessage(t, cur, msg, func() { Freeze(cross(cur), id) })1076	uassert.AbortsWithMessage(t, cur, msg, func() { Archive(cross(cur), id) })1077	testing.SetOriginSend(nil)1078}10791080func TestArchivedSplitRefusesDepositAndClaim(cur realm, t *testing.T) {1081	resetState()1082	testing.SetRealm(testing.NewUserRealm(owner))1083	id := CreateSplit(cross(cur), addrList(alice), "10000")1084	deposit(cur, owner, id, 100)1085	testing.SetRealm(testing.NewUserRealm(alice))1086	Claim(cross(cur), id)1087	testing.SetRealm(testing.NewUserRealm(owner))1088	Archive(cross(cur), id)10891090	// audit Y2 gap 3: the terminal state must refuse both value paths1091	testing.SetRealm(testing.NewUserRealm(owner))1092	testing.SetOriginSend(chain.Coins{{Denom: Denom, Amount: 50}})1093	uassert.AbortsWithMessage(t, cur, "split is archived: "+id,1094		func() { Deposit(cross(cur), id) })1095	testing.SetOriginSend(nil)1096	testing.SetRealm(testing.NewUserRealm(alice))1097	uassert.AbortsWithMessage(t, cur, "split is archived: "+id,1098		func() { Claim(cross(cur), id) })1099}11001101func TestDeposit_TwoCoinEnvelopeRejected(cur realm, t *testing.T) {1102	resetState()1103	testing.SetRealm(testing.NewUserRealm(owner))1104	id := CreateSplit(cross(cur), addrList(alice), "10000")11051106	// audit Y2 gap 4: only zero-coin and wrong-denom were pinned; a1107	// two-coin envelope must also be refused by the exact len==1 check1108	testing.SetOriginSend(chain.Coins{{Denom: Denom, Amount: 100}, {Denom: "ufoo", Amount: 1}})1109	uassert.AbortsWithMessage(t, cur,1110		"deposit must send exactly one coin of "+Denom,1111		func() { Deposit(cross(cur), id) })1112	testing.SetOriginSend(nil)1113	uassert.Equal(t, int64(0), splits[id].TotalDeposited)1114}11151116func TestDust_EqualShareTieGoesToFirst(cur realm, t *testing.T) {1117	resetState()1118	testing.SetRealm(testing.NewUserRealm(owner))1119	id := CreateSplit(cross(cur), addrList(alice, bob), "5000,5000")11201121	// audit Y2 gap 5: the dust rule at an exact share tie is1122	// first-listed-wins (strict > never replaces dustIdx) — pin it1123	deposit(cur, owner, id, 101)1124	uassert.Equal(t, int64(51), splits[id].Balances[alice])1125	uassert.Equal(t, int64(50), splits[id].Balances[bob])1126}1127

Functions

  • AcceptFeeAdmin(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string})

  • AcceptOwnership(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, splitID string)

  • Archive(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, splitID string)

  • Claim(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, splitID string) int64

  • ClaimFees(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}) int64

  • CreateSplit(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, recipientList string, shareList string) string

  • Deposit(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, splitID string)

  • Freeze(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, splitID string)

  • GetClaimable(splitID string, addr string) int64

  • GetFeeInfo() string

  • GetPendingOwner(splitID string) string

  • GetSplitInfo(splitID string) string

  • NominateFeeAdmin(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, nominee string)

  • Render(path string) string

  • SetFee(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, bps int64)

  • TransferOwnership(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, splitID string, newOwner string)

  • UpdateShares(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, splitID string, recipientList string, shareList string)

Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.

Rendered

RenderedRawgnoweb ↗

vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.