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  }