Skip to content
markpaper

src/signing/appendix.test.ts

v0.2.0 · 3.4 KB

Download file
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);
  });
});
All files