github.com/gagliardetto/solana-go@v1.11.0/programs/serum/types.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  package serum
    18  
    19  import (
    20  	"encoding/binary"
    21  	"encoding/hex"
    22  	"fmt"
    23  	"math/big"
    24  
    25  	bin "github.com/gagliardetto/binary"
    26  	"github.com/gagliardetto/solana-go"
    27  	"go.uber.org/zap"
    28  )
    29  
    30  type AccountFlag uint64
    31  
    32  const (
    33  	AccountFlagInitialized = AccountFlag(1 << iota)
    34  	AccountFlagMarket
    35  	AccountFlagOpenOrders
    36  	AccountFlagRequestQueue
    37  	AccountFlagEventQueue
    38  	AccountFlagBids
    39  	AccountFlagAsks
    40  	AccountFlagDisabled
    41  )
    42  
    43  func (a *AccountFlag) Is(flag AccountFlag) bool { return *a&flag != 0 }
    44  func (a *AccountFlag) String() string {
    45  	status := "unknown"
    46  	account_type := "unknown"
    47  	if a.Is(AccountFlagInitialized) {
    48  		status = "initialized"
    49  	} else if a.Is(AccountFlagDisabled) {
    50  		status = "disabled"
    51  	}
    52  	if a.Is(AccountFlagMarket) {
    53  		account_type = "market"
    54  	} else if a.Is(AccountFlagOpenOrders) {
    55  		account_type = "open orders"
    56  	} else if a.Is(AccountFlagRequestQueue) {
    57  		account_type = "request queue"
    58  	} else if a.Is(AccountFlagEventQueue) {
    59  		account_type = "event queue"
    60  	} else if a.Is(AccountFlagBids) {
    61  		account_type = "bids"
    62  	} else if a.Is(AccountFlagAsks) {
    63  		account_type = "asks"
    64  	}
    65  	return fmt.Sprintf("%s %s", status, account_type)
    66  }
    67  
    68  type Side uint32
    69  
    70  const (
    71  	SideBid = iota
    72  	SideAsk
    73  )
    74  
    75  type MarketV2 struct {
    76  	SerumPadding           [5]byte `json:"-"`
    77  	AccountFlags           AccountFlag
    78  	OwnAddress             solana.PublicKey
    79  	VaultSignerNonce       bin.Uint64
    80  	BaseMint               solana.PublicKey
    81  	QuoteMint              solana.PublicKey
    82  	BaseVault              solana.PublicKey
    83  	BaseDepositsTotal      bin.Uint64
    84  	BaseFeesAccrued        bin.Uint64
    85  	QuoteVault             solana.PublicKey
    86  	QuoteDepositsTotal     bin.Uint64
    87  	QuoteFeesAccrued       bin.Uint64
    88  	QuoteDustThreshold     bin.Uint64
    89  	RequestQueue           solana.PublicKey
    90  	EventQueue             solana.PublicKey
    91  	Bids                   solana.PublicKey
    92  	Asks                   solana.PublicKey
    93  	BaseLotSize            bin.Uint64
    94  	QuoteLotSize           bin.Uint64
    95  	FeeRateBPS             bin.Uint64
    96  	ReferrerRebatesAccrued bin.Uint64
    97  	EndPadding             [7]byte `json:"-"`
    98  }
    99  
   100  func (m *MarketV2) Decode(in []byte) error {
   101  	decoder := bin.NewBinDecoder(in)
   102  	err := decoder.Decode(&m)
   103  	if err != nil {
   104  		return fmt.Errorf("unpack: %w", err)
   105  	}
   106  	return nil
   107  }
   108  
   109  type Orderbook struct {
   110  	SerumPadding [5]byte `json:"-"`
   111  	AccountFlags AccountFlag
   112  	BumpIndex    uint32  `bin:"sizeof=Nodes"`
   113  	ZeroPaddingA [4]byte `json:"-"`
   114  	FreeListLen  uint32
   115  	ZeroPaddingB [4]byte `json:"-"`
   116  	FreeListHead uint32
   117  	Root         uint32
   118  	LeafCount    uint32
   119  	ZeroPaddingC [4]byte `json:"-"`
   120  	Nodes        []*Slab
   121  }
   122  
   123  func (o *Orderbook) Items(descending bool, f func(node *SlabLeafNode) error) error {
   124  	if o.LeafCount == 0 {
   125  		return nil
   126  	}
   127  
   128  	index := uint32(0)
   129  	stack := []uint32{o.Root}
   130  	for len(stack) > 0 {
   131  		index, stack = stack[len(stack)-1], stack[:len(stack)-1]
   132  		if traceEnabled {
   133  			zlog.Debug("looking at slab index", zap.Int("index", int(index)))
   134  		}
   135  		slab := o.Nodes[index]
   136  		impl := slab.Impl
   137  		switch s := impl.(type) {
   138  		case *SlabInnerNode:
   139  			if descending {
   140  				stack = append(stack, s.Children[0], s.Children[1])
   141  			} else {
   142  				stack = append(stack, s.Children[1], s.Children[0])
   143  			}
   144  		case *SlabLeafNode:
   145  			if traceEnabled {
   146  				zlog.Debug("found leaf", zap.Int("leaf", int(index)))
   147  			}
   148  			if err := f(s); err != nil {
   149  				return err
   150  			}
   151  		}
   152  	}
   153  	return nil
   154  }
   155  
   156  var SlabFactoryImplDef = bin.NewVariantDefinition(bin.Uint32TypeIDEncoding, []bin.VariantType{
   157  	{Name: "uninitialized", Type: (*SlabUninitialized)(nil)},
   158  	{Name: "inner_node", Type: (*SlabInnerNode)(nil)},
   159  	{Name: "leaf_node", Type: (*SlabLeafNode)(nil)},
   160  	{Name: "free_node", Type: (*SlabFreeNode)(nil)},
   161  	{Name: "last_free_node", Type: (*SlabLastFreeNode)(nil)},
   162  })
   163  
   164  type Slab struct {
   165  	bin.BaseVariant
   166  }
   167  
   168  var _ bin.EncoderDecoder = &Slab{}
   169  
   170  func (s *Slab) UnmarshalWithDecoder(decoder *bin.Decoder) error {
   171  	return s.BaseVariant.UnmarshalBinaryVariant(decoder, SlabFactoryImplDef)
   172  }
   173  
   174  func (s Slab) MarshalWithEncoder(encoder *bin.Encoder) error {
   175  	err := encoder.WriteUint32(s.TypeID.Uint32(), binary.LittleEndian)
   176  	if err != nil {
   177  		return err
   178  	}
   179  	return encoder.Encode(s.Impl)
   180  }
   181  
   182  type SlabUninitialized struct {
   183  	Padding  [4]byte  `json:"-"`
   184  	PaddingA [64]byte `json:"-"` // ensure variant is 68 bytes
   185  }
   186  
   187  type SlabInnerNode struct {
   188  	PrefixLen uint32
   189  	Key       bin.Uint128
   190  	Children  [2]uint32
   191  	Padding   [40]byte `json:"-"` // ensure variant is 68 bytes
   192  }
   193  
   194  type SlabFreeNode struct {
   195  	Next    uint32
   196  	Padding [64]byte `json:"-"` // ensure variant is 68 bytes
   197  }
   198  
   199  type SlabLastFreeNode struct {
   200  	Padding  [4]byte  `json:"-"`
   201  	PaddingA [64]byte `json:"-"` // ensure variant is 68 bytes
   202  }
   203  
   204  type SlabLeafNode struct {
   205  	OwnerSlot     uint8
   206  	FeeTier       uint8
   207  	Padding       [2]byte `json:"-"`
   208  	Key           bin.Uint128
   209  	Owner         solana.PublicKey
   210  	Quantity      bin.Uint64
   211  	ClientOrderId bin.Uint64
   212  }
   213  
   214  func (s *SlabLeafNode) GetPrice() *big.Int {
   215  	raw := s.Key.BigInt().Bytes()
   216  	if len(raw) <= 8 {
   217  		return big.NewInt(0)
   218  	}
   219  	v := new(big.Int).SetBytes(raw[0 : len(raw)-8])
   220  	return v
   221  }
   222  
   223  type OrderID bin.Uint128
   224  
   225  func NewOrderID(orderID string) (OrderID, error) {
   226  	d, err := hex.DecodeString(orderID)
   227  	if err != nil {
   228  		return OrderID(bin.Uint128{
   229  			Lo: 0,
   230  			Hi: 0,
   231  		}), fmt.Errorf("unable to decode order ID: %w", err)
   232  	}
   233  
   234  	if len(d) < 16 {
   235  		return OrderID(bin.Uint128{
   236  			Lo: 0,
   237  			Hi: 0,
   238  		}), fmt.Errorf("order ID too short expecting at least 16 bytes got %d", len(d))
   239  	}
   240  
   241  	return OrderID(bin.Uint128{
   242  		Lo: binary.BigEndian.Uint64(d[8:]),
   243  		Hi: binary.BigEndian.Uint64(d[:8]),
   244  	}), nil
   245  }
   246  
   247  func (o OrderID) Price() uint64 {
   248  	return o.Hi
   249  }
   250  
   251  func (o OrderID) HexString(withPrefix bool) string {
   252  	number := make([]byte, 16)
   253  	binary.BigEndian.PutUint64(number[:], o.Hi)  // old price
   254  	binary.BigEndian.PutUint64(number[8:], o.Lo) // old seq_number
   255  	str := hex.EncodeToString(number)
   256  	if withPrefix {
   257  		return "0x" + str
   258  	}
   259  	return str
   260  }
   261  
   262  func (o OrderID) SeqNum(side Side) uint64 {
   263  	if side == SideBid {
   264  		return ^o.Lo
   265  	}
   266  
   267  	return o.Lo
   268  }
   269  
   270  type OpenOrders struct {
   271  	SerumPadding           [5]byte `json:"-"`
   272  	AccountFlags           AccountFlag
   273  	Market                 solana.PublicKey
   274  	Owner                  solana.PublicKey
   275  	NativeBaseTokenFree    bin.Uint64
   276  	NativeBaseTokenTotal   bin.Uint64
   277  	NativeQuoteTokenFree   bin.Uint64
   278  	NativeQuoteTokenTotal  bin.Uint64
   279  	FreeSlotBits           bin.Uint128
   280  	IsBidBits              bin.Uint128 // Bids is 1,  Ask is 0
   281  	Orders                 [128]OrderID
   282  	ClientOrderIDs         [128]bin.Uint64
   283  	ReferrerRebatesAccrued bin.Uint64
   284  	EndPadding             [7]byte `json:"-"`
   285  }
   286  
   287  type Order struct {
   288  	ID   OrderID
   289  	Side Side
   290  }
   291  
   292  func (o *Order) SeqNum() uint64 {
   293  	return o.ID.SeqNum(o.Side)
   294  }
   295  
   296  func (o *Order) Price() uint64 {
   297  	return o.ID.Price()
   298  }
   299  
   300  func (o *OpenOrders) GetOrder(index uint32) *Order {
   301  	order := &Order{
   302  		ID:   o.Orders[index],
   303  		Side: SideBid,
   304  	}
   305  	isZero, err := IsBitZero(o.IsBidBits, index)
   306  	if err != nil {
   307  		panic("this should never happen")
   308  	}
   309  	if isZero {
   310  		order.Side = SideAsk
   311  	}
   312  	return order
   313  }
   314  
   315  func (o *OpenOrders) Decode(in []byte) error {
   316  	decoder := bin.NewBinDecoder(in)
   317  	err := decoder.Decode(&o)
   318  	if err != nil {
   319  		return fmt.Errorf("unpack: %w", err)
   320  	}
   321  	return nil
   322  }
   323  
   324  func IsBitZero(v bin.Uint128, bitIndex uint32) (bool, error) {
   325  	if bitIndex > 127 {
   326  		return false, fmt.Errorf("bit index out of range")
   327  	}
   328  
   329  	if bitIndex > 63 {
   330  		bitIndex = bitIndex - 64
   331  		mask := uint64(1 << bitIndex)
   332  		return (v.Hi&mask == 0), nil
   333  	}
   334  	mask := uint64(1 << bitIndex)
   335  	return (v.Lo&mask == 0), nil
   336  }