diff --git a/package.json b/package.json index 2d131c6..f8b8462 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@polymarket/clob-client", "description": "Typescript client for Polymarket's CLOB", - "version": "4.5.4", + "version": "4.5.5", "contributors": [ { "name": "Jonathan Amenechi", diff --git a/src/client.ts b/src/client.ts index c13ccb7..df8abcb 100644 --- a/src/client.ts +++ b/src/client.ts @@ -103,6 +103,7 @@ import { } from "./endpoints"; import { OrderBuilder } from "./order-builder/builder"; import { END_CURSOR, INITIAL_CURSOR } from "./constants"; +import { calculateMarketPrice } from "./order-builder/helpers"; export class ClobClient { readonly host: string; @@ -507,8 +508,11 @@ export class ClobClient { const negRisk = options?.negRisk ?? false; if (!userMarketOrder.price) { - const marketPrice = await this.getPrice(tokenID, Side.BUY); - userMarketOrder.price = parseFloat(marketPrice); + userMarketOrder.price = await this.calculateMarketPrice( + tokenID, + Side.BUY, + userMarketOrder.amount, + ); } if (!priceValid(userMarketOrder.price, tickSize)) { @@ -824,6 +828,28 @@ export class ClobClient { return this.get(`${this.host}${GET_MARKET_TRADES_EVENTS}${conditionID}`); } + public async calculateMarketPrice( + tokenID: string, + side: Side, + amount: number, + ): Promise { + const book = await this.getOrderBook(tokenID); + if (!book) { + throw new Error("no orderbook"); + } + if (side === Side.BUY) { + if (!book.asks) { + throw new Error("no match"); + } + return calculateMarketPrice(book.asks, amount); + } else { + if (!book.bids) { + throw new Error("no match"); + } + return calculateMarketPrice(book.bids, amount); + } + } + private canL1Auth(): void { if (this.signer === undefined) { throw L1_AUTH_UNAVAILABLE_ERROR; diff --git a/src/order-builder/helpers.ts b/src/order-builder/helpers.ts index 7d01698..ad0143a 100644 --- a/src/order-builder/helpers.ts +++ b/src/order-builder/helpers.ts @@ -16,6 +16,7 @@ import { TickSize, RoundConfig, CreateOrderOptions, + OrderSummary, } from "../types"; import { decimalPlaces, roundDown, roundNormal, roundUp } from "../utilities"; import { @@ -268,7 +269,7 @@ export const buildMarketBuyOrderCreationArgs = async ( feeRateBps, nonce, signer, - expiration: (userMarketOrder.expiration || 0).toString(), + expiration: "0", signatureType, } as OrderData; }; @@ -301,3 +302,15 @@ export const createMarketBuyOrder = async ( return buildOrder(eoaSigner, exchangeContract, chainId, orderData); }; + +export const calculateMarketPrice = (positions: OrderSummary[], amountToMatch: number) => { + let sum = 0; + for (let i = 0; i < positions.length; i++) { + const p = positions[i]; + sum += parseFloat(p.size) * parseFloat(p.price); + if (sum >= amountToMatch) { + return parseFloat(p.price); + } + } + throw new Error("no match"); +}; diff --git a/src/types.ts b/src/types.ts index c4fa537..15043bf 100644 --- a/src/types.ts +++ b/src/types.ts @@ -138,11 +138,6 @@ export interface UserMarketOrder { */ nonce?: number; - /** - * Timestamp after which the order is expired. - */ - expiration?: number; - /** * Address of the order taker. The zero address is used to indicate a public order */ diff --git a/tests/order-builder/helpers.test.ts b/tests/order-builder/helpers.test.ts index ce0be73..925b2e5 100644 --- a/tests/order-builder/helpers.test.ts +++ b/tests/order-builder/helpers.test.ts @@ -1,6 +1,6 @@ import "mocha"; import { expect } from "chai"; -import { UserOrder, Side, Chain, UserMarketOrder } from "../../src/types"; +import { UserOrder, Side, Chain, UserMarketOrder, OrderSummary } from "../../src/types"; import { buildOrderCreationArgs, buildOrder, @@ -10,6 +10,7 @@ import { getOrderRawAmounts, getMarketBuyOrderRawAmounts, ROUNDING_CONFIG, + calculateMarketPrice, } from "../../src/order-builder/helpers"; import { OrderData, SignatureType, Side as UtilsSide } from "@polymarket/order-utils"; import { Wallet } from "@ethersproject/wallet"; @@ -2475,7 +2476,6 @@ describe("helpers", () => { amount: 100, feeRateBps: 111, nonce: 123, - expiration: 50000, }; const orderData: OrderData = await buildMarketBuyOrderCreationArgs( "0x0000000000000000000000000000000000000001", @@ -2494,7 +2494,7 @@ describe("helpers", () => { feeRateBps: "111", nonce: "123", signer: "0x0000000000000000000000000000000000000001", - expiration: "50000", + expiration: "0", signatureType: SignatureType.EOA, }); }); @@ -2506,7 +2506,6 @@ describe("helpers", () => { amount: 100, feeRateBps: 111, nonce: 123, - expiration: 50000, }; const orderData: OrderData = await buildMarketBuyOrderCreationArgs( "0x0000000000000000000000000000000000000001", @@ -2525,7 +2524,7 @@ describe("helpers", () => { feeRateBps: "111", nonce: "123", signer: "0x0000000000000000000000000000000000000001", - expiration: "50000", + expiration: "0", signatureType: SignatureType.EOA, }); }); @@ -2537,7 +2536,6 @@ describe("helpers", () => { amount: 100, feeRateBps: 111, nonce: 123, - expiration: 50000, }; const orderData: OrderData = await buildMarketBuyOrderCreationArgs( "0x0000000000000000000000000000000000000001", @@ -2556,7 +2554,7 @@ describe("helpers", () => { feeRateBps: "111", nonce: "123", signer: "0x0000000000000000000000000000000000000001", - expiration: "50000", + expiration: "0", signatureType: SignatureType.EOA, }); }); @@ -2568,7 +2566,6 @@ describe("helpers", () => { amount: 100, feeRateBps: 111, nonce: 123, - expiration: 50000, }; const orderData: OrderData = await buildMarketBuyOrderCreationArgs( "0x0000000000000000000000000000000000000001", @@ -2587,7 +2584,7 @@ describe("helpers", () => { feeRateBps: "111", nonce: "123", signer: "0x0000000000000000000000000000000000000001", - expiration: "50000", + expiration: "0", signatureType: SignatureType.EOA, }); }); @@ -2602,7 +2599,6 @@ describe("helpers", () => { amount: 100, feeRateBps: 111, nonce: 123, - expiration: 50000, }; const orderData: OrderData = await buildMarketBuyOrderCreationArgs( "0x0000000000000000000000000000000000000001", @@ -2621,7 +2617,7 @@ describe("helpers", () => { feeRateBps: "111", nonce: "123", signer: "0x0000000000000000000000000000000000000001", - expiration: "50000", + expiration: "0", signatureType: SignatureType.EOA, }); }); @@ -2788,7 +2784,6 @@ describe("helpers", () => { amount: 100, feeRateBps: 111, nonce: 123, - expiration: 50000, }; const orderData: OrderData = await buildMarketBuyOrderCreationArgs( "0x0000000000000000000000000000000000000001", @@ -2807,7 +2802,7 @@ describe("helpers", () => { feeRateBps: "111", nonce: "123", signer: "0x0000000000000000000000000000000000000001", - expiration: "50000", + expiration: "0", signatureType: SignatureType.EOA, }); }); @@ -2974,7 +2969,6 @@ describe("helpers", () => { amount: 100, feeRateBps: 111, nonce: 123, - expiration: 50000, }; const orderData: OrderData = await buildMarketBuyOrderCreationArgs( "0x0000000000000000000000000000000000000001", @@ -2993,7 +2987,7 @@ describe("helpers", () => { feeRateBps: "111", nonce: "123", signer: "0x0000000000000000000000000000000000000001", - expiration: "50000", + expiration: "0", signatureType: SignatureType.EOA, }); }); @@ -3160,7 +3154,6 @@ describe("helpers", () => { amount: 100, feeRateBps: 111, nonce: 123, - expiration: 50000, }; const orderData: OrderData = await buildMarketBuyOrderCreationArgs( "0x0000000000000000000000000000000000000001", @@ -3179,7 +3172,7 @@ describe("helpers", () => { feeRateBps: "111", nonce: "123", signer: "0x0000000000000000000000000000000000000001", - expiration: "50000", + expiration: "0", signatureType: SignatureType.EOA, }); }); @@ -3350,7 +3343,6 @@ describe("helpers", () => { amount: 100, feeRateBps: 111, nonce: 123, - expiration: 50000, }; const signedOrder = await createMarketBuyOrder( @@ -3372,7 +3364,7 @@ describe("helpers", () => { expect(signedOrder.makerAmount).equal("100000000"); expect(signedOrder.takerAmount).equal("200000000"); expect(signedOrder.side).equal(UtilsSide.BUY); - expect(signedOrder.expiration).equal("50000"); + expect(signedOrder.expiration).equal("0"); expect(signedOrder.nonce).equal("123"); expect(signedOrder.feeRateBps).equal("111"); expect(signedOrder.signatureType).equal(SignatureType.EOA); @@ -3386,7 +3378,6 @@ describe("helpers", () => { amount: 100, feeRateBps: 111, nonce: 123, - expiration: 50000, }; const signedOrder = await createMarketBuyOrder( @@ -3408,7 +3399,7 @@ describe("helpers", () => { expect(signedOrder.makerAmount).equal("100000000"); expect(signedOrder.takerAmount).equal("178571400"); expect(signedOrder.side).equal(UtilsSide.BUY); - expect(signedOrder.expiration).equal("50000"); + expect(signedOrder.expiration).equal("0"); expect(signedOrder.nonce).equal("123"); expect(signedOrder.feeRateBps).equal("111"); expect(signedOrder.signatureType).equal(SignatureType.EOA); @@ -3422,7 +3413,6 @@ describe("helpers", () => { amount: 100, feeRateBps: 111, nonce: 123, - expiration: 50000, }; const signedOrder = await createMarketBuyOrder( @@ -3444,7 +3434,7 @@ describe("helpers", () => { expect(signedOrder.makerAmount).equal("100000000"); expect(signedOrder.takerAmount).equal("1785714280"); expect(signedOrder.side).equal(UtilsSide.BUY); - expect(signedOrder.expiration).equal("50000"); + expect(signedOrder.expiration).equal("0"); expect(signedOrder.nonce).equal("123"); expect(signedOrder.feeRateBps).equal("111"); expect(signedOrder.signatureType).equal(SignatureType.EOA); @@ -3458,7 +3448,6 @@ describe("helpers", () => { amount: 100, feeRateBps: 111, nonce: 123, - expiration: 50000, }; const signedOrder = await createMarketBuyOrder( @@ -3480,7 +3469,7 @@ describe("helpers", () => { expect(signedOrder.makerAmount).equal("100000000"); expect(signedOrder.takerAmount).equal("17857142857"); expect(signedOrder.side).equal(UtilsSide.BUY); - expect(signedOrder.expiration).equal("50000"); + expect(signedOrder.expiration).equal("0"); expect(signedOrder.nonce).equal("123"); expect(signedOrder.feeRateBps).equal("111"); expect(signedOrder.signatureType).equal(SignatureType.EOA); @@ -3489,7 +3478,7 @@ describe("helpers", () => { }); }); - describe("CTF Exchange", () => { + describe("Neg Risk CTF Exchange", () => { describe("buy order", async () => { it("0.1", async () => { const order: UserMarketOrder = { @@ -3498,7 +3487,6 @@ describe("helpers", () => { amount: 100, feeRateBps: 111, nonce: 123, - expiration: 50000, }; const signedOrder = await createMarketBuyOrder( @@ -3520,7 +3508,7 @@ describe("helpers", () => { expect(signedOrder.makerAmount).equal("100000000"); expect(signedOrder.takerAmount).equal("200000000"); expect(signedOrder.side).equal(UtilsSide.BUY); - expect(signedOrder.expiration).equal("50000"); + expect(signedOrder.expiration).equal("0"); expect(signedOrder.nonce).equal("123"); expect(signedOrder.feeRateBps).equal("111"); expect(signedOrder.signatureType).equal(SignatureType.EOA); @@ -3534,7 +3522,6 @@ describe("helpers", () => { amount: 100, feeRateBps: 111, nonce: 123, - expiration: 50000, }; const signedOrder = await createMarketBuyOrder( @@ -3556,7 +3543,7 @@ describe("helpers", () => { expect(signedOrder.makerAmount).equal("100000000"); expect(signedOrder.takerAmount).equal("178571400"); expect(signedOrder.side).equal(UtilsSide.BUY); - expect(signedOrder.expiration).equal("50000"); + expect(signedOrder.expiration).equal("0"); expect(signedOrder.nonce).equal("123"); expect(signedOrder.feeRateBps).equal("111"); expect(signedOrder.signatureType).equal(SignatureType.EOA); @@ -3570,7 +3557,6 @@ describe("helpers", () => { amount: 100, feeRateBps: 111, nonce: 123, - expiration: 50000, }; const signedOrder = await createMarketBuyOrder( @@ -3592,7 +3578,7 @@ describe("helpers", () => { expect(signedOrder.makerAmount).equal("100000000"); expect(signedOrder.takerAmount).equal("1785714280"); expect(signedOrder.side).equal(UtilsSide.BUY); - expect(signedOrder.expiration).equal("50000"); + expect(signedOrder.expiration).equal("0"); expect(signedOrder.nonce).equal("123"); expect(signedOrder.feeRateBps).equal("111"); expect(signedOrder.signatureType).equal(SignatureType.EOA); @@ -3606,7 +3592,6 @@ describe("helpers", () => { amount: 100, feeRateBps: 111, nonce: 123, - expiration: 50000, }; const signedOrder = await createMarketBuyOrder( @@ -3628,7 +3613,7 @@ describe("helpers", () => { expect(signedOrder.makerAmount).equal("100000000"); expect(signedOrder.takerAmount).equal("17857142857"); expect(signedOrder.side).equal(UtilsSide.BUY); - expect(signedOrder.expiration).equal("50000"); + expect(signedOrder.expiration).equal("0"); expect(signedOrder.nonce).equal("123"); expect(signedOrder.feeRateBps).equal("111"); expect(signedOrder.signatureType).equal(SignatureType.EOA); @@ -3637,4 +3622,96 @@ describe("helpers", () => { }); }); }); + + describe.only("calculateMarketPrice", () => { + describe("BUY", () => { + it("empty orderbook", () => { + expect(() => calculateMarketPrice([], 100)).to.throw("no match"); + }); + it("not enough", () => { + const positions = [ + { price: "0.5", size: "100" }, + { price: "0.4", size: "100" }, + ] as OrderSummary[]; + expect(() => calculateMarketPrice(positions, 100)).to.throw("no match"); + }); + it("ok", () => { + let positions = [ + { price: "0.5", size: "100" }, + { price: "0.4", size: "100" }, + { price: "0.3", size: "100" }, + ] as OrderSummary[]; + expect(calculateMarketPrice(positions, 100)).equal(0.3); + + positions = [ + { price: "0.5", size: "100" }, + { price: "0.4", size: "200" }, + { price: "0.3", size: "100" }, + ] as OrderSummary[]; + expect(calculateMarketPrice(positions, 100)).equal(0.4); + + positions = [ + { price: "0.5", size: "120" }, + { price: "0.4", size: "100" }, + { price: "0.3", size: "100" }, + ] as OrderSummary[]; + expect(calculateMarketPrice(positions, 100)).equal(0.4); + + positions = [ + { price: "0.5", size: "200" }, + { price: "0.4", size: "100" }, + { price: "0.3", size: "100" }, + ] as OrderSummary[]; + expect(calculateMarketPrice(positions, 100)).equal(0.5); + }); + }); + describe("SELL", () => { + it("empty orderbook", () => { + expect(() => calculateMarketPrice([], 100)).to.throw("no match"); + }); + it("not enough", () => { + const positions = [ + { price: "0.4", size: "100" }, + { price: "0.5", size: "100" }, + ] as OrderSummary[]; + expect(() => calculateMarketPrice(positions, 100)).to.throw("no match"); + }); + it("ok", () => { + let positions = [ + { price: "0.3", size: "100" }, + { price: "0.4", size: "100" }, + { price: "0.5", size: "100" }, + ] as OrderSummary[]; + expect(calculateMarketPrice(positions, 100)).equal(0.5); + + positions = [ + { price: "0.3", size: "100" }, + { price: "0.4", size: "300" }, + { price: "0.5", size: "100" }, + ] as OrderSummary[]; + expect(calculateMarketPrice(positions, 100)).equal(0.4); + + positions = [ + { price: "0.3", size: "100" }, + { price: "0.4", size: "200" }, + { price: "0.5", size: "100" }, + ] as OrderSummary[]; + expect(calculateMarketPrice(positions, 100)).equal(0.4); + + positions = [ + { price: "0.3", size: "300" }, + { price: "0.4", size: "100" }, + { price: "0.5", size: "100" }, + ] as OrderSummary[]; + expect(calculateMarketPrice(positions, 100)).equal(0.4); + + positions = [ + { price: "0.3", size: "334" }, + { price: "0.4", size: "100" }, + { price: "0.5", size: "100" }, + ] as OrderSummary[]; + expect(calculateMarketPrice(positions, 100)).equal(0.3); + }); + }); + }); });