grc1155.gno
4.56 Kb · 155 lines
1package block
2
3import (
4 "chain"
5 "chain/runtime/unsafe"
6 "strconv"
7
8 "gno.land/p/akkadia/v0/accesscontrol"
9 "gno.land/p/akkadia/v0/grc1155"
10 "gno.land/r/akkadia/v0/admin"
11)
12
13const (
14 MintEvent = "Mint"
15)
16
17func BalanceOf(user address, tokenID grc1155.TokenID) int64 {
18 assertMigrationStateAvailable()
19 return mintedBlockStore.BalanceOf(user, tokenID)
20}
21
22func BalanceOfBatch(ul []address, tokenIDs []grc1155.TokenID) []int64 {
23 assertMigrationStateAvailable()
24 return mintedBlockStore.BalanceOfBatch(ul, tokenIDs)
25}
26
27func balanceOfSafe(user address, tokenID grc1155.TokenID) (int64, bool) {
28 return mintedBlockStore.BalanceOfSafe(user, tokenID)
29}
30
31func IsApprovedForAll(owner, operator address) bool {
32 assertMigrationStateAvailable()
33 return mintedBlockStore.IsApprovedForAll(owner, operator)
34}
35
36func SetApprovalForAll(cur realm, operator address, approved bool) {
37 assertNotFrozen()
38 caller := accesscontrol.MustGetUserCaller(0, cur)
39 mintedBlockStore.SetApprovalForAll(caller, operator, approved)
40}
41
42func TransferFrom(cur realm, from, to address, tokenID grc1155.TokenID, amount int64) {
43 assertNotFrozen()
44 caller := accesscontrol.MustGetUserCaller(0, cur)
45 mintedBlockStore.Transfer(caller, from, to, tokenID, amount)
46}
47
48func BatchTransferFrom(cur realm, from, to address, tokenIDs []grc1155.TokenID, amounts []int64) {
49 assertNotFrozen()
50 caller := accesscontrol.MustGetUserCaller(0, cur)
51 mintedBlockStore.BatchTransfer(caller, from, to, tokenIDs, amounts)
52}
53
54// Mint mints block tokens with payment (anyone can mint)
55func Mint(cur realm, to address, blockID uint32, amount int64) {
56 assertNotFrozen()
57
58 if amount < 1 {
59 panic("amount must be positive")
60 }
61
62 caller := accesscontrol.MustGetUserCaller(0, cur)
63 if !mintedBlockStore.CanUserMint(blockID, caller) {
64 panic("mint not allowed: caller is not the allowed minter")
65 }
66
67 block := blockStore.MustGet(blockID)
68
69 maxSupply := mustGetMintableSupply(block)
70 if mintedBlockStore.exceedsSupply(blockID, amount, maxSupply) {
71 panic("supply exceeded")
72 }
73 totalMintPrice := mustGetTotalMintPrice(block, amount)
74
75 // MustGetUserCaller guarantees a direct user call, so OriginSend cannot be borrowed through an intermediate realm.
76 payment := unsafe.OriginSend().AmountOf("ugnot")
77 assertExactPayment(totalMintPrice, payment)
78
79 creatorStr, found := block["creator"]
80 if !found {
81 panic("creator not found in block: " + block["id"])
82 }
83 feeCollector := admin.GetFeeCollector()
84
85 tokenID := blockIDToTokenID(blockID)
86 mintedBlockStore.Mint(caller, to, tokenID, amount, maxSupply)
87 creatorShare, feeCollectorShare := distributeShares(cur, creatorStr, feeCollector, totalMintPrice, creatorBPS)
88
89 chain.Emit(MintEvent,
90 "blockId", string(tokenID),
91 "amount", strconv.FormatInt(amount, 10),
92 "totalPrice", strconv.FormatInt(totalMintPrice, 10),
93 "creator", creatorStr,
94 "creatorShare", strconv.FormatInt(creatorShare, 10),
95 "feeCollector", feeCollector.String(),
96 "feeCollectorShare", strconv.FormatInt(feeCollectorShare, 10),
97 )
98}
99
100func Burn(cur realm, from address, tokenID grc1155.TokenID, amount int64) {
101 assertNotFrozen()
102 caller := mustGetBurnCaller(0, cur, from)
103 mintedBlockStore.Burn(caller, from, tokenID, amount)
104}
105
106func BurnBatch(cur realm, from address, tokenIDs []grc1155.TokenID, amounts []int64) {
107 assertNotFrozen()
108 caller := mustGetBurnCaller(0, cur, from)
109
110 // Burn tokens in one operation
111 mintedBlockStore.BatchBurn(caller, from, tokenIDs, amounts)
112}
113
114func mustGetBurnCaller(_ int, rlm realm, from address) address {
115 caller := accesscontrol.MustGetUserCaller(0, rlm)
116 if admin.IsAdmin(caller) {
117 return caller
118 }
119 if caller != from {
120 panic("caller must be admin or the user")
121 }
122 return caller
123}
124
125func ListSupplies(tokeIDs ...grc1155.TokenID) []map[string]string {
126 assertMigrationStateAvailable()
127 return mintedBlockStore.ListSupplies(tokeIDs...)
128}
129
130// GetInventory returns all block tokens owned by user with their balances.
131func GetInventory(user address) []map[string]string {
132 assertMigrationStateAvailable()
133 return mintedBlockStore.GetInventory(user)
134}
135
136func mustGetMintableSupply(block map[string]string) int64 {
137 maxSupply := mustParseInt64(block["maxSupply"])
138 if maxSupply == -1 {
139 panic("block not mintable")
140 }
141 return maxSupply
142}
143
144func mustGetTotalMintPrice(block map[string]string, amount int64) int64 {
145 // mintPrice is a uint32 block property; parse it as uint32 first to preserve
146 // the domain bound, then widen to int64 for payment arithmetic.
147 mintPrice := int64(mustParseUint32(block["mintPrice"]))
148 if mintPrice == 0 || amount == 0 {
149 return 0
150 }
151 if mintPrice > int64(9223372036854775807)/amount {
152 panic("mint price overflow")
153 }
154 return mintPrice * amount
155}