github.com/gagliardetto/solana-go@v1.11.0/programs/token/instructions.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 // A Token program on the Solana blockchain. 16 // This program defines a common implementation for Fungible and Non Fungible tokens. 17 18 package token 19 20 import ( 21 "bytes" 22 "fmt" 23 24 ag_spew "github.com/davecgh/go-spew/spew" 25 ag_binary "github.com/gagliardetto/binary" 26 ag_solanago "github.com/gagliardetto/solana-go" 27 ag_text "github.com/gagliardetto/solana-go/text" 28 ag_treeout "github.com/gagliardetto/treeout" 29 ) 30 31 // Maximum number of multisignature signers (max N) 32 const MAX_SIGNERS = 11 33 34 var ProgramID ag_solanago.PublicKey = ag_solanago.TokenProgramID 35 36 func SetProgramID(pubkey ag_solanago.PublicKey) { 37 ProgramID = pubkey 38 ag_solanago.RegisterInstructionDecoder(ProgramID, registryDecodeInstruction) 39 } 40 41 const ProgramName = "Token" 42 43 func init() { 44 if !ProgramID.IsZero() { 45 ag_solanago.RegisterInstructionDecoder(ProgramID, registryDecodeInstruction) 46 } 47 } 48 49 const ( 50 // Initializes a new mint and optionally deposits all the newly minted 51 // tokens in an account. 52 // 53 // The `InitializeMint` instruction requires no signers and MUST be 54 // included within the same Transaction as the system program's 55 // `CreateAccount` instruction that creates the account being initialized. 56 // Otherwise another party can acquire ownership of the uninitialized 57 // account. 58 Instruction_InitializeMint uint8 = iota 59 60 // Initializes a new account to hold tokens. If this account is associated 61 // with the native mint then the token balance of the initialized account 62 // will be equal to the amount of SOL in the account. If this account is 63 // associated with another mint, that mint must be initialized before this 64 // command can succeed. 65 // 66 // The `InitializeAccount` instruction requires no signers and MUST be 67 // included within the same Transaction as the system program's 68 // `CreateAccount` instruction that creates the account being initialized. 69 // Otherwise another party can acquire ownership of the uninitialized 70 // account. 71 Instruction_InitializeAccount 72 73 // Initializes a multisignature account with N provided signers. 74 // 75 // Multisignature accounts can used in place of any single owner/delegate 76 // accounts in any token instruction that require an owner/delegate to be 77 // present. The variant field represents the number of signers (M) 78 // required to validate this multisignature account. 79 // 80 // The `InitializeMultisig` instruction requires no signers and MUST be 81 // included within the same Transaction as the system program's 82 // `CreateAccount` instruction that creates the account being initialized. 83 // Otherwise another party can acquire ownership of the uninitialized 84 // account. 85 Instruction_InitializeMultisig 86 87 // Transfers tokens from one account to another either directly or via a 88 // delegate. If this account is associated with the native mint then equal 89 // amounts of SOL and Tokens will be transferred to the destination 90 // account. 91 Instruction_Transfer 92 93 // Approves a delegate. A delegate is given the authority over tokens on 94 // behalf of the source account's owner. 95 Instruction_Approve 96 97 // Revokes the delegate's authority. 98 Instruction_Revoke 99 100 // Sets a new authority of a mint or account. 101 Instruction_SetAuthority 102 103 // Mints new tokens to an account. The native mint does not support 104 // minting. 105 Instruction_MintTo 106 107 // Burns tokens by removing them from an account. `Burn` does not support 108 // accounts associated with the native mint, use `CloseAccount` instead. 109 Instruction_Burn 110 111 // Close an account by transferring all its SOL to the destination account. 112 // Non-native accounts may only be closed if its token amount is zero. 113 Instruction_CloseAccount 114 115 // Freeze an Initialized account using the Mint's freeze_authority (if set). 116 Instruction_FreezeAccount 117 118 // Thaw a Frozen account using the Mint's freeze_authority (if set). 119 Instruction_ThawAccount 120 121 // Transfers tokens from one account to another either directly or via a 122 // delegate. If this account is associated with the native mint then equal 123 // amounts of SOL and Tokens will be transferred to the destination 124 // account. 125 // 126 // This instruction differs from Transfer in that the token mint and 127 // decimals value is checked by the caller. This may be useful when 128 // creating transactions offline or within a hardware wallet. 129 Instruction_TransferChecked 130 131 // Approves a delegate. A delegate is given the authority over tokens on 132 // behalf of the source account's owner. 133 // 134 // This instruction differs from Approve in that the token mint and 135 // decimals value is checked by the caller. This may be useful when 136 // creating transactions offline or within a hardware wallet. 137 Instruction_ApproveChecked 138 139 // Mints new tokens to an account. The native mint does not support minting. 140 // 141 // This instruction differs from MintTo in that the decimals value is 142 // checked by the caller. This may be useful when creating transactions 143 // offline or within a hardware wallet. 144 Instruction_MintToChecked 145 146 // Burns tokens by removing them from an account. `BurnChecked` does not 147 // support accounts associated with the native mint, use `CloseAccount` 148 // instead. 149 // 150 // This instruction differs from Burn in that the decimals value is checked 151 // by the caller. This may be useful when creating transactions offline or 152 // within a hardware wallet. 153 Instruction_BurnChecked 154 155 // Like InitializeAccount, but the owner pubkey is passed via instruction data 156 // rather than the accounts list. This variant may be preferable when using 157 // Cross Program Invocation from an instruction that does not need the owner's 158 // `AccountInfo` otherwise. 159 Instruction_InitializeAccount2 160 161 // Given a wrapped / native token account (a token account containing SOL) 162 // updates its amount field based on the account's underlying `lamports`. 163 // This is useful if a non-wrapped SOL account uses `system_instruction::transfer` 164 // to move lamports to a wrapped token account, and needs to have its token 165 // `amount` field updated. 166 Instruction_SyncNative 167 168 // Like InitializeAccount2, but does not require the Rent sysvar to be provided. 169 Instruction_InitializeAccount3 170 171 // Like InitializeMultisig, but does not require the Rent sysvar to be provided. 172 Instruction_InitializeMultisig2 173 174 // Like InitializeMint, but does not require the Rent sysvar to be provided. 175 Instruction_InitializeMint2 176 ) 177 178 // InstructionIDToName returns the name of the instruction given its ID. 179 func InstructionIDToName(id uint8) string { 180 switch id { 181 case Instruction_InitializeMint: 182 return "InitializeMint" 183 case Instruction_InitializeAccount: 184 return "InitializeAccount" 185 case Instruction_InitializeMultisig: 186 return "InitializeMultisig" 187 case Instruction_Transfer: 188 return "Transfer" 189 case Instruction_Approve: 190 return "Approve" 191 case Instruction_Revoke: 192 return "Revoke" 193 case Instruction_SetAuthority: 194 return "SetAuthority" 195 case Instruction_MintTo: 196 return "MintTo" 197 case Instruction_Burn: 198 return "Burn" 199 case Instruction_CloseAccount: 200 return "CloseAccount" 201 case Instruction_FreezeAccount: 202 return "FreezeAccount" 203 case Instruction_ThawAccount: 204 return "ThawAccount" 205 case Instruction_TransferChecked: 206 return "TransferChecked" 207 case Instruction_ApproveChecked: 208 return "ApproveChecked" 209 case Instruction_MintToChecked: 210 return "MintToChecked" 211 case Instruction_BurnChecked: 212 return "BurnChecked" 213 case Instruction_InitializeAccount2: 214 return "InitializeAccount2" 215 case Instruction_SyncNative: 216 return "SyncNative" 217 case Instruction_InitializeAccount3: 218 return "InitializeAccount3" 219 case Instruction_InitializeMultisig2: 220 return "InitializeMultisig2" 221 case Instruction_InitializeMint2: 222 return "InitializeMint2" 223 default: 224 return "" 225 } 226 } 227 228 type Instruction struct { 229 ag_binary.BaseVariant 230 } 231 232 func (inst *Instruction) EncodeToTree(parent ag_treeout.Branches) { 233 if enToTree, ok := inst.Impl.(ag_text.EncodableToTree); ok { 234 enToTree.EncodeToTree(parent) 235 } else { 236 parent.Child(ag_spew.Sdump(inst)) 237 } 238 } 239 240 var InstructionImplDef = ag_binary.NewVariantDefinition( 241 ag_binary.Uint8TypeIDEncoding, 242 []ag_binary.VariantType{ 243 { 244 "InitializeMint", (*InitializeMint)(nil), 245 }, 246 { 247 "InitializeAccount", (*InitializeAccount)(nil), 248 }, 249 { 250 "InitializeMultisig", (*InitializeMultisig)(nil), 251 }, 252 { 253 "Transfer", (*Transfer)(nil), 254 }, 255 { 256 "Approve", (*Approve)(nil), 257 }, 258 { 259 "Revoke", (*Revoke)(nil), 260 }, 261 { 262 "SetAuthority", (*SetAuthority)(nil), 263 }, 264 { 265 "MintTo", (*MintTo)(nil), 266 }, 267 { 268 "Burn", (*Burn)(nil), 269 }, 270 { 271 "CloseAccount", (*CloseAccount)(nil), 272 }, 273 { 274 "FreezeAccount", (*FreezeAccount)(nil), 275 }, 276 { 277 "ThawAccount", (*ThawAccount)(nil), 278 }, 279 { 280 "TransferChecked", (*TransferChecked)(nil), 281 }, 282 { 283 "ApproveChecked", (*ApproveChecked)(nil), 284 }, 285 { 286 "MintToChecked", (*MintToChecked)(nil), 287 }, 288 { 289 "BurnChecked", (*BurnChecked)(nil), 290 }, 291 { 292 "InitializeAccount2", (*InitializeAccount2)(nil), 293 }, 294 { 295 "SyncNative", (*SyncNative)(nil), 296 }, 297 { 298 "InitializeAccount3", (*InitializeAccount3)(nil), 299 }, 300 { 301 "InitializeMultisig2", (*InitializeMultisig2)(nil), 302 }, 303 { 304 "InitializeMint2", (*InitializeMint2)(nil), 305 }, 306 }, 307 ) 308 309 func (inst *Instruction) ProgramID() ag_solanago.PublicKey { 310 return ProgramID 311 } 312 313 func (inst *Instruction) Accounts() (out []*ag_solanago.AccountMeta) { 314 return inst.Impl.(ag_solanago.AccountsGettable).GetAccounts() 315 } 316 317 func (inst *Instruction) Data() ([]byte, error) { 318 buf := new(bytes.Buffer) 319 if err := ag_binary.NewBinEncoder(buf).Encode(inst); err != nil { 320 return nil, fmt.Errorf("unable to encode instruction: %w", err) 321 } 322 return buf.Bytes(), nil 323 } 324 325 func (inst *Instruction) TextEncode(encoder *ag_text.Encoder, option *ag_text.Option) error { 326 return encoder.Encode(inst.Impl, option) 327 } 328 329 func (inst *Instruction) UnmarshalWithDecoder(decoder *ag_binary.Decoder) error { 330 return inst.BaseVariant.UnmarshalBinaryVariant(decoder, InstructionImplDef) 331 } 332 333 func (inst Instruction) MarshalWithEncoder(encoder *ag_binary.Encoder) error { 334 err := encoder.WriteUint8(inst.TypeID.Uint8()) 335 if err != nil { 336 return fmt.Errorf("unable to write variant type: %w", err) 337 } 338 return encoder.Encode(inst.Impl) 339 } 340 341 func registryDecodeInstruction(accounts []*ag_solanago.AccountMeta, data []byte) (interface{}, error) { 342 inst, err := DecodeInstruction(accounts, data) 343 if err != nil { 344 return nil, err 345 } 346 return inst, nil 347 } 348 349 func DecodeInstruction(accounts []*ag_solanago.AccountMeta, data []byte) (*Instruction, error) { 350 inst := new(Instruction) 351 if err := ag_binary.NewBinDecoder(data).Decode(inst); err != nil { 352 return nil, fmt.Errorf("unable to decode instruction: %w", err) 353 } 354 if v, ok := inst.Impl.(ag_solanago.AccountsSettable); ok { 355 err := v.SetAccounts(accounts) 356 if err != nil { 357 return nil, fmt.Errorf("unable to set accounts for instruction: %w", err) 358 } 359 } 360 return inst, nil 361 }