@stelis/agent-q-core 0.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/README.md +144 -0
- package/dist/adapter-internal.d.ts +5 -0
- package/dist/adapter-internal.js +6 -0
- package/dist/adapter-internal.js.map +1 -0
- package/dist/config.d.ts +74 -0
- package/dist/config.js +489 -0
- package/dist/config.js.map +1 -0
- package/dist/core.d.ts +320 -0
- package/dist/core.js +840 -0
- package/dist/core.js.map +1 -0
- package/dist/device.d.ts +55 -0
- package/dist/device.js +23 -0
- package/dist/device.js.map +1 -0
- package/dist/errors.d.ts +6 -0
- package/dist/errors.js +20 -0
- package/dist/errors.js.map +1 -0
- package/dist/host-output-schema.d.ts +2437 -0
- package/dist/host-output-schema.js +655 -0
- package/dist/host-output-schema.js.map +1 -0
- package/dist/protocol-error.d.ts +4 -0
- package/dist/protocol-error.js +9 -0
- package/dist/protocol-error.js.map +1 -0
- package/dist/protocol-management-primitives.d.ts +27 -0
- package/dist/protocol-management-primitives.js +51 -0
- package/dist/protocol-management-primitives.js.map +1 -0
- package/dist/protocol-primitives.d.ts +53 -0
- package/dist/protocol-primitives.js +331 -0
- package/dist/protocol-primitives.js.map +1 -0
- package/dist/protocol.d.ts +207 -0
- package/dist/protocol.js +897 -0
- package/dist/protocol.js.map +1 -0
- package/dist/provider-protocol.d.ts +262 -0
- package/dist/provider-protocol.js +637 -0
- package/dist/provider-protocol.js.map +1 -0
- package/dist/public-error.d.ts +9 -0
- package/dist/public-error.js +79 -0
- package/dist/public-error.js.map +1 -0
- package/dist/safe-text.d.ts +31 -0
- package/dist/safe-text.js +143 -0
- package/dist/safe-text.js.map +1 -0
- package/dist/transport-invariants.d.ts +18 -0
- package/dist/transport-invariants.js +24 -0
- package/dist/transport-invariants.js.map +1 -0
- package/dist/usb.d.ts +76 -0
- package/dist/usb.js +454 -0
- package/dist/usb.js.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,637 @@
|
|
|
1
|
+
import { MAX_FIRMWARE_NAME_LENGTH, MAX_FIRMWARE_VERSION_LENGTH, MAX_HARDWARE_ID_LENGTH, isDeviceState, isClientName, isProvisioningState, isSafeDeviceId, isSafeRequestId, isSessionId, sanitizeDisplayText, } from "./safe-text.js";
|
|
2
|
+
import { ProtocolError } from "./protocol-error.js";
|
|
3
|
+
import { ED25519_PUBLIC_KEY_BASE64_PATTERN, MAX_ACCOUNTS_PER_RESPONSE, MAX_CAPABILITY_ACCOUNTS_PER_CHAIN, MAX_CAPABILITY_CHAINS, MAX_RAW_PROTOCOL_JSON_BYTES, MAX_SESSION_TTL_MS, MAX_SIGN_RESULT_PAYLOAD_BASE64_CHARS, MAX_SIGNING_CAPABILITIES, MAX_SUI_SIGN_PERSONAL_MESSAGE_BASE64_CHARS, MAX_SUI_SIGN_PERSONAL_MESSAGE_BYTES, MAX_SUI_SIGN_TRANSACTION_TX_BYTES, MAX_SUI_SIGN_TRANSACTION_TX_BYTES_BASE64_CHARS, PROTOCOL_VERSION, HASH_ID_PATTERN, RULE_REF_PATTERN, SIGN_RESULT_ERROR_MESSAGES, SUI_ADDRESS_PATTERN, SUI_CHAIN_ID, SUI_DERIVATION_PATH, SUI_ED25519_SIGNATURE_BASE64_PATTERN, SUI_SIGN_PERSONAL_MESSAGE_METHOD, SUI_SIGN_TRANSACTION_METHOD, SUI_SIGN_TRANSACTION_NETWORKS, consumeProtocolResponseChunk, createRequestId, asRecord, hasOnlyObjectKeys, hasSecretPayloadKey, isRecord, isSuiAddressForPublicKey, rejectSecretPayload, requireOnlyKeys, utf8ByteLength, validateCanonicalBase64Syntax, } from "./protocol-primitives.js";
|
|
4
|
+
import { SIGN_CHAIN_PATTERN, SIGN_METHOD_PATTERN, } from "./protocol-management-primitives.js";
|
|
5
|
+
export { ProtocolError };
|
|
6
|
+
export { ED25519_PUBLIC_KEY_BASE64_PATTERN, MAX_ACCOUNTS_PER_RESPONSE, MAX_CAPABILITY_ACCOUNTS_PER_CHAIN, MAX_CAPABILITY_CHAINS, MAX_RAW_PROTOCOL_JSON_BYTES, MAX_SESSION_TTL_MS, MAX_SIGN_RESULT_PAYLOAD_BASE64_CHARS, MAX_SIGNING_CAPABILITIES, MAX_SUI_SIGN_PERSONAL_MESSAGE_BASE64_CHARS, MAX_SUI_SIGN_PERSONAL_MESSAGE_BYTES, MAX_SUI_SIGN_TRANSACTION_TX_BYTES, MAX_SUI_SIGN_TRANSACTION_TX_BYTES_BASE64_CHARS, PROTOCOL_VERSION, SIGN_RESULT_ERROR_MESSAGES, SUI_ADDRESS_PATTERN, SUI_CHAIN_ID, SUI_DERIVATION_PATH, SUI_ED25519_SIGNATURE_BASE64_PATTERN, SUI_SIGN_PERSONAL_MESSAGE_METHOD, SUI_SIGN_TRANSACTION_METHOD, SUI_SIGN_TRANSACTION_NETWORKS, consumeProtocolResponseChunk, createRequestId, isSuiAddressForPublicKey, };
|
|
7
|
+
export { AGENT_Q_USB_PRODUCT_ID_NUMBER, AGENT_Q_USB_VENDOR_ID_NUMBER, DEFAULT_AGENT_Q_USB_BAUD_RATE, INTERNAL_CONNECT_DEADLINE_MS, INTERNAL_DISCONNECT_DEADLINE_MS, INTERNAL_SIGN_PERSONAL_MESSAGE_DEADLINE_MS, INTERNAL_SIGN_TRANSACTION_DEADLINE_MS, INTERNAL_USB_DEADLINE_MS, MAX_PROTOCOL_RESPONSE_LINE_BYTES, } from "./transport-invariants.js";
|
|
8
|
+
export function makeConnectRequest(clientName, id = createRequestId()) {
|
|
9
|
+
validateRequestId(id);
|
|
10
|
+
if (!isClientName(clientName)) {
|
|
11
|
+
throw new ProtocolError("invalid_client_name", "clientName must be 1-64 printable ASCII characters.");
|
|
12
|
+
}
|
|
13
|
+
return { id, version: PROTOCOL_VERSION, type: "connect", params: { clientName } };
|
|
14
|
+
}
|
|
15
|
+
export function makeDisconnectRequest(sessionId, id = createRequestId()) {
|
|
16
|
+
validateRequestId(id);
|
|
17
|
+
validateSessionId(sessionId);
|
|
18
|
+
return { id, version: PROTOCOL_VERSION, type: "disconnect", sessionId };
|
|
19
|
+
}
|
|
20
|
+
export function makeGetCapabilitiesRequest(sessionId, id = createRequestId()) {
|
|
21
|
+
validateRequestId(id);
|
|
22
|
+
validateSessionId(sessionId);
|
|
23
|
+
return { id, version: PROTOCOL_VERSION, type: "get_capabilities", sessionId };
|
|
24
|
+
}
|
|
25
|
+
export function makeGetAccountsRequest(sessionId, id = createRequestId()) {
|
|
26
|
+
validateRequestId(id);
|
|
27
|
+
validateSessionId(sessionId);
|
|
28
|
+
return { id, version: PROTOCOL_VERSION, type: "get_accounts", sessionId };
|
|
29
|
+
}
|
|
30
|
+
// `id` is required (and not freshly generated): get_result/ack_result target a specific
|
|
31
|
+
// prior request's buffered signing result, so the caller passes that request's id.
|
|
32
|
+
export function makeGetResultRequest(sessionId, id) {
|
|
33
|
+
validateRequestId(id);
|
|
34
|
+
validateSessionId(sessionId);
|
|
35
|
+
return { id, version: PROTOCOL_VERSION, type: "get_result", sessionId };
|
|
36
|
+
}
|
|
37
|
+
export function makeAckResultRequest(sessionId, id) {
|
|
38
|
+
validateRequestId(id);
|
|
39
|
+
validateSessionId(sessionId);
|
|
40
|
+
return { id, version: PROTOCOL_VERSION, type: "ack_result", sessionId };
|
|
41
|
+
}
|
|
42
|
+
export function makeSignTransactionRequest(sessionId, chain, method, params, id = createRequestId()) {
|
|
43
|
+
validateRequestId(id);
|
|
44
|
+
const route = identifySignRoute(SUI_SIGN_TRANSACTION_METHOD, chain, method);
|
|
45
|
+
validateSessionId(sessionId);
|
|
46
|
+
const normalizedParams = validateSignTransactionParams(params);
|
|
47
|
+
const request = {
|
|
48
|
+
id,
|
|
49
|
+
version: PROTOCOL_VERSION,
|
|
50
|
+
type: SUI_SIGN_TRANSACTION_METHOD,
|
|
51
|
+
sessionId,
|
|
52
|
+
chain: route.chain,
|
|
53
|
+
method: route.method,
|
|
54
|
+
params: normalizedParams,
|
|
55
|
+
};
|
|
56
|
+
validateRawRequestSize(request, "sign_transaction");
|
|
57
|
+
return request;
|
|
58
|
+
}
|
|
59
|
+
export function makeSignPersonalMessageRequest(sessionId, chain, method, params, id = createRequestId()) {
|
|
60
|
+
validateRequestId(id);
|
|
61
|
+
const route = identifySignRoute(SUI_SIGN_PERSONAL_MESSAGE_METHOD, chain, method);
|
|
62
|
+
validateSessionId(sessionId);
|
|
63
|
+
const normalizedParams = validateSignPersonalMessageParams(params);
|
|
64
|
+
const request = {
|
|
65
|
+
id,
|
|
66
|
+
version: PROTOCOL_VERSION,
|
|
67
|
+
type: SUI_SIGN_PERSONAL_MESSAGE_METHOD,
|
|
68
|
+
sessionId,
|
|
69
|
+
chain: route.chain,
|
|
70
|
+
method: route.method,
|
|
71
|
+
params: normalizedParams,
|
|
72
|
+
};
|
|
73
|
+
validateRawRequestSize(request, "sign_personal_message");
|
|
74
|
+
return request;
|
|
75
|
+
}
|
|
76
|
+
export function serializeProviderProtocolRequest(request) {
|
|
77
|
+
return `${JSON.stringify(normalizeProviderProtocolRequest(request))}\n`;
|
|
78
|
+
}
|
|
79
|
+
export function parseJsonLine(line) {
|
|
80
|
+
try {
|
|
81
|
+
return JSON.parse(line);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
throw new ProtocolError("invalid_json", "Invalid JSON response.");
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
export function parseProviderProtocolResponse(line, expectedId) {
|
|
88
|
+
const value = parseJsonLine(line);
|
|
89
|
+
if (!isRecord(value)) {
|
|
90
|
+
throw new ProtocolError("protocol_error", "Protocol response must be an object.");
|
|
91
|
+
}
|
|
92
|
+
if (value.version !== PROTOCOL_VERSION) {
|
|
93
|
+
throw new ProtocolError("incompatible_version", "Unsupported protocol response version.");
|
|
94
|
+
}
|
|
95
|
+
if (expectedId !== undefined && value.id !== expectedId) {
|
|
96
|
+
throw new ProtocolError("protocol_error", "Protocol response id does not match request id.");
|
|
97
|
+
}
|
|
98
|
+
if (value.type === "error") {
|
|
99
|
+
return sanitizeProtocolErrorResponse(value);
|
|
100
|
+
}
|
|
101
|
+
if (value.type === "connect_result") {
|
|
102
|
+
return sanitizeConnectResponse(value);
|
|
103
|
+
}
|
|
104
|
+
if (value.type === "disconnect_result") {
|
|
105
|
+
return sanitizeDisconnectResponse(value);
|
|
106
|
+
}
|
|
107
|
+
if (value.type === "capabilities") {
|
|
108
|
+
return sanitizeCapabilitiesResponse(value);
|
|
109
|
+
}
|
|
110
|
+
if (value.type === "accounts") {
|
|
111
|
+
return sanitizeAccountsResponse(value);
|
|
112
|
+
}
|
|
113
|
+
if (value.type === "sign_result") {
|
|
114
|
+
return sanitizeSignResultResponse(value);
|
|
115
|
+
}
|
|
116
|
+
if (value.type === "ack_result") {
|
|
117
|
+
return sanitizeAckResultResponse(value);
|
|
118
|
+
}
|
|
119
|
+
throw new ProtocolError("protocol_error", "Provider protocol response type is unsupported.");
|
|
120
|
+
}
|
|
121
|
+
export function assertConnectResponse(response) {
|
|
122
|
+
if (response.type === "error") {
|
|
123
|
+
throw new ProtocolError(response.error.code, response.error.message);
|
|
124
|
+
}
|
|
125
|
+
if (response.type !== "connect_result") {
|
|
126
|
+
throw new ProtocolError("protocol_error", "Protocol response type is not connect_result.");
|
|
127
|
+
}
|
|
128
|
+
return response;
|
|
129
|
+
}
|
|
130
|
+
export function assertDisconnectResponse(response) {
|
|
131
|
+
if (response.type === "error") {
|
|
132
|
+
throw new ProtocolError(response.error.code, response.error.message);
|
|
133
|
+
}
|
|
134
|
+
if (response.type !== "disconnect_result") {
|
|
135
|
+
throw new ProtocolError("protocol_error", "Protocol response type is not disconnect_result.");
|
|
136
|
+
}
|
|
137
|
+
return response;
|
|
138
|
+
}
|
|
139
|
+
export function assertAckResultResponse(response) {
|
|
140
|
+
if (response.type === "error") {
|
|
141
|
+
throw new ProtocolError(response.error.code, response.error.message);
|
|
142
|
+
}
|
|
143
|
+
if (response.type !== "ack_result") {
|
|
144
|
+
throw new ProtocolError("protocol_error", "Protocol response type is not ack_result.");
|
|
145
|
+
}
|
|
146
|
+
return response;
|
|
147
|
+
}
|
|
148
|
+
export function assertCapabilitiesResponse(response) {
|
|
149
|
+
if (response.type === "error") {
|
|
150
|
+
throw new ProtocolError(response.error.code, response.error.message);
|
|
151
|
+
}
|
|
152
|
+
if (response.type !== "capabilities") {
|
|
153
|
+
throw new ProtocolError("protocol_error", "Protocol response type is not capabilities.");
|
|
154
|
+
}
|
|
155
|
+
return response;
|
|
156
|
+
}
|
|
157
|
+
export function assertAccountsResponse(response) {
|
|
158
|
+
if (response.type === "error") {
|
|
159
|
+
throw new ProtocolError(response.error.code, response.error.message);
|
|
160
|
+
}
|
|
161
|
+
if (response.type !== "accounts") {
|
|
162
|
+
throw new ProtocolError("protocol_error", "Protocol response type is not accounts.");
|
|
163
|
+
}
|
|
164
|
+
return response;
|
|
165
|
+
}
|
|
166
|
+
export function assertSignResultResponse(response) {
|
|
167
|
+
if (response.type === "error") {
|
|
168
|
+
throw new ProtocolError(response.error.code, response.error.message);
|
|
169
|
+
}
|
|
170
|
+
if (response.type !== "sign_result") {
|
|
171
|
+
throw new ProtocolError("protocol_error", "Protocol response type is not sign_result.");
|
|
172
|
+
}
|
|
173
|
+
return response;
|
|
174
|
+
}
|
|
175
|
+
function normalizeProviderProtocolRequest(request) {
|
|
176
|
+
if (!isRecord(request)) {
|
|
177
|
+
throw new ProtocolError("invalid_method", "Provider protocol request must be an object.");
|
|
178
|
+
}
|
|
179
|
+
if (request.version !== PROTOCOL_VERSION) {
|
|
180
|
+
throw new ProtocolError("unsupported_version", "Unsupported provider protocol request version.");
|
|
181
|
+
}
|
|
182
|
+
switch (request.type) {
|
|
183
|
+
case "connect": {
|
|
184
|
+
requireOnlyKeys(request, ["id", "version", "type", "params"], "connect request");
|
|
185
|
+
const params = asRecord(request.params, "connect params");
|
|
186
|
+
requireOnlyKeys(params, ["clientName"], "connect params");
|
|
187
|
+
return makeConnectRequest(params.clientName, request.id);
|
|
188
|
+
}
|
|
189
|
+
case "disconnect":
|
|
190
|
+
requireOnlyKeys(request, ["id", "version", "type", "sessionId"], "disconnect request");
|
|
191
|
+
return makeDisconnectRequest(request.sessionId, request.id);
|
|
192
|
+
case "get_accounts":
|
|
193
|
+
requireOnlyKeys(request, ["id", "version", "type", "sessionId"], "get_accounts request");
|
|
194
|
+
return makeGetAccountsRequest(request.sessionId, request.id);
|
|
195
|
+
case "get_result":
|
|
196
|
+
requireOnlyKeys(request, ["id", "version", "type", "sessionId"], "get_result request");
|
|
197
|
+
return makeGetResultRequest(request.sessionId, request.id);
|
|
198
|
+
case "ack_result":
|
|
199
|
+
requireOnlyKeys(request, ["id", "version", "type", "sessionId"], "ack_result request");
|
|
200
|
+
return makeAckResultRequest(request.sessionId, request.id);
|
|
201
|
+
case "get_capabilities":
|
|
202
|
+
requireOnlyKeys(request, ["id", "version", "type", "sessionId"], "get_capabilities request");
|
|
203
|
+
return makeGetCapabilitiesRequest(request.sessionId, request.id);
|
|
204
|
+
case "sign_personal_message":
|
|
205
|
+
requireOnlyKeys(request, ["id", "version", "type", "sessionId", "chain", "method", "params"], "sign_personal_message request");
|
|
206
|
+
requireOnlyKeys(asRecord(request.params, "sign_personal_message params"), ["network", "message"], "sign_personal_message params");
|
|
207
|
+
return makeSignPersonalMessageRequest(request.sessionId, request.chain, request.method, request.params, request.id);
|
|
208
|
+
case "sign_transaction":
|
|
209
|
+
requireOnlyKeys(request, ["id", "version", "type", "sessionId", "chain", "method", "params"], "sign_transaction request");
|
|
210
|
+
requireOnlyKeys(asRecord(request.params, "sign_transaction params"), ["network", "txBytes"], "sign_transaction params");
|
|
211
|
+
return makeSignTransactionRequest(request.sessionId, request.chain, request.method, request.params, request.id);
|
|
212
|
+
default:
|
|
213
|
+
throw new ProtocolError("invalid_method", "Provider protocol request type is unsupported.");
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
function validateRequestId(id) {
|
|
217
|
+
if (!isSafeRequestId(id)) {
|
|
218
|
+
throw new ProtocolError("invalid_request_id", "Invalid request id.");
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
function validateSessionId(sessionId) {
|
|
222
|
+
if (!isSessionId(sessionId)) {
|
|
223
|
+
throw new ProtocolError("invalid_session", "Invalid sessionId.");
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
export function validateSignTransactionInput(chain, method, params, requestType = "sign_transaction") {
|
|
227
|
+
identifySignRoute(SUI_SIGN_TRANSACTION_METHOD, chain, method);
|
|
228
|
+
return validateSignTransactionParams(params, requestType);
|
|
229
|
+
}
|
|
230
|
+
export function validateSignTransactionParams(params, requestType = "sign_transaction") {
|
|
231
|
+
const normalized = asSignParamsRecord(params, requestType, ["network", "txBytes"]);
|
|
232
|
+
return {
|
|
233
|
+
network: validateSuiNetwork(normalized.network),
|
|
234
|
+
txBytes: validateCanonicalBase64Syntax(normalized.txBytes, MAX_RAW_PROTOCOL_JSON_BYTES, "sui/sign_transaction txBytes"),
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
export function validateSignPersonalMessageInput(chain, method, params, requestType = "sign_personal_message") {
|
|
238
|
+
identifySignRoute(SUI_SIGN_PERSONAL_MESSAGE_METHOD, chain, method);
|
|
239
|
+
return validateSignPersonalMessageParams(params, requestType);
|
|
240
|
+
}
|
|
241
|
+
export function validateSignPersonalMessageParams(params, requestType = "sign_personal_message") {
|
|
242
|
+
const normalized = asSignParamsRecord(params, requestType, ["network", "message"]);
|
|
243
|
+
return {
|
|
244
|
+
network: validateSuiNetwork(normalized.network),
|
|
245
|
+
message: validateCanonicalBase64Syntax(normalized.message, MAX_RAW_PROTOCOL_JSON_BYTES, "sui/sign_personal_message message"),
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
export function identifySignRoute(operation, chain, method) {
|
|
249
|
+
if (typeof chain !== "string" ||
|
|
250
|
+
!SIGN_CHAIN_PATTERN.test(chain) ||
|
|
251
|
+
typeof method !== "string" ||
|
|
252
|
+
!SIGN_METHOD_PATTERN.test(method)) {
|
|
253
|
+
throw new ProtocolError("invalid_params", "Signing route identifiers are invalid.");
|
|
254
|
+
}
|
|
255
|
+
if (chain !== SUI_CHAIN_ID) {
|
|
256
|
+
// TODO: Add a new explicit chain case only when its Firmware adapter,
|
|
257
|
+
// core validation, capabilities, provider surface, tests, docs, and
|
|
258
|
+
// hardware evidence implement the same contract.
|
|
259
|
+
throw new ProtocolError("unsupported_chain", "Signing chain is unsupported.");
|
|
260
|
+
}
|
|
261
|
+
switch (operation) {
|
|
262
|
+
case SUI_SIGN_TRANSACTION_METHOD:
|
|
263
|
+
if (method === SUI_SIGN_TRANSACTION_METHOD) {
|
|
264
|
+
return {
|
|
265
|
+
operation,
|
|
266
|
+
chain: SUI_CHAIN_ID,
|
|
267
|
+
method: SUI_SIGN_TRANSACTION_METHOD,
|
|
268
|
+
route: "sui_sign_transaction",
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
break;
|
|
272
|
+
case SUI_SIGN_PERSONAL_MESSAGE_METHOD:
|
|
273
|
+
if (method === SUI_SIGN_PERSONAL_MESSAGE_METHOD) {
|
|
274
|
+
return {
|
|
275
|
+
operation,
|
|
276
|
+
chain: SUI_CHAIN_ID,
|
|
277
|
+
method: SUI_SIGN_PERSONAL_MESSAGE_METHOD,
|
|
278
|
+
route: "sui_sign_personal_message",
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
throw new ProtocolError("unsupported_method", "Signing method is unsupported.");
|
|
284
|
+
}
|
|
285
|
+
function asSignParamsRecord(value, label, keys) {
|
|
286
|
+
const params = asRecord(value, `${label} params`);
|
|
287
|
+
if (hasSecretPayloadKey(params)) {
|
|
288
|
+
throw new ProtocolError("invalid_params", `${label} params must not include secret material.`);
|
|
289
|
+
}
|
|
290
|
+
requireOnlyKeys(params, keys, `${label} params`);
|
|
291
|
+
return params;
|
|
292
|
+
}
|
|
293
|
+
function validateSuiNetwork(value) {
|
|
294
|
+
if (!SUI_SIGN_TRANSACTION_NETWORKS.includes(value)) {
|
|
295
|
+
throw new ProtocolError("invalid_params", "sui signing network is unsupported.");
|
|
296
|
+
}
|
|
297
|
+
return value;
|
|
298
|
+
}
|
|
299
|
+
function validateRawRequestSize(request, method) {
|
|
300
|
+
if (utf8ByteLength(JSON.stringify(request)) > MAX_RAW_PROTOCOL_JSON_BYTES) {
|
|
301
|
+
throw new ProtocolError("invalid_params", `${method} request is too large for the runtime.`);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
function sanitizeProtocolErrorResponse(value) {
|
|
305
|
+
if (!hasOnlyObjectKeys(value, ["id", "version", "type", "error"])) {
|
|
306
|
+
throw new ProtocolError("protocol_error", "Protocol error response contains unsupported fields.");
|
|
307
|
+
}
|
|
308
|
+
if (value.id !== undefined && typeof value.id !== "string") {
|
|
309
|
+
throw new ProtocolError("protocol_error", "Protocol error response id is malformed.");
|
|
310
|
+
}
|
|
311
|
+
const error = asRecord(value.error, "Protocol error response error");
|
|
312
|
+
if (typeof error.code !== "string" || typeof error.message !== "string") {
|
|
313
|
+
throw new ProtocolError("protocol_error", "Protocol error response is malformed.");
|
|
314
|
+
}
|
|
315
|
+
requireOnlyKeys(error, ["code", "message"], "Protocol error response error");
|
|
316
|
+
return {
|
|
317
|
+
id: value.id,
|
|
318
|
+
version: PROTOCOL_VERSION,
|
|
319
|
+
type: "error",
|
|
320
|
+
error: { code: error.code, message: error.message },
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
function sanitizeConnectResponse(value) {
|
|
324
|
+
if (typeof value.id !== "string") {
|
|
325
|
+
throw new ProtocolError("protocol_error", "Connect response id is malformed.");
|
|
326
|
+
}
|
|
327
|
+
if (value.status === "approved") {
|
|
328
|
+
requireOnlyKeys(value, ["id", "version", "type", "status", "sessionId", "sessionTtlMs", "device"], "Connect approved response");
|
|
329
|
+
const device = sanitizeStrictDeviceStatus(value.device, "Connect response device object");
|
|
330
|
+
if (!isSessionId(value.sessionId)) {
|
|
331
|
+
throw new ProtocolError("protocol_error", "Connect response sessionId is malformed.");
|
|
332
|
+
}
|
|
333
|
+
if (typeof value.sessionTtlMs !== "number" ||
|
|
334
|
+
!Number.isInteger(value.sessionTtlMs) ||
|
|
335
|
+
value.sessionTtlMs <= 0 ||
|
|
336
|
+
value.sessionTtlMs > MAX_SESSION_TTL_MS) {
|
|
337
|
+
throw new ProtocolError("protocol_error", "Connect response sessionTtlMs is malformed.");
|
|
338
|
+
}
|
|
339
|
+
return {
|
|
340
|
+
id: value.id,
|
|
341
|
+
version: PROTOCOL_VERSION,
|
|
342
|
+
type: "connect_result",
|
|
343
|
+
status: "approved",
|
|
344
|
+
sessionId: value.sessionId,
|
|
345
|
+
sessionTtlMs: value.sessionTtlMs,
|
|
346
|
+
device,
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
if (value.status === "rejected") {
|
|
350
|
+
requireOnlyKeys(value, ["id", "version", "type", "status", "error"], "Connect rejected response");
|
|
351
|
+
const error = asRecord(value.error, "Connect rejected response error");
|
|
352
|
+
if (typeof error.code !== "string" || typeof error.message !== "string") {
|
|
353
|
+
throw new ProtocolError("protocol_error", "Connect rejected response error object is malformed.");
|
|
354
|
+
}
|
|
355
|
+
requireOnlyKeys(error, ["code", "message"], "Connect rejected response error");
|
|
356
|
+
return {
|
|
357
|
+
id: value.id,
|
|
358
|
+
version: PROTOCOL_VERSION,
|
|
359
|
+
type: "connect_result",
|
|
360
|
+
status: "rejected",
|
|
361
|
+
error: { code: error.code, message: error.message },
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
throw new ProtocolError("protocol_error", "Connect response status is unsupported.");
|
|
365
|
+
}
|
|
366
|
+
function sanitizeDisconnectResponse(value) {
|
|
367
|
+
requireOnlyKeys(value, ["id", "version", "type", "status"], "Disconnect response");
|
|
368
|
+
if (typeof value.id !== "string" || value.status !== "disconnected") {
|
|
369
|
+
throw new ProtocolError("protocol_error", "Disconnect response is malformed.");
|
|
370
|
+
}
|
|
371
|
+
return { id: value.id, version: PROTOCOL_VERSION, type: "disconnect_result", status: "disconnected" };
|
|
372
|
+
}
|
|
373
|
+
function sanitizeAckResultResponse(value) {
|
|
374
|
+
requireOnlyKeys(value, ["id", "version", "type", "status"], "Ack result response");
|
|
375
|
+
if (typeof value.id !== "string" || value.status !== "acked") {
|
|
376
|
+
throw new ProtocolError("protocol_error", "Ack result response is malformed.");
|
|
377
|
+
}
|
|
378
|
+
return { id: value.id, version: PROTOCOL_VERSION, type: "ack_result", status: "acked" };
|
|
379
|
+
}
|
|
380
|
+
function sanitizeCapabilitiesResponse(value) {
|
|
381
|
+
if (typeof value.id !== "string") {
|
|
382
|
+
throw new ProtocolError("protocol_error", "Capabilities response id is malformed.");
|
|
383
|
+
}
|
|
384
|
+
rejectSecretPayload(value, "Capabilities response");
|
|
385
|
+
requireOnlyKeys(value, ["id", "version", "type", "chains", "signing"], "Capabilities response");
|
|
386
|
+
if (!Array.isArray(value.chains) || value.chains.length !== MAX_CAPABILITY_CHAINS) {
|
|
387
|
+
throw new ProtocolError("protocol_error", "Capabilities response chains are malformed.");
|
|
388
|
+
}
|
|
389
|
+
const signing = sanitizeSigningCapabilities(value.signing);
|
|
390
|
+
return {
|
|
391
|
+
id: value.id,
|
|
392
|
+
version: PROTOCOL_VERSION,
|
|
393
|
+
type: "capabilities",
|
|
394
|
+
chains: value.chains.map((entry) => sanitizeCapabilityChain(entry)),
|
|
395
|
+
...(signing === undefined ? {} : { signing }),
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
function sanitizeAccountsResponse(value) {
|
|
399
|
+
if (typeof value.id !== "string") {
|
|
400
|
+
throw new ProtocolError("protocol_error", "Accounts response id is malformed.");
|
|
401
|
+
}
|
|
402
|
+
rejectSecretPayload(value, "Accounts response");
|
|
403
|
+
requireOnlyKeys(value, ["id", "version", "type", "accounts"], "Accounts response");
|
|
404
|
+
if (!Array.isArray(value.accounts) || value.accounts.length !== MAX_ACCOUNTS_PER_RESPONSE) {
|
|
405
|
+
throw new ProtocolError("protocol_error", "Accounts response accounts are malformed.");
|
|
406
|
+
}
|
|
407
|
+
return {
|
|
408
|
+
id: value.id,
|
|
409
|
+
version: PROTOCOL_VERSION,
|
|
410
|
+
type: "accounts",
|
|
411
|
+
accounts: value.accounts.map((entry) => sanitizeAccount(entry)),
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
function sanitizeCapabilityChain(value) {
|
|
415
|
+
const chain = asRecord(value, "Capability chain entry");
|
|
416
|
+
rejectSecretPayload(chain, "Capability chain entry");
|
|
417
|
+
requireOnlyKeys(chain, ["id", "accounts", "methods"], "Capability chain entry");
|
|
418
|
+
if (chain.id !== SUI_CHAIN_ID) {
|
|
419
|
+
throw new ProtocolError("protocol_error", "Capability chain is unsupported.");
|
|
420
|
+
}
|
|
421
|
+
if (!Array.isArray(chain.accounts) || chain.accounts.length !== MAX_CAPABILITY_ACCOUNTS_PER_CHAIN) {
|
|
422
|
+
throw new ProtocolError("protocol_error", "Capability accounts are malformed.");
|
|
423
|
+
}
|
|
424
|
+
if (!Array.isArray(chain.methods) || chain.methods.length !== 0) {
|
|
425
|
+
throw new ProtocolError("protocol_error", "Capability method is unsupported.");
|
|
426
|
+
}
|
|
427
|
+
return {
|
|
428
|
+
id: SUI_CHAIN_ID,
|
|
429
|
+
accounts: chain.accounts.map((entry) => sanitizeCapabilityAccount(entry)),
|
|
430
|
+
methods: [],
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
function sanitizeCapabilityAccount(value) {
|
|
434
|
+
const account = asRecord(value, "Capability account entry");
|
|
435
|
+
rejectSecretPayload(account, "Capability account entry");
|
|
436
|
+
requireOnlyKeys(account, ["keyScheme", "derivationPath"], "Capability account entry");
|
|
437
|
+
if (account.keyScheme !== "ed25519" || account.derivationPath !== SUI_DERIVATION_PATH) {
|
|
438
|
+
throw new ProtocolError("protocol_error", "Capability account entry is unsupported.");
|
|
439
|
+
}
|
|
440
|
+
return { keyScheme: "ed25519", derivationPath: SUI_DERIVATION_PATH };
|
|
441
|
+
}
|
|
442
|
+
function sanitizeSigningCapabilities(value) {
|
|
443
|
+
if (value === undefined) {
|
|
444
|
+
return undefined;
|
|
445
|
+
}
|
|
446
|
+
const signing = asRecord(value, "Signing capabilities");
|
|
447
|
+
requireOnlyKeys(signing, ["authorization", "methods"], "Signing capabilities");
|
|
448
|
+
if (signing.authorization !== "user" && signing.authorization !== "policy") {
|
|
449
|
+
throw new ProtocolError("protocol_error", "Signing authorization is unsupported.");
|
|
450
|
+
}
|
|
451
|
+
if (!Array.isArray(signing.methods) || signing.methods.length === 0 || signing.methods.length > MAX_SIGNING_CAPABILITIES) {
|
|
452
|
+
throw new ProtocolError("protocol_error", "Signing capabilities are malformed.");
|
|
453
|
+
}
|
|
454
|
+
const methods = signing.methods.map((entry) => sanitizeSigningCapabilityEntry(entry));
|
|
455
|
+
const methodNames = new Set(methods.map((entry) => entry.method));
|
|
456
|
+
if (methodNames.size !== methods.length) {
|
|
457
|
+
throw new ProtocolError("protocol_error", "Signing capability is duplicated.");
|
|
458
|
+
}
|
|
459
|
+
if (signing.authorization === "policy") {
|
|
460
|
+
if (methods.length !== 1 || !methodNames.has(SUI_SIGN_TRANSACTION_METHOD)) {
|
|
461
|
+
throw new ProtocolError("protocol_error", "Signing capability is unsupported.");
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
else if (methods.length !== 2 ||
|
|
465
|
+
!methodNames.has(SUI_SIGN_TRANSACTION_METHOD) ||
|
|
466
|
+
!methodNames.has(SUI_SIGN_PERSONAL_MESSAGE_METHOD)) {
|
|
467
|
+
throw new ProtocolError("protocol_error", "Signing capability is unsupported.");
|
|
468
|
+
}
|
|
469
|
+
return { authorization: signing.authorization, methods };
|
|
470
|
+
}
|
|
471
|
+
function sanitizeSigningCapabilityEntry(value) {
|
|
472
|
+
const entry = asRecord(value, "Signing capability entry");
|
|
473
|
+
rejectSecretPayload(entry, "Signing capability entry");
|
|
474
|
+
requireOnlyKeys(entry, ["chain", "method"], "Signing capability entry");
|
|
475
|
+
if (entry.chain !== SUI_CHAIN_ID ||
|
|
476
|
+
(entry.method !== SUI_SIGN_TRANSACTION_METHOD && entry.method !== SUI_SIGN_PERSONAL_MESSAGE_METHOD)) {
|
|
477
|
+
throw new ProtocolError("protocol_error", "Signing capability is unsupported.");
|
|
478
|
+
}
|
|
479
|
+
return { chain: SUI_CHAIN_ID, method: entry.method };
|
|
480
|
+
}
|
|
481
|
+
function sanitizeAccount(value) {
|
|
482
|
+
const account = asRecord(value, "Account entry");
|
|
483
|
+
rejectSecretPayload(account, "Account entry");
|
|
484
|
+
requireOnlyKeys(account, ["chain", "address", "publicKey", "keyScheme", "derivationPath"], "Account entry");
|
|
485
|
+
if (account.chain !== SUI_CHAIN_ID ||
|
|
486
|
+
typeof account.address !== "string" ||
|
|
487
|
+
!SUI_ADDRESS_PATTERN.test(account.address) ||
|
|
488
|
+
typeof account.publicKey !== "string" ||
|
|
489
|
+
!ED25519_PUBLIC_KEY_BASE64_PATTERN.test(account.publicKey) ||
|
|
490
|
+
!isSuiAddressForPublicKey(account.address, account.publicKey) ||
|
|
491
|
+
account.keyScheme !== "ed25519" ||
|
|
492
|
+
account.derivationPath !== SUI_DERIVATION_PATH) {
|
|
493
|
+
throw new ProtocolError("protocol_error", "Account entry is unsupported.");
|
|
494
|
+
}
|
|
495
|
+
return {
|
|
496
|
+
chain: SUI_CHAIN_ID,
|
|
497
|
+
address: account.address,
|
|
498
|
+
publicKey: account.publicKey,
|
|
499
|
+
keyScheme: "ed25519",
|
|
500
|
+
derivationPath: SUI_DERIVATION_PATH,
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
function sanitizeSignResultResponse(value) {
|
|
504
|
+
if (typeof value.id !== "string" || typeof value.status !== "string") {
|
|
505
|
+
throw new ProtocolError("protocol_error", "Sign result response is malformed.");
|
|
506
|
+
}
|
|
507
|
+
rejectSecretPayload(value, "Sign result response");
|
|
508
|
+
if (value.status === "signed") {
|
|
509
|
+
requireOnlyKeys(value, ["id", "version", "type", "authorization", "status", "chain", "method", "signature", "messageBytes"], "Sign result response");
|
|
510
|
+
if ((value.authorization !== "user" && value.authorization !== "policy") ||
|
|
511
|
+
value.chain !== SUI_CHAIN_ID ||
|
|
512
|
+
typeof value.signature !== "string" ||
|
|
513
|
+
!SUI_ED25519_SIGNATURE_BASE64_PATTERN.test(value.signature)) {
|
|
514
|
+
throw new ProtocolError("protocol_error", "Sign result response is malformed.");
|
|
515
|
+
}
|
|
516
|
+
if (value.method === SUI_SIGN_TRANSACTION_METHOD) {
|
|
517
|
+
if (value.messageBytes !== undefined) {
|
|
518
|
+
throw new ProtocolError("protocol_error", "Sign result response is malformed.");
|
|
519
|
+
}
|
|
520
|
+
return {
|
|
521
|
+
id: value.id,
|
|
522
|
+
version: PROTOCOL_VERSION,
|
|
523
|
+
type: "sign_result",
|
|
524
|
+
authorization: value.authorization,
|
|
525
|
+
status: "signed",
|
|
526
|
+
chain: SUI_CHAIN_ID,
|
|
527
|
+
method: SUI_SIGN_TRANSACTION_METHOD,
|
|
528
|
+
signature: value.signature,
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
if (value.method === SUI_SIGN_PERSONAL_MESSAGE_METHOD) {
|
|
532
|
+
if (value.authorization !== "user") {
|
|
533
|
+
throw new ProtocolError("protocol_error", "Sign result response is malformed.");
|
|
534
|
+
}
|
|
535
|
+
const messageBytes = validateCanonicalBase64Syntax(value.messageBytes, MAX_SIGN_RESULT_PAYLOAD_BASE64_CHARS, "sui/sign_personal_message messageBytes", "protocol_error");
|
|
536
|
+
return {
|
|
537
|
+
id: value.id,
|
|
538
|
+
version: PROTOCOL_VERSION,
|
|
539
|
+
type: "sign_result",
|
|
540
|
+
authorization: "user",
|
|
541
|
+
status: "signed",
|
|
542
|
+
chain: SUI_CHAIN_ID,
|
|
543
|
+
method: SUI_SIGN_PERSONAL_MESSAGE_METHOD,
|
|
544
|
+
signature: value.signature,
|
|
545
|
+
messageBytes,
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
throw new ProtocolError("protocol_error", "Sign result response is malformed.");
|
|
549
|
+
}
|
|
550
|
+
if (value.status === "user_rejected" || value.status === "user_timed_out") {
|
|
551
|
+
requireOnlyKeys(value, ["id", "version", "type", "authorization", "status", "error"], "Sign result response");
|
|
552
|
+
if (value.authorization !== "user") {
|
|
553
|
+
throw new ProtocolError("protocol_error", "Sign result response is malformed.");
|
|
554
|
+
}
|
|
555
|
+
const expectedCode = value.status;
|
|
556
|
+
const error = sanitizeSignResultError(value.error, expectedCode);
|
|
557
|
+
return { id: value.id, version: PROTOCOL_VERSION, type: "sign_result", authorization: "user", status: expectedCode, error };
|
|
558
|
+
}
|
|
559
|
+
if (value.status === "policy_rejected") {
|
|
560
|
+
requireOnlyKeys(value, ["id", "version", "type", "authorization", "status", "policyHash", "ruleRef", "error"], "Sign result response");
|
|
561
|
+
if (value.authorization !== "policy" ||
|
|
562
|
+
typeof value.policyHash !== "string" ||
|
|
563
|
+
!HASH_ID_PATTERN.test(value.policyHash) ||
|
|
564
|
+
typeof value.ruleRef !== "string" ||
|
|
565
|
+
!RULE_REF_PATTERN.test(value.ruleRef)) {
|
|
566
|
+
throw new ProtocolError("protocol_error", "Sign result response is malformed.");
|
|
567
|
+
}
|
|
568
|
+
const error = sanitizeSignResultError(value.error, "policy_rejected");
|
|
569
|
+
return {
|
|
570
|
+
id: value.id,
|
|
571
|
+
version: PROTOCOL_VERSION,
|
|
572
|
+
type: "sign_result",
|
|
573
|
+
authorization: "policy",
|
|
574
|
+
status: "policy_rejected",
|
|
575
|
+
policyHash: value.policyHash,
|
|
576
|
+
ruleRef: value.ruleRef,
|
|
577
|
+
error,
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
if (value.status === "signing_failed") {
|
|
581
|
+
requireOnlyKeys(value, ["id", "version", "type", "authorization", "status", "error"], "Sign result response");
|
|
582
|
+
if (value.authorization !== "user" && value.authorization !== "policy") {
|
|
583
|
+
throw new ProtocolError("protocol_error", "Sign result response is malformed.");
|
|
584
|
+
}
|
|
585
|
+
const error = sanitizeSignResultError(value.error, "signing_failed");
|
|
586
|
+
return {
|
|
587
|
+
id: value.id,
|
|
588
|
+
version: PROTOCOL_VERSION,
|
|
589
|
+
type: "sign_result",
|
|
590
|
+
authorization: value.authorization,
|
|
591
|
+
status: "signing_failed",
|
|
592
|
+
error,
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
throw new ProtocolError("protocol_error", "Sign result response is malformed.");
|
|
596
|
+
}
|
|
597
|
+
function sanitizeSignResultError(value, expectedCode) {
|
|
598
|
+
const error = asRecord(value, "Sign result error");
|
|
599
|
+
requireOnlyKeys(error, ["code", "message"], "Sign result error");
|
|
600
|
+
if (error.code !== expectedCode || error.message !== SIGN_RESULT_ERROR_MESSAGES[expectedCode]) {
|
|
601
|
+
throw new ProtocolError("protocol_error", "Sign result response error is not canonical.");
|
|
602
|
+
}
|
|
603
|
+
return { code: expectedCode, message: SIGN_RESULT_ERROR_MESSAGES[expectedCode] };
|
|
604
|
+
}
|
|
605
|
+
function sanitizeStrictDeviceStatus(value, label) {
|
|
606
|
+
const device = asRecord(value, label);
|
|
607
|
+
requireOnlyKeys(device, ["deviceId", "state", "firmwareName", "hardware", "firmwareVersion"], label);
|
|
608
|
+
if (!isSafeDeviceId(device.deviceId) || !isDeviceState(device.state)) {
|
|
609
|
+
throw new ProtocolError("protocol_error", `${label} is malformed.`);
|
|
610
|
+
}
|
|
611
|
+
return {
|
|
612
|
+
deviceId: device.deviceId,
|
|
613
|
+
state: device.state,
|
|
614
|
+
firmwareName: sanitizeDisplayText(device.firmwareName, MAX_FIRMWARE_NAME_LENGTH),
|
|
615
|
+
hardware: sanitizeDisplayText(device.hardware, MAX_HARDWARE_ID_LENGTH),
|
|
616
|
+
firmwareVersion: sanitizeDisplayText(device.firmwareVersion, MAX_FIRMWARE_VERSION_LENGTH),
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
export function sanitizeDeviceStatus(value) {
|
|
620
|
+
if (!isRecord(value) || !isSafeDeviceId(value.deviceId) || !isDeviceState(value.state)) {
|
|
621
|
+
return null;
|
|
622
|
+
}
|
|
623
|
+
return {
|
|
624
|
+
deviceId: value.deviceId,
|
|
625
|
+
state: value.state,
|
|
626
|
+
firmwareName: sanitizeDisplayText(value.firmwareName, MAX_FIRMWARE_NAME_LENGTH),
|
|
627
|
+
hardware: sanitizeDisplayText(value.hardware, MAX_HARDWARE_ID_LENGTH),
|
|
628
|
+
firmwareVersion: sanitizeDisplayText(value.firmwareVersion, MAX_FIRMWARE_VERSION_LENGTH),
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
export function sanitizeProvisioningStatus(value) {
|
|
632
|
+
if (!isRecord(value) || !isProvisioningState(value.state)) {
|
|
633
|
+
return null;
|
|
634
|
+
}
|
|
635
|
+
return { state: value.state };
|
|
636
|
+
}
|
|
637
|
+
//# sourceMappingURL=provider-protocol.js.map
|