@polymarket/clob-client-v2 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -141,6 +141,28 @@ var ApiError = class extends Error {
141
141
  }
142
142
  };
143
143
 
144
+ // src/fees/index.ts
145
+ var MIN_FEE_SLIPPAGE_PERCENTAGE = 1;
146
+ var MAX_FEE_SLIPPAGE_PERCENTAGE = 100;
147
+ function validateFeeSlippage(feeSlippage) {
148
+ if (!Number.isFinite(feeSlippage) || feeSlippage < 0 || feeSlippage > MAX_FEE_SLIPPAGE_PERCENTAGE || feeSlippage > 0 && feeSlippage < MIN_FEE_SLIPPAGE_PERCENTAGE) {
149
+ throw new Error("feeSlippage must be 0 or a percentage between 1 and 100");
150
+ }
151
+ }
152
+ function adjustBuyAmountForFees(amount, price, userUSDCBalance, feeRate, feeExponent, builderTakerFeeRate, feeSlippage = 0) {
153
+ validateFeeSlippage(feeSlippage);
154
+ const platformFeeRate = feeRate * (price * (1 - price)) ** feeExponent;
155
+ const effectivePlatformFeeRate = platformFeeRate * (1 + feeSlippage / 100);
156
+ const feeBaseAmount = Math.min(amount, userUSDCBalance);
157
+ const platformFee = feeBaseAmount / price * effectivePlatformFeeRate;
158
+ const builderFee = feeBaseAmount * builderTakerFeeRate;
159
+ const totalCost = amount + platformFee + builderFee;
160
+ if (userUSDCBalance <= totalCost) {
161
+ return Math.max(userUSDCBalance - platformFee - builderFee, 0);
162
+ }
163
+ return amount;
164
+ }
165
+
144
166
  // src/signing/constants.ts
145
167
  var MSG_TO_SIGN = "This message attests that I control the given wallet";
146
168
 
@@ -202,8 +224,8 @@ var signTypedDataWithSigner = async ({
202
224
  };
203
225
 
204
226
  // src/signing/eip712.ts
205
- var buildClobEip712Signature = async (signer, chainId, timestamp, nonce) => {
206
- const address = await getSignerAddress(signer);
227
+ var buildClobEip712Signature = async (signer, chainId, timestamp, nonce, address) => {
228
+ const resolvedAddress = address ?? await getSignerAddress(signer);
207
229
  const ts = timestamp.toString();
208
230
  const domain = {
209
231
  name: "ClobAuthDomain",
@@ -219,7 +241,7 @@ var buildClobEip712Signature = async (signer, chainId, timestamp, nonce) => {
219
241
  ]
220
242
  };
221
243
  const value = {
222
- address,
244
+ address: resolvedAddress,
223
245
  timestamp: ts,
224
246
  nonce,
225
247
  message: MSG_TO_SIGN
@@ -252,7 +274,7 @@ var buildPolyHmacSignature = (secret, timestamp, method, requestPath, body) => {
252
274
  };
253
275
 
254
276
  // src/headers/index.ts
255
- var createL1Headers = async (signer, chainId, nonce, timestamp) => {
277
+ var createL1Headers = async (signer, chainId, nonce, timestamp, address) => {
256
278
  let ts = Math.floor(Date.now() / 1e3);
257
279
  if (timestamp !== void 0) {
258
280
  ts = timestamp;
@@ -261,10 +283,10 @@ var createL1Headers = async (signer, chainId, nonce, timestamp) => {
261
283
  if (nonce !== void 0) {
262
284
  n = nonce;
263
285
  }
264
- const sig = await buildClobEip712Signature(signer, chainId, ts, n);
265
- const address = await getSignerAddress(signer);
286
+ const sig = await buildClobEip712Signature(signer, chainId, ts, n, address);
287
+ const resolvedAddress = address ?? await getSignerAddress(signer);
266
288
  const headers = {
267
- POLY_ADDRESS: address,
289
+ POLY_ADDRESS: resolvedAddress,
268
290
  POLY_SIGNATURE: sig,
269
291
  POLY_TIMESTAMP: `${ts}`,
270
292
  POLY_NONCE: `${n}`
@@ -719,22 +741,51 @@ var SignatureTypeV2 = /* @__PURE__ */ ((SignatureTypeV22) => {
719
741
  })(SignatureTypeV2 || {});
720
742
 
721
743
  // src/order-utils/exchangeOrderBuilderV2.ts
744
+ var ORDER_TYPE_STRING = "Order(uint256 salt,address maker,address signer,uint256 tokenId,uint256 makerAmount,uint256 takerAmount,uint8 side,uint8 signatureType,uint256 timestamp,bytes32 metadata,bytes32 builder)";
745
+ var ORDER_TYPE_HASH = (0, import_viem3.keccak256)((0, import_viem3.toHex)(ORDER_TYPE_STRING));
746
+ var DOMAIN_TYPE_HASH = (0, import_viem3.keccak256)(
747
+ (0, import_viem3.toHex)("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
748
+ );
749
+ var TYPED_DATA_SIGN_STRUCT = [
750
+ { name: "contents", type: "Order" },
751
+ { name: "name", type: "string" },
752
+ { name: "version", type: "string" },
753
+ { name: "chainId", type: "uint256" },
754
+ { name: "verifyingContract", type: "address" },
755
+ { name: "salt", type: "bytes32" }
756
+ ];
757
+ var CTF_EXCHANGE_NAME_HASH = (0, import_viem3.keccak256)((0, import_viem3.toHex)(CTF_EXCHANGE_V2_DOMAIN_NAME));
758
+ var CTF_EXCHANGE_VERSION_HASH = (0, import_viem3.keccak256)((0, import_viem3.toHex)(CTF_EXCHANGE_V2_DOMAIN_VERSION));
722
759
  var ExchangeOrderBuilderV2 = class {
723
760
  constructor(contractAddress, chainId, signer, generateSalt = generateOrderSalt) {
724
761
  this.contractAddress = contractAddress;
725
762
  this.chainId = chainId;
726
763
  this.signer = signer;
727
764
  this.generateSalt = generateSalt;
765
+ this.appDomainSep = (0, import_viem3.keccak256)(
766
+ (0, import_viem3.encodeAbiParameters)(
767
+ [
768
+ { type: "bytes32" },
769
+ { type: "bytes32" },
770
+ { type: "bytes32" },
771
+ { type: "uint256" },
772
+ { type: "address" }
773
+ ],
774
+ [
775
+ DOMAIN_TYPE_HASH,
776
+ CTF_EXCHANGE_NAME_HASH,
777
+ CTF_EXCHANGE_VERSION_HASH,
778
+ BigInt(chainId),
779
+ contractAddress
780
+ ]
781
+ )
782
+ );
728
783
  }
729
784
  contractAddress;
730
785
  chainId;
731
786
  signer;
732
787
  generateSalt;
733
- /**
734
- * build an order object including the signature.
735
- * @param orderData
736
- * @returns a SignedOrder object (order + signature)
737
- */
788
+ appDomainSep;
738
789
  async buildSignedOrder(orderData) {
739
790
  const order = await this.buildOrder(orderData);
740
791
  const orderTypedData = this.buildOrderTypedData(order);
@@ -744,11 +795,6 @@ var ExchangeOrderBuilderV2 = class {
744
795
  signature: orderSignature
745
796
  };
746
797
  }
747
- /**
748
- * Creates an Order object from order data.
749
- * @param OrderData
750
- * @returns a Order object (not signed)
751
- */
752
798
  async buildOrder({
753
799
  maker,
754
800
  tokenId,
@@ -765,9 +811,11 @@ var ExchangeOrderBuilderV2 = class {
765
811
  if (!signer) {
766
812
  signer = maker;
767
813
  }
768
- const signerAddress = await getSignerAddress(this.signer);
769
- if (signer !== signerAddress) {
770
- throw new Error("signer does not match");
814
+ if (signatureType !== 3 /* POLY_1271 */) {
815
+ const signerAddress = await getSignerAddress(this.signer);
816
+ if (signer !== signerAddress) {
817
+ throw new Error("signer does not match");
818
+ }
771
819
  }
772
820
  return {
773
821
  salt: this.generateSalt(),
@@ -784,11 +832,6 @@ var ExchangeOrderBuilderV2 = class {
784
832
  expiration: expiration ?? "0"
785
833
  };
786
834
  }
787
- /**
788
- * Parses an Order object to EIP712 typed data
789
- * @param order
790
- * @returns a EIP712TypedData object
791
- */
792
835
  buildOrderTypedData(order) {
793
836
  return {
794
837
  primaryType: "Order",
@@ -817,29 +860,78 @@ var ExchangeOrderBuilderV2 = class {
817
860
  }
818
861
  };
819
862
  }
820
- /**
821
- * Generates order's signature from a EIP712TypedData object + the signer address
822
- * @param typedData
823
- * @returns a OrderSignature that is an string
824
- */
825
- buildOrderSignature(typedData) {
863
+ async buildOrderSignature(typedData) {
826
864
  delete typedData.types.EIP712Domain;
827
- return signTypedDataWithSigner({
865
+ const msg = typedData.message;
866
+ if (msg.signatureType !== 3 /* POLY_1271 */) {
867
+ return signTypedDataWithSigner({
868
+ signer: this.signer,
869
+ domain: typedData.domain,
870
+ types: typedData.types,
871
+ value: typedData.message,
872
+ primaryType: typedData.primaryType
873
+ });
874
+ }
875
+ const contentsHash = (0, import_viem3.keccak256)(
876
+ (0, import_viem3.encodeAbiParameters)(
877
+ [
878
+ { type: "bytes32" },
879
+ { type: "uint256" },
880
+ { type: "address" },
881
+ { type: "address" },
882
+ { type: "uint256" },
883
+ { type: "uint256" },
884
+ { type: "uint256" },
885
+ { type: "uint8" },
886
+ { type: "uint8" },
887
+ { type: "uint256" },
888
+ { type: "bytes32" },
889
+ { type: "bytes32" }
890
+ ],
891
+ [
892
+ ORDER_TYPE_HASH,
893
+ BigInt(msg.salt),
894
+ msg.maker,
895
+ msg.signer,
896
+ BigInt(msg.tokenId),
897
+ BigInt(msg.makerAmount),
898
+ BigInt(msg.takerAmount),
899
+ msg.side,
900
+ msg.signatureType,
901
+ BigInt(msg.timestamp),
902
+ msg.metadata,
903
+ msg.builder
904
+ ]
905
+ )
906
+ );
907
+ const innerSig = await signTypedDataWithSigner({
828
908
  signer: this.signer,
829
- domain: typedData.domain,
830
- types: typedData.types,
831
- value: typedData.message,
832
- primaryType: typedData.primaryType
909
+ domain: {
910
+ name: CTF_EXCHANGE_V2_DOMAIN_NAME,
911
+ version: CTF_EXCHANGE_V2_DOMAIN_VERSION,
912
+ chainId: this.chainId,
913
+ verifyingContract: this.contractAddress
914
+ },
915
+ types: {
916
+ TypedDataSign: TYPED_DATA_SIGN_STRUCT,
917
+ Order: CTF_EXCHANGE_V2_ORDER_STRUCT
918
+ },
919
+ primaryType: "TypedDataSign",
920
+ value: {
921
+ contents: typedData.message,
922
+ name: "DepositWallet",
923
+ version: "1",
924
+ chainId: this.chainId,
925
+ verifyingContract: msg.signer,
926
+ salt: bytes32Zero
927
+ }
833
928
  });
929
+ const ctLen = ORDER_TYPE_STRING.length;
930
+ const lenHex = ctLen.toString(16).padStart(4, "0");
931
+ return `0x${innerSig.slice(2)}${this.appDomainSep.slice(2)}${contentsHash.slice(2)}${(0, import_viem3.toHex)(ORDER_TYPE_STRING).slice(2)}${lenHex}`;
834
932
  }
835
- /**
836
- * Generates the hash of the order from a EIP712TypedData object.
837
- * @param orderTypedData
838
- * @returns a OrderHash that is an string
839
- */
840
933
  buildOrderHash(orderTypedData) {
841
- const digest = (0, import_viem3.hashTypedData)(orderTypedData);
842
- return digest;
934
+ return (0, import_viem3.hashTypedData)(orderTypedData);
843
935
  }
844
936
  };
845
937
 
@@ -1143,9 +1235,10 @@ var ROUNDING_CONFIG = {
1143
1235
  var createMarketOrder = async (eoaSigner, chainId, signatureType, funderAddress, userMarketOrder, options, version) => {
1144
1236
  const eoaSignerAddress = await getSignerAddress(eoaSigner);
1145
1237
  const maker = funderAddress === void 0 ? eoaSignerAddress : funderAddress;
1238
+ const signerForOrder = signatureType === 3 /* POLY_1271 */ ? maker : eoaSignerAddress;
1146
1239
  const contractConfig = getContractConfig(chainId);
1147
1240
  const orderData = await buildMarketOrderCreationArgs(
1148
- eoaSignerAddress,
1241
+ signerForOrder,
1149
1242
  maker,
1150
1243
  signatureType,
1151
1244
  userMarketOrder,
@@ -1173,9 +1266,10 @@ var createMarketOrder = async (eoaSigner, chainId, signatureType, funderAddress,
1173
1266
  var createOrder = async (eoaSigner, chainId, signatureType, funderAddress, userOrder, options, version) => {
1174
1267
  const eoaSignerAddress = await getSignerAddress(eoaSigner);
1175
1268
  const maker = funderAddress === void 0 ? eoaSignerAddress : funderAddress;
1269
+ const signerForOrder = signatureType === 3 /* POLY_1271 */ ? maker : eoaSignerAddress;
1176
1270
  const contractConfig = getContractConfig(chainId);
1177
1271
  const orderData = await buildOrderCreationArgs(
1178
- eoaSignerAddress,
1272
+ signerForOrder,
1179
1273
  maker,
1180
1274
  signatureType,
1181
1275
  userOrder,
@@ -1300,15 +1394,6 @@ var OrderBuilder = class {
1300
1394
  };
1301
1395
 
1302
1396
  // src/client.ts
1303
- function adjustBuyAmountForFees(amount, price, userUSDCBalance, feeRate, feeExponent, builderTakerFeeRate) {
1304
- const platformFeeRate = feeRate * (price * (1 - price)) ** feeExponent;
1305
- const platformFee = amount / price * platformFeeRate;
1306
- const totalCost = amount + platformFee + amount * builderTakerFeeRate;
1307
- if (userUSDCBalance <= totalCost) {
1308
- return userUSDCBalance / (1 + platformFeeRate / price + builderTakerFeeRate);
1309
- }
1310
- return amount;
1311
- }
1312
1397
  var ClobClient = class {
1313
1398
  host;
1314
1399
  chainId;
@@ -1326,9 +1411,12 @@ var ClobClient = class {
1326
1411
  tokenConditionMap;
1327
1412
  useServerTime;
1328
1413
  builderConfig;
1414
+ signatureType;
1415
+ funderAddress;
1329
1416
  cachedVersion;
1330
1417
  retryOnError;
1331
1418
  throwOnError;
1419
+ feeSlippage;
1332
1420
  constructor({
1333
1421
  host,
1334
1422
  chain,
@@ -1340,7 +1428,8 @@ var ClobClient = class {
1340
1428
  builderConfig,
1341
1429
  getSigner,
1342
1430
  retryOnError,
1343
- throwOnError
1431
+ throwOnError,
1432
+ feeSlippage
1344
1433
  }) {
1345
1434
  this.host = host.endsWith("/") ? host.slice(0, -1) : host;
1346
1435
  this.chainId = chain;
@@ -1357,6 +1446,8 @@ var ClobClient = class {
1357
1446
  funderAddress,
1358
1447
  getSigner
1359
1448
  );
1449
+ this.signatureType = signatureType ?? 0 /* EOA */;
1450
+ this.funderAddress = funderAddress;
1360
1451
  this.tickSizes = {};
1361
1452
  this.negRisk = {};
1362
1453
  this.feeRates = {};
@@ -1365,6 +1456,8 @@ var ClobClient = class {
1365
1456
  this.tokenConditionMap = {};
1366
1457
  this.retryOnError = retryOnError;
1367
1458
  this.throwOnError = throwOnError;
1459
+ this.feeSlippage = feeSlippage ?? 0;
1460
+ validateFeeSlippage(this.feeSlippage);
1368
1461
  this.useServerTime = useServerTime;
1369
1462
  if (builderConfig !== void 0) {
1370
1463
  this.builderConfig = builderConfig;
@@ -1862,8 +1955,19 @@ var ClobClient = class {
1862
1955
  `invalid price (${orderToSign.price}), min: ${parseFloat(tickSize)} - max: ${1 - parseFloat(tickSize)}`
1863
1956
  );
1864
1957
  }
1865
- const negRisk = options?.negRisk ?? await this.getNegRisk(tokenID);
1958
+ orderToSign.price = roundNormal(orderToSign.price, ROUNDING_CONFIG[tickSize].price);
1866
1959
  const version = await this.resolveVersion();
1960
+ if (version === 2 && orderToSign.side === "BUY" /* BUY */ && "userUSDCBalance" in orderToSign && orderToSign.userUSDCBalance !== void 0) {
1961
+ const adjustedAmount = await this.adjustBuyAmountForBalance(
1962
+ tokenID,
1963
+ orderToSign.size * orderToSign.price,
1964
+ orderToSign.price,
1965
+ orderToSign.userUSDCBalance,
1966
+ orderToSign.builderCode
1967
+ );
1968
+ orderToSign.size = adjustedAmount / orderToSign.price;
1969
+ }
1970
+ const negRisk = options?.negRisk ?? await this.getNegRisk(tokenID);
1867
1971
  if (version === 1) {
1868
1972
  const userFeeRateBps = "feeRateBps" in orderToSign ? orderToSign.feeRateBps : void 0;
1869
1973
  const feeRateBps = await this._resolveFeeRateBps(tokenID, userFeeRateBps);
@@ -1903,15 +2007,12 @@ var ClobClient = class {
1903
2007
  await this.ensureBuilderFeeRateCached(orderToSign.builderCode);
1904
2008
  if (orderToSign.side === "BUY" /* BUY */ && "userUSDCBalance" in orderToSign && orderToSign.userUSDCBalance !== void 0) {
1905
2009
  const price = orderToSign.price;
1906
- const { userUSDCBalance } = orderToSign;
1907
- const builderTakerFeeRate = this.isBuilderOrder(orderToSign.builderCode) ? this.builderFeeRates[orderToSign.builderCode ?? ""]?.taker ?? 0 : 0;
1908
- orderToSign.amount = adjustBuyAmountForFees(
2010
+ orderToSign.amount = await this.adjustBuyAmountForBalance(
2011
+ tokenID,
1909
2012
  orderToSign.amount,
1910
2013
  price,
1911
- userUSDCBalance,
1912
- this.feeInfos[tokenID].rate,
1913
- this.feeInfos[tokenID].exponent,
1914
- builderTakerFeeRate
2014
+ orderToSign.userUSDCBalance,
2015
+ orderToSign.builderCode
1915
2016
  );
1916
2017
  }
1917
2018
  const negRisk = options?.negRisk ?? await this.getNegRisk(tokenID);
@@ -2371,6 +2472,24 @@ var ClobClient = class {
2371
2472
  isBuilderOrder(builderCode) {
2372
2473
  return builderCode !== void 0 && builderCode !== bytes32Zero;
2373
2474
  }
2475
+ async getBuilderTakerFeeRate(builderCode) {
2476
+ if (!this.isBuilderOrder(builderCode)) return 0;
2477
+ await this.ensureBuilderFeeRateCached(builderCode);
2478
+ return this.builderFeeRates[builderCode ?? ""]?.taker ?? 0;
2479
+ }
2480
+ async adjustBuyAmountForBalance(tokenID, amount, price, userUSDCBalance, builderCode) {
2481
+ await this._ensureMarketInfoCached(tokenID);
2482
+ const builderTakerFeeRate = await this.getBuilderTakerFeeRate(builderCode);
2483
+ return adjustBuyAmountForFees(
2484
+ amount,
2485
+ price,
2486
+ userUSDCBalance,
2487
+ this.feeInfos[tokenID].rate,
2488
+ this.feeInfos[tokenID].exponent,
2489
+ builderTakerFeeRate,
2490
+ this.feeSlippage
2491
+ );
2492
+ }
2374
2493
  async _ensureMarketInfoCached(tokenID) {
2375
2494
  if (tokenID in this.feeInfos) return;
2376
2495
  if (!(tokenID in this.tokenConditionMap)) {