diff --git a/src/exchanges/lighter/decimal.ts b/src/exchanges/lighter/decimal.ts index a8c0654..adb3f94 100644 --- a/src/exchanges/lighter/decimal.ts +++ b/src/exchanges/lighter/decimal.ts @@ -46,6 +46,18 @@ export function decimalToScaled(value: number | string | bigint, decimals: numbe return sign === -1 ? -result : result; } +export function scaleQuantityWithMinimum(value: number | string | bigint, decimals: number): bigint { + const scaled = decimalToScaled(value, decimals); + if (scaled !== 0n) { + return scaled; + } + const numeric = typeof value === "bigint" ? Number(value) : Number(value); + if (!Number.isFinite(numeric) || numeric === 0) { + return scaled; + } + return numeric < 0 ? -1n : 1n; +} + export function scaledToDecimalString(value: bigint | number | string, decimals: number): string { if (typeof value === "number") { if (!Number.isFinite(value)) throw new Error(`Invalid scaled number: ${value}`); diff --git a/src/exchanges/lighter/gateway.ts b/src/exchanges/lighter/gateway.ts index 48c90e8..7d3e9ca 100644 --- a/src/exchanges/lighter/gateway.ts +++ b/src/exchanges/lighter/gateway.ts @@ -33,7 +33,7 @@ import { IMMEDIATE_OR_CANCEL_EXPIRY_PLACEHOLDER, type LighterEnvironment, } from "./constants"; -import { decimalToScaled, scaledToDecimalString } from "./decimal"; +import { decimalToScaled, scaledToDecimalString, scaleQuantityWithMinimum } from "./decimal"; import { lighterOrderToAster, toAccountSnapshot, toDepth, toKlines, toOrders, toTicker } from "./mappers"; interface SimpleEvent { @@ -888,7 +888,7 @@ export class LighterGateway { } const side = params.side; const isAsk = side === "SELL" ? 1 : 0; - const baseAmount = decimalToScaled(params.quantity, this.sizeDecimals); + const baseAmount = scaleQuantityWithMinimum(params.quantity, this.sizeDecimals); const baseAmountScaledString = scaledToDecimalString(baseAmount, this.sizeDecimals); const clientOrderIndex = BigInt(Date.now() % Number.MAX_SAFE_INTEGER); let priceScaled = params.price != null ? decimalToScaled(params.price, this.priceDecimals) : null; diff --git a/tests/lighter/decimal.test.ts b/tests/lighter/decimal.test.ts new file mode 100644 index 0000000..3deb1de --- /dev/null +++ b/tests/lighter/decimal.test.ts @@ -0,0 +1,27 @@ +import { describe, expect, it } from "vitest"; + +import { scaleQuantityWithMinimum, scaledToDecimalString, decimalToScaled } from "../../src/exchanges/lighter/decimal"; + +describe("scaleQuantityWithMinimum", () => { + it("raises tiny positive quantities to the minimum step", () => { + const scaled = scaleQuantityWithMinimum(0.00001, 4); + expect(scaled).toBe(1n); + expect(scaledToDecimalString(scaled, 4)).toBe("0.0001"); + }); + + it("raises tiny negative quantities to the negative minimum step", () => { + const scaled = scaleQuantityWithMinimum(-0.00001, 4); + expect(scaled).toBe(-1n); + expect(scaledToDecimalString(scaled, 4)).toBe("-0.0001"); + }); + + it("keeps zero quantities at zero", () => { + const scaled = scaleQuantityWithMinimum(0, 4); + expect(scaled).toBe(0n); + }); + + it("preserves values already aligned to the step", () => { + const aligned = scaleQuantityWithMinimum(0.0002, 4); + expect(aligned).toBe(decimalToScaled(0.0002, 4)); + }); +});