1package grc20factory23import (4 "gno.land/p/moul/md/v0"5 p "gno.land/p/nt/avl/pager/v0"6 "gno.land/p/nt/avl/rotree/v0"7 "gno.land/p/nt/avl/v0"8 "gno.land/p/nt/grc20/v0"9 "gno.land/p/nt/mux/v0"10 "gno.land/p/nt/ownable/v0"11 "gno.land/p/nt/seqid/v0"12 "gno.land/p/nt/ufmt/v0"13 "gno.land/r/nt/grc20reg/v0"14)1516var (17 instances avl.Tree // symbol -> *instance18 nextTokenID seqid.ID19 pager = p.NewPager(rotree.Wrap(&instances, nil), 20, false)20)2122type instance struct {23 token *grc20.Token24 ledger *grc20.PrivateLedger25 admin *ownable.Ownable26 faucet int64 // per-request amount. disabled if 0.27}2829func New(cur realm, name, symbol string, decimals int, initialMint, faucet int64) {30 caller := cur.Previous().Address()31 NewWithAdmin(cur, name, symbol, decimals, initialMint, faucet, caller)32}3334func NewWithAdmin(cur realm, name, symbol string, decimals int, initialMint, faucet int64, admin address) {35 exists := instances.Has(symbol)36 if exists {37 panic("token already exists")38 }3940 token, ledger := grc20.NewToken(name, symbol, decimals, nextTokenID.Next(), cur)41 if initialMint > 0 {42 ledger.Mint(admin, initialMint)43 }4445 inst := instance{46 token: token,47 ledger: ledger,48 admin: ownable.NewWithAddress(admin),49 faucet: faucet,50 }51 instances.Set(symbol, &inst)5253 grc20reg.Register(cross(cur), token, symbol)54}5556func Bank(symbol string) *grc20.Token {57 inst := mustGetInstance(symbol)58 return inst.token59}6061func TotalSupply(symbol string) int64 {62 inst := mustGetInstance(symbol)63 return inst.token.ReadonlyTeller().TotalSupply()64}6566func HasAddr(symbol string, owner address) bool {67 inst := mustGetInstance(symbol)68 return inst.token.HasAddr(owner)69}7071func BalanceOf(symbol string, owner address) int64 {72 inst := mustGetInstance(symbol)73 return inst.token.ReadonlyTeller().BalanceOf(owner)74}7576func Allowance(symbol string, owner, spender address) int64 {77 inst := mustGetInstance(symbol)78 return inst.token.ReadonlyTeller().Allowance(owner, spender)79}8081func Transfer(cur realm, symbol string, to address, amount int64) {82 inst := mustGetInstance(symbol)83 caller := cur.Previous().Address()84 teller := inst.ledger.ImpersonateTeller(caller)85 checkErr(teller.Transfer(0, cur, to, amount))86}8788func Approve(cur realm, symbol string, spender address, amount int64) {89 inst := mustGetInstance(symbol)90 caller := cur.Previous().Address()91 teller := inst.ledger.ImpersonateTeller(caller)92 checkErr(teller.Approve(0, cur, spender, amount))93}9495func TransferFrom(cur realm, symbol string, from, to address, amount int64) {96 inst := mustGetInstance(symbol)97 caller := cur.Previous().Address()98 teller := inst.ledger.ImpersonateTeller(caller)99 checkErr(teller.TransferFrom(0, cur, from, to, amount))100}101102// faucet.103func Faucet(cur realm, symbol string) {104 inst := mustGetInstance(symbol)105 if inst.faucet == 0 {106 panic("faucet disabled for this token")107 }108 // FIXME: add limits?109 // FIXME: add payment in gnot?110 caller := cur.Previous().Address()111 checkErr(inst.ledger.Mint(caller, inst.faucet))112}113114func Mint(cur realm, symbol string, to address, amount int64) {115 inst := mustGetInstance(symbol)116 inst.admin.AssertOwnedBy(cur.Previous().Address())117 checkErr(inst.ledger.Mint(to, amount))118}119120func Burn(cur realm, symbol string, from address, amount int64) {121 inst := mustGetInstance(symbol)122 inst.admin.AssertOwnedBy(cur.Previous().Address())123 checkErr(inst.ledger.Burn(from, amount))124}125126// instance admin functionality127func DropInstanceOwnership(cur realm, symbol string) {128 inst := mustGetInstance(symbol)129 checkErr(inst.admin.DropOwnership(0, cur))130}131132func TransferInstanceOwnership(cur realm, symbol string, newOwner address) {133 inst := mustGetInstance(symbol)134 checkErr(inst.admin.TransferOwnership(0, cur, newOwner))135}136137func ListTokens(pageNumber, pageSize int) []*grc20.Token {138 page := pager.GetPageWithSize(pageNumber, pageSize)139140 tokens := make([]*grc20.Token, len(page.Items))141 for i := range page.Items {142 tokens[i] = page.Items[i].Value.(*instance).token143 }144145 return tokens146}147148func Render(path string) string {149 router := mux.NewRouter()150 router.HandleFunc("", renderHome)151 router.HandleFunc("{symbol}", renderToken)152 router.HandleFunc("{symbol}/balance/{address}", renderBalance)153 return router.Render(path)154}155156func renderHome(res *mux.ResponseWriter, req *mux.Request) {157 out := md.H1(ufmt.Sprintf("GRC20 Tokens (%d)", instances.Size()))158159 // Get the current page of tokens based on the request path.160 page := pager.MustGetPageByPath(req.RawPath)161162 // Render the list of tokens.163 for _, item := range page.Items {164 token := item.Value.(*instance).token165 out += md.BulletItem(166 md.Link(167 ufmt.Sprintf("%s ($%s)", token.GetName(), token.GetSymbol()),168 ufmt.Sprintf("/r/demo/grc20factory:%s", token.GetSymbol()),169 ),170 )171 }172 out += "\n"173174 // Add the page picker.175 out += md.Paragraph(page.Picker(req.Path))176177 res.Write(out)178}179180func renderToken(res *mux.ResponseWriter, req *mux.Request) {181 // Get the token symbol from the request.182 symbol := req.GetVar("symbol")183 inst := mustGetInstance(symbol)184185 // Render the token details.186 out := inst.token.RenderHome()187 out += md.BulletItem(188 ufmt.Sprintf("%s: %s", md.Bold("Admin"), inst.admin.Owner()),189 )190191 res.Write(out)192}193194func renderBalance(res *mux.ResponseWriter, req *mux.Request) {195 var (196 symbol = req.GetVar("symbol")197 addr = req.GetVar("address")198 )199200 // Get the balance of the specified address for the token.201 inst := mustGetInstance(symbol)202 balance := inst.token.BalanceOf(address(addr))203204 // Render the balance information.205 out := md.Paragraph(206 ufmt.Sprintf("%s balance: %d", md.Bold(addr), balance),207 )208209 res.Write(out)210}211212func mustGetInstance(symbol string) *instance {213 t := instances.Get(symbol)214 if t == nil {215 panic("token instance does not exist")216 }217 return t.(*instance)218}219220func checkErr(err error) {221 if err != nil {222 panic(err.Error())223 }224}225Allowance(symbol string, owner string, spender string) int64
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}, symbol string, spender string, amount int64)
BalanceOf(symbol string, owner string) int64
Bank(symbol string) *gno.land/p/nt/grc20/v0.Token
Burn(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}, symbol string, from string, amount int64)
DropInstanceOwnership(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}, symbol string)
Faucet(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}, symbol string)
HasAddr(symbol string, owner string) bool
ListTokens(pageNumber int, pageSize int) []*gno.land/p/nt/grc20/v0.Token
Mint(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}, symbol string, to string, amount int64)
New(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}, name string, symbol string, decimals int, initialMint int64, faucet int64)
NewWithAdmin(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}, name string, symbol string, decimals int, initialMint int64, faucet int64, admin string)
Render(path string) string
TotalSupply(symbol string) int64
Transfer(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}, symbol string, to string, amount int64)
TransferFrom(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}, symbol string, from string, to string, amount int64)
TransferInstanceOwnership(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}, symbol string, newOwner string)
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.
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.