From 02a0d2771e434e9cb2cf9388551a41c9422b53bf Mon Sep 17 00:00:00 2001 From: kartojal Date: Wed, 23 Sep 2026 13:31:38 +0200 Subject: [PATCH] Revert automatic V3 routing (#105) This reverts commit 9cfb25ffd36fd3872f3cc9fb29e6a2c607d2a626. --- README.md | 5 -- src/client.ts | 25 ++-------- src/protocol.ts | 17 ------- src/types/clob.ts | 1 - tests/client/orderVersion.test.ts | 80 ------------------------------- tests/protocol.test.ts | 19 -------- 6 files changed, 5 insertions(+), 142 deletions(-) delete mode 100644 src/protocol.ts delete mode 100644 tests/client/orderVersion.test.ts delete mode 100644 tests/protocol.test.ts diff --git a/README.md b/README.md index 4e2cec5..7243407 100644 --- a/README.md +++ b/README.md @@ -48,11 +48,6 @@ console.log(resp); See [examples](examples/) for more information. -Limit and market orders automatically use Exchange V3 for protocol V2 position IDs. -Legacy CTF token IDs use the server's exchange version. To override automatic -selection, pass `version: 1`, `version: 2`, or `version: 3` in the order options, -for example `{ tickSize: "0.01", version: 3 }`. - ### Market Orders ```ts diff --git a/src/client.ts b/src/client.ts index 090f6cc..19d9ed8 100644 --- a/src/client.ts +++ b/src/client.ts @@ -87,7 +87,6 @@ import { } from "./order-builder/helpers/index.js"; import { OrderBuilder } from "./order-builder/index.js"; import { SignatureTypeV2 } from "./order-utils/model/signatureTypeV2.js"; -import { isV2PositionId } from "./protocol.js"; import type { ClobSigner } from "./signing/signer.js"; import type { ApiKeyCreds, @@ -910,7 +909,7 @@ export class ClobClient { } orderToSign.price = roundNormal(orderToSign.price, ROUNDING_CONFIG[tickSize].price); - const version = await this.resolveOrderVersion(tokenID, options?.version); + const version = options?.version ?? (await this.resolveVersion()); if ( version !== 1 && @@ -1001,7 +1000,7 @@ export class ClobClient { } const negRisk = options?.negRisk ?? (await this.getNegRisk(tokenID)); - const version = await this.resolveOrderVersion(tokenID, options?.version); + const version = options?.version ?? (await this.resolveVersion()); if (version === 1) { const userFeeRateBps = @@ -1044,7 +1043,7 @@ export class ClobClient { ): Promise { let postOrderResponse: OrderResponse | undefined; - await this._retryOnVersionUpdate(userOrder.tokenID, options?.version, async () => { + await this._retryOnVersionUpdate(async () => { const order = await this.createOrder(userOrder, options); postOrderResponse = await this.postOrder(order, orderType, postOnly, deferExec); }); @@ -1060,7 +1059,7 @@ export class ClobClient { ): Promise { let postOrderMarketResponse: OrderResponse | undefined; - await this._retryOnVersionUpdate(userMarketOrder.tokenID, options?.version, async () => { + await this._retryOnVersionUpdate(async () => { const order = await this.createMarketOrder(userMarketOrder, options); postOrderMarketResponse = await this.postOrder(order, orderType, false, deferExec); }); @@ -1762,10 +1761,6 @@ export class ClobClient { return marketFeeRateBps; } - private async resolveOrderVersion(tokenID: string, version?: number): Promise { - return version ?? (isV2PositionId(tokenID) ? 3 : this.resolveVersion()); - } - private async resolveVersion(forceUpdate: boolean = false): Promise { // Use cached version if given if (!forceUpdate && this.cachedVersion !== undefined) { @@ -1779,17 +1774,7 @@ export class ClobClient { return apiVersion; } - private async _retryOnVersionUpdate( - tokenID: string, - explicitVersion: number | undefined, - retryFunc: () => Promise, - ) { - // Only server-selected versions can change during migration recovery. - if (explicitVersion != null || isV2PositionId(tokenID)) { - await retryFunc(); - return; - } - + private async _retryOnVersionUpdate(retryFunc: () => Promise) { const version = await this.resolveVersion(); for (let attempt = 0; attempt < 2; attempt++) { diff --git a/src/protocol.ts b/src/protocol.ts deleted file mode 100644 index 66f4ef5..0000000 --- a/src/protocol.ts +++ /dev/null @@ -1,17 +0,0 @@ -const UINT256_MAX = (1n << 256n) - 1n; -const V2_RESERVED_BITS_MASK = ((1n << 64n) - 1n) << 40n; - -/** - * Classifies the protocol V2 position namespace. Registered CTF token IDs do - * not occupy this namespace; CLOB still validates that the asset exists. - * @internal - */ -export function isV2PositionId(tokenID: string): boolean { - try { - if (tokenID.trim().length === 0) return false; - const value = BigInt(tokenID); - return value >= 0n && value <= UINT256_MAX && (value & V2_RESERVED_BITS_MASK) === 0n; - } catch { - return false; - } -} diff --git a/src/types/clob.ts b/src/types/clob.ts index 96f4c53..3d87ab8 100644 --- a/src/types/clob.ts +++ b/src/types/clob.ts @@ -252,7 +252,6 @@ export type OrderVersion = 1 | 2 | 3; export type CreateOrderOptions = { tickSize: TickSize; negRisk?: boolean; - /** Omit to select Exchange V3 for protocol V2 positions, or the server version for CTF tokens. */ version?: OrderVersion; }; diff --git a/tests/client/orderVersion.test.ts b/tests/client/orderVersion.test.ts deleted file mode 100644 index 77a84fd..0000000 --- a/tests/client/orderVersion.test.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { Wallet } from "@ethersproject/wallet"; -import { afterEach, describe, expect, it, vi } from "vitest"; -import { ClobClient } from "../../src/client.js"; -import { getContractConfig } from "../../src/config.js"; -import { ORDER_VERSION_MISMATCH_ERROR } from "../../src/constants.js"; -import * as httpHelpers from "../../src/http-helpers/index.js"; -import { Chain, type OrderVersion, Side } from "../../src/types/index.js"; - -const POSITION = ((1n << 248n) | 1n).toString(); -const CTF_TOKEN = ((1n << 200n) | (1n << 40n)).toString(); -const success = { success: true, orderID: "order", status: "live" }; -const mismatch = { error: ORDER_VERSION_MISMATCH_ERROR, status: 400 }; - -afterEach(() => vi.restoreAllMocks()); - -describe.each(["createAndPostOrder", "createAndPostMarketOrder"] as const)("%s routing", method => { - function setup() { - const wallet = new Wallet( - "0x0000000000000000000000000000000000000000000000000000000000000001", - ); - const sign = vi.spyOn(wallet, "_signTypedData"); - const client = new ClobClient({ - host: "http://localhost:8080", - chain: Chain.POLYGON, - signer: wallet, - creds: { key: "key", secret: "c2VjcmV0", passphrase: "passphrase" }, - }); - vi.spyOn(client, "getTickSize").mockResolvedValue("0.01"); - vi.spyOn(client, "getFeeRateBps").mockResolvedValue(0); - for (const tokenID of [POSITION, CTF_TOKEN]) - client.feeInfos[tokenID] = { rate: 0, exponent: 0 }; - const getVersion = vi.spyOn(client, "getVersion").mockResolvedValue(2); - const post = vi.spyOn(httpHelpers, "post").mockResolvedValue(success); - const place = (tokenID: string, version?: OrderVersion) => { - const order = { tokenID, price: 0.4, size: 100, amount: 40, side: Side.BUY }; - const options = { tickSize: "0.01", negRisk: false, version } as const; - return method === "createAndPostOrder" - ? client.createAndPostOrder(order, options) - : client.createAndPostMarketOrder(order, options); - }; - return { sign, getVersion, post, place }; - } - - it("routes V2 positions without /version and keeps the legacy cache separate", async () => { - const { sign, getVersion, place } = setup(); - expect(await place(POSITION)).toEqual(success); - expect(getVersion).not.toHaveBeenCalled(); - expect(sign.mock.calls[0][0]).toMatchObject({ - version: "3", - verifyingContract: getContractConfig(Chain.POLYGON).exchangeV3, - }); - for (const tokenID of [CTF_TOKEN, POSITION, CTF_TOKEN]) await place(tokenID); - expect(sign.mock.calls.map(([domain]) => domain.version)).toEqual(["3", "2", "3", "2"]); - expect(getVersion).toHaveBeenCalledTimes(1); - }); - - it.each([1, 2, 3] as const)("honors explicit version %s without /version", async version => { - const { sign, getVersion, place } = setup(); - await place(POSITION, version); - expect(sign.mock.calls[0][0].version).toBe(String(version)); - expect(getVersion).not.toHaveBeenCalled(); - }); - - it.each([ - { tokenID: POSITION, version: undefined, signedVersions: ["3"] }, - { tokenID: CTF_TOKEN, version: 3, signedVersions: ["3"] }, - { tokenID: CTF_TOKEN, version: undefined, signedVersions: ["1", "2"] }, - ] as const)("retries a migration mismatch only for automatic legacy routing: %j", async ({ - tokenID, - version, - signedVersions, - }) => { - const { sign, getVersion, post, place } = setup(); - getVersion.mockResolvedValueOnce(1).mockResolvedValue(2); - post.mockResolvedValueOnce(mismatch).mockResolvedValue(success); - await place(tokenID, version); - expect(sign.mock.calls.map(([domain]) => domain.version)).toEqual(signedVersions); - expect(post).toHaveBeenCalledTimes(signedVersions.length); - }); -}); diff --git a/tests/protocol.test.ts b/tests/protocol.test.ts deleted file mode 100644 index 123d7ec..0000000 --- a/tests/protocol.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { expect, it } from "vitest"; -import { isV2PositionId } from "../src/protocol.js"; - -it.each([ - ["0", true], - [(1n << 39n).toString(), true], - [(1n << 40n).toString(), false], - [(1n << 103n).toString(), false], - [(1n << 104n).toString(), true], - ["0x1", true], - ["", false], - [" ", false], - ["invalid", false], - ["-1", false], - [((1n << 256n) - 1n).toString(), false], - [(1n << 256n).toString(), false], -] as const)("classifies token ID %j as V2: %s", (tokenID, expected) => { - expect(isV2PositionId(tokenID)).toBe(expected); -});