← Back to Realms

Realm

gno.land/r/g1t2kg2vtr3fukg43eujkn6x53gfdyakhngt4sfd/gnofly/nfts/skins

Overview

Realm Path
gno.land/r/g1t2kg2vtr3fukg43eujkn6x53gfdyakhngt4sfd/gnofly/nfts/skins
Exported Functions
12
State Entries
39
Source Files
10
Total Package Entries
79

Exported Functions

12 exported functions

State

39 state entries

Source Code

FILES
buy.gno
go
1package skins
2
3import (
4	"chain"
5	"chain/banker"
6
7	// Only for OriginSend, in Buy. Caller identity always comes from
8	// cur.Previous(), never from this package.
9	"chain/runtime/unsafe"
10
11	"gno.land/p/nt/grc721/v0"
12)
13
14// Buy buys one token of a skin design for the caller and returns its id. The
15// exact price must be attached to the transaction (-send "<price>ugnot") and
16// is forwarded to the treasury of the game realm before the call returns.
17//
18// An address owns at most one token of a design: a skin cannot be given
19// away, so a second copy would be of no use. It must be called directly by a
20// user (maketx call): a call relayed by another realm or by a maketx run
21// script is refused.
22func Buy(cur realm, skinID string) int64 {
23	buyer := callerOf(0, cur)
24
25	// Paid-call guard: two checks that are only correct together.
26	//
27	// (a) The caller is a plain user account. Only then is the -send
28	//     envelope guaranteed to have landed at this realm's address.
29	if !cur.Previous().IsUserCall() {
30		panic(errNotUserCall)
31	}
32	d := getDesign(skinID)
33	if d == nil {
34		panic(errUnknownSkin)
35	}
36	if !d.listed {
37		panic(errSkinUnlisted)
38	}
39	if d.maxSupply > 0 && d.minted >= d.maxSupply {
40		panic(errSoldOut)
41	}
42	key := ownedKey(buyer, skinID)
43	if owned.Has(key) {
44		panic(errAlreadyOwned)
45	}
46
47	// (b) The envelope is exactly the price: one coin, the right denom, the
48	//     right amount.
49	sent := unsafe.OriginSend()
50	if len(sent) != 1 || sent[0].Denom != payDenom || sent[0].Amount != d.price {
51		panic("gnofly: wrong payment, send exactly " + itoa(d.price) + payDenom)
52	}
53	treasury := roles.Treasury()
54	payment := chain.NewCoins(chain.NewCoin(payDenom, d.price))
55	banker.NewBanker(banker.BankerTypeOriginSend, cur).SendCoins(cur.Address(), treasury, payment)
56
57	id := nextID
58	nextID++
59	tid := grc721.TokenID(itoa(id))
60	if err := ledger.Mint(buyer, tid); err != nil {
61		panic(err)
62	}
63	tokenSkin.Set(string(tid), skinID)
64	owned.Set(key, id)
65	d.minted++
66
67	chain.Emit("SkinBought",
68		"id", itoa(id),
69		"skin", skinID,
70		"buyer", buyer.String(),
71		"price", itoa(d.price),
72		"treasury", treasury.String(),
73	)
74	return id
75}
76
77// Burn destroys a skin token. Only its owner can. The storage deposit paid at
78// purchase comes back; the price does not. The owner can buy the design
79// again, while it is listed.
80func Burn(cur realm, id int64) {
81	caller := callerOf(0, cur)
82	skinID := skinOf(id)
83	if skinID == "" {
84		panic(errNotMinted)
85	}
86	if ownerOf(id) != caller {
87		panic(errNotSkinOwner)
88	}
89	tid := grc721.TokenID(itoa(id))
90	if err := ledger.Burn(tid); err != nil {
91		panic(err)
92	}
93	tokenSkin.Remove(string(tid))
94	owned.Remove(ownedKey(caller, skinID))
95	chain.Emit("SkinBurned", "id", itoa(id), "skin", skinID, "owner", caller.String())
96}
97
98// ownedKey is the key of owned: the address, then the design.
99func ownedKey(addr address, skinID string) string {
100	return addr.String() + "/" + skinID
101}
102
103// skinOf returns the design of token id, "" when there is no such token.
104func skinOf(id int64) string {
105	v := tokenSkin.Get(itoa(id))
106	if v == nil {
107		return ""
108	}
109	return v.(string)
110}
111
112// ownerOf returns who owns token id, "" for none.
113func ownerOf(id int64) address {
114	owner, err := token.OwnerOf(grc721.TokenID(itoa(id)))
115	if err != nil {
116		return ""
117	}
118	return owner
119}
120

Raw Package Data

Raw JSON data