@ckb-ccc/core 1.21.0 → 1.22.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/CHANGELOG.md +50 -0
- package/README.md +3 -3
- package/dist/advanced.d.mts +1 -1
- package/dist/advanced.mjs +1 -1
- package/dist/{advancedBarrel-B_9PyL2D.d.mts → advancedBarrel-Daj_LJkz.d.mts} +139 -19
- package/dist/advancedBarrel-Daj_LJkz.d.mts.map +1 -0
- package/dist/advancedBarrel.d.mts +1 -1
- package/dist/advancedBarrel.mjs +1 -1
- package/dist/barrel-pRBtHOAW.mjs +7 -0
- package/dist/barrel-pRBtHOAW.mjs.map +1 -0
- package/dist/barrel.d.mts +2 -2
- package/dist/barrel.mjs +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +1 -1
- package/dist/transformers-lavDTtAW.mjs +2 -0
- package/dist/transformers-lavDTtAW.mjs.map +1 -0
- package/dist.commonjs/advanced.d.ts +1 -1
- package/dist.commonjs/advanced.js +1 -1
- package/dist.commonjs/{advancedBarrel-Cll2j9KP.d.ts → advancedBarrel-CJL3rfuW.d.ts} +139 -19
- package/dist.commonjs/advancedBarrel-CJL3rfuW.d.ts.map +1 -0
- package/dist.commonjs/advancedBarrel.d.ts +1 -1
- package/dist.commonjs/advancedBarrel.js +1 -1
- package/dist.commonjs/barrel--PaV2IZx.js +23 -0
- package/dist.commonjs/barrel--PaV2IZx.js.map +1 -0
- package/dist.commonjs/barrel.d.ts +2 -2
- package/dist.commonjs/barrel.js +1 -1
- package/dist.commonjs/index.d.ts +2 -2
- package/dist.commonjs/index.js +1 -1
- package/dist.commonjs/transformers-VBXQGQKe.js +2 -0
- package/dist.commonjs/transformers-VBXQGQKe.js.map +1 -0
- package/package.json +1 -1
- package/src/ckb/transaction.ts +20 -4
- package/src/client/client.ts +8 -2
- package/src/client/clientPublicMainnet.advanced.ts +9 -0
- package/src/client/clientPublicTestnet.advanced.ts +9 -0
- package/src/client/jsonRpc/client.ts +1 -1
- package/src/fixedPoint/index.ts +17 -8
- package/src/jsonRpc/requestor.ts +32 -8
- package/src/jsonRpc/transports/fallback.ts +6 -2
- package/src/jsonRpc/transports/http.ts +20 -17
- package/src/jsonRpc/transports/transport.ts +11 -1
- package/src/jsonRpc/transports/webSocket.ts +87 -27
- package/src/signer/btc/psbt.ts +4 -2
- package/src/signer/jsonRpc/handler.ts +394 -133
- package/src/signer/jsonRpc/signer.ts +178 -34
- package/src/signer/jsonRpc/transformers.ts +4 -0
- package/src/utils/abortSignal.ts +14 -0
- package/src/utils/index.ts +150 -0
- package/dist/advancedBarrel-B_9PyL2D.d.mts.map +0 -1
- package/dist/barrel-CrN8Ibc4.mjs +0 -7
- package/dist/barrel-CrN8Ibc4.mjs.map +0 -1
- package/dist/transformers-7aume1sD.mjs +0 -2
- package/dist/transformers-7aume1sD.mjs.map +0 -1
- package/dist.commonjs/advancedBarrel-Cll2j9KP.d.ts.map +0 -1
- package/dist.commonjs/barrel-BL6-tNI3.js +0 -23
- package/dist.commonjs/barrel-BL6-tNI3.js.map +0 -1
- package/dist.commonjs/transformers-1XBitiyh.js +0 -2
- 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
|
-
|
|
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: (
|
|
22
|
-
|
|
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
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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:
|
|
38
|
-
message: "
|
|
234
|
+
code: SignerJsonRpcErrorCode.DuplicateRequestId,
|
|
235
|
+
message: "Request ID already exists",
|
|
39
236
|
});
|
|
40
237
|
}
|
|
41
238
|
|
|
42
|
-
return
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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:
|
|
60
|
-
message: "
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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:
|
|
150
|
-
message:
|
|
301
|
+
code: SignerJsonRpcErrorCode.ServerError,
|
|
302
|
+
message: "No signer is connected",
|
|
151
303
|
});
|
|
152
304
|
}
|
|
153
305
|
|
|
154
|
-
return
|
|
155
|
-
}
|
|
156
|
-
}
|
|
306
|
+
return signer;
|
|
307
|
+
}
|
|
157
308
|
|
|
158
|
-
async
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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:
|
|
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:
|
|
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
|
|
197
|
-
if (!Array.isArray(payload.params)
|
|
421
|
+
function requireParams(payload: JsonRpcPayload, count?: number) {
|
|
422
|
+
if (!Array.isArray(payload.params)) {
|
|
198
423
|
throw new JsonRpcError({
|
|
199
|
-
code:
|
|
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,
|