pmxtjs 2.48.6 → 2.49.1
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/README.md +72 -10
- package/dist/esm/generated/src/models/Balance.d.ts +6 -0
- package/dist/esm/generated/src/models/Balance.js +2 -0
- package/dist/esm/generated/src/models/BuiltOrder.d.ts +6 -0
- package/dist/esm/generated/src/models/BuiltOrder.js +2 -0
- package/dist/esm/generated/src/models/ErrorDetail.d.ts +58 -2
- package/dist/esm/generated/src/models/ErrorDetail.js +37 -0
- package/dist/esm/generated/src/models/ExchangeOptions.d.ts +70 -0
- package/dist/esm/generated/src/models/ExchangeOptions.js +53 -0
- package/dist/esm/generated/src/models/MatchedMarketPair.d.ts +1 -1
- package/dist/esm/generated/src/models/Order.d.ts +18 -0
- package/dist/esm/generated/src/models/Order.js +6 -0
- package/dist/esm/generated/src/models/OrderLevel.d.ts +6 -0
- package/dist/esm/generated/src/models/OrderLevel.js +2 -0
- package/dist/esm/generated/src/models/Position.d.ts +33 -9
- package/dist/esm/generated/src/models/Position.js +12 -12
- package/dist/esm/generated/src/models/UnifiedSeries.d.ts +4 -4
- package/dist/esm/generated/src/models/UserTrade.d.ts +18 -0
- package/dist/esm/generated/src/models/UserTrade.js +6 -0
- package/dist/esm/generated/src/models/index.d.ts +1 -0
- package/dist/esm/generated/src/models/index.js +1 -0
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/pmxt/client.d.ts +106 -5
- package/dist/esm/pmxt/client.js +400 -6
- package/dist/esm/pmxt/constants.d.ts +11 -0
- package/dist/esm/pmxt/constants.js +13 -0
- package/dist/esm/pmxt/errors.d.ts +3 -0
- package/dist/esm/pmxt/errors.js +9 -0
- package/dist/esm/pmxt/escrow.d.ts +39 -0
- package/dist/esm/pmxt/escrow.js +78 -0
- package/dist/esm/pmxt/feed-client.d.ts +3 -0
- package/dist/esm/pmxt/feed-client.js +11 -2
- package/dist/esm/pmxt/hosted-errors.d.ts +84 -0
- package/dist/esm/pmxt/hosted-errors.js +186 -0
- package/dist/esm/pmxt/hosted-mappers.d.ts +45 -0
- package/dist/esm/pmxt/hosted-mappers.js +291 -0
- package/dist/esm/pmxt/hosted-routing.d.ts +69 -0
- package/dist/esm/pmxt/hosted-routing.js +119 -0
- package/dist/esm/pmxt/hosted-typed-data.d.ts +36 -0
- package/dist/esm/pmxt/hosted-typed-data.js +580 -0
- package/dist/esm/pmxt/models.d.ts +46 -8
- package/dist/esm/pmxt/server-manager.d.ts +4 -0
- package/dist/esm/pmxt/server-manager.js +6 -0
- package/dist/esm/pmxt/signers.d.ts +57 -0
- package/dist/esm/pmxt/signers.js +50 -0
- package/dist/esm/pmxt/ws-client.js +2 -1
- package/dist/generated/src/models/Balance.d.ts +6 -0
- package/dist/generated/src/models/Balance.js +2 -0
- package/dist/generated/src/models/BuiltOrder.d.ts +6 -0
- package/dist/generated/src/models/BuiltOrder.js +2 -0
- package/dist/generated/src/models/ErrorDetail.d.ts +58 -2
- package/dist/generated/src/models/ErrorDetail.js +38 -0
- package/dist/generated/src/models/ExchangeOptions.d.ts +70 -0
- package/dist/generated/src/models/ExchangeOptions.js +60 -0
- package/dist/generated/src/models/MatchedMarketPair.d.ts +1 -1
- package/dist/generated/src/models/Order.d.ts +18 -0
- package/dist/generated/src/models/Order.js +6 -0
- package/dist/generated/src/models/OrderLevel.d.ts +6 -0
- package/dist/generated/src/models/OrderLevel.js +2 -0
- package/dist/generated/src/models/Position.d.ts +33 -9
- package/dist/generated/src/models/Position.js +12 -12
- package/dist/generated/src/models/UnifiedSeries.d.ts +4 -4
- package/dist/generated/src/models/UserTrade.d.ts +18 -0
- package/dist/generated/src/models/UserTrade.js +6 -0
- package/dist/generated/src/models/index.d.ts +1 -0
- package/dist/generated/src/models/index.js +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/pmxt/client.d.ts +106 -5
- package/dist/pmxt/client.js +399 -5
- package/dist/pmxt/constants.d.ts +11 -0
- package/dist/pmxt/constants.js +14 -1
- package/dist/pmxt/errors.d.ts +3 -0
- package/dist/pmxt/errors.js +11 -1
- package/dist/pmxt/escrow.d.ts +39 -0
- package/dist/pmxt/escrow.js +82 -0
- package/dist/pmxt/feed-client.d.ts +3 -0
- package/dist/pmxt/feed-client.js +11 -2
- package/dist/pmxt/hosted-errors.d.ts +84 -0
- package/dist/pmxt/hosted-errors.js +201 -0
- package/dist/pmxt/hosted-mappers.d.ts +45 -0
- package/dist/pmxt/hosted-mappers.js +302 -0
- package/dist/pmxt/hosted-routing.d.ts +69 -0
- package/dist/pmxt/hosted-routing.js +126 -0
- package/dist/pmxt/hosted-typed-data.d.ts +36 -0
- package/dist/pmxt/hosted-typed-data.js +619 -0
- package/dist/pmxt/models.d.ts +46 -8
- package/dist/pmxt/server-manager.d.ts +4 -0
- package/dist/pmxt/server-manager.js +6 -0
- package/dist/pmxt/signers.d.ts +57 -0
- package/dist/pmxt/signers.js +55 -0
- package/dist/pmxt/ws-client.js +2 -1
- package/generated/.openapi-generator/FILES +2 -0
- package/generated/docs/Balance.md +2 -0
- package/generated/docs/BuiltOrder.md +2 -0
- package/generated/docs/ErrorDetail.md +9 -0
- package/generated/docs/ExchangeOptions.md +47 -0
- package/generated/docs/Order.md +6 -0
- package/generated/docs/OrderLevel.md +2 -0
- package/generated/docs/Position.md +9 -0
- package/generated/docs/UserTrade.md +6 -0
- package/generated/package.json +1 -1
- package/generated/src/models/Balance.ts +8 -0
- package/generated/src/models/BuiltOrder.ts +8 -0
- package/generated/src/models/ErrorDetail.ts +67 -2
- package/generated/src/models/ExchangeOptions.ts +115 -0
- package/generated/src/models/MatchedMarketPair.ts +1 -1
- package/generated/src/models/Order.ts +24 -0
- package/generated/src/models/OrderLevel.ts +8 -0
- package/generated/src/models/Position.ts +45 -17
- package/generated/src/models/UnifiedSeries.ts +4 -4
- package/generated/src/models/UserTrade.ts +24 -0
- package/generated/src/models/index.ts +1 -0
- package/index.ts +1 -0
- package/package.json +11 -2
- package/pmxt/client.ts +495 -9
- package/pmxt/constants.ts +15 -0
- package/pmxt/errors.ts +11 -0
- package/pmxt/escrow.ts +93 -0
- package/pmxt/feed-client.ts +14 -2
- package/pmxt/hosted-errors.ts +216 -0
- package/pmxt/hosted-mappers.ts +312 -0
- package/pmxt/hosted-routing.ts +165 -0
- package/pmxt/hosted-typed-data.ts +767 -0
- package/pmxt/models.ts +65 -8
- package/pmxt/server-manager.ts +7 -0
- package/pmxt/signers.ts +86 -0
- package/pmxt/ws-client.ts +2 -1
package/dist/esm/pmxt/client.js
CHANGED
|
@@ -7,9 +7,21 @@
|
|
|
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 } from "./errors.js";
|
|
10
|
+
import { PmxtError, fromServerError, InvalidOrder, NotSupported } from "./errors.js";
|
|
11
11
|
import { resolvePmxtBaseUrl } from "./constants.js";
|
|
12
12
|
import { SidecarWsClient } from "./ws-client.js";
|
|
13
|
+
// Hosted-mode trading dispatch.
|
|
14
|
+
// These modules are introduced as part of the hosted trading mode rollout.
|
|
15
|
+
// Some of them may be authored by parallel agents; until they all land, the
|
|
16
|
+
// import names below are the conventional ones from the plan. Cross-module
|
|
17
|
+
// "Cannot find module" errors during the parallel landing window resolve
|
|
18
|
+
// once the matching files exist.
|
|
19
|
+
import { HOSTED_TRADING_VENUES, _tradingRequest, resolveWalletAddress, formatRoutePath, HOSTED_METHOD_ROUTES, } from "./hosted-routing.js";
|
|
20
|
+
import { orderFromV0, positionFromV0, balanceFromV0, userTradeFromV0, to6dec, } from "./hosted-mappers.js";
|
|
21
|
+
import { validateTypedData, validateEconomics, verifySignature, } from "./hosted-typed-data.js";
|
|
22
|
+
import { EthersSigner } from "./signers.js";
|
|
23
|
+
import { Escrow } from "./escrow.js";
|
|
24
|
+
import { MissingWalletAddress, InvalidSignature as HostedInvalidSignature, } from "./hosted-errors.js";
|
|
13
25
|
/**
|
|
14
26
|
* Resolve a MarketOutcome shorthand to a plain outcome ID string.
|
|
15
27
|
* Accepts either a raw string ID or a MarketOutcome object.
|
|
@@ -136,10 +148,13 @@ export class Exchange {
|
|
|
136
148
|
"kalshi",
|
|
137
149
|
"opinion",
|
|
138
150
|
]);
|
|
151
|
+
// Public so structural interfaces like `HostedClientLike`
|
|
152
|
+
// (./hosted-routing) can read the venue name and hosted credentials
|
|
153
|
+
// without violating protected-access on this base class.
|
|
139
154
|
exchangeName;
|
|
155
|
+
pmxtApiKey;
|
|
140
156
|
apiKey;
|
|
141
157
|
privateKey;
|
|
142
|
-
pmxtApiKey;
|
|
143
158
|
proxyAddress;
|
|
144
159
|
signatureType;
|
|
145
160
|
api;
|
|
@@ -147,6 +162,12 @@ export class Exchange {
|
|
|
147
162
|
serverManager;
|
|
148
163
|
initPromise;
|
|
149
164
|
isHosted;
|
|
165
|
+
/** Wallet address used for hosted endpoints that operate on a wallet. */
|
|
166
|
+
walletAddress;
|
|
167
|
+
/** External signer used for hosted writes. */
|
|
168
|
+
signer;
|
|
169
|
+
/** Escrow namespace — populated in hosted mode for trading-allowlisted venues. */
|
|
170
|
+
escrow;
|
|
150
171
|
_hostedAccount;
|
|
151
172
|
_accountDiscoveryPromise;
|
|
152
173
|
/**
|
|
@@ -166,6 +187,8 @@ export class Exchange {
|
|
|
166
187
|
this.privateKey = options.privateKey;
|
|
167
188
|
this.proxyAddress = options.proxyAddress;
|
|
168
189
|
this.signatureType = options.signatureType;
|
|
190
|
+
this.walletAddress = options.walletAddress;
|
|
191
|
+
this.signer = options.signer;
|
|
169
192
|
// Resolve base URL + hosted API key via the shared precedence
|
|
170
193
|
// rules. See constants.ts for the full resolution table.
|
|
171
194
|
const resolved = resolvePmxtBaseUrl({
|
|
@@ -175,6 +198,26 @@ export class Exchange {
|
|
|
175
198
|
const baseUrl = resolved.baseUrl;
|
|
176
199
|
this.pmxtApiKey = resolved.pmxtApiKey;
|
|
177
200
|
this.isHosted = resolved.isHosted;
|
|
201
|
+
// Hosted trading bridge: if the caller passed a privateKey but no
|
|
202
|
+
// explicit signer, lazily wrap it in an EthersSigner so that
|
|
203
|
+
// `pmxt.Polymarket(pmxtApiKey, privateKey)` just works without the
|
|
204
|
+
// user touching `signer`. EthersSigner's constructor synchronously
|
|
205
|
+
// builds an ethers.Wallet, so this remains a synchronous bridge —
|
|
206
|
+
// the constructor stays sync.
|
|
207
|
+
if (this.pmxtApiKey && this.privateKey && !this.signer) {
|
|
208
|
+
try {
|
|
209
|
+
this.signer = new EthersSigner(this.privateKey);
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
// ethers not installed — defer the error to the first
|
|
213
|
+
// hosted write that actually needs the signer. Read-only
|
|
214
|
+
// hosted callers don't need ethers.
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
// Instantiate Escrow namespace for hosted-trading-allowlisted venues.
|
|
218
|
+
if (this.pmxtApiKey && HOSTED_TRADING_VENUES.has(this.exchangeName)) {
|
|
219
|
+
this.escrow = new Escrow(this);
|
|
220
|
+
}
|
|
178
221
|
// auto_start_server defaults: true for local, false for hosted.
|
|
179
222
|
// An explicit value in the options always wins.
|
|
180
223
|
const autoStartServer = options.autoStartServer !== undefined
|
|
@@ -255,6 +298,25 @@ export class Exchange {
|
|
|
255
298
|
}
|
|
256
299
|
return headers;
|
|
257
300
|
}
|
|
301
|
+
/**
|
|
302
|
+
* Returns true when this client should dispatch trading methods through
|
|
303
|
+
* the hosted PMXT trading API (`pmxtApiKey` set AND venue is on the
|
|
304
|
+
* hosted-trading allowlist). Used to gate every Group A method.
|
|
305
|
+
*/
|
|
306
|
+
isHostedTradingMode() {
|
|
307
|
+
return Boolean(this.pmxtApiKey) && HOSTED_TRADING_VENUES.has(this.exchangeName);
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Require a configured signer for a hosted write. Returns the signer or
|
|
311
|
+
* throws {@link MissingWalletAddress} (consistent with the error class
|
|
312
|
+
* surfaced for missing wallet wiring on hosted writes).
|
|
313
|
+
*/
|
|
314
|
+
requireHostedSigner() {
|
|
315
|
+
if (!this.signer) {
|
|
316
|
+
throw new MissingWalletAddress("hosted write requires a signer (pass `signer` or `privateKey`)");
|
|
317
|
+
}
|
|
318
|
+
return this.signer;
|
|
319
|
+
}
|
|
258
320
|
/**
|
|
259
321
|
* Resolve the current sidecar base URL.
|
|
260
322
|
*
|
|
@@ -849,6 +911,9 @@ export class Exchange {
|
|
|
849
911
|
}
|
|
850
912
|
}
|
|
851
913
|
async submitOrder(built) {
|
|
914
|
+
if (this.isHostedTradingMode()) {
|
|
915
|
+
return this._hostedSubmitOrder(built);
|
|
916
|
+
}
|
|
852
917
|
await this.initPromise;
|
|
853
918
|
try {
|
|
854
919
|
const args = [];
|
|
@@ -875,7 +940,80 @@ export class Exchange {
|
|
|
875
940
|
throw new PmxtError(`Failed to submitOrder: ${error}`);
|
|
876
941
|
}
|
|
877
942
|
}
|
|
943
|
+
/**
|
|
944
|
+
* Hosted-mode submitOrder: validate the stored build response, sign the
|
|
945
|
+
* typed_data (and pull_typed_data for Opinion cross-chain sells), then
|
|
946
|
+
* POST to `/v0/trade/submit-order`.
|
|
947
|
+
*/
|
|
948
|
+
async _hostedSubmitOrder(built) {
|
|
949
|
+
const signer = this.requireHostedSigner();
|
|
950
|
+
if (!this.walletAddress) {
|
|
951
|
+
throw new MissingWalletAddress("hosted submitOrder requires walletAddress");
|
|
952
|
+
}
|
|
953
|
+
// BuiltOrder is the SDK-side wrapper around the build response —
|
|
954
|
+
// expect typed_data, optional pull_typed_data, built_order_id, and
|
|
955
|
+
// the originating build_request to be present.
|
|
956
|
+
const payload = built;
|
|
957
|
+
const typedData = payload["typed_data"];
|
|
958
|
+
if (!typedData) {
|
|
959
|
+
throw new HostedInvalidSignature(0, "typed_data missing from built order");
|
|
960
|
+
}
|
|
961
|
+
const buildRequest = payload["build_request"]
|
|
962
|
+
?? payload["params"]?.["build_request"];
|
|
963
|
+
const side = String(buildRequest?.["side"] ?? "buy");
|
|
964
|
+
const primaryRoute = this._hostedTypedDataRoute(side, false);
|
|
965
|
+
// Layer 1: schema, Layer 2: economics.
|
|
966
|
+
validateTypedData(typedData, primaryRoute, this.walletAddress);
|
|
967
|
+
if (buildRequest) {
|
|
968
|
+
validateEconomics(typedData, primaryRoute, buildRequest, payload);
|
|
969
|
+
}
|
|
970
|
+
const signature = await signer.signTypedData(typedData);
|
|
971
|
+
// Layer 3: post-sign recovery + canonical check.
|
|
972
|
+
verifySignature(typedData, signature, signer.address);
|
|
973
|
+
const body = {
|
|
974
|
+
built_order_id: payload["built_order_id"],
|
|
975
|
+
signature,
|
|
976
|
+
};
|
|
977
|
+
const pullTypedData = payload["pull_typed_data"];
|
|
978
|
+
if (pullTypedData) {
|
|
979
|
+
const pullRoute = this._hostedTypedDataRoute(side, true);
|
|
980
|
+
if (pullRoute) {
|
|
981
|
+
validateTypedData(pullTypedData, pullRoute, this.walletAddress);
|
|
982
|
+
}
|
|
983
|
+
const pullSig = await signer.signTypedData(pullTypedData);
|
|
984
|
+
verifySignature(pullTypedData, pullSig, signer.address);
|
|
985
|
+
body["pull_signature"] = pullSig;
|
|
986
|
+
}
|
|
987
|
+
const route = HOSTED_METHOD_ROUTES.get("submitOrder");
|
|
988
|
+
const data = await _tradingRequest(this, { method: route.method, path: route.path, body });
|
|
989
|
+
return orderFromV0(data);
|
|
990
|
+
}
|
|
991
|
+
/**
|
|
992
|
+
* Resolve the per-(venue, side, pull) typed-data schema route used by
|
|
993
|
+
* `validateTypedData` / `validateEconomics`. Returns undefined for the
|
|
994
|
+
* pull leg when a venue/side combo doesn't have one.
|
|
995
|
+
*/
|
|
996
|
+
_hostedTypedDataRoute(side, isPull) {
|
|
997
|
+
const venue = this.exchangeName;
|
|
998
|
+
const sideLower = side.toLowerCase();
|
|
999
|
+
if (venue === "polymarket") {
|
|
1000
|
+
return sideLower === "sell" ? "polymarket_sell" : "polymarket_buy";
|
|
1001
|
+
}
|
|
1002
|
+
// opinion
|
|
1003
|
+
if (sideLower === "buy")
|
|
1004
|
+
return "opinion_buy";
|
|
1005
|
+
// sell — polygon, or BSC pull leg for cross-chain
|
|
1006
|
+
return isPull ? "opinion_sell_bsc_pull" : "opinion_sell_polygon";
|
|
1007
|
+
}
|
|
1008
|
+
_hostedCancelTypedDataRoute(isPull) {
|
|
1009
|
+
if (this.exchangeName === "polymarket")
|
|
1010
|
+
return "cancel_polymarket";
|
|
1011
|
+
return isPull ? "cancel_opinion_bsc_pull" : "cancel_opinion_polygon";
|
|
1012
|
+
}
|
|
878
1013
|
async cancelOrder(orderId) {
|
|
1014
|
+
if (this.isHostedTradingMode()) {
|
|
1015
|
+
return this._hostedCancelOrder(orderId);
|
|
1016
|
+
}
|
|
879
1017
|
await this.initPromise;
|
|
880
1018
|
try {
|
|
881
1019
|
const args = [];
|
|
@@ -902,7 +1040,47 @@ export class Exchange {
|
|
|
902
1040
|
throw new PmxtError(`Failed to cancelOrder: ${error}`);
|
|
903
1041
|
}
|
|
904
1042
|
}
|
|
1043
|
+
/**
|
|
1044
|
+
* Hosted-mode cancelOrder: build the cancel typed_data on the server,
|
|
1045
|
+
* validate + sign (dual-sign for Opinion cross-chain), then submit.
|
|
1046
|
+
*/
|
|
1047
|
+
async _hostedCancelOrder(orderId) {
|
|
1048
|
+
const signer = this.requireHostedSigner();
|
|
1049
|
+
if (!this.walletAddress) {
|
|
1050
|
+
throw new MissingWalletAddress("hosted cancelOrder requires walletAddress");
|
|
1051
|
+
}
|
|
1052
|
+
const buildRoute = HOSTED_METHOD_ROUTES.get("cancelOrderBuild");
|
|
1053
|
+
const buildResp = await _tradingRequest(this, {
|
|
1054
|
+
method: buildRoute.method,
|
|
1055
|
+
path: buildRoute.path,
|
|
1056
|
+
body: { order_id: orderId },
|
|
1057
|
+
});
|
|
1058
|
+
const typedData = buildResp["typed_data"];
|
|
1059
|
+
if (!typedData) {
|
|
1060
|
+
throw new HostedInvalidSignature(0, "typed_data missing from cancel build response");
|
|
1061
|
+
}
|
|
1062
|
+
validateTypedData(typedData, this._hostedCancelTypedDataRoute(false), this.walletAddress);
|
|
1063
|
+
const signature = await signer.signTypedData(typedData);
|
|
1064
|
+
verifySignature(typedData, signature, signer.address);
|
|
1065
|
+
const body = {
|
|
1066
|
+
cancel_id: buildResp["cancel_id"],
|
|
1067
|
+
signature,
|
|
1068
|
+
};
|
|
1069
|
+
const pullTypedData = buildResp["pull_typed_data"];
|
|
1070
|
+
if (pullTypedData) {
|
|
1071
|
+
validateTypedData(pullTypedData, this._hostedCancelTypedDataRoute(true), this.walletAddress);
|
|
1072
|
+
const pullSig = await signer.signTypedData(pullTypedData);
|
|
1073
|
+
verifySignature(pullTypedData, pullSig, signer.address);
|
|
1074
|
+
body["pull_signature"] = pullSig;
|
|
1075
|
+
}
|
|
1076
|
+
const route = HOSTED_METHOD_ROUTES.get("cancelOrder");
|
|
1077
|
+
const data = await _tradingRequest(this, { method: route.method, path: route.path, body });
|
|
1078
|
+
return orderFromV0(data);
|
|
1079
|
+
}
|
|
905
1080
|
async fetchOrder(orderId) {
|
|
1081
|
+
if (this.isHostedTradingMode()) {
|
|
1082
|
+
return this._hostedFetchOrder(orderId);
|
|
1083
|
+
}
|
|
906
1084
|
await this.initPromise;
|
|
907
1085
|
try {
|
|
908
1086
|
const args = [];
|
|
@@ -929,7 +1107,16 @@ export class Exchange {
|
|
|
929
1107
|
throw new PmxtError(`Failed to fetchOrder: ${error}`);
|
|
930
1108
|
}
|
|
931
1109
|
}
|
|
1110
|
+
async _hostedFetchOrder(orderId) {
|
|
1111
|
+
const route = HOSTED_METHOD_ROUTES.get("fetchOrder");
|
|
1112
|
+
const path = formatRoutePath(route, { order_id: orderId });
|
|
1113
|
+
const data = await _tradingRequest(this, { method: route.method, path });
|
|
1114
|
+
return orderFromV0(data);
|
|
1115
|
+
}
|
|
932
1116
|
async fetchOpenOrders(marketId) {
|
|
1117
|
+
if (this.isHostedTradingMode()) {
|
|
1118
|
+
return this._hostedFetchOpenOrders(marketId);
|
|
1119
|
+
}
|
|
933
1120
|
await this.initPromise;
|
|
934
1121
|
try {
|
|
935
1122
|
const args = [];
|
|
@@ -957,7 +1144,24 @@ export class Exchange {
|
|
|
957
1144
|
throw new PmxtError(`Failed to fetchOpenOrders: ${error}`);
|
|
958
1145
|
}
|
|
959
1146
|
}
|
|
1147
|
+
async _hostedFetchOpenOrders(marketId) {
|
|
1148
|
+
const address = resolveWalletAddress(this, undefined);
|
|
1149
|
+
const route = HOSTED_METHOD_ROUTES.get("fetchOpenOrders");
|
|
1150
|
+
const params = { address };
|
|
1151
|
+
if (marketId !== undefined)
|
|
1152
|
+
params["market_id"] = marketId;
|
|
1153
|
+
const data = await _tradingRequest(this, {
|
|
1154
|
+
method: route.method,
|
|
1155
|
+
path: route.path,
|
|
1156
|
+
params,
|
|
1157
|
+
});
|
|
1158
|
+
const items = (Array.isArray(data) ? data : data?.["orders"] ?? []);
|
|
1159
|
+
return items.map(orderFromV0);
|
|
1160
|
+
}
|
|
960
1161
|
async fetchMyTrades(params) {
|
|
1162
|
+
if (this.isHostedTradingMode()) {
|
|
1163
|
+
return this._hostedFetchMyTrades(params);
|
|
1164
|
+
}
|
|
961
1165
|
await this.initPromise;
|
|
962
1166
|
try {
|
|
963
1167
|
const args = [];
|
|
@@ -985,7 +1189,35 @@ export class Exchange {
|
|
|
985
1189
|
throw new PmxtError(`Failed to fetchMyTrades: ${error}`);
|
|
986
1190
|
}
|
|
987
1191
|
}
|
|
1192
|
+
async _hostedFetchMyTrades(params) {
|
|
1193
|
+
const address = resolveWalletAddress(this, undefined);
|
|
1194
|
+
const route = HOSTED_METHOD_ROUTES.get("fetchMyTrades");
|
|
1195
|
+
const path = formatRoutePath(route, { address });
|
|
1196
|
+
const q = {};
|
|
1197
|
+
if (params?.marketId)
|
|
1198
|
+
q["market_id"] = params.marketId;
|
|
1199
|
+
if (params?.outcomeId)
|
|
1200
|
+
q["outcome_id"] = params.outcomeId;
|
|
1201
|
+
if (params?.limit !== undefined)
|
|
1202
|
+
q["limit"] = String(params.limit);
|
|
1203
|
+
if (params?.cursor)
|
|
1204
|
+
q["cursor"] = params.cursor;
|
|
1205
|
+
if (params?.since)
|
|
1206
|
+
q["since"] = String(params.since.getTime());
|
|
1207
|
+
if (params?.until)
|
|
1208
|
+
q["until"] = String(params.until.getTime());
|
|
1209
|
+
const data = await _tradingRequest(this, {
|
|
1210
|
+
method: route.method,
|
|
1211
|
+
path,
|
|
1212
|
+
params: Object.keys(q).length ? q : undefined,
|
|
1213
|
+
});
|
|
1214
|
+
const items = (Array.isArray(data) ? data : data?.["trades"] ?? []);
|
|
1215
|
+
return items.map(userTradeFromV0);
|
|
1216
|
+
}
|
|
988
1217
|
async fetchClosedOrders(params) {
|
|
1218
|
+
if (this.isHostedTradingMode()) {
|
|
1219
|
+
throw new NotSupported("Settled orders are modeled as trades — use fetchMyTrades().");
|
|
1220
|
+
}
|
|
989
1221
|
await this.initPromise;
|
|
990
1222
|
try {
|
|
991
1223
|
const args = [];
|
|
@@ -1014,6 +1246,9 @@ export class Exchange {
|
|
|
1014
1246
|
}
|
|
1015
1247
|
}
|
|
1016
1248
|
async fetchAllOrders(params) {
|
|
1249
|
+
if (this.isHostedTradingMode()) {
|
|
1250
|
+
throw new NotSupported("Use fetchOpenOrders() and fetchMyTrades() separately.");
|
|
1251
|
+
}
|
|
1017
1252
|
await this.initPromise;
|
|
1018
1253
|
try {
|
|
1019
1254
|
const args = [];
|
|
@@ -1042,6 +1277,9 @@ export class Exchange {
|
|
|
1042
1277
|
}
|
|
1043
1278
|
}
|
|
1044
1279
|
async fetchPositions(address) {
|
|
1280
|
+
if (this.isHostedTradingMode()) {
|
|
1281
|
+
return this._hostedFetchPositions(address);
|
|
1282
|
+
}
|
|
1045
1283
|
await this.initPromise;
|
|
1046
1284
|
try {
|
|
1047
1285
|
const args = [];
|
|
@@ -1069,7 +1307,18 @@ export class Exchange {
|
|
|
1069
1307
|
throw new PmxtError(`Failed to fetchPositions: ${error}`);
|
|
1070
1308
|
}
|
|
1071
1309
|
}
|
|
1310
|
+
async _hostedFetchPositions(address) {
|
|
1311
|
+
const resolvedAddr = resolveWalletAddress(this, address);
|
|
1312
|
+
const route = HOSTED_METHOD_ROUTES.get("fetchPositions");
|
|
1313
|
+
const path = formatRoutePath(route, { address: resolvedAddr });
|
|
1314
|
+
const data = await _tradingRequest(this, { method: route.method, path });
|
|
1315
|
+
const items = (Array.isArray(data) ? data : data?.["positions"] ?? []);
|
|
1316
|
+
return items.map(positionFromV0);
|
|
1317
|
+
}
|
|
1072
1318
|
async fetchBalance(address) {
|
|
1319
|
+
if (this.isHostedTradingMode()) {
|
|
1320
|
+
return this._hostedFetchBalance(address);
|
|
1321
|
+
}
|
|
1073
1322
|
await this.initPromise;
|
|
1074
1323
|
try {
|
|
1075
1324
|
const args = [];
|
|
@@ -1097,6 +1346,18 @@ export class Exchange {
|
|
|
1097
1346
|
throw new PmxtError(`Failed to fetchBalance: ${error}`);
|
|
1098
1347
|
}
|
|
1099
1348
|
}
|
|
1349
|
+
async _hostedFetchBalance(address) {
|
|
1350
|
+
const resolvedAddr = resolveWalletAddress(this, address);
|
|
1351
|
+
const route = HOSTED_METHOD_ROUTES.get("fetchBalance");
|
|
1352
|
+
const path = formatRoutePath(route, { address: resolvedAddr });
|
|
1353
|
+
const data = await _tradingRequest(this, { method: route.method, path });
|
|
1354
|
+
// Hosted balance is a single USDC escrow record; wrap in an array
|
|
1355
|
+
// to match the existing Balance[] return shape.
|
|
1356
|
+
if (Array.isArray(data)) {
|
|
1357
|
+
return data.map(balanceFromV0);
|
|
1358
|
+
}
|
|
1359
|
+
return [balanceFromV0(data)];
|
|
1360
|
+
}
|
|
1100
1361
|
async unwatchOrderBook(outcomeId) {
|
|
1101
1362
|
await this.initPromise;
|
|
1102
1363
|
try {
|
|
@@ -1769,6 +2030,9 @@ export class Exchange {
|
|
|
1769
2030
|
* ```
|
|
1770
2031
|
*/
|
|
1771
2032
|
async buildOrder(params) {
|
|
2033
|
+
if (this.isHostedTradingMode()) {
|
|
2034
|
+
return this._hostedBuildOrder(params);
|
|
2035
|
+
}
|
|
1772
2036
|
if (this.isHosted) {
|
|
1773
2037
|
throw new PmxtError("Trade execution is not available through the hosted API. " +
|
|
1774
2038
|
"Use the local PMXT SDK with your venue credentials instead. " +
|
|
@@ -1899,6 +2163,106 @@ export class Exchange {
|
|
|
1899
2163
|
}
|
|
1900
2164
|
return convertOrder(submitData);
|
|
1901
2165
|
}
|
|
2166
|
+
/**
|
|
2167
|
+
* Hosted-mode buildOrder: validate inputs locally, then POST to the
|
|
2168
|
+
* trading service's `build-order` endpoint and return a BuiltOrder
|
|
2169
|
+
* that carries the original build_request for Layer-2 economic checks
|
|
2170
|
+
* at submit time.
|
|
2171
|
+
*/
|
|
2172
|
+
async _hostedBuildOrder(params) {
|
|
2173
|
+
const body = this._hostedBuildOrderBody(params);
|
|
2174
|
+
const route = HOSTED_METHOD_ROUTES.get("buildOrder");
|
|
2175
|
+
const data = await _tradingRequest(this, {
|
|
2176
|
+
method: route.method,
|
|
2177
|
+
path: route.path,
|
|
2178
|
+
body,
|
|
2179
|
+
});
|
|
2180
|
+
// Attach the originating build_request so submit can run economic
|
|
2181
|
+
// validation without an extra catalog round-trip.
|
|
2182
|
+
const built = { ...data, build_request: body };
|
|
2183
|
+
return built;
|
|
2184
|
+
}
|
|
2185
|
+
/**
|
|
2186
|
+
* Hosted-mode createOrder: build → sign → submit single-call wrapper.
|
|
2187
|
+
*/
|
|
2188
|
+
async _hostedCreateOrder(params) {
|
|
2189
|
+
const built = await this._hostedBuildOrder(params);
|
|
2190
|
+
return this._hostedSubmitOrder(built);
|
|
2191
|
+
}
|
|
2192
|
+
/**
|
|
2193
|
+
* Construct the hosted build-order request body and validate inputs
|
|
2194
|
+
* locally per the v0 contract (denom/side compatibility, > 6-decimal
|
|
2195
|
+
* precision rejected via {@link to6dec}).
|
|
2196
|
+
*/
|
|
2197
|
+
_hostedBuildOrderBody(params) {
|
|
2198
|
+
let marketId = params.marketId;
|
|
2199
|
+
let outcomeId = params.outcomeId;
|
|
2200
|
+
if (params.outcome) {
|
|
2201
|
+
if (marketId !== undefined || outcomeId !== undefined) {
|
|
2202
|
+
throw new InvalidOrder("cannot specify both 'outcome' and 'marketId'/'outcomeId'");
|
|
2203
|
+
}
|
|
2204
|
+
const outcome = params.outcome;
|
|
2205
|
+
if (!outcome.marketId) {
|
|
2206
|
+
throw new InvalidOrder("outcome.marketId is not set; ensure the outcome comes from a fetched market");
|
|
2207
|
+
}
|
|
2208
|
+
marketId = outcome.marketId;
|
|
2209
|
+
outcomeId = outcome.outcomeId;
|
|
2210
|
+
}
|
|
2211
|
+
const side = String(params.side);
|
|
2212
|
+
const orderType = String(params.type ?? "market");
|
|
2213
|
+
const denom = params["denom"];
|
|
2214
|
+
// denom/side compatibility per v0:
|
|
2215
|
+
// market buy -> denom='usdc'
|
|
2216
|
+
// market sell -> denom='shares'
|
|
2217
|
+
// any limit -> denom='shares'
|
|
2218
|
+
let resolvedDenom;
|
|
2219
|
+
if (orderType === "market") {
|
|
2220
|
+
if (side === "buy") {
|
|
2221
|
+
if (denom && denom !== "usdc") {
|
|
2222
|
+
throw new InvalidOrder("market buy requires denom='usdc'");
|
|
2223
|
+
}
|
|
2224
|
+
resolvedDenom = "usdc";
|
|
2225
|
+
}
|
|
2226
|
+
else if (side === "sell") {
|
|
2227
|
+
if (denom && denom !== "shares") {
|
|
2228
|
+
throw new InvalidOrder("market sell requires denom='shares'");
|
|
2229
|
+
}
|
|
2230
|
+
resolvedDenom = "shares";
|
|
2231
|
+
}
|
|
2232
|
+
else {
|
|
2233
|
+
throw new InvalidOrder(`unknown side: ${side}`);
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
else {
|
|
2237
|
+
if (denom && denom !== "shares") {
|
|
2238
|
+
throw new InvalidOrder("limit orders require denom='shares'");
|
|
2239
|
+
}
|
|
2240
|
+
resolvedDenom = "shares";
|
|
2241
|
+
}
|
|
2242
|
+
if (!(Number(params.amount) > 0)) {
|
|
2243
|
+
throw new InvalidOrder("amount must be positive");
|
|
2244
|
+
}
|
|
2245
|
+
// to6dec throws InvalidOrder for sub-micro precision.
|
|
2246
|
+
const amount6dec = to6dec(params.amount).toString();
|
|
2247
|
+
const body = {
|
|
2248
|
+
market_id: marketId,
|
|
2249
|
+
outcome_id: outcomeId,
|
|
2250
|
+
side,
|
|
2251
|
+
order_type: orderType,
|
|
2252
|
+
denom: resolvedDenom,
|
|
2253
|
+
amount: params.amount,
|
|
2254
|
+
amount_6dec: amount6dec,
|
|
2255
|
+
};
|
|
2256
|
+
if (params.price !== undefined)
|
|
2257
|
+
body["price"] = params.price;
|
|
2258
|
+
const extra = params;
|
|
2259
|
+
if (extra["slippage_pct"] !== undefined) {
|
|
2260
|
+
body["slippage_pct"] = extra["slippage_pct"];
|
|
2261
|
+
}
|
|
2262
|
+
if (this.walletAddress)
|
|
2263
|
+
body["user_address"] = this.walletAddress;
|
|
2264
|
+
return body;
|
|
2265
|
+
}
|
|
1902
2266
|
/**
|
|
1903
2267
|
* @example
|
|
1904
2268
|
* ```typescript
|
|
@@ -1913,10 +2277,15 @@ export class Exchange {
|
|
|
1913
2277
|
* ```
|
|
1914
2278
|
*/
|
|
1915
2279
|
async createOrder(params) {
|
|
2280
|
+
// SOR escape path (preserved): legacy hosted SOR flow uses a venue-side
|
|
2281
|
+
// SDK to execute the legs, only when a privateKey is present.
|
|
2282
|
+
if (this.isHosted && this.exchangeName === 'sor' && this.privateKey) {
|
|
2283
|
+
return this._executeSorOrder(params);
|
|
2284
|
+
}
|
|
2285
|
+
if (this.isHostedTradingMode()) {
|
|
2286
|
+
return this._hostedCreateOrder(params);
|
|
2287
|
+
}
|
|
1916
2288
|
if (this.isHosted) {
|
|
1917
|
-
if (this.exchangeName === 'sor' && this.privateKey) {
|
|
1918
|
-
return this._executeSorOrder(params);
|
|
1919
|
-
}
|
|
1920
2289
|
throw new PmxtError("Trade execution is not available through the hosted API. " +
|
|
1921
2290
|
"Use the local PMXT SDK with your venue credentials instead. " +
|
|
1922
2291
|
"See https://pmxt.dev/docs/quickstart for setup instructions.");
|
|
@@ -2548,15 +2917,40 @@ export class Hyperliquid extends Exchange {
|
|
|
2548
2917
|
/**
|
|
2549
2918
|
* SuiBets exchange client.
|
|
2550
2919
|
*
|
|
2920
|
+
* SuiBets is a decentralised P2P sports betting exchange on Sui mainnet.
|
|
2921
|
+
* No house edge. 2% platform fee.
|
|
2922
|
+
* Contract: 0xd51fe151bec66a15b086a67c1cfce9b05759ddac1d73fcd3e14324ad202b2e59
|
|
2923
|
+
*
|
|
2551
2924
|
* @example
|
|
2552
2925
|
* ```typescript
|
|
2553
2926
|
* const suibets = new SuiBets();
|
|
2554
|
-
* const markets = await suibets.fetchMarkets();
|
|
2927
|
+
* const markets = await suibets.fetchMarkets({ limit: 20 });
|
|
2928
|
+
*
|
|
2929
|
+
* // With wallet for fetchPositions()
|
|
2930
|
+
* const me = new SuiBets({ walletAddress: '0xabc...' });
|
|
2931
|
+
* const positions = await me.fetchPositions();
|
|
2555
2932
|
* ```
|
|
2556
2933
|
*/
|
|
2557
2934
|
export class SuiBets extends Exchange {
|
|
2935
|
+
_walletAddress;
|
|
2558
2936
|
constructor(options = {}) {
|
|
2559
2937
|
super("suibets", options);
|
|
2938
|
+
this._walletAddress = options.walletAddress;
|
|
2939
|
+
}
|
|
2940
|
+
/**
|
|
2941
|
+
* Includes walletAddress in the credentials sent to the sidecar so
|
|
2942
|
+
* that fetchPositions() can reach the /api/p2p/my endpoint.
|
|
2943
|
+
* Falls back to SUIBETS_WALLET_ADDRESS env var on the sidecar side
|
|
2944
|
+
* when walletAddress is not set here.
|
|
2945
|
+
*/
|
|
2946
|
+
getCredentials() {
|
|
2947
|
+
const base = super.getCredentials();
|
|
2948
|
+
if (!this._walletAddress)
|
|
2949
|
+
return base;
|
|
2950
|
+
return {
|
|
2951
|
+
...(base ?? {}),
|
|
2952
|
+
walletAddress: this._walletAddress,
|
|
2953
|
+
};
|
|
2560
2954
|
}
|
|
2561
2955
|
}
|
|
2562
2956
|
/**
|
|
@@ -51,3 +51,14 @@ export declare function resolvePmxtBaseUrl(args: {
|
|
|
51
51
|
pmxtApiKey?: string;
|
|
52
52
|
isHosted: boolean;
|
|
53
53
|
};
|
|
54
|
+
/**
|
|
55
|
+
* Lowercase 0x-prefixed escrow addresses that are pre-funded by pmxt for
|
|
56
|
+
* hosted trading. Orders routed through these addresses use the shared
|
|
57
|
+
* escrow balance rather than a per-venue deposit.
|
|
58
|
+
*/
|
|
59
|
+
export declare const PREFUNDED_ESCROW_ADDRESSES: ReadonlySet<string>;
|
|
60
|
+
/**
|
|
61
|
+
* Lowercase 0x-prefixed escrow addresses that the hosted trading API treats
|
|
62
|
+
* as venue-owned escrows. Currently empty; populated as venues onboard.
|
|
63
|
+
*/
|
|
64
|
+
export declare const VENUE_ESCROW_ADDRESSES: ReadonlySet<string>;
|
|
@@ -58,3 +58,16 @@ export function resolvePmxtBaseUrl(args) {
|
|
|
58
58
|
return pick(HOSTED_URL);
|
|
59
59
|
return pick(LOCAL_URL);
|
|
60
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Lowercase 0x-prefixed escrow addresses that are pre-funded by pmxt for
|
|
63
|
+
* hosted trading. Orders routed through these addresses use the shared
|
|
64
|
+
* escrow balance rather than a per-venue deposit.
|
|
65
|
+
*/
|
|
66
|
+
export const PREFUNDED_ESCROW_ADDRESSES = new Set([
|
|
67
|
+
"0x3ad326f78b1390b9a5dc5f00e7f62f8632de23e2",
|
|
68
|
+
]);
|
|
69
|
+
/**
|
|
70
|
+
* Lowercase 0x-prefixed escrow addresses that the hosted trading API treats
|
|
71
|
+
* as venue-owned escrows. Currently empty; populated as venues onboard.
|
|
72
|
+
*/
|
|
73
|
+
export const VENUE_ESCROW_ADDRESSES = new Set();
|
|
@@ -51,5 +51,8 @@ export declare class NetworkError extends PmxtError {
|
|
|
51
51
|
export declare class ExchangeNotAvailable extends PmxtError {
|
|
52
52
|
constructor(message: string, exchange?: string);
|
|
53
53
|
}
|
|
54
|
+
export declare class NotSupported extends PmxtError {
|
|
55
|
+
constructor(message: string, exchange?: string);
|
|
56
|
+
}
|
|
54
57
|
/** Convert a server error response object into a typed PmxtError. */
|
|
55
58
|
export declare function fromServerError(errorData: unknown): PmxtError;
|
package/dist/esm/pmxt/errors.js
CHANGED
|
@@ -90,6 +90,11 @@ export class ExchangeNotAvailable extends PmxtError {
|
|
|
90
90
|
super(message, "EXCHANGE_NOT_AVAILABLE", true, exchange);
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
|
+
export class NotSupported extends PmxtError {
|
|
94
|
+
constructor(message, exchange) {
|
|
95
|
+
super(message, "NOT_SUPPORTED", false, exchange);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
93
98
|
// Error code to class mapping
|
|
94
99
|
const ERROR_CODE_MAP = {
|
|
95
100
|
BAD_REQUEST: BadRequest,
|
|
@@ -130,3 +135,7 @@ export function fromServerError(errorData) {
|
|
|
130
135
|
}
|
|
131
136
|
return new PmxtError(message, code, retryable, exchange);
|
|
132
137
|
}
|
|
138
|
+
// Hosted error classes live in ./hosted-errors. Re-exported from index.ts
|
|
139
|
+
// at the package root, NOT here — re-exporting from errors.ts creates a
|
|
140
|
+
// circular dependency (hosted-errors imports PmxtError from errors.ts)
|
|
141
|
+
// that crashes tsx/CJS module loaders even though tsc and ts-jest tolerate it.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hosted-mode escrow namespace.
|
|
3
|
+
*
|
|
4
|
+
* Exposes the `/v0/escrow/*` endpoints of the pmxt hosted trading API as a
|
|
5
|
+
* small ergonomic helper class. Each method returns the raw upstream JSON
|
|
6
|
+
* payload as `unknown` — typed mappers can be layered on later without
|
|
7
|
+
* breaking the wire shape.
|
|
8
|
+
*
|
|
9
|
+
* Mirrors `sdks/python/pmxt/escrow.py`.
|
|
10
|
+
*/
|
|
11
|
+
import { HostedClientLike } from "./hosted-routing.js";
|
|
12
|
+
export declare class Escrow {
|
|
13
|
+
private readonly client;
|
|
14
|
+
constructor(client: HostedClientLike);
|
|
15
|
+
/**
|
|
16
|
+
* Build an unsigned approve transaction for a given ERC-20 `token`. When
|
|
17
|
+
* `amountWei` is omitted, the server returns an unlimited approval.
|
|
18
|
+
*/
|
|
19
|
+
approveTx(token: string, amountWei?: bigint): Promise<unknown>;
|
|
20
|
+
/**
|
|
21
|
+
* Build an unsigned deposit transaction for `amount` (USDC, 6-decimal
|
|
22
|
+
* grid). Accepts number, decimal string, or BigInt in micro-units.
|
|
23
|
+
*/
|
|
24
|
+
depositTx(amount: number | string | bigint): Promise<unknown>;
|
|
25
|
+
/**
|
|
26
|
+
* Build an unsigned withdraw transaction. `action` selects the stage of
|
|
27
|
+
* the withdrawal lifecycle: `request` initiates, `claim` finalizes after
|
|
28
|
+
* the timelock, `cancel` aborts a pending request.
|
|
29
|
+
*/
|
|
30
|
+
withdrawTx(action: "request" | "claim" | "cancel", amount?: number | string | bigint): Promise<unknown>;
|
|
31
|
+
/**
|
|
32
|
+
* List the user's withdrawal records. `include` is forwarded verbatim and
|
|
33
|
+
* defaults to `"pending,events"` to match the Python SDK.
|
|
34
|
+
*/
|
|
35
|
+
withdrawals(opts?: {
|
|
36
|
+
include?: string;
|
|
37
|
+
address?: string;
|
|
38
|
+
}): Promise<unknown>;
|
|
39
|
+
}
|