solana-go/programs/token/instructions.go

362 lines
11 KiB
Go

// Copyright 2021 github.com/gagliardetto
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// A Token program on the Solana blockchain.
// This program defines a common implementation for Fungible and Non Fungible tokens.
package token
import (
"bytes"
"fmt"
ag_spew "github.com/davecgh/go-spew/spew"
ag_binary "github.com/gagliardetto/binary"
ag_solanago "github.com/gagliardetto/solana-go"
ag_text "github.com/gagliardetto/solana-go/text"
ag_treeout "github.com/gagliardetto/treeout"
)
// Maximum number of multisignature signers (max N)
const MAX_SIGNERS = 11
var ProgramID ag_solanago.PublicKey = ag_solanago.TokenProgramID
func SetProgramID(pubkey ag_solanago.PublicKey) {
ProgramID = pubkey
ag_solanago.RegisterInstructionDecoder(ProgramID, registryDecodeInstruction)
}
const ProgramName = "Token"
func init() {
if !ProgramID.IsZero() {
ag_solanago.RegisterInstructionDecoder(ProgramID, registryDecodeInstruction)
}
}
const (
// Initializes a new mint and optionally deposits all the newly minted
// tokens in an account.
//
// The `InitializeMint` instruction requires no signers and MUST be
// included within the same Transaction as the system program's
// `CreateAccount` instruction that creates the account being initialized.
// Otherwise another party can acquire ownership of the uninitialized
// account.
Instruction_InitializeMint uint8 = iota
// Initializes a new account to hold tokens. If this account is associated
// with the native mint then the token balance of the initialized account
// will be equal to the amount of SOL in the account. If this account is
// associated with another mint, that mint must be initialized before this
// command can succeed.
//
// The `InitializeAccount` instruction requires no signers and MUST be
// included within the same Transaction as the system program's
// `CreateAccount` instruction that creates the account being initialized.
// Otherwise another party can acquire ownership of the uninitialized
// account.
Instruction_InitializeAccount
// Initializes a multisignature account with N provided signers.
//
// Multisignature accounts can used in place of any single owner/delegate
// accounts in any token instruction that require an owner/delegate to be
// present. The variant field represents the number of signers (M)
// required to validate this multisignature account.
//
// The `InitializeMultisig` instruction requires no signers and MUST be
// included within the same Transaction as the system program's
// `CreateAccount` instruction that creates the account being initialized.
// Otherwise another party can acquire ownership of the uninitialized
// account.
Instruction_InitializeMultisig
// Transfers tokens from one account to another either directly or via a
// delegate. If this account is associated with the native mint then equal
// amounts of SOL and Tokens will be transferred to the destination
// account.
Instruction_Transfer
// Approves a delegate. A delegate is given the authority over tokens on
// behalf of the source account's owner.
Instruction_Approve
// Revokes the delegate's authority.
Instruction_Revoke
// Sets a new authority of a mint or account.
Instruction_SetAuthority
// Mints new tokens to an account. The native mint does not support
// minting.
Instruction_MintTo
// Burns tokens by removing them from an account. `Burn` does not support
// accounts associated with the native mint, use `CloseAccount` instead.
Instruction_Burn
// Close an account by transferring all its SOL to the destination account.
// Non-native accounts may only be closed if its token amount is zero.
Instruction_CloseAccount
// Freeze an Initialized account using the Mint's freeze_authority (if set).
Instruction_FreezeAccount
// Thaw a Frozen account using the Mint's freeze_authority (if set).
Instruction_ThawAccount
// Transfers tokens from one account to another either directly or via a
// delegate. If this account is associated with the native mint then equal
// amounts of SOL and Tokens will be transferred to the destination
// account.
//
// This instruction differs from Transfer in that the token mint and
// decimals value is checked by the caller. This may be useful when
// creating transactions offline or within a hardware wallet.
Instruction_TransferChecked
// Approves a delegate. A delegate is given the authority over tokens on
// behalf of the source account's owner.
//
// This instruction differs from Approve in that the token mint and
// decimals value is checked by the caller. This may be useful when
// creating transactions offline or within a hardware wallet.
Instruction_ApproveChecked
// Mints new tokens to an account. The native mint does not support minting.
//
// This instruction differs from MintTo in that the decimals value is
// checked by the caller. This may be useful when creating transactions
// offline or within a hardware wallet.
Instruction_MintToChecked
// Burns tokens by removing them from an account. `BurnChecked` does not
// support accounts associated with the native mint, use `CloseAccount`
// instead.
//
// This instruction differs from Burn in that the decimals value is checked
// by the caller. This may be useful when creating transactions offline or
// within a hardware wallet.
Instruction_BurnChecked
// Like InitializeAccount, but the owner pubkey is passed via instruction data
// rather than the accounts list. This variant may be preferable when using
// Cross Program Invocation from an instruction that does not need the owner's
// `AccountInfo` otherwise.
Instruction_InitializeAccount2
// Given a wrapped / native token account (a token account containing SOL)
// updates its amount field based on the account's underlying `lamports`.
// This is useful if a non-wrapped SOL account uses `system_instruction::transfer`
// to move lamports to a wrapped token account, and needs to have its token
// `amount` field updated.
Instruction_SyncNative
// Like InitializeAccount2, but does not require the Rent sysvar to be provided.
Instruction_InitializeAccount3
// Like InitializeMultisig, but does not require the Rent sysvar to be provided.
Instruction_InitializeMultisig2
// Like InitializeMint, but does not require the Rent sysvar to be provided.
Instruction_InitializeMint2
)
// InstructionIDToName returns the name of the instruction given its ID.
func InstructionIDToName(id uint8) string {
switch id {
case Instruction_InitializeMint:
return "InitializeMint"
case Instruction_InitializeAccount:
return "InitializeAccount"
case Instruction_InitializeMultisig:
return "InitializeMultisig"
case Instruction_Transfer:
return "Transfer"
case Instruction_Approve:
return "Approve"
case Instruction_Revoke:
return "Revoke"
case Instruction_SetAuthority:
return "SetAuthority"
case Instruction_MintTo:
return "MintTo"
case Instruction_Burn:
return "Burn"
case Instruction_CloseAccount:
return "CloseAccount"
case Instruction_FreezeAccount:
return "FreezeAccount"
case Instruction_ThawAccount:
return "ThawAccount"
case Instruction_TransferChecked:
return "TransferChecked"
case Instruction_ApproveChecked:
return "ApproveChecked"
case Instruction_MintToChecked:
return "MintToChecked"
case Instruction_BurnChecked:
return "BurnChecked"
case Instruction_InitializeAccount2:
return "InitializeAccount2"
case Instruction_SyncNative:
return "SyncNative"
case Instruction_InitializeAccount3:
return "InitializeAccount3"
case Instruction_InitializeMultisig2:
return "InitializeMultisig2"
case Instruction_InitializeMint2:
return "InitializeMint2"
default:
return ""
}
}
type Instruction struct {
ag_binary.BaseVariant
}
func (inst *Instruction) EncodeToTree(parent ag_treeout.Branches) {
if enToTree, ok := inst.Impl.(ag_text.EncodableToTree); ok {
enToTree.EncodeToTree(parent)
} else {
parent.Child(ag_spew.Sdump(inst))
}
}
var InstructionImplDef = ag_binary.NewVariantDefinition(
ag_binary.Uint8TypeIDEncoding,
[]ag_binary.VariantType{
{
"InitializeMint", (*InitializeMint)(nil),
},
{
"InitializeAccount", (*InitializeAccount)(nil),
},
{
"InitializeMultisig", (*InitializeMultisig)(nil),
},
{
"Transfer", (*Transfer)(nil),
},
{
"Approve", (*Approve)(nil),
},
{
"Revoke", (*Revoke)(nil),
},
{
"SetAuthority", (*SetAuthority)(nil),
},
{
"MintTo", (*MintTo)(nil),
},
{
"Burn", (*Burn)(nil),
},
{
"CloseAccount", (*CloseAccount)(nil),
},
{
"FreezeAccount", (*FreezeAccount)(nil),
},
{
"ThawAccount", (*ThawAccount)(nil),
},
{
"TransferChecked", (*TransferChecked)(nil),
},
{
"ApproveChecked", (*ApproveChecked)(nil),
},
{
"MintToChecked", (*MintToChecked)(nil),
},
{
"BurnChecked", (*BurnChecked)(nil),
},
{
"InitializeAccount2", (*InitializeAccount2)(nil),
},
{
"SyncNative", (*SyncNative)(nil),
},
{
"InitializeAccount3", (*InitializeAccount3)(nil),
},
{
"InitializeMultisig2", (*InitializeMultisig2)(nil),
},
{
"InitializeMint2", (*InitializeMint2)(nil),
},
},
)
func (inst *Instruction) ProgramID() ag_solanago.PublicKey {
return ProgramID
}
func (inst *Instruction) Accounts() (out []*ag_solanago.AccountMeta) {
return inst.Impl.(ag_solanago.AccountsGettable).GetAccounts()
}
func (inst *Instruction) Data() ([]byte, error) {
buf := new(bytes.Buffer)
if err := ag_binary.NewBinEncoder(buf).Encode(inst); err != nil {
return nil, fmt.Errorf("unable to encode instruction: %w", err)
}
return buf.Bytes(), nil
}
func (inst *Instruction) TextEncode(encoder *ag_text.Encoder, option *ag_text.Option) error {
return encoder.Encode(inst.Impl, option)
}
func (inst *Instruction) UnmarshalWithDecoder(decoder *ag_binary.Decoder) error {
return inst.BaseVariant.UnmarshalBinaryVariant(decoder, InstructionImplDef)
}
func (inst *Instruction) MarshalWithEncoder(encoder *ag_binary.Encoder) error {
err := encoder.WriteUint8(inst.TypeID.Uint8())
if err != nil {
return fmt.Errorf("unable to write variant type: %w", err)
}
return encoder.Encode(inst.Impl)
}
func registryDecodeInstruction(accounts []*ag_solanago.AccountMeta, data []byte) (interface{}, error) {
inst, err := DecodeInstruction(accounts, data)
if err != nil {
return nil, err
}
return inst, nil
}
func DecodeInstruction(accounts []*ag_solanago.AccountMeta, data []byte) (*Instruction, error) {
inst := new(Instruction)
if err := ag_binary.NewBinDecoder(data).Decode(inst); err != nil {
return nil, fmt.Errorf("unable to decode instruction: %w", err)
}
if v, ok := inst.Impl.(ag_solanago.AccountsSettable); ok {
err := v.SetAccounts(accounts)
if err != nil {
return nil, fmt.Errorf("unable to set accounts for instruction: %w", err)
}
}
return inst, nil
}