@ckb-ccc/core 1.21.0 → 1.22.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.
Files changed (55) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +3 -3
  3. package/dist/advanced.d.mts +1 -1
  4. package/dist/advanced.mjs +1 -1
  5. package/dist/{advancedBarrel-B_9PyL2D.d.mts → advancedBarrel-Daj_LJkz.d.mts} +139 -19
  6. package/dist/advancedBarrel-Daj_LJkz.d.mts.map +1 -0
  7. package/dist/advancedBarrel.d.mts +1 -1
  8. package/dist/advancedBarrel.mjs +1 -1
  9. package/dist/barrel-CFOWSjcM.mjs +7 -0
  10. package/dist/barrel-CFOWSjcM.mjs.map +1 -0
  11. package/dist/barrel.d.mts +2 -2
  12. package/dist/barrel.mjs +1 -1
  13. package/dist/index.d.mts +2 -2
  14. package/dist/index.mjs +1 -1
  15. package/dist/transformers-DAMi6ZOq.mjs +2 -0
  16. package/dist/transformers-DAMi6ZOq.mjs.map +1 -0
  17. package/dist.commonjs/advanced.d.ts +1 -1
  18. package/dist.commonjs/advanced.js +1 -1
  19. package/dist.commonjs/{advancedBarrel-Cll2j9KP.d.ts → advancedBarrel-CJL3rfuW.d.ts} +139 -19
  20. package/dist.commonjs/advancedBarrel-CJL3rfuW.d.ts.map +1 -0
  21. package/dist.commonjs/advancedBarrel.d.ts +1 -1
  22. package/dist.commonjs/advancedBarrel.js +1 -1
  23. package/dist.commonjs/barrel-DZkApbAR.js +23 -0
  24. package/dist.commonjs/barrel-DZkApbAR.js.map +1 -0
  25. package/dist.commonjs/barrel.d.ts +2 -2
  26. package/dist.commonjs/barrel.js +1 -1
  27. package/dist.commonjs/index.d.ts +2 -2
  28. package/dist.commonjs/index.js +1 -1
  29. package/dist.commonjs/transformers-C6jbFO7W.js +2 -0
  30. package/dist.commonjs/transformers-C6jbFO7W.js.map +1 -0
  31. package/package.json +1 -1
  32. package/src/client/client.ts +8 -2
  33. package/src/client/clientPublicMainnet.advanced.ts +9 -0
  34. package/src/client/clientPublicTestnet.advanced.ts +9 -0
  35. package/src/jsonRpc/requestor.ts +32 -8
  36. package/src/jsonRpc/transports/fallback.ts +6 -2
  37. package/src/jsonRpc/transports/http.ts +20 -17
  38. package/src/jsonRpc/transports/transport.ts +11 -1
  39. package/src/jsonRpc/transports/webSocket.ts +84 -26
  40. package/src/signer/btc/psbt.ts +4 -2
  41. package/src/signer/jsonRpc/handler.ts +394 -133
  42. package/src/signer/jsonRpc/signer.ts +178 -34
  43. package/src/signer/jsonRpc/transformers.ts +4 -0
  44. package/src/utils/abortSignal.ts +14 -0
  45. package/src/utils/index.ts +148 -0
  46. package/dist/advancedBarrel-B_9PyL2D.d.mts.map +0 -1
  47. package/dist/barrel-CrN8Ibc4.mjs +0 -7
  48. package/dist/barrel-CrN8Ibc4.mjs.map +0 -1
  49. package/dist/transformers-7aume1sD.mjs +0 -2
  50. package/dist/transformers-7aume1sD.mjs.map +0 -1
  51. package/dist.commonjs/advancedBarrel-Cll2j9KP.d.ts.map +0 -1
  52. package/dist.commonjs/barrel-BL6-tNI3.js +0 -23
  53. package/dist.commonjs/barrel-BL6-tNI3.js.map +0 -1
  54. package/dist.commonjs/transformers-1XBitiyh.js +0 -2
  55. package/dist.commonjs/transformers-1XBitiyh.js.map +0 -1
@@ -1,11 +1,16 @@
1
+ import { randomBytes } from "@noble/hashes/utils.js";
1
2
  import type { Transaction } from "../../ckb/index.js";
2
3
  import { JsonRpcTransformers } from "../../client/jsonRpc/advanced.js";
4
+ import { hexFrom } from "../../hex/index.js";
3
5
  import { JsonRpcError, type JsonRpcPayload } from "../../jsonRpc/index.js";
6
+ import { abortSignalAny, sleep } from "../../utils/index.js";
7
+ import { OwnerUnique } from "../../utils/owner/unique.js";
4
8
  import type { Signer } from "../signer/index.js";
5
9
  import { signerJsonRpcNetworkIdFromAddressPrefix } from "./network.js";
6
10
  import {
7
11
  SignerJsonRpcTransformers,
8
12
  type SignerJsonRpcInfo,
13
+ type SignerJsonRpcInfoPayload,
9
14
  type SignerJsonRpcMessageToSign,
10
15
  } from "./transformers.js";
11
16
 
@@ -14,156 +19,376 @@ export type SignerJsonRpcConfirmation =
14
19
  | { method: "sign_message"; message: SignerJsonRpcMessageToSign }
15
20
  | { method: "sign_transaction"; transaction: Transaction };
16
21
 
17
- export type SignerJsonRpcHandlerConfig = {
22
+ const GET_RESULT_CACHE_MS = 120_000;
23
+ const GET_RESULT_HISTORY_CAPACITY = 128;
24
+ const GET_RESULT_WAIT_MS = 10_000;
25
+ const GET_RESULT_PENDING_RESULT = Symbol("pending");
26
+
27
+ type SignerJsonRpcResultEntry = {
28
+ completion: Promise<unknown>;
29
+ retrieved: boolean;
30
+ };
31
+
32
+ export enum SignerJsonRpcErrorCode {
33
+ MethodNotFound = -32601,
34
+ InvalidParams = -32602,
35
+ ServerError = -32000,
36
+ InvalidState = -32001,
37
+ NetworkMismatch = -32002,
38
+ UserRejected = -32003,
39
+ InvalidSession = -32004,
40
+ DuplicateRequestId = -32005,
41
+ }
42
+
43
+ export type SignerJsonRpcProviderSessionOptions = {
44
+ /** Aborts when the provider session is disposed. */
45
+ signal?: AbortSignal;
46
+ };
47
+
48
+ export type SignerJsonRpcResultRecord =
49
+ | { status: "not_found" }
50
+ | { status: "pending" }
51
+ | { status: "completed"; result: unknown };
52
+
53
+ export type SignerJsonRpcProviderSessionConfig = {
18
54
  getSigner: () => Signer | undefined;
19
55
  getSignerMetadata?: () => Pick<SignerJsonRpcInfo, "name" | "icon">;
20
56
  /** Connects the requested network and returns its connected Signer. */
21
- connect: (networkId: string) => Promise<Signer>;
22
- confirmRequest: (request: SignerJsonRpcConfirmation) => Promise<boolean>;
57
+ connect: (
58
+ networkId: string,
59
+ options?: SignerJsonRpcProviderSessionOptions,
60
+ ) => Promise<Signer>;
61
+ confirmRequest: (
62
+ request: SignerJsonRpcConfirmation,
63
+ options?: SignerJsonRpcProviderSessionOptions,
64
+ ) => Promise<boolean>;
23
65
  };
24
66
 
25
- export type SignerJsonRpcHandler = (payload: JsonRpcPayload) => unknown;
67
+ export class SignerJsonRpcProviderSession {
68
+ private readonly abortController = new AbortController();
69
+ private readonly id = hexFrom(randomBytes(16));
70
+ private readonly resultRecords = new Map<string, SignerJsonRpcResultEntry>();
71
+ private readonly resultHistory = new Map<string, SignerJsonRpcResultEntry>();
72
+ private readonly handlers;
73
+ private info?: SignerJsonRpcInfoPayload;
74
+ private connected = false;
26
75
 
27
- export function buildSignerJsonRpcHandler({
28
- connect,
29
- confirmRequest,
30
- getSigner,
31
- getSignerMetadata = () => ({}),
32
- }: SignerJsonRpcHandlerConfig): SignerJsonRpcHandler {
33
- const requireSigner = () => {
34
- const signer = getSigner();
35
- if (!signer) {
76
+ private constructor(
77
+ private readonly config: SignerJsonRpcProviderSessionConfig,
78
+ ) {
79
+ this.handlers = new Map<
80
+ string,
81
+ readonly [
82
+ number,
83
+ (
84
+ params: unknown[],
85
+ options?: SignerJsonRpcProviderSessionOptions,
86
+ ) => unknown,
87
+ ]
88
+ >([
89
+ [
90
+ "get_info",
91
+ [
92
+ 0,
93
+ () =>
94
+ (this.info ??= buildSignerInfo(
95
+ this.id,
96
+ this.requireSigner(),
97
+ this.config.getSignerMetadata?.() ?? {},
98
+ )),
99
+ ],
100
+ ],
101
+ [
102
+ "connect",
103
+ [
104
+ 1,
105
+ async ([networkId], options) => {
106
+ this.connected = false;
107
+ if (typeof networkId !== "string" || !networkId) {
108
+ throw new JsonRpcError({
109
+ code: SignerJsonRpcErrorCode.InvalidParams,
110
+ message: "Invalid network ID",
111
+ });
112
+ }
113
+
114
+ await this.requireConfirmation(
115
+ { method: "connect", networkId },
116
+ options,
117
+ );
118
+
119
+ const signer = await this.config.connect(networkId, options);
120
+ options?.signal?.throwIfAborted();
121
+ const actualNetworkId = signerJsonRpcNetworkIdFromAddressPrefix(
122
+ signer.client.addressPrefix,
123
+ );
124
+ if (actualNetworkId !== networkId) {
125
+ throw new JsonRpcError({
126
+ code: SignerJsonRpcErrorCode.NetworkMismatch,
127
+ message: `Signer uses ${actualNetworkId}, expected ${networkId}`,
128
+ });
129
+ }
130
+
131
+ this.connected = true;
132
+ return null;
133
+ },
134
+ ],
135
+ ],
136
+ [
137
+ "get_scripts",
138
+ [
139
+ 0,
140
+ async () =>
141
+ (await this.requireSigner().getAddressObjs()).map(({ script }) =>
142
+ JsonRpcTransformers.scriptFrom(script),
143
+ ),
144
+ ],
145
+ ],
146
+ [
147
+ "get_native_address",
148
+ [0, () => this.requireSigner().getInternalAddress()],
149
+ ],
150
+ ["get_identity", [0, () => this.requireSigner().getIdentity()]],
151
+ [
152
+ "sign_message",
153
+ [
154
+ 1,
155
+ async ([message], options) => {
156
+ const parsedMessage = parseMessageParam(message);
157
+ await this.requireConfirmation(
158
+ {
159
+ method: "sign_message",
160
+ message: SignerJsonRpcTransformers.messageFrom(parsedMessage),
161
+ },
162
+ options,
163
+ );
164
+ return this.requireSigner().signMessageRaw(parsedMessage);
165
+ },
166
+ ],
167
+ ],
168
+ [
169
+ "prepare_transaction",
170
+ [
171
+ 1,
172
+ async ([transaction]) =>
173
+ JsonRpcTransformers.transactionFrom(
174
+ await this.requireSigner().prepareTransaction(
175
+ parseTransactionParam(transaction),
176
+ ),
177
+ ),
178
+ ],
179
+ ],
180
+ [
181
+ "sign_transaction",
182
+ [
183
+ 1,
184
+ async ([transaction], options) => {
185
+ const parsedTransaction = parseTransactionParam(transaction);
186
+ await this.requireConfirmation(
187
+ { method: "sign_transaction", transaction: parsedTransaction },
188
+ options,
189
+ );
190
+ return JsonRpcTransformers.transactionFrom(
191
+ await this.requireSigner().signOnlyTransaction(parsedTransaction),
192
+ );
193
+ },
194
+ ],
195
+ ],
196
+ ]);
197
+ }
198
+
199
+ static open(
200
+ config: SignerJsonRpcProviderSessionConfig,
201
+ ): OwnerUnique<SignerJsonRpcProviderSession> {
202
+ const session = new SignerJsonRpcProviderSession(config);
203
+ return new OwnerUnique(session, (session) => session.dispose());
204
+ }
205
+
206
+ get isConnected() {
207
+ return this.connected;
208
+ }
209
+
210
+ handle = (payload: JsonRpcPayload) => {
211
+ const signal = this.abortController.signal;
212
+ signal.throwIfAborted();
213
+
214
+ const [metadata, ...params] = requireParams(payload);
215
+ const { requestId, sessionId } = parseRequestMetadata(metadata);
216
+
217
+ if (payload.method === "get_result") {
218
+ const [targetRequestId] = params;
219
+ requireParams(payload, 2);
220
+ requireRequestId(targetRequestId);
221
+ return this.getResult(
222
+ this.resultRecords.get(targetRequestId) ??
223
+ this.resultHistory.get(targetRequestId),
224
+ signal,
225
+ );
226
+ }
227
+
228
+ requireRequestId(requestId);
229
+ if (
230
+ this.resultRecords.has(requestId) ||
231
+ this.resultHistory.has(requestId)
232
+ ) {
36
233
  throw new JsonRpcError({
37
- code: -32000,
38
- message: "No signer is connected",
234
+ code: SignerJsonRpcErrorCode.DuplicateRequestId,
235
+ message: "Request ID already exists",
39
236
  });
40
237
  }
41
238
 
42
- return signer;
43
- };
44
-
45
- const handlers = new Map<string, SignerJsonRpcHandler>([
46
- [
47
- "get_info",
48
- (payload) => {
49
- requireParams(payload, 0);
50
- return buildSignerInfo(requireSigner(), getSignerMetadata());
51
- },
52
- ],
53
- [
54
- "connect",
55
- async (payload) => {
56
- const [networkId] = requireParams(payload, 1);
57
- if (typeof networkId !== "string" || !networkId) {
239
+ return this.executeRequest(requestId, async () => {
240
+ signal.throwIfAborted();
241
+ const isGetInfo = payload.method === "get_info";
242
+ if (isGetInfo) {
243
+ if (sessionId !== undefined) {
58
244
  throw new JsonRpcError({
59
- code: -32602,
60
- message: "Invalid network ID",
245
+ code: SignerJsonRpcErrorCode.InvalidParams,
246
+ message: "get_info does not accept a session ID",
61
247
  });
62
248
  }
249
+ } else {
250
+ this.requireSession(sessionId);
251
+ }
63
252
 
64
- await requireConfirmation(confirmRequest, {
65
- method: "connect",
66
- networkId,
253
+ const entry = this.handlers.get(payload.method);
254
+ if (!entry) {
255
+ throw new JsonRpcError({
256
+ code: SignerJsonRpcErrorCode.MethodNotFound,
257
+ message: `Unsupported method: ${payload.method}`,
67
258
  });
259
+ }
260
+ const [paramCount, handler] = entry;
261
+ requireParams(payload, paramCount + 1);
68
262
 
69
- const signer = await connect(networkId);
70
- const actualNetworkId = signerJsonRpcNetworkIdFromAddressPrefix(
71
- signer.client.addressPrefix,
72
- );
73
- if (actualNetworkId !== networkId) {
74
- throw new JsonRpcError({
75
- code: -32002,
76
- message: `Signer uses ${actualNetworkId}, expected ${networkId}`,
77
- });
78
- }
79
-
80
- return null;
81
- },
82
- ],
83
- [
84
- "get_scripts",
85
- async (payload) => {
86
- requireParams(payload, 0);
87
- return (await requireSigner().getAddressObjs()).map(({ script }) =>
88
- JsonRpcTransformers.scriptFrom(script),
89
- );
90
- },
91
- ],
92
- [
93
- "get_native_address",
94
- (payload) => {
95
- requireParams(payload, 0);
96
- return requireSigner().getInternalAddress();
97
- },
98
- ],
99
- [
100
- "get_identity",
101
- (payload) => {
102
- requireParams(payload, 0);
103
- return requireSigner().getIdentity();
104
- },
105
- ],
106
- [
107
- "sign_message",
108
- async (payload) => {
109
- const [message] = requireParams(payload, 1);
110
- const parsedMessage = parseMessageParam(message);
111
- await requireConfirmation(confirmRequest, {
112
- method: "sign_message",
113
- message: SignerJsonRpcTransformers.messageFrom(parsedMessage),
263
+ if (!isGetInfo && payload.method !== "connect" && !this.connected) {
264
+ throw new JsonRpcError({
265
+ code: SignerJsonRpcErrorCode.InvalidState,
266
+ message: "Connect must be approved before this request",
114
267
  });
115
- return requireSigner().signMessageRaw(parsedMessage);
116
- },
117
- ],
118
- [
119
- "prepare_transaction",
120
- async (payload) => {
121
- const [transaction] = requireParams(payload, 1);
122
- return JsonRpcTransformers.transactionFrom(
123
- await requireSigner().prepareTransaction(
124
- parseTransactionParam(transaction),
125
- ),
126
- );
127
- },
128
- ],
129
- [
130
- "sign_transaction",
131
- async (payload) => {
132
- const [transaction] = requireParams(payload, 1);
133
- const parsedTransaction = parseTransactionParam(transaction);
134
- await requireConfirmation(confirmRequest, {
135
- method: "sign_transaction",
136
- transaction: parsedTransaction,
137
- });
138
- return JsonRpcTransformers.transactionFrom(
139
- await requireSigner().signOnlyTransaction(parsedTransaction),
140
- );
141
- },
142
- ],
143
- ]);
144
-
145
- return (payload: JsonRpcPayload) => {
146
- const handler = handlers.get(payload.method);
147
- if (!handler) {
268
+ }
269
+ const result = await handler(params, { signal });
270
+ signal.throwIfAborted();
271
+ return result;
272
+ });
273
+ };
274
+
275
+ private dispose() {
276
+ this.connected = false;
277
+ this.resultRecords.clear();
278
+ this.resultHistory.clear();
279
+ this.abortController.abort(
280
+ new JsonRpcError({
281
+ code: SignerJsonRpcErrorCode.InvalidSession,
282
+ message: "Invalid or expired session",
283
+ }),
284
+ );
285
+ }
286
+
287
+ private requireSession(value: unknown) {
288
+ if (value === this.id) {
289
+ return;
290
+ }
291
+ throw new JsonRpcError({
292
+ code: SignerJsonRpcErrorCode.InvalidSession,
293
+ message: "Invalid or expired session",
294
+ });
295
+ }
296
+
297
+ private requireSigner() {
298
+ const signer = this.config.getSigner();
299
+ if (!signer) {
148
300
  throw new JsonRpcError({
149
- code: -32601,
150
- message: `Unsupported method: ${payload.method}`,
301
+ code: SignerJsonRpcErrorCode.ServerError,
302
+ message: "No signer is connected",
151
303
  });
152
304
  }
153
305
 
154
- return handler(payload);
155
- };
156
- }
306
+ return signer;
307
+ }
157
308
 
158
- async function requireConfirmation(
159
- confirmRequest: SignerJsonRpcHandlerConfig["confirmRequest"],
160
- request: SignerJsonRpcConfirmation,
161
- ) {
162
- if (!(await confirmRequest(request))) {
163
- throw new JsonRpcError({
164
- code: -32003,
165
- message: "User rejected request",
166
- });
309
+ private async requireConfirmation(
310
+ request: SignerJsonRpcConfirmation,
311
+ options?: SignerJsonRpcProviderSessionOptions,
312
+ ) {
313
+ const confirmed = await this.config.confirmRequest(request, options);
314
+ options?.signal?.throwIfAborted();
315
+ if (!confirmed) {
316
+ throw new JsonRpcError({
317
+ code: SignerJsonRpcErrorCode.UserRejected,
318
+ message: "User rejected request",
319
+ });
320
+ }
321
+ }
322
+
323
+ private executeRequest(requestId: string, request: () => unknown) {
324
+ const completion = Promise.resolve().then(request);
325
+ const entry = { completion, retrieved: false };
326
+ this.resultRecords.set(requestId, entry);
327
+ const expire = () => {
328
+ setTimeout(() => {
329
+ if (this.resultRecords.get(requestId) === entry) {
330
+ this.resultRecords.delete(requestId);
331
+ this.resultHistory.set(requestId, entry);
332
+ this.trimResultHistory();
333
+ }
334
+ }, GET_RESULT_CACHE_MS);
335
+ };
336
+ void completion.then(expire, expire);
337
+ return completion;
338
+ }
339
+
340
+ private trimResultHistory() {
341
+ while (this.resultHistory.size > GET_RESULT_HISTORY_CAPACITY) {
342
+ let oldestRetrieved: string | undefined;
343
+ for (const [requestId, entry] of this.resultHistory) {
344
+ if (entry.retrieved) {
345
+ oldestRetrieved = requestId;
346
+ break;
347
+ }
348
+ }
349
+
350
+ this.resultHistory.delete(
351
+ oldestRetrieved ?? this.resultHistory.keys().next().value!,
352
+ );
353
+ }
354
+ }
355
+
356
+ private async getResult(
357
+ entry: SignerJsonRpcResultEntry | undefined,
358
+ signal?: AbortSignal,
359
+ ): Promise<SignerJsonRpcResultRecord> {
360
+ if (!entry) {
361
+ return { status: "not_found" };
362
+ }
363
+
364
+ const controller = new AbortController();
365
+ try {
366
+ const result = await Promise.race([
367
+ entry.completion.then(
368
+ (value) => ({ status: "fulfilled" as const, value }),
369
+ (reason: unknown) => ({ status: "rejected" as const, reason }),
370
+ ),
371
+ sleep(
372
+ GET_RESULT_WAIT_MS,
373
+ signal
374
+ ? abortSignalAny([signal, controller.signal])
375
+ : controller.signal,
376
+ ).then(
377
+ (): typeof GET_RESULT_PENDING_RESULT => GET_RESULT_PENDING_RESULT,
378
+ ),
379
+ ]);
380
+ if (result === GET_RESULT_PENDING_RESULT) {
381
+ return { status: "pending" };
382
+ }
383
+
384
+ entry.retrieved = true;
385
+ if (result.status === "rejected") {
386
+ throw result.reason;
387
+ }
388
+ return { status: "completed", result: result.value };
389
+ } finally {
390
+ controller.abort();
391
+ }
167
392
  }
168
393
  }
169
394
 
@@ -172,7 +397,7 @@ function parseMessageParam(message: unknown) {
172
397
  return SignerJsonRpcTransformers.messageTo(message);
173
398
  } catch (cause) {
174
399
  throw new JsonRpcError({
175
- code: -32602,
400
+ code: SignerJsonRpcErrorCode.InvalidParams,
176
401
  message:
177
402
  cause instanceof Error ? cause.message : "Invalid signer message",
178
403
  });
@@ -186,17 +411,23 @@ function parseTransactionParam(transaction: unknown) {
186
411
  );
187
412
  } catch (cause) {
188
413
  throw new JsonRpcError({
189
- code: -32602,
414
+ code: SignerJsonRpcErrorCode.InvalidParams,
190
415
  message:
191
416
  cause instanceof Error ? cause.message : "Invalid signer transaction",
192
417
  });
193
418
  }
194
419
  }
195
420
 
196
- function requireParams(payload: JsonRpcPayload, count: number) {
197
- if (!Array.isArray(payload.params) || payload.params.length !== count) {
421
+ function requireParams(payload: JsonRpcPayload, count?: number) {
422
+ if (!Array.isArray(payload.params)) {
198
423
  throw new JsonRpcError({
199
- code: -32602,
424
+ code: SignerJsonRpcErrorCode.InvalidParams,
425
+ message: `${payload.method} expects positional parameters`,
426
+ });
427
+ }
428
+ if (count !== undefined && payload.params.length !== count) {
429
+ throw new JsonRpcError({
430
+ code: SignerJsonRpcErrorCode.InvalidParams,
200
431
  message: `${payload.method} expects ${count} parameter${count === 1 ? "" : "s"}`,
201
432
  });
202
433
  }
@@ -204,11 +435,41 @@ function requireParams(payload: JsonRpcPayload, count: number) {
204
435
  return payload.params;
205
436
  }
206
437
 
438
+ function parseRequestMetadata(value: unknown) {
439
+ if (
440
+ typeof value !== "object" ||
441
+ value === null ||
442
+ ("request_id" in value && typeof value.request_id !== "string") ||
443
+ ("session_id" in value && typeof value.session_id !== "string")
444
+ ) {
445
+ throw new JsonRpcError({
446
+ code: SignerJsonRpcErrorCode.InvalidParams,
447
+ message: "Invalid request metadata",
448
+ });
449
+ }
450
+
451
+ return {
452
+ requestId: "request_id" in value ? value.request_id : undefined,
453
+ sessionId: "session_id" in value ? value.session_id : undefined,
454
+ };
455
+ }
456
+
457
+ function requireRequestId(value: unknown): asserts value is string {
458
+ if (typeof value !== "string" || !value) {
459
+ throw new JsonRpcError({
460
+ code: SignerJsonRpcErrorCode.InvalidParams,
461
+ message: "Invalid request ID",
462
+ });
463
+ }
464
+ }
465
+
207
466
  function buildSignerInfo(
467
+ sessionId: string,
208
468
  signer: Signer,
209
469
  metadata: Pick<SignerJsonRpcInfo, "name" | "icon">,
210
470
  ) {
211
471
  return SignerJsonRpcTransformers.infoFrom({
472
+ sessionId,
212
473
  type: signer.type,
213
474
  signType: signer.signType,
214
475
  name: metadata.name,