@elizaos/plugin-polymarket-app 2.0.3-beta.6 → 2.0.3-beta.7
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/PolymarketAppView.d.ts +3 -0
- package/dist/PolymarketAppView.d.ts.map +1 -0
- package/dist/PolymarketAppView.helpers.d.ts +10 -0
- package/dist/PolymarketAppView.helpers.d.ts.map +1 -0
- package/dist/PolymarketAppView.helpers.js +30 -0
- package/dist/PolymarketAppView.helpers.js.map +1 -0
- package/dist/PolymarketAppView.interact.d.ts +3 -0
- package/dist/PolymarketAppView.interact.d.ts.map +1 -0
- package/dist/PolymarketAppView.interact.js +70 -0
- package/dist/PolymarketAppView.interact.js.map +1 -0
- package/dist/PolymarketAppView.js +704 -0
- package/dist/PolymarketAppView.js.map +1 -0
- package/dist/PolymarketPositionsPanel.d.ts +13 -0
- package/dist/PolymarketPositionsPanel.d.ts.map +1 -0
- package/dist/PolymarketPositionsPanel.js +349 -0
- package/dist/PolymarketPositionsPanel.js.map +1 -0
- package/dist/PolymarketView.d.ts +13 -0
- package/dist/PolymarketView.d.ts.map +1 -0
- package/dist/PolymarketView.js +58 -0
- package/dist/PolymarketView.js.map +1 -0
- package/dist/__fixtures__/contract.d.ts +9 -0
- package/dist/__fixtures__/contract.d.ts.map +1 -0
- package/dist/__fixtures__/contract.js +263 -0
- package/dist/__fixtures__/contract.js.map +1 -0
- package/dist/actions.d.ts +39 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +661 -0
- package/dist/actions.js.map +1 -0
- package/dist/client.d.ts +25 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +42 -0
- package/dist/client.js.map +1 -0
- package/dist/components/PolymarketSpatialView.d.ts +34 -0
- package/dist/components/PolymarketSpatialView.d.ts.map +1 -0
- package/dist/components/PolymarketSpatialView.js +248 -0
- package/dist/components/PolymarketSpatialView.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/orderbook.d.ts +15 -0
- package/dist/orderbook.d.ts.map +1 -0
- package/dist/orderbook.js +45 -0
- package/dist/orderbook.js.map +1 -0
- package/dist/plugin.d.ts +3 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +108 -0
- package/dist/plugin.js.map +1 -0
- package/dist/polymarket-app.d.ts +4 -0
- package/dist/polymarket-app.d.ts.map +1 -0
- package/dist/polymarket-app.js +18 -0
- package/dist/polymarket-app.js.map +1 -0
- package/dist/polymarket-contracts.d.ts +162 -0
- package/dist/polymarket-contracts.d.ts.map +1 -0
- package/dist/polymarket-contracts.js +16 -0
- package/dist/polymarket-contracts.js.map +1 -0
- package/dist/polymarket-view-bundle.d.ts +3 -0
- package/dist/polymarket-view-bundle.d.ts.map +1 -0
- package/dist/polymarket-view-bundle.js +7 -0
- package/dist/polymarket-view-bundle.js.map +1 -0
- package/dist/provider-text.d.ts +5 -0
- package/dist/provider-text.d.ts.map +1 -0
- package/dist/provider-text.js +44 -0
- package/dist/provider-text.js.map +1 -0
- package/dist/provider.d.ts +3 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +21 -0
- package/dist/provider.js.map +1 -0
- package/dist/register-routes.d.ts +2 -0
- package/dist/register-routes.d.ts.map +1 -0
- package/dist/register-routes.js +6 -0
- package/dist/register-routes.js.map +1 -0
- package/dist/register-terminal-view.d.ts +15 -0
- package/dist/register-terminal-view.d.ts.map +1 -0
- package/dist/register-terminal-view.js +25 -0
- package/dist/register-terminal-view.js.map +1 -0
- package/dist/register.d.ts +2 -0
- package/dist/register.d.ts.map +1 -0
- package/dist/register.js +17 -0
- package/dist/register.js.map +1 -0
- package/dist/routes.d.ts +7 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +455 -0
- package/dist/routes.js.map +1 -0
- package/dist/ui.d.ts +6 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +11 -0
- package/dist/ui.js.map +1 -0
- package/dist/usePolymarketState.d.ts +13 -0
- package/dist/usePolymarketState.d.ts.map +1 -0
- package/dist/usePolymarketState.js +59 -0
- package/dist/usePolymarketState.js.map +1 -0
- package/dist/views/bundle.js +535 -0
- package/dist/views/bundle.js.map +1 -0
- package/package.json +6 -6
package/dist/register.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import "./polymarket-app.js";
|
|
2
|
+
import { registerAppShellPage } from "@elizaos/ui/app-shell-registry";
|
|
3
|
+
if (typeof window === "undefined") {
|
|
4
|
+
void import("./register-terminal-view.js").then((m) => m.registerPolymarketTerminalView()).catch(() => {
|
|
5
|
+
});
|
|
6
|
+
}
|
|
7
|
+
registerAppShellPage({
|
|
8
|
+
id: "polymarket",
|
|
9
|
+
pluginId: "@elizaos/plugin-polymarket-app",
|
|
10
|
+
label: "Polymarket",
|
|
11
|
+
icon: "BarChart2",
|
|
12
|
+
path: "/polymarket",
|
|
13
|
+
loader: () => import("./polymarket-view-bundle.js").then((m) => ({
|
|
14
|
+
default: m.PolymarketView
|
|
15
|
+
}))
|
|
16
|
+
});
|
|
17
|
+
//# sourceMappingURL=register.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/register.ts"],"sourcesContent":["import \"./polymarket-app.js\";\nimport { registerAppShellPage } from \"@elizaos/ui/app-shell-registry\";\n\n// In a terminal host (the Node agent, no DOM), register the Polymarket view so\n// it renders inline in the terminal. Lazy + DOM-guarded so the terminal engine\n// stays out of browser/mobile bundles.\nif (typeof window === \"undefined\") {\n void import(\"./register-terminal-view.js\")\n .then((m) => m.registerPolymarketTerminalView())\n .catch(() => {\n // Terminal rendering is best-effort; never block plugin load.\n });\n}\n\n// iOS/Android disable DynamicViewLoader, so register this view's already-bundled\n// component as an in-process app-shell page. Web/desktop dedupe it against the\n// agent-served bundle entry (network wins -> DynamicViewLoader), so it only adds\n// the render path on native. See packages/app/src/mobile-plugin-views.ts.\nregisterAppShellPage({\n id: \"polymarket\",\n pluginId: \"@elizaos/plugin-polymarket-app\",\n label: \"Polymarket\",\n icon: \"BarChart2\",\n path: \"/polymarket\",\n loader: () =>\n import(\"./polymarket-view-bundle.js\").then((m) => ({\n default: m.PolymarketView,\n })),\n});\n"],"mappings":"AAAA,OAAO;AACP,SAAS,4BAA4B;AAKrC,IAAI,OAAO,WAAW,aAAa;AACjC,OAAK,OAAO,6BAA6B,EACtC,KAAK,CAAC,MAAM,EAAE,+BAA+B,CAAC,EAC9C,MAAM,MAAM;AAAA,EAEb,CAAC;AACL;AAMA,qBAAqB;AAAA,EACnB,IAAI;AAAA,EACJ,UAAU;AAAA,EACV,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,QAAQ,MACN,OAAO,6BAA6B,EAAE,KAAK,CAAC,OAAO;AAAA,IACjD,SAAS,EAAE;AAAA,EACb,EAAE;AACN,CAAC;","names":[]}
|
package/dist/routes.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type http from "node:http";
|
|
2
|
+
export interface PolymarketRouteState {
|
|
3
|
+
fetchImpl?: typeof fetch;
|
|
4
|
+
env?: Record<string, string | undefined>;
|
|
5
|
+
}
|
|
6
|
+
export declare function handlePolymarketRoute(req: http.IncomingMessage, res: http.ServerResponse, pathname: string, method: string, state?: PolymarketRouteState): Promise<boolean>;
|
|
7
|
+
//# sourceMappingURL=routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAyBlC,MAAM,WAAW,oBAAoB;IACnC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;CAC1C;AA+ED,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,IAAI,CAAC,eAAe,EACzB,GAAG,EAAE,IAAI,CAAC,cAAc,EACxB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,KAAK,GAAE,oBAAyB,GAC/B,OAAO,CAAC,OAAO,CAAC,CA4ClB"}
|
package/dist/routes.js
ADDED
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
import { sendJson, sendJsonError } from "@elizaos/app-core/api/response";
|
|
2
|
+
import { logger } from "@elizaos/core";
|
|
3
|
+
import {
|
|
4
|
+
derivePolymarketTopOfBook
|
|
5
|
+
} from "./orderbook.js";
|
|
6
|
+
import {
|
|
7
|
+
POLYMARKET_CLOB_API_BASE,
|
|
8
|
+
POLYMARKET_DATA_API_BASE,
|
|
9
|
+
POLYMARKET_GAMMA_API_BASE,
|
|
10
|
+
POLYMARKET_TRADING_ENV_VARS
|
|
11
|
+
} from "./polymarket-contracts.js";
|
|
12
|
+
const POLYMARKET_ADDRESS_ENV_KEYS = [
|
|
13
|
+
"POLYMARKET_WALLET_ADDRESS",
|
|
14
|
+
"POLYMARKET_ADDRESS",
|
|
15
|
+
"STEWARD_EVM_ADDRESS",
|
|
16
|
+
"ELIZA_MANAGED_EVM_ADDRESS"
|
|
17
|
+
];
|
|
18
|
+
const HEX_ADDRESS_PATTERN = /^0x[a-fA-F0-9]{40}$/;
|
|
19
|
+
const DISABLED_TRADING_REASON = "Trading and order management are disabled in this app integration. Configure a signed CLOB execution path before enabling these routes.";
|
|
20
|
+
const TRADING_DISABLED_REASON = "Signed Polymarket CLOB trading is disabled in this app integration.";
|
|
21
|
+
const TRADING_ENV_ALIASES = {
|
|
22
|
+
CLOB_API_KEY: ["POLYMARKET_CLOB_API_KEY"],
|
|
23
|
+
CLOB_API_SECRET: ["POLYMARKET_CLOB_SECRET"],
|
|
24
|
+
CLOB_API_PASSPHRASE: ["POLYMARKET_CLOB_PASSPHRASE"]
|
|
25
|
+
};
|
|
26
|
+
async function handlePolymarketRoute(req, res, pathname, method, state = {}) {
|
|
27
|
+
if (!pathname.startsWith("/api/polymarket")) return false;
|
|
28
|
+
if (method === "GET" && pathname === "/api/polymarket/status") {
|
|
29
|
+
sendJson(res, 200, buildStatusResponse(state.env ?? process.env));
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
if (method === "GET" && pathname === "/api/polymarket/markets") {
|
|
33
|
+
await handleMarkets(req, res, state.fetchImpl ?? fetch);
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
if (method === "GET" && pathname === "/api/polymarket/market") {
|
|
37
|
+
await handleMarket(req, res, state.fetchImpl ?? fetch);
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
if (method === "GET" && pathname === "/api/polymarket/orderbook") {
|
|
41
|
+
await handleOrderbook(req, res, state.fetchImpl ?? fetch);
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
if (pathname === "/api/polymarket/orders") {
|
|
45
|
+
sendJson(res, 501, buildDisabledResponse());
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
if (method === "GET" && pathname === "/api/polymarket/positions") {
|
|
49
|
+
await handlePositions(
|
|
50
|
+
req,
|
|
51
|
+
res,
|
|
52
|
+
state.fetchImpl ?? fetch,
|
|
53
|
+
state.env ?? process.env
|
|
54
|
+
);
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
if (pathname === "/api/polymarket/positions") {
|
|
58
|
+
sendJson(res, 501, buildDisabledResponse());
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
function resolvePolymarketAddress(env) {
|
|
64
|
+
for (const key of POLYMARKET_ADDRESS_ENV_KEYS) {
|
|
65
|
+
const raw = env[key]?.trim();
|
|
66
|
+
if (raw && HEX_ADDRESS_PATTERN.test(raw)) return raw;
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
function buildStatusResponse(env) {
|
|
71
|
+
const missing = POLYMARKET_TRADING_ENV_VARS.filter(
|
|
72
|
+
(name) => !hasTradingEnvVar(env, name)
|
|
73
|
+
);
|
|
74
|
+
const credentialsReady = missing.length === 0;
|
|
75
|
+
const accountAddress = resolvePolymarketAddress(env);
|
|
76
|
+
return {
|
|
77
|
+
publicReads: {
|
|
78
|
+
ready: true,
|
|
79
|
+
reason: null,
|
|
80
|
+
gammaApiBase: POLYMARKET_GAMMA_API_BASE,
|
|
81
|
+
dataApiBase: POLYMARKET_DATA_API_BASE
|
|
82
|
+
},
|
|
83
|
+
account: {
|
|
84
|
+
ready: accountAddress !== null,
|
|
85
|
+
reason: accountAddress ? null : "No Polymarket wallet address configured. Set POLYMARKET_WALLET_ADDRESS (or a managed EVM address) to read positions.",
|
|
86
|
+
address: accountAddress
|
|
87
|
+
},
|
|
88
|
+
trading: {
|
|
89
|
+
ready: false,
|
|
90
|
+
credentialsReady,
|
|
91
|
+
reason: credentialsReady ? TRADING_DISABLED_REASON : "Trading requires POLYMARKET_PRIVATE_KEY plus CLOB API key, secret, and passphrase.",
|
|
92
|
+
missing,
|
|
93
|
+
clobApiBase: POLYMARKET_CLOB_API_BASE
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function hasTradingEnvVar(env, name) {
|
|
98
|
+
if (env[name]?.trim()) return true;
|
|
99
|
+
return (TRADING_ENV_ALIASES[name] ?? []).some((alias) => env[alias]?.trim());
|
|
100
|
+
}
|
|
101
|
+
async function handleMarkets(req, res, fetchImpl) {
|
|
102
|
+
const requestUrl = new URL(req.url ?? "/api/polymarket/markets", "http://x");
|
|
103
|
+
const gammaUrl = new URL("/markets", POLYMARKET_GAMMA_API_BASE);
|
|
104
|
+
gammaUrl.searchParams.set(
|
|
105
|
+
"active",
|
|
106
|
+
readBooleanQuery(requestUrl, "active", true)
|
|
107
|
+
);
|
|
108
|
+
gammaUrl.searchParams.set(
|
|
109
|
+
"closed",
|
|
110
|
+
readBooleanQuery(requestUrl, "closed", false)
|
|
111
|
+
);
|
|
112
|
+
gammaUrl.searchParams.set("order", readOrderQuery(requestUrl));
|
|
113
|
+
gammaUrl.searchParams.set(
|
|
114
|
+
"ascending",
|
|
115
|
+
readBooleanQuery(requestUrl, "ascending", false)
|
|
116
|
+
);
|
|
117
|
+
gammaUrl.searchParams.set(
|
|
118
|
+
"limit",
|
|
119
|
+
String(readIntegerQuery(requestUrl, "limit", 20, 1, 100))
|
|
120
|
+
);
|
|
121
|
+
gammaUrl.searchParams.set(
|
|
122
|
+
"offset",
|
|
123
|
+
String(readIntegerQuery(requestUrl, "offset", 0, 0, 1e4))
|
|
124
|
+
);
|
|
125
|
+
const tagId = requestUrl.searchParams.get("tag_id")?.trim();
|
|
126
|
+
if (tagId) gammaUrl.searchParams.set("tag_id", tagId);
|
|
127
|
+
try {
|
|
128
|
+
const payload = await fetchJson(fetchImpl, gammaUrl);
|
|
129
|
+
if (!Array.isArray(payload)) {
|
|
130
|
+
sendJsonError(
|
|
131
|
+
res,
|
|
132
|
+
502,
|
|
133
|
+
"Polymarket Gamma returned an invalid markets payload"
|
|
134
|
+
);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const response = {
|
|
138
|
+
markets: payload.flatMap(readGammaMarket),
|
|
139
|
+
source: { api: "gamma", endpoint: gammaUrl.toString() }
|
|
140
|
+
};
|
|
141
|
+
sendJson(res, 200, response);
|
|
142
|
+
} catch (error) {
|
|
143
|
+
logger.error(
|
|
144
|
+
{
|
|
145
|
+
error: error instanceof Error ? error.message : String(error)
|
|
146
|
+
},
|
|
147
|
+
"[PolymarketRoutes] Gamma markets request failed"
|
|
148
|
+
);
|
|
149
|
+
sendJsonError(res, 502, "Polymarket markets request failed");
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async function handleMarket(req, res, fetchImpl) {
|
|
153
|
+
const requestUrl = new URL(req.url ?? "/api/polymarket/market", "http://x");
|
|
154
|
+
const id = requestUrl.searchParams.get("id")?.trim();
|
|
155
|
+
const slug = requestUrl.searchParams.get("slug")?.trim();
|
|
156
|
+
if (!id && !slug) {
|
|
157
|
+
sendJsonError(res, 400, "Missing market id or slug");
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
const gammaUrl = id ? new URL(`/markets/${encodeURIComponent(id)}`, POLYMARKET_GAMMA_API_BASE) : new URL("/markets", POLYMARKET_GAMMA_API_BASE);
|
|
161
|
+
if (slug && !id) gammaUrl.searchParams.set("slug", slug);
|
|
162
|
+
try {
|
|
163
|
+
const payload = await fetchJson(fetchImpl, gammaUrl);
|
|
164
|
+
const record = Array.isArray(payload) ? payload[0] : payload;
|
|
165
|
+
const market = readGammaMarket(record)[0] ?? null;
|
|
166
|
+
const response = {
|
|
167
|
+
market,
|
|
168
|
+
source: { api: "gamma", endpoint: gammaUrl.toString() }
|
|
169
|
+
};
|
|
170
|
+
sendJson(res, 200, response);
|
|
171
|
+
} catch (error) {
|
|
172
|
+
logger.error(
|
|
173
|
+
{
|
|
174
|
+
error: error instanceof Error ? error.message : String(error)
|
|
175
|
+
},
|
|
176
|
+
"[PolymarketRoutes] Gamma market request failed"
|
|
177
|
+
);
|
|
178
|
+
sendJsonError(res, 502, "Polymarket market request failed");
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
async function handleOrderbook(req, res, fetchImpl) {
|
|
182
|
+
const requestUrl = new URL(
|
|
183
|
+
req.url ?? "/api/polymarket/orderbook",
|
|
184
|
+
"http://x"
|
|
185
|
+
);
|
|
186
|
+
const tokenId = requestUrl.searchParams.get("token_id")?.trim() || requestUrl.searchParams.get("tokenId")?.trim();
|
|
187
|
+
if (!tokenId) {
|
|
188
|
+
sendJsonError(res, 400, "Missing token_id");
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const clobUrl = new URL("/book", POLYMARKET_CLOB_API_BASE);
|
|
192
|
+
clobUrl.searchParams.set("token_id", tokenId);
|
|
193
|
+
try {
|
|
194
|
+
const payload = await fetchJson(fetchImpl, clobUrl);
|
|
195
|
+
if (!isRecord(payload)) {
|
|
196
|
+
sendJsonError(res, 502, "Polymarket CLOB returned an invalid orderbook");
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const record = payload;
|
|
200
|
+
const bids = readOrderbookLevels(record.bids);
|
|
201
|
+
const asks = readOrderbookLevels(record.asks);
|
|
202
|
+
const top = derivePolymarketTopOfBook({ bids, asks });
|
|
203
|
+
const response = {
|
|
204
|
+
tokenId,
|
|
205
|
+
market: readString(record.market),
|
|
206
|
+
assetId: readString(record.asset_id),
|
|
207
|
+
bids,
|
|
208
|
+
asks,
|
|
209
|
+
bestBid: top.bestBid?.price ?? null,
|
|
210
|
+
bestBidSize: top.bestBid?.size ?? null,
|
|
211
|
+
bestAsk: top.bestAsk?.price ?? null,
|
|
212
|
+
bestAskSize: top.bestAsk?.size ?? null,
|
|
213
|
+
midpoint: top.midpoint,
|
|
214
|
+
spread: top.spread,
|
|
215
|
+
bidLevels: bids.length,
|
|
216
|
+
askLevels: asks.length,
|
|
217
|
+
lastTradePrice: readNumericString(record.last_trade_price),
|
|
218
|
+
tickSize: readNumericString(record.tick_size),
|
|
219
|
+
source: { api: "clob", endpoint: clobUrl.toString() }
|
|
220
|
+
};
|
|
221
|
+
sendJson(res, 200, response);
|
|
222
|
+
} catch (error) {
|
|
223
|
+
logger.error(
|
|
224
|
+
{
|
|
225
|
+
error: error instanceof Error ? error.message : String(error)
|
|
226
|
+
},
|
|
227
|
+
"[PolymarketRoutes] CLOB orderbook request failed"
|
|
228
|
+
);
|
|
229
|
+
sendJsonError(res, 502, "Polymarket orderbook request failed");
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
async function handlePositions(req, res, fetchImpl, env) {
|
|
233
|
+
const requestUrl = new URL(
|
|
234
|
+
req.url ?? "/api/polymarket/positions",
|
|
235
|
+
"http://x"
|
|
236
|
+
);
|
|
237
|
+
const user = requestUrl.searchParams.get("user")?.trim() || resolvePolymarketAddress(env);
|
|
238
|
+
if (!user) {
|
|
239
|
+
sendJsonError(
|
|
240
|
+
res,
|
|
241
|
+
400,
|
|
242
|
+
"Missing user wallet address and no agent Polymarket address is configured"
|
|
243
|
+
);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
const dataUrl = new URL("/positions", POLYMARKET_DATA_API_BASE);
|
|
247
|
+
dataUrl.searchParams.set("user", user);
|
|
248
|
+
dataUrl.searchParams.set(
|
|
249
|
+
"limit",
|
|
250
|
+
String(readIntegerQuery(requestUrl, "limit", 50, 1, 250))
|
|
251
|
+
);
|
|
252
|
+
try {
|
|
253
|
+
const payload = await fetchJson(fetchImpl, dataUrl);
|
|
254
|
+
if (!Array.isArray(payload)) {
|
|
255
|
+
sendJsonError(
|
|
256
|
+
res,
|
|
257
|
+
502,
|
|
258
|
+
"Polymarket Data API returned an invalid positions payload"
|
|
259
|
+
);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
const positions = payload.flatMap(readDataPosition);
|
|
263
|
+
const response = {
|
|
264
|
+
positions,
|
|
265
|
+
user,
|
|
266
|
+
summary: summarizePositions(positions),
|
|
267
|
+
source: { api: "data", endpoint: dataUrl.toString() }
|
|
268
|
+
};
|
|
269
|
+
sendJson(res, 200, response);
|
|
270
|
+
} catch (error) {
|
|
271
|
+
logger.error(
|
|
272
|
+
{
|
|
273
|
+
error: error instanceof Error ? error.message : String(error)
|
|
274
|
+
},
|
|
275
|
+
"[PolymarketRoutes] Data API positions request failed"
|
|
276
|
+
);
|
|
277
|
+
sendJsonError(res, 502, "Polymarket positions request failed");
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
async function fetchJson(fetchImpl, url) {
|
|
281
|
+
const response = await fetchImpl(url, {
|
|
282
|
+
headers: { accept: "application/json" },
|
|
283
|
+
signal: AbortSignal.timeout(15e3)
|
|
284
|
+
});
|
|
285
|
+
if (!response.ok) {
|
|
286
|
+
const text = await response.text().catch(() => "");
|
|
287
|
+
throw new Error(
|
|
288
|
+
`${url.pathname} failed with ${response.status}: ${text.slice(0, 200)}`
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
return response.json();
|
|
292
|
+
}
|
|
293
|
+
function readGammaMarket(value) {
|
|
294
|
+
if (!isRecord(value)) return [];
|
|
295
|
+
const record = value;
|
|
296
|
+
const id = readString(record.id);
|
|
297
|
+
if (!id) return [];
|
|
298
|
+
const outcomes = readStringArray(record.outcomes);
|
|
299
|
+
const outcomePrices = readStringArray(record.outcomePrices);
|
|
300
|
+
return [
|
|
301
|
+
{
|
|
302
|
+
id,
|
|
303
|
+
slug: readString(record.slug),
|
|
304
|
+
question: readString(record.question),
|
|
305
|
+
description: readString(record.description),
|
|
306
|
+
category: readString(record.category),
|
|
307
|
+
active: readBoolean(record.active),
|
|
308
|
+
closed: readBoolean(record.closed),
|
|
309
|
+
archived: readBoolean(record.archived),
|
|
310
|
+
restricted: readBoolean(record.restricted),
|
|
311
|
+
enableOrderBook: readBoolean(record.enableOrderBook),
|
|
312
|
+
conditionId: readString(record.conditionId),
|
|
313
|
+
clobTokenIds: readStringArray(record.clobTokenIds),
|
|
314
|
+
outcomes: outcomes.map((name, index) => ({
|
|
315
|
+
name,
|
|
316
|
+
price: outcomePrices[index] ?? null
|
|
317
|
+
})),
|
|
318
|
+
liquidity: readNumericString(record.liquidity),
|
|
319
|
+
volume: readNumericString(record.volume),
|
|
320
|
+
volume24hr: readNumericString(record.volume24hr),
|
|
321
|
+
lastTradePrice: readNumericString(record.lastTradePrice),
|
|
322
|
+
bestBid: readNumericString(record.bestBid),
|
|
323
|
+
bestAsk: readNumericString(record.bestAsk),
|
|
324
|
+
image: readString(record.image),
|
|
325
|
+
icon: readString(record.icon),
|
|
326
|
+
endDate: readString(record.endDate),
|
|
327
|
+
startDate: readString(record.startDate),
|
|
328
|
+
updatedAt: readString(record.updatedAt)
|
|
329
|
+
}
|
|
330
|
+
];
|
|
331
|
+
}
|
|
332
|
+
function readDataPosition(value) {
|
|
333
|
+
if (!isRecord(value)) return [];
|
|
334
|
+
const record = value;
|
|
335
|
+
return [
|
|
336
|
+
{
|
|
337
|
+
marketId: readString(record.marketId),
|
|
338
|
+
conditionId: readString(record.conditionId),
|
|
339
|
+
question: readString(record.question),
|
|
340
|
+
outcome: readString(record.outcome),
|
|
341
|
+
size: readNumericString(record.size),
|
|
342
|
+
currentValue: readNumericString(record.currentValue),
|
|
343
|
+
cashPnl: readNumericString(record.cashPnl),
|
|
344
|
+
percentPnl: readNumericString(record.percentPnl),
|
|
345
|
+
icon: readString(record.icon),
|
|
346
|
+
slug: readString(record.slug)
|
|
347
|
+
}
|
|
348
|
+
];
|
|
349
|
+
}
|
|
350
|
+
function summarizePositions(positions) {
|
|
351
|
+
if (positions.length === 0) return null;
|
|
352
|
+
let totalValue = 0;
|
|
353
|
+
let totalCashPnl = 0;
|
|
354
|
+
let hasValue = false;
|
|
355
|
+
let hasPnl = false;
|
|
356
|
+
let openPositions = 0;
|
|
357
|
+
for (const position of positions) {
|
|
358
|
+
const size = parseFiniteNumber(position.size);
|
|
359
|
+
if (size !== null && Math.abs(size) > 1e-9) {
|
|
360
|
+
openPositions += 1;
|
|
361
|
+
}
|
|
362
|
+
const value = parseFiniteNumber(position.currentValue);
|
|
363
|
+
if (value !== null) {
|
|
364
|
+
totalValue += value;
|
|
365
|
+
hasValue = true;
|
|
366
|
+
}
|
|
367
|
+
const pnl = parseFiniteNumber(position.cashPnl);
|
|
368
|
+
if (pnl !== null) {
|
|
369
|
+
totalCashPnl += pnl;
|
|
370
|
+
hasPnl = true;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
const costBasis = totalValue - totalCashPnl;
|
|
374
|
+
const totalPercentPnl = hasValue && hasPnl && Math.abs(costBasis) > 1e-9 ? String(totalCashPnl / costBasis) : null;
|
|
375
|
+
return {
|
|
376
|
+
totalValue: hasValue ? String(totalValue) : null,
|
|
377
|
+
totalCashPnl: hasPnl ? String(totalCashPnl) : null,
|
|
378
|
+
totalPercentPnl,
|
|
379
|
+
openPositions
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
function parseFiniteNumber(value) {
|
|
383
|
+
if (value === null) return null;
|
|
384
|
+
const parsed = Number(value);
|
|
385
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
386
|
+
}
|
|
387
|
+
function buildDisabledResponse() {
|
|
388
|
+
return {
|
|
389
|
+
enabled: false,
|
|
390
|
+
reason: DISABLED_TRADING_REASON,
|
|
391
|
+
requiredForTrading: POLYMARKET_TRADING_ENV_VARS
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
function readIntegerQuery(url, key, fallback, min, max) {
|
|
395
|
+
const raw = url.searchParams.get(key);
|
|
396
|
+
if (!raw) return fallback;
|
|
397
|
+
const parsed = Number.parseInt(raw, 10);
|
|
398
|
+
if (!Number.isFinite(parsed)) return fallback;
|
|
399
|
+
return Math.min(max, Math.max(min, parsed));
|
|
400
|
+
}
|
|
401
|
+
function readBooleanQuery(url, key, fallback) {
|
|
402
|
+
const raw = url.searchParams.get(key);
|
|
403
|
+
if (raw === "true" || raw === "false") return raw;
|
|
404
|
+
return String(fallback);
|
|
405
|
+
}
|
|
406
|
+
function readOrderQuery(url) {
|
|
407
|
+
const raw = url.searchParams.get("order")?.trim();
|
|
408
|
+
return raw || "volume_24hr";
|
|
409
|
+
}
|
|
410
|
+
function isRecord(value) {
|
|
411
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
412
|
+
}
|
|
413
|
+
function readString(value) {
|
|
414
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
415
|
+
}
|
|
416
|
+
function readBoolean(value) {
|
|
417
|
+
return typeof value === "boolean" ? value : null;
|
|
418
|
+
}
|
|
419
|
+
function readNumericString(value) {
|
|
420
|
+
if (typeof value === "string" && value.trim()) return value.trim();
|
|
421
|
+
if (typeof value === "number" && Number.isFinite(value)) return String(value);
|
|
422
|
+
return null;
|
|
423
|
+
}
|
|
424
|
+
function readOrderbookLevels(value) {
|
|
425
|
+
if (!Array.isArray(value)) return [];
|
|
426
|
+
return value.flatMap((item) => {
|
|
427
|
+
if (!isRecord(item)) return [];
|
|
428
|
+
const price = readNumericString(item.price);
|
|
429
|
+
const size = readNumericString(item.size);
|
|
430
|
+
return price && size ? [{ price, size }] : [];
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
function readStringArray(value) {
|
|
434
|
+
if (Array.isArray(value)) {
|
|
435
|
+
return value.flatMap((item) => {
|
|
436
|
+
const text2 = readString(item);
|
|
437
|
+
return text2 ? [text2] : [];
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
const text = readString(value);
|
|
441
|
+
if (!text) return [];
|
|
442
|
+
try {
|
|
443
|
+
const parsed = JSON.parse(text);
|
|
444
|
+
return Array.isArray(parsed) ? parsed.flatMap((item) => {
|
|
445
|
+
const itemText = readString(item);
|
|
446
|
+
return itemText ? [itemText] : [];
|
|
447
|
+
}) : [];
|
|
448
|
+
} catch {
|
|
449
|
+
return [];
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
export {
|
|
453
|
+
handlePolymarketRoute
|
|
454
|
+
};
|
|
455
|
+
//# sourceMappingURL=routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/routes.ts"],"sourcesContent":["import type http from \"node:http\";\nimport { sendJson, sendJsonError } from \"@elizaos/app-core/api/response\";\nimport { logger } from \"@elizaos/core\";\nimport {\n derivePolymarketTopOfBook,\n type PolymarketOrderbookLevel,\n} from \"./orderbook.js\";\nimport {\n POLYMARKET_CLOB_API_BASE,\n POLYMARKET_DATA_API_BASE,\n POLYMARKET_GAMMA_API_BASE,\n POLYMARKET_TRADING_ENV_VARS,\n type PolymarketDisabledResponse,\n type PolymarketMarket,\n type PolymarketMarketOutcome,\n type PolymarketMarketResponse,\n type PolymarketMarketsResponse,\n type PolymarketOrderbookResponse,\n type PolymarketPosition,\n type PolymarketPositionsResponse,\n type PolymarketPositionsSummary,\n type PolymarketStatusResponse,\n type PolymarketTradingEnvVar,\n} from \"./polymarket-contracts.js\";\n\nexport interface PolymarketRouteState {\n fetchImpl?: typeof fetch;\n env?: Record<string, string | undefined>;\n}\n\ninterface GammaMarketRecord {\n id?: unknown;\n slug?: unknown;\n question?: unknown;\n description?: unknown;\n category?: unknown;\n active?: unknown;\n closed?: unknown;\n archived?: unknown;\n restricted?: unknown;\n enableOrderBook?: unknown;\n conditionId?: unknown;\n clobTokenIds?: unknown;\n outcomes?: unknown;\n outcomePrices?: unknown;\n liquidity?: unknown;\n volume?: unknown;\n volume24hr?: unknown;\n lastTradePrice?: unknown;\n bestBid?: unknown;\n bestAsk?: unknown;\n image?: unknown;\n icon?: unknown;\n endDate?: unknown;\n startDate?: unknown;\n updatedAt?: unknown;\n}\n\ninterface DataPositionRecord {\n marketId?: unknown;\n conditionId?: unknown;\n question?: unknown;\n outcome?: unknown;\n size?: unknown;\n currentValue?: unknown;\n cashPnl?: unknown;\n percentPnl?: unknown;\n icon?: unknown;\n slug?: unknown;\n}\n\ninterface ClobOrderbookRecord {\n market?: unknown;\n asset_id?: unknown;\n bids?: unknown;\n asks?: unknown;\n tick_size?: unknown;\n last_trade_price?: unknown;\n}\n\n// Env keys consulted (in order) to resolve the agent's Polygon wallet for\n// reading its own Polymarket positions. POLYMARKET_WALLET_ADDRESS is the\n// venue-specific override; the STEWARD/managed keys mirror the resolution the\n// sibling Hyperliquid app-plugin uses so a single managed EVM address powers\n// both venues.\nconst POLYMARKET_ADDRESS_ENV_KEYS = [\n \"POLYMARKET_WALLET_ADDRESS\",\n \"POLYMARKET_ADDRESS\",\n \"STEWARD_EVM_ADDRESS\",\n \"ELIZA_MANAGED_EVM_ADDRESS\",\n] as const;\n\nconst HEX_ADDRESS_PATTERN = /^0x[a-fA-F0-9]{40}$/;\n\nconst DISABLED_TRADING_REASON =\n \"Trading and order management are disabled in this app integration. Configure a signed CLOB execution path before enabling these routes.\";\n\nconst TRADING_DISABLED_REASON =\n \"Signed Polymarket CLOB trading is disabled in this app integration.\";\n\nconst TRADING_ENV_ALIASES: Partial<Record<PolymarketTradingEnvVar, string[]>> =\n {\n CLOB_API_KEY: [\"POLYMARKET_CLOB_API_KEY\"],\n CLOB_API_SECRET: [\"POLYMARKET_CLOB_SECRET\"],\n CLOB_API_PASSPHRASE: [\"POLYMARKET_CLOB_PASSPHRASE\"],\n };\n\nexport async function handlePolymarketRoute(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n pathname: string,\n method: string,\n state: PolymarketRouteState = {},\n): Promise<boolean> {\n if (!pathname.startsWith(\"/api/polymarket\")) return false;\n\n if (method === \"GET\" && pathname === \"/api/polymarket/status\") {\n sendJson(res, 200, buildStatusResponse(state.env ?? process.env));\n return true;\n }\n\n if (method === \"GET\" && pathname === \"/api/polymarket/markets\") {\n await handleMarkets(req, res, state.fetchImpl ?? fetch);\n return true;\n }\n\n if (method === \"GET\" && pathname === \"/api/polymarket/market\") {\n await handleMarket(req, res, state.fetchImpl ?? fetch);\n return true;\n }\n\n if (method === \"GET\" && pathname === \"/api/polymarket/orderbook\") {\n await handleOrderbook(req, res, state.fetchImpl ?? fetch);\n return true;\n }\n\n if (pathname === \"/api/polymarket/orders\") {\n sendJson(res, 501, buildDisabledResponse());\n return true;\n }\n\n if (method === \"GET\" && pathname === \"/api/polymarket/positions\") {\n await handlePositions(\n req,\n res,\n state.fetchImpl ?? fetch,\n state.env ?? process.env,\n );\n return true;\n }\n\n if (pathname === \"/api/polymarket/positions\") {\n sendJson(res, 501, buildDisabledResponse());\n return true;\n }\n\n return false;\n}\n\nfunction resolvePolymarketAddress(\n env: Record<string, string | undefined>,\n): string | null {\n for (const key of POLYMARKET_ADDRESS_ENV_KEYS) {\n const raw = env[key]?.trim();\n if (raw && HEX_ADDRESS_PATTERN.test(raw)) return raw;\n }\n return null;\n}\n\nfunction buildStatusResponse(\n env: Record<string, string | undefined>,\n): PolymarketStatusResponse {\n const missing = POLYMARKET_TRADING_ENV_VARS.filter(\n (name) => !hasTradingEnvVar(env, name),\n );\n const credentialsReady = missing.length === 0;\n const accountAddress = resolvePolymarketAddress(env);\n return {\n publicReads: {\n ready: true,\n reason: null,\n gammaApiBase: POLYMARKET_GAMMA_API_BASE,\n dataApiBase: POLYMARKET_DATA_API_BASE,\n },\n account: {\n ready: accountAddress !== null,\n reason: accountAddress\n ? null\n : \"No Polymarket wallet address configured. Set POLYMARKET_WALLET_ADDRESS (or a managed EVM address) to read positions.\",\n address: accountAddress,\n },\n trading: {\n ready: false,\n credentialsReady,\n reason: credentialsReady\n ? TRADING_DISABLED_REASON\n : \"Trading requires POLYMARKET_PRIVATE_KEY plus CLOB API key, secret, and passphrase.\",\n missing,\n clobApiBase: POLYMARKET_CLOB_API_BASE,\n },\n };\n}\n\nfunction hasTradingEnvVar(\n env: Record<string, string | undefined>,\n name: PolymarketTradingEnvVar,\n): boolean {\n if (env[name]?.trim()) return true;\n return (TRADING_ENV_ALIASES[name] ?? []).some((alias) => env[alias]?.trim());\n}\n\nasync function handleMarkets(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n fetchImpl: typeof fetch,\n): Promise<void> {\n const requestUrl = new URL(req.url ?? \"/api/polymarket/markets\", \"http://x\");\n const gammaUrl = new URL(\"/markets\", POLYMARKET_GAMMA_API_BASE);\n gammaUrl.searchParams.set(\n \"active\",\n readBooleanQuery(requestUrl, \"active\", true),\n );\n gammaUrl.searchParams.set(\n \"closed\",\n readBooleanQuery(requestUrl, \"closed\", false),\n );\n gammaUrl.searchParams.set(\"order\", readOrderQuery(requestUrl));\n gammaUrl.searchParams.set(\n \"ascending\",\n readBooleanQuery(requestUrl, \"ascending\", false),\n );\n gammaUrl.searchParams.set(\n \"limit\",\n String(readIntegerQuery(requestUrl, \"limit\", 20, 1, 100)),\n );\n gammaUrl.searchParams.set(\n \"offset\",\n String(readIntegerQuery(requestUrl, \"offset\", 0, 0, 10_000)),\n );\n const tagId = requestUrl.searchParams.get(\"tag_id\")?.trim();\n if (tagId) gammaUrl.searchParams.set(\"tag_id\", tagId);\n\n try {\n const payload = await fetchJson(fetchImpl, gammaUrl);\n if (!Array.isArray(payload)) {\n sendJsonError(\n res,\n 502,\n \"Polymarket Gamma returned an invalid markets payload\",\n );\n return;\n }\n const response: PolymarketMarketsResponse = {\n markets: payload.flatMap(readGammaMarket),\n source: { api: \"gamma\", endpoint: gammaUrl.toString() },\n };\n sendJson(res, 200, response);\n } catch (error) {\n logger.error(\n {\n error: error instanceof Error ? error.message : String(error),\n },\n \"[PolymarketRoutes] Gamma markets request failed\",\n );\n sendJsonError(res, 502, \"Polymarket markets request failed\");\n }\n}\n\nasync function handleMarket(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n fetchImpl: typeof fetch,\n): Promise<void> {\n const requestUrl = new URL(req.url ?? \"/api/polymarket/market\", \"http://x\");\n const id = requestUrl.searchParams.get(\"id\")?.trim();\n const slug = requestUrl.searchParams.get(\"slug\")?.trim();\n\n if (!id && !slug) {\n sendJsonError(res, 400, \"Missing market id or slug\");\n return;\n }\n\n const gammaUrl = id\n ? new URL(`/markets/${encodeURIComponent(id)}`, POLYMARKET_GAMMA_API_BASE)\n : new URL(\"/markets\", POLYMARKET_GAMMA_API_BASE);\n if (slug && !id) gammaUrl.searchParams.set(\"slug\", slug);\n\n try {\n const payload = await fetchJson(fetchImpl, gammaUrl);\n const record = Array.isArray(payload) ? payload[0] : payload;\n const market = readGammaMarket(record)[0] ?? null;\n const response: PolymarketMarketResponse = {\n market,\n source: { api: \"gamma\", endpoint: gammaUrl.toString() },\n };\n sendJson(res, 200, response);\n } catch (error) {\n logger.error(\n {\n error: error instanceof Error ? error.message : String(error),\n },\n \"[PolymarketRoutes] Gamma market request failed\",\n );\n sendJsonError(res, 502, \"Polymarket market request failed\");\n }\n}\n\nasync function handleOrderbook(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n fetchImpl: typeof fetch,\n): Promise<void> {\n const requestUrl = new URL(\n req.url ?? \"/api/polymarket/orderbook\",\n \"http://x\",\n );\n const tokenId =\n requestUrl.searchParams.get(\"token_id\")?.trim() ||\n requestUrl.searchParams.get(\"tokenId\")?.trim();\n if (!tokenId) {\n sendJsonError(res, 400, \"Missing token_id\");\n return;\n }\n\n const clobUrl = new URL(\"/book\", POLYMARKET_CLOB_API_BASE);\n clobUrl.searchParams.set(\"token_id\", tokenId);\n\n try {\n const payload = await fetchJson(fetchImpl, clobUrl);\n if (!isRecord(payload)) {\n sendJsonError(res, 502, \"Polymarket CLOB returned an invalid orderbook\");\n return;\n }\n const record = payload as ClobOrderbookRecord;\n const bids = readOrderbookLevels(record.bids);\n const asks = readOrderbookLevels(record.asks);\n const top = derivePolymarketTopOfBook({ bids, asks });\n const response: PolymarketOrderbookResponse = {\n tokenId,\n market: readString(record.market),\n assetId: readString(record.asset_id),\n bids,\n asks,\n bestBid: top.bestBid?.price ?? null,\n bestBidSize: top.bestBid?.size ?? null,\n bestAsk: top.bestAsk?.price ?? null,\n bestAskSize: top.bestAsk?.size ?? null,\n midpoint: top.midpoint,\n spread: top.spread,\n bidLevels: bids.length,\n askLevels: asks.length,\n lastTradePrice: readNumericString(record.last_trade_price),\n tickSize: readNumericString(record.tick_size),\n source: { api: \"clob\", endpoint: clobUrl.toString() },\n };\n sendJson(res, 200, response);\n } catch (error) {\n logger.error(\n {\n error: error instanceof Error ? error.message : String(error),\n },\n \"[PolymarketRoutes] CLOB orderbook request failed\",\n );\n sendJsonError(res, 502, \"Polymarket orderbook request failed\");\n }\n}\n\nasync function handlePositions(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n fetchImpl: typeof fetch,\n env: Record<string, string | undefined>,\n): Promise<void> {\n const requestUrl = new URL(\n req.url ?? \"/api/polymarket/positions\",\n \"http://x\",\n );\n // Prefer an explicit `user` query, else fall back to the agent's configured\n // Polygon wallet so the AppView can read the agent's own positions without\n // prompting for an address (mirrors the HL app-plugin's account resolution).\n const user =\n requestUrl.searchParams.get(\"user\")?.trim() ||\n resolvePolymarketAddress(env);\n if (!user) {\n sendJsonError(\n res,\n 400,\n \"Missing user wallet address and no agent Polymarket address is configured\",\n );\n return;\n }\n\n const dataUrl = new URL(\"/positions\", POLYMARKET_DATA_API_BASE);\n dataUrl.searchParams.set(\"user\", user);\n dataUrl.searchParams.set(\n \"limit\",\n String(readIntegerQuery(requestUrl, \"limit\", 50, 1, 250)),\n );\n\n try {\n const payload = await fetchJson(fetchImpl, dataUrl);\n if (!Array.isArray(payload)) {\n sendJsonError(\n res,\n 502,\n \"Polymarket Data API returned an invalid positions payload\",\n );\n return;\n }\n const positions = payload.flatMap(readDataPosition);\n const response: PolymarketPositionsResponse = {\n positions,\n user,\n summary: summarizePositions(positions),\n source: { api: \"data\", endpoint: dataUrl.toString() },\n };\n sendJson(res, 200, response);\n } catch (error) {\n logger.error(\n {\n error: error instanceof Error ? error.message : String(error),\n },\n \"[PolymarketRoutes] Data API positions request failed\",\n );\n sendJsonError(res, 502, \"Polymarket positions request failed\");\n }\n}\n\nasync function fetchJson(fetchImpl: typeof fetch, url: URL): Promise<unknown> {\n const response = await fetchImpl(url, {\n headers: { accept: \"application/json\" },\n signal: AbortSignal.timeout(15_000),\n });\n if (!response.ok) {\n const text = await response.text().catch(() => \"\");\n throw new Error(\n `${url.pathname} failed with ${response.status}: ${text.slice(0, 200)}`,\n );\n }\n return response.json();\n}\n\nfunction readGammaMarket(value: unknown): PolymarketMarket[] {\n if (!isRecord(value)) return [];\n const record = value as GammaMarketRecord;\n const id = readString(record.id);\n if (!id) return [];\n const outcomes = readStringArray(record.outcomes);\n const outcomePrices = readStringArray(record.outcomePrices);\n return [\n {\n id,\n slug: readString(record.slug),\n question: readString(record.question),\n description: readString(record.description),\n category: readString(record.category),\n active: readBoolean(record.active),\n closed: readBoolean(record.closed),\n archived: readBoolean(record.archived),\n restricted: readBoolean(record.restricted),\n enableOrderBook: readBoolean(record.enableOrderBook),\n conditionId: readString(record.conditionId),\n clobTokenIds: readStringArray(record.clobTokenIds),\n outcomes: outcomes.map<PolymarketMarketOutcome>((name, index) => ({\n name,\n price: outcomePrices[index] ?? null,\n })),\n liquidity: readNumericString(record.liquidity),\n volume: readNumericString(record.volume),\n volume24hr: readNumericString(record.volume24hr),\n lastTradePrice: readNumericString(record.lastTradePrice),\n bestBid: readNumericString(record.bestBid),\n bestAsk: readNumericString(record.bestAsk),\n image: readString(record.image),\n icon: readString(record.icon),\n endDate: readString(record.endDate),\n startDate: readString(record.startDate),\n updatedAt: readString(record.updatedAt),\n },\n ];\n}\n\nfunction readDataPosition(value: unknown): PolymarketPosition[] {\n if (!isRecord(value)) return [];\n const record = value as DataPositionRecord;\n return [\n {\n marketId: readString(record.marketId),\n conditionId: readString(record.conditionId),\n question: readString(record.question),\n outcome: readString(record.outcome),\n size: readNumericString(record.size),\n currentValue: readNumericString(record.currentValue),\n cashPnl: readNumericString(record.cashPnl),\n percentPnl: readNumericString(record.percentPnl),\n icon: readString(record.icon),\n slug: readString(record.slug),\n },\n ];\n}\n\n/**\n * Aggregate a wallet's open positions into the account-health summary: total\n * current value, total cash PnL, and the implied return on cost basis\n * (cost basis = value - pnl). Returns null when there are no positions so the\n * view can render an honest empty state. Mirrors the HL app-plugin's summed\n * unrealized-PnL aggregate.\n */\nfunction summarizePositions(\n positions: readonly PolymarketPosition[],\n): PolymarketPositionsSummary | null {\n if (positions.length === 0) return null;\n\n let totalValue = 0;\n let totalCashPnl = 0;\n let hasValue = false;\n let hasPnl = false;\n let openPositions = 0;\n\n for (const position of positions) {\n // Only size-bearing rows count as \"open\" — the view filters out zero/dust\n // rows the same way (Math.abs(size) > 1e-9), so the StatTile count and the\n // rendered table must agree.\n const size = parseFiniteNumber(position.size);\n if (size !== null && Math.abs(size) > 1e-9) {\n openPositions += 1;\n }\n const value = parseFiniteNumber(position.currentValue);\n if (value !== null) {\n totalValue += value;\n hasValue = true;\n }\n const pnl = parseFiniteNumber(position.cashPnl);\n if (pnl !== null) {\n totalCashPnl += pnl;\n hasPnl = true;\n }\n }\n\n // Cost basis is value minus realized/unrealized gain; only meaningful when\n // both aggregates are readable and the basis is non-zero.\n const costBasis = totalValue - totalCashPnl;\n const totalPercentPnl =\n hasValue && hasPnl && Math.abs(costBasis) > 1e-9\n ? String(totalCashPnl / costBasis)\n : null;\n\n return {\n totalValue: hasValue ? String(totalValue) : null,\n totalCashPnl: hasPnl ? String(totalCashPnl) : null,\n totalPercentPnl,\n openPositions,\n };\n}\n\nfunction parseFiniteNumber(value: string | null): number | null {\n if (value === null) return null;\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : null;\n}\n\nfunction buildDisabledResponse(): PolymarketDisabledResponse {\n return {\n enabled: false,\n reason: DISABLED_TRADING_REASON,\n requiredForTrading: POLYMARKET_TRADING_ENV_VARS,\n };\n}\n\nfunction readIntegerQuery(\n url: URL,\n key: string,\n fallback: number,\n min: number,\n max: number,\n): number {\n const raw = url.searchParams.get(key);\n if (!raw) return fallback;\n const parsed = Number.parseInt(raw, 10);\n if (!Number.isFinite(parsed)) return fallback;\n return Math.min(max, Math.max(min, parsed));\n}\n\nfunction readBooleanQuery(url: URL, key: string, fallback: boolean): string {\n const raw = url.searchParams.get(key);\n if (raw === \"true\" || raw === \"false\") return raw;\n return String(fallback);\n}\n\nfunction readOrderQuery(url: URL): string {\n const raw = url.searchParams.get(\"order\")?.trim();\n return raw || \"volume_24hr\";\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction readString(value: unknown): string | null {\n return typeof value === \"string\" && value.trim() ? value.trim() : null;\n}\n\nfunction readBoolean(value: unknown): boolean | null {\n return typeof value === \"boolean\" ? value : null;\n}\n\nfunction readNumericString(value: unknown): string | null {\n if (typeof value === \"string\" && value.trim()) return value.trim();\n if (typeof value === \"number\" && Number.isFinite(value)) return String(value);\n return null;\n}\n\nfunction readOrderbookLevels(\n value: unknown,\n): readonly PolymarketOrderbookLevel[] {\n if (!Array.isArray(value)) return [];\n return value.flatMap((item) => {\n if (!isRecord(item)) return [];\n const price = readNumericString(item.price);\n const size = readNumericString(item.size);\n return price && size ? [{ price, size }] : [];\n });\n}\n\nfunction readStringArray(value: unknown): readonly string[] {\n if (Array.isArray(value)) {\n return value.flatMap((item) => {\n const text = readString(item);\n return text ? [text] : [];\n });\n }\n const text = readString(value);\n if (!text) return [];\n try {\n const parsed: unknown = JSON.parse(text);\n return Array.isArray(parsed)\n ? parsed.flatMap((item) => {\n const itemText = readString(item);\n return itemText ? [itemText] : [];\n })\n : [];\n } catch {\n return [];\n }\n}\n"],"mappings":"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","names":["text"]}
|
package/dist/ui.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from "./PolymarketAppView.helpers.ts";
|
|
2
|
+
export * from "./PolymarketAppView.tsx";
|
|
3
|
+
export { PolymarketView } from "./PolymarketView.tsx";
|
|
4
|
+
export { POLYMARKET_APP_NAME, polymarketApp } from "./polymarket-app.ts";
|
|
5
|
+
export * from "./usePolymarketState.ts";
|
|
6
|
+
//# sourceMappingURL=ui.d.ts.map
|
package/dist/ui.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"AAAA,cAAc,gCAAgC,CAAC;AAC/C,cAAc,yBAAyB,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzE,cAAc,yBAAyB,CAAC"}
|
package/dist/ui.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from "./PolymarketAppView.helpers.js";
|
|
2
|
+
export * from "./PolymarketAppView.js";
|
|
3
|
+
import { PolymarketView } from "./PolymarketView.js";
|
|
4
|
+
import { POLYMARKET_APP_NAME, polymarketApp } from "./polymarket-app.js";
|
|
5
|
+
export * from "./usePolymarketState.js";
|
|
6
|
+
export {
|
|
7
|
+
POLYMARKET_APP_NAME,
|
|
8
|
+
PolymarketView,
|
|
9
|
+
polymarketApp
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=ui.js.map
|
package/dist/ui.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ui.ts"],"sourcesContent":["export * from \"./PolymarketAppView.helpers.js\";\nexport * from \"./PolymarketAppView.js\";\nexport { PolymarketView } from \"./PolymarketView.js\";\nexport { POLYMARKET_APP_NAME, polymarketApp } from \"./polymarket-app.js\";\nexport * from \"./usePolymarketState.js\";\n"],"mappings":"AAAA,cAAc;AACd,cAAc;AACd,SAAS,sBAAsB;AAC/B,SAAS,qBAAqB,qBAAqB;AACnD,cAAc;","names":[]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import "./client";
|
|
2
|
+
import type { PolymarketMarket, PolymarketPositionsResponse, PolymarketStatusResponse } from "./polymarket-contracts";
|
|
3
|
+
export declare function usePolymarketState(): {
|
|
4
|
+
status: PolymarketStatusResponse | null;
|
|
5
|
+
markets: readonly PolymarketMarket[];
|
|
6
|
+
selectedMarket: PolymarketMarket | null;
|
|
7
|
+
setSelectedMarket: import("react").Dispatch<import("react").SetStateAction<PolymarketMarket | null>>;
|
|
8
|
+
positions: PolymarketPositionsResponse | null;
|
|
9
|
+
loading: boolean;
|
|
10
|
+
error: string | null;
|
|
11
|
+
refresh: () => Promise<void>;
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=usePolymarketState.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePolymarketState.d.ts","sourceRoot":"","sources":["../src/usePolymarketState.ts"],"names":[],"mappings":"AAEA,OAAO,UAAU,CAAC;AAElB,OAAO,KAAK,EACV,gBAAgB,EAChB,2BAA2B,EAC3B,wBAAwB,EACzB,MAAM,wBAAwB,CAAC;AAEhC,wBAAgB,kBAAkB;;;;;;;;;EA4DjC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { client } from "@elizaos/app-core";
|
|
2
|
+
import { useCallback, useEffect, useState } from "react";
|
|
3
|
+
import "./client.js";
|
|
4
|
+
function usePolymarketState() {
|
|
5
|
+
const [status, setStatus] = useState(null);
|
|
6
|
+
const [markets, setMarkets] = useState([]);
|
|
7
|
+
const [selectedMarket, setSelectedMarket] = useState(
|
|
8
|
+
null
|
|
9
|
+
);
|
|
10
|
+
const [positions, setPositions] = useState(null);
|
|
11
|
+
const [loading, setLoading] = useState(true);
|
|
12
|
+
const [error, setError] = useState(null);
|
|
13
|
+
const refresh = useCallback(async () => {
|
|
14
|
+
setLoading(true);
|
|
15
|
+
setError(null);
|
|
16
|
+
const polymarketClient = client;
|
|
17
|
+
try {
|
|
18
|
+
const [statusResponse, marketsResponse] = await Promise.all([
|
|
19
|
+
polymarketClient.polymarketStatus(),
|
|
20
|
+
polymarketClient.polymarketMarkets({ limit: 25 })
|
|
21
|
+
]);
|
|
22
|
+
setStatus(statusResponse);
|
|
23
|
+
setMarkets(marketsResponse.markets);
|
|
24
|
+
setSelectedMarket(marketsResponse.markets[0] ?? null);
|
|
25
|
+
if (statusResponse.account.ready) {
|
|
26
|
+
try {
|
|
27
|
+
setPositions(await polymarketClient.polymarketPositions());
|
|
28
|
+
} catch {
|
|
29
|
+
setPositions(null);
|
|
30
|
+
}
|
|
31
|
+
} else {
|
|
32
|
+
setPositions(null);
|
|
33
|
+
}
|
|
34
|
+
} catch (err) {
|
|
35
|
+
setError(
|
|
36
|
+
err instanceof Error ? err.message : "Polymarket refresh failed"
|
|
37
|
+
);
|
|
38
|
+
} finally {
|
|
39
|
+
setLoading(false);
|
|
40
|
+
}
|
|
41
|
+
}, []);
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
void refresh();
|
|
44
|
+
}, [refresh]);
|
|
45
|
+
return {
|
|
46
|
+
status,
|
|
47
|
+
markets,
|
|
48
|
+
selectedMarket,
|
|
49
|
+
setSelectedMarket,
|
|
50
|
+
positions,
|
|
51
|
+
loading,
|
|
52
|
+
error,
|
|
53
|
+
refresh
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
export {
|
|
57
|
+
usePolymarketState
|
|
58
|
+
};
|
|
59
|
+
//# sourceMappingURL=usePolymarketState.js.map
|