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  }