PathrockNetwork Gno Explorer
HomeBlocksTransactionsRealmsPackagesValidators

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/g1n4pl5uc4yt5r96m9w6fmdznx3x0jyg8l6arhmt/gnomi/pointsv2

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
pointsv2
Namespace
g1n4pl5uc4yt5r96m9w6fmdznx3x0jyg8l6arhmt / gnomi
Files
2 (gnomod.toml)
Exported functions
19
Module
gno.land/r/g1n4pl5uc4yt5r96m9w6fmdznx3x0jyg8l6arhmt/gnomi/pointsv2
gno
0.9

Files (2)

  • gnomod.tomltoml
  • pointsv2.gnogno
pointsv2.gnogno
1// Package pointsv2 is referral + check-in + trade/create points for gnomemepad.2//3// Trade/create awards are pad-only: allowed pad packages call OnTrade / OnCreate4// after a successful user action. EOAs cannot self-award trade points.5//6//	SetReferrer / CheckIn / AwardPoints / Leaderboard — same as v17//	AllowPad / RevokePad — admin allowlist of pad package paths8//	OnTrade / OnCreate — called by pad via cross(cur)9//	ParamsInfo — extended for UI10package pointsv21112import (13	"chain"14	"chain/runtime"15	"strconv"16	"strings"1718	"gno.land/p/nt/avl/v0"19)2021const (22	PointsReferrerBonus  int64 = 5023	PointsRefereeBonus   int64 = 2524	PointsCheckIn        int64 = 525	CheckInIntervalH     int64 = 10026	// Trade / create (v2)27	PointsCreateBonus    int64 = 30  // creator on successful Create28	PointsBuyBase        int64 = 2   // base points per buy29	PointsSellBase       int64 = 1   // base points per sell30	PointsPerGnotBuy     int64 = 10  // extra pts per full GNOT bought (volume)31	PointsPerGnotSell    int64 = 3   // extra pts per full GNOT sold32	MaxTradePtsPerHeight int64 = 200 // per-user per-height cap (anti-spam)33	UgnotPerGnot         int64 = 1_000_00034	LeaderboardMax       int   = 5035	MaxLeaderboardReturn int   = 2036	MaxPadPathLen        int   = 20037)3839var (40	admin  address41	inited bool42	// pointsByAddr: address string -> int6443	pointsByAddr avl.Tree44	// referrerOf: address string -> referrer address string45	referrerOf avl.Tree46	// lastCheckIn: address string -> height int6447	lastCheckIn avl.Tree48	// allowedPads: package path -> true49	allowedPads avl.Tree50	// tradePtsAtHeight: "addr|height" -> int64 points already awarded that height51	tradePtsAtHeight avl.Tree52	// totalTradePts / totalCreatePts counters for ops53	totalTradePts  int6454	totalCreatePts int6455)5657func Init(cur realm) {58	if inited {59		panic("pointsv2: already initialized")60	}61	if !cur.Previous().IsUserCall() {62		panic("pointsv2: EOA only")63	}64	admin = cur.Previous().Address()65	inited = true66	pointsByAddr = avl.Tree{}67	referrerOf = avl.Tree{}68	lastCheckIn = avl.Tree{}69	allowedPads = avl.Tree{}70	tradePtsAtHeight = avl.Tree{}71	chain.Emit("Init", "admin", admin.String())72}7374func requireInit() {75	if !inited {76		panic("pointsv2: call Init first")77	}78}7980func requireAdmin(cur realm) {81	requireInit()82	if !cur.Previous().IsUserCall() {83		panic("pointsv2: EOA only")84	}85	if cur.Previous().Address() != admin {86		panic("pointsv2: not admin")87	}88}8990func getPts(addr string) int64 {91	v := pointsByAddr.Get(addr)92	if v == nil {93		return 094	}95	n, ok := v.(int64)96	if !ok {97		return 098	}99	return n100}101102func addPts(addr string, delta int64) {103	if delta == 0 || addr == "" {104		return105	}106	cur := getPts(addr)107	next := cur + delta108	if next < 0 {109		next = 0110	}111	pointsByAddr.Set(addr, next)112}113114// AllowPad registers a pad package path that may call OnTrade / OnCreate.115func AllowPad(cur realm, padPkg string) {116	requireAdmin(cur)117	padPkg = strings.TrimSpace(padPkg)118	if padPkg == "" || len(padPkg) > MaxPadPathLen {119		panic("pointsv2: invalid pad path")120	}121	if !strings.HasPrefix(padPkg, "gno.land/r/") {122		panic("pointsv2: pad must be gno.land/r/…")123	}124	allowedPads.Set(padPkg, true)125	chain.Emit("AllowPad", "pad", padPkg)126}127128// RevokePad removes a pad from the allowlist.129func RevokePad(cur realm, padPkg string) {130	requireAdmin(cur)131	padPkg = strings.TrimSpace(padPkg)132	allowedPads.Remove(padPkg)133	chain.Emit("RevokePad", "pad", padPkg)134}135136// IsPadAllowed reports whether path may award trade points.137func IsPadAllowed(padPkg string) bool {138	requireInit()139	return allowedPads.Has(strings.TrimSpace(padPkg))140}141142// ListPads returns allowed pad paths, one per line.143func ListPads() string {144	requireInit()145	out := ""146	allowedPads.Iterate("", "", func(key string, _ any) bool {147		if out != "" {148			out += "\n"149		}150		out += key151		return false152	})153	return out154}155156func requireAllowedPad(cur realm) string {157	requireInit()158	prev := cur.Previous()159	// Must be a realm call (pad), not EOA160	if prev.IsUserCall() {161		panic("pointsv2: pad realm only")162	}163	path := prev.PkgPath()164	if path == "" || !allowedPads.Has(path) {165		panic("pointsv2: pad not allowed")166	}167	return path168}169170func heightCapKey(addr string, h int64) string {171	return addr + "|" + strconv.FormatInt(h, 10)172}173174func remainingHeightCap(addr string, h int64) int64 {175	k := heightCapKey(addr, h)176	used := int64(0)177	if v := tradePtsAtHeight.Get(k); v != nil {178		used, _ = v.(int64)179	}180	left := MaxTradePtsPerHeight - used181	if left < 0 {182		return 0183	}184	return left185}186187func consumeHeightCap(addr string, h, award int64) int64 {188	if award <= 0 {189		return 0190	}191	left := remainingHeightCap(addr, h)192	if left <= 0 {193		return 0194	}195	if award > left {196		award = left197	}198	k := heightCapKey(addr, h)199	used := int64(0)200	if v := tradePtsAtHeight.Get(k); v != nil {201		used, _ = v.(int64)202	}203	tradePtsAtHeight.Set(k, used+award)204	return award205}206207// OnTrade awards points for a pad buy (side=0) or sell (side=1).208// volumeUgnot is the GNOT notional of the trade (sent in / received out).209// Returns points awarded (0 if capped).210func OnTrade(cur realm, trader address, launchID string, side int64, volumeUgnot int64) int64 {211	_ = requireAllowedPad(cur)212	if !trader.IsValid() {213		return 0214	}215	if volumeUgnot < 0 {216		volumeUgnot = 0217	}218	launchID = strings.TrimSpace(launchID)219	if launchID == "" {220		return 0221	}222223	base := PointsBuyBase224	perG := PointsPerGnotBuy225	if side == 1 {226		base = PointsSellBase227		perG = PointsPerGnotSell228	} else if side != 0 {229		// ignore open / unknown230		return 0231	}232233	gnotFull := volumeUgnot / UgnotPerGnot234	award := base + gnotFull*perG235	if award <= 0 {236		return 0237	}238239	me := trader.String()240	h := runtime.ChainHeight()241	award = consumeHeightCap(me, h, award)242	if award <= 0 {243		return 0244	}245	addPts(me, award)246	totalTradePts += award247	chain.Emit("OnTrade",248		"trader", me,249		"id", launchID,250		"side", strconv.FormatInt(side, 10),251		"vol", strconv.FormatInt(volumeUgnot, 10),252		"pts", strconv.FormatInt(award, 10),253	)254	return award255}256257// OnCreate awards create bonus to the launch creator (pad-only).258func OnCreate(cur realm, creator address, launchID string) int64 {259	_ = requireAllowedPad(cur)260	if !creator.IsValid() {261		return 0262	}263	launchID = strings.TrimSpace(launchID)264	if launchID == "" {265		return 0266	}267	me := creator.String()268	h := runtime.ChainHeight()269	award := consumeHeightCap(me, h, PointsCreateBonus)270	if award <= 0 {271		return 0272	}273	addPts(me, award)274	totalCreatePts += award275	chain.Emit("OnCreate", "creator", me, "id", launchID, "pts", strconv.FormatInt(award, 10))276	return award277}278279// SetReferrer binds caller to a referrer once. Self-referral rejected.280func SetReferrer(cur realm, referrer address) {281	requireInit()282	if !cur.Previous().IsUserCall() {283		panic("pointsv2: EOA only")284	}285	if !referrer.IsValid() {286		panic("pointsv2: invalid referrer")287	}288	me := cur.Previous().Address()289	if me == referrer {290		panic("pointsv2: cannot refer self")291	}292	meS := me.String()293	if referrerOf.Has(meS) {294		panic("pointsv2: referrer already set")295	}296	refS := referrer.String()297	referrerOf.Set(meS, refS)298	addPts(refS, PointsReferrerBonus)299	addPts(meS, PointsRefereeBonus)300	chain.Emit("SetReferrer", "user", meS, "referrer", refS)301}302303// CheckIn grants PointsCheckIn if enough heights passed since last check-in.304func CheckIn(cur realm) int64 {305	requireInit()306	if !cur.Previous().IsUserCall() {307		panic("pointsv2: EOA only")308	}309	me := cur.Previous().Address().String()310	h := runtime.ChainHeight()311	last := int64(0)312	if v := lastCheckIn.Get(me); v != nil {313		last, _ = v.(int64)314	}315	if last > 0 && h-last < CheckInIntervalH {316		panic("pointsv2: check-in too soon")317	}318	lastCheckIn.Set(me, h)319	addPts(me, PointsCheckIn)320	chain.Emit("CheckIn", "user", me, "points", strconv.FormatInt(PointsCheckIn, 10))321	return getPts(me)322}323324// AwardPoints is admin-only (campaigns / corrections).325func AwardPoints(cur realm, to address, amount int64) {326	requireAdmin(cur)327	if !to.IsValid() {328		panic("pointsv2: invalid to")329	}330	if amount == 0 {331		panic("pointsv2: amount zero")332	}333	addPts(to.String(), amount)334	chain.Emit("Award", "to", to.String(), "amount", strconv.FormatInt(amount, 10))335}336337func GetPoints(addr string) int64 {338	requireInit()339	return getPts(strings.TrimSpace(addr))340}341342func GetReferrer(addr string) string {343	requireInit()344	v := referrerOf.Get(strings.TrimSpace(addr))345	if v == nil {346		return ""347	}348	s, _ := v.(string)349	return s350}351352func Leaderboard(n int) string {353	requireInit()354	if n <= 0 {355		n = 10356	}357	if n > MaxLeaderboardReturn {358		n = MaxLeaderboardReturn359	}360	type row struct {361		addr string362		pts  int64363	}364	rows := make([]row, 0, LeaderboardMax)365	pointsByAddr.Iterate("", "", func(key string, value any) bool {366		pts, _ := value.(int64)367		if pts <= 0 {368			return false369		}370		rows = append(rows, row{addr: key, pts: pts})371		if len(rows) >= LeaderboardMax {372			return true373		}374		return false375	})376	for i := 0; i < len(rows); i++ {377		best := i378		for j := i + 1; j < len(rows); j++ {379			if rows[j].pts > rows[best].pts {380				best = j381			}382		}383		rows[i], rows[best] = rows[best], rows[i]384	}385	if n > len(rows) {386		n = len(rows)387	}388	out := ""389	for i := 0; i < n; i++ {390		if out != "" {391			out += "\n"392		}393		out += rows[i].addr + "|" + strconv.FormatInt(rows[i].pts, 10)394	}395	return out396}397398func UserCount() int {399	requireInit()400	return pointsByAddr.Size()401}402403func PadCount() int {404	requireInit()405	return allowedPads.Size()406}407408func TransferAdmin(cur realm, newAdmin address) {409	requireAdmin(cur)410	if !newAdmin.IsValid() {411		panic("pointsv2: invalid address")412	}413	old := admin414	admin = newAdmin415	chain.Emit("TransferAdmin", "from", old.String(), "to", newAdmin.String())416}417418func Admin() string {419	requireInit()420	return admin.String()421}422423// ParamsInfo for UI:424//425//	referrer|referee|checkIn|interval|createBonus|buyBase|sellBase|ptsPerGnotBuy|ptsPerGnotSell|maxPerHeight|tradePtsTotal|createPtsTotal|v2426func ParamsInfo() string {427	return strconv.FormatInt(PointsReferrerBonus, 10) + "|" +428		strconv.FormatInt(PointsRefereeBonus, 10) + "|" +429		strconv.FormatInt(PointsCheckIn, 10) + "|" +430		strconv.FormatInt(CheckInIntervalH, 10) + "|" +431		strconv.FormatInt(PointsCreateBonus, 10) + "|" +432		strconv.FormatInt(PointsBuyBase, 10) + "|" +433		strconv.FormatInt(PointsSellBase, 10) + "|" +434		strconv.FormatInt(PointsPerGnotBuy, 10) + "|" +435		strconv.FormatInt(PointsPerGnotSell, 10) + "|" +436		strconv.FormatInt(MaxTradePtsPerHeight, 10) + "|" +437		strconv.FormatInt(totalTradePts, 10) + "|" +438		strconv.FormatInt(totalCreatePts, 10) + "|v2"439}440441func Render(path string) string {442	path = strings.Trim(path, "/")443	if !inited {444		return "# gnomi pointsv2\n\n> Not initialized.\n"445	}446	out := "# gnomi pointsv2\n\n"447	out += "- Users: **" + strconv.Itoa(pointsByAddr.Size()) + "**\n"448	out += "- Allowed pads: **" + strconv.Itoa(allowedPads.Size()) + "**\n"449	out += "- Trade pts issued: **" + strconv.FormatInt(totalTradePts, 10) + "**\n"450	out += "- Create pts issued: **" + strconv.FormatInt(totalCreatePts, 10) + "**\n\n"451	out += "## API\n\n- SetReferrer / CheckIn\n- OnTrade / OnCreate (pad allowlist)\n- AllowPad / Leaderboard\n"452	_ = path453	return out454}455456func resetForTest() {457	var zero address458	admin = zero459	inited = false460	pointsByAddr = avl.Tree{}461	referrerOf = avl.Tree{}462	lastCheckIn = avl.Tree{}463	allowedPads = avl.Tree{}464	tradePtsAtHeight = avl.Tree{}465	totalTradePts = 0466	totalCreatePts = 0467}468

Functions

  • Admin() string

  • AllowPad(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}, padPkg string)

  • AwardPoints(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}, to string, amount int64)

  • CheckIn(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

  • GetPoints(addr string) int64

  • GetReferrer(addr string) string

  • Init(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})

  • IsPadAllowed(padPkg string) bool

  • Leaderboard(n int) string

  • ListPads() string

  • OnCreate(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}, creator string, launchID string) int64

  • OnTrade(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}, trader string, launchID string, side int64, volumeUgnot int64) int64

  • PadCount() int

  • ParamsInfo() string

  • Render(path string) string

  • RevokePad(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}, padPkg string)

  • SetReferrer(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}, referrer string)

  • TransferAdmin(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}, newAdmin string)

  • UserCount() int

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.