github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/sdk/bank/msgs.go (about) 1 package bank 2 3 import ( 4 "github.com/gnolang/gno/tm2/pkg/amino" 5 "github.com/gnolang/gno/tm2/pkg/crypto" 6 "github.com/gnolang/gno/tm2/pkg/errors" 7 "github.com/gnolang/gno/tm2/pkg/std" 8 ) 9 10 // RouterKey is they name of the bank module 11 const RouterKey = ModuleName 12 13 // MsgSend - high level transaction of the coin module 14 type MsgSend struct { 15 FromAddress crypto.Address `json:"from_address" yaml:"from_address"` 16 ToAddress crypto.Address `json:"to_address" yaml:"to_address"` 17 Amount std.Coins `json:"amount" yaml:"amount"` 18 } 19 20 var _ std.Msg = MsgSend{} 21 22 // NewMsgSend - construct arbitrary multi-in, multi-out send msg. 23 func NewMsgSend(fromAddr, toAddr crypto.Address, amount std.Coins) MsgSend { 24 return MsgSend{FromAddress: fromAddr, ToAddress: toAddr, Amount: amount} 25 } 26 27 // Route Implements Msg. 28 func (msg MsgSend) Route() string { return RouterKey } 29 30 // Type Implements Msg. 31 func (msg MsgSend) Type() string { return "send" } 32 33 // ValidateBasic Implements Msg. 34 func (msg MsgSend) ValidateBasic() error { 35 if msg.FromAddress.IsZero() { 36 return std.ErrInvalidAddress("missing sender address") 37 } 38 if msg.ToAddress.IsZero() { 39 return std.ErrInvalidAddress("missing recipient address") 40 } 41 if !msg.Amount.IsValid() { 42 return std.ErrInvalidCoins("send amount is invalid: " + msg.Amount.String()) 43 } 44 if !msg.Amount.IsAllPositive() { 45 return std.ErrInsufficientCoins("send amount must be positive") 46 } 47 return nil 48 } 49 50 // GetSignBytes Implements Msg. 51 func (msg MsgSend) GetSignBytes() []byte { 52 return std.MustSortJSON(amino.MustMarshalJSON(msg)) 53 } 54 55 // GetSigners Implements Msg. 56 func (msg MsgSend) GetSigners() []crypto.Address { 57 return []crypto.Address{msg.FromAddress} 58 } 59 60 // MsgMultiSend - high level transaction of the coin module 61 type MsgMultiSend struct { 62 Inputs []Input `json:"inputs" yaml:"inputs"` 63 Outputs []Output `json:"outputs" yaml:"outputs"` 64 } 65 66 var _ std.Msg = MsgMultiSend{} 67 68 // NewMsgMultiSend - construct arbitrary multi-in, multi-out send msg. 69 func NewMsgMultiSend(in []Input, out []Output) MsgMultiSend { 70 return MsgMultiSend{Inputs: in, Outputs: out} 71 } 72 73 // Route Implements Msg 74 func (msg MsgMultiSend) Route() string { return RouterKey } 75 76 // Type Implements Msg 77 func (msg MsgMultiSend) Type() string { return "multisend" } 78 79 // ValidateBasic Implements Msg. 80 func (msg MsgMultiSend) ValidateBasic() error { 81 // this just makes sure all the inputs and outputs are properly formatted, 82 // not that they actually have the money inside 83 if len(msg.Inputs) == 0 { 84 return ErrNoInputs() 85 } 86 if len(msg.Outputs) == 0 { 87 return ErrNoOutputs() 88 } 89 90 return ValidateInputsOutputs(msg.Inputs, msg.Outputs) 91 } 92 93 // GetSignBytes Implements Msg. 94 func (msg MsgMultiSend) GetSignBytes() []byte { 95 return std.MustSortJSON(amino.MustMarshalJSON(msg)) 96 } 97 98 // GetSigners Implements Msg. 99 func (msg MsgMultiSend) GetSigners() []crypto.Address { 100 addrs := make([]crypto.Address, len(msg.Inputs)) 101 for i, in := range msg.Inputs { 102 addrs[i] = in.Address 103 } 104 return addrs 105 } 106 107 // Input models transaction input 108 type Input struct { 109 Address crypto.Address `json:"address" yaml:"address"` 110 Coins std.Coins `json:"coins" yaml:"coins"` 111 } 112 113 // ValidateBasic - validate transaction input 114 func (in Input) ValidateBasic() error { 115 if len(in.Address) == 0 { 116 return std.ErrInvalidAddress(in.Address.String()) 117 } 118 if !in.Coins.IsValid() { 119 return std.ErrInvalidCoins(in.Coins.String()) 120 } 121 if !in.Coins.IsAllPositive() { 122 return std.ErrInvalidCoins(in.Coins.String()) 123 } 124 return nil 125 } 126 127 // NewInput - create a transaction input, used with MsgMultiSend 128 func NewInput(addr crypto.Address, coins std.Coins) Input { 129 return Input{ 130 Address: addr, 131 Coins: coins, 132 } 133 } 134 135 // Output models transaction outputs 136 type Output struct { 137 Address crypto.Address `json:"address" yaml:"address"` 138 Coins std.Coins `json:"coins" yaml:"coins"` 139 } 140 141 // ValidateBasic - validate transaction output 142 func (out Output) ValidateBasic() error { 143 if len(out.Address) == 0 { 144 return std.ErrInvalidAddress(out.Address.String()) 145 } 146 if !out.Coins.IsValid() { 147 return std.ErrInvalidCoins(out.Coins.String()) 148 } 149 if !out.Coins.IsAllPositive() { 150 return std.ErrInvalidCoins(out.Coins.String()) 151 } 152 return nil 153 } 154 155 // NewOutput - create a transaction output, used with MsgMultiSend 156 func NewOutput(addr crypto.Address, coins std.Coins) Output { 157 return Output{ 158 Address: addr, 159 Coins: coins, 160 } 161 } 162 163 // ValidateInputsOutputs validates that each respective input and output is 164 // valid and that the sum of inputs is equal to the sum of outputs. 165 func ValidateInputsOutputs(inputs []Input, outputs []Output) error { 166 var totalIn, totalOut std.Coins 167 168 for _, in := range inputs { 169 if err := in.ValidateBasic(); err != nil { 170 return errors.Wrap(err, "") 171 } 172 totalIn = totalIn.Add(in.Coins) 173 } 174 175 for _, out := range outputs { 176 if err := out.ValidateBasic(); err != nil { 177 return errors.Wrap(err, "") 178 } 179 totalOut = totalOut.Add(out.Coins) 180 } 181 182 // make sure inputs and outputs match 183 if !totalIn.IsEqual(totalOut) { 184 return ErrInputOutputMismatch() 185 } 186 187 return nil 188 }