web3-tools-mcp 2.0.2 → 3.0.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/CHANGELOG.md +97 -0
- package/README.md +5 -0
- package/dist/anvil.d.ts +17 -19
- package/dist/anvil.d.ts.map +1 -1
- package/dist/anvil.js +124 -228
- package/dist/anvil.js.map +1 -1
- package/dist/chains.d.ts +9 -1
- package/dist/chains.d.ts.map +1 -1
- package/dist/chains.js +11 -2
- package/dist/chains.js.map +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js.map +1 -1
- package/dist/context.d.ts +22 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +24 -0
- package/dist/context.js.map +1 -0
- package/dist/hosted.d.ts +12 -0
- package/dist/hosted.d.ts.map +1 -0
- package/dist/hosted.js +12 -0
- package/dist/hosted.js.map +1 -0
- package/dist/http-server.d.ts +13 -0
- package/dist/http-server.d.ts.map +1 -1
- package/dist/http-server.js +13 -4
- package/dist/http-server.js.map +1 -1
- package/dist/index.js +15 -11
- package/dist/index.js.map +1 -1
- package/dist/lib.d.ts +24 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/lib.js +23 -0
- package/dist/lib.js.map +1 -0
- package/dist/mcp-server.d.ts +4 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +43 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/oauth.d.ts +41 -6
- package/dist/oauth.d.ts.map +1 -1
- package/dist/oauth.js +75 -41
- package/dist/oauth.js.map +1 -1
- package/dist/preview.d.ts +7 -0
- package/dist/preview.d.ts.map +1 -1
- package/dist/preview.js +14 -2
- package/dist/preview.js.map +1 -1
- package/dist/signing-requests.d.ts +81 -0
- package/dist/signing-requests.d.ts.map +1 -0
- package/dist/signing-requests.js +138 -0
- package/dist/signing-requests.js.map +1 -0
- package/dist/tools/index.d.ts +27 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +37 -15
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/{advanced.d.ts → read/advanced.d.ts} +30 -26
- package/dist/tools/read/advanced.d.ts.map +1 -0
- package/dist/tools/{advanced.js → read/advanced.js} +10 -10
- package/dist/tools/read/advanced.js.map +1 -0
- package/dist/tools/{balance.d.ts → read/balance.d.ts} +8 -7
- package/dist/tools/read/balance.d.ts.map +1 -0
- package/dist/tools/{balance.js → read/balance.js} +2 -2
- package/dist/tools/read/balance.js.map +1 -0
- package/dist/tools/{contract-info.d.ts → read/contract-info.d.ts} +18 -15
- package/dist/tools/read/contract-info.d.ts.map +1 -0
- package/dist/tools/{contract-info.js → read/contract-info.js} +2 -2
- package/dist/tools/read/contract-info.js.map +1 -0
- package/dist/tools/{contract.d.ts → read/contract.d.ts} +14 -12
- package/dist/tools/read/contract.d.ts.map +1 -0
- package/dist/tools/{contract.js → read/contract.js} +2 -2
- package/dist/tools/read/contract.js.map +1 -0
- package/dist/tools/{ens.d.ts → read/ens.d.ts} +30 -25
- package/dist/tools/read/ens.d.ts.map +1 -0
- package/dist/tools/{ens.js → read/ens.js} +2 -2
- package/dist/tools/read/ens.js.map +1 -0
- package/dist/tools/read/gas.d.ts +171 -0
- package/dist/tools/read/gas.d.ts.map +1 -0
- package/dist/tools/read/gas.js +368 -0
- package/dist/tools/read/gas.js.map +1 -0
- package/dist/tools/{logs.d.ts → read/logs.d.ts} +6 -5
- package/dist/tools/read/logs.d.ts.map +1 -0
- package/dist/tools/{logs.js → read/logs.js} +2 -2
- package/dist/tools/read/logs.js.map +1 -0
- package/dist/tools/{signatures.d.ts → read/signatures.d.ts} +8 -4
- package/dist/tools/read/signatures.d.ts.map +1 -0
- package/dist/tools/{signatures.js → read/signatures.js} +1 -1
- package/dist/tools/read/signatures.js.map +1 -0
- package/dist/tools/read/signing.d.ts +27 -0
- package/dist/tools/read/signing.d.ts.map +1 -0
- package/dist/tools/read/signing.js +75 -0
- package/dist/tools/read/signing.js.map +1 -0
- package/dist/tools/sign/errors.d.ts +9 -0
- package/dist/tools/sign/errors.d.ts.map +1 -0
- package/dist/tools/sign/errors.js +29 -0
- package/dist/tools/sign/errors.js.map +1 -0
- package/dist/tools/{transactions.d.ts → sign/transactions.d.ts} +48 -62
- package/dist/tools/sign/transactions.d.ts.map +1 -0
- package/dist/tools/sign/transactions.js +316 -0
- package/dist/tools/sign/transactions.js.map +1 -0
- package/dist/tools/sign/wallet.d.ts +50 -0
- package/dist/tools/sign/wallet.d.ts.map +1 -0
- package/dist/tools/sign/wallet.js +180 -0
- package/dist/tools/sign/wallet.js.map +1 -0
- package/dist/utils.d.ts +5 -2
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +5 -2
- package/dist/utils.js.map +1 -1
- package/dist/wallet-client.d.ts +33 -4
- package/dist/wallet-client.d.ts.map +1 -1
- package/dist/wallet-client.js +90 -24
- package/dist/wallet-client.js.map +1 -1
- package/dist/walletconnect.d.ts +100 -13
- package/dist/walletconnect.d.ts.map +1 -1
- package/dist/walletconnect.js +215 -50
- package/dist/walletconnect.js.map +1 -1
- package/package.json +10 -4
- package/src/anvil.ts +150 -271
- package/src/chains.ts +14 -2
- package/src/client.ts +0 -1
- package/src/context.ts +27 -0
- package/src/hosted.ts +11 -0
- package/src/http-server.ts +30 -5
- package/src/index.ts +15 -12
- package/src/lib.ts +24 -0
- package/src/mcp-server.ts +47 -0
- package/src/oauth.ts +98 -45
- package/src/preview.ts +21 -2
- package/src/signing-requests.ts +198 -0
- package/src/tools/index.ts +67 -16
- package/src/tools/{advanced.ts → read/advanced.ts} +13 -11
- package/src/tools/{balance.ts → read/balance.ts} +3 -3
- package/src/tools/{contract-info.ts → read/contract-info.ts} +3 -3
- package/src/tools/{contract.ts → read/contract.ts} +3 -3
- package/src/tools/{ens.ts → read/ens.ts} +3 -3
- package/src/tools/read/gas.ts +480 -0
- package/src/tools/{logs.ts → read/logs.ts} +3 -3
- package/src/tools/{signatures.ts → read/signatures.ts} +2 -2
- package/src/tools/read/signing.ts +86 -0
- package/src/tools/sign/errors.ts +28 -0
- package/src/tools/sign/transactions.ts +428 -0
- package/src/tools/sign/wallet.ts +216 -0
- package/src/utils.ts +5 -2
- package/src/wallet-client.ts +91 -22
- package/src/walletconnect.ts +250 -48
- package/dist/tools/advanced.d.ts.map +0 -1
- package/dist/tools/advanced.js.map +0 -1
- package/dist/tools/balance.d.ts.map +0 -1
- package/dist/tools/balance.js.map +0 -1
- package/dist/tools/contract-info.d.ts.map +0 -1
- package/dist/tools/contract-info.js.map +0 -1
- package/dist/tools/contract.d.ts.map +0 -1
- package/dist/tools/contract.js.map +0 -1
- package/dist/tools/ens.d.ts.map +0 -1
- package/dist/tools/ens.js.map +0 -1
- package/dist/tools/gas.d.ts +0 -99
- package/dist/tools/gas.d.ts.map +0 -1
- package/dist/tools/gas.js +0 -181
- package/dist/tools/gas.js.map +0 -1
- package/dist/tools/logs.d.ts.map +0 -1
- package/dist/tools/logs.js.map +0 -1
- package/dist/tools/signatures.d.ts.map +0 -1
- package/dist/tools/signatures.js.map +0 -1
- package/dist/tools/transactions.d.ts.map +0 -1
- package/dist/tools/transactions.js +0 -365
- package/dist/tools/transactions.js.map +0 -1
- package/src/tools/gas.ts +0 -219
- package/src/tools/transactions.ts +0 -456
package/src/wallet-client.ts
CHANGED
|
@@ -3,18 +3,23 @@ import { randomBytes } from 'node:crypto'
|
|
|
3
3
|
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
4
4
|
import { homedir } from 'node:os'
|
|
5
5
|
import { join } from 'node:path'
|
|
6
|
-
import { LOCAL_PORT_ATTEMPTS, type TransactionRequest, type TransactionResponse, WalletRelay } from 'web3-wallet-relay'
|
|
6
|
+
import { LOCAL_PORT_ATTEMPTS, roomToken, type TransactionRequest, type TransactionResponse, WalletRelay } from 'web3-wallet-relay'
|
|
7
7
|
import { WebSocket } from 'ws'
|
|
8
|
+
import { HOSTED } from './hosted.js'
|
|
8
9
|
|
|
9
10
|
const REQUEST_TIMEOUT = 300_000
|
|
10
11
|
|
|
11
|
-
/**
|
|
12
|
-
*
|
|
13
|
-
|
|
12
|
+
/**
|
|
13
|
+
* How long a client with nothing in flight keeps its socket. A hosted server makes one per
|
|
14
|
+
* person who ever signed, and without this each would hold a relay connection for the life
|
|
15
|
+
* of the process. The next call reconnects.
|
|
16
|
+
*/
|
|
17
|
+
const IDLE_TIMEOUT = 10 * 60_000
|
|
14
18
|
|
|
15
|
-
|
|
19
|
+
/** Always the signer token: this URL goes to a browser, which may not act as a requester. */
|
|
20
|
+
function pairingUrlFor(port: number, secret: string, identity: string): string {
|
|
16
21
|
const base = HOSTED ? (process.env.MCP_PUBLIC_URL ?? `http://localhost:${port}`) : `http://127.0.0.1:${port}`
|
|
17
|
-
return `${base.replace(/\/$/, '')}/#t=${
|
|
22
|
+
return `${base.replace(/\/$/, '')}/#t=${roomToken(secret, identity, 'signer')}`
|
|
18
23
|
}
|
|
19
24
|
const SIGNER_WAIT_TIMEOUT = 30_000
|
|
20
25
|
const CONNECT_ATTEMPTS = 8
|
|
@@ -85,13 +90,23 @@ export class WalletClient {
|
|
|
85
90
|
private pageUrl: string | undefined
|
|
86
91
|
private pageAgent: string | undefined
|
|
87
92
|
private lastOpenedAt = 0
|
|
88
|
-
|
|
93
|
+
private idleTimer: NodeJS.Timeout | null = null
|
|
94
|
+
/** Instance fields so tests can shorten the waits instead of sitting out the real ones. */
|
|
89
95
|
private signerWaitTimeout = SIGNER_WAIT_TIMEOUT
|
|
96
|
+
private idleTimeout = IDLE_TIMEOUT
|
|
90
97
|
|
|
91
98
|
private readonly remoteUrl = process.env.WALLET_SERVER_URL
|
|
92
99
|
private readonly remoteToken = process.env.WALLET_TOKEN
|
|
93
100
|
|
|
94
|
-
|
|
101
|
+
/**
|
|
102
|
+
* `identity` is the room this client works in. Everything below it — the socket's token,
|
|
103
|
+
* the pairing link it hands out — is signed for that room, so two identities on one relay
|
|
104
|
+
* cannot see each other's pages or transactions.
|
|
105
|
+
*/
|
|
106
|
+
constructor(
|
|
107
|
+
private identity: string,
|
|
108
|
+
private relayOptions: { port?: number; token?: string } = {}
|
|
109
|
+
) {}
|
|
95
110
|
|
|
96
111
|
get isRemote(): boolean {
|
|
97
112
|
return Boolean(this.remoteUrl)
|
|
@@ -124,8 +139,8 @@ export class WalletClient {
|
|
|
124
139
|
// minute to come back.
|
|
125
140
|
for (let attempt = 0; attempt < CONNECT_ATTEMPTS; attempt++) {
|
|
126
141
|
try {
|
|
127
|
-
await this.openSocket(url, this.remoteToken)
|
|
128
|
-
this.pairingUrl = `${this.remoteUrl}#t=${this.remoteToken}`
|
|
142
|
+
await this.openSocket(url, roomToken(this.remoteToken, this.identity, 'requester'))
|
|
143
|
+
this.pairingUrl = `${this.remoteUrl}#t=${roomToken(this.remoteToken, this.identity, 'signer')}`
|
|
129
144
|
return
|
|
130
145
|
} catch (error) {
|
|
131
146
|
lastError = error as Error
|
|
@@ -147,23 +162,24 @@ export class WalletClient {
|
|
|
147
162
|
* the fix would be for the higher port to hand over when a lower one answers.
|
|
148
163
|
*/
|
|
149
164
|
private async connectLocal(): Promise<void> {
|
|
150
|
-
const
|
|
165
|
+
const secret = this.relayOptions.token ?? localToken()
|
|
166
|
+
const requester = roomToken(secret, this.identity, 'requester')
|
|
151
167
|
const ports = this.relayOptions.port ? [this.relayOptions.port] : LOCAL_PORTS
|
|
152
168
|
|
|
153
169
|
for (const port of ports) {
|
|
154
170
|
try {
|
|
155
|
-
await this.openSocket(`ws://127.0.0.1:${port}`,
|
|
156
|
-
this.pairingUrl = pairingUrlFor(port,
|
|
171
|
+
await this.openSocket(`ws://127.0.0.1:${port}`, requester)
|
|
172
|
+
this.pairingUrl = pairingUrlFor(port, secret, this.identity)
|
|
157
173
|
return
|
|
158
174
|
} catch {
|
|
159
175
|
// Nothing on this port, or something that isn't our relay — keep looking.
|
|
160
176
|
}
|
|
161
177
|
}
|
|
162
178
|
|
|
163
|
-
this.relay = new WalletRelay({ port: ports[0], token })
|
|
179
|
+
this.relay = new WalletRelay({ port: ports[0], token: secret })
|
|
164
180
|
await this.relay.start()
|
|
165
|
-
await this.openSocket(`ws://127.0.0.1:${this.relay.getPort()}`,
|
|
166
|
-
this.pairingUrl = this.relay.
|
|
181
|
+
await this.openSocket(`ws://127.0.0.1:${this.relay.getPort()}`, requester)
|
|
182
|
+
this.pairingUrl = pairingUrlFor(this.relay.getPort(), secret, this.identity)
|
|
167
183
|
}
|
|
168
184
|
|
|
169
185
|
/**
|
|
@@ -198,6 +214,7 @@ export class WalletClient {
|
|
|
198
214
|
clearTimeout(timer)
|
|
199
215
|
ws.off('message', onHandshake)
|
|
200
216
|
this.ws = ws
|
|
217
|
+
this.rearmIdle()
|
|
201
218
|
resolve()
|
|
202
219
|
}
|
|
203
220
|
|
|
@@ -249,11 +266,27 @@ export class WalletClient {
|
|
|
249
266
|
const pending = this.pending.get(message.id)
|
|
250
267
|
if (!pending) return
|
|
251
268
|
this.pending.delete(message.id)
|
|
269
|
+
this.rearmIdle()
|
|
252
270
|
|
|
253
271
|
if (message.success) pending.resolve(message.result)
|
|
254
272
|
else pending.reject(new Error(message.error || 'Transaction failed'))
|
|
255
273
|
}
|
|
256
274
|
|
|
275
|
+
/**
|
|
276
|
+
* Drop the socket, and this client, once nothing has needed it for a while. The owner of
|
|
277
|
+
* a local relay is exempt: it is the one that has to stop that relay at shutdown.
|
|
278
|
+
*/
|
|
279
|
+
private rearmIdle() {
|
|
280
|
+
if (this.idleTimer) clearTimeout(this.idleTimer)
|
|
281
|
+
if (this.relay) return
|
|
282
|
+
this.idleTimer = setTimeout(() => {
|
|
283
|
+
if (this.pending.size > 0) return this.rearmIdle()
|
|
284
|
+
this.ws?.close()
|
|
285
|
+
if (clients.get(this.identity) === this) clients.delete(this.identity)
|
|
286
|
+
}, this.idleTimeout)
|
|
287
|
+
this.idleTimer.unref()
|
|
288
|
+
}
|
|
289
|
+
|
|
257
290
|
/** Wait for a wallet page to connect, opening the browser first when running locally. */
|
|
258
291
|
async waitForSigner(): Promise<void> {
|
|
259
292
|
await this.connect()
|
|
@@ -291,10 +324,14 @@ export class WalletClient {
|
|
|
291
324
|
|
|
292
325
|
return new Promise((resolve, reject) => {
|
|
293
326
|
this.pending.set(request.id, { resolve, reject })
|
|
327
|
+
this.rearmIdle()
|
|
294
328
|
ws.send(JSON.stringify(request))
|
|
295
329
|
|
|
296
330
|
setTimeout(() => {
|
|
297
|
-
if (this.pending.delete(request.id))
|
|
331
|
+
if (this.pending.delete(request.id)) {
|
|
332
|
+
this.rearmIdle()
|
|
333
|
+
reject(new Error('Transaction request timed out'))
|
|
334
|
+
}
|
|
298
335
|
}, REQUEST_TIMEOUT)
|
|
299
336
|
})
|
|
300
337
|
}
|
|
@@ -318,8 +355,11 @@ export class WalletClient {
|
|
|
318
355
|
*/
|
|
319
356
|
getUrl(): string {
|
|
320
357
|
if (this.pairingUrl) return this.pairingUrl
|
|
321
|
-
if (this.remoteUrl)
|
|
322
|
-
|
|
358
|
+
if (this.remoteUrl) {
|
|
359
|
+
const token = this.remoteToken ? roomToken(this.remoteToken, this.identity, 'signer') : ''
|
|
360
|
+
return `${this.remoteUrl}#t=${token}`
|
|
361
|
+
}
|
|
362
|
+
return pairingUrlFor(LOCAL_PORTS[0], this.relayOptions.token ?? localToken(), this.identity)
|
|
323
363
|
}
|
|
324
364
|
|
|
325
365
|
/** A relay we do not own, or a server with no desktop, has no browser for us to open. */
|
|
@@ -388,15 +428,44 @@ export class WalletClient {
|
|
|
388
428
|
}
|
|
389
429
|
|
|
390
430
|
async stop(): Promise<void> {
|
|
431
|
+
if (this.idleTimer) clearTimeout(this.idleTimer)
|
|
391
432
|
this.ws?.close()
|
|
392
433
|
await this.relay?.stop()
|
|
393
434
|
this.relay = null
|
|
394
435
|
}
|
|
395
436
|
}
|
|
396
437
|
|
|
397
|
-
|
|
438
|
+
const clients = new Map<string, WalletClient>()
|
|
398
439
|
|
|
399
|
-
|
|
400
|
-
|
|
440
|
+
let relayOptions: { port?: number; token?: string } = {}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Where the relay is, for every client this process makes.
|
|
444
|
+
*
|
|
445
|
+
* A property of the deployment rather than of any one person: a hosted server runs the
|
|
446
|
+
* relay on its own port with its own secret, and each identity's client has to reach that
|
|
447
|
+
* one. Set it before anybody signs — a client that cannot find a relay starts its own,
|
|
448
|
+
* which then has nobody in it.
|
|
449
|
+
*/
|
|
450
|
+
export function configureWalletRelay(options: { port?: number; token?: string }): void {
|
|
451
|
+
relayOptions = options
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* One client per identity, because everything it holds is that person's: the socket, the
|
|
456
|
+
* in-flight requests, and which page is open with which account. A single-user server has
|
|
457
|
+
* exactly one, which is every self-hosted run.
|
|
458
|
+
*/
|
|
459
|
+
export function getWalletClient(identity: string): WalletClient {
|
|
460
|
+
let client = clients.get(identity)
|
|
461
|
+
if (!client) {
|
|
462
|
+
client = new WalletClient(identity, relayOptions)
|
|
463
|
+
clients.set(identity, client)
|
|
464
|
+
}
|
|
401
465
|
return client
|
|
402
466
|
}
|
|
467
|
+
|
|
468
|
+
/** Every live client, for shutdown — callers outside a tool call have no identity of their own. */
|
|
469
|
+
export function allWalletClients(): WalletClient[] {
|
|
470
|
+
return [...clients.values()]
|
|
471
|
+
}
|
package/src/walletconnect.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { mkdirSync } from 'node:fs'
|
|
2
|
+
import { createRequire } from 'node:module'
|
|
2
3
|
import { homedir } from 'node:os'
|
|
3
4
|
import { join } from 'node:path'
|
|
4
5
|
import { getClientManager } from './client.js'
|
|
6
|
+
import { DEFAULT_IDENTITY } from './context.js'
|
|
5
7
|
import { getKeyValueStorage } from './kv-storage.js'
|
|
6
8
|
import { log } from './log.js'
|
|
7
9
|
import type { ChainName } from './types.js'
|
|
@@ -28,6 +30,42 @@ interface Session {
|
|
|
28
30
|
peer?: string
|
|
29
31
|
}
|
|
30
32
|
|
|
33
|
+
/** Sessions do not record who paired them, so the mapping is kept alongside in the same store. */
|
|
34
|
+
const BINDINGS_KEY = 'web3-tools-mcp:session-identities'
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The sessions an identity may sign with.
|
|
38
|
+
*
|
|
39
|
+
* Matched by the binding recorded when the session was approved, never by the accounts it
|
|
40
|
+
* reports — a wallet states its own account list, so trusting that would let a peer claim
|
|
41
|
+
* someone else's address and be handed their signing requests.
|
|
42
|
+
*
|
|
43
|
+
* An unbound session is one paired before bindings existed. It counts only for the shared
|
|
44
|
+
* identity, which is a server with a single user; under a real identity it belongs to
|
|
45
|
+
* nobody rather than to whoever asks first.
|
|
46
|
+
*/
|
|
47
|
+
export function ownedSessions<T extends { topic: string }>(live: T[], bindings: Record<string, string>, identity: string): T[] {
|
|
48
|
+
return live.filter((s) => bindings[s.topic] === identity || (identity === DEFAULT_IDENTITY && !bindings[s.topic]))
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Bindings with no session behind them any more.
|
|
53
|
+
*
|
|
54
|
+
* An empty session list yields nothing: a store that has not hydrated looks exactly like one
|
|
55
|
+
* with no sessions, and clearing every binding would strand wallets that are still paired.
|
|
56
|
+
*/
|
|
57
|
+
export function orphanedBindings(live: { topic: string }[], bindings: Record<string, string>): string[] {
|
|
58
|
+
if (live.length === 0) return []
|
|
59
|
+
const known = new Set(live.map((session) => session.topic))
|
|
60
|
+
return Object.keys(bindings).filter((topic) => !known.has(topic))
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** What was paired before and is not paired now — a wallet that ended its own session. */
|
|
64
|
+
export function droppedSessions<T extends { topic: string }>(before: T[], after: { topic: string }[]): T[] {
|
|
65
|
+
const live = new Set(after.map((session) => session.topic))
|
|
66
|
+
return before.filter((session) => !live.has(session.topic))
|
|
67
|
+
}
|
|
68
|
+
|
|
31
69
|
const METHODS = ['eth_sendTransaction', 'personal_sign', 'eth_signTypedData_v4']
|
|
32
70
|
const EVENTS = ['chainChanged', 'accountsChanged']
|
|
33
71
|
const APPROVAL_TIMEOUT = 300_000
|
|
@@ -37,9 +75,7 @@ const APPROVAL_TIMEOUT = 300_000
|
|
|
37
75
|
* a local one is only reachable from the machine it runs on, and says so.
|
|
38
76
|
*/
|
|
39
77
|
function serverUrl(): string {
|
|
40
|
-
|
|
41
|
-
if (process.env.MCP_HTTP_PORT) return `http://localhost:${process.env.MCP_HTTP_PORT}`
|
|
42
|
-
return 'http://localhost'
|
|
78
|
+
return process.env.MCP_PUBLIC_URL?.replace(/\/$/, '') ?? 'http://localhost'
|
|
43
79
|
}
|
|
44
80
|
|
|
45
81
|
function storePath(): string {
|
|
@@ -54,6 +90,8 @@ export class PhoneSigner {
|
|
|
54
90
|
private pairing: { uri: string; expiresAt: number } | undefined
|
|
55
91
|
/** Label shown in the wallet; whoever is driving this server says who they are. */
|
|
56
92
|
private agent: string | undefined
|
|
93
|
+
/** The last edit of the bindings key; the next one queues behind it. */
|
|
94
|
+
private bindingsEdit: Promise<unknown> = Promise.resolve()
|
|
57
95
|
|
|
58
96
|
constructor(private projectId: string) {}
|
|
59
97
|
|
|
@@ -87,26 +125,100 @@ export class PhoneSigner {
|
|
|
87
125
|
return this.starting
|
|
88
126
|
}
|
|
89
127
|
|
|
90
|
-
/**
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
128
|
+
/**
|
|
129
|
+
* Who each session belongs to, kept in the client's own store so it lands in the file or
|
|
130
|
+
* in Redis exactly as the sessions themselves do.
|
|
131
|
+
*/
|
|
132
|
+
private async bindings(): Promise<Record<string, string>> {
|
|
133
|
+
return (await this.client?.core.storage.getItem<Record<string, string>>(BINDINGS_KEY)) ?? {}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* All bindings live under one key, so an edit is a read-modify-write — and two approvals
|
|
138
|
+
* settling together would each read the same map and the second would erase the first.
|
|
139
|
+
* Edits queue behind each other instead.
|
|
140
|
+
*
|
|
141
|
+
* ponytail: serialised per process only. Several server processes on one Redis still
|
|
142
|
+
* race; one key per topic would end that.
|
|
143
|
+
*/
|
|
144
|
+
private updateBindings(edit: (bindings: Record<string, string>) => void): Promise<void> {
|
|
145
|
+
const next = this.bindingsEdit.then(async () => {
|
|
146
|
+
const bindings = await this.bindings()
|
|
147
|
+
edit(bindings)
|
|
148
|
+
await this.client?.core.storage.setItem(BINDINGS_KEY, bindings)
|
|
149
|
+
})
|
|
150
|
+
this.bindingsEdit = next.catch(() => {})
|
|
151
|
+
return next
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private bind(topic: string, identity: string): Promise<void> {
|
|
155
|
+
return this.updateBindings((bindings) => {
|
|
156
|
+
bindings[topic] = identity
|
|
157
|
+
})
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Some wallets keep one session per site, so approving a second pairing in the same app
|
|
162
|
+
* ends the first — the session vanishes from the store and the binding is left pointing at
|
|
163
|
+
* nothing. Worth saying out loud: `pair_phone_wallet` has just listed that wallet as still
|
|
164
|
+
* paired, and the next `wallet_status` will quietly show one fewer.
|
|
165
|
+
*/
|
|
166
|
+
private async reportReplaced(identity: string, before: Session[]): Promise<void> {
|
|
167
|
+
const dropped = droppedSessions(before, await this.sessions(identity))
|
|
168
|
+
if (dropped.length === 0) return
|
|
169
|
+
|
|
170
|
+
for (const session of dropped) {
|
|
171
|
+
log(
|
|
172
|
+
'warning',
|
|
173
|
+
'WalletConnect',
|
|
174
|
+
`${session.peer ?? 'A wallet'} ended its earlier pairing for ${session.accounts[0] ?? 'an account'} when this one was approved — ` +
|
|
175
|
+
'it keeps one session per site. Pair a different wallet app to hold both at once.'
|
|
176
|
+
)
|
|
105
177
|
}
|
|
178
|
+
|
|
179
|
+
await this.sweepBindings()
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Bindings outlive the sessions they point at, so they accumulate. Swept here rather than
|
|
184
|
+
* on every read: a pairing has just settled, which is the one moment the session store is
|
|
185
|
+
* known to be populated.
|
|
186
|
+
*/
|
|
187
|
+
private async sweepBindings(): Promise<void> {
|
|
188
|
+
const orphans = orphanedBindings(this.client?.session.getAll() ?? [], await this.bindings())
|
|
189
|
+
if (orphans.length === 0) return
|
|
190
|
+
|
|
191
|
+
await this.updateBindings((bindings) => {
|
|
192
|
+
for (const topic of orphans) delete bindings[topic]
|
|
193
|
+
})
|
|
194
|
+
log('info', 'WalletConnect', `Cleared ${orphans.length} binding(s) whose session is gone`)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Every session this identity owns, live or not — what disconnecting should sweep. */
|
|
198
|
+
private async owned(identity: string) {
|
|
199
|
+
await this.start()
|
|
200
|
+
return ownedSessions(this.client?.session.getAll() ?? [], await this.bindings(), identity)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* One of this identity's paired wallets.
|
|
205
|
+
*
|
|
206
|
+
* `account` picks between them when more than one is paired; without it the most recent
|
|
207
|
+
* wins, which is the only one most people have. Selecting by address is safe here in a
|
|
208
|
+
* way that *identifying* by address would not be: the candidates are already narrowed to
|
|
209
|
+
* sessions this identity was bound to when they were approved, so a wallet claiming an
|
|
210
|
+
* address it does not hold can only reach its own owner.
|
|
211
|
+
*/
|
|
212
|
+
async session(identity: string = DEFAULT_IDENTITY, account?: string): Promise<Session | undefined> {
|
|
213
|
+
const paired = await this.sessions(identity)
|
|
214
|
+
if (!account) return paired[paired.length - 1]
|
|
215
|
+
|
|
216
|
+
const wanted = account.toLowerCase()
|
|
217
|
+
return paired.find((session) => session.accounts.some((a) => a.toLowerCase() === wanted))
|
|
106
218
|
}
|
|
107
219
|
|
|
108
|
-
async isPaired(): Promise<boolean> {
|
|
109
|
-
return Boolean(await this.session())
|
|
220
|
+
async isPaired(identity?: string): Promise<boolean> {
|
|
221
|
+
return Boolean(await this.session(identity))
|
|
110
222
|
}
|
|
111
223
|
|
|
112
224
|
/**
|
|
@@ -124,15 +236,31 @@ export class PhoneSigner {
|
|
|
124
236
|
* settles, so every reply the wallet sends goes to a topic nobody is listening on. A
|
|
125
237
|
* stale name in the dialog is a far smaller cost than signing that silently hangs.
|
|
126
238
|
*/
|
|
239
|
+
/**
|
|
240
|
+
* Hand every session one identity owns to another. A login pairs before anybody is known,
|
|
241
|
+
* under a placeholder, and the wallet it proved becomes that person's to sign with.
|
|
242
|
+
*/
|
|
243
|
+
async rebind(from: string, to: string): Promise<void> {
|
|
244
|
+
await this.start()
|
|
245
|
+
await this.updateBindings((bindings) => {
|
|
246
|
+
for (const [topic, identity] of Object.entries(bindings)) {
|
|
247
|
+
if (identity === from) bindings[topic] = to
|
|
248
|
+
}
|
|
249
|
+
})
|
|
250
|
+
}
|
|
251
|
+
|
|
127
252
|
nameAgent(agent?: string): void {
|
|
128
253
|
if (agent && !this.client) this.agent = agent
|
|
129
254
|
}
|
|
130
255
|
|
|
131
|
-
async pair(chains: ChainName[]): Promise<string> {
|
|
256
|
+
async pair(chains: ChainName[], identity: string = DEFAULT_IDENTITY): Promise<string> {
|
|
132
257
|
await this.start()
|
|
133
258
|
log('info', 'WalletConnect', `Pairing as "${this.agent ?? 'default name'}" for ${chains.length} chain(s)`)
|
|
134
259
|
if (!this.client) throw new Error('WalletConnect failed to start')
|
|
135
260
|
|
|
261
|
+
// What this identity had before, so the approval can tell whether the wallet kept it.
|
|
262
|
+
const before = await this.sessions(identity)
|
|
263
|
+
|
|
136
264
|
const clientManager = getClientManager()
|
|
137
265
|
const eip155 = chains.map((chain) => `eip155:${clientManager.getChainId(chain)}`)
|
|
138
266
|
|
|
@@ -146,16 +274,37 @@ export class PhoneSigner {
|
|
|
146
274
|
this.pairing = { uri, expiresAt: Date.now() + APPROVAL_TIMEOUT }
|
|
147
275
|
|
|
148
276
|
approval()
|
|
149
|
-
.then((session) =>
|
|
277
|
+
.then(async (session) => {
|
|
278
|
+
// Recorded as the session settles: whoever asked for this pairing owns it.
|
|
279
|
+
await this.bind(session.topic, identity)
|
|
280
|
+
log('info', 'WalletConnect', `Paired with ${session.peer?.metadata?.name ?? 'a wallet'}`)
|
|
281
|
+
await this.reportReplaced(identity, before)
|
|
282
|
+
})
|
|
150
283
|
.catch((error) => log('warning', 'WalletConnect', `Pairing was not completed: ${error.message}`))
|
|
151
284
|
|
|
152
285
|
return uri
|
|
153
286
|
}
|
|
154
287
|
|
|
155
|
-
/** Send a request to
|
|
156
|
-
async request(
|
|
157
|
-
|
|
158
|
-
|
|
288
|
+
/** Send a request to one of this identity's wallets. Throws if the chain is outside the session. */
|
|
289
|
+
async request(
|
|
290
|
+
chain: ChainName,
|
|
291
|
+
method: string,
|
|
292
|
+
params: unknown[],
|
|
293
|
+
target: {
|
|
294
|
+
identity?: string
|
|
295
|
+
account?: string
|
|
296
|
+
/**
|
|
297
|
+
* Called once the request is with the wallet and only a human is left. Everything that
|
|
298
|
+
* can fail quickly — no session, a chain this one never approved — has already thrown
|
|
299
|
+
* by then, so the caller can stop waiting without hiding a real error.
|
|
300
|
+
*/
|
|
301
|
+
onWaiting?: () => void
|
|
302
|
+
} = {}
|
|
303
|
+
): Promise<unknown> {
|
|
304
|
+
const session = await this.session(target.identity, target.account)
|
|
305
|
+
if (!session || !this.client) {
|
|
306
|
+
throw new Error(target.account ? `No phone wallet paired for account ${target.account}` : 'No phone wallet paired')
|
|
307
|
+
}
|
|
159
308
|
|
|
160
309
|
const chainId = getClientManager().getChainId(chain)
|
|
161
310
|
if (!session.chains.includes(chainId)) {
|
|
@@ -171,11 +320,17 @@ export class PhoneSigner {
|
|
|
171
320
|
`→ ${method} on ${chain}, session ${session.topic.slice(0, 8)}, waiting for ${session.peer ?? 'wallet'}`
|
|
172
321
|
)
|
|
173
322
|
try {
|
|
174
|
-
const
|
|
323
|
+
const answer = this.client.request({
|
|
175
324
|
topic: session.topic,
|
|
176
325
|
chainId: `eip155:${chainId}`,
|
|
177
326
|
request: { method, params }
|
|
178
327
|
})
|
|
328
|
+
|
|
329
|
+
// Neither MetaMask nor Rabby pushes a notification, so nobody is going to notice this
|
|
330
|
+
// in the next few seconds unless they already had the wallet open.
|
|
331
|
+
target.onWaiting?.()
|
|
332
|
+
|
|
333
|
+
const result = await answer
|
|
179
334
|
log('info', 'WalletConnect', `← ${method} answered in ${Date.now() - started}ms`)
|
|
180
335
|
return result
|
|
181
336
|
} catch (error) {
|
|
@@ -185,33 +340,57 @@ export class PhoneSigner {
|
|
|
185
340
|
}
|
|
186
341
|
}
|
|
187
342
|
|
|
188
|
-
/** Every
|
|
189
|
-
async sessions(): Promise<Session[]> {
|
|
190
|
-
await this.
|
|
191
|
-
return
|
|
192
|
-
|
|
193
|
-
accounts
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
343
|
+
/** Every wallet this identity can currently sign with, oldest pairing first. */
|
|
344
|
+
async sessions(identity: string = DEFAULT_IDENTITY): Promise<Session[]> {
|
|
345
|
+
const live = (await this.owned(identity)).filter((session) => session.expiry * 1000 > Date.now())
|
|
346
|
+
return live.map((session) => {
|
|
347
|
+
// "eip155:8453:0xabc…" → address / chain id
|
|
348
|
+
const accounts = session.namespaces.eip155?.accounts ?? []
|
|
349
|
+
return {
|
|
350
|
+
topic: session.topic,
|
|
351
|
+
accounts: [...new Set(accounts.map((a) => a.split(':')[2] as string))],
|
|
352
|
+
chains: [...new Set(accounts.map((a) => Number(a.split(':')[1])))],
|
|
353
|
+
peer: session.peer?.metadata?.name
|
|
354
|
+
}
|
|
355
|
+
})
|
|
197
356
|
}
|
|
198
357
|
|
|
199
358
|
/**
|
|
200
|
-
* Disconnect
|
|
201
|
-
*
|
|
202
|
-
*
|
|
359
|
+
* Disconnect one of this identity's wallets, or all of them when no account is named.
|
|
360
|
+
*
|
|
361
|
+
* Pairings underneath are swept only once no session is left at all. Each pairing attempt
|
|
362
|
+
* leaves one behind whether or not a wallet ever approved it, so they accumulate quietly —
|
|
363
|
+
* but a pairing does not record who made it, so dropping them while anyone still has a
|
|
364
|
+
* session could take somebody else's with it.
|
|
203
365
|
*/
|
|
204
|
-
async
|
|
366
|
+
async disconnect(identity: string = DEFAULT_IDENTITY, account?: string): Promise<{ sessions: number; pairings: number }> {
|
|
205
367
|
await this.start()
|
|
206
368
|
if (!this.client) return { sessions: 0, pairings: 0 }
|
|
207
369
|
|
|
208
|
-
|
|
209
|
-
|
|
370
|
+
let topics: Set<string>
|
|
371
|
+
if (account) {
|
|
372
|
+
// Same rule that picks a wallet to sign with, so you can drop exactly what you chose.
|
|
373
|
+
const match = await this.session(identity, account)
|
|
374
|
+
if (!match) throw new Error(`No paired wallet holds ${account}`)
|
|
375
|
+
topics = new Set([match.topic])
|
|
376
|
+
} else {
|
|
377
|
+
// Expired sessions included: they are still this identity's to clean up.
|
|
378
|
+
topics = new Set((await this.owned(identity)).map((session) => session.topic))
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
for (const topic of topics) {
|
|
210
382
|
await this.client
|
|
211
|
-
.disconnect({ topic
|
|
383
|
+
.disconnect({ topic, reason: { code: 6000, message: 'User disconnected' } })
|
|
212
384
|
.catch((error) => log('warning', 'WalletConnect', `Could not disconnect a session: ${error.message}`))
|
|
213
385
|
}
|
|
214
386
|
|
|
387
|
+
await this.updateBindings((bindings) => {
|
|
388
|
+
for (const topic of topics) delete bindings[topic]
|
|
389
|
+
})
|
|
390
|
+
|
|
391
|
+
const remaining = this.client.session.getAll().length
|
|
392
|
+
if (remaining > 0) return { sessions: topics.size, pairings: 0 }
|
|
393
|
+
|
|
215
394
|
const pairings = this.client.core.pairing.getPairings()
|
|
216
395
|
for (const pairing of pairings) {
|
|
217
396
|
await this.client.core.pairing
|
|
@@ -219,11 +398,7 @@ export class PhoneSigner {
|
|
|
219
398
|
.catch((error) => log('warning', 'WalletConnect', `Could not drop a pairing: ${error.message}`))
|
|
220
399
|
}
|
|
221
400
|
|
|
222
|
-
return { sessions:
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
async unpair(): Promise<void> {
|
|
226
|
-
await this.disconnectAll()
|
|
401
|
+
return { sessions: topics.size, pairings: pairings.length }
|
|
227
402
|
}
|
|
228
403
|
}
|
|
229
404
|
|
|
@@ -281,6 +456,33 @@ export async function pairingLinks(projectId: string, uri: string, search?: stri
|
|
|
281
456
|
)
|
|
282
457
|
}
|
|
283
458
|
|
|
459
|
+
// qrcode-terminal's own encoder, already a dependency for the chat QR; it ships untyped.
|
|
460
|
+
const require = createRequire(import.meta.url)
|
|
461
|
+
const QRCode = require('qrcode-terminal/vendor/QRCode') as new (
|
|
462
|
+
typeNumber: number,
|
|
463
|
+
level: number
|
|
464
|
+
) => { addData(text: string): void; make(): void; getModuleCount(): number; isDark(row: number, col: number): boolean }
|
|
465
|
+
const LOW_CORRECTION = 1
|
|
466
|
+
|
|
467
|
+
/** A QR as SVG, for a page to show. The terminal QR is only legible in monospace. */
|
|
468
|
+
export function qrSvg(text: string): string {
|
|
469
|
+
const qr = new QRCode(-1, LOW_CORRECTION)
|
|
470
|
+
qr.addData(text)
|
|
471
|
+
qr.make()
|
|
472
|
+
|
|
473
|
+
const size = qr.getModuleCount()
|
|
474
|
+
const quiet = 4
|
|
475
|
+
let path = ''
|
|
476
|
+
for (let row = 0; row < size; row++) {
|
|
477
|
+
for (let col = 0; col < size; col++) {
|
|
478
|
+
if (qr.isDark(row, col)) path += `M${col + quiet} ${row + quiet}h1v1h-1z`
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
const box = size + quiet * 2
|
|
483
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${box} ${box}" shape-rendering="crispEdges"><rect width="${box}" height="${box}" fill="#fff"/><path d="${path}"/></svg>`
|
|
484
|
+
}
|
|
485
|
+
|
|
284
486
|
let signer: PhoneSigner | null = null
|
|
285
487
|
|
|
286
488
|
/** Configured only when a WalletConnect project id is present. */
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"advanced.d.ts","sourceRoot":"","sources":["../../src/tools/advanced.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAMvB,wBAqZC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"advanced.js","sourceRoot":"","sources":["../../src/tools/advanced.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"balance.d.ts","sourceRoot":"","sources":["../../src/tools/balance.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAavB,wBA4JC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"balance.js","sourceRoot":"","sources":["../../src/tools/balance.ts"],"names":[],"mappings":"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"}
|