From fd950b41cc0ff58172e331de0f0761dabd64d1f7 Mon Sep 17 00:00:00 2001 From: Julien Cassis Date: Mon, 21 Dec 2020 09:59:46 -0500 Subject: [PATCH] Added new instructions --- cmd/slnc/cmd/get_spl_token.go | 2 +- programs/serum/instruction.go | 113 ++++++++++++++++++++++++++++- programs/serum/instruction_test.go | 68 ++++++++++++++--- programs/serum/market.go | 23 +----- programs/serum/queue.go | 19 ++++- programs/serum/types.go | 7 ++ rpc/ws/client_test.go | 2 +- 7 files changed, 198 insertions(+), 36 deletions(-) diff --git a/cmd/slnc/cmd/get_spl_token.go b/cmd/slnc/cmd/get_spl_token.go index 0ebb623..408a31a 100644 --- a/cmd/slnc/cmd/get_spl_token.go +++ b/cmd/slnc/cmd/get_spl_token.go @@ -65,7 +65,7 @@ var getSPLTokenCmd = &cobra.Command{ text.EncoderColorCyan.Print("Address: ") fmt.Println(keyedAcct.Pubkey.String()) - text.EncoderColorCyan.Print("Owner: ") + text.EncoderColorCyan.Print("OpenOrders: ") fmt.Println(keyedAcct.Account.Owner.String()) text.EncoderColorCyan.Print("Lamports: ") diff --git a/programs/serum/instruction.go b/programs/serum/instruction.go index 61028cd..2849813 100644 --- a/programs/serum/instruction.go +++ b/programs/serum/instruction.go @@ -76,6 +76,9 @@ var InstructionDefVariant = bin.NewVariantDefinition(bin.Uint32TypeIDEncoding, [ {"cancel_order", (*InstructionCancelOrder)(nil)}, {"settle_funds", (*InstructionSettleFunds)(nil)}, {"cancel_order_by_client_id", (*InstructionCancelOrderByClientId)(nil)}, + {"disable_market", (*InstructionDisableMarketAccounts)(nil)}, + {"sweep_fees", (*InstructionSweepFees)(nil)}, + {"new_order_v2", (*InstructionNewOrderV2)(nil)}, }) type InitializeMarketAccounts struct { @@ -115,7 +118,7 @@ type NewOrderAccounts struct { OpenOrders *solana.AccountMeta `text:"linear,notype"` RequestQueue *solana.AccountMeta `text:"linear,notype"` Payer *solana.AccountMeta `text:"linear,notype"` - Owner *solana.AccountMeta `text:"linear,notype"` + Owner *solana.AccountMeta `text:"linear,notype"` // The owner of the open orders, i.e. the trader CoinVault *solana.AccountMeta `text:"linear,notype"` PCVault *solana.AccountMeta `text:"linear,notype"` SPLTokenProgram *solana.AccountMeta `text:"linear,notype"` @@ -124,7 +127,7 @@ type NewOrderAccounts struct { } type InstructionNewOrder struct { - Side uint32 + Side Side LimitPrice uint64 MaxQuantity uint64 OrderType OrderType @@ -300,7 +303,7 @@ type CancelOrderByClientIdAccounts struct { type InstructionCancelOrderByClientId struct { ClientID uint64 - Accounts *CancelOrderByClientIdAccounts + Accounts *CancelOrderByClientIdAccounts `bin:"-"` } func (i *InstructionCancelOrderByClientId) SetAccounts(accounts []*solana.AccountMeta, instructionActIdx []uint8) error { @@ -316,3 +319,107 @@ func (i *InstructionCancelOrderByClientId) SetAccounts(accounts []*solana.Accoun return nil } + +type DisableMarketAccounts struct { + Market *solana.AccountMeta `text:"linear,notype"` + DisableAuthority *solana.AccountMeta `text:"linear,notype"` +} + +type InstructionDisableMarketAccounts struct { + Accounts *DisableMarketAccounts `bin:"-"` +} + +func (i *InstructionDisableMarketAccounts) SetAccounts(accounts []*solana.AccountMeta, instructionActIdx []uint8) error { + if len(instructionActIdx) < 2 { + return fmt.Errorf("insuficient account, Disable Market requires at-least 2 accounts not %d", len(accounts)) + } + + i.Accounts = &DisableMarketAccounts{ + Market: accounts[instructionActIdx[0]], + DisableAuthority: accounts[instructionActIdx[1]], + } + + return nil +} + +type SweepFeesAccounts struct { + Market *solana.AccountMeta `text:"linear,notype"` + PCVault *solana.AccountMeta `text:"linear,notype"` + FeeSweepingAuthority *solana.AccountMeta `text:"linear,notype"` + FeeReceivableAccount *solana.AccountMeta `text:"linear,notype"` + VaultSigner *solana.AccountMeta `text:"linear,notype"` + SPLTokenProgram *solana.AccountMeta `text:"linear,notype"` +} + +type InstructionSweepFees struct { + Accounts *SweepFeesAccounts `bin:"-"` +} + +func (i *InstructionSweepFees) SetAccounts(accounts []*solana.AccountMeta, instructionActIdx []uint8) error { + if len(instructionActIdx) < 6 { + return fmt.Errorf("insuficient account, Sweep Fees requires at-least 6 accounts not %d", len(accounts)) + } + + i.Accounts = &SweepFeesAccounts{ + Market: accounts[instructionActIdx[0]], + PCVault: accounts[instructionActIdx[1]], + FeeSweepingAuthority: accounts[instructionActIdx[2]], + FeeReceivableAccount: accounts[instructionActIdx[3]], + VaultSigner: accounts[instructionActIdx[4]], + SPLTokenProgram: accounts[instructionActIdx[5]], + } + + return nil +} + +type NewOrderV2Accounts struct { + Market *solana.AccountMeta `text:"linear,notype"` // the market + OpenOrders *solana.AccountMeta `text:"linear,notype"` // the OpenOrders account to use + RequestQueue *solana.AccountMeta `text:"linear,notype"` // the request queue + Payer *solana.AccountMeta `text:"linear,notype"` // the (coin or price currency) account paying for the order + Owner *solana.AccountMeta `text:"linear,notype"` // owner of the OpenOrders account + CoinVault *solana.AccountMeta `text:"linear,notype"` // coin vault + PCVault *solana.AccountMeta `text:"linear,notype"` // pc vault + SPLTokenProgram *solana.AccountMeta `text:"linear,notype"` // spl token program + RentSysvar *solana.AccountMeta `text:"linear,notype"` // the rent sysvar + FeeDiscount *solana.AccountMeta `text:"linear,notype"` // (optional) the (M)SRM account used for fee discounts +} + +type SelfTradeBehavior uint32 + +const ( + SelfTradeBehaviorDecrementTake = iota + SelfTradeBehaviorCancelProvide +) + +type InstructionNewOrderV2 struct { + Side Side + LimitPrice uint64 + MaxQuantity uint64 + OrderType OrderType + ClientID uint64 + SelfTradeBehavior SelfTradeBehavior + + Accounts *NewOrderV2Accounts `bin:"-"` +} + +func (i *InstructionNewOrderV2) SetAccounts(accounts []*solana.AccountMeta, instructionActIdx []uint8) error { + if len(instructionActIdx) < 10 { + return fmt.Errorf("insuficient account, New Order V2 requires at-least 10 accounts not %d", len(accounts)) + } + + i.Accounts = &NewOrderV2Accounts{ + Market: accounts[instructionActIdx[0]], + OpenOrders: accounts[instructionActIdx[1]], + RequestQueue: accounts[instructionActIdx[2]], + Payer: accounts[instructionActIdx[3]], + Owner: accounts[instructionActIdx[4]], + CoinVault: accounts[instructionActIdx[5]], + PCVault: accounts[instructionActIdx[6]], + SPLTokenProgram: accounts[instructionActIdx[7]], + RentSysvar: accounts[instructionActIdx[8]], + FeeDiscount: accounts[instructionActIdx[9]], + } + + return nil +} diff --git a/programs/serum/instruction_test.go b/programs/serum/instruction_test.go index a070765..d64d6e3 100644 --- a/programs/serum/instruction_test.go +++ b/programs/serum/instruction_test.go @@ -4,18 +4,68 @@ import ( "encoding/hex" "testing" - "github.com/stretchr/testify/assert" - bin "github.com/dfuse-io/binary" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestDecodeInstruction(t *testing.T) { - x := `00020000000500` - data, err := hex.DecodeString(x) - require.NoError(t, err) - var instruction *Instruction - err = bin.NewDecoder(data).Decode(&instruction) - require.NoError(t, err) - assert.Equal(t, instruction.Version, uint8(0)) + tests := []struct { + name string + hexData string + expectInstruction *Instruction + }{ + { + name: "New Order", + hexData: "000900000001000000b80600000000000010eb09000000000000000000168106e091da511601000000", + expectInstruction: &Instruction{ + BaseVariant: bin.BaseVariant{ + TypeID: 9, + Impl: &InstructionNewOrderV2{ + Side: SideAsk, + LimitPrice: 1720, + MaxQuantity: 650000, + OrderType: OrderTypeLimit, + ClientID: 1608306862011613462, + SelfTradeBehavior: SelfTradeBehaviorCancelProvide, + }, + }, + Version: 0, + }, + }, + { + name: "Match Order", + hexData: "0002000000ffff", + expectInstruction: &Instruction{ + BaseVariant: bin.BaseVariant{ + TypeID: 2, + Impl: &InstructionMatchOrder{ + Limit: 65535, + }, + }, + Version: 0, + }, + }, + { + name: "Settle Funds", + hexData: "0005000000", + expectInstruction: &Instruction{ + BaseVariant: bin.BaseVariant{ + TypeID: 5, + Impl: &InstructionSettleFunds{}, + }, + Version: 0, + }, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + data, err := hex.DecodeString(test.hexData) + require.NoError(t, err) + var instruction *Instruction + err = bin.NewDecoder(data).Decode(&instruction) + require.NoError(t, err) + assert.Equal(t, test.expectInstruction, instruction) + }) + } } diff --git a/programs/serum/market.go b/programs/serum/market.go index 1d9cb5f..70f8962 100644 --- a/programs/serum/market.go +++ b/programs/serum/market.go @@ -90,25 +90,10 @@ func (s SideLayout) getSide() SideLayoutType { return SideLayoutTypeUnknown } -type OrderType string +type OrderType uint32 const ( - OrderTypeUnknown OrderType = "UNKNOWN" - OrderTypeLimit OrderType = "LIMIT" - OrderTypeImmediateOrCancel OrderType = "IMMEDIATE_OR_CANCEL" - OrderTypePostOnly OrderType = "POST_ONLY" + OrderTypeLimit = OrderType(0) + OrderTypeImmediateOrCancel + OrderTypePostOnly ) - -type OrderTypeLayout uint32 - -func (o OrderTypeLayout) getOrderType() OrderType { - switch o { - case 0: - return OrderTypeLimit - case 1: - return OrderTypeImmediateOrCancel - case 2: - return OrderTypePostOnly - } - return OrderTypeUnknown -} diff --git a/programs/serum/queue.go b/programs/serum/queue.go index a45a20d..24bc192 100644 --- a/programs/serum/queue.go +++ b/programs/serum/queue.go @@ -55,11 +55,20 @@ type Request struct { Padding [4]byte `json:"-"` MaxCoinQtyOrCancelId bin.Uint64 NativePCQtyLocked bin.Uint64 - OrderId bin.Uint128 - Owner [4]bin.Uint64 + OrderID bin.Uint128 + OpenOrders [4]bin.Uint64 // this is the openOrder address ClientOrderID bin.Uint64 } +func (r *Request) Equal(other *Request) bool { + //return (r.OrderID.Hi == other.OrderID.Hi && r.OrderID.Lo == other.OrderID.Lo) && + // (r.MaxCoinQtyOrCancelId == other.MaxCoinQtyOrCancelId) && + // (r.NativePCQtyLocked == other.NativePCQtyLocked) + return (r.OrderID.Hi == other.OrderID.Hi && r.OrderID.Lo == other.OrderID.Lo) && + (r.MaxCoinQtyOrCancelId == other.MaxCoinQtyOrCancelId) && + (r.NativePCQtyLocked == other.NativePCQtyLocked) +} + type EventQueue struct { SerumPadding [5]byte `json:"-"` @@ -80,7 +89,7 @@ func (q *EventQueue) Decode(data []byte) error { type EventFlag uint8 const ( - EventFlagFill = RequestFlag(1 << iota) + EventFlagFill = EventFlag(1 << iota) EventFlagOut EventFlagBid EventFlagMaker @@ -106,6 +115,10 @@ type Event struct { ClientOrderID uint64 } +func (e *Event) Equal(other *Event) bool { + return e.OrderID.Hi == other.OrderID.Hi && e.OrderID.Lo == other.OrderID.Lo +} + func (e *Event) Side() EventSide { if Has(uint8(e.Flag), uint8(EventFlagBid)) { return EventSideBid diff --git a/programs/serum/types.go b/programs/serum/types.go index 9ee305f..74097e7 100644 --- a/programs/serum/types.go +++ b/programs/serum/types.go @@ -61,6 +61,13 @@ func (a *AccountFlag) String() string { return fmt.Sprintf("%s %s", status, account_type) } +type Side uint32 + +const ( + SideBid = iota + SideAsk +) + type MarketV2 struct { SerumPadding [5]byte `json:"-"` AccountFlags AccountFlag diff --git a/rpc/ws/client_test.go b/rpc/ws/client_test.go index ad0d658..64c68e5 100644 --- a/rpc/ws/client_test.go +++ b/rpc/ws/client_test.go @@ -45,7 +45,7 @@ func Test_AccountSubscribe(t *testing.T) { return } text.NewEncoder(os.Stdout).Encode(data, nil) - fmt.Println("Owner: ", data.(*AccountResult).Value.Account.Owner) + fmt.Println("OpenOrders: ", data.(*AccountResult).Value.Account.Owner) fmt.Println("data: ", data.(*AccountResult).Value.Account.Data) return