@ckb-ccc/core 1.19.1 → 1.20.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 +49 -0
- package/dist/advanced.d.mts +2 -2
- package/dist/advanced.mjs +1 -1
- package/{dist.commonjs/advancedBarrel-CSQ2SmeJ.d.ts → dist/advancedBarrel-Di0YBlWc.d.mts} +2029 -1639
- package/dist/advancedBarrel-Di0YBlWc.d.mts.map +1 -0
- package/dist/advancedBarrel.d.mts +2 -2
- package/dist/advancedBarrel.mjs +1 -1
- package/dist/advancedBarrel.mjs.map +1 -1
- package/dist/barrel-G6e8gZrB.mjs +7 -0
- package/dist/barrel-G6e8gZrB.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-DxPrDQko.mjs +2 -0
- package/dist/transformers-DxPrDQko.mjs.map +1 -0
- package/dist.commonjs/advanced.d.ts +2 -2
- package/dist.commonjs/advanced.js +1 -1
- package/{dist/advancedBarrel-BwJIDnvL.d.mts → dist.commonjs/advancedBarrel-B57gAzdK.d.ts} +2030 -1638
- package/dist.commonjs/advancedBarrel-B57gAzdK.d.ts.map +1 -0
- package/dist.commonjs/advancedBarrel.d.ts +2 -2
- package/dist.commonjs/advancedBarrel.js +1 -1
- package/dist.commonjs/advancedBarrel.js.map +1 -1
- package/dist.commonjs/barrel-BOCFtFjq.js +23 -0
- package/dist.commonjs/barrel-BOCFtFjq.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/{rolldown-runtime-DWNI8pZO.js → rolldown-runtime-Chgba0Kb.js} +1 -1
- package/dist.commonjs/transformers-0FqvcPy2.js +2 -0
- package/dist.commonjs/transformers-0FqvcPy2.js.map +1 -0
- package/package.json +8 -7
- package/src/advancedBarrel.ts +1 -0
- package/src/bytes/advanced.ts +1 -6
- package/src/bytes/index.ts +37 -10
- package/src/ckb/transaction.ts +7 -5
- package/src/client/client.ts +75 -19
- package/src/client/clientPublicMainnet.ts +75 -36
- package/src/client/clientPublicTestnet.ts +75 -36
- package/src/client/jsonRpc/client.ts +77 -30
- package/src/client/jsonRpc/transformers.ts +4 -4
- package/src/jsonRpc/index.ts +1 -0
- package/src/jsonRpc/requestor.ts +74 -21
- package/src/jsonRpc/transports/advanced.ts +0 -4
- package/src/jsonRpc/transports/factory.ts +12 -5
- package/src/jsonRpc/transports/fallback.ts +9 -5
- package/src/jsonRpc/transports/http.ts +21 -17
- package/src/jsonRpc/transports/index.ts +4 -0
- package/src/jsonRpc/transports/transport.ts +32 -6
- package/src/jsonRpc/transports/webSocket.ts +67 -20
- package/src/keystore/index.ts +3 -3
- package/src/molecule/codec.ts +20 -17
- package/src/signer/advanced.ts +1 -0
- package/src/signer/btc/verify.ts +2 -2
- package/src/signer/ckb/secp256k1Signing.ts +3 -3
- package/src/signer/doge/signerDoge.ts +1 -1
- package/src/signer/doge/signerDogePrivateKey.ts +1 -1
- package/src/signer/index.ts +1 -0
- package/src/signer/jsonRpc/handler.ts +217 -0
- package/src/signer/jsonRpc/index.ts +2 -0
- package/src/signer/jsonRpc/network.ts +15 -0
- package/src/signer/jsonRpc/signer.ts +234 -0
- package/src/signer/jsonRpc/transformers.ts +67 -0
- package/src/utils/abortSignal.ts +42 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/owner/aggregated.ts +56 -0
- package/src/utils/owner/index.ts +4 -0
- package/src/utils/owner/owner.ts +141 -0
- package/src/utils/owner/refCount.ts +78 -0
- package/src/utils/owner/unique.ts +30 -0
- package/tsdown.config.mts +1 -0
- package/dist/address.advanced-Cka_9tcR.mjs +0 -2
- package/dist/address.advanced-Cka_9tcR.mjs.map +0 -1
- package/dist/advancedBarrel-BwJIDnvL.d.mts.map +0 -1
- package/dist/barrel-DsK2p-Rh.mjs +0 -7
- package/dist/barrel-DsK2p-Rh.mjs.map +0 -1
- package/dist.commonjs/address.advanced-BTrmYDic.js +0 -2
- package/dist.commonjs/address.advanced-BTrmYDic.js.map +0 -1
- package/dist.commonjs/advancedBarrel-CSQ2SmeJ.d.ts.map +0 -1
- package/dist.commonjs/barrel-D8_wJLfr.js +0 -23
- package/dist.commonjs/barrel-D8_wJLfr.js.map +0 -1
package/src/molecule/codec.ts
CHANGED
|
@@ -85,7 +85,7 @@ export function fixedItemVec<Encodable, Decoded>(
|
|
|
85
85
|
`fixedItemVec: too short buffer, expected at least 4 bytes, but got ${value.byteLength}`,
|
|
86
86
|
);
|
|
87
87
|
}
|
|
88
|
-
const itemCount = uint32From(value.
|
|
88
|
+
const itemCount = uint32From(value.subarray(0, 4));
|
|
89
89
|
const byteLength = 4 + itemCount * itemByteLength;
|
|
90
90
|
if (value.byteLength !== byteLength) {
|
|
91
91
|
throw new Error(
|
|
@@ -98,7 +98,7 @@ export function fixedItemVec<Encodable, Decoded>(
|
|
|
98
98
|
for (let offset = 4; offset < byteLength; offset += itemByteLength) {
|
|
99
99
|
decodedArray.push(
|
|
100
100
|
itemCodec.decode(
|
|
101
|
-
value.
|
|
101
|
+
value.subarray(offset, offset + itemByteLength),
|
|
102
102
|
config,
|
|
103
103
|
),
|
|
104
104
|
);
|
|
@@ -145,7 +145,7 @@ export function dynItemVec<Encodable, Decoded>(
|
|
|
145
145
|
`dynItemVec: too short buffer, expected at least 4 bytes, but got ${value.byteLength}`,
|
|
146
146
|
);
|
|
147
147
|
}
|
|
148
|
-
const byteLength = uint32From(value.
|
|
148
|
+
const byteLength = uint32From(value.subarray(0, 4));
|
|
149
149
|
if (byteLength !== value.byteLength) {
|
|
150
150
|
throw new Error(
|
|
151
151
|
`dynItemVec: invalid buffer size, expected ${byteLength}, but got ${value.byteLength}`,
|
|
@@ -156,10 +156,10 @@ export function dynItemVec<Encodable, Decoded>(
|
|
|
156
156
|
return [];
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
-
const offset = uint32From(value.
|
|
159
|
+
const offset = uint32From(value.subarray(4, 8));
|
|
160
160
|
const itemCount = (offset - 4) / 4;
|
|
161
161
|
const offsets = Array.from(new Array(itemCount), (_, index) =>
|
|
162
|
-
uint32From(value.
|
|
162
|
+
uint32From(value.subarray(4 + index * 4, 8 + index * 4)),
|
|
163
163
|
);
|
|
164
164
|
offsets.push(byteLength);
|
|
165
165
|
try {
|
|
@@ -167,7 +167,7 @@ export function dynItemVec<Encodable, Decoded>(
|
|
|
167
167
|
for (let index = 0; index < offsets.length - 1; index++) {
|
|
168
168
|
const start = offsets[index];
|
|
169
169
|
const end = offsets[index + 1];
|
|
170
|
-
const itemBuffer = value.
|
|
170
|
+
const itemBuffer = value.subarray(start, end);
|
|
171
171
|
decodedArray.push(itemCodec.decode(itemBuffer, config));
|
|
172
172
|
}
|
|
173
173
|
return decodedArray;
|
|
@@ -250,14 +250,14 @@ export function byteVec<Encodable, Decoded>(
|
|
|
250
250
|
`byteVec: too short buffer, expected at least 4 bytes, but got ${value.byteLength}`,
|
|
251
251
|
);
|
|
252
252
|
}
|
|
253
|
-
const byteLength = uint32From(value.
|
|
253
|
+
const byteLength = uint32From(value.subarray(0, 4));
|
|
254
254
|
if (byteLength !== value.byteLength - 4) {
|
|
255
255
|
throw new Error(
|
|
256
256
|
`byteVec: invalid buffer size, expected ${byteLength}, but got ${value.byteLength}`,
|
|
257
257
|
);
|
|
258
258
|
}
|
|
259
259
|
try {
|
|
260
|
-
return codec.decode(value.
|
|
260
|
+
return codec.decode(value.subarray(4), config);
|
|
261
261
|
} catch (e: unknown) {
|
|
262
262
|
throw new Error(`byteVec - ${getMessage(e)}`, { cause: e });
|
|
263
263
|
}
|
|
@@ -331,8 +331,8 @@ export function table<
|
|
|
331
331
|
`table: too short buffer, expected at least 4 bytes, but got ${value.byteLength}`,
|
|
332
332
|
);
|
|
333
333
|
}
|
|
334
|
-
const byteLength = uint32From(value.
|
|
335
|
-
const headerLength = uint32From(value.
|
|
334
|
+
const byteLength = uint32From(value.subarray(0, 4));
|
|
335
|
+
const headerLength = uint32From(value.subarray(4, 8));
|
|
336
336
|
const actualFieldCount = (headerLength - 4) / 4;
|
|
337
337
|
|
|
338
338
|
if (byteLength !== value.byteLength) {
|
|
@@ -353,12 +353,12 @@ export function table<
|
|
|
353
353
|
);
|
|
354
354
|
}
|
|
355
355
|
const offsets = keys.map((_, index) =>
|
|
356
|
-
uint32From(value.
|
|
356
|
+
uint32From(value.subarray(4 + index * 4, 8 + index * 4)),
|
|
357
357
|
);
|
|
358
358
|
// If there are extra fields, add the last offset to the offsets array
|
|
359
359
|
if (actualFieldCount > keys.length) {
|
|
360
360
|
offsets.push(
|
|
361
|
-
uint32From(value.
|
|
361
|
+
uint32From(value.subarray(4 + keys.length * 4, 8 + keys.length * 4)),
|
|
362
362
|
);
|
|
363
363
|
} else {
|
|
364
364
|
// If there are no extra fields, add the byte length to the offsets array
|
|
@@ -370,7 +370,7 @@ export function table<
|
|
|
370
370
|
const end = offsets[i + 1];
|
|
371
371
|
const field = keys[i];
|
|
372
372
|
const codec = codecLayout[field];
|
|
373
|
-
const payload = value.
|
|
373
|
+
const payload = value.subarray(start, end);
|
|
374
374
|
try {
|
|
375
375
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
376
376
|
Object.assign(object, { [field]: codec.decode(payload, config) });
|
|
@@ -501,7 +501,7 @@ export function union<T extends Record<string, CodecLike<any, any>>>(
|
|
|
501
501
|
},
|
|
502
502
|
decode(buffer, config) {
|
|
503
503
|
const value = bytesFrom(buffer);
|
|
504
|
-
const fieldIndex = uint32From(value.
|
|
504
|
+
const fieldIndex = uint32From(value.subarray(0, 4));
|
|
505
505
|
const keys = Object.keys(codecLayout);
|
|
506
506
|
|
|
507
507
|
const field = (() => {
|
|
@@ -529,7 +529,7 @@ export function union<T extends Record<string, CodecLike<any, any>>>(
|
|
|
529
529
|
return {
|
|
530
530
|
type: field,
|
|
531
531
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
532
|
-
value: codecLayout[field].decode(value.
|
|
532
|
+
value: codecLayout[field].decode(value.subarray(4), config),
|
|
533
533
|
} as UnionDecoded<T>;
|
|
534
534
|
},
|
|
535
535
|
});
|
|
@@ -573,7 +573,7 @@ export function struct<
|
|
|
573
573
|
const object = {};
|
|
574
574
|
let offset = 0;
|
|
575
575
|
Object.entries(codecLayout).forEach(([key, codec]) => {
|
|
576
|
-
const payload = value.
|
|
576
|
+
const payload = value.subarray(offset, offset + codec.byteLength!);
|
|
577
577
|
try {
|
|
578
578
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
579
579
|
Object.assign(object, { [key]: codec.decode(payload, config) });
|
|
@@ -627,7 +627,10 @@ export function array<Encodable, Decoded>(
|
|
|
627
627
|
const result: Array<Decoded> = [];
|
|
628
628
|
for (let i = 0; i < value.byteLength; i += itemCodec.byteLength!) {
|
|
629
629
|
result.push(
|
|
630
|
-
itemCodec.decode(
|
|
630
|
+
itemCodec.decode(
|
|
631
|
+
value.subarray(i, i + itemCodec.byteLength!),
|
|
632
|
+
config,
|
|
633
|
+
),
|
|
631
634
|
);
|
|
632
635
|
}
|
|
633
636
|
return result;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./jsonRpc/transformers.js";
|
package/src/signer/btc/verify.ts
CHANGED
|
@@ -78,7 +78,7 @@ export function btcP2pkhAddressFromPublicKey(
|
|
|
78
78
|
* @public
|
|
79
79
|
*/
|
|
80
80
|
export function btcPublicKeyFromP2pkhAddress(address: string): Hex {
|
|
81
|
-
return hexFrom(bs58check.decode(address).
|
|
81
|
+
return hexFrom(bs58check.decode(address).subarray(1));
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
/**
|
|
@@ -92,7 +92,7 @@ export function verifyMessageBtcEcdsa(
|
|
|
92
92
|
const challenge =
|
|
93
93
|
typeof message === "string" ? message : hexFrom(message).slice(2);
|
|
94
94
|
|
|
95
|
-
const rawSign = bytesFrom(signature, "base64").
|
|
95
|
+
const rawSign = bytesFrom(signature, "base64").subarray(1);
|
|
96
96
|
|
|
97
97
|
return secp256k1.verify(
|
|
98
98
|
bytesFrom(rawSign),
|
|
@@ -21,7 +21,7 @@ export function signMessageSecp256k1(
|
|
|
21
21
|
format: "recovered",
|
|
22
22
|
prehash: false,
|
|
23
23
|
});
|
|
24
|
-
return hexFrom(bytesConcat(signature.
|
|
24
|
+
return hexFrom(bytesConcat(signature.subarray(1), signature.subarray(0, 1)));
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
/**
|
|
@@ -40,7 +40,7 @@ export function verifyMessageSecp256k1(
|
|
|
40
40
|
): boolean {
|
|
41
41
|
const signatureBytes = bytesFrom(signature);
|
|
42
42
|
return secp256k1.verify(
|
|
43
|
-
bytesConcat(signatureBytes.
|
|
43
|
+
bytesConcat(signatureBytes.subarray(64), signatureBytes.subarray(0, 64)),
|
|
44
44
|
bytesFrom(message),
|
|
45
45
|
bytesFrom(publicKey),
|
|
46
46
|
{ format: "recovered", prehash: false },
|
|
@@ -62,7 +62,7 @@ export function recoverMessageSecp256k1(
|
|
|
62
62
|
const signatureBytes = bytesFrom(signature);
|
|
63
63
|
return hexFrom(
|
|
64
64
|
secp256k1.recoverPublicKey(
|
|
65
|
-
bytesConcat(signatureBytes.
|
|
65
|
+
bytesConcat(signatureBytes.subarray(64), signatureBytes.subarray(0, 64)),
|
|
66
66
|
bytesFrom(message),
|
|
67
67
|
{ prehash: false },
|
|
68
68
|
),
|
|
@@ -53,7 +53,7 @@ export abstract class SignerDoge extends Signer {
|
|
|
53
53
|
* @returns A promise that resolves to an array of Address objects.
|
|
54
54
|
*/
|
|
55
55
|
async getAddressObjs(): Promise<Address[]> {
|
|
56
|
-
const hash = bs58check.decode(await this.getDogeAddress()).
|
|
56
|
+
const hash = bs58check.decode(await this.getDogeAddress()).subarray(1);
|
|
57
57
|
|
|
58
58
|
return [
|
|
59
59
|
await Address.fromKnownScript(
|
package/src/signer/index.ts
CHANGED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import type { Transaction } from "../../ckb/index.js";
|
|
2
|
+
import { JsonRpcTransformers } from "../../client/jsonRpc/advanced.js";
|
|
3
|
+
import { JsonRpcError, type JsonRpcPayload } from "../../jsonRpc/index.js";
|
|
4
|
+
import type { Signer } from "../signer/index.js";
|
|
5
|
+
import { signerJsonRpcNetworkIdFromAddressPrefix } from "./network.js";
|
|
6
|
+
import {
|
|
7
|
+
SignerJsonRpcTransformers,
|
|
8
|
+
type SignerJsonRpcInfo,
|
|
9
|
+
type SignerJsonRpcMessageToSign,
|
|
10
|
+
} from "./transformers.js";
|
|
11
|
+
|
|
12
|
+
export type SignerJsonRpcConfirmation =
|
|
13
|
+
| { method: "connect"; networkId: string }
|
|
14
|
+
| { method: "sign_message"; message: SignerJsonRpcMessageToSign }
|
|
15
|
+
| { method: "sign_transaction"; transaction: Transaction };
|
|
16
|
+
|
|
17
|
+
export type SignerJsonRpcHandlerConfig = {
|
|
18
|
+
getSigner: () => Signer | undefined;
|
|
19
|
+
getSignerMetadata?: () => Pick<SignerJsonRpcInfo, "name" | "icon">;
|
|
20
|
+
/** Connects the requested network and returns its connected Signer. */
|
|
21
|
+
connect: (networkId: string) => Promise<Signer>;
|
|
22
|
+
confirmRequest: (request: SignerJsonRpcConfirmation) => Promise<boolean>;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type SignerJsonRpcHandler = (payload: JsonRpcPayload) => unknown;
|
|
26
|
+
|
|
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) {
|
|
36
|
+
throw new JsonRpcError({
|
|
37
|
+
code: -32000,
|
|
38
|
+
message: "No signer is connected",
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
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) {
|
|
58
|
+
throw new JsonRpcError({
|
|
59
|
+
code: -32602,
|
|
60
|
+
message: "Invalid network ID",
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
await requireConfirmation(confirmRequest, {
|
|
65
|
+
method: "connect",
|
|
66
|
+
networkId,
|
|
67
|
+
});
|
|
68
|
+
|
|
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),
|
|
114
|
+
});
|
|
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) {
|
|
148
|
+
throw new JsonRpcError({
|
|
149
|
+
code: -32601,
|
|
150
|
+
message: `Unsupported method: ${payload.method}`,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return handler(payload);
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
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
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function parseMessageParam(message: unknown) {
|
|
171
|
+
try {
|
|
172
|
+
return SignerJsonRpcTransformers.messageTo(message);
|
|
173
|
+
} catch (cause) {
|
|
174
|
+
throw new JsonRpcError({
|
|
175
|
+
code: -32602,
|
|
176
|
+
message:
|
|
177
|
+
cause instanceof Error ? cause.message : "Invalid signer message",
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function parseTransactionParam(transaction: unknown) {
|
|
183
|
+
try {
|
|
184
|
+
return JsonRpcTransformers.transactionTo(
|
|
185
|
+
transaction as Parameters<typeof JsonRpcTransformers.transactionTo>[0],
|
|
186
|
+
);
|
|
187
|
+
} catch (cause) {
|
|
188
|
+
throw new JsonRpcError({
|
|
189
|
+
code: -32602,
|
|
190
|
+
message:
|
|
191
|
+
cause instanceof Error ? cause.message : "Invalid signer transaction",
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function requireParams(payload: JsonRpcPayload, count: number) {
|
|
197
|
+
if (!Array.isArray(payload.params) || payload.params.length !== count) {
|
|
198
|
+
throw new JsonRpcError({
|
|
199
|
+
code: -32602,
|
|
200
|
+
message: `${payload.method} expects ${count} parameter${count === 1 ? "" : "s"}`,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return payload.params;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function buildSignerInfo(
|
|
208
|
+
signer: Signer,
|
|
209
|
+
metadata: Pick<SignerJsonRpcInfo, "name" | "icon">,
|
|
210
|
+
) {
|
|
211
|
+
return SignerJsonRpcTransformers.infoFrom({
|
|
212
|
+
type: signer.type,
|
|
213
|
+
signType: signer.signType,
|
|
214
|
+
name: metadata.name,
|
|
215
|
+
icon: metadata.icon,
|
|
216
|
+
});
|
|
217
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const CKB_MAINNET_NETWORK_ID = "ckb-mainnet";
|
|
2
|
+
const CKB_TESTNET_NETWORK_ID = "ckb-testnet";
|
|
3
|
+
|
|
4
|
+
// TODO: In the next major version, read the network ID directly from Client
|
|
5
|
+
// instead of inferring it from addressPrefix.
|
|
6
|
+
export function signerJsonRpcNetworkIdFromAddressPrefix(addressPrefix: string) {
|
|
7
|
+
if (addressPrefix === "ckb") {
|
|
8
|
+
return CKB_MAINNET_NETWORK_ID;
|
|
9
|
+
}
|
|
10
|
+
if (addressPrefix === "ckt") {
|
|
11
|
+
return CKB_TESTNET_NETWORK_ID;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
throw new Error(`Unsupported address prefix: ${addressPrefix}`);
|
|
15
|
+
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { Address } from "../../address/index.js";
|
|
2
|
+
import { Script } from "../../ckb/index.js";
|
|
3
|
+
import { Client } from "../../client/index.js";
|
|
4
|
+
import {
|
|
5
|
+
JsonRpcScript,
|
|
6
|
+
JsonRpcTransformers,
|
|
7
|
+
} from "../../client/jsonRpc/advanced.js";
|
|
8
|
+
import { RequestorJsonRpc } from "../../jsonRpc/index.js";
|
|
9
|
+
import { Signer } from "../signer/index.js";
|
|
10
|
+
import { signerJsonRpcNetworkIdFromAddressPrefix } from "./network.js";
|
|
11
|
+
import {
|
|
12
|
+
SignerJsonRpcInfo,
|
|
13
|
+
SignerJsonRpcTransformers,
|
|
14
|
+
} from "./transformers.js";
|
|
15
|
+
|
|
16
|
+
export type SignerJsonRpcConfig = Omit<
|
|
17
|
+
Parameters<typeof RequestorJsonRpc.new>[0],
|
|
18
|
+
"onError"
|
|
19
|
+
> & {
|
|
20
|
+
/** Cleans up integration-owned resources before replacement is announced. */
|
|
21
|
+
disconnectHandler?: () => PromiseLike<void> | void;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export class SignerJsonRpc extends Signer {
|
|
25
|
+
private connecting?: Promise<void>;
|
|
26
|
+
private disconnecting?: Promise<void>;
|
|
27
|
+
private scriptsPromise?: Promise<Script[]>;
|
|
28
|
+
private internalAddressPromise?: Promise<string>;
|
|
29
|
+
private identityPromise?: Promise<string>;
|
|
30
|
+
private readonly replacedListeners = new Set<() => void>();
|
|
31
|
+
|
|
32
|
+
private constructor(
|
|
33
|
+
client: Client,
|
|
34
|
+
readonly requestor: RequestorJsonRpc,
|
|
35
|
+
private info: SignerJsonRpcInfo,
|
|
36
|
+
private readonly disconnectHandler?: () => PromiseLike<void> | void,
|
|
37
|
+
) {
|
|
38
|
+
super(client);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Creates a Signer that borrows an existing Transport. */
|
|
42
|
+
static async new(client: Client, config: SignerJsonRpcConfig) {
|
|
43
|
+
const { disconnectHandler, ...requestorConfig } = config;
|
|
44
|
+
const requestor = RequestorJsonRpc.new(requestorConfig);
|
|
45
|
+
const info = (await requestor.request(
|
|
46
|
+
"get_info",
|
|
47
|
+
[],
|
|
48
|
+
[],
|
|
49
|
+
SignerJsonRpcTransformers.infoTo,
|
|
50
|
+
)) as SignerJsonRpcInfo;
|
|
51
|
+
|
|
52
|
+
return new SignerJsonRpc(client, requestor, info, disconnectHandler);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
get type() {
|
|
56
|
+
return this.info.type;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
get signType() {
|
|
60
|
+
return this.info.signType;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
get url() {
|
|
64
|
+
return this.requestor.url;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
get name() {
|
|
68
|
+
return this.info.name;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
get icon() {
|
|
72
|
+
return this.info.icon;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
connect(): Promise<void> {
|
|
76
|
+
if (this.disconnecting) {
|
|
77
|
+
return this.disconnecting.then(() => this.connect());
|
|
78
|
+
}
|
|
79
|
+
if (this.connecting) {
|
|
80
|
+
return this.connecting;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const connecting = this.requestConnect(
|
|
84
|
+
signerJsonRpcNetworkIdFromAddressPrefix(this.client.addressPrefix),
|
|
85
|
+
).catch((cause: unknown) => {
|
|
86
|
+
if (this.connecting === connecting) {
|
|
87
|
+
this.connecting = undefined;
|
|
88
|
+
}
|
|
89
|
+
throw cause;
|
|
90
|
+
});
|
|
91
|
+
this.connecting = connecting;
|
|
92
|
+
return connecting;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
disconnect(): Promise<void> {
|
|
96
|
+
this.clearReadCache();
|
|
97
|
+
if (this.disconnecting) {
|
|
98
|
+
return this.disconnecting;
|
|
99
|
+
}
|
|
100
|
+
if (!this.connecting) {
|
|
101
|
+
return Promise.resolve();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
this.connecting = undefined;
|
|
105
|
+
this.disconnecting = Promise.resolve()
|
|
106
|
+
.then(() => this.disconnectHandler?.())
|
|
107
|
+
.finally(() => {
|
|
108
|
+
this.replace();
|
|
109
|
+
this.disconnecting = undefined;
|
|
110
|
+
});
|
|
111
|
+
return this.disconnecting;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
onReplaced(listener: () => void) {
|
|
115
|
+
this.replacedListeners.add(listener);
|
|
116
|
+
return () => this.replacedListeners.delete(listener);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
replace() {
|
|
120
|
+
this.connecting = undefined;
|
|
121
|
+
this.clearReadCache();
|
|
122
|
+
const listeners = [...this.replacedListeners];
|
|
123
|
+
this.replacedListeners.clear();
|
|
124
|
+
listeners.forEach((listener) => listener());
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async isConnected() {
|
|
128
|
+
return (
|
|
129
|
+
this.connecting?.then(
|
|
130
|
+
() => true,
|
|
131
|
+
() => false,
|
|
132
|
+
) ?? false
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
getScripts(): Promise<Script[]> {
|
|
137
|
+
if (this.scriptsPromise) {
|
|
138
|
+
return this.scriptsPromise.then((scripts) => [...scripts]);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const pending = this.requestor
|
|
142
|
+
.request("get_scripts", [], [], (scripts: JsonRpcScript[]) =>
|
|
143
|
+
scripts.map((script) => JsonRpcTransformers.scriptTo(script)),
|
|
144
|
+
)
|
|
145
|
+
.catch((cause: unknown) => {
|
|
146
|
+
if (this.scriptsPromise === pending) {
|
|
147
|
+
this.scriptsPromise = undefined;
|
|
148
|
+
}
|
|
149
|
+
throw cause;
|
|
150
|
+
}) as Promise<Script[]>;
|
|
151
|
+
this.scriptsPromise = pending;
|
|
152
|
+
return pending.then((scripts) => [...scripts]);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async getAddressObjs() {
|
|
156
|
+
return (await this.getScripts()).map((script) =>
|
|
157
|
+
Address.fromScript(script, this.client),
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
getInternalAddress(): Promise<string> {
|
|
162
|
+
if (this.internalAddressPromise) {
|
|
163
|
+
return this.internalAddressPromise;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const pending = this.requestor
|
|
167
|
+
.request("get_native_address", [], [])
|
|
168
|
+
.catch((cause: unknown) => {
|
|
169
|
+
if (this.internalAddressPromise === pending) {
|
|
170
|
+
this.internalAddressPromise = undefined;
|
|
171
|
+
}
|
|
172
|
+
throw cause;
|
|
173
|
+
}) as Promise<string>;
|
|
174
|
+
this.internalAddressPromise = pending;
|
|
175
|
+
return pending;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
getIdentity(): Promise<string> {
|
|
179
|
+
if (this.identityPromise) {
|
|
180
|
+
return this.identityPromise;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const pending = this.requestor
|
|
184
|
+
.request("get_identity", [], [])
|
|
185
|
+
.catch((cause: unknown) => {
|
|
186
|
+
if (this.identityPromise === pending) {
|
|
187
|
+
this.identityPromise = undefined;
|
|
188
|
+
}
|
|
189
|
+
throw cause;
|
|
190
|
+
}) as Promise<string>;
|
|
191
|
+
this.identityPromise = pending;
|
|
192
|
+
return pending;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
signMessageRaw = this.buildSender("sign_message", [
|
|
196
|
+
SignerJsonRpcTransformers.messageFrom,
|
|
197
|
+
]) as Signer["signMessageRaw"];
|
|
198
|
+
|
|
199
|
+
prepareTransaction = this.buildSender(
|
|
200
|
+
"prepare_transaction",
|
|
201
|
+
[JsonRpcTransformers.transactionFrom],
|
|
202
|
+
JsonRpcTransformers.transactionTo,
|
|
203
|
+
) as Signer["prepareTransaction"];
|
|
204
|
+
|
|
205
|
+
signOnlyTransaction = this.buildSender(
|
|
206
|
+
"sign_transaction",
|
|
207
|
+
[JsonRpcTransformers.transactionFrom],
|
|
208
|
+
JsonRpcTransformers.transactionTo,
|
|
209
|
+
) as Signer["signOnlyTransaction"];
|
|
210
|
+
|
|
211
|
+
buildSender(
|
|
212
|
+
rpcMethod: Parameters<RequestorJsonRpc["request"]>[0],
|
|
213
|
+
inTransformers?: Parameters<RequestorJsonRpc["request"]>[2],
|
|
214
|
+
outTransformer?: Parameters<RequestorJsonRpc["request"]>[3],
|
|
215
|
+
): (...request: unknown[]) => Promise<unknown> {
|
|
216
|
+
return async (...request: unknown[]) =>
|
|
217
|
+
this.requestor.request(
|
|
218
|
+
rpcMethod,
|
|
219
|
+
request,
|
|
220
|
+
inTransformers,
|
|
221
|
+
outTransformer,
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
private clearReadCache() {
|
|
226
|
+
this.scriptsPromise = undefined;
|
|
227
|
+
this.internalAddressPromise = undefined;
|
|
228
|
+
this.identityPromise = undefined;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
private requestConnect = this.buildSender("connect", []) as (
|
|
232
|
+
networkId: string,
|
|
233
|
+
) => Promise<void>;
|
|
234
|
+
}
|