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 }