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 }