Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

allowlist.gno

2.51 Kb · 89 lines
 1package mint
 2
 3import (
 4	"strings"
 5
 6	"gno.land/r/sys/users"
 7	validators "gno.land/r/sys/validators/v2"
 8
 9	"gno.land/p/g19dzk54hhsdc3gllpksuj6gu4a3e6fl8cr6rn4w/sinopia/safe/v1"
10	"gno.land/r/g19dzk54hhsdc3gllpksuj6gu4a3e6fl8cr6rn4w/sinopia/core"
11)
12
13// Allowlist sets (design §4.6). A phase's set is a comma-separated
14// union of terms; an address is eligible if any term admits it:
15//
16//	users             registered in r/sys/users (live check)
17//	validators        in the active validator set (live check)
18//	holders:<cid>     holds a token of collection <cid>
19//	merkle:<root>     in a sha256 merkle tree; the minter passes the proof
20//
21// A term may carry "|"-separated provenance notes for humans, e.g.
22// "merkle:<root>|src:<git-sha>|data:<sha256>"; notes never affect logic.
23const (
24	MaxSetLen   = 512
25	MaxSetTerms = 4
26)
27
28type setTerm struct {
29	name, arg string
30}
31
32// parseSet validates a set and returns its terms; it panics on any error so
33// AddPhase rejects a malformed set before it is ever frozen.
34func parseSet(set string) []setTerm {
35	safe.AssertText("set", set, MaxSetLen)
36	parts := strings.Split(set, ",")
37	if len(parts) > MaxSetTerms {
38		panic("set: at most 4 terms")
39	}
40	terms := make([]setTerm, 0, len(parts))
41	for _, part := range parts {
42		body, _, _ := strings.Cut(part, "|")
43		name, arg, _ := strings.Cut(body, ":")
44		switch name {
45		case "users", "validators":
46			if arg != "" {
47				panic("set: " + name + " takes no argument")
48			}
49		case "holders":
50			core.Info(arg) // panics for an unknown collection
51		case "merkle":
52			arg = safe.AssertHex("set merkle root", arg, 32)
53		default:
54			panic("set: unknown term " + name)
55		}
56		terms = append(terms, setTerm{name: name, arg: arg})
57	}
58	return terms
59}
60
61// eligible reports whether addr may mint in a phase with this set, and why
62// not. Live sets are evaluated at mint time, so a deleted user or a removed
63// validator loses eligibility immediately.
64func eligible(cid, set string, addr address, proof string) (bool, string) {
65	if set == "" {
66		return true, ""
67	}
68	for _, t := range parseSet(set) {
69		switch t.name {
70		case "users":
71			if users.ResolveAddress(addr) != nil {
72				return true, ""
73			}
74		case "validators":
75			if validators.IsValidator(addr) {
76				return true, ""
77			}
78		case "holders":
79			if n, err := core.BalanceOf(t.arg, addr); err == nil && n > 0 {
80				return true, ""
81			}
82		case "merkle":
83			if proof != "" && safe.MerkleVerify(t.arg, safe.AllowlistLeaf(cid, addr), proof) {
84				return true, ""
85			}
86		}
87	}
88	return false, "address is not in this phase's allowlist (" + set + ")"
89}