← Back to Realms

Realm

gno.land/r/moul/x/daily/erc20/v0

Overview

Realm Path
gno.land/r/moul/x/daily/erc20/v0
Exported Functions
10
State Entries
11
Source Files
2
Total Package Entries
23

Exported Functions

10 exported functions

State

11 state entries

Source Code

FILES
erc20.gno
go
1// Package erc20 implements MiniToken, a minimal fungible token modeled on the
2// Solidity ERC-20 idea, ported to gno.land (gno 0.9 / sapphire).
3//
4// State is package-level and persistent. All state-mutating exported functions
5// are crossing functions (they take `cur realm` as the first parameter) per the
6// gno 0.9 interrealm convention, and identify the caller via
7// cur.Previous().Address() after checking cur.IsCurrent().
8package erc20
9
10import (
11	"errors"
12	"sort"
13	"strconv"
14	"strings"
15)
16
17const (
18	name   = "MiniToken"
19	symbol = "MINI"
20)
21
22var (
23	// balances maps holder address -> token balance.
24	balances = map[address]uint64{}
25	// allowances[owner][spender] = amount the spender may pull from owner.
26	allowances = map[address]map[address]uint64{}
27	// totalSupply is the sum of all balances.
28	totalSupply uint64
29)
30
31var (
32	errZeroAddress  = errors.New("erc20: zero address")
33	errInsufficient = errors.New("erc20: insufficient balance")
34	errAllowanceLow = errors.New("erc20: insufficient allowance")
35	errOverflow     = errors.New("erc20: supply overflow")
36	errSpoofedRealm = errors.New("erc20: spoofed realm")
37	errNotUser      = errors.New("erc20: caller is not a user account")
38)
39
40// caller returns the immediate caller's address for a crossing function.
41// It enforces the gno 0.9 authentication primitive: cur must be the live
42// crossing frame before its Previous() is trusted.
43func caller(cur realm) address {
44	if !cur.IsCurrent() {
45		panic(errSpoofedRealm)
46	}
47	prev := cur.Previous()
48	if !prev.IsUser() {
49		panic(errNotUser)
50	}
51	return prev.Address()
52}
53
54// Mint creates `amount` new tokens and credits them to `to`, increasing the
55// total supply. Crossing function.
56func Mint(cur realm, to address, amount uint64) {
57	_ = caller(cur) // authenticate the crossing frame; anyone may mint in this demo
58	if !to.IsValid() {
59		panic(errZeroAddress)
60	}
61	if amount == 0 {
62		return
63	}
64	if totalSupply+amount < totalSupply {
65		panic(errOverflow)
66	}
67	totalSupply += amount
68	balances[to] += amount
69}
70
71// Transfer moves `amount` tokens from the caller to `to`. Crossing function.
72func Transfer(cur realm, to address, amount uint64) {
73	from := caller(cur)
74	if !to.IsValid() {
75		panic(errZeroAddress)
76	}
77	if err := transfer(from, to, amount); err != nil {
78		panic(err)
79	}
80}
81
82// Approve authorizes `spender` to pull up to `amount` tokens from the caller.
83// Crossing function.
84func Approve(cur realm, spender address, amount uint64) {
85	owner := caller(cur)
86	if !spender.IsValid() {
87		panic(errZeroAddress)
88	}
89	m := allowances[owner]
90	if m == nil {
91		m = map[address]uint64{}
92		allowances[owner] = m
93	}
94	m[spender] = amount
95}
96
97// TransferFrom moves `amount` tokens from `from` to `to`, spending the caller's
98// allowance granted by `from`. Crossing function.
99func TransferFrom(cur realm, from, to address, amount uint64) {
100	spender := caller(cur)
101	if !from.IsValid() || !to.IsValid() {
102		panic(errZeroAddress)
103	}
104	allowed := allowances[from][spender]
105	if allowed < amount {
106		panic(errAllowanceLow)
107	}
108	if err := transfer(from, to, amount); err != nil {
109		panic(err)
110	}
111	allowances[from][spender] = allowed - amount
112}
113
114// transfer is the internal, non-crossing balance mover. Pure logic on package
115// state; returns an error rather than panicking so callers control reverts.
116func transfer(from, to address, amount uint64) error {
117	if amount == 0 {
118		return nil
119	}
120	if balances[from] < amount {
121		return errInsufficient
122	}
123	balances[from] -= amount
124	balances[to] += amount
125	return nil
126}
127
128// BalanceOf returns the token balance of `holder`. Read-only, non-crossing.
129func BalanceOf(holder address) uint64 {
130	return balances[holder]
131}
132
133// Allowance returns how much `spender` may still pull from `owner`.
134func Allowance(owner, spender address) uint64 {
135	return allowances[owner][spender]
136}
137
138// TotalSupply returns the total number of tokens in existence.
139func TotalSupply() uint64 {
140	return totalSupply
141}
142
143// Name returns the token name.
144func Name() string { return name }
145
146// Symbol returns the token symbol.
147func Symbol() string { return symbol }
148
149// Render produces the gnoweb Markdown view of the token: metadata plus a
150// deterministically-ordered table of non-zero balances. Render is NOT a
151// crossing function (no `cur realm` parameter).
152func Render(path string) string {
153	var b strings.Builder
154
155	b.WriteString("# " + name + " (" + symbol + ")\n\n")
156	b.WriteString("A minimal ERC-20-style fungible token on gno.land.\n\n")
157	b.WriteString("- **Name:** " + name + "\n")
158	b.WriteString("- **Symbol:** " + symbol + "\n")
159	b.WriteString("- **Total supply:** " + strconv.FormatUint(totalSupply, 10) + "\n\n")
160
161	// Collect and sort holder addresses for deterministic output. Map iteration
162	// order is not a stable contract, so we sort the string keys (gno's sort
163	// package has no generic Slice; Strings is the deterministic primitive).
164	keys := make([]string, 0, len(balances))
165	byKey := make(map[string]uint64, len(balances))
166	for addr, bal := range balances {
167		if bal == 0 {
168			continue
169		}
170		s := addr.String()
171		keys = append(keys, s)
172		byKey[s] = bal
173	}
174	sort.Strings(keys)
175
176	b.WriteString("## Balances\n\n")
177	if len(keys) == 0 {
178		b.WriteString("_No holders yet — call `Mint` to create tokens._\n")
179		return b.String()
180	}
181
182	b.WriteString("| Address | Amount |\n")
183	b.WriteString("| --- | ---: |\n")
184	for _, k := range keys {
185		b.WriteString("| `" + k + "` | " +
186			strconv.FormatUint(byKey[k], 10) + " |\n")
187	}
188
189	return b.String()
190}
191

Raw Package Data

Raw JSON data