github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/sdk/bank/keeper.go (about)

     1  package bank
     2  
     3  import (
     4  	"fmt"
     5  	"log/slog"
     6  
     7  	"github.com/gnolang/gno/tm2/pkg/crypto"
     8  	"github.com/gnolang/gno/tm2/pkg/sdk"
     9  	"github.com/gnolang/gno/tm2/pkg/sdk/auth"
    10  	"github.com/gnolang/gno/tm2/pkg/std"
    11  )
    12  
    13  // bank.Keeper defines a module interface that facilitates the transfer of
    14  // coins between accounts without the possibility of creating coins.
    15  type BankKeeperI interface {
    16  	ViewKeeperI
    17  
    18  	InputOutputCoins(ctx sdk.Context, inputs []Input, outputs []Output) error
    19  	SendCoins(ctx sdk.Context, fromAddr crypto.Address, toAddr crypto.Address, amt std.Coins) error
    20  
    21  	SubtractCoins(ctx sdk.Context, addr crypto.Address, amt std.Coins) (std.Coins, error)
    22  	AddCoins(ctx sdk.Context, addr crypto.Address, amt std.Coins) (std.Coins, error)
    23  	SetCoins(ctx sdk.Context, addr crypto.Address, amt std.Coins) error
    24  }
    25  
    26  var _ BankKeeperI = BankKeeper{}
    27  
    28  // BBankKeeper only allows transfers between accounts without the possibility of
    29  // creating coins. It implements the BankKeeper interface.
    30  type BankKeeper struct {
    31  	ViewKeeper
    32  
    33  	acck auth.AccountKeeper
    34  }
    35  
    36  // NewBankKeeper returns a new BankKeeper.
    37  func NewBankKeeper(acck auth.AccountKeeper) BankKeeper {
    38  	return BankKeeper{
    39  		ViewKeeper: NewViewKeeper(acck),
    40  		acck:       acck,
    41  	}
    42  }
    43  
    44  // InputOutputCoins handles a list of inputs and outputs
    45  func (bank BankKeeper) InputOutputCoins(ctx sdk.Context, inputs []Input, outputs []Output) error {
    46  	// Safety check ensuring that when sending coins the bank must maintain the
    47  	// Check supply invariant and validity of Coins.
    48  	if err := ValidateInputsOutputs(inputs, outputs); err != nil {
    49  		return err
    50  	}
    51  
    52  	for _, in := range inputs {
    53  		_, err := bank.SubtractCoins(ctx, in.Address, in.Coins)
    54  		if err != nil {
    55  			return err
    56  		}
    57  
    58  		/*
    59  			ctx.EventManager().EmitEvent(
    60  				sdk.NewEvent(
    61  					sdk.EventTypeMessage,
    62  					sdk.NewAttribute(types.AttributeKeySender, in.Address.String()),
    63  				),
    64  			)
    65  		*/
    66  	}
    67  
    68  	for _, out := range outputs {
    69  		_, err := bank.AddCoins(ctx, out.Address, out.Coins)
    70  		if err != nil {
    71  			return err
    72  		}
    73  
    74  		/*
    75  			ctx.EventManager().EmitEvent(
    76  				sdk.NewEvent(
    77  					types.EventTypeTransfer,
    78  					sdk.NewAttribute(types.AttributeKeyRecipient, out.Address.String()),
    79  				),
    80  			)
    81  		*/
    82  	}
    83  
    84  	return nil
    85  }
    86  
    87  // SendCoins moves coins from one account to another
    88  func (bank BankKeeper) SendCoins(ctx sdk.Context, fromAddr crypto.Address, toAddr crypto.Address, amt std.Coins) error {
    89  	_, err := bank.SubtractCoins(ctx, fromAddr, amt)
    90  	if err != nil {
    91  		return err
    92  	}
    93  
    94  	_, err = bank.AddCoins(ctx, toAddr, amt)
    95  	if err != nil {
    96  		return err
    97  	}
    98  
    99  	/*
   100  		ctx.EventManager().EmitEvents(sdk.Events{
   101  			sdk.NewEvent(
   102  				types.EventTypeTransfer,
   103  				sdk.NewAttribute(types.AttributeKeyRecipient, toAddr.String()),
   104  				sdk.NewAttribute(sdk.AttributeKeyAmount, amt.String()),
   105  			),
   106  			sdk.NewEvent(
   107  				sdk.EventTypeMessage,
   108  				sdk.NewAttribute(types.AttributeKeySender, fromAddr.String()),
   109  			),
   110  		})
   111  	*/
   112  
   113  	return nil
   114  }
   115  
   116  // SubtractCoins subtracts amt from the coins at the addr.
   117  //
   118  // CONTRACT: If the account is a vesting account, the amount has to be spendable.
   119  func (bank BankKeeper) SubtractCoins(ctx sdk.Context, addr crypto.Address, amt std.Coins) (std.Coins, error) {
   120  	if !amt.IsValid() {
   121  		return nil, std.ErrInvalidCoins(amt.String())
   122  	}
   123  
   124  	oldCoins := std.NewCoins()
   125  	acc := bank.acck.GetAccount(ctx, addr)
   126  	if acc != nil {
   127  		oldCoins = acc.GetCoins()
   128  	}
   129  
   130  	newCoins := oldCoins.SubUnsafe(amt)
   131  	if !newCoins.IsValid() {
   132  		err := std.ErrInsufficientCoins(
   133  			fmt.Sprintf("insufficient account funds; %s < %s", oldCoins, amt),
   134  		)
   135  		return nil, err
   136  	}
   137  	err := bank.SetCoins(ctx, addr, newCoins)
   138  
   139  	return newCoins, err
   140  }
   141  
   142  // AddCoins adds amt to the coins at the addr.
   143  func (bank BankKeeper) AddCoins(ctx sdk.Context, addr crypto.Address, amt std.Coins) (std.Coins, error) {
   144  	if !amt.IsValid() {
   145  		return nil, std.ErrInvalidCoins(amt.String())
   146  	}
   147  
   148  	oldCoins := bank.GetCoins(ctx, addr)
   149  	newCoins := oldCoins.Add(amt)
   150  
   151  	if !newCoins.IsValid() {
   152  		return amt, std.ErrInsufficientCoins(
   153  			fmt.Sprintf("insufficient account funds; %s < %s", oldCoins, amt),
   154  		)
   155  	}
   156  
   157  	err := bank.SetCoins(ctx, addr, newCoins)
   158  	return newCoins, err
   159  }
   160  
   161  // SetCoins sets the coins at the addr.
   162  func (bank BankKeeper) SetCoins(ctx sdk.Context, addr crypto.Address, amt std.Coins) error {
   163  	if !amt.IsValid() {
   164  		return std.ErrInvalidCoins(amt.String())
   165  	}
   166  
   167  	acc := bank.acck.GetAccount(ctx, addr)
   168  	if acc == nil {
   169  		acc = bank.acck.NewAccountWithAddress(ctx, addr)
   170  	}
   171  
   172  	err := acc.SetCoins(amt)
   173  	if err != nil {
   174  		panic(err)
   175  	}
   176  
   177  	bank.acck.SetAccount(ctx, acc)
   178  	return nil
   179  }
   180  
   181  // ----------------------------------------
   182  // ViewKeeper
   183  
   184  // ViewKeeperI defines a module interface that facilitates read only access to
   185  // account balances.
   186  type ViewKeeperI interface {
   187  	GetCoins(ctx sdk.Context, addr crypto.Address) std.Coins
   188  	HasCoins(ctx sdk.Context, addr crypto.Address, amt std.Coins) bool
   189  }
   190  
   191  var _ ViewKeeperI = ViewKeeper{}
   192  
   193  // ViewKeeper implements a read only keeper implementation of ViewKeeperI.
   194  type ViewKeeper struct {
   195  	acck auth.AccountKeeper
   196  }
   197  
   198  // NewViewKeeper returns a new ViewKeeper.
   199  func NewViewKeeper(acck auth.AccountKeeper) ViewKeeper {
   200  	return ViewKeeper{acck: acck}
   201  }
   202  
   203  // Logger returns a module-specific logger.
   204  func (view ViewKeeper) Logger(ctx sdk.Context) *slog.Logger {
   205  	return ctx.Logger().With("module", fmt.Sprintf("x/%s", ModuleName))
   206  }
   207  
   208  // GetCoins returns the coins at the addr.
   209  func (view ViewKeeper) GetCoins(ctx sdk.Context, addr crypto.Address) std.Coins {
   210  	acc := view.acck.GetAccount(ctx, addr)
   211  	if acc == nil {
   212  		return std.NewCoins()
   213  	}
   214  	return acc.GetCoins()
   215  }
   216  
   217  // HasCoins returns whether or not an account has at least amt coins.
   218  func (view ViewKeeper) HasCoins(ctx sdk.Context, addr crypto.Address, amt std.Coins) bool {
   219  	return view.GetCoins(ctx, addr).IsAllGTE(amt)
   220  }