github.com/Finschia/finschia-sdk@v0.48.1/x/token/keeper/supply.go (about) 1 package keeper 2 3 import ( 4 sdk "github.com/Finschia/finschia-sdk/types" 5 sdkerrors "github.com/Finschia/finschia-sdk/types/errors" 6 "github.com/Finschia/finschia-sdk/x/token" 7 ) 8 9 func (k Keeper) Issue(ctx sdk.Context, class token.Contract, owner, to sdk.AccAddress, amount sdk.Int) string { 10 contractID := k.issue(ctx, class) 11 12 event := token.EventIssued{ 13 Creator: owner.String(), 14 ContractId: contractID, 15 Name: class.Name, 16 Symbol: class.Symbol, 17 Uri: class.Uri, 18 Meta: class.Meta, 19 Decimals: class.Decimals, 20 Mintable: class.Mintable, 21 } 22 if err := ctx.EventManager().EmitTypedEvent(&event); err != nil { 23 panic(err) 24 } 25 26 permissions := []token.Permission{ 27 token.PermissionModify, 28 } 29 if class.Mintable { 30 permissions = append(permissions, 31 token.PermissionMint, 32 token.PermissionBurn, 33 ) 34 } 35 36 for _, permission := range permissions { 37 k.Grant(ctx, contractID, nil, owner, permission) 38 } 39 40 k.mintToken(ctx, contractID, to, amount) 41 42 if err := ctx.EventManager().EmitTypedEvent(&token.EventMinted{ 43 ContractId: contractID, 44 Operator: owner.String(), 45 To: to.String(), 46 Amount: amount, 47 }); err != nil { 48 panic(err) 49 } 50 51 return contractID 52 } 53 54 func (k Keeper) issue(ctx sdk.Context, class token.Contract) string { 55 contractID := k.classKeeper.NewID(ctx) 56 class.Id = contractID 57 k.setClass(ctx, class) 58 59 return contractID 60 } 61 62 func (k Keeper) GetClass(ctx sdk.Context, contractID string) (*token.Contract, error) { 63 store := ctx.KVStore(k.storeKey) 64 bz := store.Get(classKey(contractID)) 65 if bz == nil { 66 return nil, token.ErrTokenNotExist.Wrapf("no class for %s", contractID) 67 } 68 69 var class token.Contract 70 if err := k.cdc.Unmarshal(bz, &class); err != nil { 71 panic(err) 72 } 73 74 return &class, nil 75 } 76 77 func (k Keeper) setClass(ctx sdk.Context, class token.Contract) { 78 store := ctx.KVStore(k.storeKey) 79 bz, err := k.cdc.Marshal(&class) 80 if err != nil { 81 panic(err) 82 } 83 84 store.Set(classKey(class.Id), bz) 85 } 86 87 func (k Keeper) Mint(ctx sdk.Context, contractID string, grantee, to sdk.AccAddress, amount sdk.Int) error { 88 if err := k.mint(ctx, contractID, grantee, to, amount); err != nil { 89 return err 90 } 91 92 event := token.EventMinted{ 93 ContractId: contractID, 94 Operator: grantee.String(), 95 To: to.String(), 96 Amount: amount, 97 } 98 if err := ctx.EventManager().EmitTypedEvent(&event); err != nil { 99 panic(err) 100 } 101 return nil 102 } 103 104 func (k Keeper) mint(ctx sdk.Context, contractID string, grantee, to sdk.AccAddress, amount sdk.Int) error { 105 if _, err := k.GetGrant(ctx, contractID, grantee, token.PermissionMint); err != nil { 106 return token.ErrTokenNoPermission.Wrap(err.Error()) 107 } 108 109 k.mintToken(ctx, contractID, to, amount) 110 111 return nil 112 } 113 114 func (k Keeper) mintToken(ctx sdk.Context, contractID string, addr sdk.AccAddress, amount sdk.Int) { 115 k.addToken(ctx, contractID, addr, amount) 116 117 minted := k.GetMinted(ctx, contractID) 118 minted = minted.Add(amount) 119 k.setMinted(ctx, contractID, minted) 120 121 supply := k.GetSupply(ctx, contractID) 122 supply = supply.Add(amount) 123 k.setSupply(ctx, contractID, supply) 124 } 125 126 func (k Keeper) Burn(ctx sdk.Context, contractID string, from sdk.AccAddress, amount sdk.Int) error { 127 if err := k.burn(ctx, contractID, from, amount); err != nil { 128 return err 129 } 130 131 event := token.EventBurned{ 132 ContractId: contractID, 133 Operator: from.String(), 134 From: from.String(), 135 Amount: amount, 136 } 137 if err := ctx.EventManager().EmitTypedEvent(&event); err != nil { 138 panic(err) 139 } 140 return nil 141 } 142 143 func (k Keeper) burn(ctx sdk.Context, contractID string, from sdk.AccAddress, amount sdk.Int) error { 144 if _, err := k.GetGrant(ctx, contractID, from, token.PermissionBurn); err != nil { 145 return token.ErrTokenNoPermission.Wrap(err.Error()) 146 } 147 148 if err := k.burnToken(ctx, contractID, from, amount); err != nil { 149 return err 150 } 151 152 return nil 153 } 154 155 func (k Keeper) OperatorBurn(ctx sdk.Context, contractID string, operator, from sdk.AccAddress, amount sdk.Int) error { 156 if err := k.operatorBurn(ctx, contractID, operator, from, amount); err != nil { 157 return err 158 } 159 160 event := token.EventBurned{ 161 ContractId: contractID, 162 Operator: operator.String(), 163 From: from.String(), 164 Amount: amount, 165 } 166 if err := ctx.EventManager().EmitTypedEvent(&event); err != nil { 167 panic(err) 168 } 169 return nil 170 } 171 172 func (k Keeper) operatorBurn(ctx sdk.Context, contractID string, operator, from sdk.AccAddress, amount sdk.Int) error { 173 _, err := k.GetGrant(ctx, contractID, operator, token.PermissionBurn) 174 if err != nil { 175 return token.ErrTokenNoPermission.Wrap(err.Error()) 176 } 177 if _, err := k.GetAuthorization(ctx, contractID, from, operator); err != nil { 178 return token.ErrTokenNotApproved.Wrap(err.Error()) 179 } 180 181 if err := k.burnToken(ctx, contractID, from, amount); err != nil { 182 return err 183 } 184 185 return nil 186 } 187 188 func (k Keeper) burnToken(ctx sdk.Context, contractID string, addr sdk.AccAddress, amount sdk.Int) error { 189 if err := k.subtractToken(ctx, contractID, addr, amount); err != nil { 190 return err 191 } 192 193 burnt := k.GetBurnt(ctx, contractID) 194 burnt = burnt.Add(amount) 195 k.setBurnt(ctx, contractID, burnt) 196 197 supply := k.GetSupply(ctx, contractID) 198 supply = supply.Sub(amount) 199 k.setSupply(ctx, contractID, supply) 200 201 return nil 202 } 203 204 func (k Keeper) getStatistics(ctx sdk.Context, contractID string, keyPrefix []byte) sdk.Int { 205 store := ctx.KVStore(k.storeKey) 206 amount := sdk.ZeroInt() 207 bz := store.Get(statisticsKey(keyPrefix, contractID)) 208 if bz != nil { 209 if err := amount.Unmarshal(bz); err != nil { 210 panic(err) 211 } 212 } 213 214 return amount 215 } 216 217 // The caller must validate `amount`. 218 func (k Keeper) setStatistics(ctx sdk.Context, contractID string, amount sdk.Int, keyPrefix []byte) { 219 store := ctx.KVStore(k.storeKey) 220 key := statisticsKey(keyPrefix, contractID) 221 if amount.IsZero() { 222 store.Delete(key) 223 } else { 224 bz, err := amount.Marshal() 225 if err != nil { 226 panic(err) 227 } 228 store.Set(key, bz) 229 } 230 } 231 232 func (k Keeper) GetSupply(ctx sdk.Context, contractID string) sdk.Int { 233 return k.getStatistics(ctx, contractID, supplyKeyPrefix) 234 } 235 236 func (k Keeper) GetMinted(ctx sdk.Context, contractID string) sdk.Int { 237 return k.getStatistics(ctx, contractID, mintKeyPrefix) 238 } 239 240 func (k Keeper) GetBurnt(ctx sdk.Context, contractID string) sdk.Int { 241 return k.getStatistics(ctx, contractID, burnKeyPrefix) 242 } 243 244 func (k Keeper) setSupply(ctx sdk.Context, contractID string, amount sdk.Int) { 245 k.setStatistics(ctx, contractID, amount, supplyKeyPrefix) 246 } 247 248 func (k Keeper) setMinted(ctx sdk.Context, contractID string, amount sdk.Int) { 249 k.setStatistics(ctx, contractID, amount, mintKeyPrefix) 250 } 251 252 func (k Keeper) setBurnt(ctx sdk.Context, contractID string, amount sdk.Int) { 253 k.setStatistics(ctx, contractID, amount, burnKeyPrefix) 254 } 255 256 func (k Keeper) Modify(ctx sdk.Context, contractID string, grantee sdk.AccAddress, changes []token.Attribute) error { 257 if err := k.modify(ctx, contractID, changes); err != nil { 258 return err 259 } 260 261 event := token.EventModified{ 262 ContractId: contractID, 263 Operator: grantee.String(), 264 Changes: changes, 265 } 266 if err := ctx.EventManager().EmitTypedEvent(&event); err != nil { 267 panic(err) 268 } 269 return nil 270 } 271 272 func (k Keeper) modify(ctx sdk.Context, contractID string, changes []token.Attribute) error { 273 class, err := k.GetClass(ctx, contractID) 274 if err != nil { 275 panic(err) 276 } 277 278 modifiers := map[token.AttributeKey]func(string){ 279 token.AttributeKeyName: func(name string) { 280 class.Name = name 281 }, 282 token.AttributeKeyURI: func(uri string) { 283 class.Uri = uri 284 }, 285 token.AttributeKeyMeta: func(meta string) { 286 class.Meta = meta 287 }, 288 } 289 for _, change := range changes { 290 key := token.AttributeKeyFromString(change.Key) 291 modifiers[key](change.Value) 292 } 293 294 k.setClass(ctx, *class) 295 296 return nil 297 } 298 299 func (k Keeper) Grant(ctx sdk.Context, contractID string, granter, grantee sdk.AccAddress, permission token.Permission) { 300 k.grant(ctx, contractID, grantee, permission) 301 302 event := token.EventGranted{ 303 ContractId: contractID, 304 Granter: granter.String(), 305 Grantee: grantee.String(), 306 Permission: permission, 307 } 308 if err := ctx.EventManager().EmitTypedEvent(&event); err != nil { 309 panic(err) 310 } 311 } 312 313 func (k Keeper) grant(ctx sdk.Context, contractID string, grantee sdk.AccAddress, permission token.Permission) { 314 k.setGrant(ctx, contractID, grantee, permission) 315 } 316 317 func (k Keeper) Abandon(ctx sdk.Context, contractID string, grantee sdk.AccAddress, permission token.Permission) { 318 k.deleteGrant(ctx, contractID, grantee, permission) 319 320 event := token.EventRenounced{ 321 ContractId: contractID, 322 Grantee: grantee.String(), 323 Permission: permission, 324 } 325 if err := ctx.EventManager().EmitTypedEvent(&event); err != nil { 326 panic(err) 327 } 328 } 329 330 func (k Keeper) GetGrant(ctx sdk.Context, contractID string, grantee sdk.AccAddress, permission token.Permission) (*token.Grant, error) { 331 var grant *token.Grant 332 store := ctx.KVStore(k.storeKey) 333 if store.Has(grantKey(contractID, grantee, permission)) { 334 grant = &token.Grant{ 335 Grantee: grantee.String(), 336 Permission: permission, 337 } 338 return grant, nil 339 } 340 341 return nil, sdkerrors.ErrNotFound.Wrapf("%s has no %s permission", grantee, permission) 342 } 343 344 func (k Keeper) setGrant(ctx sdk.Context, contractID string, grantee sdk.AccAddress, permission token.Permission) { 345 store := ctx.KVStore(k.storeKey) 346 key := grantKey(contractID, grantee, permission) 347 store.Set(key, []byte{}) 348 } 349 350 func (k Keeper) deleteGrant(ctx sdk.Context, contractID string, grantee sdk.AccAddress, permission token.Permission) { 351 store := ctx.KVStore(k.storeKey) 352 key := grantKey(contractID, grantee, permission) 353 store.Delete(key) 354 }