src/signing/appendix.test.ts
v0.2.0 · 3.4 KB
import { describe, expect, it } from 'vitest';
import {
APPENDIX_VERSION,
buildAppendix,
EXPIRATION_NEVER,
expirationAtUnixSeconds,
isTakerType,
parseAppendix,
RESTING_ORDER_TYPES,
ReduceOnlyNotTakerError,
TAKER_ORDER_TYPES,
} from './appendix.js';
describe('buildAppendix', () => {
it('matches the live values: DEFAULT 1, IOC 513, IOC+RO 2561, POST_ONLY 1537', () => {
expect(buildAppendix('default')).toBe(1n);
expect(buildAppendix('ioc')).toBe(513n);
expect(buildAppendix('ioc', true)).toBe(2561n);
expect(buildAppendix('post_only')).toBe(1537n);
expect(buildAppendix('fok')).toBe(1025n);
expect(buildAppendix('fok', true)).toBe(3073n);
expect(APPENDIX_VERSION).toBe(1n);
});
it('refuses reduce-only on resting types at build time (venue code 2067)', () => {
expect(() => buildAppendix('default', true)).toThrow(ReduceOnlyNotTakerError);
expect(() => buildAppendix('post_only', true)).toThrow(/2067/);
try {
buildAppendix('post_only', true);
} catch (e) {
expect((e as ReduceOnlyNotTakerError).orderType).toBe('post_only');
}
expect(() => buildAppendix('market' as never)).toThrow(RangeError);
});
it('classifies taker and resting types', () => {
expect(TAKER_ORDER_TYPES).toEqual(['ioc', 'fok']);
expect(RESTING_ORDER_TYPES).toEqual(['default', 'post_only']);
expect(isTakerType('ioc')).toBe(true);
expect(isTakerType('post_only')).toBe(false);
});
});
describe('parseAppendix', () => {
it('decodes live values from bigint and string', () => {
expect(parseAppendix(1n)).toMatchObject({ version: 1, orderType: 'default', reduceOnly: false, isolated: false });
expect(parseAppendix('513')).toMatchObject({ orderType: 'ioc', reduceOnly: false });
expect(parseAppendix('2561')).toMatchObject({ orderType: 'ioc', reduceOnly: true });
expect(parseAppendix(1537n)).toMatchObject({ orderType: 'post_only', reduceOnly: false });
expect(parseAppendix(1025n)).toMatchObject({ orderType: 'fok' });
});
it('round-trips every order type / reduce-only combination the builder allows', () => {
for (const t of ['default', 'ioc', 'fok', 'post_only'] as const) {
expect(parseAppendix(buildAppendix(t)).orderType).toBe(t);
}
for (const t of TAKER_ORDER_TYPES)
expect(parseAppendix(buildAppendix(t, true))).toMatchObject({ orderType: t, reduceOnly: true });
});
it('exposes the documented-only fields', () => {
const isolated = 1n | (1n << 8n);
expect(parseAppendix(isolated).isolated).toBe(true);
const trigger = 1n | (2n << 12n);
expect(parseAppendix(trigger).trigger).toBe(2);
const value = 1n | (7n << 64n);
expect(parseAppendix(value).value).toBe(7n);
expect(() => parseAppendix(-1n)).toThrow(RangeError);
expect(() => parseAppendix('x')).toThrow(TypeError);
});
});
describe('expiration', () => {
it('EXPIRATION_NEVER is uint64 max as seen on live resting orders', () => {
expect(EXPIRATION_NEVER).toBe(18446744073709551615n);
expect(EXPIRATION_NEVER.toString()).toBe('18446744073709551615');
});
it('expirationAtUnixSeconds (experimental) validates its input', () => {
expect(expirationAtUnixSeconds(1780000000)).toBe(1780000000n);
expect(() => expirationAtUnixSeconds(0)).toThrow(RangeError);
expect(() => expirationAtUnixSeconds(1.5)).toThrow(RangeError);
expect(() => expirationAtUnixSeconds(2 ** 64)).toThrow(RangeError);
});
});