serum-js/src/layout.js

170 lines
4.1 KiB
JavaScript

// @ts-nocheck
import { bits, Blob, Layout, u32, UInt } from 'buffer-layout';
import { PublicKey } from '@solana/web3.js';
import BN from 'bn.js';
class Zeros extends Blob {
decode(b, offset) {
const slice = super.decode(b, offset);
if (!slice.every((v) => v === 0)) {
throw new Error('nonzero padding bytes');
}
return slice;
}
}
export function zeros(length) {
return new Zeros(length);
}
class PublicKeyLayout extends Blob {
constructor(property) {
super(32, property);
}
decode(b, offset) {
return new PublicKey(super.decode(b, offset));
}
encode(src, b, offset) {
return super.encode(src.toBuffer(), b, offset);
}
}
export function publicKeyLayout(property) {
return new PublicKeyLayout(property);
}
class BNLayout extends Blob {
decode(b, offset) {
return new BN(super.decode(b, offset), 10, 'le');
}
encode(src, b, offset) {
return super.encode(src.toArrayLike(Buffer, 'le', this.span), b, offset);
}
}
export function u64(property) {
return new BNLayout(8, property);
}
export function u128(property) {
return new BNLayout(16, property);
}
export class WideBits extends Layout {
constructor(property) {
super(8, property);
this._lower = bits(u32(), false);
this._upper = bits(u32(), false);
}
addBoolean(property) {
if (this._lower.fields.length < 32) {
this._lower.addBoolean(property);
} else {
this._upper.addBoolean(property);
}
}
decode(b, offset = 0) {
const lowerDecoded = this._lower.decode(b, offset);
const upperDecoded = this._upper.decode(b, offset + this._lower.span);
return { ...lowerDecoded, ...upperDecoded };
}
encode(src, b, offset = 0) {
return (
this._lower.encode(src, b, offset) +
this._upper.encode(src, b, offset + this._lower.span)
);
}
}
export class VersionedLayout extends Layout {
constructor(version, inner, property) {
super(inner.span > 0 ? inner.span + 1 : inner.span, property);
this.version = version;
this.inner = inner;
}
decode(b, offset = 0) {
// if (b.readUInt8(offset) !== this._version) {
// throw new Error('invalid version');
// }
return this.inner.decode(b, offset + 1);
}
encode(src, b, offset = 0) {
b.writeUInt8(this.version, offset);
return 1 + this.inner.encode(src, b, offset + 1);
}
getSpan(b, offset = 0) {
return 1 + this.inner.getSpan(b, offset + 1);
}
}
class EnumLayout extends UInt {
constructor(values, span, property) {
super(span, property);
this.values = values;
}
encode(src, b, offset) {
if (this.values[src] !== undefined) {
return super.encode(this.values[src], b, offset);
}
throw new Error('Invalid ' + this.property);
}
decode(b, offset) {
const decodedValue = super.decode(b, offset);
const entry = Object.entries(this.values).find(
([, value]) => value === decodedValue,
);
if (entry) {
return entry[0];
}
throw new Error('Invalid ' + this.property);
}
}
export function sideLayout(property) {
return new EnumLayout({ buy: 0, sell: 1 }, 4, property);
}
export function orderTypeLayout(property) {
return new EnumLayout({ limit: 0, ioc: 1, postOnly: 2 }, 4, property);
}
const ACCOUNT_FLAGS_LAYOUT = new WideBits();
ACCOUNT_FLAGS_LAYOUT.addBoolean('initialized');
ACCOUNT_FLAGS_LAYOUT.addBoolean('market');
ACCOUNT_FLAGS_LAYOUT.addBoolean('openOrders');
ACCOUNT_FLAGS_LAYOUT.addBoolean('requestQueue');
ACCOUNT_FLAGS_LAYOUT.addBoolean('eventQueue');
ACCOUNT_FLAGS_LAYOUT.addBoolean('bids');
ACCOUNT_FLAGS_LAYOUT.addBoolean('asks');
export function accountFlagsLayout(property = 'accountFlags') {
return ACCOUNT_FLAGS_LAYOUT.replicate(property);
}
export function setLayoutDecoder(layout, decoder) {
const originalDecode = layout.decode;
layout.decode = function decode(b, offset = 0) {
return decoder(originalDecode.call(this, b, offset));
};
}
export function setLayoutEncoder(layout, encoder) {
const originalEncode = layout.encode;
layout.encode = function encode(src, b, offset) {
return originalEncode.call(this, encoder(src), b, offset);
};
return layout;
}