@walletconnect/cli-sdk 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 +131 -0
- package/dist/cli.js +680 -0
- package/dist/index.cjs +108 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +146 -0
- package/dist/index.d.ts +146 -0
- package/dist/index.js +108 -0
- package/dist/index.js.map +1 -0
- package/package.json +57 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,680 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/client.ts
|
|
4
|
+
import { EventEmitter } from "events";
|
|
5
|
+
import { execFileSync } from "child_process";
|
|
6
|
+
import { homedir } from "os";
|
|
7
|
+
import { join } from "path";
|
|
8
|
+
import { KeyValueStorage } from "@walletconnect/keyvaluestorage";
|
|
9
|
+
import { SignClient } from "@walletconnect/sign-client";
|
|
10
|
+
|
|
11
|
+
// src/browser-ui/server.ts
|
|
12
|
+
import http from "http";
|
|
13
|
+
var SCRIPT_CLOSE = "</script>";
|
|
14
|
+
function buildHTML(uri) {
|
|
15
|
+
return `<!DOCTYPE html>
|
|
16
|
+
<html lang="en">
|
|
17
|
+
<head>
|
|
18
|
+
<meta charset="UTF-8">
|
|
19
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
20
|
+
<title>WalletConnect \u2014 Connect Wallet</title>
|
|
21
|
+
<style>
|
|
22
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
23
|
+
body {
|
|
24
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
25
|
+
background: #1a1a2e;
|
|
26
|
+
color: #e0e0e0;
|
|
27
|
+
display: flex;
|
|
28
|
+
justify-content: center;
|
|
29
|
+
align-items: center;
|
|
30
|
+
min-height: 100vh;
|
|
31
|
+
}
|
|
32
|
+
.container {
|
|
33
|
+
text-align: center;
|
|
34
|
+
padding: 2rem;
|
|
35
|
+
max-width: 420px;
|
|
36
|
+
}
|
|
37
|
+
h1 { font-size: 1.5rem; margin-bottom: 0.5rem; color: #fff; }
|
|
38
|
+
.subtitle { color: #8888aa; margin-bottom: 2rem; }
|
|
39
|
+
#qr-container {
|
|
40
|
+
background: #fff;
|
|
41
|
+
border-radius: 16px;
|
|
42
|
+
padding: 24px;
|
|
43
|
+
display: inline-block;
|
|
44
|
+
margin-bottom: 1.5rem;
|
|
45
|
+
}
|
|
46
|
+
#qr-container canvas { display: block; }
|
|
47
|
+
.uri-box {
|
|
48
|
+
background: #16213e;
|
|
49
|
+
border: 1px solid #333;
|
|
50
|
+
border-radius: 8px;
|
|
51
|
+
padding: 12px;
|
|
52
|
+
word-break: break-all;
|
|
53
|
+
font-size: 0.75rem;
|
|
54
|
+
color: #8888aa;
|
|
55
|
+
margin-bottom: 1.5rem;
|
|
56
|
+
cursor: pointer;
|
|
57
|
+
transition: border-color 0.2s;
|
|
58
|
+
}
|
|
59
|
+
.uri-box:hover { border-color: #5566ff; }
|
|
60
|
+
.uri-box:active { border-color: #3344dd; }
|
|
61
|
+
#status {
|
|
62
|
+
font-size: 0.9rem;
|
|
63
|
+
padding: 8px 16px;
|
|
64
|
+
border-radius: 8px;
|
|
65
|
+
display: inline-block;
|
|
66
|
+
}
|
|
67
|
+
.status-waiting { background: #16213e; color: #8888aa; }
|
|
68
|
+
.status-connected { background: #0a3d2a; color: #4ade80; }
|
|
69
|
+
.status-error { background: #3d0a0a; color: #f87171; }
|
|
70
|
+
</style>
|
|
71
|
+
</head>
|
|
72
|
+
<body>
|
|
73
|
+
<div class="container">
|
|
74
|
+
<h1>Connect Your Wallet</h1>
|
|
75
|
+
<p class="subtitle">Scan the QR code with your mobile wallet</p>
|
|
76
|
+
<div id="qr-container"><canvas id="qr"></canvas></div>
|
|
77
|
+
<div class="uri-box" onclick="navigator.clipboard.writeText('${uri}').then(()=>this.textContent='Copied!').catch(()=>{})">
|
|
78
|
+
${uri}
|
|
79
|
+
</div>
|
|
80
|
+
<div id="status" class="status-waiting">Waiting for connection...</div>
|
|
81
|
+
</div>
|
|
82
|
+
<script src="https://cdn.jsdelivr.net/npm/qrcode@1/build/qrcode.min.js">${SCRIPT_CLOSE}
|
|
83
|
+
<script>
|
|
84
|
+
QRCode.toCanvas(document.getElementById("qr"), "${uri}", {
|
|
85
|
+
width: 280,
|
|
86
|
+
margin: 0,
|
|
87
|
+
color: { dark: "#1a1a2e", light: "#ffffff" }
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const evtSource = new EventSource("/events");
|
|
91
|
+
evtSource.onmessage = function(event) {
|
|
92
|
+
const data = JSON.parse(event.data);
|
|
93
|
+
const el = document.getElementById("status");
|
|
94
|
+
el.className = "status-" + data.status;
|
|
95
|
+
el.textContent = data.message || data.status;
|
|
96
|
+
if (data.status === "connected") {
|
|
97
|
+
evtSource.close();
|
|
98
|
+
setTimeout(() => window.close(), 2000);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
${SCRIPT_CLOSE}
|
|
102
|
+
</body>
|
|
103
|
+
</html>`;
|
|
104
|
+
}
|
|
105
|
+
function createBrowserUI(preferredPort) {
|
|
106
|
+
let server = null;
|
|
107
|
+
const sseClients = [];
|
|
108
|
+
return {
|
|
109
|
+
async start(uri) {
|
|
110
|
+
const html = buildHTML(uri);
|
|
111
|
+
server = http.createServer((req, res) => {
|
|
112
|
+
if (req.url === "/events") {
|
|
113
|
+
res.writeHead(200, {
|
|
114
|
+
"Content-Type": "text/event-stream",
|
|
115
|
+
"Cache-Control": "no-cache",
|
|
116
|
+
Connection: "keep-alive"
|
|
117
|
+
});
|
|
118
|
+
res.write(`data: ${JSON.stringify({ status: "waiting", message: "Waiting for connection..." })}
|
|
119
|
+
|
|
120
|
+
`);
|
|
121
|
+
const client = { res };
|
|
122
|
+
sseClients.push(client);
|
|
123
|
+
req.on("close", () => {
|
|
124
|
+
const idx = sseClients.indexOf(client);
|
|
125
|
+
if (idx !== -1) sseClients.splice(idx, 1);
|
|
126
|
+
});
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
130
|
+
res.end(html);
|
|
131
|
+
});
|
|
132
|
+
const port = await new Promise((resolve, reject) => {
|
|
133
|
+
server.listen(preferredPort || 0, "127.0.0.1", () => {
|
|
134
|
+
const addr = server.address();
|
|
135
|
+
resolve(addr.port);
|
|
136
|
+
});
|
|
137
|
+
server.on("error", reject);
|
|
138
|
+
});
|
|
139
|
+
const url = `http://127.0.0.1:${port}`;
|
|
140
|
+
try {
|
|
141
|
+
const { default: open } = await import("open");
|
|
142
|
+
await open(url);
|
|
143
|
+
} catch {
|
|
144
|
+
console.log(`Open your browser at: ${url}`);
|
|
145
|
+
}
|
|
146
|
+
return { port, url };
|
|
147
|
+
},
|
|
148
|
+
updateStatus(status, message) {
|
|
149
|
+
const payload = JSON.stringify({
|
|
150
|
+
status,
|
|
151
|
+
message: message || status
|
|
152
|
+
});
|
|
153
|
+
for (const client of sseClients) {
|
|
154
|
+
try {
|
|
155
|
+
client.res.write(`data: ${payload}
|
|
156
|
+
|
|
157
|
+
`);
|
|
158
|
+
} catch {
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
async stop() {
|
|
163
|
+
for (const client of sseClients) {
|
|
164
|
+
try {
|
|
165
|
+
client.res.end();
|
|
166
|
+
} catch {
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
sseClients.length = 0;
|
|
170
|
+
if (server) {
|
|
171
|
+
await new Promise((resolve) => {
|
|
172
|
+
server.close(() => resolve());
|
|
173
|
+
});
|
|
174
|
+
server = null;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// src/session.ts
|
|
181
|
+
function createSessionManager() {
|
|
182
|
+
return {
|
|
183
|
+
getExistingSession(client) {
|
|
184
|
+
const sessions = client.session.getAll();
|
|
185
|
+
if (sessions.length === 0) return null;
|
|
186
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
187
|
+
const valid = sessions.filter((s) => s.expiry > now).sort((a, b) => b.expiry - a.expiry);
|
|
188
|
+
return valid.length > 0 ? valid[0] : null;
|
|
189
|
+
},
|
|
190
|
+
isSessionValid(session) {
|
|
191
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
192
|
+
return session.expiry > now;
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// src/terminal-ui.ts
|
|
198
|
+
import qrcodeModule from "qrcode-terminal";
|
|
199
|
+
function createTerminalUI() {
|
|
200
|
+
return {
|
|
201
|
+
displayQR(uri) {
|
|
202
|
+
console.log("\nScan this QR code with your wallet app:\n");
|
|
203
|
+
qrcodeModule.generate(uri, { small: true });
|
|
204
|
+
console.log("\nOr copy this URI:", uri);
|
|
205
|
+
console.log("\nWaiting for wallet connection...\n");
|
|
206
|
+
},
|
|
207
|
+
showStatus(message) {
|
|
208
|
+
console.log(`[WalletConnect] ${message}`);
|
|
209
|
+
},
|
|
210
|
+
showError(message) {
|
|
211
|
+
console.error(`[WalletConnect] Error: ${message}`);
|
|
212
|
+
},
|
|
213
|
+
showSuccess(message) {
|
|
214
|
+
console.log(`[WalletConnect] ${message}`);
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// src/client.ts
|
|
220
|
+
var DEFAULT_METHODS = [
|
|
221
|
+
"eth_sendTransaction",
|
|
222
|
+
"eth_signTransaction",
|
|
223
|
+
"personal_sign",
|
|
224
|
+
"eth_sign",
|
|
225
|
+
"eth_signTypedData",
|
|
226
|
+
"eth_signTypedData_v4"
|
|
227
|
+
];
|
|
228
|
+
var DEFAULT_EVENTS = ["chainChanged", "accountsChanged"];
|
|
229
|
+
var DEFAULT_CHAINS = ["eip155:1"];
|
|
230
|
+
var DEFAULT_STORAGE_PATH = join(homedir(), ".walletconnect-cli");
|
|
231
|
+
var WalletConnectCLI = class extends EventEmitter {
|
|
232
|
+
constructor(options) {
|
|
233
|
+
super();
|
|
234
|
+
this.signClient = null;
|
|
235
|
+
this.currentSession = null;
|
|
236
|
+
this.browserUI = null;
|
|
237
|
+
this.sessionManager = createSessionManager();
|
|
238
|
+
this.options = options;
|
|
239
|
+
}
|
|
240
|
+
// ---------- Public API ----------------------------------------------- //
|
|
241
|
+
async tryRestore() {
|
|
242
|
+
const client = await this.ensureClient();
|
|
243
|
+
const existing = this.sessionManager.getExistingSession(client);
|
|
244
|
+
if (!existing) return null;
|
|
245
|
+
this.currentSession = existing;
|
|
246
|
+
return this.buildConnectResult(existing);
|
|
247
|
+
}
|
|
248
|
+
async connect(connectOptions) {
|
|
249
|
+
const client = await this.ensureClient();
|
|
250
|
+
if (this.options.autoConnect !== false) {
|
|
251
|
+
const existing = this.sessionManager.getExistingSession(client);
|
|
252
|
+
if (existing) {
|
|
253
|
+
this.currentSession = existing;
|
|
254
|
+
const result2 = this.buildConnectResult(existing);
|
|
255
|
+
this.emit("connect", result2);
|
|
256
|
+
return result2;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
const chains = this.options.chains || DEFAULT_CHAINS;
|
|
260
|
+
const methods = this.options.methods || DEFAULT_METHODS;
|
|
261
|
+
const events = this.options.events || DEFAULT_EVENTS;
|
|
262
|
+
const optionalNamespaces = connectOptions?.optionalNamespaces || {
|
|
263
|
+
eip155: { chains, methods, events }
|
|
264
|
+
};
|
|
265
|
+
const { uri, approval } = await client.connect({ optionalNamespaces });
|
|
266
|
+
if (!uri) {
|
|
267
|
+
throw new Error("Failed to generate WalletConnect URI");
|
|
268
|
+
}
|
|
269
|
+
if (this.options.ui === "browser") {
|
|
270
|
+
await this.showBrowserUI(uri);
|
|
271
|
+
} else {
|
|
272
|
+
const terminalUI = createTerminalUI();
|
|
273
|
+
terminalUI.displayQR(uri);
|
|
274
|
+
}
|
|
275
|
+
const session = await approval();
|
|
276
|
+
this.currentSession = session;
|
|
277
|
+
if (this.browserUI) {
|
|
278
|
+
this.browserUI.updateStatus("connected", `Connected to ${session.peer.metadata.name}`);
|
|
279
|
+
setTimeout(() => this.browserUI?.stop(), 3e3);
|
|
280
|
+
}
|
|
281
|
+
const result = this.buildConnectResult(session);
|
|
282
|
+
this.emit("connect", result);
|
|
283
|
+
return result;
|
|
284
|
+
}
|
|
285
|
+
async request(options) {
|
|
286
|
+
const client = await this.ensureClient();
|
|
287
|
+
const topic = options.topic || this.currentSession?.topic;
|
|
288
|
+
if (!topic) {
|
|
289
|
+
throw new Error("No active session. Call connect() first.");
|
|
290
|
+
}
|
|
291
|
+
this.logRequestDetails(options);
|
|
292
|
+
try {
|
|
293
|
+
return await client.request({
|
|
294
|
+
topic,
|
|
295
|
+
chainId: options.chainId,
|
|
296
|
+
request: options.request
|
|
297
|
+
});
|
|
298
|
+
} catch (error) {
|
|
299
|
+
const message = this.extractErrorMessage(error);
|
|
300
|
+
if (message.includes("rejected") || message.includes("denied") || message.includes("cancelled")) {
|
|
301
|
+
throw new Error("Request rejected by user");
|
|
302
|
+
}
|
|
303
|
+
if (typeof error === "object" && error !== null) {
|
|
304
|
+
const errObj = error;
|
|
305
|
+
if (errObj.code === 0 && !errObj.message) {
|
|
306
|
+
throw new Error("Request rejected or timed out in wallet");
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
throw error;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
async disconnect() {
|
|
313
|
+
if (!this.currentSession) return;
|
|
314
|
+
try {
|
|
315
|
+
const client = await this.ensureClient();
|
|
316
|
+
const swallow = () => {
|
|
317
|
+
};
|
|
318
|
+
client.core.relayer.on("error", swallow);
|
|
319
|
+
try {
|
|
320
|
+
await client.disconnect({
|
|
321
|
+
topic: this.currentSession.topic,
|
|
322
|
+
reason: { code: 6e3, message: "User disconnected" }
|
|
323
|
+
});
|
|
324
|
+
} finally {
|
|
325
|
+
client.core.relayer.off("error", swallow);
|
|
326
|
+
}
|
|
327
|
+
} catch {
|
|
328
|
+
}
|
|
329
|
+
this.currentSession = null;
|
|
330
|
+
this.emit("disconnect");
|
|
331
|
+
}
|
|
332
|
+
isConnected() {
|
|
333
|
+
if (!this.currentSession) return false;
|
|
334
|
+
return this.sessionManager.isSessionValid(this.currentSession);
|
|
335
|
+
}
|
|
336
|
+
getAccounts() {
|
|
337
|
+
if (!this.currentSession) return [];
|
|
338
|
+
return Object.values(this.currentSession.namespaces).flatMap((ns) => ns.accounts || []);
|
|
339
|
+
}
|
|
340
|
+
getSession() {
|
|
341
|
+
return this.currentSession;
|
|
342
|
+
}
|
|
343
|
+
async destroy() {
|
|
344
|
+
if (this.browserUI) {
|
|
345
|
+
await this.browserUI.stop();
|
|
346
|
+
this.browserUI = null;
|
|
347
|
+
}
|
|
348
|
+
this.removeAllListeners();
|
|
349
|
+
if (this.signClient) {
|
|
350
|
+
try {
|
|
351
|
+
await this.signClient.core.relayer.transportClose();
|
|
352
|
+
} catch {
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
this.signClient = null;
|
|
356
|
+
this.currentSession = null;
|
|
357
|
+
}
|
|
358
|
+
// Type-safe event emitter overrides
|
|
359
|
+
on(event, listener) {
|
|
360
|
+
return super.on(event, listener);
|
|
361
|
+
}
|
|
362
|
+
once(event, listener) {
|
|
363
|
+
return super.once(event, listener);
|
|
364
|
+
}
|
|
365
|
+
off(event, listener) {
|
|
366
|
+
return super.off(event, listener);
|
|
367
|
+
}
|
|
368
|
+
emit(event, ...args) {
|
|
369
|
+
return super.emit(event, ...args);
|
|
370
|
+
}
|
|
371
|
+
// ---------- Private -------------------------------------------------- //
|
|
372
|
+
logRequestDetails(options) {
|
|
373
|
+
const walletName = this.currentSession?.peer.metadata.name;
|
|
374
|
+
if (walletName) {
|
|
375
|
+
console.log(`
|
|
376
|
+
Requesting approval on ${walletName}...`);
|
|
377
|
+
}
|
|
378
|
+
if (options.request.method === "eth_sendTransaction") {
|
|
379
|
+
const params = options.request.params;
|
|
380
|
+
const data = params[0]?.data;
|
|
381
|
+
if (data && data !== "0x") {
|
|
382
|
+
try {
|
|
383
|
+
const decoded = execFileSync("cast", ["4d", data], {
|
|
384
|
+
encoding: "utf-8",
|
|
385
|
+
timeout: 5e3,
|
|
386
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
387
|
+
}).trim();
|
|
388
|
+
if (decoded) {
|
|
389
|
+
console.log(`
|
|
390
|
+
Decoded calldata:
|
|
391
|
+
${decoded.split("\n").map((l) => ` ${l}`).join("\n")}
|
|
392
|
+
`);
|
|
393
|
+
}
|
|
394
|
+
} catch {
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
async ensureClient() {
|
|
400
|
+
if (this.signClient) return this.signClient;
|
|
401
|
+
const storagePath = this.options.storagePath || DEFAULT_STORAGE_PATH;
|
|
402
|
+
const storage = new KeyValueStorage({ database: storagePath });
|
|
403
|
+
this.signClient = await SignClient.init({
|
|
404
|
+
projectId: this.options.projectId,
|
|
405
|
+
metadata: this.options.metadata,
|
|
406
|
+
storage,
|
|
407
|
+
logger: this.options.logger || "silent"
|
|
408
|
+
});
|
|
409
|
+
this.signClient.on("session_update", ({ topic }) => {
|
|
410
|
+
const session = this.signClient?.session.get(topic);
|
|
411
|
+
if (session) {
|
|
412
|
+
this.currentSession = session;
|
|
413
|
+
this.emit("session_update", session);
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
this.signClient.on("session_delete", ({ topic }) => {
|
|
417
|
+
if (this.currentSession?.topic === topic) {
|
|
418
|
+
this.currentSession = null;
|
|
419
|
+
}
|
|
420
|
+
this.emit("session_delete", { topic });
|
|
421
|
+
this.emit("disconnect");
|
|
422
|
+
});
|
|
423
|
+
return this.signClient;
|
|
424
|
+
}
|
|
425
|
+
async showBrowserUI(uri) {
|
|
426
|
+
this.browserUI = createBrowserUI(this.options.port);
|
|
427
|
+
try {
|
|
428
|
+
const { url } = await this.browserUI.start(uri);
|
|
429
|
+
console.log(`
|
|
430
|
+
Connect your wallet at: ${url}
|
|
431
|
+
`);
|
|
432
|
+
} catch {
|
|
433
|
+
console.log("Could not open browser, falling back to terminal QR code.");
|
|
434
|
+
const terminalUI = createTerminalUI();
|
|
435
|
+
terminalUI.displayQR(uri);
|
|
436
|
+
this.browserUI = null;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
buildConnectResult(session) {
|
|
440
|
+
const accounts = Object.values(session.namespaces).flatMap((ns) => ns.accounts || []);
|
|
441
|
+
return {
|
|
442
|
+
session,
|
|
443
|
+
accounts,
|
|
444
|
+
topic: session.topic
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
extractErrorMessage(error) {
|
|
448
|
+
if (error instanceof Error) return error.message;
|
|
449
|
+
if (typeof error === "object" && error !== null) {
|
|
450
|
+
const errObj = error;
|
|
451
|
+
if (errObj.message) return errObj.message;
|
|
452
|
+
return JSON.stringify(error);
|
|
453
|
+
}
|
|
454
|
+
return String(error);
|
|
455
|
+
}
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
// src/config.ts
|
|
459
|
+
import { homedir as homedir2 } from "os";
|
|
460
|
+
import { join as join2 } from "path";
|
|
461
|
+
import { readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
462
|
+
var CONFIG_DIR = join2(homedir2(), ".walletconnect-cli");
|
|
463
|
+
var CONFIG_FILE = join2(CONFIG_DIR, "config.json");
|
|
464
|
+
function readConfig() {
|
|
465
|
+
try {
|
|
466
|
+
return JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
|
|
467
|
+
} catch {
|
|
468
|
+
return {};
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
function writeConfig(config) {
|
|
472
|
+
mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 });
|
|
473
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
|
|
474
|
+
}
|
|
475
|
+
function getConfigValue(key) {
|
|
476
|
+
return readConfig()[key];
|
|
477
|
+
}
|
|
478
|
+
function setConfigValue(key, value) {
|
|
479
|
+
const config = readConfig();
|
|
480
|
+
config[key] = value;
|
|
481
|
+
writeConfig(config);
|
|
482
|
+
}
|
|
483
|
+
function resolveProjectId() {
|
|
484
|
+
return process.env.WALLETCONNECT_PROJECT_ID || getConfigValue("projectId");
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// src/cli.ts
|
|
488
|
+
var METADATA = {
|
|
489
|
+
name: "walletconnect",
|
|
490
|
+
description: "WalletConnect CLI",
|
|
491
|
+
url: "https://github.com/ARI/walletconnect-cli-sdk",
|
|
492
|
+
icons: []
|
|
493
|
+
};
|
|
494
|
+
function usage() {
|
|
495
|
+
console.log(`Usage: walletconnect <command> [options]
|
|
496
|
+
|
|
497
|
+
Commands:
|
|
498
|
+
connect Connect to a wallet via QR code
|
|
499
|
+
whoami Show current session info
|
|
500
|
+
sign <message> Sign a message with the connected wallet
|
|
501
|
+
disconnect Disconnect the current session
|
|
502
|
+
config set <k> <v> Set a config value (e.g. project-id)
|
|
503
|
+
config get <k> Get a config value
|
|
504
|
+
|
|
505
|
+
Options:
|
|
506
|
+
--browser Use browser UI instead of terminal QR code
|
|
507
|
+
--help Show this help message
|
|
508
|
+
|
|
509
|
+
Config keys:
|
|
510
|
+
project-id WalletConnect Cloud project ID
|
|
511
|
+
|
|
512
|
+
Environment:
|
|
513
|
+
WALLETCONNECT_PROJECT_ID Overrides config project-id when set`);
|
|
514
|
+
}
|
|
515
|
+
function getProjectId() {
|
|
516
|
+
const id = resolveProjectId();
|
|
517
|
+
if (!id) {
|
|
518
|
+
console.error("Error: No project ID found. Set via: walletconnect config set project-id <id>");
|
|
519
|
+
process.exit(1);
|
|
520
|
+
}
|
|
521
|
+
return id;
|
|
522
|
+
}
|
|
523
|
+
function createSDK(options) {
|
|
524
|
+
return new WalletConnectCLI({
|
|
525
|
+
projectId: options.projectId || "",
|
|
526
|
+
metadata: METADATA,
|
|
527
|
+
ui: options.browser ? "browser" : "terminal"
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
function parseAccount(caip10) {
|
|
531
|
+
const lastColon = caip10.lastIndexOf(":");
|
|
532
|
+
return {
|
|
533
|
+
chain: caip10.slice(0, lastColon),
|
|
534
|
+
address: caip10.slice(lastColon + 1)
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
async function cmdConnect(browser) {
|
|
538
|
+
const projectId = getProjectId();
|
|
539
|
+
const sdk = createSDK({ projectId, browser });
|
|
540
|
+
try {
|
|
541
|
+
console.log("Scan this QR code with your wallet app:\n");
|
|
542
|
+
const result = await sdk.connect();
|
|
543
|
+
console.log("\nConnected!");
|
|
544
|
+
for (const account of result.accounts) {
|
|
545
|
+
const { chain, address } = parseAccount(account);
|
|
546
|
+
console.log(` Chain: ${chain}`);
|
|
547
|
+
console.log(` Address: ${address}`);
|
|
548
|
+
}
|
|
549
|
+
} finally {
|
|
550
|
+
await sdk.destroy();
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
async function cmdWhoami() {
|
|
554
|
+
const sdk = createSDK({});
|
|
555
|
+
try {
|
|
556
|
+
const result = await sdk.tryRestore();
|
|
557
|
+
if (!result) {
|
|
558
|
+
console.log("Not connected.");
|
|
559
|
+
process.exit(1);
|
|
560
|
+
}
|
|
561
|
+
const walletName = result.session.peer.metadata.name;
|
|
562
|
+
console.log(` Wallet: ${walletName}`);
|
|
563
|
+
for (const account of result.accounts) {
|
|
564
|
+
const { chain, address } = parseAccount(account);
|
|
565
|
+
console.log(` Chain: ${chain}`);
|
|
566
|
+
console.log(` Address: ${address}`);
|
|
567
|
+
}
|
|
568
|
+
const expiry = new Date(result.session.expiry * 1e3);
|
|
569
|
+
console.log(` Expires: ${expiry.toLocaleString()}`);
|
|
570
|
+
} finally {
|
|
571
|
+
await sdk.destroy();
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
async function cmdSign(message, browser) {
|
|
575
|
+
const projectId = getProjectId();
|
|
576
|
+
const sdk = createSDK({ projectId, browser });
|
|
577
|
+
try {
|
|
578
|
+
let result = await sdk.tryRestore();
|
|
579
|
+
if (!result) {
|
|
580
|
+
console.log("No existing session. Connecting...\n");
|
|
581
|
+
result = await sdk.connect();
|
|
582
|
+
console.log();
|
|
583
|
+
}
|
|
584
|
+
const { chain, address } = parseAccount(result.accounts[0]);
|
|
585
|
+
const hexMessage = "0x" + Buffer.from(message, "utf8").toString("hex");
|
|
586
|
+
const signature = await sdk.request({
|
|
587
|
+
chainId: chain,
|
|
588
|
+
request: {
|
|
589
|
+
method: "personal_sign",
|
|
590
|
+
params: [hexMessage, address]
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
console.log(` Message: ${message}`);
|
|
594
|
+
console.log(` Signature: ${signature}`);
|
|
595
|
+
} finally {
|
|
596
|
+
await sdk.destroy();
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
async function cmdDisconnect() {
|
|
600
|
+
const sdk = createSDK({});
|
|
601
|
+
try {
|
|
602
|
+
const result = await sdk.tryRestore();
|
|
603
|
+
if (!result) {
|
|
604
|
+
console.log("Not connected.");
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
await sdk.disconnect();
|
|
608
|
+
console.log("Disconnected.");
|
|
609
|
+
} finally {
|
|
610
|
+
await sdk.destroy();
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
async function main() {
|
|
614
|
+
const args = process.argv.slice(2);
|
|
615
|
+
const browser = args.includes("--browser");
|
|
616
|
+
const filtered = args.filter((a) => a !== "--browser");
|
|
617
|
+
const command = filtered[0];
|
|
618
|
+
switch (command) {
|
|
619
|
+
case "connect":
|
|
620
|
+
await cmdConnect(browser);
|
|
621
|
+
break;
|
|
622
|
+
case "whoami":
|
|
623
|
+
await cmdWhoami();
|
|
624
|
+
break;
|
|
625
|
+
case "sign": {
|
|
626
|
+
const message = filtered[1];
|
|
627
|
+
if (!message) {
|
|
628
|
+
console.error("Usage: walletconnect sign <message>");
|
|
629
|
+
process.exit(1);
|
|
630
|
+
}
|
|
631
|
+
await cmdSign(message, browser);
|
|
632
|
+
break;
|
|
633
|
+
}
|
|
634
|
+
case "disconnect":
|
|
635
|
+
await cmdDisconnect();
|
|
636
|
+
break;
|
|
637
|
+
case "config": {
|
|
638
|
+
const action = filtered[1];
|
|
639
|
+
const key = filtered[2];
|
|
640
|
+
if (action === "set") {
|
|
641
|
+
const value = filtered[3];
|
|
642
|
+
if (key === "project-id" && value) {
|
|
643
|
+
setConfigValue("projectId", value);
|
|
644
|
+
console.log(`Saved project-id to ~/.walletconnect-cli/config.json`);
|
|
645
|
+
} else {
|
|
646
|
+
console.error("Usage: walletconnect config set project-id <value>");
|
|
647
|
+
process.exit(1);
|
|
648
|
+
}
|
|
649
|
+
} else if (action === "get") {
|
|
650
|
+
if (key === "project-id") {
|
|
651
|
+
const value = getConfigValue("projectId");
|
|
652
|
+
console.log(value || "(not set)");
|
|
653
|
+
} else {
|
|
654
|
+
console.error("Usage: walletconnect config get project-id");
|
|
655
|
+
process.exit(1);
|
|
656
|
+
}
|
|
657
|
+
} else {
|
|
658
|
+
console.error("Usage: walletconnect config <set|get> <key> [value]");
|
|
659
|
+
process.exit(1);
|
|
660
|
+
}
|
|
661
|
+
break;
|
|
662
|
+
}
|
|
663
|
+
case "--help":
|
|
664
|
+
case "-h":
|
|
665
|
+
case void 0:
|
|
666
|
+
usage();
|
|
667
|
+
break;
|
|
668
|
+
default:
|
|
669
|
+
console.error(`Unknown command: ${command}`);
|
|
670
|
+
usage();
|
|
671
|
+
process.exit(1);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
main().then(
|
|
675
|
+
() => process.exit(0),
|
|
676
|
+
(err) => {
|
|
677
|
+
console.error(err instanceof Error ? err.message : err);
|
|
678
|
+
process.exit(1);
|
|
679
|
+
}
|
|
680
|
+
);
|