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  }