github.com/gagliardetto/solana-go@v1.11.0/programs/serum/instruction.go (about)

     1  // Copyright 2021 github.com/gagliardetto
     2  // This file has been modified by github.com/gagliardetto
     3  //
     4  // Copyright 2020 dfuse Platform Inc.
     5  //
     6  // Licensed under the Apache License, Version 2.0 (the "License");
     7  // you may not use this file except in compliance with the License.
     8  // You may obtain a copy of the License at
     9  //
    10  //      http://www.apache.org/licenses/LICENSE-2.0
    11  //
    12  // Unless required by applicable law or agreed to in writing, software
    13  // distributed under the License is distributed on an "AS IS" BASIS,
    14  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15  // See the License for the specific language governing permissions and
    16  // limitations under the License.
    17  
    18  package serum
    19  
    20  import (
    21  	"encoding/binary"
    22  	"fmt"
    23  
    24  	bin "github.com/gagliardetto/binary"
    25  	"github.com/gagliardetto/solana-go"
    26  	"github.com/gagliardetto/solana-go/text"
    27  )
    28  
    29  func init() {
    30  	solana.RegisterInstructionDecoder(DEXProgramIDV2, registryDecodeInstruction)
    31  }
    32  
    33  func registryDecodeInstruction(accounts []*solana.AccountMeta, data []byte) (interface{}, error) {
    34  	inst, err := DecodeInstruction(accounts, data)
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  	return inst, nil
    39  }
    40  
    41  func DecodeInstruction(accounts []*solana.AccountMeta, data []byte) (*Instruction, error) {
    42  	// FIXME: can't we dedupe this in some ways? It's copied in all of the programs' folders.
    43  	var inst Instruction
    44  	if err := bin.NewBinDecoder(data).Decode(&inst); err != nil {
    45  		return nil, fmt.Errorf("unable to decode instruction for serum program: %w", err)
    46  	}
    47  
    48  	if v, ok := inst.Impl.(solana.AccountsSettable); ok {
    49  		err := v.SetAccounts(accounts)
    50  		if err != nil {
    51  			return nil, fmt.Errorf("unable to set accounts for instruction: %w", err)
    52  		}
    53  	}
    54  
    55  	return &inst, nil
    56  }
    57  
    58  var InstructionDefVariant = bin.NewVariantDefinition(bin.Uint32TypeIDEncoding, []bin.VariantType{
    59  	{Name: "initialize_market", Type: (*InstructionInitializeMarket)(nil)},
    60  	{Name: "new_order", Type: (*InstructionNewOrder)(nil)},
    61  	{Name: "match_orders", Type: (*InstructionMatchOrder)(nil)},
    62  	{Name: "consume_events", Type: (*InstructionConsumeEvents)(nil)},
    63  	{Name: "cancel_order", Type: (*InstructionCancelOrder)(nil)},
    64  	{Name: "settle_funds", Type: (*InstructionSettleFunds)(nil)},
    65  	{Name: "cancel_order_by_client_id", Type: (*InstructionCancelOrderByClientId)(nil)},
    66  	{Name: "disable_market", Type: (*InstructionDisableMarketAccounts)(nil)},
    67  	{Name: "sweep_fees", Type: (*InstructionSweepFees)(nil)},
    68  	{Name: "new_order_v2", Type: (*InstructionNewOrderV2)(nil)},
    69  
    70  	// Added in DEX V3
    71  	{Name: "new_order_v3", Type: (*InstructionNewOrderV3)(nil)},
    72  	{Name: "cancel_order_v2", Type: (*InstructionCancelOrderV2)(nil)},
    73  	{Name: "cancel_order_by_client_id_v2", Type: (*InstructionCancelOrderByClientIdV2)(nil)},
    74  	{Name: "send_take", Type: (*InstructionSendTake)(nil)},
    75  })
    76  
    77  type Instruction struct {
    78  	bin.BaseVariant
    79  	Version uint8
    80  }
    81  
    82  var _ bin.EncoderDecoder = &Instruction{}
    83  
    84  func (i *Instruction) TextEncode(encoder *text.Encoder, option *text.Option) error {
    85  	return encoder.Encode(i.Impl, option)
    86  }
    87  
    88  func (i *Instruction) UnmarshalWithDecoder(decoder *bin.Decoder) (err error) {
    89  	i.Version, err = decoder.ReadUint8()
    90  	if err != nil {
    91  		return fmt.Errorf("unable to read version: %w", err)
    92  	}
    93  	return i.BaseVariant.UnmarshalBinaryVariant(decoder, InstructionDefVariant)
    94  }
    95  
    96  func (i Instruction) MarshalWithEncoder(encoder *bin.Encoder) error {
    97  	err := encoder.WriteUint8(i.Version)
    98  	if err != nil {
    99  		return fmt.Errorf("unable to write instruction version: %w", err)
   100  	}
   101  
   102  	err = encoder.WriteUint32(i.TypeID.Uint32(), binary.LittleEndian)
   103  	if err != nil {
   104  		return fmt.Errorf("unable to write variant type: %w", err)
   105  	}
   106  	return encoder.Encode(i.Impl)
   107  }
   108  
   109  type InitializeMarketAccounts struct {
   110  	Market        *solana.AccountMeta `text:"linear,notype"`
   111  	SPLCoinToken  *solana.AccountMeta `text:"linear,notype"`
   112  	SPLPriceToken *solana.AccountMeta `text:"linear,notype"`
   113  	CoinMint      *solana.AccountMeta `text:"linear,notype"`
   114  	PriceMint     *solana.AccountMeta `text:"linear,notype"`
   115  }
   116  
   117  type InstructionInitializeMarket struct {
   118  	BaseLotSize        uint64
   119  	QuoteLotSize       uint64
   120  	FeeRateBps         uint16
   121  	VaultSignerNonce   uint64
   122  	QuoteDustThreshold uint64
   123  
   124  	Accounts *InitializeMarketAccounts `bin:"-"`
   125  }
   126  
   127  func (i *InstructionInitializeMarket) SetAccounts(accounts []*solana.AccountMeta) error {
   128  	if len(accounts) < 9 {
   129  		return fmt.Errorf("insufficient account, Initialize Market requires at-least 8 accounts not %d", len(accounts))
   130  	}
   131  	i.Accounts = &InitializeMarketAccounts{
   132  		Market:        accounts[0],
   133  		SPLCoinToken:  accounts[5],
   134  		SPLPriceToken: accounts[6],
   135  		CoinMint:      accounts[7],
   136  		PriceMint:     accounts[8],
   137  	}
   138  	return nil
   139  }
   140  
   141  type NewOrderAccounts struct {
   142  	Market             *solana.AccountMeta `text:"linear,notype"`
   143  	OpenOrders         *solana.AccountMeta `text:"linear,notype"`
   144  	RequestQueue       *solana.AccountMeta `text:"linear,notype"`
   145  	Payer              *solana.AccountMeta `text:"linear,notype"`
   146  	Owner              *solana.AccountMeta `text:"linear,notype"` // The owner of the open orders, i.e. the trader
   147  	CoinVault          *solana.AccountMeta `text:"linear,notype"`
   148  	PCVault            *solana.AccountMeta `text:"linear,notype"`
   149  	SPLTokenProgram    *solana.AccountMeta `text:"linear,notype"`
   150  	Rent               *solana.AccountMeta `text:"linear,notype"`
   151  	SRMDiscountAccount *solana.AccountMeta `text:"linear,notype"`
   152  }
   153  
   154  // InstructionNewOrder seems to be unused after DEX v3 (unconfirmed claim)
   155  type InstructionNewOrder struct {
   156  	Side        Side
   157  	LimitPrice  uint64
   158  	MaxQuantity uint64
   159  	OrderType   OrderType
   160  	ClientID    uint64
   161  
   162  	Accounts *NewOrderAccounts `bin:"-"`
   163  }
   164  
   165  func (i *InstructionNewOrder) SetAccounts(accounts []*solana.AccountMeta) error {
   166  	if len(accounts) < 9 {
   167  		return fmt.Errorf("insufficient account, New Order requires at-least 10 accounts not %d", len(accounts))
   168  	}
   169  	i.Accounts = &NewOrderAccounts{
   170  		Market:          accounts[0],
   171  		OpenOrders:      accounts[1],
   172  		RequestQueue:    accounts[2],
   173  		Payer:           accounts[3],
   174  		Owner:           accounts[4],
   175  		CoinVault:       accounts[5],
   176  		PCVault:         accounts[6],
   177  		SPLTokenProgram: accounts[7],
   178  		Rent:            accounts[8],
   179  	}
   180  
   181  	if len(accounts) >= 10 {
   182  		i.Accounts.SRMDiscountAccount = accounts[9]
   183  	}
   184  
   185  	return nil
   186  }
   187  
   188  type MatchOrderAccounts struct {
   189  	Market            *solana.AccountMeta `text:"linear,notype"`
   190  	RequestQueue      *solana.AccountMeta `text:"linear,notype"`
   191  	EventQueue        *solana.AccountMeta `text:"linear,notype"`
   192  	Bids              *solana.AccountMeta `text:"linear,notype"`
   193  	Asks              *solana.AccountMeta `text:"linear,notype"`
   194  	CoinFeeReceivable *solana.AccountMeta `text:"linear,notype"`
   195  	PCFeeReceivable   *solana.AccountMeta `text:"linear,notype"`
   196  }
   197  
   198  // InstructionMatchOrder seems to be unused after DEX v3 (unconfirmed claim)
   199  type InstructionMatchOrder struct {
   200  	Limit uint16
   201  
   202  	Accounts *MatchOrderAccounts `bin:"-"`
   203  }
   204  
   205  func (i *InstructionMatchOrder) SetAccounts(accounts []*solana.AccountMeta) error {
   206  	if len(accounts) < 7 {
   207  		return fmt.Errorf("insufficient account, Match Order requires at-least 7 accounts not %d\n", len(accounts))
   208  	}
   209  	i.Accounts = &MatchOrderAccounts{
   210  		Market:            accounts[0],
   211  		RequestQueue:      accounts[1],
   212  		EventQueue:        accounts[2],
   213  		Bids:              accounts[3],
   214  		Asks:              accounts[4],
   215  		CoinFeeReceivable: accounts[5],
   216  		PCFeeReceivable:   accounts[6],
   217  	}
   218  	return nil
   219  }
   220  
   221  type ConsumeEventsAccounts struct {
   222  	OpenOrders        []*solana.AccountMeta `text:"linear,notype"`
   223  	Market            *solana.AccountMeta   `text:"linear,notype"`
   224  	EventQueue        *solana.AccountMeta   `text:"linear,notype"`
   225  	CoinFeeReceivable *solana.AccountMeta   `text:"linear,notype"`
   226  	PCFeeReceivable   *solana.AccountMeta   `text:"linear,notype"`
   227  }
   228  
   229  type InstructionConsumeEvents struct {
   230  	Limit uint16
   231  
   232  	Accounts *ConsumeEventsAccounts `bin:"-"`
   233  }
   234  
   235  func (i *InstructionConsumeEvents) SetAccounts(accounts []*solana.AccountMeta) error {
   236  	l := len(accounts)
   237  	if l < 4 {
   238  		return fmt.Errorf("insufficient account, Consume Events requires at-least 4 accounts not %d", len(accounts))
   239  	}
   240  	i.Accounts = &ConsumeEventsAccounts{
   241  		Market:            accounts[l-4],
   242  		EventQueue:        accounts[l-3],
   243  		CoinFeeReceivable: accounts[l-2],
   244  		PCFeeReceivable:   accounts[l-1],
   245  	}
   246  
   247  	for idx := 0; idx < l-4; idx++ {
   248  		i.Accounts.OpenOrders = append(i.Accounts.OpenOrders, accounts[idx])
   249  	}
   250  
   251  	return nil
   252  }
   253  
   254  type CancelOrderAccounts struct {
   255  	Market       *solana.AccountMeta `text:"linear,notype"`
   256  	OpenOrders   *solana.AccountMeta `text:"linear,notype"`
   257  	RequestQueue *solana.AccountMeta `text:"linear,notype"`
   258  	Owner        *solana.AccountMeta `text:"linear,notype"`
   259  }
   260  
   261  // InstructionCancelOrder seems to be unused after DEX v3 (unconfirmed claim)
   262  type InstructionCancelOrder struct {
   263  	Side          Side
   264  	OrderID       bin.Uint128
   265  	OpenOrders    solana.PublicKey
   266  	OpenOrderSlot uint8
   267  
   268  	Accounts *CancelOrderAccounts `bin:"-"`
   269  }
   270  
   271  func (i *InstructionCancelOrder) SetAccounts(accounts []*solana.AccountMeta) error {
   272  	if len(accounts) < 4 {
   273  		return fmt.Errorf("insufficient account, Cancel Order requires at-least 4 accounts not %d\n", len(accounts))
   274  	}
   275  	i.Accounts = &CancelOrderAccounts{
   276  		Market:       accounts[0],
   277  		OpenOrders:   accounts[1],
   278  		RequestQueue: accounts[2],
   279  		Owner:        accounts[3],
   280  	}
   281  
   282  	return nil
   283  }
   284  
   285  type SettleFundsAccounts struct {
   286  	Market           *solana.AccountMeta `text:"linear,notype"`
   287  	OpenOrders       *solana.AccountMeta `text:"linear,notype"`
   288  	Owner            *solana.AccountMeta `text:"linear,notype"`
   289  	CoinVault        *solana.AccountMeta `text:"linear,notype"`
   290  	PCVault          *solana.AccountMeta `text:"linear,notype"`
   291  	CoinWallet       *solana.AccountMeta `text:"linear,notype"`
   292  	PCWallet         *solana.AccountMeta `text:"linear,notype"`
   293  	Signer           *solana.AccountMeta `text:"linear,notype"`
   294  	SPLTokenProgram  *solana.AccountMeta `text:"linear,notype"`
   295  	ReferrerPCWallet *solana.AccountMeta `text:"linear,notype"`
   296  }
   297  
   298  type InstructionSettleFunds struct {
   299  	Accounts *SettleFundsAccounts `bin:"-"`
   300  }
   301  
   302  func (i *InstructionSettleFunds) SetAccounts(accounts []*solana.AccountMeta) error {
   303  	if len(accounts) < 9 {
   304  		return fmt.Errorf("insufficient account, Settle Funds requires at-least 10 accounts not %d", len(accounts))
   305  	}
   306  	i.Accounts = &SettleFundsAccounts{
   307  		Market:          accounts[0],
   308  		OpenOrders:      accounts[1],
   309  		Owner:           accounts[2],
   310  		CoinVault:       accounts[3],
   311  		PCVault:         accounts[4],
   312  		CoinWallet:      accounts[5],
   313  		PCWallet:        accounts[6],
   314  		Signer:          accounts[7],
   315  		SPLTokenProgram: accounts[8],
   316  	}
   317  
   318  	if len(accounts) >= 10 {
   319  		i.Accounts.ReferrerPCWallet = accounts[9]
   320  	}
   321  
   322  	return nil
   323  }
   324  
   325  type CancelOrderByClientIdAccounts struct {
   326  	Market       *solana.AccountMeta `text:"linear,notype"`
   327  	OpenOrders   *solana.AccountMeta `text:"linear,notype"`
   328  	RequestQueue *solana.AccountMeta `text:"linear,notype"`
   329  	Owner        *solana.AccountMeta `text:"linear,notype"`
   330  }
   331  
   332  // InstructionCancelOrderByClientId seems to be unused after DEX v3 (unconfirmed claim)
   333  type InstructionCancelOrderByClientId struct {
   334  	ClientID uint64
   335  
   336  	Accounts *CancelOrderByClientIdAccounts `bin:"-"`
   337  }
   338  
   339  func (i *InstructionCancelOrderByClientId) SetAccounts(accounts []*solana.AccountMeta) error {
   340  	if len(accounts) < 4 {
   341  		return fmt.Errorf("insufficient account, Cancel Order By Client Id requires at-least 4 accounts not %d", len(accounts))
   342  	}
   343  	i.Accounts = &CancelOrderByClientIdAccounts{
   344  		Market:       accounts[0],
   345  		OpenOrders:   accounts[1],
   346  		RequestQueue: accounts[2],
   347  		Owner:        accounts[3],
   348  	}
   349  
   350  	return nil
   351  }
   352  
   353  type DisableMarketAccounts struct {
   354  	Market           *solana.AccountMeta `text:"linear,notype"`
   355  	DisableAuthority *solana.AccountMeta `text:"linear,notype"`
   356  }
   357  
   358  type InstructionDisableMarketAccounts struct {
   359  	Accounts *DisableMarketAccounts `bin:"-"`
   360  }
   361  
   362  func (i *InstructionDisableMarketAccounts) SetAccounts(accounts []*solana.AccountMeta) error {
   363  	if len(accounts) < 2 {
   364  		return fmt.Errorf("insufficient account, Disable Market requires at-least 2 accounts not %d", len(accounts))
   365  	}
   366  
   367  	i.Accounts = &DisableMarketAccounts{
   368  		Market:           accounts[0],
   369  		DisableAuthority: accounts[1],
   370  	}
   371  
   372  	return nil
   373  }
   374  
   375  type SweepFeesAccounts struct {
   376  	Market               *solana.AccountMeta `text:"linear,notype"`
   377  	PCVault              *solana.AccountMeta `text:"linear,notype"`
   378  	FeeSweepingAuthority *solana.AccountMeta `text:"linear,notype"`
   379  	FeeReceivableAccount *solana.AccountMeta `text:"linear,notype"`
   380  	VaultSigner          *solana.AccountMeta `text:"linear,notype"`
   381  	SPLTokenProgram      *solana.AccountMeta `text:"linear,notype"`
   382  }
   383  
   384  type InstructionSweepFees struct {
   385  	Accounts *SweepFeesAccounts `bin:"-"`
   386  }
   387  
   388  func (i *InstructionSweepFees) SetAccounts(accounts []*solana.AccountMeta) error {
   389  	if len(accounts) < 6 {
   390  		return fmt.Errorf("insufficient account, Sweep Fees requires at-least 6 accounts not %d", len(accounts))
   391  	}
   392  
   393  	i.Accounts = &SweepFeesAccounts{
   394  		Market:               accounts[0],
   395  		PCVault:              accounts[1],
   396  		FeeSweepingAuthority: accounts[2],
   397  		FeeReceivableAccount: accounts[3],
   398  		VaultSigner:          accounts[4],
   399  		SPLTokenProgram:      accounts[5],
   400  	}
   401  
   402  	return nil
   403  }
   404  
   405  type NewOrderV2Accounts struct {
   406  	Market          *solana.AccountMeta `text:"linear,notype"` // the market
   407  	OpenOrders      *solana.AccountMeta `text:"linear,notype"` // the OpenOrders account to use
   408  	RequestQueue    *solana.AccountMeta `text:"linear,notype"` // the request queue
   409  	Payer           *solana.AccountMeta `text:"linear,notype"` // the (coin or price currency) account paying for the order
   410  	Owner           *solana.AccountMeta `text:"linear,notype"` // owner of the OpenOrders account
   411  	CoinVault       *solana.AccountMeta `text:"linear,notype"` // coin vault
   412  	PCVault         *solana.AccountMeta `text:"linear,notype"` // pc vault
   413  	SPLTokenProgram *solana.AccountMeta `text:"linear,notype"` // spl token program
   414  	RentSysvar      *solana.AccountMeta `text:"linear,notype"` // the rent sysvar
   415  	FeeDiscount     *solana.AccountMeta `text:"linear,notype"` // (optional) the (M)SRM account used for fee discounts
   416  }
   417  
   418  type SelfTradeBehavior uint32
   419  
   420  const (
   421  	SelfTradeBehaviorDecrementTake = iota
   422  	SelfTradeBehaviorCancelProvide
   423  
   424  	// Added in DEX V3
   425  
   426  	SelfTradeBehaviorAbortTransaction
   427  )
   428  
   429  // InstructionNewOrderV2 seems to be unused after DEX v3 (unconfirmed claim)
   430  type InstructionNewOrderV2 struct {
   431  	Side              Side
   432  	LimitPrice        uint64
   433  	MaxQuantity       uint64
   434  	OrderType         OrderType
   435  	ClientID          uint64
   436  	SelfTradeBehavior SelfTradeBehavior
   437  
   438  	Accounts *NewOrderV2Accounts `bin:"-"`
   439  }
   440  
   441  func (i *InstructionNewOrderV2) SetAccounts(accounts []*solana.AccountMeta) error {
   442  	if len(accounts) < 9 {
   443  		return fmt.Errorf("insufficient account, New Order V2 requires at-least 9 accounts + 1 optional not %d", len(accounts))
   444  	}
   445  
   446  	i.Accounts = &NewOrderV2Accounts{
   447  		Market:          accounts[0],
   448  		OpenOrders:      accounts[1],
   449  		RequestQueue:    accounts[2],
   450  		Payer:           accounts[3],
   451  		Owner:           accounts[4],
   452  		CoinVault:       accounts[5],
   453  		PCVault:         accounts[6],
   454  		SPLTokenProgram: accounts[7],
   455  		RentSysvar:      accounts[8],
   456  	}
   457  
   458  	if len(accounts) == 10 {
   459  		i.Accounts.FeeDiscount = accounts[9]
   460  	}
   461  
   462  	return nil
   463  }
   464  
   465  // DEX V3 Support
   466  
   467  type NewOrderV3Accounts struct {
   468  	Market          *solana.AccountMeta `text:"linear,notype"` // the market
   469  	OpenOrders      *solana.AccountMeta `text:"linear,notype"` // the OpenOrders account to use
   470  	RequestQueue    *solana.AccountMeta `text:"linear,notype"` // the request queue
   471  	EventQueue      *solana.AccountMeta `text:"linear,notype"` // the event queue
   472  	Bidder          *solana.AccountMeta `text:"linear,notype"` // bids
   473  	Asker           *solana.AccountMeta `text:"linear,notype"` // asks
   474  	Payer           *solana.AccountMeta `text:"linear,notype"` // the (coin or price currency) account paying for the order
   475  	Owner           *solana.AccountMeta `text:"linear,notype"` // owner of the OpenOrders account
   476  	CoinVault       *solana.AccountMeta `text:"linear,notype"` // coin vault
   477  	PCVault         *solana.AccountMeta `text:"linear,notype"` // pc vault
   478  	SPLTokenProgram *solana.AccountMeta `text:"linear,notype"` // spl token program
   479  	RentSysvar      *solana.AccountMeta `text:"linear,notype"` // the rent sysvar
   480  	FeeDiscount     *solana.AccountMeta `text:"linear,notype"` // (optional) the (M)SRM account used for fee discounts
   481  }
   482  
   483  type InstructionNewOrderV3 struct {
   484  	Side                             Side
   485  	LimitPrice                       uint64
   486  	MaxCoinQuantity                  uint64
   487  	MaxNativePCQuantityIncludingFees uint64
   488  	SelfTradeBehavior                SelfTradeBehavior
   489  	OrderType                        OrderType
   490  	ClientOrderID                    uint64
   491  	Limit                            uint16
   492  
   493  	Accounts *NewOrderV3Accounts `bin:"-"`
   494  }
   495  
   496  func (i *InstructionNewOrderV3) SetAccounts(accounts []*solana.AccountMeta) error {
   497  	if len(accounts) < 13 {
   498  		return fmt.Errorf("insufficient account, New Order V3 requires at-least 13 accounts not %d", len(accounts))
   499  	}
   500  
   501  	i.Accounts = &NewOrderV3Accounts{
   502  		Market:          accounts[0],
   503  		OpenOrders:      accounts[1],
   504  		RequestQueue:    accounts[2],
   505  		EventQueue:      accounts[3],
   506  		Bidder:          accounts[4],
   507  		Asker:           accounts[5],
   508  		Payer:           accounts[6],
   509  		Owner:           accounts[7],
   510  		CoinVault:       accounts[8],
   511  		PCVault:         accounts[9],
   512  		SPLTokenProgram: accounts[10],
   513  		RentSysvar:      accounts[11],
   514  		FeeDiscount:     accounts[12],
   515  	}
   516  
   517  	return nil
   518  }
   519  
   520  type CancelOrderV2Accounts struct {
   521  	Market     *solana.AccountMeta `text:"linear,notype"` // 0. `[writable]` market
   522  	Bids       *solana.AccountMeta `text:"linear,notype"` // 1. `[writable]` bids
   523  	Asks       *solana.AccountMeta `text:"linear,notype"` // 2. `[writable]` asks
   524  	OpenOrders *solana.AccountMeta `text:"linear,notype"` // 3. `[writable]` OpenOrders
   525  	Owner      *solana.AccountMeta `text:"linear,notype"` // 4. `[signer]` the OpenOrders owner
   526  	EventQueue *solana.AccountMeta `text:"linear,notype"` // 5. `[writable]` event_q
   527  }
   528  
   529  type InstructionCancelOrderV2 struct {
   530  	Side    Side
   531  	OrderID bin.Uint128
   532  
   533  	Accounts *CancelOrderV2Accounts `bin:"-"`
   534  }
   535  
   536  func (i *InstructionCancelOrderV2) SetAccounts(accounts []*solana.AccountMeta) error {
   537  	if len(accounts) < 6 {
   538  		return fmt.Errorf("insufficient account, Cancel Order V2 requires at-least 6 accounts not %d", len(accounts))
   539  	}
   540  	i.Accounts = &CancelOrderV2Accounts{
   541  		Market:     accounts[0],
   542  		Bids:       accounts[1],
   543  		Asks:       accounts[2],
   544  		OpenOrders: accounts[3],
   545  		Owner:      accounts[4],
   546  		EventQueue: accounts[5],
   547  	}
   548  
   549  	return nil
   550  }
   551  
   552  type CancelOrderByClientIdV2Accounts struct {
   553  	Market     *solana.AccountMeta `text:"linear,notype"` // 0. `[writable]` market
   554  	Bids       *solana.AccountMeta `text:"linear,notype"` // 1. `[writable]` bids
   555  	Asks       *solana.AccountMeta `text:"linear,notype"` // 2. `[writable]` asks
   556  	OpenOrders *solana.AccountMeta `text:"linear,notype"` // 3. `[writable]` OpenOrders
   557  	Owner      *solana.AccountMeta `text:"linear,notype"` // 4. `[signer]` the OpenOrders owner
   558  	EventQueue *solana.AccountMeta `text:"linear,notype"` // 5. `[writable]` event_q
   559  }
   560  
   561  type InstructionCancelOrderByClientIdV2 struct {
   562  	ClientID uint64
   563  
   564  	Accounts *CancelOrderByClientIdV2Accounts `bin:"-"`
   565  }
   566  
   567  func (i *InstructionCancelOrderByClientIdV2) SetAccounts(accounts []*solana.AccountMeta) error {
   568  	if len(accounts) < 6 {
   569  		return fmt.Errorf("insufficient account, Cancel Order By Client Id V2 requires at-least 6 accounts not %d", len(accounts))
   570  	}
   571  	i.Accounts = &CancelOrderByClientIdV2Accounts{
   572  		Market:     accounts[0],
   573  		Bids:       accounts[1],
   574  		Asks:       accounts[2],
   575  		OpenOrders: accounts[3],
   576  		Owner:      accounts[4],
   577  		EventQueue: accounts[5],
   578  	}
   579  
   580  	return nil
   581  }
   582  
   583  // InstructionSendTakeAccounts defined from comment in serum-dex contract code, was never able to validate it's correct
   584  type InstructionSendTakeAccounts struct {
   585  	Market     *solana.AccountMeta `text:"linear,notype"` // 0. `[writable]` market
   586  	Bids       *solana.AccountMeta `text:"linear,notype"` // 1. `[writable]` bids
   587  	Asks       *solana.AccountMeta `text:"linear,notype"` // 2. `[writable]` asks
   588  	OpenOrders *solana.AccountMeta `text:"linear,notype"` // 3. `[writable]` OpenOrders
   589  	Owner      *solana.AccountMeta `text:"linear,notype"` // 4. `[]`
   590  }
   591  
   592  type InstructionSendTake struct {
   593  	Side                             Side
   594  	LimitPrice                       uint64
   595  	MaxCoinQuantity                  uint64
   596  	MaxNativePCQuantityIncludingFees uint64
   597  	MinCoinQuantity                  uint64
   598  	MinNativePCQuantity              uint64
   599  	Limit                            uint16
   600  
   601  	Accounts *InstructionSendTakeAccounts `bin:"-"`
   602  }
   603  
   604  func (i *InstructionSendTake) SetAccounts(accounts []*solana.AccountMeta) error {
   605  	if len(accounts) < 5 {
   606  		return fmt.Errorf("insufficient account, Send Take requires at-least 5 accounts not %d", len(accounts))
   607  	}
   608  	i.Accounts = &InstructionSendTakeAccounts{
   609  		Market:     accounts[0],
   610  		Bids:       accounts[1],
   611  		Asks:       accounts[2],
   612  		OpenOrders: accounts[3],
   613  		Owner:      accounts[4],
   614  	}
   615  	return nil
   616  }