/
nft.go
210 lines (178 loc) · 6.61 KB
/
nft.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
package keeper
import (
"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/likecoin/likecoin-chain/v3/backport/cosmos-sdk/v0.46.0-rc1/x/nft"
)
// Mint defines a method for minting a new nft
func (k Keeper) Mint(ctx sdk.Context, token nft.NFT, receiver sdk.AccAddress) error {
if !k.HasClass(ctx, token.ClassId) {
return sdkerrors.Wrap(nft.ErrClassNotExists, token.ClassId)
}
if k.HasNFT(ctx, token.ClassId, token.Id) {
return sdkerrors.Wrap(nft.ErrNFTExists, token.Id)
}
k.setNFT(ctx, token)
k.setOwner(ctx, token.ClassId, token.Id, receiver)
k.incrTotalSupply(ctx, token.ClassId)
ctx.EventManager().EmitTypedEvent(&nft.EventMint{
ClassId: token.ClassId,
Id: token.Id,
Owner: receiver.String(),
})
return nil
}
// Burn defines a method for burning a nft from a specific account.
// Note: When the upper module uses this method, it needs to authenticate nft
func (k Keeper) Burn(ctx sdk.Context, classID string, nftID string) error {
if !k.HasClass(ctx, classID) {
return sdkerrors.Wrap(nft.ErrClassNotExists, classID)
}
if !k.HasNFT(ctx, classID, nftID) {
return sdkerrors.Wrap(nft.ErrNFTNotExists, nftID)
}
owner := k.GetOwner(ctx, classID, nftID)
nftStore := k.getNFTStore(ctx, classID)
nftStore.Delete([]byte(nftID))
k.deleteOwner(ctx, classID, nftID, owner)
k.decrTotalSupply(ctx, classID)
ctx.EventManager().EmitTypedEvent(&nft.EventBurn{
ClassId: classID,
Id: nftID,
Owner: owner.String(),
})
return nil
}
// Update defines a method for updating an exist nft
// Note: When the upper module uses this method, it needs to authenticate nft
func (k Keeper) Update(ctx sdk.Context, token nft.NFT) error {
if !k.HasClass(ctx, token.ClassId) {
return sdkerrors.Wrap(nft.ErrClassNotExists, token.ClassId)
}
if !k.HasNFT(ctx, token.ClassId, token.Id) {
return sdkerrors.Wrap(nft.ErrNFTNotExists, token.Id)
}
k.setNFT(ctx, token)
return nil
}
// Transfer defines a method for sending a nft from one account to another account.
// Note: When the upper module uses this method, it needs to authenticate nft
func (k Keeper) Transfer(ctx sdk.Context,
classID string,
nftID string,
receiver sdk.AccAddress,
) error {
if !k.HasClass(ctx, classID) {
return sdkerrors.Wrap(nft.ErrClassNotExists, classID)
}
if !k.HasNFT(ctx, classID, nftID) {
return sdkerrors.Wrap(nft.ErrNFTNotExists, nftID)
}
owner := k.GetOwner(ctx, classID, nftID)
k.deleteOwner(ctx, classID, nftID, owner)
k.setOwner(ctx, classID, nftID, receiver)
return nil
}
// GetNFT returns the nft information of the specified classID and nftID
func (k Keeper) GetNFT(ctx sdk.Context, classID, nftID string) (nft.NFT, bool) {
store := k.getNFTStore(ctx, classID)
bz := store.Get([]byte(nftID))
if len(bz) == 0 {
return nft.NFT{}, false
}
var nft nft.NFT
k.cdc.MustUnmarshal(bz, &nft)
return nft, true
}
// GetNFTsOfClassByOwner returns all nft information of the specified classID under the specified owner
func (k Keeper) GetNFTsOfClassByOwner(ctx sdk.Context, classID string, owner sdk.AccAddress) (nfts []nft.NFT) {
ownerStore := k.getClassStoreByOwner(ctx, owner, classID)
iterator := ownerStore.Iterator(nil, nil)
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
nft, has := k.GetNFT(ctx, classID, string(iterator.Key()))
if has {
nfts = append(nfts, nft)
}
}
return nfts
}
// GetNFTsOfClass returns all nft information under the specified classID
func (k Keeper) GetNFTsOfClass(ctx sdk.Context, classID string) (nfts []nft.NFT) {
nftStore := k.getNFTStore(ctx, classID)
iterator := nftStore.Iterator(nil, nil)
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
var nft nft.NFT
k.cdc.MustUnmarshal(iterator.Value(), &nft)
nfts = append(nfts, nft)
}
return nfts
}
// GetOwner returns the owner information of the specified nft
func (k Keeper) GetOwner(ctx sdk.Context, classID string, nftID string) sdk.AccAddress {
store := ctx.KVStore(k.storeKey)
bz := store.Get(ownerStoreKey(classID, nftID))
return sdk.AccAddress(bz)
}
// GetBalance returns the specified account, the number of all nfts under the specified classID
func (k Keeper) GetBalance(ctx sdk.Context, classID string, owner sdk.AccAddress) uint64 {
nfts := k.GetNFTsOfClassByOwner(ctx, classID, owner)
return uint64(len(nfts))
}
// GetTotalSupply returns the number of all nfts under the specified classID
func (k Keeper) GetTotalSupply(ctx sdk.Context, classID string) uint64 {
store := ctx.KVStore(k.storeKey)
bz := store.Get(classTotalSupply(classID))
return sdk.BigEndianToUint64(bz)
}
// HasNFT determines whether the specified classID and nftID exist
func (k Keeper) HasNFT(ctx sdk.Context, classID, id string) bool {
store := k.getNFTStore(ctx, classID)
return store.Has([]byte(id))
}
func (k Keeper) setNFT(ctx sdk.Context, token nft.NFT) {
nftStore := k.getNFTStore(ctx, token.ClassId)
bz := k.cdc.MustMarshal(&token)
nftStore.Set([]byte(token.Id), bz)
}
func (k Keeper) setOwner(ctx sdk.Context, classID, nftID string, owner sdk.AccAddress) {
store := ctx.KVStore(k.storeKey)
store.Set(ownerStoreKey(classID, nftID), owner.Bytes())
ownerStore := k.getClassStoreByOwner(ctx, owner, classID)
ownerStore.Set([]byte(nftID), Placeholder)
}
func (k Keeper) deleteOwner(ctx sdk.Context, classID, nftID string, owner sdk.AccAddress) {
store := ctx.KVStore(k.storeKey)
store.Delete(ownerStoreKey(classID, nftID))
ownerStore := k.getClassStoreByOwner(ctx, owner, classID)
ownerStore.Delete([]byte(nftID))
}
func (k Keeper) getNFTStore(ctx sdk.Context, classID string) prefix.Store {
store := ctx.KVStore(k.storeKey)
return prefix.NewStore(store, nftStoreKey(classID))
}
func (k Keeper) getClassStoreByOwner(ctx sdk.Context, owner sdk.AccAddress, classID string) prefix.Store {
store := ctx.KVStore(k.storeKey)
key := nftOfClassByOwnerStoreKey(owner, classID)
return prefix.NewStore(store, key)
}
func (k Keeper) prefixStoreNftOfClassByOwner(ctx sdk.Context, owner sdk.AccAddress) prefix.Store {
store := ctx.KVStore(k.storeKey)
key := prefixNftOfClassByOwnerStoreKey(owner)
return prefix.NewStore(store, key)
}
func (k Keeper) incrTotalSupply(ctx sdk.Context, classID string) {
supply := k.GetTotalSupply(ctx, classID) + 1
k.updateTotalSupply(ctx, classID, supply)
}
func (k Keeper) decrTotalSupply(ctx sdk.Context, classID string) {
supply := k.GetTotalSupply(ctx, classID) - 1
k.updateTotalSupply(ctx, classID, supply)
}
func (k Keeper) updateTotalSupply(ctx sdk.Context, classID string, supply uint64) {
store := ctx.KVStore(k.storeKey)
supplyKey := classTotalSupply(classID)
store.Set(supplyKey, sdk.Uint64ToBigEndian(supply))
}