@ledgerhq/hw-app-canton 0.6.0 → 0.7.0-nightly.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/src/Canton.ts CHANGED
@@ -1,19 +1,17 @@
1
1
  import type Transport from "@ledgerhq/hw-transport";
2
- import { UserRefusedAddress, UserRefusedOnDevice } from "@ledgerhq/errors";
2
+ import { TransportStatusError } from "@ledgerhq/errors";
3
3
  import BIPPath from "bip32-path";
4
4
 
5
5
  const CLA = 0xe0;
6
6
 
7
7
  const P1_NON_CONFIRM = 0x00;
8
8
  const P1_CONFIRM = 0x01;
9
+ const P1_SIGN_UNTYPED_VERSIONED_MESSAGE = 0x01;
10
+ const P1_SIGN_PREPARED_TRANSACTION = 0x02;
9
11
 
10
- // P2 indicating no information.
11
12
  const P2_NONE = 0x00;
12
- // P2 indicating first APDU in a large request.
13
13
  const P2_FIRST = 0x01;
14
- // P2 indicating that this is not the last APDU in a large request.
15
14
  const P2_MORE = 0x02;
16
- // P2 indicating that this is the last APDU of a message in a multi message request.
17
15
  const P2_MSG_END = 0x04;
18
16
 
19
17
  const INS = {
@@ -30,6 +28,7 @@ const STATUS = {
30
28
 
31
29
  const ED25519_SIGNATURE_HEX_LENGTH = 128; // hex characters (64 bytes)
32
30
  const CANTON_SIGNATURE_HEX_LENGTH = 132; // hex characters (66 bytes with framing)
31
+ const MAX_APDU_DATA_LENGTH = 255;
33
32
 
34
33
  export type AppConfig = {
35
34
  version: string;
@@ -38,10 +37,25 @@ export type AppConfig = {
38
37
  export type CantonAddress = {
39
38
  publicKey: string;
40
39
  address: string;
41
- path: string; // TODO: check if necessary
40
+ path: string;
42
41
  };
43
42
 
44
- export type CantonSignature = string;
43
+ export type CantonSignature = {
44
+ signature: string;
45
+ applicationSignature?: string;
46
+ };
47
+
48
+ export type CantonPreparedTransaction = {
49
+ damlTransaction: Uint8Array;
50
+ nodes: Uint8Array[];
51
+ metadata: Uint8Array;
52
+ inputContracts: Uint8Array[];
53
+ };
54
+
55
+ export type CantonUntypedVersionedMessage = {
56
+ transactions: string[];
57
+ challenge?: string;
58
+ };
45
59
 
46
60
  /**
47
61
  * Canton BOLOS API
@@ -71,9 +85,10 @@ export default class Canton {
71
85
  const serializedPath = this.serializePath(bipPath);
72
86
 
73
87
  const p1 = display ? P1_CONFIRM : P1_NON_CONFIRM;
74
- const response = await this.transport.send(CLA, INS.GET_ADDR, p1, P2_NONE, serializedPath);
75
88
 
76
- const responseData = this.handleTransportResponse(response, "address");
89
+ const response = await this.transport.send(CLA, INS.GET_ADDR, p1, P2_NONE, serializedPath);
90
+ this.checkTransportResponse(response);
91
+ const responseData = this.extractResponseData(response);
77
92
  const { publicKey } = this.extractPublicKeyAndChainCode(responseData);
78
93
 
79
94
  const address = "canton_" + this.publicKeyToAddress(publicKey);
@@ -86,13 +101,34 @@ export default class Canton {
86
101
  }
87
102
 
88
103
  /**
89
- * Sign a Canton transaction.
104
+ * Sign a Canton transaction
105
+ * using the appropriate signing method based on transaction type.
90
106
  *
91
107
  * @param path a path in BIP-32 format
92
- * @param txHash the transaction hash to sign
108
+ * @param data either prepared transaction components, untyped versioned message, or txHash string (backwards compatibility)
93
109
  * @return the signature
94
110
  */
95
- async signTransaction(path: string, txHash: string): Promise<CantonSignature> {
111
+ async signTransaction(
112
+ path: string,
113
+ data: CantonPreparedTransaction | CantonUntypedVersionedMessage | string,
114
+ ): Promise<CantonSignature> {
115
+ // Backwards compatibility: handle txHash string format
116
+ if (typeof data === "string") {
117
+ return this.signTxHash(path, data);
118
+ }
119
+
120
+ if ("damlTransaction" in data) {
121
+ return this.signPreparedTransaction(path, data);
122
+ } else {
123
+ return this.signUntypedVersionedMessage(path, data);
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Sign a transaction hash (backwards compatibility)
129
+ * @private
130
+ */
131
+ private async signTxHash(path: string, txHash: string): Promise<CantonSignature> {
96
132
  // 1. Send the derivation path
97
133
  const bipPath = BIPPath.fromString(path).toPathArray();
98
134
  const serializedPath = this.serializePath(bipPath);
@@ -100,26 +136,187 @@ export default class Canton {
100
136
  const pathResponse = await this.transport.send(
101
137
  CLA,
102
138
  INS.SIGN,
103
- P1_NON_CONFIRM,
139
+ P1_SIGN_UNTYPED_VERSIONED_MESSAGE,
104
140
  P2_FIRST | P2_MORE,
105
141
  serializedPath,
106
142
  );
107
143
 
108
- this.handleTransportResponse(pathResponse, "transaction");
144
+ this.checkTransportResponse(pathResponse);
145
+
146
+ // 2. Send the transaction hash as a single transaction
147
+ const transactionBuffer = Buffer.from(txHash, "hex");
109
148
 
110
- // 2. Send the transaction hash
111
149
  const response = await this.transport.send(
112
150
  CLA,
113
151
  INS.SIGN,
114
- P1_NON_CONFIRM,
152
+ P1_SIGN_UNTYPED_VERSIONED_MESSAGE,
115
153
  P2_MSG_END,
116
- Buffer.from(txHash, "hex"),
154
+ transactionBuffer,
155
+ );
156
+
157
+ this.checkTransportResponse(response);
158
+ const responseData = this.extractResponseData(response);
159
+ return this.parseSignatureResponse(responseData);
160
+ }
161
+
162
+ /**
163
+ * Sign a prepared Canton transaction
164
+ * @private
165
+ */
166
+ private async signPreparedTransaction(
167
+ path: string,
168
+ components: CantonPreparedTransaction,
169
+ ): Promise<CantonSignature> {
170
+ let responseData: Buffer | null = null;
171
+
172
+ // 1. Send the derivation path
173
+ const bipPath = BIPPath.fromString(path).toPathArray();
174
+ const serializedPath = this.serializePath(bipPath);
175
+
176
+ const pathResponse = await this.transport.send(
177
+ CLA,
178
+ INS.SIGN,
179
+ P1_SIGN_PREPARED_TRANSACTION,
180
+ P2_FIRST | P2_MORE,
181
+ serializedPath,
117
182
  );
118
183
 
119
- const responseData = this.handleTransportResponse(response, "transaction");
120
- const rawSignature = responseData.toString("hex");
184
+ this.checkTransportResponse(pathResponse);
185
+
186
+ // 2. Send the DAML transaction
187
+ await this.sendChunkedData({
188
+ ins: INS.SIGN,
189
+ p1: P1_SIGN_PREPARED_TRANSACTION,
190
+ payload: Buffer.from(components.damlTransaction),
191
+ isFinal: false,
192
+ });
193
+
194
+ // 3. Send each node
195
+ for (const [i, node] of components.nodes.entries()) {
196
+ this.validateUint8Array(node, `Node at index ${i}`);
197
+ await this.sendChunkedData({
198
+ ins: INS.SIGN,
199
+ p1: P1_SIGN_PREPARED_TRANSACTION,
200
+ payload: Buffer.from(node),
201
+ isFinal: false,
202
+ });
203
+ }
204
+
205
+ // 4. Send the metadata
206
+ const isFinal = components.inputContracts.length === 0;
207
+ const result = await this.sendChunkedData({
208
+ ins: INS.SIGN,
209
+ p1: P1_SIGN_PREPARED_TRANSACTION,
210
+ payload: Buffer.from(components.metadata),
211
+ isFinal,
212
+ });
213
+
214
+ if (isFinal) {
215
+ responseData = result;
216
+ }
217
+
218
+ // 5. Send each input contract - last one should return data
219
+ for (const [i, inputContract] of components.inputContracts.entries()) {
220
+ this.validateUint8Array(inputContract, `Input contract at index ${i}`);
121
221
 
122
- return this.cleanSignatureFormat(rawSignature);
222
+ const isFinal = i === components.inputContracts.length - 1;
223
+ const result = await this.sendChunkedData({
224
+ ins: INS.SIGN,
225
+ p1: P1_SIGN_PREPARED_TRANSACTION,
226
+ payload: Buffer.from(inputContract),
227
+ isFinal,
228
+ });
229
+
230
+ if (isFinal) {
231
+ responseData = result;
232
+ }
233
+ }
234
+
235
+ if (!responseData) {
236
+ throw new Error("No response data received from device");
237
+ }
238
+
239
+ return this.parseSignatureResponse(responseData);
240
+ }
241
+
242
+ /**
243
+ * Sign topology transactions for Canton onboarding
244
+ * @private
245
+ */
246
+ private async signUntypedVersionedMessage(
247
+ path: string,
248
+ data: CantonUntypedVersionedMessage,
249
+ ): Promise<CantonSignature> {
250
+ const { transactions, challenge } = data;
251
+
252
+ if (!transactions || transactions.length === 0) {
253
+ throw new TypeError("At least one transaction is required");
254
+ }
255
+
256
+ // 1. Send the derivation path with optional challenge
257
+ const bipPath = BIPPath.fromString(path).toPathArray();
258
+ const serializedPath = this.serializePath(bipPath);
259
+
260
+ let pathData = serializedPath;
261
+ if (challenge) {
262
+ const challengeBuffer = Buffer.from(challenge, "hex");
263
+ pathData = Buffer.concat([serializedPath, challengeBuffer]);
264
+ }
265
+
266
+ const pathResponse = await this.transport.send(
267
+ CLA,
268
+ INS.SIGN,
269
+ P1_SIGN_UNTYPED_VERSIONED_MESSAGE,
270
+ P2_FIRST | P2_MORE,
271
+ pathData,
272
+ );
273
+
274
+ this.checkTransportResponse(pathResponse);
275
+
276
+ // 2. Send each transaction using chunking for large data
277
+ for (const [i, transaction] of transactions.entries()) {
278
+ if (!transaction) {
279
+ throw new TypeError(`Transaction at index ${i} is undefined or null`);
280
+ }
281
+
282
+ const transactionBuffer = Buffer.from(transaction, "hex");
283
+ const isLastTransaction = i === transactions.length - 1;
284
+
285
+ if (transactionBuffer.length <= MAX_APDU_DATA_LENGTH) {
286
+ // Small transaction - send directly
287
+ const p2 = isLastTransaction ? P2_MSG_END : P2_MORE | P2_MSG_END;
288
+
289
+ const response = await this.transport.send(
290
+ CLA,
291
+ INS.SIGN,
292
+ P1_SIGN_UNTYPED_VERSIONED_MESSAGE,
293
+ p2,
294
+ transactionBuffer,
295
+ );
296
+
297
+ if (isLastTransaction) {
298
+ this.checkTransportResponse(response);
299
+ const responseData = this.extractResponseData(response);
300
+ return this.parseSignatureResponse(responseData, challenge);
301
+ } else {
302
+ this.checkTransportResponse(response);
303
+ }
304
+ } else {
305
+ // Large transaction - use chunking
306
+ const responseData = await this.sendChunkedData({
307
+ ins: INS.SIGN,
308
+ p1: P1_SIGN_UNTYPED_VERSIONED_MESSAGE,
309
+ payload: transactionBuffer,
310
+ isFinal: isLastTransaction,
311
+ });
312
+
313
+ if (isLastTransaction && responseData) {
314
+ return this.parseSignatureResponse(responseData, challenge);
315
+ }
316
+ }
317
+ }
318
+
319
+ throw new TypeError("No transactions provided");
123
320
  }
124
321
 
125
322
  /**
@@ -135,7 +332,8 @@ export default class Canton {
135
332
  Buffer.alloc(0),
136
333
  );
137
334
 
138
- const responseData = this.handleTransportResponse(response, "version");
335
+ this.checkTransportResponse(response);
336
+ const responseData = this.extractResponseData(response);
139
337
  const { major, minor, patch } = this.extractVersion(responseData);
140
338
 
141
339
  return {
@@ -144,47 +342,137 @@ export default class Canton {
144
342
  }
145
343
 
146
344
  /**
147
- * Converts 65-byte Canton format to 64-byte Ed25519:
148
- * [40][64_bytes_signature][00] (132 hex chars)
345
+ * Validate Uint8Array with descriptive error message
346
+ * @private
347
+ */
348
+ private validateUint8Array(value: unknown, context: string): void {
349
+ if (!value) {
350
+ throw new TypeError(`${context} is undefined or null`);
351
+ }
352
+ if (!(value instanceof Uint8Array)) {
353
+ throw new TypeError(`${context} is not a Uint8Array: ${typeof value}`);
354
+ }
355
+ }
356
+
357
+ /**
358
+ * Unified chunking strategy for sending data to device
149
359
  * @private
150
360
  */
151
- private cleanSignatureFormat(signature: string): CantonSignature {
361
+ private async sendChunkedData({
362
+ ins,
363
+ p1,
364
+ payload,
365
+ isFinal = false,
366
+ }: {
367
+ ins: number;
368
+ p1: number;
369
+ payload: Buffer;
370
+ isFinal?: boolean;
371
+ }): Promise<Buffer | null> {
372
+ const chunks = this.createChunks(payload);
373
+ let responseData: Buffer | null = null;
374
+
375
+ for (let i = 0; i < chunks.length; i++) {
376
+ const isLastChunk = i === chunks.length - 1;
377
+ let p2 = P2_MORE;
378
+
379
+ if (isLastChunk) {
380
+ p2 = isFinal ? P2_MSG_END : P2_MORE | P2_MSG_END;
381
+ }
382
+
383
+ const response = await this.transport.send(CLA, ins, p1, p2, chunks[i]);
384
+
385
+ if (isFinal && isLastChunk) {
386
+ this.checkTransportResponse(response);
387
+ responseData = this.extractResponseData(response);
388
+ } else {
389
+ this.checkTransportResponse(response);
390
+ }
391
+ }
392
+
393
+ return responseData;
394
+ }
395
+
396
+ /**
397
+ * Create optimized chunks from payload
398
+ * @private
399
+ */
400
+ private createChunks(payload: Buffer): Buffer[] {
401
+ if (payload.length <= MAX_APDU_DATA_LENGTH) {
402
+ return [payload];
403
+ }
404
+
405
+ const totalChunks = Math.ceil(payload.length / MAX_APDU_DATA_LENGTH);
406
+ const chunks: Buffer[] = new Array(totalChunks);
407
+ let offset = 0;
408
+
409
+ for (let i = 0; i < totalChunks; i++) {
410
+ const chunkSize = Math.min(MAX_APDU_DATA_LENGTH, payload.length - offset);
411
+ chunks[i] = payload.slice(offset, offset + chunkSize);
412
+ offset += chunkSize;
413
+ }
414
+
415
+ return chunks;
416
+ }
417
+
418
+ /**
419
+ * Parse signature response - handles both TLV format (onboarding) and single signatures
420
+ * @private
421
+ */
422
+
423
+ private parseSignatureResponse(response: Buffer, challenge?: string): CantonSignature {
424
+ // Handle TLV (Type-Length-Value) format: [40][64B main][00][40][64B challenge] = 262 hex chars (131 bytes)
425
+ if (
426
+ response.length === 131 &&
427
+ response.readUInt8(0) === 0x40 &&
428
+ response.readUInt8(65) === 0x00 &&
429
+ response.readUInt8(66) === 0x40
430
+ ) {
431
+ const signature = response.slice(1, 65).toString("hex");
432
+ const applicationSignature = response.slice(67, 131).toString("hex");
433
+
434
+ return {
435
+ signature,
436
+ ...(challenge && { applicationSignature }),
437
+ };
438
+ }
439
+
440
+ // Handle single signature formats
441
+ const signature = response.toString("hex");
442
+
443
+ // Pure 64-byte Ed25519 signature = 128 hex chars (64 bytes)
152
444
  if (signature.length === ED25519_SIGNATURE_HEX_LENGTH) {
153
- return signature;
445
+ return { signature };
154
446
  }
155
447
 
448
+ // Canton-framed signature: [40][64B Ed25519 sig][00] = 132 hex chars (65 bytes)
156
449
  if (signature.length === CANTON_SIGNATURE_HEX_LENGTH) {
157
450
  const cleanedSignature = signature.slice(2, -2);
158
- return cleanedSignature;
451
+ return { signature: cleanedSignature };
159
452
  }
160
453
 
161
- console.warn(`[Canton]: Unknown signature format (${signature.length} chars)`);
162
- return signature;
454
+ return { signature };
163
455
  }
164
456
 
165
457
  /**
166
- * Helper method to handle transport response and check for errors
458
+ * Check transport response for errors and throw appropriate exceptions
167
459
  * @private
168
460
  */
169
- private handleTransportResponse(
170
- response: Buffer,
171
- errorType: "address" | "transaction" | "version",
172
- ): Buffer {
461
+ private checkTransportResponse(response: Buffer): void {
173
462
  const statusCode = response.readUInt16BE(response.length - 2);
174
- const responseData = response.slice(0, response.length - 2);
175
-
176
- if (statusCode === STATUS.USER_CANCEL) {
177
- switch (errorType) {
178
- case "address":
179
- throw new UserRefusedAddress();
180
- case "transaction":
181
- throw new UserRefusedOnDevice();
182
- default:
183
- throw new Error();
184
- }
463
+
464
+ if (statusCode !== STATUS.OK) {
465
+ throw new TransportStatusError(statusCode);
185
466
  }
467
+ }
186
468
 
187
- return responseData;
469
+ /**
470
+ * Extract response data from transport response
471
+ * APDU responses have format: [data][status_code(2_bytes)]
472
+ * @private
473
+ */
474
+ private extractResponseData(response: Buffer): Buffer {
475
+ return response.slice(0, -2);
188
476
  }
189
477
 
190
478
  /**
@@ -251,9 +539,9 @@ export default class Canton {
251
539
  */
252
540
  private extractVersion(data: Buffer): { major: number; minor: number; patch: number } {
253
541
  return {
254
- major: parseInt(data.subarray(0, 1).toString("hex"), 16),
255
- minor: parseInt(data.subarray(1, 2).toString("hex"), 16),
256
- patch: parseInt(data.subarray(2, 3).toString("hex"), 16),
542
+ major: Number.parseInt(data.subarray(0, 1).toString("hex"), 16),
543
+ minor: Number.parseInt(data.subarray(1, 2).toString("hex"), 16),
544
+ patch: Number.parseInt(data.subarray(2, 3).toString("hex"), 16),
257
545
  };
258
546
  }
259
547
  }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=Canton.integ.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Canton.integ.test.d.ts","sourceRoot":"","sources":["../src/Canton.integ.test.ts"],"names":[],"mappings":""}
@@ -1,88 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
- var __importDefault = (this && this.__importDefault) || function (mod) {
26
- return (mod && mod.__esModule) ? mod : { "default": mod };
27
- };
28
- Object.defineProperty(exports, "__esModule", { value: true });
29
- const hw_transport_node_speculos_http_1 = __importStar(require("@ledgerhq/hw-transport-node-speculos-http"));
30
- const Canton_1 = __importDefault(require("./Canton"));
31
- describe("AppCanton", () => {
32
- let transport;
33
- beforeAll(async () => {
34
- transport = await hw_transport_node_speculos_http_1.default.open({});
35
- });
36
- afterAll(async () => {
37
- transport.close();
38
- });
39
- describe("getAppConfiguration", () => {
40
- it("returns app version", async () => {
41
- // GIVEN
42
- const app = new Canton_1.default(transport);
43
- // WHEN
44
- const result = await app.getAppConfiguration();
45
- // THEN
46
- expect(result).toEqual({
47
- version: "2.2.2",
48
- });
49
- });
50
- });
51
- describe("getAddress", () => {
52
- it("retrieves address from app", async () => {
53
- // GIVEN
54
- const app = new Canton_1.default(transport);
55
- const derivationPath = "44'/6767'/0'/0'/0'";
56
- // WHEN
57
- const result = await app.getAddress(derivationPath);
58
- // THEN
59
- expect(result).toEqual({
60
- address: "canton_1a7a97e0",
61
- publicKey: "c59f7f29374d24506dd6490a5db472cf00958e195e146f3dc9c97f96d5c51097",
62
- });
63
- });
64
- });
65
- describe("signTransaction", () => {
66
- it("returns sign transaction", async () => {
67
- // GIVEN
68
- const app = new Canton_1.default(transport);
69
- const derivationPath = "44'/6767'/0'/0'/0'";
70
- const txHash = "d1e98829444207b0e170346b2e80b58a2ffc602b01e190fb742016d407c84efd";
71
- // WHEN
72
- const signPromise = app.signTransaction(derivationPath, txHash);
73
- // Waiting Speculos receive APDUs
74
- const delay = (ms) => new Promise(f => setTimeout(f, ms));
75
- await delay(500);
76
- // Valid transaction butotn interaction sequence
77
- await transport.button(hw_transport_node_speculos_http_1.SpeculosButton.BOTH);
78
- await transport.button(hw_transport_node_speculos_http_1.SpeculosButton.RIGHT);
79
- await transport.button(hw_transport_node_speculos_http_1.SpeculosButton.RIGHT);
80
- await transport.button(hw_transport_node_speculos_http_1.SpeculosButton.RIGHT);
81
- await transport.button(hw_transport_node_speculos_http_1.SpeculosButton.BOTH);
82
- const result = await signPromise;
83
- // THEN
84
- expect(result).toEqual("40a65f53c3657bc04efefb67a425ba093a5cb5391d18142f148bb2c48daacf316114cff920a58d5996ca828c7ce265f537f1d7fca8fa82c3c73bd944a96e701a0000");
85
- });
86
- });
87
- });
88
- //# sourceMappingURL=Canton.integ.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Canton.integ.test.js","sourceRoot":"","sources":["../src/Canton.integ.test.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,6GAAkG;AAClG,sDAA8B;AAE9B,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;IACzB,IAAI,SAAgC,CAAC;IAErC,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,SAAS,GAAG,MAAM,yCAAqB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IACH,QAAQ,CAAC,KAAK,IAAI,EAAE;QAClB,SAAS,CAAC,KAAK,EAAE,CAAC;IACpB,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;QACnC,EAAE,CAAC,qBAAqB,EAAE,KAAK,IAAI,EAAE;YACnC,QAAQ;YACR,MAAM,GAAG,GAAG,IAAI,gBAAM,CAAC,SAAS,CAAC,CAAC;YAElC,OAAO;YACP,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,mBAAmB,EAAE,CAAC;YAE/C,OAAO;YACP,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC;gBACrB,OAAO,EAAE,OAAO;aACjB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;QAC1B,EAAE,CAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;YAC1C,QAAQ;YACR,MAAM,GAAG,GAAG,IAAI,gBAAM,CAAC,SAAS,CAAC,CAAC;YAClC,MAAM,cAAc,GAAG,oBAAoB,CAAC;YAE5C,OAAO;YACP,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;YAEpD,OAAO;YACP,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC;gBACrB,OAAO,EAAE,iBAAiB;gBAC1B,SAAS,EAAE,kEAAkE;aAC9E,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;QAC/B,EAAE,CAAC,0BAA0B,EAAE,KAAK,IAAI,EAAE;YACxC,QAAQ;YACR,MAAM,GAAG,GAAG,IAAI,gBAAM,CAAC,SAAS,CAAC,CAAC;YAClC,MAAM,cAAc,GAAG,oBAAoB,CAAC;YAC5C,MAAM,MAAM,GAAG,kEAAkE,CAAC;YAElF,OAAO;YACP,MAAM,WAAW,GAAG,GAAG,CAAC,eAAe,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;YAEhE,iCAAiC;YACjC,MAAM,KAAK,GAAG,CAAC,EAAU,EAAiB,EAAE,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;YACjF,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;YACjB,gDAAgD;YAChD,MAAM,SAAS,CAAC,MAAM,CAAC,gDAAc,CAAC,IAAI,CAAC,CAAC;YAC5C,MAAM,SAAS,CAAC,MAAM,CAAC,gDAAc,CAAC,KAAK,CAAC,CAAC;YAC7C,MAAM,SAAS,CAAC,MAAM,CAAC,gDAAc,CAAC,KAAK,CAAC,CAAC;YAC7C,MAAM,SAAS,CAAC,MAAM,CAAC,gDAAc,CAAC,KAAK,CAAC,CAAC;YAC7C,MAAM,SAAS,CAAC,MAAM,CAAC,gDAAc,CAAC,IAAI,CAAC,CAAC;YAE5C,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC;YAEjC,OAAO;YACP,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CACpB,sIAAsI,CACvI,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=Canton.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Canton.test.d.ts","sourceRoot":"","sources":["../src/Canton.test.ts"],"names":[],"mappings":""}