From 223a9d501c0e624f3986a547ace3adf621c00896 Mon Sep 17 00:00:00 2001 From: crazywriter1 Date: Wed, 19 Aug 2026 14:30:24 +0300 Subject: [PATCH] fix: reject --oid values above 2^53-1 on perp cancel Large Hyperliquid uint64 order IDs are silently rounded by JS Number, which could cancel the wrong order. Refuse at input for the same reason the response path withholds unsafe oids. --- .changeset/perp-cancel-oid-safe-integer.md | 5 +++++ src/__tests__/perp.test.js | 15 +++++++++++++++ src/perp.js | 6 ++++++ 3 files changed, 26 insertions(+) create mode 100644 .changeset/perp-cancel-oid-safe-integer.md diff --git a/.changeset/perp-cancel-oid-safe-integer.md b/.changeset/perp-cancel-oid-safe-integer.md new file mode 100644 index 00000000..53a6756b --- /dev/null +++ b/.changeset/perp-cancel-oid-safe-integer.md @@ -0,0 +1,5 @@ +--- +"nansen-cli": patch +--- + +Reject `--oid` values above 2^53-1 on `perp cancel`: large Hyperliquid uint64 order IDs would be silently rounded by JS Number, potentially cancelling the wrong order. diff --git a/src/__tests__/perp.test.js b/src/__tests__/perp.test.js index f5a7d0b4..c8212070 100644 --- a/src/__tests__/perp.test.js +++ b/src/__tests__/perp.test.js @@ -260,6 +260,21 @@ describe('perp cancel validation', () => { cmds.cancel([], null, {}, { coin: 'ETH', oid: '0', wallet: 'x' }), ).rejects.toThrow(/Invalid --oid "0"/); }); + + it('rejects --oid above 2^53-1 to prevent silent rounding of large uint64 ids', async () => { + const unsafeOid = String(Number.MAX_SAFE_INTEGER + 1); // 9007199254740992 + await expect( + cmds.cancel([], null, {}, { coin: 'ETH', oid: unsafeOid, wallet: 'x' }), + ).rejects.toThrow(/exceeds safe integer precision/); + }); + + it('accepts --oid exactly at 2^53-1 (MAX_SAFE_INTEGER)', async () => { + const safeOid = String(Number.MAX_SAFE_INTEGER); // 9007199254740991 + // Should not throw on validation; will fail later on missing wallet — that's fine. + await expect( + cmds.cancel([], null, {}, { coin: 'ETH', oid: safeOid, wallet: 'x' }), + ).rejects.not.toThrow(/exceeds safe integer precision/); + }); }); describe('perp meta listing (L1)', () => { diff --git a/src/perp.js b/src/perp.js index 4a601156..ea072afa 100644 --- a/src/perp.js +++ b/src/perp.js @@ -466,6 +466,12 @@ function parsePositiveInt(raw, name) { if (!Number.isInteger(n) || n <= 0) { throw invalid(`Invalid --${name} "${raw}". Must be a positive integer.`); } + // Hyperliquid order IDs are uint64. JS Number loses precision above 2^53-1, + // so parseInt would silently round a large oid and cancel the wrong order. + // Refuse here for the same reason the response path withholds unsafe oids. + if (!Number.isSafeInteger(n)) { + throw invalid(`Invalid --${name} "${raw}". Value exceeds safe integer precision (2^53-1); copy the exact order ID from "nansen perp positions".`); + } return n; }