create-polymarket-strategy 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +598 -11
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/templates/worker/package.json +1 -1
- package/templates/worker/src/durable-objects/positions.ts +189 -2
- package/templates/worker/src/durable-objects/scheduler.ts +108 -0
- package/templates/worker/src/lib/quote-manager.ts +358 -0
- package/templates/worker/src/scanner.ts +55 -3
package/dist/index.js
CHANGED
|
@@ -4,9 +4,294 @@
|
|
|
4
4
|
import { program } from "commander";
|
|
5
5
|
import fs from "fs-extra";
|
|
6
6
|
import path from "path";
|
|
7
|
-
import { execSync } from "child_process";
|
|
7
|
+
import { execSync as execSync2 } from "child_process";
|
|
8
8
|
import pc from "picocolors";
|
|
9
9
|
import { fileURLToPath } from "url";
|
|
10
|
+
|
|
11
|
+
// src/wallet/index.ts
|
|
12
|
+
import { ethers } from "ethers";
|
|
13
|
+
|
|
14
|
+
// src/wallet/constants.ts
|
|
15
|
+
var CHAIN_ID = 137;
|
|
16
|
+
var RPC_URL = "https://1rpc.io/matic";
|
|
17
|
+
var CLOB_API = "https://clob.polymarket.com";
|
|
18
|
+
var USDC_NATIVE = "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359";
|
|
19
|
+
var USDC_E = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174";
|
|
20
|
+
var CTF_ADDRESS = "0x4D97DCd97eC945f40cF65F87097ACe5EA0476045";
|
|
21
|
+
var EXCHANGE_CONTRACTS = [
|
|
22
|
+
{ address: "0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E", name: "CTF Exchange" },
|
|
23
|
+
{ address: "0xC5d563A36AE78145C45a50134d48A1215220f80a", name: "Neg Risk CTF Exchange" },
|
|
24
|
+
{ address: "0xd91E80cF2E7be2e162c6513ceD06f1dD0dA35296", name: "Neg Risk Adapter" }
|
|
25
|
+
];
|
|
26
|
+
var CLOB_AUTH_DOMAIN = {
|
|
27
|
+
name: "ClobAuthDomain",
|
|
28
|
+
version: "1",
|
|
29
|
+
chainId: CHAIN_ID
|
|
30
|
+
};
|
|
31
|
+
var CLOB_AUTH_MESSAGE = "This message attests that I control the given wallet";
|
|
32
|
+
var ERC20_ABI = [
|
|
33
|
+
"function balanceOf(address owner) view returns (uint256)",
|
|
34
|
+
"function approve(address spender, uint256 amount) returns (bool)",
|
|
35
|
+
"function allowance(address owner, address spender) view returns (uint256)"
|
|
36
|
+
];
|
|
37
|
+
var ERC1155_ABI = [
|
|
38
|
+
"function setApprovalForAll(address operator, bool approved)",
|
|
39
|
+
"function isApprovedForAll(address account, address operator) view returns (bool)"
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
// src/wallet/index.ts
|
|
43
|
+
function generateWallet() {
|
|
44
|
+
const wallet = ethers.Wallet.createRandom();
|
|
45
|
+
return {
|
|
46
|
+
address: wallet.address,
|
|
47
|
+
privateKey: wallet.privateKey
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
async function signClobAuthMessage(wallet, timestamp, nonce) {
|
|
51
|
+
const types = {
|
|
52
|
+
ClobAuth: [
|
|
53
|
+
{ name: "address", type: "address" },
|
|
54
|
+
{ name: "timestamp", type: "string" },
|
|
55
|
+
{ name: "nonce", type: "uint256" },
|
|
56
|
+
{ name: "message", type: "string" }
|
|
57
|
+
]
|
|
58
|
+
};
|
|
59
|
+
const value = {
|
|
60
|
+
address: wallet.address,
|
|
61
|
+
timestamp: timestamp.toString(),
|
|
62
|
+
nonce,
|
|
63
|
+
message: CLOB_AUTH_MESSAGE
|
|
64
|
+
};
|
|
65
|
+
return wallet.signTypedData(CLOB_AUTH_DOMAIN, types, value);
|
|
66
|
+
}
|
|
67
|
+
async function deriveApiCredentials(privateKey, nonce = 0) {
|
|
68
|
+
const wallet = new ethers.Wallet(privateKey);
|
|
69
|
+
const timestamp = Math.floor(Date.now() / 1e3);
|
|
70
|
+
const signature = await signClobAuthMessage(wallet, timestamp, nonce);
|
|
71
|
+
const headers = {
|
|
72
|
+
"POLY_ADDRESS": wallet.address,
|
|
73
|
+
"POLY_SIGNATURE": signature,
|
|
74
|
+
"POLY_TIMESTAMP": timestamp.toString(),
|
|
75
|
+
"POLY_NONCE": nonce.toString()
|
|
76
|
+
};
|
|
77
|
+
let response = await fetch(`${CLOB_API}/auth/derive-api-key`, {
|
|
78
|
+
method: "GET",
|
|
79
|
+
headers
|
|
80
|
+
});
|
|
81
|
+
if (!response.ok) {
|
|
82
|
+
response = await fetch(`${CLOB_API}/auth/api-key`, {
|
|
83
|
+
method: "POST",
|
|
84
|
+
headers
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
if (!response.ok) {
|
|
88
|
+
const text = await response.text();
|
|
89
|
+
throw new Error(`Failed to derive API credentials: ${response.status} ${text}`);
|
|
90
|
+
}
|
|
91
|
+
const data = await response.json();
|
|
92
|
+
return {
|
|
93
|
+
apiKey: data.apiKey,
|
|
94
|
+
apiSecret: data.secret,
|
|
95
|
+
apiPassphrase: data.passphrase
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function getProvider() {
|
|
99
|
+
return new ethers.JsonRpcProvider(RPC_URL, CHAIN_ID);
|
|
100
|
+
}
|
|
101
|
+
async function getBalances(address) {
|
|
102
|
+
const provider = getProvider();
|
|
103
|
+
const polBalance = await provider.getBalance(address);
|
|
104
|
+
const pol = Number(ethers.formatEther(polBalance));
|
|
105
|
+
const usdcNativeContract = new ethers.Contract(USDC_NATIVE, ERC20_ABI, provider);
|
|
106
|
+
const usdcNativeBalance = await usdcNativeContract.balanceOf(address);
|
|
107
|
+
const usdcNative = Number(ethers.formatUnits(usdcNativeBalance, 6));
|
|
108
|
+
const usdcEContract = new ethers.Contract(USDC_E, ERC20_ABI, provider);
|
|
109
|
+
const usdcEBalance = await usdcEContract.balanceOf(address);
|
|
110
|
+
const usdcE = Number(ethers.formatUnits(usdcEBalance, 6));
|
|
111
|
+
return {
|
|
112
|
+
address,
|
|
113
|
+
pol,
|
|
114
|
+
usdcNative,
|
|
115
|
+
usdcE,
|
|
116
|
+
totalUsdc: usdcNative + usdcE
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
async function approveExchangeContracts(privateKey, onProgress) {
|
|
120
|
+
const provider = getProvider();
|
|
121
|
+
const wallet = new ethers.Wallet(privateKey, provider);
|
|
122
|
+
const results = [];
|
|
123
|
+
const usdcE = new ethers.Contract(USDC_E, ERC20_ABI, wallet);
|
|
124
|
+
const ctf = new ethers.Contract(CTF_ADDRESS, ERC1155_ABI, wallet);
|
|
125
|
+
const maxApproval = ethers.MaxUint256;
|
|
126
|
+
for (const exchange of EXCHANGE_CONTRACTS) {
|
|
127
|
+
const result = {
|
|
128
|
+
contract: exchange.name,
|
|
129
|
+
usdcApproved: false,
|
|
130
|
+
ctfApproved: false
|
|
131
|
+
};
|
|
132
|
+
const currentAllowance = await usdcE.allowance(wallet.address, exchange.address);
|
|
133
|
+
if (currentAllowance < ethers.parseUnits("1000000000", 6)) {
|
|
134
|
+
onProgress?.(`Approving USDC.e for ${exchange.name}...`);
|
|
135
|
+
const tx = await usdcE.approve(exchange.address, maxApproval);
|
|
136
|
+
const receipt = await tx.wait();
|
|
137
|
+
result.usdcTxHash = receipt.hash;
|
|
138
|
+
result.usdcApproved = true;
|
|
139
|
+
} else {
|
|
140
|
+
onProgress?.(`USDC.e already approved for ${exchange.name}`);
|
|
141
|
+
result.usdcApproved = true;
|
|
142
|
+
}
|
|
143
|
+
const isApproved = await ctf.isApprovedForAll(wallet.address, exchange.address);
|
|
144
|
+
if (!isApproved) {
|
|
145
|
+
onProgress?.(`Approving CTF for ${exchange.name}...`);
|
|
146
|
+
const tx = await ctf.setApprovalForAll(exchange.address, true);
|
|
147
|
+
const receipt = await tx.wait();
|
|
148
|
+
result.ctfTxHash = receipt.hash;
|
|
149
|
+
result.ctfApproved = true;
|
|
150
|
+
} else {
|
|
151
|
+
onProgress?.(`CTF already approved for ${exchange.name}`);
|
|
152
|
+
result.ctfApproved = true;
|
|
153
|
+
}
|
|
154
|
+
results.push(result);
|
|
155
|
+
}
|
|
156
|
+
return results;
|
|
157
|
+
}
|
|
158
|
+
async function checkApprovals(address) {
|
|
159
|
+
const provider = getProvider();
|
|
160
|
+
const usdcE = new ethers.Contract(USDC_E, ERC20_ABI, provider);
|
|
161
|
+
const ctf = new ethers.Contract(CTF_ADDRESS, ERC1155_ABI, provider);
|
|
162
|
+
const details = [];
|
|
163
|
+
for (const exchange of EXCHANGE_CONTRACTS) {
|
|
164
|
+
const allowance = await usdcE.allowance(address, exchange.address);
|
|
165
|
+
const isCtfApproved = await ctf.isApprovedForAll(address, exchange.address);
|
|
166
|
+
details.push({
|
|
167
|
+
contract: exchange.name,
|
|
168
|
+
usdcApproved: allowance > 0n,
|
|
169
|
+
ctfApproved: isCtfApproved
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
const allApproved = details.every((d) => d.usdcApproved && d.ctfApproved);
|
|
173
|
+
return { allApproved, details };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// src/wallet/onepassword.ts
|
|
177
|
+
import { execSync } from "child_process";
|
|
178
|
+
var WALLET_ITEM_PREFIX = "Polymarket Wallet";
|
|
179
|
+
function check1PasswordCli() {
|
|
180
|
+
try {
|
|
181
|
+
execSync("which op", { encoding: "utf8", stdio: "pipe" });
|
|
182
|
+
} catch {
|
|
183
|
+
return {
|
|
184
|
+
available: false,
|
|
185
|
+
authenticated: false,
|
|
186
|
+
error: "1Password CLI not installed. Install with: brew install --cask 1password-cli"
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
try {
|
|
190
|
+
execSync("op account list", { encoding: "utf8", stdio: "pipe" });
|
|
191
|
+
return { available: true, authenticated: true };
|
|
192
|
+
} catch {
|
|
193
|
+
return {
|
|
194
|
+
available: true,
|
|
195
|
+
authenticated: false,
|
|
196
|
+
error: "1Password CLI not authenticated. Enable app integration: Settings \u2192 Developer \u2192 Integrate with 1Password CLI"
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
async function storeIn1Password(name, credentials, vault = "Private") {
|
|
201
|
+
const itemName = `${WALLET_ITEM_PREFIX} - ${name}`;
|
|
202
|
+
try {
|
|
203
|
+
const fields = [
|
|
204
|
+
`address[text]=${credentials.address}`,
|
|
205
|
+
`private_key[password]=${credentials.privateKey}`,
|
|
206
|
+
`api_key[text]=${credentials.apiKey}`,
|
|
207
|
+
`api_secret[password]=${credentials.apiSecret}`,
|
|
208
|
+
`api_passphrase[password]=${credentials.apiPassphrase}`,
|
|
209
|
+
`created_at[text]=${(/* @__PURE__ */ new Date()).toISOString()}`
|
|
210
|
+
];
|
|
211
|
+
const cmd = [
|
|
212
|
+
"op",
|
|
213
|
+
"item",
|
|
214
|
+
"create",
|
|
215
|
+
"--category=Login",
|
|
216
|
+
`--title=${itemName}`,
|
|
217
|
+
`--vault=${vault}`,
|
|
218
|
+
...fields.map((f) => `"${f}"`)
|
|
219
|
+
].join(" ");
|
|
220
|
+
execSync(cmd, { encoding: "utf8", stdio: "pipe" });
|
|
221
|
+
return { itemName, success: true };
|
|
222
|
+
} catch (error) {
|
|
223
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
224
|
+
return { itemName, success: false, error: msg };
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
async function getFrom1Password(name) {
|
|
228
|
+
const itemName = `${WALLET_ITEM_PREFIX} - ${name}`;
|
|
229
|
+
try {
|
|
230
|
+
const output = execSync(
|
|
231
|
+
`op item get "${itemName}" --format json`,
|
|
232
|
+
{ encoding: "utf8", stdio: "pipe" }
|
|
233
|
+
);
|
|
234
|
+
const item = JSON.parse(output);
|
|
235
|
+
const fields = {};
|
|
236
|
+
for (const field of item.fields || []) {
|
|
237
|
+
if (field.label && field.value) {
|
|
238
|
+
fields[field.label] = field.value;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (!fields.address || !fields.private_key) {
|
|
242
|
+
return { error: `Wallet "${itemName}" is missing required fields` };
|
|
243
|
+
}
|
|
244
|
+
return {
|
|
245
|
+
credentials: {
|
|
246
|
+
address: fields.address,
|
|
247
|
+
privateKey: fields.private_key,
|
|
248
|
+
apiKey: fields.api_key || "",
|
|
249
|
+
apiSecret: fields.api_secret || "",
|
|
250
|
+
apiPassphrase: fields.api_passphrase || ""
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
} catch (error) {
|
|
254
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
255
|
+
if (msg.includes("isn't an item")) {
|
|
256
|
+
return { error: `Wallet "${itemName}" not found in 1Password` };
|
|
257
|
+
}
|
|
258
|
+
return { error: msg };
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
function listWallets(vault) {
|
|
262
|
+
try {
|
|
263
|
+
const vaultArg = vault ? `--vault="${vault}"` : "";
|
|
264
|
+
const output = execSync(
|
|
265
|
+
`op item list --format json ${vaultArg}`,
|
|
266
|
+
{ encoding: "utf8", stdio: "pipe" }
|
|
267
|
+
);
|
|
268
|
+
const items = JSON.parse(output);
|
|
269
|
+
const wallets = [];
|
|
270
|
+
for (const item of items) {
|
|
271
|
+
if (item.title?.startsWith(WALLET_ITEM_PREFIX)) {
|
|
272
|
+
const name = item.title.replace(`${WALLET_ITEM_PREFIX} - `, "");
|
|
273
|
+
wallets.push(name);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return wallets;
|
|
277
|
+
} catch {
|
|
278
|
+
return [];
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
function getWalletAddress(name) {
|
|
282
|
+
const itemName = `${WALLET_ITEM_PREFIX} - ${name}`;
|
|
283
|
+
try {
|
|
284
|
+
const output = execSync(
|
|
285
|
+
`op item get "${itemName}" --fields address --reveal`,
|
|
286
|
+
{ encoding: "utf8", stdio: "pipe" }
|
|
287
|
+
);
|
|
288
|
+
return output.trim();
|
|
289
|
+
} catch {
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// src/index.ts
|
|
10
295
|
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
11
296
|
var TEMPLATE_DIR = path.resolve(__dirname, "..", "templates", "worker");
|
|
12
297
|
function toKebabCase(str) {
|
|
@@ -22,7 +307,10 @@ function toCamelCase(str) {
|
|
|
22
307
|
async function replaceInFile(filePath, replacements) {
|
|
23
308
|
let content = await fs.readFile(filePath, "utf-8");
|
|
24
309
|
for (const [key, value] of Object.entries(replacements)) {
|
|
25
|
-
content = content.replace(
|
|
310
|
+
content = content.replace(
|
|
311
|
+
new RegExp(key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g"),
|
|
312
|
+
value
|
|
313
|
+
);
|
|
26
314
|
}
|
|
27
315
|
await fs.writeFile(filePath, content);
|
|
28
316
|
}
|
|
@@ -59,7 +347,7 @@ async function renameFiles(dir, replacements) {
|
|
|
59
347
|
}
|
|
60
348
|
}
|
|
61
349
|
}
|
|
62
|
-
async function
|
|
350
|
+
async function createStrategy(name, options) {
|
|
63
351
|
const targetDir = path.resolve(process.cwd(), name);
|
|
64
352
|
const strategyName = toKebabCase(name);
|
|
65
353
|
const strategyClassName = toPascalCase(name);
|
|
@@ -92,21 +380,25 @@ async function create(name, options) {
|
|
|
92
380
|
if (options.install) {
|
|
93
381
|
console.log(` ${pc.green("\u2713")} Installing dependencies...`);
|
|
94
382
|
try {
|
|
95
|
-
|
|
383
|
+
execSync2("pnpm install", { cwd: targetDir, stdio: "pipe" });
|
|
96
384
|
} catch {
|
|
97
385
|
try {
|
|
98
|
-
|
|
99
|
-
} catch
|
|
100
|
-
console.log(
|
|
101
|
-
|
|
386
|
+
execSync2("npm install", { cwd: targetDir, stdio: "pipe" });
|
|
387
|
+
} catch {
|
|
388
|
+
console.log(
|
|
389
|
+
pc.yellow(" Could not install dependencies automatically.")
|
|
390
|
+
);
|
|
391
|
+
console.log(
|
|
392
|
+
pc.yellow(" Run 'npm install' or 'pnpm install' manually.")
|
|
393
|
+
);
|
|
102
394
|
}
|
|
103
395
|
}
|
|
104
396
|
}
|
|
105
397
|
if (options.git) {
|
|
106
398
|
console.log(` ${pc.green("\u2713")} Initializing git repository...`);
|
|
107
399
|
try {
|
|
108
|
-
|
|
109
|
-
|
|
400
|
+
execSync2("git init", { cwd: targetDir, stdio: "pipe" });
|
|
401
|
+
execSync2("git add -A", { cwd: targetDir, stdio: "pipe" });
|
|
110
402
|
} catch {
|
|
111
403
|
console.log(pc.yellow(" Could not initialize git repository."));
|
|
112
404
|
}
|
|
@@ -122,6 +414,301 @@ async function create(name, options) {
|
|
|
122
414
|
console.log(pc.cyan(" wrangler deploy"));
|
|
123
415
|
console.log();
|
|
124
416
|
}
|
|
125
|
-
|
|
417
|
+
async function walletCreate(options) {
|
|
418
|
+
console.log();
|
|
419
|
+
console.log(pc.cyan("Creating new Polymarket wallet..."));
|
|
420
|
+
console.log();
|
|
421
|
+
if (options.store1password) {
|
|
422
|
+
const opStatus = check1PasswordCli();
|
|
423
|
+
if (!opStatus.available || !opStatus.authenticated) {
|
|
424
|
+
console.error(pc.red(`Error: ${opStatus.error}`));
|
|
425
|
+
process.exit(1);
|
|
426
|
+
}
|
|
427
|
+
console.log(` ${pc.green("\u2713")} 1Password CLI authenticated`);
|
|
428
|
+
}
|
|
429
|
+
console.log(` ${pc.yellow("\u2192")} Generating Ethereum wallet...`);
|
|
430
|
+
const wallet = generateWallet();
|
|
431
|
+
console.log(` ${pc.green("\u2713")} Address: ${pc.bold(wallet.address)}`);
|
|
432
|
+
console.log(` ${pc.yellow("\u2192")} Deriving Polymarket API credentials...`);
|
|
433
|
+
try {
|
|
434
|
+
const apiCreds = await deriveApiCredentials(wallet.privateKey);
|
|
435
|
+
console.log(` ${pc.green("\u2713")} API credentials derived`);
|
|
436
|
+
const fullCreds = { ...wallet, ...apiCreds };
|
|
437
|
+
if (options.store1password && options.name) {
|
|
438
|
+
console.log(` ${pc.yellow("\u2192")} Storing in 1Password...`);
|
|
439
|
+
const result = await storeIn1Password(
|
|
440
|
+
options.name,
|
|
441
|
+
fullCreds,
|
|
442
|
+
options.vault || "Private"
|
|
443
|
+
);
|
|
444
|
+
if (result.success) {
|
|
445
|
+
console.log(` ${pc.green("\u2713")} Stored as "${result.itemName}"`);
|
|
446
|
+
} else {
|
|
447
|
+
console.error(pc.red(` \u2717 Failed to store: ${result.error}`));
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
console.log();
|
|
451
|
+
console.log(pc.cyan("\u2550".repeat(60)));
|
|
452
|
+
if (options.store1password && options.name) {
|
|
453
|
+
console.log(pc.bold("WALLET CREATED & STORED IN 1PASSWORD"));
|
|
454
|
+
} else {
|
|
455
|
+
console.log(pc.bold("SAVE THESE CREDENTIALS SECURELY"));
|
|
456
|
+
}
|
|
457
|
+
console.log(pc.cyan("\u2550".repeat(60)));
|
|
458
|
+
console.log();
|
|
459
|
+
console.log(`${pc.bold("Address:")} ${wallet.address}`);
|
|
460
|
+
if (!options.store1password) {
|
|
461
|
+
console.log(`${pc.bold("Private Key:")} ${wallet.privateKey}`);
|
|
462
|
+
console.log(`${pc.bold("API Key:")} ${apiCreds.apiKey}`);
|
|
463
|
+
console.log(`${pc.bold("API Secret:")} ${apiCreds.apiSecret}`);
|
|
464
|
+
console.log(`${pc.bold("API Passphrase:")} ${apiCreds.apiPassphrase}`);
|
|
465
|
+
} else {
|
|
466
|
+
console.log(pc.dim(" (credentials stored securely in 1Password)"));
|
|
467
|
+
}
|
|
468
|
+
console.log();
|
|
469
|
+
console.log(pc.cyan("\u2550".repeat(60)));
|
|
470
|
+
console.log();
|
|
471
|
+
console.log(pc.bold("Next steps:"));
|
|
472
|
+
console.log();
|
|
473
|
+
console.log(
|
|
474
|
+
` 1. ${pc.yellow("Fund this wallet on Polygon network:")}`
|
|
475
|
+
);
|
|
476
|
+
console.log(` - Send USDC to: ${wallet.address}`);
|
|
477
|
+
console.log(` - Send ~1 POL for gas fees`);
|
|
478
|
+
console.log();
|
|
479
|
+
console.log(` 2. ${pc.yellow("Run on-chain setup:")}`);
|
|
480
|
+
if (options.store1password && options.name) {
|
|
481
|
+
console.log(
|
|
482
|
+
` npx create-polymarket-strategy wallet setup --from-1password ${options.name}`
|
|
483
|
+
);
|
|
484
|
+
} else {
|
|
485
|
+
console.log(
|
|
486
|
+
` npx create-polymarket-strategy wallet setup --private-key <key>`
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
console.log();
|
|
490
|
+
console.log(` 3. ${pc.yellow("Add secrets to your worker:")}`);
|
|
491
|
+
console.log(` wrangler secret put WALLET_PRIVATE_KEY`);
|
|
492
|
+
console.log();
|
|
493
|
+
if (options.store1password && options.name) {
|
|
494
|
+
console.log(pc.dim(`To retrieve later:`));
|
|
495
|
+
console.log(pc.dim(` npx create-polymarket-strategy wallet get ${options.name}`));
|
|
496
|
+
console.log();
|
|
497
|
+
}
|
|
498
|
+
} catch (error) {
|
|
499
|
+
console.log(` ${pc.red("\u2717")} Failed to derive API credentials`);
|
|
500
|
+
console.log(
|
|
501
|
+
pc.yellow(
|
|
502
|
+
" This may be a network issue. You can derive credentials later."
|
|
503
|
+
)
|
|
504
|
+
);
|
|
505
|
+
console.log();
|
|
506
|
+
console.log(pc.cyan("\u2550".repeat(60)));
|
|
507
|
+
console.log(pc.bold("WALLET CREATED (API credentials pending)"));
|
|
508
|
+
console.log(pc.cyan("\u2550".repeat(60)));
|
|
509
|
+
console.log();
|
|
510
|
+
console.log(`${pc.bold("Address:")} ${wallet.address}`);
|
|
511
|
+
console.log(`${pc.bold("Private Key:")} ${wallet.privateKey}`);
|
|
512
|
+
console.log();
|
|
513
|
+
console.log(
|
|
514
|
+
pc.yellow(
|
|
515
|
+
"Fund the wallet and re-run to derive API credentials."
|
|
516
|
+
)
|
|
517
|
+
);
|
|
518
|
+
console.log();
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
async function walletBalance(address) {
|
|
522
|
+
console.log();
|
|
523
|
+
console.log(pc.cyan(`Checking balances for ${address}...`));
|
|
524
|
+
console.log();
|
|
525
|
+
try {
|
|
526
|
+
const balances = await getBalances(address);
|
|
527
|
+
console.log(pc.cyan("\u2550".repeat(50)));
|
|
528
|
+
console.log(pc.bold("Polymarket Wallet Balance"));
|
|
529
|
+
console.log(pc.cyan("\u2550".repeat(50)));
|
|
530
|
+
console.log();
|
|
531
|
+
console.log(
|
|
532
|
+
` ${pc.bold("POL:")} ${balances.pol.toFixed(4)} (~$${(balances.pol * 0.4).toFixed(2)})`
|
|
533
|
+
);
|
|
534
|
+
console.log(` ${pc.bold("USDC:")} $${balances.usdcNative.toFixed(2)}`);
|
|
535
|
+
console.log(
|
|
536
|
+
` ${pc.bold("USDC.e:")} $${balances.usdcE.toFixed(2)} ${pc.dim("(Polymarket uses this)")}`
|
|
537
|
+
);
|
|
538
|
+
console.log();
|
|
539
|
+
console.log(` ${pc.bold("Total USDC:")} $${balances.totalUsdc.toFixed(2)}`);
|
|
540
|
+
console.log();
|
|
541
|
+
console.log(pc.cyan("\u2550".repeat(50)));
|
|
542
|
+
console.log(`View: https://polygonscan.com/address/${address}`);
|
|
543
|
+
console.log();
|
|
544
|
+
const approvals = await checkApprovals(address);
|
|
545
|
+
if (approvals.allApproved) {
|
|
546
|
+
console.log(pc.green("\u2713 All exchange contracts approved"));
|
|
547
|
+
} else {
|
|
548
|
+
console.log(pc.yellow("\u26A0 Some contracts not approved:"));
|
|
549
|
+
for (const detail of approvals.details) {
|
|
550
|
+
const status = detail.usdcApproved && detail.ctfApproved ? pc.green("\u2713") : pc.red("\u2717");
|
|
551
|
+
console.log(` ${status} ${detail.contract}`);
|
|
552
|
+
}
|
|
553
|
+
console.log();
|
|
554
|
+
console.log(
|
|
555
|
+
pc.dim("Run 'npx create-polymarket-strategy wallet setup' to approve")
|
|
556
|
+
);
|
|
557
|
+
}
|
|
558
|
+
console.log();
|
|
559
|
+
} catch (error) {
|
|
560
|
+
console.error(pc.red(`Error: ${error}`));
|
|
561
|
+
process.exit(1);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
async function walletGet(name) {
|
|
565
|
+
console.log();
|
|
566
|
+
console.log(pc.cyan(`Retrieving wallet "${name}" from 1Password...`));
|
|
567
|
+
console.log();
|
|
568
|
+
const opStatus = check1PasswordCli();
|
|
569
|
+
if (!opStatus.available || !opStatus.authenticated) {
|
|
570
|
+
console.error(pc.red(`Error: ${opStatus.error}`));
|
|
571
|
+
process.exit(1);
|
|
572
|
+
}
|
|
573
|
+
const result = await getFrom1Password(name);
|
|
574
|
+
if (result.error) {
|
|
575
|
+
console.error(pc.red(`Error: ${result.error}`));
|
|
576
|
+
process.exit(1);
|
|
577
|
+
}
|
|
578
|
+
const creds = result.credentials;
|
|
579
|
+
console.log(pc.cyan("\u2550".repeat(60)));
|
|
580
|
+
console.log(pc.bold(`${WALLET_ITEM_PREFIX} - ${name}`));
|
|
581
|
+
console.log(pc.cyan("\u2550".repeat(60)));
|
|
582
|
+
console.log();
|
|
583
|
+
console.log(`${pc.bold("Address:")} ${creds.address}`);
|
|
584
|
+
console.log(`${pc.bold("Private Key:")} ${creds.privateKey}`);
|
|
585
|
+
if (creds.apiKey) {
|
|
586
|
+
console.log(`${pc.bold("API Key:")} ${creds.apiKey}`);
|
|
587
|
+
console.log(`${pc.bold("API Secret:")} ${creds.apiSecret}`);
|
|
588
|
+
console.log(`${pc.bold("API Passphrase:")} ${creds.apiPassphrase}`);
|
|
589
|
+
}
|
|
590
|
+
console.log();
|
|
591
|
+
}
|
|
592
|
+
async function walletList() {
|
|
593
|
+
console.log();
|
|
594
|
+
console.log(pc.cyan("Listing Polymarket wallets in 1Password..."));
|
|
595
|
+
console.log();
|
|
596
|
+
const opStatus = check1PasswordCli();
|
|
597
|
+
if (!opStatus.available || !opStatus.authenticated) {
|
|
598
|
+
console.error(pc.red(`Error: ${opStatus.error}`));
|
|
599
|
+
process.exit(1);
|
|
600
|
+
}
|
|
601
|
+
const wallets = listWallets();
|
|
602
|
+
if (wallets.length === 0) {
|
|
603
|
+
console.log(pc.yellow("No Polymarket wallets found in 1Password."));
|
|
604
|
+
console.log();
|
|
605
|
+
console.log(pc.dim("Create one with:"));
|
|
606
|
+
console.log(pc.dim(" npx create-polymarket-strategy wallet create --name my-trader --store-1password"));
|
|
607
|
+
console.log();
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
console.log(pc.bold(`Found ${wallets.length} wallet(s):`));
|
|
611
|
+
console.log();
|
|
612
|
+
for (const name of wallets) {
|
|
613
|
+
const address = getWalletAddress(name);
|
|
614
|
+
console.log(` ${pc.cyan(name)}`);
|
|
615
|
+
console.log(` ${pc.dim(address || "address unavailable")}`);
|
|
616
|
+
}
|
|
617
|
+
console.log();
|
|
618
|
+
}
|
|
619
|
+
async function walletSetup(options) {
|
|
620
|
+
let privateKey = options.privateKey;
|
|
621
|
+
if (options.from1password) {
|
|
622
|
+
const opStatus = check1PasswordCli();
|
|
623
|
+
if (!opStatus.available || !opStatus.authenticated) {
|
|
624
|
+
console.error(pc.red(`Error: ${opStatus.error}`));
|
|
625
|
+
process.exit(1);
|
|
626
|
+
}
|
|
627
|
+
const result = await getFrom1Password(options.from1password);
|
|
628
|
+
if (result.error) {
|
|
629
|
+
console.error(pc.red(`Error: ${result.error}`));
|
|
630
|
+
process.exit(1);
|
|
631
|
+
}
|
|
632
|
+
privateKey = result.credentials.privateKey;
|
|
633
|
+
}
|
|
634
|
+
if (!privateKey) {
|
|
635
|
+
console.error(pc.red("Error: Either --private-key or --from-1password is required"));
|
|
636
|
+
process.exit(1);
|
|
637
|
+
}
|
|
638
|
+
console.log();
|
|
639
|
+
console.log(pc.cyan("Setting up wallet for Polymarket trading..."));
|
|
640
|
+
console.log();
|
|
641
|
+
try {
|
|
642
|
+
const { ethers: ethers2 } = await import("ethers");
|
|
643
|
+
const wallet = new ethers2.Wallet(privateKey);
|
|
644
|
+
const address = wallet.address;
|
|
645
|
+
console.log(` ${pc.bold("Address:")} ${address}`);
|
|
646
|
+
console.log();
|
|
647
|
+
console.log(pc.yellow("Checking balances..."));
|
|
648
|
+
const balances = await getBalances(address);
|
|
649
|
+
console.log(` POL: ${balances.pol.toFixed(4)}`);
|
|
650
|
+
console.log(` USDC: $${balances.usdcNative.toFixed(2)}`);
|
|
651
|
+
console.log(` USDC.e: $${balances.usdcE.toFixed(2)}`);
|
|
652
|
+
console.log();
|
|
653
|
+
if (balances.pol < 0.01) {
|
|
654
|
+
console.error(pc.red("Error: Need at least 0.01 POL for gas fees"));
|
|
655
|
+
console.log(`Send POL to: ${address}`);
|
|
656
|
+
process.exit(1);
|
|
657
|
+
}
|
|
658
|
+
if (balances.usdcNative > 1 && balances.usdcE < 1) {
|
|
659
|
+
console.log(
|
|
660
|
+
pc.yellow(
|
|
661
|
+
"Note: You have native USDC but Polymarket requires USDC.e"
|
|
662
|
+
)
|
|
663
|
+
);
|
|
664
|
+
console.log(
|
|
665
|
+
pc.yellow("Use a DEX like Uniswap to swap USDC \u2192 USDC.e first")
|
|
666
|
+
);
|
|
667
|
+
console.log();
|
|
668
|
+
}
|
|
669
|
+
console.log(pc.yellow("Approving Polymarket exchange contracts..."));
|
|
670
|
+
console.log(
|
|
671
|
+
pc.dim("(3 contracts x 2 approvals each = up to 6 transactions)")
|
|
672
|
+
);
|
|
673
|
+
console.log();
|
|
674
|
+
const results = await approveExchangeContracts(privateKey, (msg) => {
|
|
675
|
+
console.log(` ${pc.yellow("\u2192")} ${msg}`);
|
|
676
|
+
});
|
|
677
|
+
console.log();
|
|
678
|
+
let txCount = 0;
|
|
679
|
+
for (const result of results) {
|
|
680
|
+
if (result.usdcTxHash) txCount++;
|
|
681
|
+
if (result.ctfTxHash) txCount++;
|
|
682
|
+
}
|
|
683
|
+
console.log(pc.cyan("\u2550".repeat(50)));
|
|
684
|
+
console.log(pc.bold("Setup Complete"));
|
|
685
|
+
console.log(pc.cyan("\u2550".repeat(50)));
|
|
686
|
+
console.log();
|
|
687
|
+
console.log(` ${pc.green("\u2713")} All contracts approved`);
|
|
688
|
+
console.log(` ${pc.dim(`${txCount} transaction(s) submitted`)}`);
|
|
689
|
+
console.log();
|
|
690
|
+
console.log(` USDC.e balance: $${balances.usdcE.toFixed(2)}`);
|
|
691
|
+
console.log();
|
|
692
|
+
console.log(pc.bold("Your wallet is ready for live trading!"));
|
|
693
|
+
console.log();
|
|
694
|
+
} catch (error) {
|
|
695
|
+
console.error(pc.red(`Error: ${error}`));
|
|
696
|
+
process.exit(1);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
program.name("create-polymarket-strategy").description("Create and manage Polymarket trading strategies").version("0.2.0");
|
|
700
|
+
program.argument("[name]", "Project name").option("--no-git", "Skip git initialization").option("--no-install", "Skip dependency installation").action(async (name, options) => {
|
|
701
|
+
if (name) {
|
|
702
|
+
await createStrategy(name, options);
|
|
703
|
+
} else {
|
|
704
|
+
program.help();
|
|
705
|
+
}
|
|
706
|
+
});
|
|
707
|
+
var walletCmd = program.command("wallet").description("Wallet management commands");
|
|
708
|
+
walletCmd.command("create").description("Create a new wallet and derive API credentials").option("--name <name>", "Trader/strategy name (required for 1Password storage)").option("--store-1password", "Store credentials in 1Password").option("--vault <vault>", "1Password vault name", "Private").action((options) => walletCreate(options));
|
|
709
|
+
walletCmd.command("balance").description("Check wallet balances").argument("<address>", "Wallet address").action(walletBalance);
|
|
710
|
+
walletCmd.command("setup").description("Approve exchange contracts for trading").option("--private-key <key>", "Wallet private key").option("--from-1password <name>", "Get private key from 1Password wallet").action((options) => walletSetup(options));
|
|
711
|
+
walletCmd.command("get").description("Retrieve wallet credentials from 1Password").argument("<name>", "Trader/strategy name").action(walletGet);
|
|
712
|
+
walletCmd.command("list").description("List all Polymarket wallets in 1Password").action(walletList);
|
|
126
713
|
program.parse();
|
|
127
714
|
//# sourceMappingURL=index.js.map
|