pyaver.slab

  1from dataclasses import dataclass
  2from typing import Iterable, List, NamedTuple, Optional
  3from construct import ListContainer
  4from solana.publickey import PublicKey
  5from .layouts import SLAB_LAYOUT, NodeType, CALLBACK_INFO_LEN
  6
  7class Callback(NamedTuple):
  8    user_market: PublicKey
  9    fee_tier: int
 10
 11class SlabHeader(NamedTuple):
 12    account_tag: int
 13    bump_index: int
 14    free_list_len: int
 15    free_list_head: int
 16    callback_memory_offset: int
 17    callback_free_list_len: int
 18    callback_free_list_head: int
 19    callback_bump_index: int
 20    root_node: int
 21    leaf_count: int
 22    market_address: PublicKey
 23
 24
 25# Used as dummy value for SlabNode#next.
 26NONE_NEXT = -1
 27
 28
 29# UninitializedNode, FreeNode and LastFreeNode all maps to this class.
 30@dataclass(frozen=True)
 31class SlabNode:
 32    is_initialized: bool
 33    next: int
 34
 35@dataclass(frozen=True)
 36class SlabLeafNode(SlabNode):
 37    key: int
 38    callback_info_pt: int
 39    base_quantity: int
 40    user_market: PublicKey
 41    fee_tier: int
 42
 43@dataclass(frozen=True)
 44class SlabInnerNode(SlabNode):
 45    prefix_len: int
 46    key: int
 47    children: List[int]
 48
 49
 50class Slab:
 51    """
 52    Slab object
 53
 54    A Slab is one side of the orderbook (bids or asks), which contains all of the open orders for side and outcome in a given market
 55    """
 56    def __init__(self, header: SlabHeader, nodes: List[SlabNode]): 
 57        self._header: SlabHeader = header
 58        self._nodes: List[SlabNode] = nodes
 59
 60    @staticmethod
 61    def __build(nodes: ListContainer, buffer: bytes) -> List[SlabNode]:
 62        res: List[SlabNode] = []
 63        for construct_node in nodes:
 64            node_type = construct_node.tag
 65            node = construct_node.node
 66            if node_type == NodeType.UNINTIALIZED:
 67                res.append(SlabNode(is_initialized=False, next=-1))
 68            elif node_type == NodeType.LEAF_NODE:
 69                res.append(
 70                    SlabLeafNode(
 71                        key = int.from_bytes(node.key, "little"),
 72                        callback_info_pt = node.callback_info_pt,
 73                        base_quantity = node.base_quantity,
 74                        is_initialized = True,
 75                        next = NONE_NEXT,
 76                        user_market = PublicKey(buffer[node.callback_info_pt:node.callback_info_pt+CALLBACK_INFO_LEN-1]),
 77                        fee_tier = int.from_bytes(buffer[node.callback_info_pt+CALLBACK_INFO_LEN-1:node.callback_info_pt+CALLBACK_INFO_LEN], "little"),
 78                    )
 79                )
 80            elif node_type == NodeType.INNER_NODE:
 81                res.append(
 82                    SlabInnerNode(
 83                        prefix_len = node.prefix_len,
 84                        key = int.from_bytes(node.key, "little"),
 85                        children = node.children,
 86                        is_initialized = True,
 87                        next = NONE_NEXT,
 88                    )
 89                )
 90            elif node_type == NodeType.FREE_NODE:
 91                res.append(
 92                    SlabNode(
 93                        is_initialized=True,
 94                        next=node.next
 95                    )
 96                )
 97            elif node_type == NodeType.LAST_FREE_NODE:
 98                res.append(
 99                    SlabNode(
100                        is_initialized=True,
101                        next=NONE_NEXT
102                    )
103                )
104            else:
105                raise RuntimeError("Unrecognized node type" + node.tag)
106        return res        
107
108    @staticmethod
109    def from_bytes(buffer: bytes):
110        """
111        Parses raw onchain data to Slab object
112
113        Args:
114            buffer (bytes): Raw bytes coming from onchain
115
116        Returns:
117            Slab: Slab object
118        """
119        parsed_slab = SLAB_LAYOUT.parse(buffer)
120        header = parsed_slab.header
121        nodes = parsed_slab.nodes
122        return Slab(
123            SlabHeader(
124                account_tag=header.account_tag,
125                bump_index=header.bump_index,
126                free_list_len=header.free_list_len,
127                free_list_head=header.free_list_head,
128                callback_memory_offset=header.callback_memory_offset,
129                callback_free_list_len=header.callback_free_list_len,
130                callback_free_list_head=header.callback_free_list_head,
131                callback_bump_index=header.callback_bump_index,
132                root_node=header.root_node,
133                leaf_count=header.leaf_count,
134                market_address=PublicKey(header.market_address),
135            ),
136            Slab.__build(nodes, buffer),
137        )
138
139    def get(self, search_key: int) -> Optional[SlabLeafNode]:
140        if self._header.leaf_count == 0:
141            return None
142        index: int = self._header.root_node
143        while True:
144            node: SlabNode = self._nodes[index]
145            if isinstance(node, SlabLeafNode):  # pylint: disable=no-else-return
146                return node if node.key == search_key else None
147            elif isinstance(node, SlabInnerNode):
148                if (node.key ^ search_key) >> (128 - node.prefix_len) != 0:
149                    return None
150                # Check if the n-th bit (start from the least significant, i.e. rightmost) of the key is set
151                index = node.children[(search_key >> (128 - node.prefix_len - 1)) & 1]
152            else:
153                raise RuntimeError("Should not go here! Node type not recognize.")
154
155    def __iter__(self) -> Iterable[SlabLeafNode]:
156        return self.items(False)
157
158    def items(self, descending=False) -> Iterable[SlabLeafNode]:
159        """
160        Depth first traversal of the Binary Tree of orders in the Slab
161
162        Args:
163            descending (bool, optional): Decides if the price should descending or not. Defaults to False.
164
165        Raises:
166            RuntimeError: Neither of leaf node or tree node!
167
168        Returns:
169            Iterable[SlabLeafNode]: SlabLeafNode object
170        """
171        if self._header.leaf_count == 0:
172            return
173        stack = [self._header.root_node]
174        while stack:
175            index = stack.pop()
176            node: SlabNode = self._nodes[index]
177            if isinstance(node, SlabLeafNode):
178                yield node
179            elif isinstance(node, SlabInnerNode):
180                if descending:
181                    stack.append(node.children[0])
182                    stack.append(node.children[1])
183                else:
184                    stack.append(node.children[1])
185                    stack.append(node.children[0])
186            else:
187                raise RuntimeError("Neither of leaf node or tree node!")
class Callback(typing.NamedTuple):
 8class Callback(NamedTuple):
 9    user_market: PublicKey
10    fee_tier: int

Callback(user_market, fee_tier)

Callback(user_market: solana.publickey.PublicKey, fee_tier: int)

Create new instance of Callback(user_market, fee_tier)

user_market: solana.publickey.PublicKey

Alias for field number 0

fee_tier: int

Alias for field number 1

Inherited Members
builtins.tuple
index
count
class SlabHeader(typing.NamedTuple):
12class SlabHeader(NamedTuple):
13    account_tag: int
14    bump_index: int
15    free_list_len: int
16    free_list_head: int
17    callback_memory_offset: int
18    callback_free_list_len: int
19    callback_free_list_head: int
20    callback_bump_index: int
21    root_node: int
22    leaf_count: int
23    market_address: PublicKey

SlabHeader(account_tag, bump_index, free_list_len, free_list_head, callback_memory_offset, callback_free_list_len, callback_free_list_head, callback_bump_index, root_node, leaf_count, market_address)

SlabHeader( account_tag: int, bump_index: int, free_list_len: int, free_list_head: int, callback_memory_offset: int, callback_free_list_len: int, callback_free_list_head: int, callback_bump_index: int, root_node: int, leaf_count: int, market_address: solana.publickey.PublicKey)

Create new instance of SlabHeader(account_tag, bump_index, free_list_len, free_list_head, callback_memory_offset, callback_free_list_len, callback_free_list_head, callback_bump_index, root_node, leaf_count, market_address)

account_tag: int

Alias for field number 0

bump_index: int

Alias for field number 1

free_list_len: int

Alias for field number 2

free_list_head: int

Alias for field number 3

callback_memory_offset: int

Alias for field number 4

callback_free_list_len: int

Alias for field number 5

callback_free_list_head: int

Alias for field number 6

callback_bump_index: int

Alias for field number 7

root_node: int

Alias for field number 8

leaf_count: int

Alias for field number 9

market_address: solana.publickey.PublicKey

Alias for field number 10

Inherited Members
builtins.tuple
index
count
@dataclass(frozen=True)
class SlabNode:
31@dataclass(frozen=True)
32class SlabNode:
33    is_initialized: bool
34    next: int

SlabNode(is_initialized: bool, next: int)

SlabNode(is_initialized: bool, next: int)
@dataclass(frozen=True)
class SlabLeafNode(SlabNode):
36@dataclass(frozen=True)
37class SlabLeafNode(SlabNode):
38    key: int
39    callback_info_pt: int
40    base_quantity: int
41    user_market: PublicKey
42    fee_tier: int

SlabLeafNode(is_initialized: bool, next: int, key: int, callback_info_pt: int, base_quantity: int, user_market: solana.publickey.PublicKey, fee_tier: int)

SlabLeafNode( is_initialized: bool, next: int, key: int, callback_info_pt: int, base_quantity: int, user_market: solana.publickey.PublicKey, fee_tier: int)
@dataclass(frozen=True)
class SlabInnerNode(SlabNode):
44@dataclass(frozen=True)
45class SlabInnerNode(SlabNode):
46    prefix_len: int
47    key: int
48    children: List[int]

SlabInnerNode(is_initialized: bool, next: int, prefix_len: int, key: int, children: List[int])

SlabInnerNode( is_initialized: bool, next: int, prefix_len: int, key: int, children: List[int])
class Slab:
 51class Slab:
 52    """
 53    Slab object
 54
 55    A Slab is one side of the orderbook (bids or asks), which contains all of the open orders for side and outcome in a given market
 56    """
 57    def __init__(self, header: SlabHeader, nodes: List[SlabNode]): 
 58        self._header: SlabHeader = header
 59        self._nodes: List[SlabNode] = nodes
 60
 61    @staticmethod
 62    def __build(nodes: ListContainer, buffer: bytes) -> List[SlabNode]:
 63        res: List[SlabNode] = []
 64        for construct_node in nodes:
 65            node_type = construct_node.tag
 66            node = construct_node.node
 67            if node_type == NodeType.UNINTIALIZED:
 68                res.append(SlabNode(is_initialized=False, next=-1))
 69            elif node_type == NodeType.LEAF_NODE:
 70                res.append(
 71                    SlabLeafNode(
 72                        key = int.from_bytes(node.key, "little"),
 73                        callback_info_pt = node.callback_info_pt,
 74                        base_quantity = node.base_quantity,
 75                        is_initialized = True,
 76                        next = NONE_NEXT,
 77                        user_market = PublicKey(buffer[node.callback_info_pt:node.callback_info_pt+CALLBACK_INFO_LEN-1]),
 78                        fee_tier = int.from_bytes(buffer[node.callback_info_pt+CALLBACK_INFO_LEN-1:node.callback_info_pt+CALLBACK_INFO_LEN], "little"),
 79                    )
 80                )
 81            elif node_type == NodeType.INNER_NODE:
 82                res.append(
 83                    SlabInnerNode(
 84                        prefix_len = node.prefix_len,
 85                        key = int.from_bytes(node.key, "little"),
 86                        children = node.children,
 87                        is_initialized = True,
 88                        next = NONE_NEXT,
 89                    )
 90                )
 91            elif node_type == NodeType.FREE_NODE:
 92                res.append(
 93                    SlabNode(
 94                        is_initialized=True,
 95                        next=node.next
 96                    )
 97                )
 98            elif node_type == NodeType.LAST_FREE_NODE:
 99                res.append(
100                    SlabNode(
101                        is_initialized=True,
102                        next=NONE_NEXT
103                    )
104                )
105            else:
106                raise RuntimeError("Unrecognized node type" + node.tag)
107        return res        
108
109    @staticmethod
110    def from_bytes(buffer: bytes):
111        """
112        Parses raw onchain data to Slab object
113
114        Args:
115            buffer (bytes): Raw bytes coming from onchain
116
117        Returns:
118            Slab: Slab object
119        """
120        parsed_slab = SLAB_LAYOUT.parse(buffer)
121        header = parsed_slab.header
122        nodes = parsed_slab.nodes
123        return Slab(
124            SlabHeader(
125                account_tag=header.account_tag,
126                bump_index=header.bump_index,
127                free_list_len=header.free_list_len,
128                free_list_head=header.free_list_head,
129                callback_memory_offset=header.callback_memory_offset,
130                callback_free_list_len=header.callback_free_list_len,
131                callback_free_list_head=header.callback_free_list_head,
132                callback_bump_index=header.callback_bump_index,
133                root_node=header.root_node,
134                leaf_count=header.leaf_count,
135                market_address=PublicKey(header.market_address),
136            ),
137            Slab.__build(nodes, buffer),
138        )
139
140    def get(self, search_key: int) -> Optional[SlabLeafNode]:
141        if self._header.leaf_count == 0:
142            return None
143        index: int = self._header.root_node
144        while True:
145            node: SlabNode = self._nodes[index]
146            if isinstance(node, SlabLeafNode):  # pylint: disable=no-else-return
147                return node if node.key == search_key else None
148            elif isinstance(node, SlabInnerNode):
149                if (node.key ^ search_key) >> (128 - node.prefix_len) != 0:
150                    return None
151                # Check if the n-th bit (start from the least significant, i.e. rightmost) of the key is set
152                index = node.children[(search_key >> (128 - node.prefix_len - 1)) & 1]
153            else:
154                raise RuntimeError("Should not go here! Node type not recognize.")
155
156    def __iter__(self) -> Iterable[SlabLeafNode]:
157        return self.items(False)
158
159    def items(self, descending=False) -> Iterable[SlabLeafNode]:
160        """
161        Depth first traversal of the Binary Tree of orders in the Slab
162
163        Args:
164            descending (bool, optional): Decides if the price should descending or not. Defaults to False.
165
166        Raises:
167            RuntimeError: Neither of leaf node or tree node!
168
169        Returns:
170            Iterable[SlabLeafNode]: SlabLeafNode object
171        """
172        if self._header.leaf_count == 0:
173            return
174        stack = [self._header.root_node]
175        while stack:
176            index = stack.pop()
177            node: SlabNode = self._nodes[index]
178            if isinstance(node, SlabLeafNode):
179                yield node
180            elif isinstance(node, SlabInnerNode):
181                if descending:
182                    stack.append(node.children[0])
183                    stack.append(node.children[1])
184                else:
185                    stack.append(node.children[1])
186                    stack.append(node.children[0])
187            else:
188                raise RuntimeError("Neither of leaf node or tree node!")

Slab object

A Slab is one side of the orderbook (bids or asks), which contains all of the open orders for side and outcome in a given market

Slab(header: pyaver.slab.SlabHeader, nodes: List[pyaver.slab.SlabNode])
57    def __init__(self, header: SlabHeader, nodes: List[SlabNode]): 
58        self._header: SlabHeader = header
59        self._nodes: List[SlabNode] = nodes
@staticmethod
def from_bytes(buffer: bytes)
109    @staticmethod
110    def from_bytes(buffer: bytes):
111        """
112        Parses raw onchain data to Slab object
113
114        Args:
115            buffer (bytes): Raw bytes coming from onchain
116
117        Returns:
118            Slab: Slab object
119        """
120        parsed_slab = SLAB_LAYOUT.parse(buffer)
121        header = parsed_slab.header
122        nodes = parsed_slab.nodes
123        return Slab(
124            SlabHeader(
125                account_tag=header.account_tag,
126                bump_index=header.bump_index,
127                free_list_len=header.free_list_len,
128                free_list_head=header.free_list_head,
129                callback_memory_offset=header.callback_memory_offset,
130                callback_free_list_len=header.callback_free_list_len,
131                callback_free_list_head=header.callback_free_list_head,
132                callback_bump_index=header.callback_bump_index,
133                root_node=header.root_node,
134                leaf_count=header.leaf_count,
135                market_address=PublicKey(header.market_address),
136            ),
137            Slab.__build(nodes, buffer),
138        )

Parses raw onchain data to Slab object

Args
  • buffer (bytes): Raw bytes coming from onchain
Returns

Slab: Slab object

def get(self, search_key: int) -> Optional[pyaver.slab.SlabLeafNode]:
140    def get(self, search_key: int) -> Optional[SlabLeafNode]:
141        if self._header.leaf_count == 0:
142            return None
143        index: int = self._header.root_node
144        while True:
145            node: SlabNode = self._nodes[index]
146            if isinstance(node, SlabLeafNode):  # pylint: disable=no-else-return
147                return node if node.key == search_key else None
148            elif isinstance(node, SlabInnerNode):
149                if (node.key ^ search_key) >> (128 - node.prefix_len) != 0:
150                    return None
151                # Check if the n-th bit (start from the least significant, i.e. rightmost) of the key is set
152                index = node.children[(search_key >> (128 - node.prefix_len - 1)) & 1]
153            else:
154                raise RuntimeError("Should not go here! Node type not recognize.")
def items(self, descending=False) -> Iterable[pyaver.slab.SlabLeafNode]:
159    def items(self, descending=False) -> Iterable[SlabLeafNode]:
160        """
161        Depth first traversal of the Binary Tree of orders in the Slab
162
163        Args:
164            descending (bool, optional): Decides if the price should descending or not. Defaults to False.
165
166        Raises:
167            RuntimeError: Neither of leaf node or tree node!
168
169        Returns:
170            Iterable[SlabLeafNode]: SlabLeafNode object
171        """
172        if self._header.leaf_count == 0:
173            return
174        stack = [self._header.root_node]
175        while stack:
176            index = stack.pop()
177            node: SlabNode = self._nodes[index]
178            if isinstance(node, SlabLeafNode):
179                yield node
180            elif isinstance(node, SlabInnerNode):
181                if descending:
182                    stack.append(node.children[0])
183                    stack.append(node.children[1])
184                else:
185                    stack.append(node.children[1])
186                    stack.append(node.children[0])
187            else:
188                raise RuntimeError("Neither of leaf node or tree node!")

Depth first traversal of the Binary Tree of orders in the Slab

Args
  • descending (bool, optional): Decides if the price should descending or not. Defaults to False.
Raises
  • RuntimeError: Neither of leaf node or tree node!
Returns

Iterable[SlabLeafNode]: SlabLeafNode object