1package campaign23import (4 unsaferealm "chain/runtime/unsafe"5 "strings"67 "gno.land/p/nt/avl/v0"8)910var (11 collectionOwner address = "g1wx60nkcp5lfsxmedeg7sfs47tzxkath05u63t4"12 approvedConsumer string13 entries avl.Tree // wallet.String() -> *entry14)1516type entry struct {17 Bps int6418 RemainingUses int6419}2021func callerAddress() address {22 return unsaferealm.PreviousRealm().Address()23}2425func assertOwner() {26 if callerAddress() != collectionOwner {27 panic("unauthorized: caller is not the collection owner")28 }29}3031func SetApprovedConsumer(cur realm, pkgpath string) {32 assertOwner()33 approvedConsumer = pkgpath34}3536func validateBps(bps int64) {37 if bps < 0 || bps > 10000 {38 panic("bps must be between 0 and 10000")39 }40}4142func validateMaxUses(maxUses int64) {43 if maxUses <= 0 {44 panic("maxUses must be positive")45 }46}4748func grantOne(wallet string, bps int64, maxUses int64) {49 if raw := entries.Get(wallet); raw != nil {50 if e, ok := raw.(*entry); ok {51 e.RemainingUses += maxUses52 if bps < e.Bps {53 e.Bps = bps54 }55 entries.Set(wallet, e)56 return57 }58 }59 entries.Set(wallet, &entry{Bps: bps, RemainingUses: maxUses})60}6162func AddWallet(cur realm, wallet address, bps int64, maxUses int64) {63 assertOwner()64 validateBps(bps)65 validateMaxUses(maxUses)66 grantOne(wallet.String(), bps, maxUses)67}6869func AddWalletsBatch(cur realm, walletsCSV string, bps int64, maxUses int64) {70 assertOwner()71 validateBps(bps)72 validateMaxUses(maxUses)73 for _, w := range strings.Split(walletsCSV, ",") {74 w = strings.TrimSpace(w)75 if w == "" {76 continue77 }78 grantOne(w, bps, maxUses)79 }80}8182func RemoveWallet(cur realm, wallet address) {83 assertOwner()84 entries.Remove(wallet.String())85}8687func RemoveWalletsBatch(cur realm, walletsCSV string) {88 assertOwner()89 for _, w := range strings.Split(walletsCSV, ",") {90 w = strings.TrimSpace(w)91 if w == "" {92 continue93 }94 entries.Remove(w)95 }96}9798func assertCallerIsApprovedConsumer(cur realm) {99 caller := cur.Previous().PkgPath()100 if approvedConsumer == "" || caller != approvedConsumer {101 panic("unauthorized: caller is not the approved consumer realm")102 }103}104105func Peek(wallet address) (bps int64, remainingUses int64, ok bool) {106 raw := entries.Get(wallet.String())107 e, isEntry := raw.(*entry)108 if !isEntry {109 return 0, 0, false110 }111 return e.Bps, e.RemainingUses, true112}113114func ConsumeDiscount(cur realm, wallet address) (bps int64, ok bool) {115 assertCallerIsApprovedConsumer(cur)116 raw := entries.Get(wallet.String())117 e, isEntry := raw.(*entry)118 if !isEntry {119 return 0, false120 }121 e.RemainingUses--122 if e.RemainingUses <= 0 {123 entries.Remove(wallet.String())124 } else {125 entries.Set(wallet.String(), e)126 }127 return e.Bps, true128}129130func Count() int64 { return int64(entries.Size()) }131132func EntryAt(index int) (wallet string, bps int64, remainingUses int64) {133 w, v := entries.GetByIndex(index)134 e := v.(*entry)135 return w, e.Bps, e.RemainingUses136}137AddWallet(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}, wallet string, bps int64, maxUses int64)
AddWalletsBatch(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}, walletsCSV string, bps int64, maxUses int64)
ConsumeDiscount(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}, wallet string) (bps int64, ok bool)
Count() int64
EntryAt(index int) (wallet string, bps int64, remainingUses int64)
Peek(wallet string) (bps int64, remainingUses int64, ok bool)
RemoveWallet(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}, wallet string)
RemoveWalletsBatch(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}, walletsCSV string)
SetApprovedConsumer(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}, pkgpath string)
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.
Render unavailable
rate limit exceeded — the render query is served by the Gnoland archive RPC.
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.