polytown 0.1.0
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 +113 -0
- package/dist/index.js +2056 -0
- package/package.json +31 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2056 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { program } from "commander";
|
|
5
|
+
|
|
6
|
+
// src/commands/status.ts
|
|
7
|
+
import { Command } from "commander";
|
|
8
|
+
|
|
9
|
+
// src/lib/config.ts
|
|
10
|
+
import { readFileSync, existsSync } from "fs";
|
|
11
|
+
import { join } from "path";
|
|
12
|
+
import { homedir } from "os";
|
|
13
|
+
var CONFIG_DIR = join(homedir(), ".polytown");
|
|
14
|
+
var CONFIG_ENV_PATH = join(CONFIG_DIR, ".env");
|
|
15
|
+
function loadEnvFile(path) {
|
|
16
|
+
if (!existsSync(path)) return;
|
|
17
|
+
const content = readFileSync(path, "utf-8");
|
|
18
|
+
for (const line of content.split("\n")) {
|
|
19
|
+
const trimmed = line.trim();
|
|
20
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
21
|
+
const eqIdx = trimmed.indexOf("=");
|
|
22
|
+
if (eqIdx === -1) continue;
|
|
23
|
+
const key = trimmed.slice(0, eqIdx).trim();
|
|
24
|
+
const value = trimmed.slice(eqIdx + 1).trim();
|
|
25
|
+
if (!process.env[key]) {
|
|
26
|
+
process.env[key] = value;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
var LOCAL_ENV_PATH = join(process.cwd(), ".env");
|
|
31
|
+
loadEnvFile(LOCAL_ENV_PATH);
|
|
32
|
+
loadEnvFile(CONFIG_ENV_PATH);
|
|
33
|
+
var POLYGON_CHAIN_ID = Number(process.env.POLYMARKET_CHAIN_ID || "137");
|
|
34
|
+
var RPC_URL = process.env.POLYMARKET_RPC_URL || "https://polygon.drpc.org";
|
|
35
|
+
var CLOB_URL = process.env.POLYMARKET_CLOB_URL || "https://proxy.polytown.app";
|
|
36
|
+
var GAMMA_URL = process.env.POLYMARKET_GAMMA_URL || "https://gamma-api.polymarket.com";
|
|
37
|
+
var DATA_URL = process.env.POLYMARKET_DATA_URL || "https://data-api.polymarket.com";
|
|
38
|
+
var BRIDGE_URL = process.env.POLYMARKET_BRIDGE_URL || "https://bridge-api.polymarket.com";
|
|
39
|
+
var RELAYER_URL = process.env.POLYMARKET_RELAYER_URL || "https://proxy.polytown.app/relayer";
|
|
40
|
+
function resolvePrivateKey(flag) {
|
|
41
|
+
const key = flag || process.env.POLYMARKET_PRIVATE_KEY;
|
|
42
|
+
if (!key) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
"No wallet configured. Run `polytown setup` to create or import a wallet."
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
const normalized = key.startsWith("0x") ? key : `0x${key}`;
|
|
48
|
+
return normalized;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// src/commands/status.ts
|
|
52
|
+
async function check(name, url) {
|
|
53
|
+
const start = Date.now();
|
|
54
|
+
try {
|
|
55
|
+
const res = await fetch(url);
|
|
56
|
+
const latency = Date.now() - start;
|
|
57
|
+
return { name, status: res.ok ? "ok" : `error (${res.status})`, latency };
|
|
58
|
+
} catch (e) {
|
|
59
|
+
return { name, status: `error (${e.message})`, latency: Date.now() - start };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
var statusCommand = new Command("status").description("API health checks").action(async () => {
|
|
63
|
+
const results = await Promise.all([
|
|
64
|
+
check("CLOB", `${CLOB_URL}/`),
|
|
65
|
+
check("Gamma", `${GAMMA_URL}/status`),
|
|
66
|
+
check("Data", `${DATA_URL}/`),
|
|
67
|
+
check("RPC", RPC_URL)
|
|
68
|
+
]);
|
|
69
|
+
console.log(JSON.stringify(results, null, 2));
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// src/commands/markets.ts
|
|
73
|
+
import { Command as Command2 } from "commander";
|
|
74
|
+
|
|
75
|
+
// src/lib/gamma.ts
|
|
76
|
+
function buildUrl(base, path, params) {
|
|
77
|
+
const url = new URL(path, base);
|
|
78
|
+
if (params) {
|
|
79
|
+
for (const [key, value] of Object.entries(params)) {
|
|
80
|
+
if (Array.isArray(value)) {
|
|
81
|
+
for (const v of value) {
|
|
82
|
+
if (v !== void 0) {
|
|
83
|
+
url.searchParams.append(key, String(v));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
} else if (value !== void 0) {
|
|
87
|
+
url.searchParams.set(key, String(value));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return url.toString();
|
|
92
|
+
}
|
|
93
|
+
async function fetchJson(url) {
|
|
94
|
+
const res = await fetch(url);
|
|
95
|
+
if (!res.ok) {
|
|
96
|
+
throw new Error(`HTTP ${res.status}: ${res.statusText} \u2014 ${url}`);
|
|
97
|
+
}
|
|
98
|
+
return res.json();
|
|
99
|
+
}
|
|
100
|
+
var GammaClient = class {
|
|
101
|
+
constructor(baseUrl = GAMMA_URL) {
|
|
102
|
+
this.baseUrl = baseUrl;
|
|
103
|
+
}
|
|
104
|
+
// Markets
|
|
105
|
+
async getMarkets(params) {
|
|
106
|
+
return fetchJson(buildUrl(this.baseUrl, "/markets", params));
|
|
107
|
+
}
|
|
108
|
+
async getMarket(idOrSlug) {
|
|
109
|
+
if (/^\d+$/.test(idOrSlug)) {
|
|
110
|
+
return fetchJson(buildUrl(this.baseUrl, `/markets/${idOrSlug}`));
|
|
111
|
+
}
|
|
112
|
+
const results = await fetchJson(buildUrl(this.baseUrl, "/markets", { slug: idOrSlug }));
|
|
113
|
+
return Array.isArray(results) ? results[0] : results;
|
|
114
|
+
}
|
|
115
|
+
async searchMarkets(query, params) {
|
|
116
|
+
return fetchJson(
|
|
117
|
+
buildUrl(this.baseUrl, "/markets", { text_query: query, ...params })
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
// Events
|
|
121
|
+
async getEvents(params) {
|
|
122
|
+
return fetchJson(buildUrl(this.baseUrl, "/events", params));
|
|
123
|
+
}
|
|
124
|
+
async getEvent(idOrSlug) {
|
|
125
|
+
return fetchJson(buildUrl(this.baseUrl, `/events/${idOrSlug}`));
|
|
126
|
+
}
|
|
127
|
+
// Tags
|
|
128
|
+
async getTags(params) {
|
|
129
|
+
return fetchJson(buildUrl(this.baseUrl, "/tags", params));
|
|
130
|
+
}
|
|
131
|
+
async getTag(idOrSlug) {
|
|
132
|
+
return fetchJson(buildUrl(this.baseUrl, `/tags/${idOrSlug}`));
|
|
133
|
+
}
|
|
134
|
+
async getRelated(idOrSlug) {
|
|
135
|
+
return fetchJson(buildUrl(this.baseUrl, `/tags/${idOrSlug}/related`));
|
|
136
|
+
}
|
|
137
|
+
async getRelatedTags(idOrSlug) {
|
|
138
|
+
return fetchJson(buildUrl(this.baseUrl, `/tags/${idOrSlug}/related-tags`));
|
|
139
|
+
}
|
|
140
|
+
// Series
|
|
141
|
+
async getSeries(params) {
|
|
142
|
+
return fetchJson(buildUrl(this.baseUrl, "/series", params));
|
|
143
|
+
}
|
|
144
|
+
async getSeriesById(id) {
|
|
145
|
+
return fetchJson(buildUrl(this.baseUrl, `/series/${id}`));
|
|
146
|
+
}
|
|
147
|
+
// Comments
|
|
148
|
+
async getComments(params) {
|
|
149
|
+
return fetchJson(
|
|
150
|
+
buildUrl(this.baseUrl, "/comments", params)
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
async getComment(id) {
|
|
154
|
+
return fetchJson(buildUrl(this.baseUrl, `/comments/${id}`));
|
|
155
|
+
}
|
|
156
|
+
async getCommentsByUser(address, params) {
|
|
157
|
+
return fetchJson(
|
|
158
|
+
buildUrl(this.baseUrl, `/comments/user_address/${address}`, params)
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
// Profiles
|
|
162
|
+
async getProfile(address) {
|
|
163
|
+
return fetchJson(`https://polymarket.com/api/profile/userData?address=${address}`);
|
|
164
|
+
}
|
|
165
|
+
async getProfileStats(proxyAddress) {
|
|
166
|
+
return fetchJson(`https://data-api.polymarket.com/v1/user-stats?proxyAddress=${proxyAddress}`);
|
|
167
|
+
}
|
|
168
|
+
// Sports
|
|
169
|
+
async getSports() {
|
|
170
|
+
return fetchJson(buildUrl(this.baseUrl, "/sports"));
|
|
171
|
+
}
|
|
172
|
+
async getMarketTypes() {
|
|
173
|
+
return fetchJson(buildUrl(this.baseUrl, "/sports/market-types"));
|
|
174
|
+
}
|
|
175
|
+
async getTeams(params) {
|
|
176
|
+
return fetchJson(
|
|
177
|
+
buildUrl(this.baseUrl, "/sports/teams", params)
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
// Health
|
|
181
|
+
async getStatus() {
|
|
182
|
+
const res = await fetch(buildUrl(this.baseUrl, "/status"));
|
|
183
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`);
|
|
184
|
+
const text = await res.text();
|
|
185
|
+
try {
|
|
186
|
+
return JSON.parse(text);
|
|
187
|
+
} catch {
|
|
188
|
+
return text;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
// src/commands/markets.ts
|
|
194
|
+
var marketsCommand = new Command2("markets").description(
|
|
195
|
+
"Markets commands"
|
|
196
|
+
);
|
|
197
|
+
marketsCommand.command("list").description("List markets").option("--active", "Only active markets").option("--closed", "Only closed markets").option("--cyom", "Filter by create-your-own-market").option("--limit <n>", "Limit results", parseInt).option("--offset <n>", "Offset results", parseInt).option("--order <field>", "Order by field").option("--ascending", "Sort ascending").option("--id <ids>", "Filter by market IDs (comma-separated)").option("--slug <slugs>", "Filter by slug(s)").option("--clob-token-ids <ids>", "Filter by CLOB token IDs").option("--condition-ids <ids>", "Filter by condition IDs").option("--tag-id <id>", "Filter by tag ID", parseInt).option("--related-tags", "Include related tags").option("--include-tag", "Include tag data").option("--liquidity-min <n>", "Min liquidity", parseFloat).option("--liquidity-max <n>", "Max liquidity", parseFloat).option("--volume-min <n>", "Min volume", parseFloat).option("--volume-max <n>", "Max volume", parseFloat).option("--start-date-min <date>", "Earliest start date").option("--start-date-max <date>", "Latest start date").option("--end-date-min <date>", "Earliest end date").option("--end-date-max <date>", "Latest end date").option("--uma-resolution-status <status>", "UMA resolution status").option("--game-id <id>", "Filter by game ID").option("--sports-market-types <types>", "Filter by sports market types").option("--rewards-min-size <n>", "Min rewards size", parseFloat).option("--question-ids <ids>", "Filter by question IDs").action(async (opts) => {
|
|
198
|
+
const gamma = new GammaClient();
|
|
199
|
+
const params = {
|
|
200
|
+
active: opts.active ?? true,
|
|
201
|
+
closed: opts.closed ?? false,
|
|
202
|
+
order: opts.order ?? "volume24hr",
|
|
203
|
+
ascending: opts.ascending ?? false
|
|
204
|
+
};
|
|
205
|
+
if (opts.cyom) params.cyom = true;
|
|
206
|
+
if (opts.limit) params.limit = opts.limit;
|
|
207
|
+
if (opts.offset) params.offset = opts.offset;
|
|
208
|
+
if (opts.id) params.id = opts.id;
|
|
209
|
+
if (opts.slug) params.slug = opts.slug;
|
|
210
|
+
if (opts.clobTokenIds) params.clob_token_ids = opts.clobTokenIds;
|
|
211
|
+
if (opts.conditionIds) params.condition_ids = opts.conditionIds;
|
|
212
|
+
if (opts.tagId) params.tag_id = opts.tagId;
|
|
213
|
+
if (opts.relatedTags) params.related_tags = true;
|
|
214
|
+
if (opts.includeTag) params.include_tag = true;
|
|
215
|
+
if (opts.liquidityMin) params.liquidity_num_min = opts.liquidityMin;
|
|
216
|
+
if (opts.liquidityMax) params.liquidity_num_max = opts.liquidityMax;
|
|
217
|
+
if (opts.volumeMin) params.volume_num_min = opts.volumeMin;
|
|
218
|
+
if (opts.volumeMax) params.volume_num_max = opts.volumeMax;
|
|
219
|
+
if (opts.startDateMin) params.start_date_min = opts.startDateMin;
|
|
220
|
+
if (opts.startDateMax) params.start_date_max = opts.startDateMax;
|
|
221
|
+
if (opts.endDateMin) params.end_date_min = opts.endDateMin;
|
|
222
|
+
if (opts.endDateMax) params.end_date_max = opts.endDateMax;
|
|
223
|
+
if (opts.umaResolutionStatus) params.uma_resolution_status = opts.umaResolutionStatus;
|
|
224
|
+
if (opts.gameId) params.game_id = opts.gameId;
|
|
225
|
+
if (opts.sportsMarketTypes) params.sports_market_types = opts.sportsMarketTypes;
|
|
226
|
+
if (opts.rewardsMinSize) params.rewards_min_size = opts.rewardsMinSize;
|
|
227
|
+
if (opts.questionIds) params.question_ids = opts.questionIds;
|
|
228
|
+
const result = await gamma.getMarkets(params);
|
|
229
|
+
console.log(JSON.stringify(result, null, 2));
|
|
230
|
+
});
|
|
231
|
+
marketsCommand.command("get <id_or_slug>").description("Get market by ID or slug").action(async (idOrSlug) => {
|
|
232
|
+
const gamma = new GammaClient();
|
|
233
|
+
const result = await gamma.getMarket(idOrSlug);
|
|
234
|
+
console.log(JSON.stringify(result, null, 2));
|
|
235
|
+
});
|
|
236
|
+
marketsCommand.command("search <query>").description("Search markets").option("--limit <n>", "Limit results", parseInt).action(async (query, opts) => {
|
|
237
|
+
const gamma = new GammaClient();
|
|
238
|
+
const result = await gamma.searchMarkets(query, opts);
|
|
239
|
+
console.log(JSON.stringify(result, null, 2));
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// src/commands/events.ts
|
|
243
|
+
import { Command as Command3 } from "commander";
|
|
244
|
+
var eventsCommand = new Command3("events").description(
|
|
245
|
+
"Events commands"
|
|
246
|
+
);
|
|
247
|
+
eventsCommand.command("list").description("List events").option("--active", "Only active events").option("--closed", "Only closed events").option("--archived", "Include archived events").option("--featured", "Only featured events").option("--cyom", "Filter by create-your-own-market").option("--limit <n>", "Limit results", parseInt).option("--offset <n>", "Offset results", parseInt).option("--order <field>", "Order by field").option("--ascending", "Sort ascending").option("--id <ids>", "Filter by event IDs (comma-separated)").option("--tag <tag>", "Filter by tag slug").option("--tag-id <id>", "Filter by tag ID", parseInt).option("--exclude-tag-id <ids>", "Exclude tag IDs (comma-separated)").option("--slug <slugs>", "Filter by slug(s)").option("--related-tags", "Include related tags").option("--recurrence <pattern>", "Filter by recurrence pattern").option("--liquidity-min <n>", "Min liquidity", parseFloat).option("--liquidity-max <n>", "Max liquidity", parseFloat).option("--volume-min <n>", "Min volume", parseFloat).option("--volume-max <n>", "Max volume", parseFloat).option("--start-date-min <date>", "Earliest start date").option("--start-date-max <date>", "Latest start date").option("--end-date-min <date>", "Earliest end date").option("--end-date-max <date>", "Latest end date").action(async (opts) => {
|
|
248
|
+
const gamma = new GammaClient();
|
|
249
|
+
const params = {
|
|
250
|
+
active: opts.active ?? true,
|
|
251
|
+
closed: opts.closed ?? false,
|
|
252
|
+
archived: opts.archived ?? false,
|
|
253
|
+
order: opts.order ?? "volume24hr",
|
|
254
|
+
ascending: opts.ascending ?? false
|
|
255
|
+
};
|
|
256
|
+
if (opts.featured) params.featured = true;
|
|
257
|
+
if (opts.cyom) params.cyom = true;
|
|
258
|
+
if (opts.limit) params.limit = opts.limit;
|
|
259
|
+
if (opts.offset) params.offset = opts.offset;
|
|
260
|
+
if (opts.id) params.id = opts.id;
|
|
261
|
+
if (opts.tag) params.tag_slug = opts.tag;
|
|
262
|
+
if (opts.tagId) params.tag_id = opts.tagId;
|
|
263
|
+
if (opts.excludeTagId) params.exclude_tag_id = opts.excludeTagId.split(",");
|
|
264
|
+
if (opts.slug) params.slug = opts.slug;
|
|
265
|
+
if (opts.relatedTags) params.related_tags = true;
|
|
266
|
+
if (opts.recurrence) params.recurrence = opts.recurrence;
|
|
267
|
+
if (opts.liquidityMin) params.liquidity_min = opts.liquidityMin;
|
|
268
|
+
if (opts.liquidityMax) params.liquidity_max = opts.liquidityMax;
|
|
269
|
+
if (opts.volumeMin) params.volume_min = opts.volumeMin;
|
|
270
|
+
if (opts.volumeMax) params.volume_max = opts.volumeMax;
|
|
271
|
+
if (opts.startDateMin) params.start_date_min = opts.startDateMin;
|
|
272
|
+
if (opts.startDateMax) params.start_date_max = opts.startDateMax;
|
|
273
|
+
if (opts.endDateMin) params.end_date_min = opts.endDateMin;
|
|
274
|
+
if (opts.endDateMax) params.end_date_max = opts.endDateMax;
|
|
275
|
+
const result = await gamma.getEvents(params);
|
|
276
|
+
console.log(JSON.stringify(result, null, 2));
|
|
277
|
+
});
|
|
278
|
+
eventsCommand.command("get <id_or_slug>").description("Get event by ID or slug").action(async (idOrSlug) => {
|
|
279
|
+
const gamma = new GammaClient();
|
|
280
|
+
const result = await gamma.getEvent(idOrSlug);
|
|
281
|
+
console.log(JSON.stringify(result, null, 2));
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
// src/commands/tags.ts
|
|
285
|
+
import { Command as Command4 } from "commander";
|
|
286
|
+
var tagsCommand = new Command4("tags").description("Tags commands");
|
|
287
|
+
tagsCommand.command("list").description("List tags").option("--limit <n>", "Limit results", parseInt).option("--offset <n>", "Offset results", parseInt).option("--ascending", "Sort ascending").action(async (opts) => {
|
|
288
|
+
const gamma = new GammaClient();
|
|
289
|
+
const result = await gamma.getTags(opts);
|
|
290
|
+
console.log(JSON.stringify(result, null, 2));
|
|
291
|
+
});
|
|
292
|
+
tagsCommand.command("get <id_or_slug>").description("Get tag by ID or slug").action(async (idOrSlug) => {
|
|
293
|
+
const gamma = new GammaClient();
|
|
294
|
+
const result = await gamma.getTag(idOrSlug);
|
|
295
|
+
console.log(JSON.stringify(result, null, 2));
|
|
296
|
+
});
|
|
297
|
+
tagsCommand.command("related <id_or_slug>").description("Get related items for tag").action(async (idOrSlug) => {
|
|
298
|
+
const gamma = new GammaClient();
|
|
299
|
+
const result = await gamma.getRelated(idOrSlug);
|
|
300
|
+
console.log(JSON.stringify(result, null, 2));
|
|
301
|
+
});
|
|
302
|
+
tagsCommand.command("related-tags <id_or_slug>").description("Get related tags").action(async (idOrSlug) => {
|
|
303
|
+
const gamma = new GammaClient();
|
|
304
|
+
const result = await gamma.getRelatedTags(idOrSlug);
|
|
305
|
+
console.log(JSON.stringify(result, null, 2));
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
// src/commands/series.ts
|
|
309
|
+
import { Command as Command5 } from "commander";
|
|
310
|
+
var seriesCommand = new Command5("series").description(
|
|
311
|
+
"Series commands"
|
|
312
|
+
);
|
|
313
|
+
seriesCommand.command("list").description("List series").option("--limit <n>", "Limit results", parseInt).option("--offset <n>", "Offset results", parseInt).option("--order <field>", "Order by field").option("--ascending", "Sort ascending").option("--closed", "Include closed").action(async (opts) => {
|
|
314
|
+
const gamma = new GammaClient();
|
|
315
|
+
const result = await gamma.getSeries(opts);
|
|
316
|
+
console.log(JSON.stringify(result, null, 2));
|
|
317
|
+
});
|
|
318
|
+
seriesCommand.command("get <id>").description("Get series by ID").action(async (id) => {
|
|
319
|
+
const gamma = new GammaClient();
|
|
320
|
+
const result = await gamma.getSeriesById(id);
|
|
321
|
+
console.log(JSON.stringify(result, null, 2));
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
// src/commands/comments.ts
|
|
325
|
+
import { Command as Command6 } from "commander";
|
|
326
|
+
var out = (data) => console.log(JSON.stringify(data, null, 2));
|
|
327
|
+
var commentsCommand = new Command6("comments").description("Get comments for an event by ID").argument("<event_id>", "Event ID (use 'resolve' command to get ID from URL)").option("--limit <n>", "Limit results", parseInt).option("--offset <n>", "Offset results", parseInt).option("--order <field>", "Order by (createdAt, updatedAt, reactionCount)", "createdAt").option("--ascending", "Sort ascending").option("--holders-only", "Only show holders' comments").action(async (eventId, opts) => {
|
|
328
|
+
const gamma = new GammaClient();
|
|
329
|
+
const result = await gamma.getComments({
|
|
330
|
+
parent_entity_type: "Event",
|
|
331
|
+
parent_entity_id: Number(eventId),
|
|
332
|
+
limit: opts.limit,
|
|
333
|
+
offset: opts.offset,
|
|
334
|
+
order: opts.order,
|
|
335
|
+
ascending: opts.ascending,
|
|
336
|
+
holders_only: opts.holdersOnly
|
|
337
|
+
});
|
|
338
|
+
out(result);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
// src/commands/profiles.ts
|
|
342
|
+
import { Command as Command7 } from "commander";
|
|
343
|
+
import { privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
|
|
344
|
+
|
|
345
|
+
// src/lib/wallet.ts
|
|
346
|
+
import { getAddress, encodeAbiParameters, keccak256, concat } from "viem";
|
|
347
|
+
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
|
|
348
|
+
var POLYMARKET_SAFE_FACTORY = "0xaacFeEa03eb1561C4e67d661e40682Bd20E3541b";
|
|
349
|
+
var SAFE_INIT_CODE_HASH = "0x2bce2127ff07fb632d16c8347c4ebf501f4841168bed00d9e6ef715ddb6fcecf";
|
|
350
|
+
function deriveGnosisSafeWallet(ownerAddress) {
|
|
351
|
+
const owner = getAddress(ownerAddress);
|
|
352
|
+
const salt = keccak256(
|
|
353
|
+
encodeAbiParameters([{ type: "address" }], [owner])
|
|
354
|
+
);
|
|
355
|
+
const create2Hash = keccak256(
|
|
356
|
+
concat([
|
|
357
|
+
"0xff",
|
|
358
|
+
POLYMARKET_SAFE_FACTORY,
|
|
359
|
+
salt,
|
|
360
|
+
SAFE_INIT_CODE_HASH
|
|
361
|
+
])
|
|
362
|
+
);
|
|
363
|
+
return getAddress(`0x${create2Hash.slice(26)}`);
|
|
364
|
+
}
|
|
365
|
+
function createRandomWallet() {
|
|
366
|
+
const privateKey = generatePrivateKey();
|
|
367
|
+
const account = privateKeyToAccount(privateKey);
|
|
368
|
+
return { privateKey, address: account.address };
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// src/commands/profiles.ts
|
|
372
|
+
var out2 = (data) => console.log(JSON.stringify(data, null, 2));
|
|
373
|
+
var profilesCommand = new Command7("profile").description("Get user profile by address").argument("[address]", "Wallet address (use 'resolve' for @username, defaults to your wallet)").option("--private-key <key>", "Private key").action(async (address, opts) => {
|
|
374
|
+
let addr = address;
|
|
375
|
+
if (!addr) {
|
|
376
|
+
const key = resolvePrivateKey(opts.privateKey);
|
|
377
|
+
const account = privateKeyToAccount2(key);
|
|
378
|
+
addr = deriveGnosisSafeWallet(account.address);
|
|
379
|
+
}
|
|
380
|
+
const gamma = new GammaClient();
|
|
381
|
+
const [profile, stats] = await Promise.all([
|
|
382
|
+
gamma.getProfile(addr).catch(() => null),
|
|
383
|
+
gamma.getProfileStats(addr).catch(() => null)
|
|
384
|
+
]);
|
|
385
|
+
out2({ ...profile, stats });
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
// src/commands/sports.ts
|
|
389
|
+
import { Command as Command8 } from "commander";
|
|
390
|
+
var sportsCommand = new Command8("sports").description(
|
|
391
|
+
"Sports commands"
|
|
392
|
+
);
|
|
393
|
+
sportsCommand.command("list").description("List sports").action(async () => {
|
|
394
|
+
const gamma = new GammaClient();
|
|
395
|
+
const result = await gamma.getSports();
|
|
396
|
+
console.log(JSON.stringify(result, null, 2));
|
|
397
|
+
});
|
|
398
|
+
sportsCommand.command("market-types").description("List market types").action(async () => {
|
|
399
|
+
const gamma = new GammaClient();
|
|
400
|
+
const result = await gamma.getMarketTypes();
|
|
401
|
+
console.log(JSON.stringify(result, null, 2));
|
|
402
|
+
});
|
|
403
|
+
sportsCommand.command("teams").description("List teams").option("--limit <n>", "Limit results", parseInt).option("--offset <n>", "Offset results", parseInt).option("--league <league>", "Filter by league").option("--ascending", "Sort ascending").action(async (opts) => {
|
|
404
|
+
const gamma = new GammaClient();
|
|
405
|
+
const result = await gamma.getTeams(opts);
|
|
406
|
+
console.log(JSON.stringify(result, null, 2));
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
// src/commands/clob.ts
|
|
410
|
+
import { Command as Command9 } from "commander";
|
|
411
|
+
|
|
412
|
+
// src/client.ts
|
|
413
|
+
import { ClobClient } from "@polymarket/clob-client";
|
|
414
|
+
import { createWalletClient, http } from "viem";
|
|
415
|
+
import { privateKeyToAccount as privateKeyToAccount3 } from "viem/accounts";
|
|
416
|
+
import { polygon } from "viem/chains";
|
|
417
|
+
function createReadonlyClobClient() {
|
|
418
|
+
return new ClobClient(CLOB_URL, POLYGON_CHAIN_ID);
|
|
419
|
+
}
|
|
420
|
+
async function createAuthenticatedClobClient(privateKey) {
|
|
421
|
+
const account = privateKeyToAccount3(privateKey);
|
|
422
|
+
const walletClient = createWalletClient({
|
|
423
|
+
account,
|
|
424
|
+
chain: polygon,
|
|
425
|
+
transport: http(RPC_URL)
|
|
426
|
+
});
|
|
427
|
+
const derivedAddress = deriveGnosisSafeWallet(account.address);
|
|
428
|
+
let creds;
|
|
429
|
+
const tempClient = new ClobClient(
|
|
430
|
+
CLOB_URL,
|
|
431
|
+
POLYGON_CHAIN_ID,
|
|
432
|
+
walletClient,
|
|
433
|
+
void 0,
|
|
434
|
+
2,
|
|
435
|
+
// POLY_GNOSIS_SAFE
|
|
436
|
+
derivedAddress
|
|
437
|
+
);
|
|
438
|
+
const origError = console.error;
|
|
439
|
+
console.error = () => {
|
|
440
|
+
};
|
|
441
|
+
try {
|
|
442
|
+
creds = await tempClient.deriveApiKey();
|
|
443
|
+
if (!creds?.key || !creds?.secret) throw new Error("empty");
|
|
444
|
+
} catch {
|
|
445
|
+
try {
|
|
446
|
+
creds = await tempClient.createApiKey();
|
|
447
|
+
if (!creds?.key || !creds?.secret) {
|
|
448
|
+
throw new Error("API key creation returned empty credentials");
|
|
449
|
+
}
|
|
450
|
+
} catch (e) {
|
|
451
|
+
console.error = origError;
|
|
452
|
+
throw new Error(`Failed to obtain CLOB API key: ${e.message || e}`);
|
|
453
|
+
}
|
|
454
|
+
} finally {
|
|
455
|
+
console.error = origError;
|
|
456
|
+
}
|
|
457
|
+
return new ClobClient(
|
|
458
|
+
CLOB_URL,
|
|
459
|
+
POLYGON_CHAIN_ID,
|
|
460
|
+
walletClient,
|
|
461
|
+
creds,
|
|
462
|
+
2,
|
|
463
|
+
// POLY_GNOSIS_SAFE
|
|
464
|
+
derivedAddress
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// src/commands/clob.ts
|
|
469
|
+
import { AssetType } from "@polymarket/clob-client";
|
|
470
|
+
var out3 = (data) => console.log(JSON.stringify(data, null, 2));
|
|
471
|
+
async function quietCall(fn) {
|
|
472
|
+
const origLog = console.log;
|
|
473
|
+
const origError = console.error;
|
|
474
|
+
console.log = () => {
|
|
475
|
+
};
|
|
476
|
+
console.error = () => {
|
|
477
|
+
};
|
|
478
|
+
try {
|
|
479
|
+
return await fn();
|
|
480
|
+
} catch (e) {
|
|
481
|
+
console.log = origLog;
|
|
482
|
+
console.error = origError;
|
|
483
|
+
console.error(`Error: ${formatError(e)}`);
|
|
484
|
+
process.exit(1);
|
|
485
|
+
} finally {
|
|
486
|
+
console.log = origLog;
|
|
487
|
+
console.error = origError;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
function formatError(e) {
|
|
491
|
+
const data = e?.response?.data || e?.data;
|
|
492
|
+
if (data?.error) return data.error;
|
|
493
|
+
return e?.message || String(e);
|
|
494
|
+
}
|
|
495
|
+
async function getAuthClient(opts) {
|
|
496
|
+
try {
|
|
497
|
+
const key = resolvePrivateKey(opts.privateKey);
|
|
498
|
+
return await createAuthenticatedClobClient(key);
|
|
499
|
+
} catch (e) {
|
|
500
|
+
console.error(`Error: ${formatError(e)}`);
|
|
501
|
+
process.exit(1);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
var clobCommand = new Command9("clob").description("CLOB commands");
|
|
505
|
+
clobCommand.command("ok").description("Check CLOB API health").action(async () => {
|
|
506
|
+
const client = createReadonlyClobClient();
|
|
507
|
+
const result = await client.getOk();
|
|
508
|
+
out3(result);
|
|
509
|
+
});
|
|
510
|
+
clobCommand.command("price <token_id>").description("Get price for token").requiredOption("--side <side>", "buy or sell").action(async (tokenId, opts) => {
|
|
511
|
+
const client = createReadonlyClobClient();
|
|
512
|
+
const result = await client.getPrice(tokenId, opts.side);
|
|
513
|
+
out3(result);
|
|
514
|
+
});
|
|
515
|
+
clobCommand.command("batch-prices <ids>").description("Get prices for multiple tokens (comma-separated)").requiredOption("--side <side>", "buy or sell").action(async (ids, opts) => {
|
|
516
|
+
const client = createReadonlyClobClient();
|
|
517
|
+
const tokenIds = ids.split(",");
|
|
518
|
+
const result = await client.getPrices(tokenIds.map((token_id) => ({ token_id, side: opts.side })));
|
|
519
|
+
out3(result);
|
|
520
|
+
});
|
|
521
|
+
clobCommand.command("midpoint <token_id>").description("Get midpoint for token").action(async (tokenId) => {
|
|
522
|
+
const client = createReadonlyClobClient();
|
|
523
|
+
const result = await client.getMidpoint(tokenId);
|
|
524
|
+
out3(result);
|
|
525
|
+
});
|
|
526
|
+
clobCommand.command("midpoints <token_ids>").description("Get midpoints for multiple tokens (comma-separated)").action(async (tokenIds) => {
|
|
527
|
+
const client = createReadonlyClobClient();
|
|
528
|
+
const result = await client.getMidpoints(tokenIds.split(","));
|
|
529
|
+
out3(result);
|
|
530
|
+
});
|
|
531
|
+
clobCommand.command("spread <token_id>").description("Get spread for token").option("--side <side>", "buy or sell").action(async (tokenId, opts) => {
|
|
532
|
+
const client = createReadonlyClobClient();
|
|
533
|
+
const result = await client.getSpread(tokenId);
|
|
534
|
+
out3(result);
|
|
535
|
+
});
|
|
536
|
+
clobCommand.command("spreads <token_ids>").description("Get spreads for multiple tokens (comma-separated)").action(async (tokenIds) => {
|
|
537
|
+
const client = createReadonlyClobClient();
|
|
538
|
+
const result = await client.getSpreads(tokenIds.split(","));
|
|
539
|
+
out3(result);
|
|
540
|
+
});
|
|
541
|
+
clobCommand.command("book <token_id>").description("Get order book for token").action(async (tokenId) => {
|
|
542
|
+
const client = createReadonlyClobClient();
|
|
543
|
+
const result = await client.getOrderBook(tokenId);
|
|
544
|
+
out3(result);
|
|
545
|
+
});
|
|
546
|
+
clobCommand.command("books <token_ids>").description("Get order books for multiple tokens (comma-separated)").action(async (tokenIds) => {
|
|
547
|
+
const client = createReadonlyClobClient();
|
|
548
|
+
const result = await client.getOrderBooks(tokenIds.split(","));
|
|
549
|
+
out3(result);
|
|
550
|
+
});
|
|
551
|
+
clobCommand.command("last-trade <token_id>").description("Get last trade price").action(async (tokenId) => {
|
|
552
|
+
const client = createReadonlyClobClient();
|
|
553
|
+
const result = await client.getLastTradePrice(tokenId);
|
|
554
|
+
out3(result);
|
|
555
|
+
});
|
|
556
|
+
clobCommand.command("last-trades <token_ids>").description("Get last trade prices (comma-separated)").action(async (tokenIds) => {
|
|
557
|
+
const client = createReadonlyClobClient();
|
|
558
|
+
const result = await client.getLastTradesPrices(tokenIds.split(","));
|
|
559
|
+
out3(result);
|
|
560
|
+
});
|
|
561
|
+
clobCommand.command("market <condition_id>").description("Get CLOB market by condition ID").action(async (conditionId) => {
|
|
562
|
+
const client = createReadonlyClobClient();
|
|
563
|
+
const result = await client.getMarket(conditionId);
|
|
564
|
+
out3(result);
|
|
565
|
+
});
|
|
566
|
+
clobCommand.command("tick-size <token_id>").description("Get tick size for token").action(async (tokenId) => {
|
|
567
|
+
const client = createReadonlyClobClient();
|
|
568
|
+
const result = await client.getTickSize(tokenId);
|
|
569
|
+
out3(result);
|
|
570
|
+
});
|
|
571
|
+
clobCommand.command("fee-rate <token_id>").description("Get fee rate for token").option("--private-key <key>", "Private key").action(async (tokenId, opts) => {
|
|
572
|
+
const client = await getAuthClient(opts);
|
|
573
|
+
const result = await client.getFeeRateBps(tokenId);
|
|
574
|
+
out3(result);
|
|
575
|
+
});
|
|
576
|
+
clobCommand.command("neg-risk <token_id>").description("Check if token is neg-risk").action(async (tokenId) => {
|
|
577
|
+
const client = createReadonlyClobClient();
|
|
578
|
+
const result = await client.getNegRisk(tokenId);
|
|
579
|
+
out3(result);
|
|
580
|
+
});
|
|
581
|
+
clobCommand.command("price-history <token_id>").description("Get price history").requiredOption("--interval <interval>", "Time interval (e.g. max, 1d, 1w)").option("--fidelity <fidelity>", "Data fidelity", parseInt).action(async (tokenId, opts) => {
|
|
582
|
+
const client = createReadonlyClobClient();
|
|
583
|
+
const result = await client.getPricesHistory({
|
|
584
|
+
market: tokenId,
|
|
585
|
+
interval: opts.interval,
|
|
586
|
+
fidelity: opts.fidelity
|
|
587
|
+
});
|
|
588
|
+
out3(result);
|
|
589
|
+
});
|
|
590
|
+
clobCommand.command("time").description("Get server time").action(async () => {
|
|
591
|
+
const client = createReadonlyClobClient();
|
|
592
|
+
const result = await client.getServerTime();
|
|
593
|
+
out3(result);
|
|
594
|
+
});
|
|
595
|
+
clobCommand.command("geoblock").description("Check geo-blocking status").action(async () => {
|
|
596
|
+
const client = createReadonlyClobClient();
|
|
597
|
+
try {
|
|
598
|
+
const result = await client.getOk();
|
|
599
|
+
out3({ geoblocked: false, response: result });
|
|
600
|
+
} catch (e) {
|
|
601
|
+
out3({ geoblocked: true, error: e.message });
|
|
602
|
+
}
|
|
603
|
+
});
|
|
604
|
+
clobCommand.command("create-order").description("Place a limit order").requiredOption("--token <token_id>", "Token ID").requiredOption("--side <side>", "buy or sell").requiredOption("--price <price>", "Price", parseFloat).requiredOption("--size <size>", "Size", parseFloat).option("--order-type <type>", "Order type (GTC, GTD, FOK)", "GTC").option("--post-only", "Post-only order").option("--private-key <key>", "Private key").action(async (opts) => {
|
|
605
|
+
const client = await getAuthClient(opts);
|
|
606
|
+
const order = await client.createOrder({
|
|
607
|
+
tokenID: opts.token,
|
|
608
|
+
side: opts.side.toUpperCase(),
|
|
609
|
+
price: opts.price,
|
|
610
|
+
size: opts.size,
|
|
611
|
+
...opts.postOnly ? { postOnly: true } : {}
|
|
612
|
+
});
|
|
613
|
+
const result = await quietCall(() => client.postOrder(order, opts.orderType));
|
|
614
|
+
out3(result);
|
|
615
|
+
});
|
|
616
|
+
clobCommand.command("market-order").description("Place a market order").requiredOption("--token <token_id>", "Token ID").requiredOption("--side <side>", "buy or sell").requiredOption("--amount <amount>", "Amount", parseFloat).option("--order-type <type>", "Order type", "FOK").option("--private-key <key>", "Private key").action(async (opts) => {
|
|
617
|
+
const client = await getAuthClient(opts);
|
|
618
|
+
const order = await client.createMarketOrder({
|
|
619
|
+
tokenID: opts.token,
|
|
620
|
+
side: opts.side.toUpperCase(),
|
|
621
|
+
amount: opts.amount
|
|
622
|
+
});
|
|
623
|
+
const result = await quietCall(() => client.postOrder(order, opts.orderType));
|
|
624
|
+
out3(result);
|
|
625
|
+
});
|
|
626
|
+
clobCommand.command("post-orders").description("Batch post orders").requiredOption("--tokens <ids>", "Token IDs (comma-separated)").requiredOption("--side <side>", "buy or sell").requiredOption("--prices <prices>", "Prices (comma-separated)").requiredOption("--sizes <sizes>", "Sizes (comma-separated)").option("--order-type <type>", "Order type", "GTC").option("--private-key <key>", "Private key").action(async (opts) => {
|
|
627
|
+
const client = await getAuthClient(opts);
|
|
628
|
+
const tokens = opts.tokens.split(",");
|
|
629
|
+
const prices = opts.prices.split(",").map(Number);
|
|
630
|
+
const sizes = opts.sizes.split(",").map(Number);
|
|
631
|
+
const orders = await Promise.all(
|
|
632
|
+
tokens.map(
|
|
633
|
+
(token, i) => client.createOrder({
|
|
634
|
+
tokenID: token,
|
|
635
|
+
side: opts.side.toUpperCase(),
|
|
636
|
+
price: prices[i],
|
|
637
|
+
size: sizes[i]
|
|
638
|
+
})
|
|
639
|
+
)
|
|
640
|
+
);
|
|
641
|
+
const result = await client.postOrders(orders.map((order) => ({ order, orderType: opts.orderType })));
|
|
642
|
+
out3(result);
|
|
643
|
+
});
|
|
644
|
+
clobCommand.command("orders").description("List open orders").option("--market <condition_id>", "Filter by market").option("--asset <token_id>", "Filter by asset").option("--cursor <cursor>", "Pagination cursor").option("--private-key <key>", "Private key").action(async (opts) => {
|
|
645
|
+
const client = await getAuthClient(opts);
|
|
646
|
+
const params = {};
|
|
647
|
+
if (opts.market) params.market = opts.market;
|
|
648
|
+
if (opts.asset) params.asset_id = opts.asset;
|
|
649
|
+
if (opts.cursor) params.next_cursor = opts.cursor;
|
|
650
|
+
const result = await client.getOpenOrders(params);
|
|
651
|
+
out3(result);
|
|
652
|
+
});
|
|
653
|
+
clobCommand.command("order <order_id>").description("Get order details").option("--private-key <key>", "Private key").action(async (orderId, opts) => {
|
|
654
|
+
const client = await getAuthClient(opts);
|
|
655
|
+
const result = await client.getOrder(orderId);
|
|
656
|
+
out3(result);
|
|
657
|
+
});
|
|
658
|
+
clobCommand.command("cancel <order_id>").description("Cancel an order").option("--private-key <key>", "Private key").action(async (orderId, opts) => {
|
|
659
|
+
const client = await getAuthClient(opts);
|
|
660
|
+
const result = await client.cancelOrder({ orderID: orderId });
|
|
661
|
+
out3(result);
|
|
662
|
+
});
|
|
663
|
+
clobCommand.command("cancel-orders <ids>").description("Cancel multiple orders (comma-separated)").option("--private-key <key>", "Private key").action(async (ids, opts) => {
|
|
664
|
+
const client = await getAuthClient(opts);
|
|
665
|
+
const orderIds = ids.split(",");
|
|
666
|
+
const result = await client.cancelOrders(
|
|
667
|
+
orderIds.map((id) => ({ orderID: id }))
|
|
668
|
+
);
|
|
669
|
+
out3(result);
|
|
670
|
+
});
|
|
671
|
+
clobCommand.command("cancel-all").description("Cancel all open orders").option("--private-key <key>", "Private key").action(async (opts) => {
|
|
672
|
+
const client = await getAuthClient(opts);
|
|
673
|
+
const result = await client.cancelAll();
|
|
674
|
+
out3(result);
|
|
675
|
+
});
|
|
676
|
+
clobCommand.command("cancel-market").description("Cancel orders by market").option("--market <condition_id>", "Market condition ID").option("--asset <token_id>", "Asset token ID").option("--private-key <key>", "Private key").action(async (opts) => {
|
|
677
|
+
const client = await getAuthClient(opts);
|
|
678
|
+
const params = {};
|
|
679
|
+
if (opts.market) params.market = opts.market;
|
|
680
|
+
if (opts.asset) params.asset_id = opts.asset;
|
|
681
|
+
const result = await client.cancelMarketOrders(params);
|
|
682
|
+
out3(result);
|
|
683
|
+
});
|
|
684
|
+
clobCommand.command("trades").description("Trade history").option("--market <condition_id>", "Filter by market").option("--asset <token_id>", "Filter by asset").option("--cursor <cursor>", "Pagination cursor").option("--private-key <key>", "Private key").action(async (opts) => {
|
|
685
|
+
const client = await getAuthClient(opts);
|
|
686
|
+
const params = {};
|
|
687
|
+
if (opts.market) params.market = opts.market;
|
|
688
|
+
if (opts.asset) params.asset_id = opts.asset;
|
|
689
|
+
if (opts.cursor) params.next_cursor = opts.cursor;
|
|
690
|
+
const result = await client.getTrades(params);
|
|
691
|
+
out3(result);
|
|
692
|
+
});
|
|
693
|
+
clobCommand.command("balance").description("Check balance").requiredOption("--asset-type <type>", "Asset type (collateral or conditional)").option("--token <token_id>", "Token ID (for conditional)").option("--private-key <key>", "Private key").action(async (opts) => {
|
|
694
|
+
const client = await getAuthClient(opts);
|
|
695
|
+
const params = { asset_type: opts.assetType };
|
|
696
|
+
if (opts.token) params.token_id = opts.token;
|
|
697
|
+
const result = await client.getBalanceAllowance(params);
|
|
698
|
+
out3(result);
|
|
699
|
+
});
|
|
700
|
+
clobCommand.command("update-balance").description("Refresh balance").requiredOption("--asset-type <type>", "Asset type (collateral or conditional)").option("--token <token_id>", "Token ID (for conditional)").option("--private-key <key>", "Private key").action(async (opts) => {
|
|
701
|
+
const client = await getAuthClient(opts);
|
|
702
|
+
const params = { asset_type: opts.assetType };
|
|
703
|
+
if (opts.token) params.token_id = opts.token;
|
|
704
|
+
const result = await client.updateBalanceAllowance(params);
|
|
705
|
+
out3(result);
|
|
706
|
+
});
|
|
707
|
+
clobCommand.command("notifications").description("Get notifications").option("--private-key <key>", "Private key").action(async (opts) => {
|
|
708
|
+
const client = await getAuthClient(opts);
|
|
709
|
+
const result = await client.getNotifications();
|
|
710
|
+
out3(result);
|
|
711
|
+
});
|
|
712
|
+
clobCommand.command("delete-notifications <ids>").description("Delete notifications (comma-separated)").option("--private-key <key>", "Private key").action(async (ids, opts) => {
|
|
713
|
+
const client = await getAuthClient(opts);
|
|
714
|
+
const result = await client.dropNotifications({ ids: ids.split(",") });
|
|
715
|
+
out3(result);
|
|
716
|
+
});
|
|
717
|
+
clobCommand.command("rewards").description("Daily rewards").requiredOption("--date <date>", "Date (YYYY-MM-DD)").option("--cursor <cursor>", "Pagination cursor").option("--private-key <key>", "Private key").action(async (opts) => {
|
|
718
|
+
const client = await getAuthClient(opts);
|
|
719
|
+
const result = await client.getEarningsForUserForDay(opts.date);
|
|
720
|
+
out3(result);
|
|
721
|
+
});
|
|
722
|
+
clobCommand.command("earnings").description("Daily earnings").requiredOption("--date <date>", "Date (YYYY-MM-DD)").option("--private-key <key>", "Private key").action(async (opts) => {
|
|
723
|
+
const client = await getAuthClient(opts);
|
|
724
|
+
const result = await client.getTotalEarningsForUserForDay(opts.date);
|
|
725
|
+
out3(result);
|
|
726
|
+
});
|
|
727
|
+
clobCommand.command("earnings-markets").description("Earnings by market").requiredOption("--date <date>", "Date (YYYY-MM-DD)").option("--cursor <cursor>", "Pagination cursor").option("--private-key <key>", "Private key").action(async (opts) => {
|
|
728
|
+
const client = await getAuthClient(opts);
|
|
729
|
+
const result = await client.getUserEarningsAndMarketsConfig(opts.date);
|
|
730
|
+
out3(result);
|
|
731
|
+
});
|
|
732
|
+
clobCommand.command("reward-percentages").description("Reward percentages").option("--private-key <key>", "Private key").action(async (opts) => {
|
|
733
|
+
const client = await getAuthClient(opts);
|
|
734
|
+
const result = await client.getRewardPercentages();
|
|
735
|
+
out3(result);
|
|
736
|
+
});
|
|
737
|
+
clobCommand.command("current-rewards").description("Current rewards").option("--cursor <cursor>", "Pagination cursor").option("--private-key <key>", "Private key").action(async (opts) => {
|
|
738
|
+
const client = await getAuthClient(opts);
|
|
739
|
+
const params = {};
|
|
740
|
+
if (opts.cursor) params.next_cursor = opts.cursor;
|
|
741
|
+
const result = await client.getCurrentRewards();
|
|
742
|
+
out3(result);
|
|
743
|
+
});
|
|
744
|
+
clobCommand.command("market-reward <condition_id>").description("Market rewards").option("--cursor <cursor>", "Pagination cursor").option("--private-key <key>", "Private key").action(async (conditionId, opts) => {
|
|
745
|
+
const client = await getAuthClient(opts);
|
|
746
|
+
const params = { conditionId };
|
|
747
|
+
if (opts.cursor) params.next_cursor = opts.cursor;
|
|
748
|
+
const result = await client.getRawRewardsForMarket(params);
|
|
749
|
+
out3(result);
|
|
750
|
+
});
|
|
751
|
+
clobCommand.command("order-scoring <order_id>").description("Order scoring").option("--private-key <key>", "Private key").action(async (orderId, opts) => {
|
|
752
|
+
const client = await getAuthClient(opts);
|
|
753
|
+
const result = await client.isOrderScoring({ order_id: orderId });
|
|
754
|
+
out3(result);
|
|
755
|
+
});
|
|
756
|
+
clobCommand.command("orders-scoring <ids>").description("Batch order scoring (comma-separated)").option("--private-key <key>", "Private key").action(async (ids, opts) => {
|
|
757
|
+
const client = await getAuthClient(opts);
|
|
758
|
+
const result = await client.areOrdersScoring({
|
|
759
|
+
orderIds: ids.split(",")
|
|
760
|
+
});
|
|
761
|
+
out3(result);
|
|
762
|
+
});
|
|
763
|
+
clobCommand.command("api-keys").description("List API keys").option("--private-key <key>", "Private key").action(async (opts) => {
|
|
764
|
+
const client = await getAuthClient(opts);
|
|
765
|
+
const result = await client.getApiKeys();
|
|
766
|
+
out3(result);
|
|
767
|
+
});
|
|
768
|
+
clobCommand.command("create-api-key").description("Create API key").option("--private-key <key>", "Private key").action(async (opts) => {
|
|
769
|
+
const client = await getAuthClient(opts);
|
|
770
|
+
const result = await client.createApiKey();
|
|
771
|
+
out3(result);
|
|
772
|
+
});
|
|
773
|
+
clobCommand.command("delete-api-key").description("Delete API key").option("--private-key <key>", "Private key").action(async (opts) => {
|
|
774
|
+
const client = await getAuthClient(opts);
|
|
775
|
+
const result = await client.deleteApiKey();
|
|
776
|
+
out3(result);
|
|
777
|
+
});
|
|
778
|
+
clobCommand.command("account-status").description("Account status").option("--private-key <key>", "Private key").action(async (opts) => {
|
|
779
|
+
const client = await getAuthClient(opts);
|
|
780
|
+
const [apiKeys, balance] = await Promise.all([
|
|
781
|
+
client.getApiKeys().catch(() => null),
|
|
782
|
+
client.getBalanceAllowance({ asset_type: AssetType.COLLATERAL }).catch(() => null)
|
|
783
|
+
]);
|
|
784
|
+
out3({ apiKeys, balance });
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
// src/commands/wallet.ts
|
|
788
|
+
import { Command as Command10 } from "commander";
|
|
789
|
+
import { createPublicClient, http as http2, formatUnits, parseUnits, encodeFunctionData as encodeFunctionData2, isAddress } from "viem";
|
|
790
|
+
import { polygon as polygon2 } from "viem/chains";
|
|
791
|
+
import { privateKeyToAccount as privateKeyToAccount5 } from "viem/accounts";
|
|
792
|
+
|
|
793
|
+
// src/lib/contracts.ts
|
|
794
|
+
var USDCE_ADDRESS = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174";
|
|
795
|
+
var CTF_ADDRESS = "0x4D97DCd97eC945f40cF65F87097ACe5EA0476045";
|
|
796
|
+
var CTF_EXCHANGE = "0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E";
|
|
797
|
+
var NEG_RISK_CTF_EXCHANGE = "0xC5d563A36AE78145C45a50134d48A1215220f80a";
|
|
798
|
+
var NEG_RISK_ADAPTER = "0xd91E80cF2E7be2e162c6513ceD06f1dD0dA35296";
|
|
799
|
+
var ERC20_ABI = [
|
|
800
|
+
{
|
|
801
|
+
name: "approve",
|
|
802
|
+
type: "function",
|
|
803
|
+
stateMutability: "nonpayable",
|
|
804
|
+
inputs: [
|
|
805
|
+
{ name: "spender", type: "address" },
|
|
806
|
+
{ name: "amount", type: "uint256" }
|
|
807
|
+
],
|
|
808
|
+
outputs: [{ type: "bool" }]
|
|
809
|
+
},
|
|
810
|
+
{
|
|
811
|
+
name: "allowance",
|
|
812
|
+
type: "function",
|
|
813
|
+
stateMutability: "view",
|
|
814
|
+
inputs: [
|
|
815
|
+
{ name: "owner", type: "address" },
|
|
816
|
+
{ name: "spender", type: "address" }
|
|
817
|
+
],
|
|
818
|
+
outputs: [{ type: "uint256" }]
|
|
819
|
+
},
|
|
820
|
+
{
|
|
821
|
+
name: "balanceOf",
|
|
822
|
+
type: "function",
|
|
823
|
+
stateMutability: "view",
|
|
824
|
+
inputs: [{ name: "account", type: "address" }],
|
|
825
|
+
outputs: [{ type: "uint256" }]
|
|
826
|
+
},
|
|
827
|
+
{
|
|
828
|
+
name: "transfer",
|
|
829
|
+
type: "function",
|
|
830
|
+
stateMutability: "nonpayable",
|
|
831
|
+
inputs: [
|
|
832
|
+
{ name: "to", type: "address" },
|
|
833
|
+
{ name: "amount", type: "uint256" }
|
|
834
|
+
],
|
|
835
|
+
outputs: [{ type: "bool" }]
|
|
836
|
+
}
|
|
837
|
+
];
|
|
838
|
+
var ERC1155_ABI = [
|
|
839
|
+
{
|
|
840
|
+
name: "setApprovalForAll",
|
|
841
|
+
type: "function",
|
|
842
|
+
stateMutability: "nonpayable",
|
|
843
|
+
inputs: [
|
|
844
|
+
{ name: "operator", type: "address" },
|
|
845
|
+
{ name: "approved", type: "bool" }
|
|
846
|
+
],
|
|
847
|
+
outputs: []
|
|
848
|
+
},
|
|
849
|
+
{
|
|
850
|
+
name: "isApprovedForAll",
|
|
851
|
+
type: "function",
|
|
852
|
+
stateMutability: "view",
|
|
853
|
+
inputs: [
|
|
854
|
+
{ name: "account", type: "address" },
|
|
855
|
+
{ name: "operator", type: "address" }
|
|
856
|
+
],
|
|
857
|
+
outputs: [{ type: "bool" }]
|
|
858
|
+
},
|
|
859
|
+
{
|
|
860
|
+
name: "balanceOf",
|
|
861
|
+
type: "function",
|
|
862
|
+
stateMutability: "view",
|
|
863
|
+
inputs: [
|
|
864
|
+
{ name: "account", type: "address" },
|
|
865
|
+
{ name: "id", type: "uint256" }
|
|
866
|
+
],
|
|
867
|
+
outputs: [{ type: "uint256" }]
|
|
868
|
+
}
|
|
869
|
+
];
|
|
870
|
+
var CTF_ABI = [
|
|
871
|
+
{
|
|
872
|
+
name: "splitPosition",
|
|
873
|
+
type: "function",
|
|
874
|
+
stateMutability: "nonpayable",
|
|
875
|
+
inputs: [
|
|
876
|
+
{ name: "collateralToken", type: "address" },
|
|
877
|
+
{ name: "parentCollectionId", type: "bytes32" },
|
|
878
|
+
{ name: "conditionId", type: "bytes32" },
|
|
879
|
+
{ name: "partition", type: "uint256[]" },
|
|
880
|
+
{ name: "amount", type: "uint256" }
|
|
881
|
+
],
|
|
882
|
+
outputs: []
|
|
883
|
+
},
|
|
884
|
+
{
|
|
885
|
+
name: "mergePositions",
|
|
886
|
+
type: "function",
|
|
887
|
+
stateMutability: "nonpayable",
|
|
888
|
+
inputs: [
|
|
889
|
+
{ name: "collateralToken", type: "address" },
|
|
890
|
+
{ name: "parentCollectionId", type: "bytes32" },
|
|
891
|
+
{ name: "conditionId", type: "bytes32" },
|
|
892
|
+
{ name: "partition", type: "uint256[]" },
|
|
893
|
+
{ name: "amount", type: "uint256" }
|
|
894
|
+
],
|
|
895
|
+
outputs: []
|
|
896
|
+
},
|
|
897
|
+
{
|
|
898
|
+
name: "redeemPositions",
|
|
899
|
+
type: "function",
|
|
900
|
+
stateMutability: "nonpayable",
|
|
901
|
+
inputs: [
|
|
902
|
+
{ name: "collateralToken", type: "address" },
|
|
903
|
+
{ name: "parentCollectionId", type: "bytes32" },
|
|
904
|
+
{ name: "conditionId", type: "bytes32" },
|
|
905
|
+
{ name: "indexSets", type: "uint256[]" }
|
|
906
|
+
],
|
|
907
|
+
outputs: []
|
|
908
|
+
},
|
|
909
|
+
{
|
|
910
|
+
name: "getConditionId",
|
|
911
|
+
type: "function",
|
|
912
|
+
stateMutability: "pure",
|
|
913
|
+
inputs: [
|
|
914
|
+
{ name: "oracle", type: "address" },
|
|
915
|
+
{ name: "questionId", type: "bytes32" },
|
|
916
|
+
{ name: "outcomeSlotCount", type: "uint256" }
|
|
917
|
+
],
|
|
918
|
+
outputs: [{ type: "bytes32" }]
|
|
919
|
+
},
|
|
920
|
+
{
|
|
921
|
+
name: "getCollectionId",
|
|
922
|
+
type: "function",
|
|
923
|
+
stateMutability: "view",
|
|
924
|
+
inputs: [
|
|
925
|
+
{ name: "parentCollectionId", type: "bytes32" },
|
|
926
|
+
{ name: "conditionId", type: "bytes32" },
|
|
927
|
+
{ name: "indexSet", type: "uint256" }
|
|
928
|
+
],
|
|
929
|
+
outputs: [{ type: "bytes32" }]
|
|
930
|
+
},
|
|
931
|
+
{
|
|
932
|
+
name: "getPositionId",
|
|
933
|
+
type: "function",
|
|
934
|
+
stateMutability: "pure",
|
|
935
|
+
inputs: [
|
|
936
|
+
{ name: "collateralToken", type: "address" },
|
|
937
|
+
{ name: "collectionId", type: "bytes32" }
|
|
938
|
+
],
|
|
939
|
+
outputs: [{ type: "uint256" }]
|
|
940
|
+
}
|
|
941
|
+
];
|
|
942
|
+
var NEG_RISK_ADAPTER_ABI = [
|
|
943
|
+
{
|
|
944
|
+
name: "redeemPositions",
|
|
945
|
+
type: "function",
|
|
946
|
+
stateMutability: "nonpayable",
|
|
947
|
+
inputs: [
|
|
948
|
+
{ name: "conditionId", type: "bytes32" },
|
|
949
|
+
{ name: "amounts", type: "uint256[]" }
|
|
950
|
+
],
|
|
951
|
+
outputs: []
|
|
952
|
+
}
|
|
953
|
+
];
|
|
954
|
+
|
|
955
|
+
// src/lib/relayer.ts
|
|
956
|
+
import {
|
|
957
|
+
encodeFunctionData,
|
|
958
|
+
encodePacked,
|
|
959
|
+
hashTypedData,
|
|
960
|
+
hexToBytes,
|
|
961
|
+
hexToBigInt,
|
|
962
|
+
concat as concat2,
|
|
963
|
+
zeroAddress
|
|
964
|
+
} from "viem";
|
|
965
|
+
import { privateKeyToAccount as privateKeyToAccount4 } from "viem/accounts";
|
|
966
|
+
var MULTISEND_ADDRESS = "0xA238CBeb142c10Ef7Ad8442C6D1f9E89e07e7761";
|
|
967
|
+
var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
968
|
+
var POLYMARKET_SAFE_FACTORY2 = "0xaacFeEa03eb1561C4e67d661e40682Bd20E3541b";
|
|
969
|
+
var SAFE_FACTORY_NAME = "Polymarket Contract Proxy Factory";
|
|
970
|
+
var ZERO_BYTES32 = "0x0000000000000000000000000000000000000000000000000000000000000000";
|
|
971
|
+
var SAFE_TX_TYPES = {
|
|
972
|
+
SafeTx: [
|
|
973
|
+
{ name: "to", type: "address" },
|
|
974
|
+
{ name: "value", type: "uint256" },
|
|
975
|
+
{ name: "data", type: "bytes" },
|
|
976
|
+
{ name: "operation", type: "uint8" },
|
|
977
|
+
{ name: "safeTxGas", type: "uint256" },
|
|
978
|
+
{ name: "baseGas", type: "uint256" },
|
|
979
|
+
{ name: "gasPrice", type: "uint256" },
|
|
980
|
+
{ name: "gasToken", type: "address" },
|
|
981
|
+
{ name: "refundReceiver", type: "address" },
|
|
982
|
+
{ name: "nonce", type: "uint256" }
|
|
983
|
+
]
|
|
984
|
+
};
|
|
985
|
+
function resolveBuilderCreds() {
|
|
986
|
+
const key = process.env.POLYMARKET_BUILDER_API_KEY;
|
|
987
|
+
const secret = process.env.POLYMARKET_BUILDER_SECRET;
|
|
988
|
+
const passphrase = process.env.POLYMARKET_BUILDER_PASSPHRASE;
|
|
989
|
+
if (key && secret && passphrase) {
|
|
990
|
+
return { key, secret, passphrase };
|
|
991
|
+
}
|
|
992
|
+
return null;
|
|
993
|
+
}
|
|
994
|
+
async function buildHmacSignature(secret, timestamp, method, requestPath, body) {
|
|
995
|
+
const secretBytes = Uint8Array.from(atob(secret.replace(/-/g, "+").replace(/_/g, "/")), (c) => c.charCodeAt(0));
|
|
996
|
+
let message = `${timestamp}${method}${requestPath}`;
|
|
997
|
+
if (body) {
|
|
998
|
+
message += body.replace(/'/g, '"');
|
|
999
|
+
}
|
|
1000
|
+
const key = await crypto.subtle.importKey(
|
|
1001
|
+
"raw",
|
|
1002
|
+
secretBytes,
|
|
1003
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
1004
|
+
false,
|
|
1005
|
+
["sign"]
|
|
1006
|
+
);
|
|
1007
|
+
const sig = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(message));
|
|
1008
|
+
return btoa(String.fromCharCode(...new Uint8Array(sig))).replace(/\+/g, "-").replace(/\//g, "_");
|
|
1009
|
+
}
|
|
1010
|
+
function generateBuilderHeaders(creds, method, path, body) {
|
|
1011
|
+
const timestamp = String(Math.floor(Date.now() / 1e3));
|
|
1012
|
+
return buildHmacSignature(creds.secret, timestamp, method, path, body).then((sig) => ({
|
|
1013
|
+
"Content-Type": "application/json",
|
|
1014
|
+
"POLY_BUILDER_API_KEY": creds.key,
|
|
1015
|
+
"POLY_BUILDER_PASSPHRASE": creds.passphrase,
|
|
1016
|
+
"POLY_BUILDER_SIGNATURE": sig,
|
|
1017
|
+
"POLY_BUILDER_TIMESTAMP": timestamp
|
|
1018
|
+
}));
|
|
1019
|
+
}
|
|
1020
|
+
var RelayerClient = class {
|
|
1021
|
+
constructor(baseUrl = RELAYER_URL) {
|
|
1022
|
+
this.baseUrl = baseUrl;
|
|
1023
|
+
this.creds = resolveBuilderCreds();
|
|
1024
|
+
if (!this.creds && baseUrl.includes("relayer-v2.polymarket.com")) {
|
|
1025
|
+
throw new Error(
|
|
1026
|
+
"Relayer requires authentication. Either:\n 1. Set POLYMARKET_BUILDER_API_KEY, POLYMARKET_BUILDER_SECRET, POLYMARKET_BUILDER_PASSPHRASE, or\n 2. Set POLYMARKET_RELAYER_URL to a proxy that injects builder credentials (e.g. https://proxy.polytown.app/relayer)"
|
|
1027
|
+
);
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
creds;
|
|
1031
|
+
async authedHeaders(method, path, body) {
|
|
1032
|
+
if (!this.creds) {
|
|
1033
|
+
return { "Content-Type": "application/json" };
|
|
1034
|
+
}
|
|
1035
|
+
return generateBuilderHeaders(this.creds, method, path, body);
|
|
1036
|
+
}
|
|
1037
|
+
async getNonce(safeAddress, type = "SAFE") {
|
|
1038
|
+
const path = `/nonce?address=${safeAddress}&type=${type}`;
|
|
1039
|
+
const headers = await this.authedHeaders("GET", "/nonce");
|
|
1040
|
+
const res = await fetch(`${this.baseUrl}${path}`, { headers });
|
|
1041
|
+
if (!res.ok) {
|
|
1042
|
+
const text = await res.text();
|
|
1043
|
+
throw new Error(`Relayer /nonce failed: ${res.status} ${res.statusText} \u2014 ${text}`);
|
|
1044
|
+
}
|
|
1045
|
+
const data = await res.json();
|
|
1046
|
+
return String(data.nonce ?? data);
|
|
1047
|
+
}
|
|
1048
|
+
async isDeployed(safeAddress) {
|
|
1049
|
+
const path = `/deployed?address=${safeAddress}`;
|
|
1050
|
+
const headers = await this.authedHeaders("GET", "/deployed");
|
|
1051
|
+
const res = await fetch(`${this.baseUrl}${path}`, { headers });
|
|
1052
|
+
if (!res.ok) {
|
|
1053
|
+
const text = await res.text();
|
|
1054
|
+
throw new Error(`Relayer /deployed failed: ${res.status} ${res.statusText} \u2014 ${text}`);
|
|
1055
|
+
}
|
|
1056
|
+
const data = await res.json();
|
|
1057
|
+
return !!data.deployed;
|
|
1058
|
+
}
|
|
1059
|
+
async submit(payload) {
|
|
1060
|
+
const body = JSON.stringify(payload);
|
|
1061
|
+
const headers = await this.authedHeaders("POST", "/submit", body);
|
|
1062
|
+
const res = await fetch(`${this.baseUrl}/submit`, {
|
|
1063
|
+
method: "POST",
|
|
1064
|
+
headers,
|
|
1065
|
+
body
|
|
1066
|
+
});
|
|
1067
|
+
if (!res.ok) {
|
|
1068
|
+
const text = await res.text();
|
|
1069
|
+
throw new Error(`Relayer /submit failed: ${res.status} ${res.statusText} \u2014 ${text}`);
|
|
1070
|
+
}
|
|
1071
|
+
return res.json();
|
|
1072
|
+
}
|
|
1073
|
+
async getTransaction(txId) {
|
|
1074
|
+
const res = await fetch(`${this.baseUrl}/transaction?id=${txId}`);
|
|
1075
|
+
if (!res.ok) throw new Error(`Relayer /transaction failed: ${res.status}`);
|
|
1076
|
+
return res.json();
|
|
1077
|
+
}
|
|
1078
|
+
};
|
|
1079
|
+
async function signSafeTransaction(privateKey, safeAddress, to, data, nonce, operation = 0) {
|
|
1080
|
+
const account = privateKeyToAccount4(privateKey);
|
|
1081
|
+
const structHash = hashTypedData({
|
|
1082
|
+
domain: {
|
|
1083
|
+
chainId: POLYGON_CHAIN_ID,
|
|
1084
|
+
verifyingContract: safeAddress
|
|
1085
|
+
},
|
|
1086
|
+
types: SAFE_TX_TYPES,
|
|
1087
|
+
primaryType: "SafeTx",
|
|
1088
|
+
message: {
|
|
1089
|
+
to,
|
|
1090
|
+
value: 0n,
|
|
1091
|
+
data,
|
|
1092
|
+
operation,
|
|
1093
|
+
safeTxGas: 0n,
|
|
1094
|
+
baseGas: 0n,
|
|
1095
|
+
gasPrice: 0n,
|
|
1096
|
+
gasToken: ZERO_ADDRESS,
|
|
1097
|
+
refundReceiver: ZERO_ADDRESS,
|
|
1098
|
+
nonce: BigInt(nonce)
|
|
1099
|
+
}
|
|
1100
|
+
});
|
|
1101
|
+
const signature = await account.signMessage({
|
|
1102
|
+
message: { raw: hexToBytes(structHash) }
|
|
1103
|
+
});
|
|
1104
|
+
return splitAndPackSig(signature);
|
|
1105
|
+
}
|
|
1106
|
+
function splitAndPackSig(sig) {
|
|
1107
|
+
let sigV = parseInt(sig.slice(-2), 16);
|
|
1108
|
+
switch (sigV) {
|
|
1109
|
+
case 0:
|
|
1110
|
+
case 1:
|
|
1111
|
+
sigV += 31;
|
|
1112
|
+
break;
|
|
1113
|
+
case 27:
|
|
1114
|
+
case 28:
|
|
1115
|
+
sigV += 4;
|
|
1116
|
+
break;
|
|
1117
|
+
default:
|
|
1118
|
+
throw new Error("Invalid signature v value");
|
|
1119
|
+
}
|
|
1120
|
+
const adjusted = sig.slice(0, -2) + sigV.toString(16);
|
|
1121
|
+
const r = hexToBigInt("0x" + adjusted.slice(2, 66));
|
|
1122
|
+
const s = hexToBigInt("0x" + adjusted.slice(66, 130));
|
|
1123
|
+
const v = parseInt(adjusted.slice(130, 132), 16);
|
|
1124
|
+
return encodePacked(["uint256", "uint256", "uint8"], [r, s, v]);
|
|
1125
|
+
}
|
|
1126
|
+
function encodeMultiSend(txns) {
|
|
1127
|
+
let packed = "0x";
|
|
1128
|
+
for (const txn of txns) {
|
|
1129
|
+
const dataBytes = hexToBytes(txn.data);
|
|
1130
|
+
const encoded = encodePacked(
|
|
1131
|
+
["uint8", "address", "uint256", "uint256", "bytes"],
|
|
1132
|
+
[txn.operation ?? 0, txn.to, txn.value ?? 0n, BigInt(dataBytes.length), txn.data]
|
|
1133
|
+
);
|
|
1134
|
+
packed = concat2([packed, encoded]);
|
|
1135
|
+
}
|
|
1136
|
+
return encodeFunctionData({
|
|
1137
|
+
abi: [
|
|
1138
|
+
{
|
|
1139
|
+
name: "multiSend",
|
|
1140
|
+
type: "function",
|
|
1141
|
+
inputs: [{ name: "transactions", type: "bytes" }],
|
|
1142
|
+
outputs: []
|
|
1143
|
+
}
|
|
1144
|
+
],
|
|
1145
|
+
functionName: "multiSend",
|
|
1146
|
+
args: [packed]
|
|
1147
|
+
});
|
|
1148
|
+
}
|
|
1149
|
+
async function relayTransaction(privateKey, to, data, metadata, operation = 0) {
|
|
1150
|
+
const account = privateKeyToAccount4(privateKey);
|
|
1151
|
+
const safeAddress = deriveGnosisSafeWallet(account.address);
|
|
1152
|
+
const relayer = new RelayerClient();
|
|
1153
|
+
const noncePayload = await relayer.getNonce(account.address);
|
|
1154
|
+
const signature = await signSafeTransaction(
|
|
1155
|
+
privateKey,
|
|
1156
|
+
safeAddress,
|
|
1157
|
+
to,
|
|
1158
|
+
data,
|
|
1159
|
+
noncePayload,
|
|
1160
|
+
operation
|
|
1161
|
+
);
|
|
1162
|
+
const payload = {
|
|
1163
|
+
type: "SAFE",
|
|
1164
|
+
from: account.address,
|
|
1165
|
+
to,
|
|
1166
|
+
proxyWallet: safeAddress,
|
|
1167
|
+
data,
|
|
1168
|
+
nonce: noncePayload,
|
|
1169
|
+
signature,
|
|
1170
|
+
signatureParams: {
|
|
1171
|
+
gasPrice: "0",
|
|
1172
|
+
operation: String(operation),
|
|
1173
|
+
safeTxnGas: "0",
|
|
1174
|
+
baseGas: "0",
|
|
1175
|
+
gasToken: ZERO_ADDRESS,
|
|
1176
|
+
refundReceiver: ZERO_ADDRESS
|
|
1177
|
+
},
|
|
1178
|
+
metadata
|
|
1179
|
+
};
|
|
1180
|
+
return relayer.submit(payload);
|
|
1181
|
+
}
|
|
1182
|
+
async function relayMultiSend(privateKey, txns, metadata) {
|
|
1183
|
+
const multiSendData = encodeMultiSend(txns);
|
|
1184
|
+
return relayTransaction(
|
|
1185
|
+
privateKey,
|
|
1186
|
+
MULTISEND_ADDRESS,
|
|
1187
|
+
multiSendData,
|
|
1188
|
+
metadata,
|
|
1189
|
+
1
|
|
1190
|
+
// DelegateCall for MultiSend
|
|
1191
|
+
);
|
|
1192
|
+
}
|
|
1193
|
+
function buildRedeemCalldata(conditionId, indexSets = [1n, 2n], collateral = USDCE_ADDRESS, parentCollectionId = ZERO_BYTES32) {
|
|
1194
|
+
const data = encodeFunctionData({
|
|
1195
|
+
abi: CTF_ABI,
|
|
1196
|
+
functionName: "redeemPositions",
|
|
1197
|
+
args: [collateral, parentCollectionId, conditionId, indexSets]
|
|
1198
|
+
});
|
|
1199
|
+
return { to: CTF_ADDRESS, data };
|
|
1200
|
+
}
|
|
1201
|
+
function buildNegRiskRedeemCalldata(conditionId, amounts) {
|
|
1202
|
+
const data = encodeFunctionData({
|
|
1203
|
+
abi: NEG_RISK_ADAPTER_ABI,
|
|
1204
|
+
functionName: "redeemPositions",
|
|
1205
|
+
args: [conditionId, amounts]
|
|
1206
|
+
});
|
|
1207
|
+
return { to: NEG_RISK_ADAPTER, data };
|
|
1208
|
+
}
|
|
1209
|
+
function buildSplitCalldata(conditionId, amount, partition = [1n, 2n], collateral = USDCE_ADDRESS, parentCollectionId = ZERO_BYTES32) {
|
|
1210
|
+
const data = encodeFunctionData({
|
|
1211
|
+
abi: CTF_ABI,
|
|
1212
|
+
functionName: "splitPosition",
|
|
1213
|
+
args: [collateral, parentCollectionId, conditionId, partition, amount]
|
|
1214
|
+
});
|
|
1215
|
+
return { to: CTF_ADDRESS, data };
|
|
1216
|
+
}
|
|
1217
|
+
function buildMergeCalldata(conditionId, amount, partition = [1n, 2n], collateral = USDCE_ADDRESS, parentCollectionId = ZERO_BYTES32) {
|
|
1218
|
+
const data = encodeFunctionData({
|
|
1219
|
+
abi: CTF_ABI,
|
|
1220
|
+
functionName: "mergePositions",
|
|
1221
|
+
args: [collateral, parentCollectionId, conditionId, partition, amount]
|
|
1222
|
+
});
|
|
1223
|
+
return { to: CTF_ADDRESS, data };
|
|
1224
|
+
}
|
|
1225
|
+
async function deploySafe(privateKey) {
|
|
1226
|
+
const account = privateKeyToAccount4(privateKey);
|
|
1227
|
+
const safeAddress = deriveGnosisSafeWallet(account.address);
|
|
1228
|
+
const relayer = new RelayerClient();
|
|
1229
|
+
const deployed = await relayer.isDeployed(safeAddress);
|
|
1230
|
+
if (deployed) {
|
|
1231
|
+
return { alreadyDeployed: true, safeAddress };
|
|
1232
|
+
}
|
|
1233
|
+
const signature = await account.signTypedData({
|
|
1234
|
+
domain: {
|
|
1235
|
+
name: SAFE_FACTORY_NAME,
|
|
1236
|
+
chainId: BigInt(POLYGON_CHAIN_ID),
|
|
1237
|
+
verifyingContract: POLYMARKET_SAFE_FACTORY2
|
|
1238
|
+
},
|
|
1239
|
+
types: {
|
|
1240
|
+
CreateProxy: [
|
|
1241
|
+
{ name: "paymentToken", type: "address" },
|
|
1242
|
+
{ name: "payment", type: "uint256" },
|
|
1243
|
+
{ name: "paymentReceiver", type: "address" }
|
|
1244
|
+
]
|
|
1245
|
+
},
|
|
1246
|
+
primaryType: "CreateProxy",
|
|
1247
|
+
message: {
|
|
1248
|
+
paymentToken: zeroAddress,
|
|
1249
|
+
payment: 0n,
|
|
1250
|
+
paymentReceiver: zeroAddress
|
|
1251
|
+
}
|
|
1252
|
+
});
|
|
1253
|
+
const payload = {
|
|
1254
|
+
from: account.address,
|
|
1255
|
+
to: POLYMARKET_SAFE_FACTORY2,
|
|
1256
|
+
proxyWallet: safeAddress,
|
|
1257
|
+
data: "0x",
|
|
1258
|
+
signature,
|
|
1259
|
+
signatureParams: {
|
|
1260
|
+
paymentToken: zeroAddress,
|
|
1261
|
+
payment: "0",
|
|
1262
|
+
paymentReceiver: zeroAddress
|
|
1263
|
+
},
|
|
1264
|
+
type: "SAFE-CREATE"
|
|
1265
|
+
};
|
|
1266
|
+
const body = JSON.stringify(payload);
|
|
1267
|
+
const headers = await relayer["authedHeaders"]("POST", "/submit", body);
|
|
1268
|
+
const res = await fetch(`${relayer["baseUrl"]}/submit`, {
|
|
1269
|
+
method: "POST",
|
|
1270
|
+
headers,
|
|
1271
|
+
body
|
|
1272
|
+
});
|
|
1273
|
+
if (!res.ok) {
|
|
1274
|
+
const text = await res.text();
|
|
1275
|
+
throw new Error(`Relayer deploy failed: ${res.status} ${res.statusText} \u2014 ${text}`);
|
|
1276
|
+
}
|
|
1277
|
+
return res.json();
|
|
1278
|
+
}
|
|
1279
|
+
function buildApproveCalldata() {
|
|
1280
|
+
const maxUint256 = 2n ** 256n - 1n;
|
|
1281
|
+
return [
|
|
1282
|
+
{
|
|
1283
|
+
to: USDCE_ADDRESS,
|
|
1284
|
+
data: encodeFunctionData({
|
|
1285
|
+
abi: ERC20_ABI,
|
|
1286
|
+
functionName: "approve",
|
|
1287
|
+
args: [CTF_EXCHANGE, maxUint256]
|
|
1288
|
+
})
|
|
1289
|
+
},
|
|
1290
|
+
{
|
|
1291
|
+
to: USDCE_ADDRESS,
|
|
1292
|
+
data: encodeFunctionData({
|
|
1293
|
+
abi: ERC20_ABI,
|
|
1294
|
+
functionName: "approve",
|
|
1295
|
+
args: [NEG_RISK_CTF_EXCHANGE, maxUint256]
|
|
1296
|
+
})
|
|
1297
|
+
},
|
|
1298
|
+
{
|
|
1299
|
+
to: CTF_ADDRESS,
|
|
1300
|
+
data: encodeFunctionData({
|
|
1301
|
+
abi: ERC1155_ABI,
|
|
1302
|
+
functionName: "setApprovalForAll",
|
|
1303
|
+
args: [CTF_EXCHANGE, true]
|
|
1304
|
+
})
|
|
1305
|
+
},
|
|
1306
|
+
{
|
|
1307
|
+
to: CTF_ADDRESS,
|
|
1308
|
+
data: encodeFunctionData({
|
|
1309
|
+
abi: ERC1155_ABI,
|
|
1310
|
+
functionName: "setApprovalForAll",
|
|
1311
|
+
args: [NEG_RISK_CTF_EXCHANGE, true]
|
|
1312
|
+
})
|
|
1313
|
+
}
|
|
1314
|
+
];
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
// src/commands/wallet.ts
|
|
1318
|
+
var out4 = (data) => console.log(JSON.stringify(data, null, 2));
|
|
1319
|
+
function resolveAddress(opts) {
|
|
1320
|
+
const key = resolvePrivateKey(opts.privateKey);
|
|
1321
|
+
const account = privateKeyToAccount5(key);
|
|
1322
|
+
return {
|
|
1323
|
+
key,
|
|
1324
|
+
eoa: account.address,
|
|
1325
|
+
address: deriveGnosisSafeWallet(account.address)
|
|
1326
|
+
};
|
|
1327
|
+
}
|
|
1328
|
+
var walletCommand = new Command10("wallet").description(
|
|
1329
|
+
"Wallet management"
|
|
1330
|
+
);
|
|
1331
|
+
walletCommand.command("create").description("Create a new random wallet").action(async () => {
|
|
1332
|
+
const wallet = createRandomWallet();
|
|
1333
|
+
const address = deriveGnosisSafeWallet(wallet.address);
|
|
1334
|
+
out4({
|
|
1335
|
+
privateKey: wallet.privateKey,
|
|
1336
|
+
eoa: wallet.address,
|
|
1337
|
+
address
|
|
1338
|
+
});
|
|
1339
|
+
});
|
|
1340
|
+
walletCommand.command("import <key>").description("Import wallet from private key").action(async (key) => {
|
|
1341
|
+
const normalized = key.startsWith("0x") ? key : `0x${key}`;
|
|
1342
|
+
const account = privateKeyToAccount5(normalized);
|
|
1343
|
+
const address = deriveGnosisSafeWallet(account.address);
|
|
1344
|
+
out4({
|
|
1345
|
+
eoa: account.address,
|
|
1346
|
+
address
|
|
1347
|
+
});
|
|
1348
|
+
});
|
|
1349
|
+
walletCommand.command("address").description("Show active wallet address").option("--private-key <key>", "Private key").action(async (opts) => {
|
|
1350
|
+
const { address } = resolveAddress(opts);
|
|
1351
|
+
out4({ address });
|
|
1352
|
+
});
|
|
1353
|
+
walletCommand.command("show").description("Show wallet details").option("--private-key <key>", "Private key").action(async (opts) => {
|
|
1354
|
+
const { eoa, address } = resolveAddress(opts);
|
|
1355
|
+
out4({ eoa, address });
|
|
1356
|
+
});
|
|
1357
|
+
walletCommand.command("balance").description("Show wallet USDC.e balance").option("--private-key <key>", "Private key").action(async (opts) => {
|
|
1358
|
+
const { address } = resolveAddress(opts);
|
|
1359
|
+
const publicClient = createPublicClient({ chain: polygon2, transport: http2(RPC_URL) });
|
|
1360
|
+
const usdce = await publicClient.readContract({
|
|
1361
|
+
address: USDCE_ADDRESS,
|
|
1362
|
+
abi: ERC20_ABI,
|
|
1363
|
+
functionName: "balanceOf",
|
|
1364
|
+
args: [address]
|
|
1365
|
+
});
|
|
1366
|
+
out4({
|
|
1367
|
+
address,
|
|
1368
|
+
"usdc.e": formatUnits(usdce, 6)
|
|
1369
|
+
});
|
|
1370
|
+
});
|
|
1371
|
+
walletCommand.command("withdraw <amount> <to>").description("Withdraw USDC.e from Safe wallet via relayer (gasless)").option("--private-key <key>", "Private key").action(async (amount, to, opts) => {
|
|
1372
|
+
if (!isAddress(to)) throw new Error(`Invalid address: ${to}`);
|
|
1373
|
+
const key = resolvePrivateKey(opts.privateKey);
|
|
1374
|
+
const amountInWei = parseUnits(amount, 6);
|
|
1375
|
+
const data = encodeFunctionData2({
|
|
1376
|
+
abi: ERC20_ABI,
|
|
1377
|
+
functionName: "transfer",
|
|
1378
|
+
args: [to, amountInWei]
|
|
1379
|
+
});
|
|
1380
|
+
const result = await relayTransaction(
|
|
1381
|
+
key,
|
|
1382
|
+
USDCE_ADDRESS,
|
|
1383
|
+
data,
|
|
1384
|
+
`Withdraw ${amount} USDC.e to ${to}`
|
|
1385
|
+
);
|
|
1386
|
+
out4(result);
|
|
1387
|
+
});
|
|
1388
|
+
walletCommand.command("reset").description("Reset wallet (clear env reminder)").action(async () => {
|
|
1389
|
+
out4({
|
|
1390
|
+
message: "To reset, remove POLYMARKET_PRIVATE_KEY from ~/.polytown/.env"
|
|
1391
|
+
});
|
|
1392
|
+
});
|
|
1393
|
+
|
|
1394
|
+
// src/commands/data.ts
|
|
1395
|
+
import { Command as Command11 } from "commander";
|
|
1396
|
+
import { privateKeyToAccount as privateKeyToAccount6 } from "viem/accounts";
|
|
1397
|
+
|
|
1398
|
+
// src/lib/data.ts
|
|
1399
|
+
function buildUrl2(base, path, params) {
|
|
1400
|
+
const url = new URL(path, base);
|
|
1401
|
+
if (params) {
|
|
1402
|
+
for (const [key, value] of Object.entries(params)) {
|
|
1403
|
+
if (value !== void 0) {
|
|
1404
|
+
url.searchParams.set(key, String(value));
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
return url.toString();
|
|
1409
|
+
}
|
|
1410
|
+
async function fetchJson2(url) {
|
|
1411
|
+
const res = await fetch(url);
|
|
1412
|
+
if (!res.ok) {
|
|
1413
|
+
throw new Error(`HTTP ${res.status}: ${res.statusText} \u2014 ${url}`);
|
|
1414
|
+
}
|
|
1415
|
+
return res.json();
|
|
1416
|
+
}
|
|
1417
|
+
var DataClient = class {
|
|
1418
|
+
constructor(baseUrl = DATA_URL) {
|
|
1419
|
+
this.baseUrl = baseUrl;
|
|
1420
|
+
}
|
|
1421
|
+
// User data
|
|
1422
|
+
async getPositions(address, params) {
|
|
1423
|
+
return fetchJson2(
|
|
1424
|
+
buildUrl2(this.baseUrl, "/positions", { user: address, ...params })
|
|
1425
|
+
);
|
|
1426
|
+
}
|
|
1427
|
+
async getClosedPositions(address, params) {
|
|
1428
|
+
return fetchJson2(
|
|
1429
|
+
buildUrl2(this.baseUrl, "/positions", {
|
|
1430
|
+
user: address,
|
|
1431
|
+
closed: true,
|
|
1432
|
+
...params
|
|
1433
|
+
})
|
|
1434
|
+
);
|
|
1435
|
+
}
|
|
1436
|
+
async getValue(address) {
|
|
1437
|
+
return fetchJson2(buildUrl2(this.baseUrl, "/value", { user: address }));
|
|
1438
|
+
}
|
|
1439
|
+
async getTraded(address) {
|
|
1440
|
+
return fetchJson2(buildUrl2(this.baseUrl, "/traded", { user: address }));
|
|
1441
|
+
}
|
|
1442
|
+
async getTrades(address, params) {
|
|
1443
|
+
return fetchJson2(
|
|
1444
|
+
buildUrl2(this.baseUrl, "/trades", { user: address, ...params })
|
|
1445
|
+
);
|
|
1446
|
+
}
|
|
1447
|
+
async getActivity(address, params) {
|
|
1448
|
+
return fetchJson2(
|
|
1449
|
+
buildUrl2(this.baseUrl, "/activity", { user: address, ...params })
|
|
1450
|
+
);
|
|
1451
|
+
}
|
|
1452
|
+
// Market data
|
|
1453
|
+
async getHolders(conditionId, params) {
|
|
1454
|
+
return fetchJson2(
|
|
1455
|
+
buildUrl2(this.baseUrl, "/holders", { conditionId, ...params })
|
|
1456
|
+
);
|
|
1457
|
+
}
|
|
1458
|
+
async getOpenInterest(conditionId) {
|
|
1459
|
+
return fetchJson2(
|
|
1460
|
+
buildUrl2(this.baseUrl, "/open-interest", { conditionId })
|
|
1461
|
+
);
|
|
1462
|
+
}
|
|
1463
|
+
async getVolume(eventId) {
|
|
1464
|
+
return fetchJson2(buildUrl2(this.baseUrl, "/volume", { eventId }));
|
|
1465
|
+
}
|
|
1466
|
+
// Leaderboards
|
|
1467
|
+
async getLeaderboard(params) {
|
|
1468
|
+
return fetchJson2(
|
|
1469
|
+
buildUrl2(this.baseUrl, "/leaderboard", {
|
|
1470
|
+
period: params?.period,
|
|
1471
|
+
order_by: params?.orderBy,
|
|
1472
|
+
limit: params?.limit,
|
|
1473
|
+
offset: params?.offset
|
|
1474
|
+
})
|
|
1475
|
+
);
|
|
1476
|
+
}
|
|
1477
|
+
async getBuilderLeaderboard(params) {
|
|
1478
|
+
return fetchJson2(
|
|
1479
|
+
buildUrl2(
|
|
1480
|
+
this.baseUrl,
|
|
1481
|
+
"/leaderboard/builders",
|
|
1482
|
+
params
|
|
1483
|
+
)
|
|
1484
|
+
);
|
|
1485
|
+
}
|
|
1486
|
+
async getBuilderVolume(params) {
|
|
1487
|
+
return fetchJson2(
|
|
1488
|
+
buildUrl2(
|
|
1489
|
+
this.baseUrl,
|
|
1490
|
+
"/leaderboard/builders/volume",
|
|
1491
|
+
params
|
|
1492
|
+
)
|
|
1493
|
+
);
|
|
1494
|
+
}
|
|
1495
|
+
};
|
|
1496
|
+
|
|
1497
|
+
// src/commands/data.ts
|
|
1498
|
+
var out5 = (data) => console.log(JSON.stringify(data, null, 2));
|
|
1499
|
+
function resolveAddr(address, opts) {
|
|
1500
|
+
if (address) return address;
|
|
1501
|
+
const key = resolvePrivateKey(opts?.privateKey);
|
|
1502
|
+
const account = privateKeyToAccount6(key);
|
|
1503
|
+
return deriveGnosisSafeWallet(account.address);
|
|
1504
|
+
}
|
|
1505
|
+
var dataCommand = new Command11("data").description(
|
|
1506
|
+
"Data API commands"
|
|
1507
|
+
);
|
|
1508
|
+
dataCommand.command("positions [address]").description("Get positions (defaults to your wallet)").option("--private-key <key>", "Private key").option("--limit <n>", "Limit results", parseInt).option("--offset <n>", "Offset results", parseInt).action(async (address, opts) => {
|
|
1509
|
+
const addr = resolveAddr(address, opts);
|
|
1510
|
+
const client = new DataClient();
|
|
1511
|
+
const result = await client.getPositions(addr, opts);
|
|
1512
|
+
out5(result);
|
|
1513
|
+
});
|
|
1514
|
+
dataCommand.command("closed-positions [address]").description("Get closed positions (defaults to your wallet)").option("--private-key <key>", "Private key").option("--limit <n>", "Limit results", parseInt).option("--offset <n>", "Offset results", parseInt).action(async (address, opts) => {
|
|
1515
|
+
const addr = resolveAddr(address, opts);
|
|
1516
|
+
const client = new DataClient();
|
|
1517
|
+
const result = await client.getClosedPositions(addr, opts);
|
|
1518
|
+
out5(result);
|
|
1519
|
+
});
|
|
1520
|
+
dataCommand.command("value [address]").description("Get portfolio value (defaults to your wallet)").option("--private-key <key>", "Private key").action(async (address, opts) => {
|
|
1521
|
+
const addr = resolveAddr(address, opts);
|
|
1522
|
+
const client = new DataClient();
|
|
1523
|
+
const result = await client.getValue(addr);
|
|
1524
|
+
out5(result);
|
|
1525
|
+
});
|
|
1526
|
+
dataCommand.command("traded [address]").description("Check if address has traded (defaults to your wallet)").option("--private-key <key>", "Private key").action(async (address, opts) => {
|
|
1527
|
+
const addr = resolveAddr(address, opts);
|
|
1528
|
+
const client = new DataClient();
|
|
1529
|
+
const result = await client.getTraded(addr);
|
|
1530
|
+
out5(result);
|
|
1531
|
+
});
|
|
1532
|
+
dataCommand.command("trades [address]").description("Get trade history (defaults to your wallet)").option("--private-key <key>", "Private key").option("--limit <n>", "Limit results", parseInt).option("--offset <n>", "Offset results", parseInt).action(async (address, opts) => {
|
|
1533
|
+
const addr = resolveAddr(address, opts);
|
|
1534
|
+
const client = new DataClient();
|
|
1535
|
+
const result = await client.getTrades(addr, opts);
|
|
1536
|
+
out5(result);
|
|
1537
|
+
});
|
|
1538
|
+
dataCommand.command("activity [address]").description("Get activity history (defaults to your wallet)").option("--private-key <key>", "Private key").option("--limit <n>", "Limit results", parseInt).option("--offset <n>", "Offset results", parseInt).action(async (address, opts) => {
|
|
1539
|
+
const addr = resolveAddr(address, opts);
|
|
1540
|
+
const client = new DataClient();
|
|
1541
|
+
const result = await client.getActivity(addr, opts);
|
|
1542
|
+
out5(result);
|
|
1543
|
+
});
|
|
1544
|
+
dataCommand.command("holders <condition_id>").description("Get holders for condition").option("--limit <n>", "Limit results", parseInt).action(async (conditionId, opts) => {
|
|
1545
|
+
const client = new DataClient();
|
|
1546
|
+
const result = await client.getHolders(conditionId, opts);
|
|
1547
|
+
out5(result);
|
|
1548
|
+
});
|
|
1549
|
+
dataCommand.command("open-interest <condition_id>").description("Get open interest").action(async (conditionId) => {
|
|
1550
|
+
const client = new DataClient();
|
|
1551
|
+
const result = await client.getOpenInterest(conditionId);
|
|
1552
|
+
out5(result);
|
|
1553
|
+
});
|
|
1554
|
+
dataCommand.command("volume <event_id>").description("Get volume for event").action(async (eventId) => {
|
|
1555
|
+
const client = new DataClient();
|
|
1556
|
+
const result = await client.getVolume(eventId);
|
|
1557
|
+
out5(result);
|
|
1558
|
+
});
|
|
1559
|
+
dataCommand.command("leaderboard").description("Get leaderboard").option("--period <period>", "Time period").option("--order-by <field>", "Order by field").option("--limit <n>", "Limit results", parseInt).option("--offset <n>", "Offset results", parseInt).action(async (opts) => {
|
|
1560
|
+
const client = new DataClient();
|
|
1561
|
+
const result = await client.getLeaderboard({
|
|
1562
|
+
period: opts.period,
|
|
1563
|
+
orderBy: opts.orderBy,
|
|
1564
|
+
limit: opts.limit,
|
|
1565
|
+
offset: opts.offset
|
|
1566
|
+
});
|
|
1567
|
+
out5(result);
|
|
1568
|
+
});
|
|
1569
|
+
dataCommand.command("builder-leaderboard").description("Get builder leaderboard").option("--period <period>", "Time period").option("--limit <n>", "Limit results", parseInt).option("--offset <n>", "Offset results", parseInt).action(async (opts) => {
|
|
1570
|
+
const client = new DataClient();
|
|
1571
|
+
const result = await client.getBuilderLeaderboard(opts);
|
|
1572
|
+
out5(result);
|
|
1573
|
+
});
|
|
1574
|
+
dataCommand.command("builder-volume").description("Get builder volume").option("--period <period>", "Time period").action(async (opts) => {
|
|
1575
|
+
const client = new DataClient();
|
|
1576
|
+
const result = await client.getBuilderVolume(opts);
|
|
1577
|
+
out5(result);
|
|
1578
|
+
});
|
|
1579
|
+
|
|
1580
|
+
// src/commands/approve.ts
|
|
1581
|
+
import { Command as Command12 } from "commander";
|
|
1582
|
+
import { createPublicClient as createPublicClient2, http as http3 } from "viem";
|
|
1583
|
+
import { privateKeyToAccount as privateKeyToAccount7 } from "viem/accounts";
|
|
1584
|
+
import { polygon as polygon3 } from "viem/chains";
|
|
1585
|
+
var out6 = (data) => console.log(JSON.stringify(data, null, 2));
|
|
1586
|
+
var approveCommand = new Command12("approve").description(
|
|
1587
|
+
"Token approval management"
|
|
1588
|
+
);
|
|
1589
|
+
approveCommand.command("check [address]").description("Check approval status").option("--private-key <key>", "Private key").action(async (address, opts) => {
|
|
1590
|
+
let targetAddress;
|
|
1591
|
+
if (address) {
|
|
1592
|
+
targetAddress = address;
|
|
1593
|
+
} else {
|
|
1594
|
+
const key = resolvePrivateKey(opts.privateKey);
|
|
1595
|
+
const account = privateKeyToAccount7(key);
|
|
1596
|
+
targetAddress = deriveGnosisSafeWallet(account.address);
|
|
1597
|
+
}
|
|
1598
|
+
const publicClient = createPublicClient2({
|
|
1599
|
+
chain: polygon3,
|
|
1600
|
+
transport: http3(RPC_URL)
|
|
1601
|
+
});
|
|
1602
|
+
const [usdcCtfExchange, usdcNegRisk, ctfCtfExchange, ctfNegRisk] = await Promise.all([
|
|
1603
|
+
publicClient.readContract({
|
|
1604
|
+
address: USDCE_ADDRESS,
|
|
1605
|
+
abi: ERC20_ABI,
|
|
1606
|
+
functionName: "allowance",
|
|
1607
|
+
args: [targetAddress, CTF_EXCHANGE]
|
|
1608
|
+
}),
|
|
1609
|
+
publicClient.readContract({
|
|
1610
|
+
address: USDCE_ADDRESS,
|
|
1611
|
+
abi: ERC20_ABI,
|
|
1612
|
+
functionName: "allowance",
|
|
1613
|
+
args: [targetAddress, NEG_RISK_CTF_EXCHANGE]
|
|
1614
|
+
}),
|
|
1615
|
+
publicClient.readContract({
|
|
1616
|
+
address: CTF_ADDRESS,
|
|
1617
|
+
abi: ERC1155_ABI,
|
|
1618
|
+
functionName: "isApprovedForAll",
|
|
1619
|
+
args: [targetAddress, CTF_EXCHANGE]
|
|
1620
|
+
}),
|
|
1621
|
+
publicClient.readContract({
|
|
1622
|
+
address: CTF_ADDRESS,
|
|
1623
|
+
abi: ERC1155_ABI,
|
|
1624
|
+
functionName: "isApprovedForAll",
|
|
1625
|
+
args: [targetAddress, NEG_RISK_CTF_EXCHANGE]
|
|
1626
|
+
})
|
|
1627
|
+
]);
|
|
1628
|
+
out6({
|
|
1629
|
+
address: targetAddress,
|
|
1630
|
+
"usdc.e": {
|
|
1631
|
+
ctfExchange: usdcCtfExchange.toString(),
|
|
1632
|
+
negRiskExchange: usdcNegRisk.toString()
|
|
1633
|
+
},
|
|
1634
|
+
ctf: {
|
|
1635
|
+
ctfExchange: ctfCtfExchange,
|
|
1636
|
+
negRiskExchange: ctfNegRisk
|
|
1637
|
+
}
|
|
1638
|
+
});
|
|
1639
|
+
});
|
|
1640
|
+
approveCommand.command("set").description("Set all approvals for trading (gas-free via relayer)").option("--private-key <key>", "Private key").action(async (opts) => {
|
|
1641
|
+
const key = resolvePrivateKey(opts.privateKey);
|
|
1642
|
+
const account = privateKeyToAccount7(key);
|
|
1643
|
+
const safeAddress = deriveGnosisSafeWallet(account.address);
|
|
1644
|
+
const txns = buildApproveCalldata();
|
|
1645
|
+
const result = await relayMultiSend(key, txns, "approve");
|
|
1646
|
+
out6({
|
|
1647
|
+
address: account.address,
|
|
1648
|
+
safeAddress,
|
|
1649
|
+
method: "relayer (gas-free)",
|
|
1650
|
+
...result
|
|
1651
|
+
});
|
|
1652
|
+
});
|
|
1653
|
+
approveCommand.command("deploy").description("Deploy Gnosis Safe wallet via relayer (gas-free)").option("--private-key <key>", "Private key").action(async (opts) => {
|
|
1654
|
+
const key = resolvePrivateKey(opts.privateKey);
|
|
1655
|
+
const account = privateKeyToAccount7(key);
|
|
1656
|
+
const safeAddress = deriveGnosisSafeWallet(account.address);
|
|
1657
|
+
console.log(`EOA: ${account.address}`);
|
|
1658
|
+
console.log(`Safe: ${safeAddress}`);
|
|
1659
|
+
console.log("Deploying...");
|
|
1660
|
+
const result = await deploySafe(key);
|
|
1661
|
+
out6(result);
|
|
1662
|
+
});
|
|
1663
|
+
|
|
1664
|
+
// src/commands/ctf.ts
|
|
1665
|
+
import { Command as Command13 } from "commander";
|
|
1666
|
+
import { createPublicClient as createPublicClient3, http as http4 } from "viem";
|
|
1667
|
+
import { polygon as polygon4 } from "viem/chains";
|
|
1668
|
+
var out7 = (data) => console.log(JSON.stringify(data, null, 2));
|
|
1669
|
+
var ZERO_BYTES322 = "0x0000000000000000000000000000000000000000000000000000000000000000";
|
|
1670
|
+
var ctfCommand = new Command13("ctf").description(
|
|
1671
|
+
"Conditional Token Framework commands"
|
|
1672
|
+
);
|
|
1673
|
+
ctfCommand.command("split").description("Split position").requiredOption("--condition <id>", "Condition ID").requiredOption("--amount <amount>", "Amount in USDC.e units", parseFloat).option("--collateral <address>", "Collateral token address", USDCE_ADDRESS).option("--partition <sets>", "Partition index sets (comma-separated)", "1,2").option("--parent-collection <id>", "Parent collection ID", ZERO_BYTES322).option("--private-key <key>", "Private key").action(async (opts) => {
|
|
1674
|
+
const key = resolvePrivateKey(opts.privateKey);
|
|
1675
|
+
const partition = opts.partition.split(",").map((s) => BigInt(s));
|
|
1676
|
+
const amount = BigInt(Math.round(opts.amount * 1e6));
|
|
1677
|
+
const { to, data } = buildSplitCalldata(
|
|
1678
|
+
opts.condition,
|
|
1679
|
+
amount,
|
|
1680
|
+
partition,
|
|
1681
|
+
opts.collateral,
|
|
1682
|
+
opts.parentCollection
|
|
1683
|
+
);
|
|
1684
|
+
const result = await relayTransaction(key, to, data, "split");
|
|
1685
|
+
out7({ action: "splitPosition", ...result });
|
|
1686
|
+
});
|
|
1687
|
+
ctfCommand.command("merge").description("Merge positions").requiredOption("--condition <id>", "Condition ID").requiredOption("--amount <amount>", "Amount in USDC.e units", parseFloat).option("--collateral <address>", "Collateral token address", USDCE_ADDRESS).option("--partition <sets>", "Partition index sets (comma-separated)", "1,2").option("--parent-collection <id>", "Parent collection ID", ZERO_BYTES322).option("--private-key <key>", "Private key").action(async (opts) => {
|
|
1688
|
+
const key = resolvePrivateKey(opts.privateKey);
|
|
1689
|
+
const partition = opts.partition.split(",").map((s) => BigInt(s));
|
|
1690
|
+
const amount = BigInt(Math.round(opts.amount * 1e6));
|
|
1691
|
+
const { to, data } = buildMergeCalldata(
|
|
1692
|
+
opts.condition,
|
|
1693
|
+
amount,
|
|
1694
|
+
partition,
|
|
1695
|
+
opts.collateral,
|
|
1696
|
+
opts.parentCollection
|
|
1697
|
+
);
|
|
1698
|
+
const result = await relayTransaction(key, to, data, "merge");
|
|
1699
|
+
out7({ action: "mergePositions", ...result });
|
|
1700
|
+
});
|
|
1701
|
+
ctfCommand.command("redeem").description("Redeem positions (via relayer, gas-free)").requiredOption("--condition <id>", "Condition ID").option("--collateral <address>", "Collateral token address", USDCE_ADDRESS).option("--index-sets <sets>", "Index sets (comma-separated)", "1,2").option("--parent-collection <id>", "Parent collection ID", ZERO_BYTES322).option("--private-key <key>", "Private key").action(async (opts) => {
|
|
1702
|
+
const key = resolvePrivateKey(opts.privateKey);
|
|
1703
|
+
const indexSets = opts.indexSets.split(",").map((s) => BigInt(s));
|
|
1704
|
+
const { to, data } = buildRedeemCalldata(
|
|
1705
|
+
opts.condition,
|
|
1706
|
+
indexSets,
|
|
1707
|
+
opts.collateral,
|
|
1708
|
+
opts.parentCollection
|
|
1709
|
+
);
|
|
1710
|
+
const result = await relayTransaction(key, to, data, "redeem");
|
|
1711
|
+
out7({ action: "redeemPositions", ...result });
|
|
1712
|
+
});
|
|
1713
|
+
ctfCommand.command("redeem-neg-risk").description("Redeem neg-risk positions (via relayer, gas-free)").requiredOption("--condition <id>", "Condition ID").requiredOption("--amounts <amounts>", "Amounts (comma-separated)").option("--private-key <key>", "Private key").action(async (opts) => {
|
|
1714
|
+
const key = resolvePrivateKey(opts.privateKey);
|
|
1715
|
+
const amounts = opts.amounts.split(",").map((s) => BigInt(s));
|
|
1716
|
+
const { to, data } = buildNegRiskRedeemCalldata(opts.condition, amounts);
|
|
1717
|
+
const result = await relayTransaction(key, to, data, "redeem");
|
|
1718
|
+
out7({ action: "redeemPositions (neg-risk)", ...result });
|
|
1719
|
+
});
|
|
1720
|
+
ctfCommand.command("condition-id").description("Compute condition ID").requiredOption("--oracle <address>", "Oracle address").requiredOption("--question <id>", "Question ID (bytes32)").requiredOption("--outcomes <n>", "Number of outcomes", parseInt).action(async (opts) => {
|
|
1721
|
+
const publicClient = createPublicClient3({ chain: polygon4, transport: http4(RPC_URL) });
|
|
1722
|
+
const result = await publicClient.readContract({
|
|
1723
|
+
address: CTF_ADDRESS,
|
|
1724
|
+
abi: CTF_ABI,
|
|
1725
|
+
functionName: "getConditionId",
|
|
1726
|
+
args: [opts.oracle, opts.question, BigInt(opts.outcomes)]
|
|
1727
|
+
});
|
|
1728
|
+
out7({ conditionId: result });
|
|
1729
|
+
});
|
|
1730
|
+
ctfCommand.command("collection-id").description("Compute collection ID").requiredOption("--condition <id>", "Condition ID").requiredOption("--index-set <n>", "Index set", parseInt).action(async (opts) => {
|
|
1731
|
+
const publicClient = createPublicClient3({ chain: polygon4, transport: http4(RPC_URL) });
|
|
1732
|
+
const result = await publicClient.readContract({
|
|
1733
|
+
address: CTF_ADDRESS,
|
|
1734
|
+
abi: CTF_ABI,
|
|
1735
|
+
functionName: "getCollectionId",
|
|
1736
|
+
args: [ZERO_BYTES322, opts.condition, BigInt(opts.indexSet)]
|
|
1737
|
+
});
|
|
1738
|
+
out7({ collectionId: result });
|
|
1739
|
+
});
|
|
1740
|
+
ctfCommand.command("position-id").description("Compute position ID").requiredOption("--collection <id>", "Collection ID").action(async (opts) => {
|
|
1741
|
+
const publicClient = createPublicClient3({ chain: polygon4, transport: http4(RPC_URL) });
|
|
1742
|
+
const result = await publicClient.readContract({
|
|
1743
|
+
address: CTF_ADDRESS,
|
|
1744
|
+
abi: CTF_ABI,
|
|
1745
|
+
functionName: "getPositionId",
|
|
1746
|
+
args: [USDCE_ADDRESS, opts.collection]
|
|
1747
|
+
});
|
|
1748
|
+
out7({ positionId: result.toString() });
|
|
1749
|
+
});
|
|
1750
|
+
|
|
1751
|
+
// src/commands/resolve.ts
|
|
1752
|
+
import { Command as Command14 } from "commander";
|
|
1753
|
+
var out8 = (data) => console.log(JSON.stringify(data, null, 2));
|
|
1754
|
+
var resolveCommand = new Command14("resolve").description("Resolve a Polymarket URL/username to event ID, market ID, condition ID, token IDs, or wallet address").argument("<url>", "e.g. https://polymarket.com/event/<slug>, @username").action(async (input) => {
|
|
1755
|
+
const profileMatch = input.match(/(?:polymarket\.com\/)?\@([^/?\s]+)/) || (!input.includes("/") && input.startsWith("@") ? [null, input.slice(1)] : null);
|
|
1756
|
+
if (profileMatch) {
|
|
1757
|
+
const username = profileMatch[1];
|
|
1758
|
+
const res = await fetch(`https://polymarket.com/@${username}`);
|
|
1759
|
+
if (!res.ok) throw new Error(`Profile not found: @${username}`);
|
|
1760
|
+
const html = await res.text();
|
|
1761
|
+
const walletMatch = html.match(/"proxyWallet":"(0x[a-fA-F0-9]+)"/);
|
|
1762
|
+
if (!walletMatch) throw new Error(`Could not resolve @${username}`);
|
|
1763
|
+
out8({ type: "profile", username, address: walletMatch[1] });
|
|
1764
|
+
return;
|
|
1765
|
+
}
|
|
1766
|
+
const eventMatch = input.match(/(?:polymarket\.com\/)?event\/([^/?\s]+)(?:\/([^/?\s]+))?/);
|
|
1767
|
+
if (eventMatch) {
|
|
1768
|
+
const eventSlug = eventMatch[1];
|
|
1769
|
+
const marketSlug = eventMatch[2];
|
|
1770
|
+
const gamma = new GammaClient();
|
|
1771
|
+
const events = await gamma.getEvents({ slug: eventSlug });
|
|
1772
|
+
if (!events || events.length === 0) throw new Error(`Event not found: ${eventSlug}`);
|
|
1773
|
+
const event = events[0];
|
|
1774
|
+
const result = {
|
|
1775
|
+
type: "event",
|
|
1776
|
+
eventId: event.id,
|
|
1777
|
+
slug: event.slug,
|
|
1778
|
+
title: event.title
|
|
1779
|
+
};
|
|
1780
|
+
if (marketSlug) {
|
|
1781
|
+
const markets = await gamma.getMarkets({ slug: marketSlug });
|
|
1782
|
+
if (markets && markets.length > 0) {
|
|
1783
|
+
const market = markets[0];
|
|
1784
|
+
result.type = "market";
|
|
1785
|
+
result.marketId = market.id;
|
|
1786
|
+
result.marketSlug = market.slug;
|
|
1787
|
+
result.conditionId = market.conditionId;
|
|
1788
|
+
result.question = market.question;
|
|
1789
|
+
if (market.clobTokenIds) {
|
|
1790
|
+
try {
|
|
1791
|
+
result.tokenIds = JSON.parse(market.clobTokenIds);
|
|
1792
|
+
} catch {
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
out8(result);
|
|
1798
|
+
return;
|
|
1799
|
+
}
|
|
1800
|
+
throw new Error(`Unrecognized format. Expected a Polymarket URL, event slug, or @username.`);
|
|
1801
|
+
});
|
|
1802
|
+
|
|
1803
|
+
// src/commands/setup.ts
|
|
1804
|
+
import { Command as Command15 } from "commander";
|
|
1805
|
+
import { privateKeyToAccount as privateKeyToAccount8 } from "viem/accounts";
|
|
1806
|
+
import { createPublicClient as createPublicClient4, http as http5, formatUnits as formatUnits2 } from "viem";
|
|
1807
|
+
import { polygon as polygon5 } from "viem/chains";
|
|
1808
|
+
import { select, confirm, password } from "@inquirer/prompts";
|
|
1809
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2, mkdirSync } from "fs";
|
|
1810
|
+
var green = (s) => `\x1B[32m${s}\x1B[0m`;
|
|
1811
|
+
var yellow = (s) => `\x1B[33m${s}\x1B[0m`;
|
|
1812
|
+
var red = (s) => `\x1B[31m${s}\x1B[0m`;
|
|
1813
|
+
var cyan = (s) => `\x1B[36m${s}\x1B[0m`;
|
|
1814
|
+
var bold = (s) => `\x1B[1m${s}\x1B[0m`;
|
|
1815
|
+
var dim = (s) => `\x1B[2m${s}\x1B[0m`;
|
|
1816
|
+
function step(n, total, title) {
|
|
1817
|
+
console.log(`
|
|
1818
|
+
${bold(`[${n}/${total}]`)} ${bold(title)}`);
|
|
1819
|
+
}
|
|
1820
|
+
async function pollTransaction(relayer, txId) {
|
|
1821
|
+
for (let i = 0; i < 30; i++) {
|
|
1822
|
+
await new Promise((r) => setTimeout(r, 3e3));
|
|
1823
|
+
try {
|
|
1824
|
+
const tx = await relayer.getTransaction(txId);
|
|
1825
|
+
const state = Array.isArray(tx) ? tx[0]?.state : tx?.state;
|
|
1826
|
+
if (state === "STATE_CONFIRMED" || state === "STATE_MINED") {
|
|
1827
|
+
console.log(` ${green("done")}`);
|
|
1828
|
+
return true;
|
|
1829
|
+
}
|
|
1830
|
+
if (state === "STATE_FAILED") {
|
|
1831
|
+
console.log(` ${red("failed")}`);
|
|
1832
|
+
return false;
|
|
1833
|
+
}
|
|
1834
|
+
} catch {
|
|
1835
|
+
}
|
|
1836
|
+
process.stdout.write(".");
|
|
1837
|
+
}
|
|
1838
|
+
console.log(` ${red("timeout")}`);
|
|
1839
|
+
return false;
|
|
1840
|
+
}
|
|
1841
|
+
function updateEnvFile(vars) {
|
|
1842
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
1843
|
+
let content = "";
|
|
1844
|
+
if (existsSync2(CONFIG_ENV_PATH)) {
|
|
1845
|
+
content = readFileSync2(CONFIG_ENV_PATH, "utf-8");
|
|
1846
|
+
}
|
|
1847
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
1848
|
+
const regex = new RegExp(`^${key}=.*$`, "m");
|
|
1849
|
+
if (regex.test(content)) {
|
|
1850
|
+
content = content.replace(regex, `${key}=${value}`);
|
|
1851
|
+
} else {
|
|
1852
|
+
content = content.trimEnd() + `
|
|
1853
|
+
${key}=${value}
|
|
1854
|
+
`;
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
writeFileSync(CONFIG_ENV_PATH, content, { mode: 384 });
|
|
1858
|
+
}
|
|
1859
|
+
var setupCommand = new Command15("setup").description("Interactive setup wizard for Polymarket trading").action(async () => {
|
|
1860
|
+
console.log(bold("\nPolymarket CLI Setup\n"));
|
|
1861
|
+
let privateKey;
|
|
1862
|
+
let eoaAddress;
|
|
1863
|
+
const existingKey = process.env.POLYMARKET_PRIVATE_KEY;
|
|
1864
|
+
if (existingKey) {
|
|
1865
|
+
privateKey = resolvePrivateKey(existingKey);
|
|
1866
|
+
const account = privateKeyToAccount8(privateKey);
|
|
1867
|
+
eoaAddress = account.address;
|
|
1868
|
+
step(1, 4, "Wallet");
|
|
1869
|
+
console.log(` ${green("Found existing key in environment")}`);
|
|
1870
|
+
console.log(` EOA: ${cyan(eoaAddress)}`);
|
|
1871
|
+
} else {
|
|
1872
|
+
step(1, 4, "Wallet");
|
|
1873
|
+
const walletChoice = await select({
|
|
1874
|
+
message: "How would you like to set up your wallet?",
|
|
1875
|
+
choices: [
|
|
1876
|
+
{ name: "Import existing private key", value: "import" },
|
|
1877
|
+
{ name: "Generate new wallet", value: "generate" }
|
|
1878
|
+
]
|
|
1879
|
+
});
|
|
1880
|
+
if (walletChoice === "import") {
|
|
1881
|
+
const keyInput = await password({
|
|
1882
|
+
message: "Enter your private key:",
|
|
1883
|
+
mask: "*"
|
|
1884
|
+
});
|
|
1885
|
+
privateKey = resolvePrivateKey(keyInput);
|
|
1886
|
+
const account = privateKeyToAccount8(privateKey);
|
|
1887
|
+
eoaAddress = account.address;
|
|
1888
|
+
updateEnvFile({ POLYMARKET_PRIVATE_KEY: privateKey });
|
|
1889
|
+
console.log(` EOA: ${cyan(eoaAddress)}`);
|
|
1890
|
+
console.log(` ${green("Saved to ~/.polytown/.env")}`);
|
|
1891
|
+
} else {
|
|
1892
|
+
const wallet = createRandomWallet();
|
|
1893
|
+
privateKey = wallet.privateKey;
|
|
1894
|
+
eoaAddress = wallet.address;
|
|
1895
|
+
updateEnvFile({ POLYMARKET_PRIVATE_KEY: privateKey });
|
|
1896
|
+
console.log(` EOA: ${cyan(eoaAddress)}`);
|
|
1897
|
+
console.log(` Private Key: ${yellow(wallet.privateKey)}`);
|
|
1898
|
+
console.log(` ${green("Saved to ~/.polytown/.env")}`);
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
step(2, 4, "Gnosis Safe");
|
|
1902
|
+
const safeAddress = deriveGnosisSafeWallet(eoaAddress);
|
|
1903
|
+
console.log(` Safe: ${cyan(safeAddress)}`);
|
|
1904
|
+
const relayer = new RelayerClient();
|
|
1905
|
+
const deployed = await relayer.isDeployed(safeAddress);
|
|
1906
|
+
if (deployed) {
|
|
1907
|
+
console.log(` Status: ${green("deployed")}`);
|
|
1908
|
+
} else {
|
|
1909
|
+
console.log(` Status: ${yellow("not deployed")}`);
|
|
1910
|
+
const doDeploy = await confirm({
|
|
1911
|
+
message: "Deploy your Gnosis Safe now? (gas-free via relayer)",
|
|
1912
|
+
default: true
|
|
1913
|
+
});
|
|
1914
|
+
if (doDeploy) {
|
|
1915
|
+
process.stdout.write(" Deploying...");
|
|
1916
|
+
const result = await deploySafe(privateKey);
|
|
1917
|
+
if (result.alreadyDeployed) {
|
|
1918
|
+
console.log(` ${green("already deployed")}`);
|
|
1919
|
+
} else {
|
|
1920
|
+
await pollTransaction(relayer, result.transactionID);
|
|
1921
|
+
}
|
|
1922
|
+
} else {
|
|
1923
|
+
console.log(dim(" Skipped. Run later: polymarket approve deploy"));
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
step(3, 4, "Token Approvals");
|
|
1927
|
+
const publicClient = createPublicClient4({
|
|
1928
|
+
chain: polygon5,
|
|
1929
|
+
transport: http5(RPC_URL)
|
|
1930
|
+
});
|
|
1931
|
+
const [usdcCtf, usdcNeg, ctfCtf, ctfNeg] = await Promise.all([
|
|
1932
|
+
publicClient.readContract({
|
|
1933
|
+
address: USDCE_ADDRESS,
|
|
1934
|
+
abi: ERC20_ABI,
|
|
1935
|
+
functionName: "allowance",
|
|
1936
|
+
args: [safeAddress, CTF_EXCHANGE]
|
|
1937
|
+
}),
|
|
1938
|
+
publicClient.readContract({
|
|
1939
|
+
address: USDCE_ADDRESS,
|
|
1940
|
+
abi: ERC20_ABI,
|
|
1941
|
+
functionName: "allowance",
|
|
1942
|
+
args: [safeAddress, NEG_RISK_CTF_EXCHANGE]
|
|
1943
|
+
}),
|
|
1944
|
+
publicClient.readContract({
|
|
1945
|
+
address: CTF_ADDRESS,
|
|
1946
|
+
abi: ERC1155_ABI,
|
|
1947
|
+
functionName: "isApprovedForAll",
|
|
1948
|
+
args: [safeAddress, CTF_EXCHANGE]
|
|
1949
|
+
}),
|
|
1950
|
+
publicClient.readContract({
|
|
1951
|
+
address: CTF_ADDRESS,
|
|
1952
|
+
abi: ERC1155_ABI,
|
|
1953
|
+
functionName: "isApprovedForAll",
|
|
1954
|
+
args: [safeAddress, NEG_RISK_CTF_EXCHANGE]
|
|
1955
|
+
})
|
|
1956
|
+
]);
|
|
1957
|
+
const allApproved = usdcCtf > 0n && usdcNeg > 0n && ctfCtf && ctfNeg;
|
|
1958
|
+
const tag = (ok) => ok ? green("approved") : red("not approved");
|
|
1959
|
+
console.log(` USDC.e -> CTF Exchange: ${tag(usdcCtf > 0n)}`);
|
|
1960
|
+
console.log(` USDC.e -> Neg Risk Exchange: ${tag(usdcNeg > 0n)}`);
|
|
1961
|
+
console.log(` CTF -> CTF Exchange: ${tag(ctfCtf)}`);
|
|
1962
|
+
console.log(` CTF -> Neg Risk Exchange: ${tag(ctfNeg)}`);
|
|
1963
|
+
if (!allApproved) {
|
|
1964
|
+
const doApprove = await confirm({
|
|
1965
|
+
message: "Set all approvals now? (gas-free via relayer)",
|
|
1966
|
+
default: true
|
|
1967
|
+
});
|
|
1968
|
+
if (doApprove) {
|
|
1969
|
+
process.stdout.write(" Approving...");
|
|
1970
|
+
try {
|
|
1971
|
+
const txns = buildApproveCalldata();
|
|
1972
|
+
const result = await relayMultiSend(privateKey, txns, "approve");
|
|
1973
|
+
await pollTransaction(relayer, result.transactionID);
|
|
1974
|
+
} catch (e) {
|
|
1975
|
+
console.log(` ${red("failed")}: ${e.message}`);
|
|
1976
|
+
}
|
|
1977
|
+
} else {
|
|
1978
|
+
console.log(dim(" Skipped. Run later: polymarket approve set"));
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
step(4, 4, "Balance");
|
|
1982
|
+
const balance = await publicClient.readContract({
|
|
1983
|
+
address: USDCE_ADDRESS,
|
|
1984
|
+
abi: ERC20_ABI,
|
|
1985
|
+
functionName: "balanceOf",
|
|
1986
|
+
args: [safeAddress]
|
|
1987
|
+
});
|
|
1988
|
+
const formatted = formatUnits2(balance, 6);
|
|
1989
|
+
if (balance > 0n) {
|
|
1990
|
+
console.log(` USDC.e: ${green(`$${formatted}`)}`);
|
|
1991
|
+
console.log(green(bold("\n Setup complete! You're ready to trade.\n")));
|
|
1992
|
+
} else {
|
|
1993
|
+
console.log(` USDC.e: ${yellow("$0.00")}`);
|
|
1994
|
+
console.log(`
|
|
1995
|
+
Send ${bold("USDC.e")} on ${bold("Polygon")} to:`);
|
|
1996
|
+
console.log(` ${cyan(safeAddress)}
|
|
1997
|
+
`);
|
|
1998
|
+
}
|
|
1999
|
+
});
|
|
2000
|
+
|
|
2001
|
+
// src/commands/movers.ts
|
|
2002
|
+
import { Command as Command16 } from "commander";
|
|
2003
|
+
var CATEGORIES = [
|
|
2004
|
+
"all",
|
|
2005
|
+
"sports",
|
|
2006
|
+
"politics",
|
|
2007
|
+
"crypto",
|
|
2008
|
+
"culture",
|
|
2009
|
+
"weather",
|
|
2010
|
+
"economics",
|
|
2011
|
+
"tech",
|
|
2012
|
+
"finance"
|
|
2013
|
+
];
|
|
2014
|
+
var moversCommand = new Command16("movers").description("Get biggest movers (highest price change markets)").option(
|
|
2015
|
+
"--category <category>",
|
|
2016
|
+
`Filter by category (${CATEGORIES.join(", ")})`
|
|
2017
|
+
).action(async (opts) => {
|
|
2018
|
+
const params = {};
|
|
2019
|
+
if (opts.category && opts.category !== "all") {
|
|
2020
|
+
params.category = opts.category;
|
|
2021
|
+
}
|
|
2022
|
+
const url = new URL("https://polymarket.com/api/biggest-movers");
|
|
2023
|
+
for (const [k, v] of Object.entries(params)) {
|
|
2024
|
+
url.searchParams.set(k, v);
|
|
2025
|
+
}
|
|
2026
|
+
const res = await fetch(url.toString());
|
|
2027
|
+
if (!res.ok) {
|
|
2028
|
+
throw new Error(`HTTP ${res.status}: ${res.statusText}`);
|
|
2029
|
+
}
|
|
2030
|
+
const data = await res.json();
|
|
2031
|
+
console.log(JSON.stringify(data.markets || [], null, 2));
|
|
2032
|
+
});
|
|
2033
|
+
|
|
2034
|
+
// src/index.ts
|
|
2035
|
+
program.name("polytown").description("Polymarket CLI in Bun/TypeScript").version("0.1.0");
|
|
2036
|
+
program.addCommand(statusCommand);
|
|
2037
|
+
program.addCommand(marketsCommand);
|
|
2038
|
+
program.addCommand(eventsCommand);
|
|
2039
|
+
program.addCommand(tagsCommand);
|
|
2040
|
+
program.addCommand(seriesCommand);
|
|
2041
|
+
program.addCommand(commentsCommand);
|
|
2042
|
+
program.addCommand(profilesCommand);
|
|
2043
|
+
program.addCommand(sportsCommand);
|
|
2044
|
+
program.addCommand(clobCommand);
|
|
2045
|
+
program.addCommand(walletCommand);
|
|
2046
|
+
program.addCommand(dataCommand);
|
|
2047
|
+
program.addCommand(approveCommand);
|
|
2048
|
+
program.addCommand(ctfCommand);
|
|
2049
|
+
program.addCommand(resolveCommand);
|
|
2050
|
+
program.addCommand(setupCommand);
|
|
2051
|
+
program.addCommand(moversCommand);
|
|
2052
|
+
process.on("unhandledRejection", (err) => {
|
|
2053
|
+
console.error(`\x1B[31merror:\x1B[0m ${err?.message ?? err}`);
|
|
2054
|
+
process.exit(1);
|
|
2055
|
+
});
|
|
2056
|
+
program.parseAsync(process.argv);
|