From aa0d1163013a1f23817911fce2a894b775d8c674 Mon Sep 17 00:00:00 2001 From: Cesare Naldi <3353250+cesarenaldi@users.noreply.github.com> Date: Fri, 5 Jun 2026 20:24:23 +0200 Subject: [PATCH 1/3] fix builder fee unknown code errors --- .changeset/dev-166-builder-fee-404.md | 5 ++ packages/client/src/actions/clob.test.ts | 70 +++++++++++++++++++ packages/client/src/actions/clob.ts | 21 +++++- packages/client/src/actions/orders/prepare.ts | 3 + packages/client/src/actions/orders/trade.ts | 3 + packages/client/src/errors.ts | 15 ++++ 6 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 .changeset/dev-166-builder-fee-404.md create mode 100644 packages/client/src/actions/clob.test.ts diff --git a/.changeset/dev-166-builder-fee-404.md b/.changeset/dev-166-builder-fee-404.md new file mode 100644 index 0000000..248636f --- /dev/null +++ b/.changeset/dev-166-builder-fee-404.md @@ -0,0 +1,5 @@ +--- +"@polymarket/client": patch +--- + +Map unknown builder fee responses to `UnknownBuilderCodeError`. diff --git a/packages/client/src/actions/clob.test.ts b/packages/client/src/actions/clob.test.ts new file mode 100644 index 0000000..b3e17e4 --- /dev/null +++ b/packages/client/src/actions/clob.test.ts @@ -0,0 +1,70 @@ +import { BuilderCodeSchema } from '@polymarket/bindings'; +import { HttpResponse, http } from 'msw'; +import { setupServer } from 'msw/node'; +import { afterAll, afterEach, beforeAll, describe, expect, it } from 'vitest'; +import { createPublicClient } from '../clients'; +import { production } from '../environments'; +import { RequestRejectedError } from '../errors'; +import { fetchBuilderFeeRates } from './clob'; + +const clobRoot = 'http://localhost:4020'; +const builderCode = BuilderCodeSchema.parse( + '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd', +); +const server = setupServer(); + +const client = createPublicClient({ + environment: { + ...production, + clob: clobRoot, + }, +}); + +describe('fetchBuilderFeeRates', () => { + beforeAll(() => { + server.listen({ onUnhandledRequest: 'error' }); + }); + + afterEach(() => { + server.resetHandlers(); + }); + + afterAll(() => { + server.close(); + }); + + it('maps unknown builder code responses to a typed error', async () => { + server.use( + http.get(`${clobRoot}/fees/builder-fees/${builderCode}`, () => + HttpResponse.json({ error: 'builder code not found' }, { status: 404 }), + ), + ); + + await expect( + fetchBuilderFeeRates(client, { builderCode }), + ).rejects.toMatchObject({ + builderCode, + cause: expect.any(RequestRejectedError), + name: 'UnknownBuilderCodeError', + status: 404, + }); + }); + + it('preserves non-404 request rejections', async () => { + server.use( + http.get(`${clobRoot}/fees/builder-fees/${builderCode}`, () => + HttpResponse.json( + { error: 'temporarily unavailable' }, + { status: 503 }, + ), + ), + ); + + await expect(fetchBuilderFeeRates(client, { builderCode })).rejects.toEqual( + expect.objectContaining({ + name: 'RequestRejectedError', + status: 503, + }), + ); + }); +}); diff --git a/packages/client/src/actions/clob.ts b/packages/client/src/actions/clob.ts index f20d012..55c5b09 100644 --- a/packages/client/src/actions/clob.ts +++ b/packages/client/src/actions/clob.ts @@ -48,6 +48,7 @@ import { RequestRejectedError, TransportError, UnexpectedResponseError, + UnknownBuilderCodeError, UserInputError, } from '../errors'; import { parseUserInput } from '../input'; @@ -382,12 +383,14 @@ export type FetchBuilderFeeRatesError = | RateLimitError | RequestRejectedError | TransportError + | UnknownBuilderCodeError | UnexpectedResponseError | UserInputError; export const FetchBuilderFeeRatesError = makeErrorGuard( RateLimitError, RequestRejectedError, TransportError, + UnknownBuilderCodeError, UnexpectedResponseError, UserInputError, ); @@ -410,10 +413,26 @@ export async function fetchBuilderFeeRates( return unwrap( client.clob .get(`/fees/builder-fees/${params.builderCode}`) - .andThen(validateWith(FetchBuilderFeeRatesResponseSchema)), + .andThen(validateWith(FetchBuilderFeeRatesResponseSchema)) + .mapErr((error) => mapBuilderFeeRatesError(error, params.builderCode)), ); } +function mapBuilderFeeRatesError( + error: + | RateLimitError + | RequestRejectedError + | TransportError + | UnexpectedResponseError, + builderCode: string, +) { + if (error instanceof RequestRejectedError && error.status === 404) { + return new UnknownBuilderCodeError(builderCode, { cause: error }); + } + + return error; +} + const FetchPriceRequestSchema = z.object({ tokenId: z.string(), side: OrderSideSchema, diff --git a/packages/client/src/actions/orders/prepare.ts b/packages/client/src/actions/orders/prepare.ts index 1096b75..ebfa25d 100644 --- a/packages/client/src/actions/orders/prepare.ts +++ b/packages/client/src/actions/orders/prepare.ts @@ -8,6 +8,7 @@ import { SigningError, TransportError, UnexpectedResponseError, + UnknownBuilderCodeError, UserInputError, } from '../../errors'; import { parseUserInput } from '../../input'; @@ -36,6 +37,7 @@ export type PrepareMarketOrderError = | RequestRejectedError | SigningError | TransportError + | UnknownBuilderCodeError | UnexpectedResponseError | UserInputError; export const PrepareMarketOrderError = makeErrorGuard( @@ -44,6 +46,7 @@ export const PrepareMarketOrderError = makeErrorGuard( RequestRejectedError, SigningError, TransportError, + UnknownBuilderCodeError, UnexpectedResponseError, UserInputError, ); diff --git a/packages/client/src/actions/orders/trade.ts b/packages/client/src/actions/orders/trade.ts index b520e4c..3c1bdd3 100644 --- a/packages/client/src/actions/orders/trade.ts +++ b/packages/client/src/actions/orders/trade.ts @@ -13,6 +13,7 @@ import { TransactionFailedError, TransportError, UnexpectedResponseError, + UnknownBuilderCodeError, UserInputError, } from '../../errors'; import { completeWith } from '../../workflow'; @@ -44,6 +45,7 @@ export const CreateMarketOrderError = makeErrorGuard( RequestRejectedError, SigningError, TransportError, + UnknownBuilderCodeError, UnexpectedResponseError, UserInputError, ); @@ -78,6 +80,7 @@ export const PlaceMarketOrderError = makeErrorGuard( TimeoutError, TransactionFailedError, TransportError, + UnknownBuilderCodeError, UnexpectedResponseError, UserInputError, ); diff --git a/packages/client/src/errors.ts b/packages/client/src/errors.ts index 7541009..6ad177b 100644 --- a/packages/client/src/errors.ts +++ b/packages/client/src/errors.ts @@ -88,6 +88,21 @@ export class RequestRejectedError extends PolymarketError { } } +/** + * Error thrown when builder fee metadata is requested for an unknown builder. + */ +export class UnknownBuilderCodeError extends PolymarketError { + override name = 'UnknownBuilderCodeError' as const; + + readonly builderCode: string; + readonly status = 404; + + constructor(builderCode: string, options: ErrorOptions = {}) { + super(`Unknown builder code: ${builderCode}`, options); + this.builderCode = builderCode; + } +} + /** * Error thrown when the service rejects a request because the rate limit has * been exceeded. From a6a4deba43b02abe50c256cc39bde24ddfbafa8c Mon Sep 17 00:00:00 2001 From: Cesare Naldi <3353250+cesarenaldi@users.noreply.github.com> Date: Fri, 5 Jun 2026 20:38:59 +0200 Subject: [PATCH 2/3] map unknown builder code to user input error --- .changeset/dev-166-builder-fee-404.md | 2 +- packages/client/src/actions/clob.test.ts | 70 ----------- packages/client/src/actions/clob.ts | 7 +- .../client/src/actions/orders/orders.test.ts | 119 +++++++++++++++++- packages/client/src/actions/orders/prepare.ts | 3 - packages/client/src/actions/orders/trade.ts | 3 - packages/client/src/errors.ts | 15 --- 7 files changed, 121 insertions(+), 98 deletions(-) delete mode 100644 packages/client/src/actions/clob.test.ts diff --git a/.changeset/dev-166-builder-fee-404.md b/.changeset/dev-166-builder-fee-404.md index 248636f..9e64f89 100644 --- a/.changeset/dev-166-builder-fee-404.md +++ b/.changeset/dev-166-builder-fee-404.md @@ -2,4 +2,4 @@ "@polymarket/client": patch --- -Map unknown builder fee responses to `UnknownBuilderCodeError`. +Map unknown builder fee responses to `UserInputError`. diff --git a/packages/client/src/actions/clob.test.ts b/packages/client/src/actions/clob.test.ts deleted file mode 100644 index b3e17e4..0000000 --- a/packages/client/src/actions/clob.test.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { BuilderCodeSchema } from '@polymarket/bindings'; -import { HttpResponse, http } from 'msw'; -import { setupServer } from 'msw/node'; -import { afterAll, afterEach, beforeAll, describe, expect, it } from 'vitest'; -import { createPublicClient } from '../clients'; -import { production } from '../environments'; -import { RequestRejectedError } from '../errors'; -import { fetchBuilderFeeRates } from './clob'; - -const clobRoot = 'http://localhost:4020'; -const builderCode = BuilderCodeSchema.parse( - '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd', -); -const server = setupServer(); - -const client = createPublicClient({ - environment: { - ...production, - clob: clobRoot, - }, -}); - -describe('fetchBuilderFeeRates', () => { - beforeAll(() => { - server.listen({ onUnhandledRequest: 'error' }); - }); - - afterEach(() => { - server.resetHandlers(); - }); - - afterAll(() => { - server.close(); - }); - - it('maps unknown builder code responses to a typed error', async () => { - server.use( - http.get(`${clobRoot}/fees/builder-fees/${builderCode}`, () => - HttpResponse.json({ error: 'builder code not found' }, { status: 404 }), - ), - ); - - await expect( - fetchBuilderFeeRates(client, { builderCode }), - ).rejects.toMatchObject({ - builderCode, - cause: expect.any(RequestRejectedError), - name: 'UnknownBuilderCodeError', - status: 404, - }); - }); - - it('preserves non-404 request rejections', async () => { - server.use( - http.get(`${clobRoot}/fees/builder-fees/${builderCode}`, () => - HttpResponse.json( - { error: 'temporarily unavailable' }, - { status: 503 }, - ), - ), - ); - - await expect(fetchBuilderFeeRates(client, { builderCode })).rejects.toEqual( - expect.objectContaining({ - name: 'RequestRejectedError', - status: 503, - }), - ); - }); -}); diff --git a/packages/client/src/actions/clob.ts b/packages/client/src/actions/clob.ts index 55c5b09..2a3ff50 100644 --- a/packages/client/src/actions/clob.ts +++ b/packages/client/src/actions/clob.ts @@ -48,7 +48,6 @@ import { RequestRejectedError, TransportError, UnexpectedResponseError, - UnknownBuilderCodeError, UserInputError, } from '../errors'; import { parseUserInput } from '../input'; @@ -383,14 +382,12 @@ export type FetchBuilderFeeRatesError = | RateLimitError | RequestRejectedError | TransportError - | UnknownBuilderCodeError | UnexpectedResponseError | UserInputError; export const FetchBuilderFeeRatesError = makeErrorGuard( RateLimitError, RequestRejectedError, TransportError, - UnknownBuilderCodeError, UnexpectedResponseError, UserInputError, ); @@ -427,7 +424,9 @@ function mapBuilderFeeRatesError( builderCode: string, ) { if (error instanceof RequestRejectedError && error.status === 404) { - return new UnknownBuilderCodeError(builderCode, { cause: error }); + return new UserInputError(`Unknown builder code: ${builderCode}`, { + cause: error, + }); } return error; diff --git a/packages/client/src/actions/orders/orders.test.ts b/packages/client/src/actions/orders/orders.test.ts index d398a53..69f5a4c 100644 --- a/packages/client/src/actions/orders/orders.test.ts +++ b/packages/client/src/actions/orders/orders.test.ts @@ -1,9 +1,29 @@ -import { OrderSide, OrderType, toTokenId } from '@polymarket/bindings'; +import { + BuilderCodeSchema, + OrderSide, + OrderType, + toTokenId, +} from '@polymarket/bindings'; import { SignatureType } from '@polymarket/bindings/clob'; import { WalletType } from '@polymarket/bindings/gamma'; import type { EvmAddress } from '@polymarket/types'; -import { afterEach, describe, expect, it, vi } from 'vitest'; +import { HttpResponse, http } from 'msw'; +import { setupServer } from 'msw/node'; +import { + afterAll, + afterEach, + beforeAll, + describe, + expect, + it, + vi, +} from 'vitest'; +import type { BaseSecureClient } from '../../clients'; +import { production } from '../../environments'; +import { RequestRejectedError, UserInputError } from '../../errors'; +import { ServiceClient } from '../../ServiceClient'; import { createUnsignedOrder } from './orders'; +import { prepareMarketOrder } from './prepare'; import type { OrderDraft } from './types'; const SIGNER = '0x0000000000000000000000000000000000000001' as EvmAddress; @@ -11,6 +31,26 @@ const DEPOSIT_WALLET = '0x57ffbc34de23124faeb8387fcd689d314e57accd' as EvmAddress; const PROXY_WALLET = '0x7754536ecd85c00b2e0cf9c1aa679340d8550756' as EvmAddress; const SAFE_WALLET = '0x766b6851a199bf91ae3fa13b1cfac5187355118f' as EvmAddress; +const CLOB_ROOT = 'http://localhost:4020'; +const CONDITION_ID = + '0x1111111111111111111111111111111111111111111111111111111111111111'; +const TOKEN_ID = toTokenId('1'); +const BUILDER_CODE = BuilderCodeSchema.parse( + '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd', +); +const server = setupServer(); + +beforeAll(() => { + server.listen({ onUnhandledRequest: 'error' }); +}); + +afterEach(() => { + server.resetHandlers(); +}); + +afterAll(() => { + server.close(); +}); describe('createUnsignedOrder', () => { afterEach(() => { @@ -88,6 +128,32 @@ describe('createUnsignedOrder', () => { }); }); +describe('prepareMarketOrder', () => { + it('reports unknown builder codes as user input errors when resolving buy amounts against max spend', async () => { + mockMarketOrderContext(); + server.use( + http.get(`${CLOB_ROOT}/fees/builder-fees/${BUILDER_CODE}`, () => + HttpResponse.json({ error: 'builder code not found' }, { status: 404 }), + ), + ); + const workflow = await prepareMarketOrder(createClient(), { + amount: 10, + builderCode: BUILDER_CODE, + maxSpend: 10, + side: OrderSide.BUY, + tokenId: TOKEN_ID, + }); + const result = workflow.next(); + + await expect(result).rejects.toMatchObject({ + cause: expect.any(RequestRejectedError), + message: `Unknown builder code: ${BUILDER_CODE}`, + name: 'UserInputError', + }); + await expect(result).rejects.toBeInstanceOf(UserInputError); + }); +}); + function createOrderDraft(funderAddress: EvmAddress): OrderDraft { return { chainId: 137, @@ -102,3 +168,52 @@ function createOrderDraft(funderAddress: EvmAddress): OrderDraft { tokenId: toTokenId('1'), }; } + +function createClient(): BaseSecureClient { + return { + account: { + signer: SIGNER, + wallet: SIGNER, + walletType: WalletType.EOA, + }, + clob: new ServiceClient({ root: CLOB_ROOT }), + environment: { + ...production, + clob: CLOB_ROOT, + }, + } as BaseSecureClient; +} + +function mockMarketOrderContext() { + server.use( + http.get(`${CLOB_ROOT}/tick-size`, () => + HttpResponse.json({ minimum_tick_size: 0.01 }), + ), + http.get(`${CLOB_ROOT}/book`, () => + HttpResponse.json({ + market: CONDITION_ID, + asset_id: TOKEN_ID, + timestamp: null, + bids: [], + asks: [{ price: '0.5', size: '100' }], + min_order_size: '1', + tick_size: '0.01', + neg_risk: false, + last_trade_price: null, + hash: 'a'.repeat(40), + }), + ), + http.get(`${CLOB_ROOT}/neg-risk`, () => + HttpResponse.json({ neg_risk: false }), + ), + http.get(`${CLOB_ROOT}/markets-by-token/${TOKEN_ID}`, () => + HttpResponse.json({ condition_id: CONDITION_ID }), + ), + http.get(`${CLOB_ROOT}/clob-markets/${CONDITION_ID}`, () => + HttpResponse.json({ + fd: { r: 0, e: 0 }, + t: [{ t: TOKEN_ID, o: 'Yes' }], + }), + ), + ); +} diff --git a/packages/client/src/actions/orders/prepare.ts b/packages/client/src/actions/orders/prepare.ts index ebfa25d..1096b75 100644 --- a/packages/client/src/actions/orders/prepare.ts +++ b/packages/client/src/actions/orders/prepare.ts @@ -8,7 +8,6 @@ import { SigningError, TransportError, UnexpectedResponseError, - UnknownBuilderCodeError, UserInputError, } from '../../errors'; import { parseUserInput } from '../../input'; @@ -37,7 +36,6 @@ export type PrepareMarketOrderError = | RequestRejectedError | SigningError | TransportError - | UnknownBuilderCodeError | UnexpectedResponseError | UserInputError; export const PrepareMarketOrderError = makeErrorGuard( @@ -46,7 +44,6 @@ export const PrepareMarketOrderError = makeErrorGuard( RequestRejectedError, SigningError, TransportError, - UnknownBuilderCodeError, UnexpectedResponseError, UserInputError, ); diff --git a/packages/client/src/actions/orders/trade.ts b/packages/client/src/actions/orders/trade.ts index 3c1bdd3..b520e4c 100644 --- a/packages/client/src/actions/orders/trade.ts +++ b/packages/client/src/actions/orders/trade.ts @@ -13,7 +13,6 @@ import { TransactionFailedError, TransportError, UnexpectedResponseError, - UnknownBuilderCodeError, UserInputError, } from '../../errors'; import { completeWith } from '../../workflow'; @@ -45,7 +44,6 @@ export const CreateMarketOrderError = makeErrorGuard( RequestRejectedError, SigningError, TransportError, - UnknownBuilderCodeError, UnexpectedResponseError, UserInputError, ); @@ -80,7 +78,6 @@ export const PlaceMarketOrderError = makeErrorGuard( TimeoutError, TransactionFailedError, TransportError, - UnknownBuilderCodeError, UnexpectedResponseError, UserInputError, ); diff --git a/packages/client/src/errors.ts b/packages/client/src/errors.ts index 6ad177b..7541009 100644 --- a/packages/client/src/errors.ts +++ b/packages/client/src/errors.ts @@ -88,21 +88,6 @@ export class RequestRejectedError extends PolymarketError { } } -/** - * Error thrown when builder fee metadata is requested for an unknown builder. - */ -export class UnknownBuilderCodeError extends PolymarketError { - override name = 'UnknownBuilderCodeError' as const; - - readonly builderCode: string; - readonly status = 404; - - constructor(builderCode: string, options: ErrorOptions = {}) { - super(`Unknown builder code: ${builderCode}`, options); - this.builderCode = builderCode; - } -} - /** * Error thrown when the service rejects a request because the rate limit has * been exceeded. From 0ceaf37099f92154be612e832f3dae9ff4434300 Mon Sep 17 00:00:00 2001 From: Cesare Naldi <3353250+cesarenaldi@users.noreply.github.com> Date: Fri, 5 Jun 2026 20:45:01 +0200 Subject: [PATCH 3/3] move builder code coverage to integration test --- packages/client/src/actions/clob.ts | 28 ++--- .../client/src/actions/orders/orders.test.ts | 119 +----------------- .../client/tests/integration/orders.test.ts | 26 +++- 3 files changed, 37 insertions(+), 136 deletions(-) diff --git a/packages/client/src/actions/clob.ts b/packages/client/src/actions/clob.ts index 2a3ff50..9b04fdf 100644 --- a/packages/client/src/actions/clob.ts +++ b/packages/client/src/actions/clob.ts @@ -411,27 +411,19 @@ export async function fetchBuilderFeeRates( client.clob .get(`/fees/builder-fees/${params.builderCode}`) .andThen(validateWith(FetchBuilderFeeRatesResponseSchema)) - .mapErr((error) => mapBuilderFeeRatesError(error, params.builderCode)), + .mapErr((error) => { + if (error instanceof RequestRejectedError && error.status === 404) { + return new UserInputError( + `Unknown builder code: ${params.builderCode}`, + { cause: error }, + ); + } + + return error; + }), ); } -function mapBuilderFeeRatesError( - error: - | RateLimitError - | RequestRejectedError - | TransportError - | UnexpectedResponseError, - builderCode: string, -) { - if (error instanceof RequestRejectedError && error.status === 404) { - return new UserInputError(`Unknown builder code: ${builderCode}`, { - cause: error, - }); - } - - return error; -} - const FetchPriceRequestSchema = z.object({ tokenId: z.string(), side: OrderSideSchema, diff --git a/packages/client/src/actions/orders/orders.test.ts b/packages/client/src/actions/orders/orders.test.ts index 69f5a4c..d398a53 100644 --- a/packages/client/src/actions/orders/orders.test.ts +++ b/packages/client/src/actions/orders/orders.test.ts @@ -1,29 +1,9 @@ -import { - BuilderCodeSchema, - OrderSide, - OrderType, - toTokenId, -} from '@polymarket/bindings'; +import { OrderSide, OrderType, toTokenId } from '@polymarket/bindings'; import { SignatureType } from '@polymarket/bindings/clob'; import { WalletType } from '@polymarket/bindings/gamma'; import type { EvmAddress } from '@polymarket/types'; -import { HttpResponse, http } from 'msw'; -import { setupServer } from 'msw/node'; -import { - afterAll, - afterEach, - beforeAll, - describe, - expect, - it, - vi, -} from 'vitest'; -import type { BaseSecureClient } from '../../clients'; -import { production } from '../../environments'; -import { RequestRejectedError, UserInputError } from '../../errors'; -import { ServiceClient } from '../../ServiceClient'; +import { afterEach, describe, expect, it, vi } from 'vitest'; import { createUnsignedOrder } from './orders'; -import { prepareMarketOrder } from './prepare'; import type { OrderDraft } from './types'; const SIGNER = '0x0000000000000000000000000000000000000001' as EvmAddress; @@ -31,26 +11,6 @@ const DEPOSIT_WALLET = '0x57ffbc34de23124faeb8387fcd689d314e57accd' as EvmAddress; const PROXY_WALLET = '0x7754536ecd85c00b2e0cf9c1aa679340d8550756' as EvmAddress; const SAFE_WALLET = '0x766b6851a199bf91ae3fa13b1cfac5187355118f' as EvmAddress; -const CLOB_ROOT = 'http://localhost:4020'; -const CONDITION_ID = - '0x1111111111111111111111111111111111111111111111111111111111111111'; -const TOKEN_ID = toTokenId('1'); -const BUILDER_CODE = BuilderCodeSchema.parse( - '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd', -); -const server = setupServer(); - -beforeAll(() => { - server.listen({ onUnhandledRequest: 'error' }); -}); - -afterEach(() => { - server.resetHandlers(); -}); - -afterAll(() => { - server.close(); -}); describe('createUnsignedOrder', () => { afterEach(() => { @@ -128,32 +88,6 @@ describe('createUnsignedOrder', () => { }); }); -describe('prepareMarketOrder', () => { - it('reports unknown builder codes as user input errors when resolving buy amounts against max spend', async () => { - mockMarketOrderContext(); - server.use( - http.get(`${CLOB_ROOT}/fees/builder-fees/${BUILDER_CODE}`, () => - HttpResponse.json({ error: 'builder code not found' }, { status: 404 }), - ), - ); - const workflow = await prepareMarketOrder(createClient(), { - amount: 10, - builderCode: BUILDER_CODE, - maxSpend: 10, - side: OrderSide.BUY, - tokenId: TOKEN_ID, - }); - const result = workflow.next(); - - await expect(result).rejects.toMatchObject({ - cause: expect.any(RequestRejectedError), - message: `Unknown builder code: ${BUILDER_CODE}`, - name: 'UserInputError', - }); - await expect(result).rejects.toBeInstanceOf(UserInputError); - }); -}); - function createOrderDraft(funderAddress: EvmAddress): OrderDraft { return { chainId: 137, @@ -168,52 +102,3 @@ function createOrderDraft(funderAddress: EvmAddress): OrderDraft { tokenId: toTokenId('1'), }; } - -function createClient(): BaseSecureClient { - return { - account: { - signer: SIGNER, - wallet: SIGNER, - walletType: WalletType.EOA, - }, - clob: new ServiceClient({ root: CLOB_ROOT }), - environment: { - ...production, - clob: CLOB_ROOT, - }, - } as BaseSecureClient; -} - -function mockMarketOrderContext() { - server.use( - http.get(`${CLOB_ROOT}/tick-size`, () => - HttpResponse.json({ minimum_tick_size: 0.01 }), - ), - http.get(`${CLOB_ROOT}/book`, () => - HttpResponse.json({ - market: CONDITION_ID, - asset_id: TOKEN_ID, - timestamp: null, - bids: [], - asks: [{ price: '0.5', size: '100' }], - min_order_size: '1', - tick_size: '0.01', - neg_risk: false, - last_trade_price: null, - hash: 'a'.repeat(40), - }), - ), - http.get(`${CLOB_ROOT}/neg-risk`, () => - HttpResponse.json({ neg_risk: false }), - ), - http.get(`${CLOB_ROOT}/markets-by-token/${TOKEN_ID}`, () => - HttpResponse.json({ condition_id: CONDITION_ID }), - ), - http.get(`${CLOB_ROOT}/clob-markets/${CONDITION_ID}`, () => - HttpResponse.json({ - fd: { r: 0, e: 0 }, - t: [{ t: TOKEN_ID, o: 'Yes' }], - }), - ), - ); -} diff --git a/packages/client/tests/integration/orders.test.ts b/packages/client/tests/integration/orders.test.ts index e12d581..4d5ba09 100644 --- a/packages/client/tests/integration/orders.test.ts +++ b/packages/client/tests/integration/orders.test.ts @@ -1,10 +1,11 @@ -import { OrderSide, OrderType } from '@polymarket/bindings'; +import { BuilderCodeSchema, OrderSide, OrderType } from '@polymarket/bindings'; import { OrderPostStatus } from '@polymarket/bindings/clob'; import { createSecureClient, InsufficientLiquidityError, type Market, type SecureClient, + UserInputError, } from '@polymarket/client'; import { fetchNegRisk } from '@polymarket/client/actions'; import { expectPresent } from '@polymarket/types'; @@ -20,6 +21,9 @@ import { expectAcceptedOrderResponse } from './helpers'; import { findHighVolumeLowPriceMarket } from './markets'; const market = await findHighVolumeLowPriceMarket(publicClient); +const UNKNOWN_BUILDER_CODE = BuilderCodeSchema.parse( + '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd', +); let marketOrderCleanup: | { market: Market; @@ -168,6 +172,26 @@ describe('Orders', { timeout: 60_000 }, () => { expect(order.builder).toBe(builderCode); }); + + it('reports unknown builder codes as user input errors when resolving buy amounts against max spend', async ({ + annotate, + secureClientWithDepositWallet, + }) => { + const yesTokenId = expectPresent(market.outcomes.yes.tokenId); + const minimumOrderSize = expectPresent(market.trading.minimumOrderSize); + annotate(`Market ID: ${market.id}`); + annotate(`Token ID: ${yesTokenId}`); + + await expect( + secureClientWithDepositWallet.createMarketOrder({ + amount: minimumOrderSize, + builderCode: UNKNOWN_BUILDER_CODE, + maxSpend: minimumOrderSize, + side: OrderSide.BUY, + tokenId: yesTokenId, + }), + ).rejects.toThrow(UserInputError); + }); }); describe('placeLimitOrder', () => {