Search Apps Documentation Source Content File Folder Download Copy Actions Download

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}