PathrockNetwork Gno Explorer
HomeBlocksTransactionsRealmsPackagesValidatorsAnalytics

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/merkledrop/v0

Realm
Open in gnoweb ↗

Overview

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

Files (3)

  • README.mdmarkdown
  • gnomod.tomltoml
  • merkledrop.gnogno
merkledrop.gnogno
1// Package merkledrop is an idiomatic gno.land port of the classic Solidity2// MerkleDistributor airdrop (Uniswap's contract, OpenZeppelin's MerkleProof).3//4// A fixed merkleRoot commits to a set of (address, amount) allocations. A5// recipient proves membership by supplying the sibling hashes on the path from6// their leaf up to the root. Each address may claim exactly once.7//8// Leaf and node hashing use crypto/sha256 (available in the gno stdlib), the9// same primitive Solidity distributors use via keccak — here sha256 keeps the10// off-chain tree builder trivially reproducible. Internal nodes hash the two11// children in sorted order (the OpenZeppelin "commutative" scheme), so proofs12// carry no left/right flags.13//14//	leaf   = sha256(addr.String() + "|" + amount)15//	node   = sha256(min(a,b) || max(a,b))16//17// Balances are pure accounting (uint64); no real coin moves — there is no18// msg.value on gno, so this models the allocation ledger only.19package merkledrop2021import (22	"bytes"23	"crypto/sha256"24	"encoding/hex"25	"strconv"26	"strings"2728	"chain"29	"chain/runtime/unsafe"3031	"gno.land/p/nt/avl/v0"32)3334// merkleRoot commits to the airdrop allocation set. Generated off-chain with35// the exact scheme above over the four example recipients in README.md.36const merkleRoot = "0d00b73577019dc92924f0ae001d3e1839d51fb358adda2ad1510c39eb82b13f"3738// claimed maps claimer address string -> claimed amount (uint64).39var claimed avl.Tree4041// totalClaimed is the running sum of all claimed allocations.42var totalClaimed uint644344// leaf computes the tree leaf for an (address, amount) allocation.45func leaf(claimer address, amount uint64) []byte {46	data := claimer.String() + "|" + strconv.FormatUint(amount, 10)47	h := sha256.Sum256([]byte(data))48	return h[:]49}5051// hashPair hashes two nodes in sorted order (commutative), so the proof does52// not need to encode child positions.53func hashPair(a, b []byte) []byte {54	var d []byte55	if bytes.Compare(a, b) <= 0 {56		d = append(append(d, a...), b...)57	} else {58		d = append(append(d, b...), a...)59	}60	h := sha256.Sum256(d)61	return h[:]62}6364// parseProof splits a comma-separated list of hex hashes into raw 32-byte65// nodes, panicking on any malformed element.66func parseProof(proof string) [][]byte {67	proof = strings.TrimSpace(proof)68	if proof == "" {69		return nil70	}71	parts := strings.Split(proof, ",")72	out := make([][]byte, 0, len(parts))73	for _, p := range parts {74		p = strings.TrimSpace(p)75		if p == "" {76			continue77		}78		raw, err := hex.DecodeString(p)79		if err != nil {80			panic("merkledrop: bad proof hash: " + p)81		}82		if len(raw) != sha256.Size {83			panic("merkledrop: proof hash must be 32 bytes: " + p)84		}85		out = append(out, raw)86	}87	return out88}8990// computeRoot walks the proof from the given leaf up to a candidate root and91// returns its hex encoding.92func computeRoot(leafHash []byte, proof [][]byte) string {93	node := leafHash94	for _, sib := range proof {95		node = hashPair(node, sib)96	}97	return hex.EncodeToString(node)98}99100// Verify reports whether (claimer, amount, proof) resolves to merkleRoot. Pure101// and read-only — safe to call from tests and Render.102func Verify(claimer address, amount uint64, proof string) bool {103	got := computeRoot(leaf(claimer, amount), parseProof(proof))104	return got == merkleRoot105}106107// AmountClaimed returns how much the address has already claimed (0 if none).108func AmountClaimed(claimer address) uint64 {109	if !claimed.Has(claimer.String()) {110		return 0111	}112	return claimed.Get(claimer.String()).(uint64)113}114115// HasClaimed reports whether the address already claimed.116func HasClaimed(claimer address) bool {117	return claimed.Has(claimer.String())118}119120// Claim proves the caller is entitled to `amount` via `proof` (comma-separated121// hex sibling hashes) and marks the allocation claimed. Panics on a bad proof122// or a double claim. Mirrors MerkleDistributor.claim (msg.sender is the caller).123func Claim(cur realm, amount uint64, proof string) {124	caller := unsafe.PreviousRealm().Address()125	if HasClaimed(caller) {126		panic("merkledrop: already claimed")127	}128	if !Verify(caller, amount, proof) {129		panic("merkledrop: invalid proof")130	}131	claimed.Set(caller.String(), amount)132	totalClaimed += amount133134	chain.Emit("Claimed",135		"account", caller.String(),136		"amount", strconv.FormatUint(amount, 10),137	)138}139140// Render shows the committed root, aggregate stats, and the list of claimers.141func Render(path string) string {142	var b strings.Builder143	b.WriteString("# MerkleDrop\n\n")144	b.WriteString("A fixed Merkle root gates a one-per-address airdrop. Prove your allocation with `Claim(amount, proof)`.\n\n")145146	b.WriteString("## Root\n\n")147	b.WriteString("`" + merkleRoot + "`\n\n")148149	b.WriteString("## Stats\n\n")150	b.WriteString("- Distinct claimers: " + strconv.Itoa(claimed.Size()) + "\n")151	b.WriteString("- Total claimed: " + strconv.FormatUint(totalClaimed, 10) + "\n\n")152153	b.WriteString("## Claimers\n\n")154	if claimed.Size() == 0 {155		b.WriteString("_No claims yet._\n")156		return b.String()157	}158	b.WriteString("| Address | Amount |\n|---|---|\n")159	claimed.Iterate("", "", func(key string, value interface{}) bool {160		b.WriteString("| " + key + " | " + strconv.FormatUint(value.(uint64), 10) + " |\n")161		return false162	})163	return b.String()164}165

Functions

  • AmountClaimed(claimer string) uint64

  • 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}, amount uint64, proof string)

  • HasClaimed(claimer string) bool

  • Render(path string) string

  • Verify(claimer string, amount uint64, proof string) bool

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.