github.com/gagliardetto/solana-go@v1.11.0/programs/token/TransferChecked.go (about) 1 // Copyright 2021 github.com/gagliardetto 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package token 16 17 import ( 18 "errors" 19 "fmt" 20 21 ag_binary "github.com/gagliardetto/binary" 22 ag_solanago "github.com/gagliardetto/solana-go" 23 ag_format "github.com/gagliardetto/solana-go/text/format" 24 ag_treeout "github.com/gagliardetto/treeout" 25 ) 26 27 // Transfers tokens from one account to another either directly or via a 28 // delegate. If this account is associated with the native mint then equal 29 // amounts of SOL and Tokens will be transferred to the destination 30 // account. 31 // 32 // This instruction differs from Transfer in that the token mint and 33 // decimals value is checked by the caller. This may be useful when 34 // creating transactions offline or within a hardware wallet. 35 type TransferChecked struct { 36 // The amount of tokens to transfer. 37 Amount *uint64 38 39 // Expected number of base 10 digits to the right of the decimal place. 40 Decimals *uint8 41 42 // [0] = [WRITE] source 43 // ··········· The source account. 44 // 45 // [1] = [] mint 46 // ··········· The token mint. 47 // 48 // [2] = [WRITE] destination 49 // ··········· The destination account. 50 // 51 // [3] = [] owner 52 // ··········· The source account's owner/delegate. 53 // 54 // [4...] = [SIGNER] signers 55 // ··········· M signer accounts. 56 Accounts ag_solanago.AccountMetaSlice `bin:"-" borsh_skip:"true"` 57 Signers ag_solanago.AccountMetaSlice `bin:"-" borsh_skip:"true"` 58 } 59 60 func (obj *TransferChecked) SetAccounts(accounts []*ag_solanago.AccountMeta) error { 61 obj.Accounts, obj.Signers = ag_solanago.AccountMetaSlice(accounts).SplitFrom(4) 62 return nil 63 } 64 65 func (slice TransferChecked) GetAccounts() (accounts []*ag_solanago.AccountMeta) { 66 accounts = append(accounts, slice.Accounts...) 67 accounts = append(accounts, slice.Signers...) 68 return 69 } 70 71 // NewTransferCheckedInstructionBuilder creates a new `TransferChecked` instruction builder. 72 func NewTransferCheckedInstructionBuilder() *TransferChecked { 73 nd := &TransferChecked{ 74 Accounts: make(ag_solanago.AccountMetaSlice, 4), 75 Signers: make(ag_solanago.AccountMetaSlice, 0), 76 } 77 return nd 78 } 79 80 // SetAmount sets the "amount" parameter. 81 // The amount of tokens to transfer. 82 func (inst *TransferChecked) SetAmount(amount uint64) *TransferChecked { 83 inst.Amount = &amount 84 return inst 85 } 86 87 // SetDecimals sets the "decimals" parameter. 88 // Expected number of base 10 digits to the right of the decimal place. 89 func (inst *TransferChecked) SetDecimals(decimals uint8) *TransferChecked { 90 inst.Decimals = &decimals 91 return inst 92 } 93 94 // SetSourceAccount sets the "source" account. 95 // The source account. 96 func (inst *TransferChecked) SetSourceAccount(source ag_solanago.PublicKey) *TransferChecked { 97 inst.Accounts[0] = ag_solanago.Meta(source).WRITE() 98 return inst 99 } 100 101 // GetSourceAccount gets the "source" account. 102 // The source account. 103 func (inst *TransferChecked) GetSourceAccount() *ag_solanago.AccountMeta { 104 return inst.Accounts[0] 105 } 106 107 // SetMintAccount sets the "mint" account. 108 // The token mint. 109 func (inst *TransferChecked) SetMintAccount(mint ag_solanago.PublicKey) *TransferChecked { 110 inst.Accounts[1] = ag_solanago.Meta(mint) 111 return inst 112 } 113 114 // GetMintAccount gets the "mint" account. 115 // The token mint. 116 func (inst *TransferChecked) GetMintAccount() *ag_solanago.AccountMeta { 117 return inst.Accounts[1] 118 } 119 120 // SetDestinationAccount sets the "destination" account. 121 // The destination account. 122 func (inst *TransferChecked) SetDestinationAccount(destination ag_solanago.PublicKey) *TransferChecked { 123 inst.Accounts[2] = ag_solanago.Meta(destination).WRITE() 124 return inst 125 } 126 127 // GetDestinationAccount gets the "destination" account. 128 // The destination account. 129 func (inst *TransferChecked) GetDestinationAccount() *ag_solanago.AccountMeta { 130 return inst.Accounts[2] 131 } 132 133 // SetOwnerAccount sets the "owner" account. 134 // The source account's owner/delegate. 135 func (inst *TransferChecked) SetOwnerAccount(owner ag_solanago.PublicKey, multisigSigners ...ag_solanago.PublicKey) *TransferChecked { 136 inst.Accounts[3] = ag_solanago.Meta(owner) 137 if len(multisigSigners) == 0 { 138 inst.Accounts[3].SIGNER() 139 } 140 for _, signer := range multisigSigners { 141 inst.Signers = append(inst.Signers, ag_solanago.Meta(signer).SIGNER()) 142 } 143 return inst 144 } 145 146 // GetOwnerAccount gets the "owner" account. 147 // The source account's owner/delegate. 148 func (inst *TransferChecked) GetOwnerAccount() *ag_solanago.AccountMeta { 149 return inst.Accounts[3] 150 } 151 152 func (inst TransferChecked) Build() *Instruction { 153 return &Instruction{BaseVariant: ag_binary.BaseVariant{ 154 Impl: inst, 155 TypeID: ag_binary.TypeIDFromUint8(Instruction_TransferChecked), 156 }} 157 } 158 159 // ValidateAndBuild validates the instruction parameters and accounts; 160 // if there is a validation error, it returns the error. 161 // Otherwise, it builds and returns the instruction. 162 func (inst TransferChecked) ValidateAndBuild() (*Instruction, error) { 163 if err := inst.Validate(); err != nil { 164 return nil, err 165 } 166 return inst.Build(), nil 167 } 168 169 func (inst *TransferChecked) Validate() error { 170 // Check whether all (required) parameters are set: 171 { 172 if inst.Amount == nil { 173 return errors.New("Amount parameter is not set") 174 } 175 if inst.Decimals == nil { 176 return errors.New("Decimals parameter is not set") 177 } 178 } 179 180 // Check whether all (required) accounts are set: 181 { 182 if inst.Accounts[0] == nil { 183 return errors.New("accounts.Source is not set") 184 } 185 if inst.Accounts[1] == nil { 186 return errors.New("accounts.Mint is not set") 187 } 188 if inst.Accounts[2] == nil { 189 return errors.New("accounts.Destination is not set") 190 } 191 if inst.Accounts[3] == nil { 192 return errors.New("accounts.Owner is not set") 193 } 194 if !inst.Accounts[3].IsSigner && len(inst.Signers) == 0 { 195 return fmt.Errorf("accounts.Signers is not set") 196 } 197 if len(inst.Signers) > MAX_SIGNERS { 198 return fmt.Errorf("too many signers; got %v, but max is 11", len(inst.Signers)) 199 } 200 } 201 return nil 202 } 203 204 func (inst *TransferChecked) EncodeToTree(parent ag_treeout.Branches) { 205 parent.Child(ag_format.Program(ProgramName, ProgramID)). 206 // 207 ParentFunc(func(programBranch ag_treeout.Branches) { 208 programBranch.Child(ag_format.Instruction("TransferChecked")). 209 // 210 ParentFunc(func(instructionBranch ag_treeout.Branches) { 211 212 // Parameters of the instruction: 213 instructionBranch.Child("Params").ParentFunc(func(paramsBranch ag_treeout.Branches) { 214 paramsBranch.Child(ag_format.Param(" Amount", *inst.Amount)) 215 paramsBranch.Child(ag_format.Param("Decimals", *inst.Decimals)) 216 }) 217 218 // Accounts of the instruction: 219 instructionBranch.Child("Accounts").ParentFunc(func(accountsBranch ag_treeout.Branches) { 220 accountsBranch.Child(ag_format.Meta(" source", inst.Accounts[0])) 221 accountsBranch.Child(ag_format.Meta(" mint", inst.Accounts[1])) 222 accountsBranch.Child(ag_format.Meta("destination", inst.Accounts[2])) 223 accountsBranch.Child(ag_format.Meta(" owner", inst.Accounts[3])) 224 225 signersBranch := accountsBranch.Child(fmt.Sprintf("signers[len=%v]", len(inst.Signers))) 226 for i, v := range inst.Signers { 227 if len(inst.Signers) > 9 && i < 10 { 228 signersBranch.Child(ag_format.Meta(fmt.Sprintf(" [%v]", i), v)) 229 } else { 230 signersBranch.Child(ag_format.Meta(fmt.Sprintf("[%v]", i), v)) 231 } 232 } 233 }) 234 }) 235 }) 236 } 237 238 func (obj TransferChecked) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { 239 // Serialize `Amount` param: 240 err = encoder.Encode(obj.Amount) 241 if err != nil { 242 return err 243 } 244 // Serialize `Decimals` param: 245 err = encoder.Encode(obj.Decimals) 246 if err != nil { 247 return err 248 } 249 return nil 250 } 251 func (obj *TransferChecked) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { 252 // Deserialize `Amount`: 253 err = decoder.Decode(&obj.Amount) 254 if err != nil { 255 return err 256 } 257 // Deserialize `Decimals`: 258 err = decoder.Decode(&obj.Decimals) 259 if err != nil { 260 return err 261 } 262 return nil 263 } 264 265 // NewTransferCheckedInstruction declares a new TransferChecked instruction with the provided parameters and accounts. 266 func NewTransferCheckedInstruction( 267 // Parameters: 268 amount uint64, 269 decimals uint8, 270 // Accounts: 271 source ag_solanago.PublicKey, 272 mint ag_solanago.PublicKey, 273 destination ag_solanago.PublicKey, 274 owner ag_solanago.PublicKey, 275 multisigSigners []ag_solanago.PublicKey, 276 ) *TransferChecked { 277 return NewTransferCheckedInstructionBuilder(). 278 SetAmount(amount). 279 SetDecimals(decimals). 280 SetSourceAccount(source). 281 SetMintAccount(mint). 282 SetDestinationAccount(destination). 283 SetOwnerAccount(owner, multisigSigners...) 284 }