158 lines
3.5 KiB
TypeScript
158 lines
3.5 KiB
TypeScript
import { bits, blob, struct, u32, u8 } from 'buffer-layout';
|
|
import {
|
|
accountFlagsLayout,
|
|
publicKeyLayout,
|
|
u128,
|
|
u64,
|
|
zeros,
|
|
} from './layout';
|
|
import BN from 'bn.js';
|
|
import { PublicKey } from '@solana/web3.js';
|
|
|
|
const REQUEST_QUEUE_HEADER = struct([
|
|
blob(5),
|
|
|
|
accountFlagsLayout('accountFlags'),
|
|
u32('head'),
|
|
zeros(4),
|
|
u32('count'),
|
|
zeros(4),
|
|
u32('nextSeqNum'),
|
|
zeros(4),
|
|
]);
|
|
|
|
const REQUEST_FLAGS = bits(u8(), false, 'requestFlags');
|
|
REQUEST_FLAGS.addBoolean('newOrder');
|
|
REQUEST_FLAGS.addBoolean('cancelOrder');
|
|
REQUEST_FLAGS.addBoolean('bid');
|
|
REQUEST_FLAGS.addBoolean('postOnly');
|
|
REQUEST_FLAGS.addBoolean('ioc');
|
|
|
|
const REQUEST = struct([
|
|
REQUEST_FLAGS,
|
|
u8('openOrdersSlot'),
|
|
u8('feeTier'),
|
|
blob(5),
|
|
u64('maxBaseSizeOrCancelId'),
|
|
u64('nativeQuoteQuantityLocked'),
|
|
u128('orderId'),
|
|
publicKeyLayout('openOrders'),
|
|
u64('clientOrderId'),
|
|
]);
|
|
|
|
const EVENT_QUEUE_HEADER = struct([
|
|
blob(5),
|
|
|
|
accountFlagsLayout('accountFlags'),
|
|
u32('head'),
|
|
zeros(4),
|
|
u32('count'),
|
|
zeros(4),
|
|
u32('seqNum'),
|
|
zeros(4),
|
|
]);
|
|
|
|
const EVENT_FLAGS = bits(u8(), false, 'eventFlags');
|
|
EVENT_FLAGS.addBoolean('fill');
|
|
EVENT_FLAGS.addBoolean('out');
|
|
EVENT_FLAGS.addBoolean('bid');
|
|
EVENT_FLAGS.addBoolean('maker');
|
|
|
|
const EVENT = struct([
|
|
EVENT_FLAGS,
|
|
u8('openOrdersSlot'),
|
|
u8('feeTier'),
|
|
blob(5),
|
|
u64('nativeQuantityReleased'), // Amount the user received
|
|
u64('nativeQuantityPaid'), // Amount the user paid
|
|
u64('nativeFeeOrRebate'),
|
|
u128('orderId'),
|
|
publicKeyLayout('openOrders'),
|
|
u64('clientOrderId'),
|
|
]);
|
|
|
|
export interface Event {
|
|
eventFlags: { fill: boolean; out: boolean; bid: boolean; maker: boolean };
|
|
|
|
orderId: BN;
|
|
openOrders: PublicKey;
|
|
openOrdersSlot: number;
|
|
feeTier: number;
|
|
|
|
nativeQuantityReleased: BN;
|
|
nativeQuantityPaid: BN;
|
|
nativeFeeOrRebate: BN;
|
|
}
|
|
|
|
function decodeQueue(
|
|
headerLayout,
|
|
nodeLayout,
|
|
buffer: Buffer,
|
|
history?: number,
|
|
) {
|
|
const header = headerLayout.decode(buffer);
|
|
const allocLen = Math.floor(
|
|
(buffer.length - headerLayout.span) / nodeLayout.span,
|
|
);
|
|
const nodes: any[] = [];
|
|
if (history) {
|
|
for (let i = 0; i < Math.min(history, allocLen); ++i) {
|
|
const nodeIndex =
|
|
(header.head + header.count + allocLen - 1 - i) % allocLen;
|
|
nodes.push(
|
|
nodeLayout.decode(
|
|
buffer,
|
|
headerLayout.span + nodeIndex * nodeLayout.span,
|
|
),
|
|
);
|
|
}
|
|
} else {
|
|
for (let i = 0; i < header.count; ++i) {
|
|
const nodeIndex = (header.head + i) % allocLen;
|
|
nodes.push(
|
|
nodeLayout.decode(
|
|
buffer,
|
|
headerLayout.span + nodeIndex * nodeLayout.span,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
return { header, nodes };
|
|
}
|
|
|
|
export function decodeRequestQueue(buffer: Buffer, history?: number) {
|
|
const { header, nodes } = decodeQueue(
|
|
REQUEST_QUEUE_HEADER,
|
|
REQUEST,
|
|
buffer,
|
|
history,
|
|
);
|
|
if (!header.accountFlags.initialized || !header.accountFlags.requestQueue) {
|
|
throw new Error('Invalid requests queue');
|
|
}
|
|
return nodes;
|
|
}
|
|
|
|
export function decodeEventQueue(buffer: Buffer, history?: number): Event[] {
|
|
const { header, nodes } = decodeQueue(
|
|
EVENT_QUEUE_HEADER,
|
|
EVENT,
|
|
buffer,
|
|
history,
|
|
);
|
|
if (!header.accountFlags.initialized || !header.accountFlags.eventQueue) {
|
|
throw new Error('Invalid events queue');
|
|
}
|
|
return nodes;
|
|
}
|
|
|
|
export const REQUEST_QUEUE_LAYOUT = {
|
|
HEADER: REQUEST_QUEUE_HEADER,
|
|
NODE: REQUEST,
|
|
};
|
|
|
|
export const EVENT_QUEUE_LAYOUT = {
|
|
HEADER: EVENT_QUEUE_HEADER,
|
|
NODE: EVENT,
|
|
};
|