2020-11-09 10:09:50 -08:00
|
|
|
// Copyright 2020 dfuse Platform Inc.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
2020-11-05 14:54:08 -08:00
|
|
|
package serum
|
|
|
|
|
2020-11-06 08:47:26 -08:00
|
|
|
import (
|
2020-11-23 08:12:02 -08:00
|
|
|
"encoding/binary"
|
2020-11-06 08:47:26 -08:00
|
|
|
"fmt"
|
2020-11-09 07:02:28 -08:00
|
|
|
"math/big"
|
|
|
|
|
2020-11-12 14:57:43 -08:00
|
|
|
bin "github.com/dfuse-io/binary"
|
2020-11-06 08:47:26 -08:00
|
|
|
"github.com/dfuse-io/solana-go"
|
2020-11-17 04:14:55 -08:00
|
|
|
"go.uber.org/zap"
|
2020-11-06 08:47:26 -08:00
|
|
|
)
|
2020-11-05 14:54:08 -08:00
|
|
|
|
2020-12-16 14:54:18 -08:00
|
|
|
type AccountFlag uint64
|
|
|
|
|
|
|
|
const (
|
|
|
|
AccountFlagInitialized = AccountFlag(1 << iota)
|
|
|
|
AccountFlagMarket
|
|
|
|
AccountFlagOpenOrders
|
|
|
|
AccountFlagRequestQueue
|
|
|
|
AccountFlagEventQueue
|
|
|
|
AccountFlagBids
|
|
|
|
AccountFlagAsks
|
|
|
|
AccountFlagDisabled
|
|
|
|
)
|
|
|
|
|
|
|
|
func (a *AccountFlag) Is(flag AccountFlag) bool { return *a&flag != 0 }
|
|
|
|
func (a *AccountFlag) String() string {
|
|
|
|
status := "unknown"
|
|
|
|
account_type := "unknown"
|
|
|
|
if a.Is(AccountFlagInitialized) {
|
|
|
|
status = "initialized"
|
|
|
|
} else if a.Is(AccountFlagDisabled) {
|
|
|
|
status = "disabled"
|
|
|
|
}
|
|
|
|
if a.Is(AccountFlagMarket) {
|
|
|
|
account_type = "market"
|
|
|
|
} else if a.Is(AccountFlagOpenOrders) {
|
|
|
|
account_type = "open orders"
|
|
|
|
} else if a.Is(AccountFlagRequestQueue) {
|
|
|
|
account_type = "request queue"
|
|
|
|
} else if a.Is(AccountFlagEventQueue) {
|
|
|
|
account_type = "event queue"
|
|
|
|
} else if a.Is(AccountFlagBids) {
|
|
|
|
account_type = "bids"
|
|
|
|
} else if a.Is(AccountFlagAsks) {
|
|
|
|
account_type = "asks"
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("%s %s", status, account_type)
|
|
|
|
}
|
|
|
|
|
2020-11-05 14:54:08 -08:00
|
|
|
type MarketV2 struct {
|
2020-11-13 06:52:02 -08:00
|
|
|
SerumPadding [5]byte `json:"-"`
|
2020-12-16 14:54:18 -08:00
|
|
|
AccountFlags AccountFlag
|
2020-11-13 06:52:02 -08:00
|
|
|
OwnAddress solana.PublicKey
|
|
|
|
VaultSignerNonce bin.Uint64
|
|
|
|
BaseMint solana.PublicKey
|
|
|
|
QuoteMint solana.PublicKey
|
|
|
|
BaseVault solana.PublicKey
|
|
|
|
BaseDepositsTotal bin.Uint64
|
|
|
|
BaseFeesAccrued bin.Uint64
|
|
|
|
QuoteVault solana.PublicKey
|
|
|
|
QuoteDepositsTotal bin.Uint64
|
|
|
|
QuoteFeesAccrued bin.Uint64
|
|
|
|
QuoteDustThreshold bin.Uint64
|
|
|
|
RequestQueue solana.PublicKey
|
|
|
|
EventQueue solana.PublicKey
|
|
|
|
Bids solana.PublicKey
|
|
|
|
Asks solana.PublicKey
|
|
|
|
BaseLotSize bin.Uint64
|
|
|
|
QuoteLotSize bin.Uint64
|
|
|
|
FeeRateBPS bin.Uint64
|
|
|
|
ReferrerRebatesAccrued bin.Uint64
|
|
|
|
EndPadding [7]byte `json:"-"`
|
2020-11-06 08:47:26 -08:00
|
|
|
}
|
2020-11-05 14:54:08 -08:00
|
|
|
|
2020-11-09 09:59:24 -08:00
|
|
|
func (m *MarketV2) Decode(in []byte) error {
|
2020-11-12 14:57:43 -08:00
|
|
|
decoder := bin.NewDecoder(in)
|
|
|
|
err := decoder.Decode(&m)
|
2020-11-09 09:59:24 -08:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unpack: %w", err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-11-06 08:47:26 -08:00
|
|
|
type Orderbook struct {
|
2020-11-12 14:57:43 -08:00
|
|
|
SerumPadding [5]byte `json:"-"`
|
2020-12-16 14:54:18 -08:00
|
|
|
AccountFlags AccountFlag
|
2020-11-12 14:57:43 -08:00
|
|
|
BumpIndex uint32 `bin:"sizeof=Nodes"`
|
|
|
|
ZeroPaddingA [4]byte `json:"-"`
|
|
|
|
FreeListLen uint32
|
|
|
|
ZeroPaddingB [4]byte `json:"-"`
|
|
|
|
FreeListHead uint32
|
|
|
|
Root uint32
|
|
|
|
LeafCount uint32
|
|
|
|
ZeroPaddingC [4]byte `json:"-"`
|
2020-11-13 06:52:02 -08:00
|
|
|
Nodes []*Slab
|
2020-11-11 05:39:35 -08:00
|
|
|
}
|
|
|
|
|
2020-11-06 13:31:18 -08:00
|
|
|
func (o *Orderbook) Items(descending bool, f func(node *SlabLeafNode) error) error {
|
2020-11-06 10:18:04 -08:00
|
|
|
if o.LeafCount == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-11-07 18:30:28 -08:00
|
|
|
index := uint32(0)
|
2020-11-06 10:18:04 -08:00
|
|
|
stack := []uint32{o.Root}
|
2020-11-07 18:30:28 -08:00
|
|
|
for len(stack) > 0 {
|
|
|
|
index, stack = stack[len(stack)-1], stack[:len(stack)-1]
|
2020-11-09 07:02:28 -08:00
|
|
|
if traceEnabled {
|
|
|
|
zlog.Debug("looking at slab index", zap.Int("index", int(index)))
|
|
|
|
}
|
2020-11-06 10:18:04 -08:00
|
|
|
slab := o.Nodes[index]
|
2020-11-12 14:57:43 -08:00
|
|
|
impl := slab.Impl
|
2020-11-06 13:14:55 -08:00
|
|
|
switch s := impl.(type) {
|
2020-11-06 13:31:18 -08:00
|
|
|
case *SlabInnerNode:
|
2020-11-06 10:18:04 -08:00
|
|
|
if descending {
|
|
|
|
stack = append(stack, s.Children[0], s.Children[1])
|
|
|
|
} else {
|
|
|
|
stack = append(stack, s.Children[1], s.Children[0])
|
|
|
|
}
|
2020-11-06 13:31:18 -08:00
|
|
|
case *SlabLeafNode:
|
2020-11-09 07:02:28 -08:00
|
|
|
if traceEnabled {
|
|
|
|
zlog.Debug("found leaf", zap.Int("leaf", int(index)))
|
|
|
|
}
|
2020-11-06 10:18:04 -08:00
|
|
|
f(s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2020-11-13 06:52:02 -08:00
|
|
|
|
|
|
|
var SlabFactoryImplDef = bin.NewVariantDefinition(bin.Uint32TypeIDEncoding, []bin.VariantType{
|
2020-12-15 15:07:11 -08:00
|
|
|
{Name: "uninitialized", Type: (*SlabUninitialized)(nil)},
|
|
|
|
{Name: "inner_node", Type: (*SlabInnerNode)(nil)},
|
|
|
|
{Name: "leaf_node", Type: (*SlabLeafNode)(nil)},
|
|
|
|
{Name: "free_node", Type: (*SlabFreeNode)(nil)},
|
|
|
|
{Name: "last_free_node", Type: (*SlabLastFreeNode)(nil)},
|
2020-11-13 06:52:02 -08:00
|
|
|
})
|
|
|
|
|
|
|
|
type Slab struct {
|
|
|
|
bin.BaseVariant
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Slab) UnmarshalBinary(decoder *bin.Decoder) error {
|
|
|
|
return s.BaseVariant.UnmarshalBinaryVariant(decoder, SlabFactoryImplDef)
|
|
|
|
}
|
|
|
|
func (s *Slab) MarshalBinary(encoder *bin.Encoder) error {
|
2020-11-23 08:12:02 -08:00
|
|
|
err := encoder.WriteUint32(s.TypeID, binary.LittleEndian)
|
2020-11-13 06:52:02 -08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return encoder.Encode(s.Impl)
|
|
|
|
}
|
|
|
|
|
|
|
|
type SlabUninitialized struct {
|
|
|
|
Padding [4]byte `json:"-"`
|
|
|
|
PaddingA [64]byte `json:"-"` // ensure variant is 68 bytes
|
|
|
|
}
|
|
|
|
|
|
|
|
type SlabInnerNode struct {
|
|
|
|
PrefixLen uint32
|
|
|
|
Key bin.Uint128
|
|
|
|
Children [2]uint32
|
|
|
|
Padding [40]byte `json:"-"` // ensure variant is 68 bytes
|
|
|
|
}
|
|
|
|
|
|
|
|
type SlabFreeNode struct {
|
|
|
|
Next uint32
|
|
|
|
Padding [64]byte `json:"-"` // ensure variant is 68 bytes
|
|
|
|
}
|
|
|
|
|
|
|
|
type SlabLastFreeNode struct {
|
|
|
|
Padding [4]byte `json:"-"`
|
|
|
|
PaddingA [64]byte `json:"-"` // ensure variant is 68 bytes
|
|
|
|
}
|
|
|
|
|
|
|
|
type SlabLeafNode struct {
|
|
|
|
OwnerSlot uint8
|
|
|
|
FeeTier uint8
|
|
|
|
Padding [2]byte `json:"-"`
|
|
|
|
Key bin.Uint128
|
|
|
|
Owner solana.PublicKey
|
|
|
|
Quantity bin.Uint64
|
|
|
|
ClientOrderId bin.Uint64
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *SlabLeafNode) GetPrice() *big.Int {
|
|
|
|
raw := s.Key.BigInt().Bytes()
|
|
|
|
if len(raw) <= 8 {
|
|
|
|
return big.NewInt(0)
|
|
|
|
}
|
|
|
|
v := new(big.Int).SetBytes(raw[0 : len(raw)-8])
|
|
|
|
return v
|
|
|
|
}
|
2020-11-17 04:14:55 -08:00
|
|
|
|
2020-11-20 10:15:27 -08:00
|
|
|
type OpenOrdersV2 struct {
|
|
|
|
SerumPadding [5]byte `json:"-"`
|
2020-12-16 14:54:18 -08:00
|
|
|
AccountFlags AccountFlag
|
2020-11-20 10:15:27 -08:00
|
|
|
Market solana.PublicKey
|
|
|
|
Owner solana.PublicKey
|
|
|
|
BaseTokenFree bin.Uint64
|
|
|
|
BaseTokenTotal bin.Uint64
|
|
|
|
QuoteTokenFree bin.Uint64
|
|
|
|
QuoteTokenTotal bin.Uint64
|
|
|
|
FreeSlotBits bin.Uint128
|
|
|
|
IsBidBits bin.Uint128
|
|
|
|
Orders [128]bin.Uint128
|
|
|
|
ClientIDs [128]bin.Uint64
|
|
|
|
ReferrerRebatesAccrued bin.Uint64
|
|
|
|
EndPadding [7]byte `json:"-"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *OpenOrdersV2) Decode(in []byte) error {
|
|
|
|
decoder := bin.NewDecoder(in)
|
|
|
|
err := decoder.Decode(&m)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unpack: %w", err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|