pmxtjs 2.49.10 → 2.50.2
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/esm/generated/src/apis/DefaultApi.d.ts +28 -0
- package/dist/esm/generated/src/apis/DefaultApi.js +28 -0
- package/dist/esm/index.d.ts +4 -3
- package/dist/esm/index.js +4 -3
- package/dist/esm/pmxt/client.d.ts +37 -25
- package/dist/esm/pmxt/client.js +117 -202
- package/dist/esm/pmxt/constants.d.ts +4 -0
- package/dist/esm/pmxt/constants.js +6 -0
- package/dist/esm/pmxt/hosted-routing.js +1 -0
- package/dist/esm/pmxt/hosted-typed-data.d.ts +1 -1
- package/dist/esm/pmxt/hosted-typed-data.js +53 -12
- package/dist/esm/pmxt/models.d.ts +1 -0
- package/dist/esm/pmxt/router.js +12 -3
- package/dist/esm/pmxt/signers.d.ts +7 -0
- package/dist/esm/pmxt/signers.js +30 -8
- package/dist/generated/src/apis/DefaultApi.d.ts +28 -0
- package/dist/generated/src/apis/DefaultApi.js +28 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.js +6 -1
- package/dist/pmxt/client.d.ts +37 -25
- package/dist/pmxt/client.js +116 -200
- package/dist/pmxt/constants.d.ts +4 -0
- package/dist/pmxt/constants.js +7 -1
- package/dist/pmxt/hosted-routing.js +1 -0
- package/dist/pmxt/hosted-typed-data.d.ts +1 -1
- package/dist/pmxt/hosted-typed-data.js +53 -12
- package/dist/pmxt/models.d.ts +1 -0
- package/dist/pmxt/router.js +12 -3
- package/dist/pmxt/signers.d.ts +7 -0
- package/dist/pmxt/signers.js +31 -8
- package/generated/docs/DefaultApi.md +56 -56
- package/generated/package.json +1 -1
- package/generated/src/apis/DefaultApi.ts +28 -0
- package/index.ts +12 -3
- package/package.json +2 -2
- package/pmxt/client.ts +123 -219
- package/pmxt/constants.ts +7 -0
- package/pmxt/hosted-routing.ts +1 -0
- package/pmxt/hosted-typed-data.ts +67 -17
- package/pmxt/models.ts +3 -0
- package/pmxt/router.ts +14 -3
- package/pmxt/signers.ts +28 -7
package/dist/esm/pmxt/client.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { Configuration, DefaultApi, } from "../generated/src/index.js";
|
|
8
8
|
import { MarketList, } from "./models.js";
|
|
9
9
|
import { ServerManager } from "./server-manager.js";
|
|
10
|
-
import { PmxtError, fromServerError, InvalidOrder
|
|
10
|
+
import { PmxtError, fromServerError, InvalidOrder } from "./errors.js";
|
|
11
11
|
import { resolvePmxtBaseUrl } from "./constants.js";
|
|
12
12
|
import { SidecarWsClient } from "./ws-client.js";
|
|
13
13
|
import { logger } from "./logger.js";
|
|
@@ -17,8 +17,8 @@ import { logger } from "./logger.js";
|
|
|
17
17
|
// import names below are the conventional ones from the plan. Cross-module
|
|
18
18
|
// "Cannot find module" errors during the parallel landing window resolve
|
|
19
19
|
// once the matching files exist.
|
|
20
|
-
import { HOSTED_TRADING_VENUES, _tradingRequest,
|
|
21
|
-
import { orderFromV0,
|
|
20
|
+
import { HOSTED_TRADING_VENUES, _tradingRequest, HOSTED_METHOD_ROUTES, } from "./hosted-routing.js";
|
|
21
|
+
import { orderFromV0, to6dec, } from "./hosted-mappers.js";
|
|
22
22
|
import { validateTypedData, validateEconomics, verifySignature, } from "./hosted-typed-data.js";
|
|
23
23
|
import { EthersSigner } from "./signers.js";
|
|
24
24
|
import { Escrow } from "./escrow.js";
|
|
@@ -921,9 +921,6 @@ export class Exchange {
|
|
|
921
921
|
}
|
|
922
922
|
}
|
|
923
923
|
async submitOrder(built) {
|
|
924
|
-
if (this.isHostedTradingMode()) {
|
|
925
|
-
return this._hostedSubmitOrder(built);
|
|
926
|
-
}
|
|
927
924
|
await this.initPromise;
|
|
928
925
|
if (this.isHosted) {
|
|
929
926
|
throw new PmxtError("submitOrder is not available in hosted mode. Use createOrder instead.");
|
|
@@ -953,80 +950,7 @@ export class Exchange {
|
|
|
953
950
|
throw new PmxtError(`Failed to submitOrder: ${error}`);
|
|
954
951
|
}
|
|
955
952
|
}
|
|
956
|
-
/**
|
|
957
|
-
* Hosted-mode submitOrder: validate the stored build response, sign the
|
|
958
|
-
* typed_data (and pull_typed_data for Opinion cross-chain sells), then
|
|
959
|
-
* POST to `/v0/trade/submit-order`.
|
|
960
|
-
*/
|
|
961
|
-
async _hostedSubmitOrder(built) {
|
|
962
|
-
const signer = this.requireHostedSigner();
|
|
963
|
-
if (!this.walletAddress) {
|
|
964
|
-
throw new MissingWalletAddress("hosted submitOrder requires walletAddress");
|
|
965
|
-
}
|
|
966
|
-
// BuiltOrder is the SDK-side wrapper around the build response —
|
|
967
|
-
// expect typed_data, optional pull_typed_data, built_order_id, and
|
|
968
|
-
// the originating build_request to be present.
|
|
969
|
-
const payload = built;
|
|
970
|
-
const typedData = payload["typed_data"];
|
|
971
|
-
if (!typedData) {
|
|
972
|
-
throw new HostedInvalidSignature(0, "typed_data missing from built order");
|
|
973
|
-
}
|
|
974
|
-
const buildRequest = payload["build_request"]
|
|
975
|
-
?? payload["params"]?.["build_request"];
|
|
976
|
-
const side = String(buildRequest?.["side"] ?? "buy");
|
|
977
|
-
const primaryRoute = this._hostedTypedDataRoute(side, false);
|
|
978
|
-
// Layer 1: schema, Layer 2: economics.
|
|
979
|
-
validateTypedData(typedData, primaryRoute, this.walletAddress);
|
|
980
|
-
if (buildRequest) {
|
|
981
|
-
validateEconomics(typedData, primaryRoute, buildRequest, payload);
|
|
982
|
-
}
|
|
983
|
-
const signature = await signer.signTypedData(typedData);
|
|
984
|
-
// Layer 3: post-sign recovery + canonical check.
|
|
985
|
-
verifySignature(typedData, signature, signer.address);
|
|
986
|
-
const body = {
|
|
987
|
-
built_order_id: payload["built_order_id"],
|
|
988
|
-
signature,
|
|
989
|
-
};
|
|
990
|
-
const pullTypedData = payload["pull_typed_data"];
|
|
991
|
-
if (pullTypedData) {
|
|
992
|
-
const pullRoute = this._hostedTypedDataRoute(side, true);
|
|
993
|
-
if (pullRoute) {
|
|
994
|
-
validateTypedData(pullTypedData, pullRoute, this.walletAddress);
|
|
995
|
-
}
|
|
996
|
-
const pullSig = await signer.signTypedData(pullTypedData);
|
|
997
|
-
verifySignature(pullTypedData, pullSig, signer.address);
|
|
998
|
-
body["pull_signature"] = pullSig;
|
|
999
|
-
}
|
|
1000
|
-
const route = HOSTED_METHOD_ROUTES.get("submitOrder");
|
|
1001
|
-
const data = await _tradingRequest(this, { method: route.method, path: route.path, body });
|
|
1002
|
-
return orderFromV0(data);
|
|
1003
|
-
}
|
|
1004
|
-
/**
|
|
1005
|
-
* Resolve the per-(venue, side, pull) typed-data schema route used by
|
|
1006
|
-
* `validateTypedData` / `validateEconomics`. Returns undefined for the
|
|
1007
|
-
* pull leg when a venue/side combo doesn't have one.
|
|
1008
|
-
*/
|
|
1009
|
-
_hostedTypedDataRoute(side, isPull) {
|
|
1010
|
-
const venue = this.exchangeName;
|
|
1011
|
-
const sideLower = side.toLowerCase();
|
|
1012
|
-
if (venue === "polymarket") {
|
|
1013
|
-
return sideLower === "sell" ? "polymarket_sell" : "polymarket_buy";
|
|
1014
|
-
}
|
|
1015
|
-
// opinion
|
|
1016
|
-
if (sideLower === "buy")
|
|
1017
|
-
return "opinion_buy";
|
|
1018
|
-
// sell — polygon, or BSC pull leg for cross-chain
|
|
1019
|
-
return isPull ? "opinion_sell_bsc_pull" : "opinion_sell_polygon";
|
|
1020
|
-
}
|
|
1021
|
-
_hostedCancelTypedDataRoute(isPull) {
|
|
1022
|
-
if (this.exchangeName === "polymarket")
|
|
1023
|
-
return "cancel_polymarket";
|
|
1024
|
-
return isPull ? "cancel_opinion_bsc_pull" : "cancel_opinion_polygon";
|
|
1025
|
-
}
|
|
1026
953
|
async cancelOrder(orderId) {
|
|
1027
|
-
if (this.isHostedTradingMode()) {
|
|
1028
|
-
return this._hostedCancelOrder(orderId);
|
|
1029
|
-
}
|
|
1030
954
|
await this.initPromise;
|
|
1031
955
|
try {
|
|
1032
956
|
const args = [];
|
|
@@ -1053,47 +977,7 @@ export class Exchange {
|
|
|
1053
977
|
throw new PmxtError(`Failed to cancelOrder: ${error}`);
|
|
1054
978
|
}
|
|
1055
979
|
}
|
|
1056
|
-
/**
|
|
1057
|
-
* Hosted-mode cancelOrder: build the cancel typed_data on the server,
|
|
1058
|
-
* validate + sign (dual-sign for Opinion cross-chain), then submit.
|
|
1059
|
-
*/
|
|
1060
|
-
async _hostedCancelOrder(orderId) {
|
|
1061
|
-
const signer = this.requireHostedSigner();
|
|
1062
|
-
if (!this.walletAddress) {
|
|
1063
|
-
throw new MissingWalletAddress("hosted cancelOrder requires walletAddress");
|
|
1064
|
-
}
|
|
1065
|
-
const buildRoute = HOSTED_METHOD_ROUTES.get("cancelOrderBuild");
|
|
1066
|
-
const buildResp = await _tradingRequest(this, {
|
|
1067
|
-
method: buildRoute.method,
|
|
1068
|
-
path: buildRoute.path,
|
|
1069
|
-
body: { order_id: orderId },
|
|
1070
|
-
});
|
|
1071
|
-
const typedData = buildResp["typed_data"];
|
|
1072
|
-
if (!typedData) {
|
|
1073
|
-
throw new HostedInvalidSignature(0, "typed_data missing from cancel build response");
|
|
1074
|
-
}
|
|
1075
|
-
validateTypedData(typedData, this._hostedCancelTypedDataRoute(false), this.walletAddress);
|
|
1076
|
-
const signature = await signer.signTypedData(typedData);
|
|
1077
|
-
verifySignature(typedData, signature, signer.address);
|
|
1078
|
-
const body = {
|
|
1079
|
-
cancel_id: buildResp["cancel_id"],
|
|
1080
|
-
signature,
|
|
1081
|
-
};
|
|
1082
|
-
const pullTypedData = buildResp["pull_typed_data"];
|
|
1083
|
-
if (pullTypedData) {
|
|
1084
|
-
validateTypedData(pullTypedData, this._hostedCancelTypedDataRoute(true), this.walletAddress);
|
|
1085
|
-
const pullSig = await signer.signTypedData(pullTypedData);
|
|
1086
|
-
verifySignature(pullTypedData, pullSig, signer.address);
|
|
1087
|
-
body["pull_signature"] = pullSig;
|
|
1088
|
-
}
|
|
1089
|
-
const route = HOSTED_METHOD_ROUTES.get("cancelOrder");
|
|
1090
|
-
const data = await _tradingRequest(this, { method: route.method, path: route.path, body });
|
|
1091
|
-
return orderFromV0(data);
|
|
1092
|
-
}
|
|
1093
980
|
async fetchOrder(orderId) {
|
|
1094
|
-
if (this.isHostedTradingMode()) {
|
|
1095
|
-
return this._hostedFetchOrder(orderId);
|
|
1096
|
-
}
|
|
1097
981
|
await this.initPromise;
|
|
1098
982
|
try {
|
|
1099
983
|
const args = [];
|
|
@@ -1120,16 +1004,7 @@ export class Exchange {
|
|
|
1120
1004
|
throw new PmxtError(`Failed to fetchOrder: ${error}`);
|
|
1121
1005
|
}
|
|
1122
1006
|
}
|
|
1123
|
-
async _hostedFetchOrder(orderId) {
|
|
1124
|
-
const route = HOSTED_METHOD_ROUTES.get("fetchOrder");
|
|
1125
|
-
const path = formatRoutePath(route, { order_id: orderId });
|
|
1126
|
-
const data = await _tradingRequest(this, { method: route.method, path });
|
|
1127
|
-
return orderFromV0(data);
|
|
1128
|
-
}
|
|
1129
1007
|
async fetchOpenOrders(marketId) {
|
|
1130
|
-
if (this.isHostedTradingMode()) {
|
|
1131
|
-
return this._hostedFetchOpenOrders(marketId);
|
|
1132
|
-
}
|
|
1133
1008
|
await this.initPromise;
|
|
1134
1009
|
try {
|
|
1135
1010
|
const args = [];
|
|
@@ -1157,24 +1032,7 @@ export class Exchange {
|
|
|
1157
1032
|
throw new PmxtError(`Failed to fetchOpenOrders: ${error}`);
|
|
1158
1033
|
}
|
|
1159
1034
|
}
|
|
1160
|
-
async _hostedFetchOpenOrders(marketId) {
|
|
1161
|
-
const address = resolveWalletAddress(this, undefined);
|
|
1162
|
-
const route = HOSTED_METHOD_ROUTES.get("fetchOpenOrders");
|
|
1163
|
-
const params = { address };
|
|
1164
|
-
if (marketId !== undefined)
|
|
1165
|
-
params["market_id"] = marketId;
|
|
1166
|
-
const data = await _tradingRequest(this, {
|
|
1167
|
-
method: route.method,
|
|
1168
|
-
path: route.path,
|
|
1169
|
-
params,
|
|
1170
|
-
});
|
|
1171
|
-
const items = (Array.isArray(data) ? data : data?.["orders"] ?? []);
|
|
1172
|
-
return items.map(orderFromV0);
|
|
1173
|
-
}
|
|
1174
1035
|
async fetchMyTrades(params) {
|
|
1175
|
-
if (this.isHostedTradingMode()) {
|
|
1176
|
-
return this._hostedFetchMyTrades(params);
|
|
1177
|
-
}
|
|
1178
1036
|
await this.initPromise;
|
|
1179
1037
|
try {
|
|
1180
1038
|
const args = [];
|
|
@@ -1202,35 +1060,7 @@ export class Exchange {
|
|
|
1202
1060
|
throw new PmxtError(`Failed to fetchMyTrades: ${error}`);
|
|
1203
1061
|
}
|
|
1204
1062
|
}
|
|
1205
|
-
async _hostedFetchMyTrades(params) {
|
|
1206
|
-
const address = resolveWalletAddress(this, undefined);
|
|
1207
|
-
const route = HOSTED_METHOD_ROUTES.get("fetchMyTrades");
|
|
1208
|
-
const path = formatRoutePath(route, { address });
|
|
1209
|
-
const q = {};
|
|
1210
|
-
if (params?.marketId)
|
|
1211
|
-
q["market_id"] = params.marketId;
|
|
1212
|
-
if (params?.outcomeId)
|
|
1213
|
-
q["outcome_id"] = params.outcomeId;
|
|
1214
|
-
if (params?.limit !== undefined)
|
|
1215
|
-
q["limit"] = String(params.limit);
|
|
1216
|
-
if (params?.cursor)
|
|
1217
|
-
q["cursor"] = params.cursor;
|
|
1218
|
-
if (params?.since)
|
|
1219
|
-
q["since"] = String(params.since.getTime());
|
|
1220
|
-
if (params?.until)
|
|
1221
|
-
q["until"] = String(params.until.getTime());
|
|
1222
|
-
const data = await _tradingRequest(this, {
|
|
1223
|
-
method: route.method,
|
|
1224
|
-
path,
|
|
1225
|
-
params: Object.keys(q).length ? q : undefined,
|
|
1226
|
-
});
|
|
1227
|
-
const items = (Array.isArray(data) ? data : data?.["trades"] ?? []);
|
|
1228
|
-
return items.map(userTradeFromV0);
|
|
1229
|
-
}
|
|
1230
1063
|
async fetchClosedOrders(params) {
|
|
1231
|
-
if (this.isHostedTradingMode()) {
|
|
1232
|
-
throw new NotSupported("Settled orders are modeled as trades — use fetchMyTrades().");
|
|
1233
|
-
}
|
|
1234
1064
|
await this.initPromise;
|
|
1235
1065
|
try {
|
|
1236
1066
|
const args = [];
|
|
@@ -1259,9 +1089,6 @@ export class Exchange {
|
|
|
1259
1089
|
}
|
|
1260
1090
|
}
|
|
1261
1091
|
async fetchAllOrders(params) {
|
|
1262
|
-
if (this.isHostedTradingMode()) {
|
|
1263
|
-
throw new NotSupported("Use fetchOpenOrders() and fetchMyTrades() separately.");
|
|
1264
|
-
}
|
|
1265
1092
|
await this.initPromise;
|
|
1266
1093
|
try {
|
|
1267
1094
|
const args = [];
|
|
@@ -1290,9 +1117,6 @@ export class Exchange {
|
|
|
1290
1117
|
}
|
|
1291
1118
|
}
|
|
1292
1119
|
async fetchPositions(address) {
|
|
1293
|
-
if (this.isHostedTradingMode()) {
|
|
1294
|
-
return this._hostedFetchPositions(address);
|
|
1295
|
-
}
|
|
1296
1120
|
await this.initPromise;
|
|
1297
1121
|
try {
|
|
1298
1122
|
const args = [];
|
|
@@ -1320,18 +1144,7 @@ export class Exchange {
|
|
|
1320
1144
|
throw new PmxtError(`Failed to fetchPositions: ${error}`);
|
|
1321
1145
|
}
|
|
1322
1146
|
}
|
|
1323
|
-
async _hostedFetchPositions(address) {
|
|
1324
|
-
const resolvedAddr = resolveWalletAddress(this, address);
|
|
1325
|
-
const route = HOSTED_METHOD_ROUTES.get("fetchPositions");
|
|
1326
|
-
const path = formatRoutePath(route, { address: resolvedAddr });
|
|
1327
|
-
const data = await _tradingRequest(this, { method: route.method, path });
|
|
1328
|
-
const items = (Array.isArray(data) ? data : data?.["positions"] ?? []);
|
|
1329
|
-
return items.map(positionFromV0);
|
|
1330
|
-
}
|
|
1331
1147
|
async fetchBalance(address) {
|
|
1332
|
-
if (this.isHostedTradingMode()) {
|
|
1333
|
-
return this._hostedFetchBalance(address);
|
|
1334
|
-
}
|
|
1335
1148
|
await this.initPromise;
|
|
1336
1149
|
try {
|
|
1337
1150
|
const args = [];
|
|
@@ -1359,18 +1172,6 @@ export class Exchange {
|
|
|
1359
1172
|
throw new PmxtError(`Failed to fetchBalance: ${error}`);
|
|
1360
1173
|
}
|
|
1361
1174
|
}
|
|
1362
|
-
async _hostedFetchBalance(address) {
|
|
1363
|
-
const resolvedAddr = resolveWalletAddress(this, address);
|
|
1364
|
-
const route = HOSTED_METHOD_ROUTES.get("fetchBalance");
|
|
1365
|
-
const path = formatRoutePath(route, { address: resolvedAddr });
|
|
1366
|
-
const data = await _tradingRequest(this, { method: route.method, path });
|
|
1367
|
-
// Hosted balance is a single USDC escrow record; wrap in an array
|
|
1368
|
-
// to match the existing Balance[] return shape.
|
|
1369
|
-
if (Array.isArray(data)) {
|
|
1370
|
-
return data.map(balanceFromV0);
|
|
1371
|
-
}
|
|
1372
|
-
return [balanceFromV0(data)];
|
|
1373
|
-
}
|
|
1374
1175
|
async unwatchOrderBook(outcomeId) {
|
|
1375
1176
|
await this.initPromise;
|
|
1376
1177
|
try {
|
|
@@ -2079,6 +1880,15 @@ export class Exchange {
|
|
|
2079
1880
|
if (params.fee !== undefined) {
|
|
2080
1881
|
paramsDict.fee = params.fee;
|
|
2081
1882
|
}
|
|
1883
|
+
if (params.tickSize !== undefined) {
|
|
1884
|
+
paramsDict.tickSize = params.tickSize;
|
|
1885
|
+
}
|
|
1886
|
+
if (params.negRisk !== undefined) {
|
|
1887
|
+
paramsDict.negRisk = params.negRisk;
|
|
1888
|
+
}
|
|
1889
|
+
if (params.onBehalfOf !== undefined) {
|
|
1890
|
+
paramsDict.onBehalfOf = params.onBehalfOf;
|
|
1891
|
+
}
|
|
2082
1892
|
const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/buildOrder`, {
|
|
2083
1893
|
method: 'POST',
|
|
2084
1894
|
headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
|
|
@@ -2203,6 +2013,81 @@ export class Exchange {
|
|
|
2203
2013
|
const built = await this._hostedBuildOrder(params);
|
|
2204
2014
|
return this._hostedSubmitOrder(built);
|
|
2205
2015
|
}
|
|
2016
|
+
/**
|
|
2017
|
+
* Hosted-mode submitOrder: validate the stored build response, sign the
|
|
2018
|
+
* typed_data (and pull_typed_data for cross-chain venue sells), then
|
|
2019
|
+
* POST to `/v0/trade/submit-order`.
|
|
2020
|
+
*
|
|
2021
|
+
* Restored after PR #1058 (Limitless hosted wire-up) accidentally
|
|
2022
|
+
* removed it but left the call site + signing imports intact.
|
|
2023
|
+
*/
|
|
2024
|
+
async _hostedSubmitOrder(built) {
|
|
2025
|
+
const signer = this.requireHostedSigner();
|
|
2026
|
+
if (!this.walletAddress) {
|
|
2027
|
+
throw new MissingWalletAddress("hosted submitOrder requires walletAddress");
|
|
2028
|
+
}
|
|
2029
|
+
const payload = built;
|
|
2030
|
+
const typedData = payload["typed_data"];
|
|
2031
|
+
if (!typedData) {
|
|
2032
|
+
throw new HostedInvalidSignature(0, "typed_data missing from built order");
|
|
2033
|
+
}
|
|
2034
|
+
const buildRequest = payload["build_request"]
|
|
2035
|
+
?? payload["params"]?.["build_request"];
|
|
2036
|
+
const side = String(buildRequest?.["side"] ?? "buy");
|
|
2037
|
+
const primaryRoute = this._hostedTypedDataRoute(side, false);
|
|
2038
|
+
validateTypedData(typedData, primaryRoute, this.walletAddress);
|
|
2039
|
+
if (buildRequest) {
|
|
2040
|
+
validateEconomics(typedData, primaryRoute, buildRequest, payload);
|
|
2041
|
+
}
|
|
2042
|
+
const signature = await signer.signTypedData(typedData);
|
|
2043
|
+
verifySignature(typedData, signature, signer.address);
|
|
2044
|
+
const body = {
|
|
2045
|
+
built_order_id: payload["built_order_id"],
|
|
2046
|
+
signature,
|
|
2047
|
+
};
|
|
2048
|
+
const pullTypedData = payload["pull_typed_data"];
|
|
2049
|
+
if (pullTypedData) {
|
|
2050
|
+
const pullRoute = this._hostedTypedDataRoute(side, true);
|
|
2051
|
+
if (pullRoute) {
|
|
2052
|
+
validateTypedData(pullTypedData, pullRoute, this.walletAddress);
|
|
2053
|
+
}
|
|
2054
|
+
const pullSig = await signer.signTypedData(pullTypedData);
|
|
2055
|
+
verifySignature(pullTypedData, pullSig, signer.address);
|
|
2056
|
+
body["pull_signature"] = pullSig;
|
|
2057
|
+
}
|
|
2058
|
+
const route = HOSTED_METHOD_ROUTES.get("submitOrder");
|
|
2059
|
+
const data = await _tradingRequest(this, { method: route.method, path: route.path, body });
|
|
2060
|
+
return orderFromV0(data);
|
|
2061
|
+
}
|
|
2062
|
+
/**
|
|
2063
|
+
* Resolve the per-(venue, side, pull) typed-data schema route used by
|
|
2064
|
+
* `validateTypedData` / `validateEconomics`. Returns the cross-chain
|
|
2065
|
+
* pull-leg route name for Opinion sells and Limitless cross-chain orders.
|
|
2066
|
+
*/
|
|
2067
|
+
_hostedTypedDataRoute(side, isPull) {
|
|
2068
|
+
const venue = this.exchangeName;
|
|
2069
|
+
const sideLower = side.toLowerCase();
|
|
2070
|
+
if (venue === "polymarket") {
|
|
2071
|
+
return sideLower === "sell" ? "polymarket_sell" : "polymarket_buy";
|
|
2072
|
+
}
|
|
2073
|
+
if (venue === "limitless") {
|
|
2074
|
+
if (sideLower === "buy")
|
|
2075
|
+
return "limitless_buy";
|
|
2076
|
+
return isPull ? "limitless_sell_base_pull" : "limitless_sell_polygon";
|
|
2077
|
+
}
|
|
2078
|
+
// opinion
|
|
2079
|
+
if (sideLower === "buy")
|
|
2080
|
+
return "opinion_buy";
|
|
2081
|
+
return isPull ? "opinion_sell_bsc_pull" : "opinion_sell_polygon";
|
|
2082
|
+
}
|
|
2083
|
+
_hostedCancelTypedDataRoute(isPull) {
|
|
2084
|
+
const venue = this.exchangeName;
|
|
2085
|
+
if (venue === "polymarket")
|
|
2086
|
+
return "cancel_polymarket";
|
|
2087
|
+
if (venue === "limitless")
|
|
2088
|
+
return isPull ? "cancel_limitless_base_pull" : "cancel_limitless_polygon";
|
|
2089
|
+
return isPull ? "cancel_opinion_bsc_pull" : "cancel_opinion_polygon";
|
|
2090
|
+
}
|
|
2206
2091
|
/**
|
|
2207
2092
|
* Construct the hosted build-order request body and validate inputs
|
|
2208
2093
|
* locally per the v0 contract (denom/side compatibility, > 6-decimal
|
|
@@ -2360,6 +2245,15 @@ export class Exchange {
|
|
|
2360
2245
|
if (params.fee !== undefined) {
|
|
2361
2246
|
paramsDict.fee = params.fee;
|
|
2362
2247
|
}
|
|
2248
|
+
if (params.tickSize !== undefined) {
|
|
2249
|
+
paramsDict.tickSize = params.tickSize;
|
|
2250
|
+
}
|
|
2251
|
+
if (params.negRisk !== undefined) {
|
|
2252
|
+
paramsDict.negRisk = params.negRisk;
|
|
2253
|
+
}
|
|
2254
|
+
if (params.onBehalfOf !== undefined) {
|
|
2255
|
+
paramsDict.onBehalfOf = params.onBehalfOf;
|
|
2256
|
+
}
|
|
2363
2257
|
const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/createOrder`, {
|
|
2364
2258
|
method: 'POST',
|
|
2365
2259
|
headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
|
|
@@ -2996,6 +2890,27 @@ export class SuiBets extends Exchange {
|
|
|
2996
2890
|
}
|
|
2997
2891
|
// Backwards-compatible casing alias matching the Python SDK export.
|
|
2998
2892
|
export const Suibets = SuiBets;
|
|
2893
|
+
/**
|
|
2894
|
+
* Rain exchange client.
|
|
2895
|
+
*
|
|
2896
|
+
* Rain is a permissionless AMM-plus-orderbook prediction market on Arbitrum One.
|
|
2897
|
+
* Reads are unauthenticated; trading requires an EVM privateKey.
|
|
2898
|
+
*
|
|
2899
|
+
* @example
|
|
2900
|
+
* ```typescript
|
|
2901
|
+
* const rain = new Rain();
|
|
2902
|
+
* const markets = await rain.fetchMarkets({ limit: 20 });
|
|
2903
|
+
*
|
|
2904
|
+
* // With wallet for trading
|
|
2905
|
+
* const me = new Rain({ privateKey: '0x...' });
|
|
2906
|
+
* await me.createOrder({ marketId, outcomeId, side: 'buy', type: 'market', amount: 10 });
|
|
2907
|
+
* ```
|
|
2908
|
+
*/
|
|
2909
|
+
export class Rain extends Exchange {
|
|
2910
|
+
constructor(options = {}) {
|
|
2911
|
+
super("rain", options);
|
|
2912
|
+
}
|
|
2913
|
+
}
|
|
2999
2914
|
/**
|
|
3000
2915
|
* Mock exchange client.
|
|
3001
2916
|
*
|
|
@@ -62,3 +62,7 @@ export declare const PREFUNDED_ESCROW_ADDRESSES: ReadonlySet<string>;
|
|
|
62
62
|
* as venue-owned escrows. Currently empty; populated as venues onboard.
|
|
63
63
|
*/
|
|
64
64
|
export declare const VENUE_ESCROW_ADDRESSES: ReadonlySet<string>;
|
|
65
|
+
/**
|
|
66
|
+
* Limitless VenueEscrow contract addresses on Base (chain 8453).
|
|
67
|
+
*/
|
|
68
|
+
export declare const LIMITLESS_VENUE_ESCROW_ADDRESSES: ReadonlySet<string>;
|
|
@@ -71,3 +71,9 @@ export const PREFUNDED_ESCROW_ADDRESSES = new Set([
|
|
|
71
71
|
* as venue-owned escrows. Currently empty; populated as venues onboard.
|
|
72
72
|
*/
|
|
73
73
|
export const VENUE_ESCROW_ADDRESSES = new Set();
|
|
74
|
+
/**
|
|
75
|
+
* Limitless VenueEscrow contract addresses on Base (chain 8453).
|
|
76
|
+
*/
|
|
77
|
+
export const LIMITLESS_VENUE_ESCROW_ADDRESSES = new Set([
|
|
78
|
+
"0x34c42d01aad6ded00f1a6830d90b0e9204db7855",
|
|
79
|
+
]);
|
|
@@ -19,6 +19,7 @@ export const HOSTED_TRADING_BASE_URL = "https://trade.pmxt.dev";
|
|
|
19
19
|
export const HOSTED_TRADING_VENUES = new Set([
|
|
20
20
|
"polymarket",
|
|
21
21
|
"opinion",
|
|
22
|
+
"limitless",
|
|
22
23
|
]);
|
|
23
24
|
export const HOSTED_METHOD_ROUTES = new Map([
|
|
24
25
|
["createOrder", { method: "POST", path: "/v0/trade/build-order", base: "trading" }],
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* Layer 3 uses the optional `ethers` peer dependency for typed-data verification.
|
|
10
10
|
*/
|
|
11
11
|
import { TypedData } from "./signers.js";
|
|
12
|
-
export type HostedRoute = "polymarket_buy" | "polymarket_sell" | "opinion_buy" | "opinion_sell_polygon" | "opinion_sell_bsc_pull" | "cancel_polymarket" | "cancel_opinion_polygon" | "cancel_opinion_bsc_pull";
|
|
12
|
+
export type HostedRoute = "polymarket_buy" | "polymarket_sell" | "limitless_buy" | "limitless_sell_polygon" | "limitless_sell_base_pull" | "opinion_buy" | "opinion_sell_polygon" | "opinion_sell_bsc_pull" | "cancel_polymarket" | "cancel_opinion_polygon" | "cancel_opinion_bsc_pull";
|
|
13
13
|
export declare const SECP256K1_HALF_N = 57896044618658097711785492504343953926418782139537452191302581570759080747168n;
|
|
14
14
|
/**
|
|
15
15
|
* Validate the structural shape of a typed-data payload before signing.
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { InvalidSignature } from "./hosted-errors.js";
|
|
12
12
|
import { to6dec } from "./hosted-mappers.js";
|
|
13
|
+
import { loadEthers } from "./signers.js";
|
|
13
14
|
// The constants module is updated in a parallel-agent change to add these
|
|
14
15
|
// allowlists. We import them at runtime so we don't hard-fail if the change
|
|
15
16
|
// hasn't landed yet (the imports are typed below).
|
|
@@ -87,6 +88,12 @@ const VENUE_DOMAIN = {
|
|
|
87
88
|
chainId: 56,
|
|
88
89
|
allowlistKey: "venue",
|
|
89
90
|
};
|
|
91
|
+
const LIMITLESS_VENUE_DOMAIN = {
|
|
92
|
+
name: "VenueEscrow",
|
|
93
|
+
version: "1",
|
|
94
|
+
chainId: 8453,
|
|
95
|
+
allowlistKey: "venue",
|
|
96
|
+
};
|
|
90
97
|
const SCHEMAS = {
|
|
91
98
|
polymarket_buy: {
|
|
92
99
|
primaryType: "OrderParams",
|
|
@@ -95,6 +102,27 @@ const SCHEMAS = {
|
|
|
95
102
|
messageKeys: messageKeysFromFields(ORDER_PARAMS_FIELDS),
|
|
96
103
|
walletField: "user",
|
|
97
104
|
},
|
|
105
|
+
limitless_buy: {
|
|
106
|
+
primaryType: "CrossChainOrderParams",
|
|
107
|
+
domain: PREFUNDED_DOMAIN,
|
|
108
|
+
fields: CROSS_CHAIN_ORDER_PARAMS_FIELDS,
|
|
109
|
+
messageKeys: messageKeysFromFields(CROSS_CHAIN_ORDER_PARAMS_FIELDS),
|
|
110
|
+
walletField: "user",
|
|
111
|
+
},
|
|
112
|
+
limitless_sell_polygon: {
|
|
113
|
+
primaryType: "CrossChainSellPayParams",
|
|
114
|
+
domain: PREFUNDED_DOMAIN,
|
|
115
|
+
fields: CROSS_CHAIN_SELL_PAY_PARAMS_FIELDS,
|
|
116
|
+
messageKeys: messageKeysFromFields(CROSS_CHAIN_SELL_PAY_PARAMS_FIELDS),
|
|
117
|
+
walletField: "user",
|
|
118
|
+
},
|
|
119
|
+
limitless_sell_base_pull: {
|
|
120
|
+
primaryType: "CrossChainSellPullParams",
|
|
121
|
+
domain: LIMITLESS_VENUE_DOMAIN,
|
|
122
|
+
fields: CROSS_CHAIN_SELL_PULL_PARAMS_FIELDS,
|
|
123
|
+
messageKeys: messageKeysFromFields(CROSS_CHAIN_SELL_PULL_PARAMS_FIELDS),
|
|
124
|
+
walletField: "user",
|
|
125
|
+
},
|
|
98
126
|
polymarket_sell: {
|
|
99
127
|
primaryType: "SellOrderParams",
|
|
100
128
|
domain: PREFUNDED_DOMAIN,
|
|
@@ -343,14 +371,26 @@ function validateWorstPrice(message, route, buildRequest, buildResponse) {
|
|
|
343
371
|
}
|
|
344
372
|
}
|
|
345
373
|
function validateOpinionMarketId(message, buildResponse) {
|
|
346
|
-
|
|
347
|
-
|
|
374
|
+
// The current trading-API Opinion message schema does not embed
|
|
375
|
+
// opinion_market_id — the signed economic identity is the outcome
|
|
376
|
+
// tokenId. Validate that against the build response's resolved token.
|
|
377
|
+
// The legacy opinion_market_id check only applies when the message
|
|
378
|
+
// actually carries the field (older API versions).
|
|
379
|
+
const expectedToken = firstPresent(getPath(buildResponse, "resolved", "token_id"), getPath(buildResponse, "resolved", "tokenId"), getField(buildResponse, "token_id"), getField(buildResponse, "tokenId"));
|
|
380
|
+
const actualToken = firstPresent(getField(message, "tokenId"), getField(message, "token_id"));
|
|
381
|
+
if (actualToken === MISSING)
|
|
382
|
+
economicFail("message.tokenId missing");
|
|
383
|
+
if (expectedToken !== MISSING && idValue(actualToken) !== idValue(expectedToken)) {
|
|
384
|
+
economicFail(`tokenId expected ${expectedToken} got ${actualToken}`);
|
|
385
|
+
}
|
|
386
|
+
const actualMarketId = firstPresent(getField(message, "opinion_market_id"), getField(message, "opinionMarketId"));
|
|
387
|
+
if (actualMarketId === MISSING)
|
|
388
|
+
return; // current schema: tokenId-only message
|
|
389
|
+
const expectedMarketId = firstPresent(getPath(buildResponse, "resolved", "opinion_market_id"), getPath(buildResponse, "resolved", "opinionMarketId"), getField(buildResponse, "opinion_market_id"), getField(buildResponse, "opinionMarketId"), getPath(buildResponse, "params", "opinion_market_id"), getPath(buildResponse, "params", "opinionMarketId"));
|
|
390
|
+
if (expectedMarketId === MISSING)
|
|
348
391
|
economicFail("resolved.opinion_market_id missing");
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
economicFail("message.opinion_market_id missing");
|
|
352
|
-
if (idValue(actual) !== idValue(expected)) {
|
|
353
|
-
economicFail(`opinion_market_id expected ${expected} got ${actual}`);
|
|
392
|
+
if (idValue(actualMarketId) !== idValue(expectedMarketId)) {
|
|
393
|
+
economicFail(`opinion_market_id expected ${expectedMarketId} got ${actualMarketId}`);
|
|
354
394
|
}
|
|
355
395
|
}
|
|
356
396
|
// ---------------------------------------------------------------------------
|
|
@@ -388,11 +428,10 @@ export function verifySignature(typedData, signature, walletAddress) {
|
|
|
388
428
|
}
|
|
389
429
|
let ethers;
|
|
390
430
|
try {
|
|
391
|
-
|
|
392
|
-
ethers = require("ethers");
|
|
431
|
+
ethers = loadEthers("ethers is required for hosted signature verification");
|
|
393
432
|
}
|
|
394
|
-
catch {
|
|
395
|
-
throw new InvalidSignature(0, "ethers is required for hosted signature verification");
|
|
433
|
+
catch (err) {
|
|
434
|
+
throw new InvalidSignature(0, err instanceof Error ? err.message : "ethers is required for hosted signature verification");
|
|
396
435
|
}
|
|
397
436
|
let recovered;
|
|
398
437
|
try {
|
|
@@ -475,7 +514,9 @@ function addressesEqual(left, right) {
|
|
|
475
514
|
function allowedAddresses(key, chainId) {
|
|
476
515
|
const raw = key === "prefunded"
|
|
477
516
|
? constants.PREFUNDED_ESCROW_ADDRESSES
|
|
478
|
-
:
|
|
517
|
+
: chainId === 8453
|
|
518
|
+
? constants.LIMITLESS_VENUE_ESCROW_ADDRESSES
|
|
519
|
+
: constants.VENUE_ESCROW_ADDRESSES;
|
|
479
520
|
const list = [];
|
|
480
521
|
if (raw == null) {
|
|
481
522
|
// empty
|
|
@@ -773,6 +773,7 @@ export interface FetchMatchedMarketClustersParams extends MatchedClusterFilterPa
|
|
|
773
773
|
/** Anchor the response to a specific market URL. */
|
|
774
774
|
url?: string;
|
|
775
775
|
}
|
|
776
|
+
export type MatchedMarketClusterParams = FetchMatchedMarketClustersParams;
|
|
776
777
|
/** Parameters for fetching matched event clusters. */
|
|
777
778
|
export interface FetchMatchedEventClustersParams extends MatchedClusterFilterParams {
|
|
778
779
|
/** Pass a UnifiedEvent directly instead of eventId/slug/url. */
|
package/dist/esm/pmxt/router.js
CHANGED
|
@@ -6,6 +6,14 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { Exchange } from "./client.js";
|
|
8
8
|
import { logger } from "./logger.js";
|
|
9
|
+
function withQuestionAlias(market) {
|
|
10
|
+
Object.defineProperty(market, 'question', {
|
|
11
|
+
get() { return this.title; },
|
|
12
|
+
enumerable: false,
|
|
13
|
+
configurable: true,
|
|
14
|
+
});
|
|
15
|
+
return market;
|
|
16
|
+
}
|
|
9
17
|
function convertMarket(raw) {
|
|
10
18
|
const outcomes = (raw.outcomes || []).map((o) => ({
|
|
11
19
|
outcomeId: o.outcomeId,
|
|
@@ -27,7 +35,7 @@ function convertMarket(raw) {
|
|
|
27
35
|
bestAsk: o.bestAsk,
|
|
28
36
|
metadata: o.metadata,
|
|
29
37
|
}) : undefined;
|
|
30
|
-
return {
|
|
38
|
+
return withQuestionAlias({
|
|
31
39
|
marketId: raw.marketId,
|
|
32
40
|
title: raw.title,
|
|
33
41
|
slug: raw.slug,
|
|
@@ -51,7 +59,7 @@ function convertMarket(raw) {
|
|
|
51
59
|
no: convertOutcome(raw.no),
|
|
52
60
|
up: convertOutcome(raw.up),
|
|
53
61
|
down: convertOutcome(raw.down),
|
|
54
|
-
};
|
|
62
|
+
});
|
|
55
63
|
}
|
|
56
64
|
function convertEvent(raw) {
|
|
57
65
|
return {
|
|
@@ -72,7 +80,7 @@ function convertEvent(raw) {
|
|
|
72
80
|
function parseMatchResult(raw) {
|
|
73
81
|
const marketData = raw.market || {};
|
|
74
82
|
const market = convertMarket(marketData);
|
|
75
|
-
|
|
83
|
+
const result = {
|
|
76
84
|
...market,
|
|
77
85
|
market,
|
|
78
86
|
relation: raw.relation || 'identity',
|
|
@@ -82,6 +90,7 @@ function parseMatchResult(raw) {
|
|
|
82
90
|
bestAsk: raw.bestAsk ?? marketData.bestAsk,
|
|
83
91
|
sourceMarket: raw.sourceMarket ? convertMarket(raw.sourceMarket) : undefined,
|
|
84
92
|
};
|
|
93
|
+
return withQuestionAlias(result);
|
|
85
94
|
}
|
|
86
95
|
function normalizeQueryValue(value) {
|
|
87
96
|
if (value instanceof Date)
|
|
@@ -35,6 +35,13 @@ export interface Signer {
|
|
|
35
35
|
readonly address: string;
|
|
36
36
|
signTypedData(typedData: TypedData): Promise<string>;
|
|
37
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Load ethers lazily in BOTH module systems. The CJS build has native
|
|
40
|
+
* `require`; the ESM build does not — bare `require("ethers")` there threw
|
|
41
|
+
* ReferenceError, which callers swallowed, silently dropping the signer and
|
|
42
|
+
* killing every hosted write with "hosted write requires a signer".
|
|
43
|
+
*/
|
|
44
|
+
export declare function loadEthers(installHint?: string): any;
|
|
38
45
|
/**
|
|
39
46
|
* Built-in signer backed by an ethers `Wallet`.
|
|
40
47
|
*
|