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/moul/x/daily/multisig/v0

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
v0
Namespace
moul / x / daily / multisig
Files
3 (README)(gnomod.toml)
Exported functions
4
Module
gno.land/r/moul/x/daily/multisig/v0
gno
0.9

Files (3)

  • README.mdmarkdown
  • gnomod.tomltoml
  • multisig.gnogno
multisig.gnogno
1// Package multisig implements an M-of-N approval multisig realm.2//3// It is a port of the classic Solidity multisig idea: a fixed set of owners4// is configured once, any owner may create a proposal, owners approve it, and5// once the number of distinct approvals reaches the threshold the proposal is6// marked "Executed". This realm tracks approvals only — it does not move funds.7package multisig89import (10	"errors"11	"strconv"12	"strings"1314	"chain"15	"chain/runtime/unsafe"1617	"gno.land/p/nt/avl/v0"18)1920// State of a single proposal.21type stateKind int2223const (24	statePending stateKind = iota25	stateExecuted26)2728func (s stateKind) String() string {29	if s == stateExecuted {30		return "Executed"31	}32	return "Pending"33}3435// proposal is a single approval request.36type proposal struct {37	id          int38	description string39	proposer    address40	approvals   *avl.Tree // owner address string -> struct{}{}, distinct approvers41	state       stateKind42}4344// Package-level persistent state.45var (46	owners      *avl.Tree // owner address string -> struct{}{}47	ownerCount  int48	threshold   int49	initialized bool5051	proposals   *avl.Tree // decimal id string -> *proposal52	nextID      int53)5455var (56	errAlreadyInit = errors.New("multisig: already initialized")57	errNotInit     = errors.New("multisig: not initialized")58	errBadThreshold = errors.New("multisig: threshold must be between 1 and number of owners")59	errNoOwners    = errors.New("multisig: at least one owner required")60	errNotOwner    = errors.New("multisig: caller is not an owner")61	errNoProposal  = errors.New("multisig: proposal not found")62	errDupApproval = errors.New("multisig: caller already approved this proposal")63	errExecuted    = errors.New("multisig: proposal already executed")64)6566func init() {67	owners = avl.NewTree()68	proposals = avl.NewTree()69}7071// caller returns the address of the realm/user that called into this realm.72func caller() address {73	return unsafe.PreviousRealm().Address()74}7576func isOwner(a address) bool {77	return owners.Has(a.String())78}7980// Setup configures the multisig exactly once. owners is a comma-separated list81// of bech32 addresses; threshold is the number of approvals required to execute82// a proposal. It panics (cross-realm abort) on invalid input or if already set.83func Setup(cur realm, ownersCSV string, thresholdN int) {84	if initialized {85		panic(errAlreadyInit)86	}8788	parts := strings.Split(ownersCSV, ",")89	count := 090	for _, p := range parts {91		a := strings.TrimSpace(p)92		if a == "" {93			continue94		}95		addr := address(a)96		if owners.Has(addr.String()) {97			continue // dedupe98		}99		owners.Set(addr.String(), struct{}{})100		count++101	}102	if count == 0 {103		panic(errNoOwners)104	}105	if thresholdN < 1 || thresholdN > count {106		panic(errBadThreshold)107	}108109	ownerCount = count110	threshold = thresholdN111	initialized = true112113	chain.Emit("Setup", "owners", strconv.Itoa(count), "threshold", strconv.Itoa(thresholdN))114}115116// Propose creates a new proposal authored by an owner and returns its id.117func Propose(cur realm, description string) int {118	if !initialized {119		panic(errNotInit)120	}121	c := caller()122	if !isOwner(c) {123		panic(errNotOwner)124	}125126	id := nextID127	nextID++128129	p := &proposal{130		id:          id,131		description: description,132		proposer:    c,133		approvals:   avl.NewTree(),134		state:       statePending,135	}136	proposals.Set(strconv.Itoa(id), p)137138	chain.Emit("Propose", "id", strconv.Itoa(id), "proposer", c.String())139	return id140}141142// Approve records an owner's approval for a proposal. When the count of distinct143// approvals reaches the threshold, the proposal becomes Executed.144func Approve(cur realm, id int) {145	if !initialized {146		panic(errNotInit)147	}148	c := caller()149	if !isOwner(c) {150		panic(errNotOwner)151	}152153	v := proposals.Get(strconv.Itoa(id))154	if v == nil {155		panic(errNoProposal)156	}157	p := v.(*proposal)158	if p.state == stateExecuted {159		panic(errExecuted)160	}161	if p.approvals.Has(c.String()) {162		panic(errDupApproval)163	}164165	p.approvals.Set(c.String(), struct{}{})166	chain.Emit("Approve", "id", strconv.Itoa(id), "owner", c.String(), "approvals", strconv.Itoa(p.approvals.Size()))167168	if p.approvals.Size() >= threshold {169		p.state = stateExecuted170		chain.Emit("Executed", "id", strconv.Itoa(id))171	}172}173174// Render returns a Markdown view of the multisig configuration and proposals.175func Render(path string) string {176	var b strings.Builder177	b.WriteString("# M-of-N Multisig\n\n")178179	if !initialized {180		b.WriteString("_Not initialized. Call `Setup(owners, threshold)` once._\n")181		return b.String()182	}183184	b.WriteString("**Threshold:** ")185	b.WriteString(strconv.Itoa(threshold))186	b.WriteString(" of ")187	b.WriteString(strconv.Itoa(ownerCount))188	b.WriteString("\n\n## Owners\n\n")189	owners.Iterate("", "", func(key string, _ interface{}) bool {190		b.WriteString("- `")191		b.WriteString(key)192		b.WriteString("`\n")193		return false194	})195196	b.WriteString("\n## Proposals\n\n")197	if proposals.Size() == 0 {198		b.WriteString("_No proposals yet._\n")199		return b.String()200	}201202	b.WriteString("| ID | Description | Proposer | Approvals | State |\n")203	b.WriteString("|----|-------------|----------|-----------|-------|\n")204	proposals.Iterate("", "", func(_ string, v interface{}) bool {205		p := v.(*proposal)206		b.WriteString("| ")207		b.WriteString(strconv.Itoa(p.id))208		b.WriteString(" | ")209		b.WriteString(p.description)210		b.WriteString(" | `")211		b.WriteString(p.proposer.String())212		b.WriteString("` | ")213		b.WriteString(strconv.Itoa(p.approvals.Size()))214		b.WriteString("/")215		b.WriteString(strconv.Itoa(threshold))216		b.WriteString(" | ")217		b.WriteString(p.state.String())218		b.WriteString(" |\n")219		return false220	})221222	return b.String()223}224

Functions

  • Approve(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}, id int)

  • Propose(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}, description string) int

  • Render(path string) string

  • Setup(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}, ownersCSV string, thresholdN 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.