src/transport/weights.test.ts
v0.3.0 · 4.2 KB
import { describe, expect, it } from 'vitest';
import {
DEFAULT_INFO_WEIGHT,
EXCHANGE_RESERVE_WEIGHT,
INFO_WEIGHTS,
MAX_INFO_WEIGHT,
exchangeAddressRequests,
exchangeIpWeight,
responseWeightSurcharge,
weightOf,
} from './weights.js';
describe('weightOf', () => {
it('light requests weigh 2', () => {
for (const type of ['l2Book', 'allMids', 'clearinghouseState', 'spotClearinghouseState', 'orderStatus']) {
expect(weightOf({ type })).toBe(2);
}
});
it('userRole weighs 60', () => {
expect(weightOf({ type: 'userRole' })).toBe(60);
expect(MAX_INFO_WEIGHT).toBe(Math.max(...Object.values(INFO_WEIGHTS)));
});
it('openOrders / frontendOpenOrders / extraAgents / portfolio are 20, not 2 (known under-counting bug)', () => {
for (const type of ['openOrders', 'frontendOpenOrders', 'extraAgents', 'portfolio', 'userNonFundingLedgerUpdates']) {
expect(weightOf({ type })).toBe(20);
}
});
it('meta, userFills, candleSnapshot, maxBuilderFee, userRateLimit weigh 20', () => {
for (const type of ['meta', 'metaAndAssetCtxs', 'perpDexs', 'userFills', 'userFillsByTime', 'candleSnapshot', 'maxBuilderFee', 'userRateLimit', 'userFees']) {
expect(weightOf({ type })).toBe(20);
}
});
it('unknown and prototype-named types fall back to the conservative default', () => {
expect(DEFAULT_INFO_WEIGHT).toBe(20);
expect(weightOf({ type: 'someFutureType' })).toBe(20);
expect(weightOf({ type: 'constructor' })).toBe(20);
expect(weightOf({ type: '__proto__' })).toBe(20);
expect(weightOf({ type: 'toString' })).toBe(20);
});
it('table is frozen', () => {
expect(Object.isFrozen(INFO_WEIGHTS)).toBe(true);
});
});
describe('responseWeightSurcharge (experimental)', () => {
it('charges per started block of 20 items for fills-like responses', () => {
expect(responseWeightSurcharge('userFills', new Array(1))).toBe(1);
expect(responseWeightSurcharge('userFills', new Array(20))).toBe(1);
expect(responseWeightSurcharge('userFills', new Array(21))).toBe(2);
expect(responseWeightSurcharge('userFillsByTime', new Array(2000))).toBe(100);
expect(responseWeightSurcharge('historicalOrders', new Array(40))).toBe(2);
});
it('charges per 60 candles for candleSnapshot', () => {
expect(responseWeightSurcharge('candleSnapshot', new Array(60))).toBe(1);
expect(responseWeightSurcharge('candleSnapshot', new Array(5000))).toBe(84);
});
it('is 0 for empty, non-array responses and types without a rule', () => {
expect(responseWeightSurcharge('userFills', [])).toBe(0);
expect(responseWeightSurcharge('userFills', { fills: [1, 2] })).toBe(0);
expect(responseWeightSurcharge('meta', new Array(500))).toBe(0);
expect(responseWeightSurcharge('constructor', new Array(500))).toBe(0);
});
});
describe('exchange weights', () => {
it('IP weight of a batch is 1 + floor(n/40)', () => {
expect(exchangeIpWeight()).toBe(1);
expect(exchangeIpWeight(0)).toBe(1);
expect(exchangeIpWeight(1)).toBe(1);
expect(exchangeIpWeight(39)).toBe(1);
expect(exchangeIpWeight(40)).toBe(2);
expect(exchangeIpWeight(79)).toBe(2);
expect(exchangeIpWeight(80)).toBe(3);
expect(exchangeIpWeight(Number.NaN)).toBe(1);
});
it('address budget counts every order in a batch', () => {
expect(exchangeAddressRequests()).toBe(1);
expect(exchangeAddressRequests(2)).toBe(2); // TP/SL pair
expect(exchangeAddressRequests(40)).toBe(40);
expect(exchangeAddressRequests(-3)).toBe(0);
});
it('reserves 2 per exchange action', () => {
expect(EXCHANGE_RESERVE_WEIGHT).toBe(2);
});
});
describe('conservative weights for single-source light claims', () => {
it('userDexAbstraction is 20 (not in the documented weight-2 list)', () => {
expect(weightOf({ type: 'userDexAbstraction' })).toBe(20);
});
it('surcharge on live-sized responses: 5 218 candles -> 87, 10 recent trades -> 1, 2 000 fills -> 100', () => {
expect(responseWeightSurcharge('candleSnapshot', new Array(5218))).toBe(87);
expect(responseWeightSurcharge('recentTrades', new Array(10))).toBe(1);
expect(responseWeightSurcharge('userFillsByTime', new Array(2000))).toBe(100);
expect(responseWeightSurcharge('userFillsByTime', new Array(2001))).toBe(101);
});
});