@appliedblockchain/silentdatarollup-core 1.0.8 → 1.0.9

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Applied Blockchain Ltd.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,12 +1,21 @@
1
1
  // src/Base.ts
2
2
  import debug2 from "debug";
3
+ import {
4
+ getBytes,
5
+ keccak256 as keccak2563,
6
+ toUtf8Bytes as toUtf8Bytes3,
7
+ TypedDataEncoder
8
+ } from "ethers";
3
9
 
4
10
  // src/constants.ts
11
+ import { keccak256, toUtf8Bytes } from "ethers";
5
12
  var SIGN_RPC_METHODS = [
6
13
  "eth_estimateGas",
7
14
  "eth_getProof",
8
15
  "eth_getTransactionByHash",
9
- "eth_getTransactionReceipt"
16
+ "eth_getTransactionReceipt",
17
+ "eth_getUserOperationReceipt",
18
+ "eth_getUserOperationByHash"
10
19
  ];
11
20
  var eip721Domain = {
12
21
  name: "Silent Data [Rollup]",
@@ -22,11 +31,19 @@ var DEBUG_NAMESPACE = "silentdata:core";
22
31
  var DEBUG_NAMESPACE_SILENTDATA_INTERCEPTOR = "silentdata:interceptor";
23
32
  var HEADER_SIGNATURE = "x-signature";
24
33
  var HEADER_TIMESTAMP = "x-timestamp";
34
+ var HEADER_SIGNATURE_TYPE = "x-signature-type";
25
35
  var HEADER_EIP712_SIGNATURE = "x-eip712-signature";
26
36
  var HEADER_DELEGATE = "x-delegate";
27
37
  var HEADER_DELEGATE_SIGNATURE = "x-delegate-signature";
28
38
  var HEADER_EIP712_DELEGATE_SIGNATURE = "x-eip712-delegate-signature";
29
39
  var HEADER_SIGNER_SWC = "x-signer-swc";
40
+ var HEADER_FROM_BLOCK = "x-from-block";
41
+ var ENTRYPOINT_ADDRESS = "0x34F5Bda45f2Ce00B646BD6B19D0F9817b5D8D398";
42
+ var DEFAULT_USER_OPERATION_RECEIPT_LOOKUP_RANGE = 1024;
43
+ var USER_OPERATION_EVENT_SIGNATURE = "UserOperationEvent(bytes32,address,address,uint256,bool,uint256,uint256)";
44
+ var USER_OPERATION_EVENT_HASH = keccak256(
45
+ toUtf8Bytes(USER_OPERATION_EVENT_SIGNATURE)
46
+ );
30
47
  var DEFAULT_DELEGATE_EXPIRES = 10 * 60 * 60;
31
48
  var DELEGATE_EXPIRATION_THRESHOLD_BUFFER = 5;
32
49
  var WHITELISTED_METHODS = [
@@ -42,19 +59,15 @@ var WHITELISTED_METHODS = [
42
59
  "eth_getCode",
43
60
  "eth_getFilterChanges",
44
61
  "eth_getFilterLogs",
45
- "eth_getHashrate",
46
62
  "eth_getHeaderByHash",
47
63
  "eth_getLogs",
48
64
  "eth_getProof",
49
65
  "eth_getTransactionByHash",
50
66
  "eth_getTransactionCount",
51
67
  "eth_getTransactionReceipt",
52
- "eth_hashrate",
53
68
  "eth_maxPriorityFeePerGas",
54
- "eth_mining",
55
69
  "eth_newBlockFilter",
56
70
  "eth_sendRawTransaction",
57
- "eth_submitTransaction",
58
71
  "eth_syncing",
59
72
  "eth_newFilter",
60
73
  "eth_newPendingTransactionFilter",
@@ -62,21 +75,19 @@ var WHITELISTED_METHODS = [
62
75
  "net_version",
63
76
  "net_peerCount",
64
77
  "web3_clientVersion",
65
- "web3_sha3",
66
- "zkevm_batchNumber",
67
- "zkevm_batchNumberByBlockNumber",
68
- "zkevm_consolidatedBlockNumber",
69
- "zkevm_estimateGasPrice",
70
- "zkevm_getBatchByNumber",
71
- "zkevm_getFullBlockByHash",
72
- "zkevm_getFullBlockByNumber",
73
- "zkevm_getLatestGlobalExitRoot",
74
- "zkevm_isBlockConsolidated",
75
- "zkevm_isBlockVirtualized",
76
- "zkevm_verifiedBatchNumber",
77
- "zkevm_virtualBatchNumber"
78
+ "web3_sha3"
78
79
  ];
79
80
 
81
+ // src/privateEvents.ts
82
+ import { keccak256 as keccak2562, toUtf8Bytes as toUtf8Bytes2 } from "ethers";
83
+ var PRIVATE_EVENT_SIGNATURE = "PrivateEvent(address[],bytes32,bytes)";
84
+ var PRIVATE_EVENT_SIGNATURE_HASH = keccak2562(
85
+ toUtf8Bytes2(PRIVATE_EVENT_SIGNATURE)
86
+ );
87
+ function calculateEventTypeHash(eventSignature) {
88
+ return keccak2562(toUtf8Bytes2(eventSignature));
89
+ }
90
+
80
91
  // src/types.ts
81
92
  var ChainId = /* @__PURE__ */ ((ChainId2) => {
82
93
  ChainId2[ChainId2["MAINNET"] = 380929] = "MAINNET";
@@ -239,7 +250,7 @@ var SilentDataRollupBase = class {
239
250
  this._cachedNetwork = null;
240
251
  this.config = {
241
252
  ...config,
242
- authSignatureType: config.authSignatureType ?? "RAW" /* Raw */
253
+ authSignatureType: config.authSignatureType ?? "EIP191" /* EIP191 */
243
254
  };
244
255
  this.delegateConfig = this.resolveDelegateConfig(config);
245
256
  log2(
@@ -324,37 +335,67 @@ var SilentDataRollupBase = class {
324
335
  /**
325
336
  * Signs a raw delegate header message.
326
337
  * This method can be overridden by extending classes to customize the signing process.
338
+ * The signer implementation decides whether to add EIP-191 prefix or not.
327
339
  * @param provider - The provider used for signing
328
340
  * @param message - The delegate signer message to be signed
341
+ * @param isSWC - Whether signing for smart wallet contract (EIP-1271)
329
342
  * @returns A promise that resolves to the signature string
330
343
  */
331
- async signRawDelegateHeader(provider, message) {
332
- log2("signRawDelegateHeader: Signing raw delegate header", message);
333
- const signature = await provider.signer.signMessage(message);
334
- log2("signRawDelegateHeader: Raw signature generated:", signature);
344
+ async signDelegateHeader(provider, message, isSWC) {
345
+ log2("signDelegateHeader: Signing delegate header", message);
346
+ let bytesToSign;
347
+ if (isSWC) {
348
+ const messageHash = keccak2563(toUtf8Bytes3(message));
349
+ bytesToSign = getBytes(messageHash);
350
+ log2("Signing hash bytes for SWC", messageHash);
351
+ } else {
352
+ bytesToSign = toUtf8Bytes3(message);
353
+ log2("Signing message bytes for EOA", message);
354
+ }
355
+ const signature = await provider.signer.signMessage(bytesToSign);
356
+ log2("signDelegateHeader: Signature generated:", signature);
335
357
  return signature;
336
358
  }
337
359
  /**
338
360
  * Signs a typed delegate header message.
339
361
  * This method can be overridden by extending classes to customize the signing process.
340
362
  * @param provider - The provider used for signing
363
+ * @param chainId - The chain ID
341
364
  * @param message - The delegate signer message to be signed
365
+ * @param isSWC - Whether signing for smart wallet contract (EIP-1271)
342
366
  * @returns A promise that resolves to the signature string
343
367
  */
344
- async signTypedDelegateHeader(provider, chainId, message) {
368
+ async signTypedDelegateHeader(provider, chainId, message, isSWC) {
345
369
  log2("signTypedDelegateHeader: Signing typed delegate header");
346
370
  log2(
347
371
  "signTypedDelegateHeader: Typed message:",
348
372
  JSON.stringify(message, null, 2)
349
373
  );
374
+ const domain = { ...eip721Domain, chainId };
375
+ if (isSWC) {
376
+ const messageHash = TypedDataEncoder.hash(
377
+ domain,
378
+ delegateEIP721Types,
379
+ message
380
+ );
381
+ const hashBytes = getBytes(messageHash);
382
+ const signature2 = await provider.signer.signMessage(hashBytes);
383
+ log2("signTypedDelegateHeader: Typed SWC signature generated:", signature2);
384
+ return signature2;
385
+ }
350
386
  const signature = await provider.signer.signTypedData(
351
- { ...eip721Domain, chainId },
387
+ domain,
352
388
  delegateEIP721Types,
353
389
  message
354
390
  );
355
391
  log2("signTypedDelegateHeader: Signature generated:", signature);
356
392
  return signature;
357
393
  }
394
+ /**
395
+ * IMPORTANT: Return the cached promise (currentPromise), not the resolved value.
396
+ * This ensures multiple concurrent callers share the same in-flight request,
397
+ * preventing redundant API calls.
398
+ */
358
399
  async getDelegateHeaders(provider) {
359
400
  log2("Getting delegate headers");
360
401
  if (!this.delegateHeadersPromise) {
@@ -362,7 +403,9 @@ var SilentDataRollupBase = class {
362
403
  }
363
404
  const currentPromise = this.delegateHeadersPromise;
364
405
  try {
365
- return await currentPromise;
406
+ const delegateHeaders = await currentPromise;
407
+ log2("Delegate headers:", JSON.stringify(delegateHeaders, null, 2));
408
+ return currentPromise;
366
409
  } catch (error) {
367
410
  log2("Error getting delegate headers:", error);
368
411
  throw new Error("Failed to get delegate headers");
@@ -393,13 +436,16 @@ var SilentDataRollupBase = class {
393
436
  [HEADER_DELEGATE]: JSON.stringify(delegateSignerMessage)
394
437
  };
395
438
  const chainId = (await this.getCachedNetwork(provider)).chainId.toString();
439
+ const isSWC = !!this.config.smartWalletAddress;
396
440
  switch (signatureType) {
441
+ case "EIP191" /* EIP191 */:
397
442
  case "RAW" /* Raw */: {
398
- log2("Generating delegate raw signature");
443
+ log2("Generating delegate signature");
399
444
  const delegateMessageToSign = chainId + JSON.stringify(delegateSignerMessage);
400
- headers[HEADER_DELEGATE_SIGNATURE] = await this.signRawDelegateHeader(
445
+ headers[HEADER_DELEGATE_SIGNATURE] = await this.signDelegateHeader(
401
446
  provider,
402
- delegateMessageToSign
447
+ delegateMessageToSign,
448
+ isSWC
403
449
  );
404
450
  break;
405
451
  }
@@ -408,7 +454,8 @@ var SilentDataRollupBase = class {
408
454
  headers[HEADER_EIP712_DELEGATE_SIGNATURE] = await this.signTypedDelegateHeader(
409
455
  provider,
410
456
  chainId,
411
- delegateSignerMessage
457
+ delegateSignerMessage,
458
+ isSWC
412
459
  );
413
460
  break;
414
461
  default:
@@ -429,24 +476,57 @@ var SilentDataRollupBase = class {
429
476
  [HEADER_TIMESTAMP]: xTimestamp
430
477
  };
431
478
  const chainId = (await this.getCachedNetwork(provider)).chainId.toString();
432
- const signatureType = this.config.authSignatureType;
479
+ const signatureType = this.config.authSignatureType ?? "EIP191" /* EIP191 */;
480
+ const isSWC = !!this.config.smartWalletAddress;
481
+ let payloadToSign = payload;
482
+ const isGetUserOperationReceipt = !Array.isArray(payload) && payload.method === "eth_getUserOperationReceipt";
483
+ if (isGetUserOperationReceipt) {
484
+ log2(
485
+ "Detected eth_getUserOperationReceipt, building custom eth_getLogs payload for signing"
486
+ );
487
+ let fromBlock;
488
+ try {
489
+ fromBlock = await this.getFromBlockForUserOperationReceipt(provider);
490
+ headers[HEADER_FROM_BLOCK] = fromBlock.toString();
491
+ log2(`Added ${HEADER_FROM_BLOCK} header:`, fromBlock.toString());
492
+ } catch (error) {
493
+ log2(
494
+ "Error calculating fromBlock for eth_getUserOperationReceipt:",
495
+ error
496
+ );
497
+ throw new Error(
498
+ "Failed to calculate fromBlock for eth_getUserOperationReceipt"
499
+ );
500
+ }
501
+ payloadToSign = this.buildGetUserOperationReceiptSigningPayload(
502
+ payload,
503
+ fromBlock
504
+ );
505
+ log2(
506
+ "Using custom eth_getLogs payload for signing:",
507
+ JSON.stringify(payloadToSign, null, 2)
508
+ );
509
+ }
433
510
  switch (signatureType) {
511
+ case "EIP191" /* EIP191 */:
434
512
  case "RAW" /* Raw */:
435
- log2("Generating auth header raw signature");
436
- headers[HEADER_SIGNATURE] = await this.signAuthHeaderRawMessage(
513
+ log2("Generating auth header signature");
514
+ headers[HEADER_SIGNATURE] = await this.signAuthHeader(
437
515
  provider,
438
- payload,
516
+ payloadToSign,
439
517
  xTimestamp,
440
- chainId
518
+ chainId,
519
+ isSWC
441
520
  );
442
521
  break;
443
522
  case "EIP712" /* EIP712 */:
444
- log2("Generating auth headerEIP712 signature");
445
- headers[HEADER_EIP712_SIGNATURE] = await this.signAuthHeaderTypedData(
523
+ log2("Generating auth header typed signature");
524
+ headers[HEADER_EIP712_SIGNATURE] = await this.signTypedAuthHeader(
446
525
  provider,
447
- payload,
526
+ payloadToSign,
448
527
  xTimestamp,
449
- chainId
528
+ chainId,
529
+ isSWC
450
530
  );
451
531
  break;
452
532
  default:
@@ -455,26 +535,53 @@ var SilentDataRollupBase = class {
455
535
  log2("Auth headers:", JSON.stringify(headers, null, 2));
456
536
  return headers;
457
537
  }
458
- async signAuthHeaderRawMessage(provider, payload, timestamp, chainId) {
459
- const xMessage = this.prepareSignatureMessage(chainId, payload, timestamp);
538
+ /**
539
+ * Signs auth header.
540
+ */
541
+ async signAuthHeader(provider, payload, timestamp, chainId, isSWC) {
542
+ const xMessage = this.prepareMessage(chainId, payload, timestamp);
460
543
  const delegateSigner = await this.getDelegateSigner(this);
461
544
  const signer = delegateSigner ?? provider.signer;
462
- const signature = await this.signMessage(signer, xMessage);
463
- log2("Message signed. Signature:", signature);
545
+ const usingDelegate = !!delegateSigner;
546
+ let bytesToSign;
547
+ if (isSWC && !usingDelegate) {
548
+ const messageHash = keccak2563(toUtf8Bytes3(xMessage));
549
+ bytesToSign = getBytes(messageHash);
550
+ log2("Signing hash bytes for SWC");
551
+ } else {
552
+ bytesToSign = toUtf8Bytes3(xMessage);
553
+ log2("Signing message bytes for EOA");
554
+ }
555
+ const signature = await signer.signMessage(bytesToSign);
556
+ log2("Message signed raw. Signature:", signature);
464
557
  return signature;
465
558
  }
466
- async signAuthHeaderTypedData(provider, payload, timestamp, chainId) {
467
- const message = this.prepareSignatureTypedData(payload, timestamp);
559
+ /**
560
+ * Signs auth header using typed data signature.
561
+ */
562
+ async signTypedAuthHeader(provider, payload, timestamp, chainId, isSWC) {
563
+ const message = this.prepareTypedData(payload, timestamp);
468
564
  const types = getAuthEIP721Types(payload);
469
565
  const delegateSigner = await this.getDelegateSigner(this);
470
566
  const signer = delegateSigner ?? provider.signer;
567
+ const usingDelegate = !!delegateSigner;
471
568
  const domain = { ...eip721Domain, chainId };
569
+ if (isSWC && !usingDelegate) {
570
+ const messageHash2 = TypedDataEncoder.hash(domain, types, message);
571
+ log2("EIP-712 hash (SWC without delegate):", messageHash2);
572
+ const hashBytes = getBytes(messageHash2);
573
+ const signature2 = await signer.signMessage(hashBytes);
574
+ log2("Message signed with EIP-712 for SWC. Signature:", signature2);
575
+ return signature2;
576
+ }
472
577
  log2(
473
578
  "Signing typed data",
474
579
  JSON.stringify({ message, types, domain }, null, 2)
475
580
  );
581
+ const messageHash = TypedDataEncoder.hash(domain, types, message);
582
+ log2("EIP-712 hash (EOA):", messageHash);
476
583
  const signature = await this.signTypedData(signer, domain, types, message);
477
- log2("Message signed. Signature:", signature);
584
+ log2("Message signed with EIP-712. Signature:", signature);
478
585
  return signature;
479
586
  }
480
587
  /**
@@ -506,10 +613,81 @@ var SilentDataRollupBase = class {
506
613
  contractMethodsToSign
507
614
  });
508
615
  }
616
+ /**
617
+ * Calculates the fromBlock value for eth_getUserOperationReceipt requests.
618
+ * Gets the current block number and subtracts the configured userOperationReceiptLookupRange.
619
+ *
620
+ * IMPORTANT: The bundler strictly validates this value and will reject the request if:
621
+ * - The header is missing
622
+ * - The value is < 0
623
+ * - The value is > current block number
624
+ * - The value is too far back (< currentBlock - userOperationReceiptLookupRange)
625
+ *
626
+ * This method ensures the returned value is always within the valid range:
627
+ * max(0, currentBlock - userOperationReceiptLookupRange) <= fromBlock <= currentBlock
628
+ *
629
+ * @param provider - The provider to use for fetching the current block number
630
+ * @returns A promise that resolves to the fromBlock value as a bigint
631
+ * @throws Error if unable to fetch the current block number
632
+ */
633
+ async getFromBlockForUserOperationReceipt(provider) {
634
+ const lookupRange = BigInt(
635
+ this.config.userOperationReceiptLookupRange ?? DEFAULT_USER_OPERATION_RECEIPT_LOOKUP_RANGE
636
+ );
637
+ log2("User operation receipt lookup range:", lookupRange.toString());
638
+ let currentBlockNumber;
639
+ if (typeof provider.getBlockNumber === "function") {
640
+ currentBlockNumber = BigInt(await provider.getBlockNumber());
641
+ } else if (typeof provider.request === "function") {
642
+ const blockNumberHex = await provider.request({
643
+ method: "eth_blockNumber",
644
+ params: []
645
+ });
646
+ currentBlockNumber = BigInt(blockNumberHex);
647
+ } else {
648
+ throw new Error(
649
+ "Provider does not support getBlockNumber or request method"
650
+ );
651
+ }
652
+ log2("Current block number:", currentBlockNumber.toString());
653
+ const fromBlock = currentBlockNumber > lookupRange ? currentBlockNumber - lookupRange : 0n;
654
+ log2("Calculated fromBlock:", fromBlock.toString());
655
+ return fromBlock;
656
+ }
657
+ /**
658
+ * Builds a custom eth_getLogs payload for signing when the original request is eth_getUserOperationReceipt.
659
+ * This method can be overridden to customize the payload construction.
660
+ *
661
+ * IMPORTANT: The bundler must reconstruct this exact payload to verify the signature.
662
+ * The bundler should use the same `id` from the original eth_getUserOperationReceipt request
663
+ * when constructing the eth_getLogs request to send to the RPC node.
664
+ *
665
+ * @param payload - The original eth_getUserOperationReceipt payload
666
+ * @param fromBlock - The fromBlock value to use in the eth_getLogs filter
667
+ * @returns A JsonRpcPayload with method 'eth_getLogs' to be used for signing
668
+ */
669
+ buildGetUserOperationReceiptSigningPayload(payload, fromBlock) {
670
+ return {
671
+ jsonrpc: payload.jsonrpc,
672
+ method: "eth_getLogs",
673
+ params: [
674
+ {
675
+ address: ENTRYPOINT_ADDRESS,
676
+ fromBlock: `0x${fromBlock.toString(16)}`,
677
+ topics: [
678
+ PRIVATE_EVENT_SIGNATURE_HASH,
679
+ USER_OPERATION_EVENT_HASH
680
+ // eventType
681
+ ]
682
+ }
683
+ ],
684
+ id: payload.id ?? 1
685
+ };
686
+ }
509
687
  /**
510
688
  * Prepares the message to be signed for the x-signature header.
511
689
  */
512
- prepareSignatureMessage(chainId, payload, timestamp) {
690
+ prepareMessage(chainId, payload, timestamp) {
513
691
  log2("Preparing raw message for signing", {
514
692
  payload: JSON.stringify(payload, null, 2),
515
693
  timestamp
@@ -522,7 +700,7 @@ var SilentDataRollupBase = class {
522
700
  /**
523
701
  * Prepares the message to be signed for the x-eip712-signature header.
524
702
  */
525
- prepareSignatureTypedData(payload, timestamp) {
703
+ prepareTypedData(payload, timestamp) {
526
704
  log2("Preparing payload for signTypedData");
527
705
  const preparedPayload = Array.isArray(payload) ? payload.map(prepareTypedDataPayload) : prepareTypedDataPayload(payload);
528
706
  const message = {
@@ -535,8 +713,8 @@ var SilentDataRollupBase = class {
535
713
  };
536
714
 
537
715
  // src/contract.ts
538
- import { Contract, Interface } from "ethers";
539
- var SilentDataRollupContract = class extends Contract {
716
+ import { Contract as Contract2, Interface } from "ethers";
717
+ var SilentDataRollupContract = class extends Contract2 {
540
718
  constructor(config) {
541
719
  const { address, abi, runner, contractMethodsToSign } = config;
542
720
  const contractInterface = new Interface(abi);
@@ -555,16 +733,6 @@ var SilentDataRollupContract = class extends Contract {
555
733
  }
556
734
  };
557
735
 
558
- // src/privateEvents.ts
559
- import { keccak256, toUtf8Bytes } from "ethers";
560
- var PRIVATE_EVENT_SIGNATURE = "PrivateEvent(address[],bytes32,bytes)";
561
- var PRIVATE_EVENT_SIGNATURE_HASH = keccak256(
562
- toUtf8Bytes(PRIVATE_EVENT_SIGNATURE)
563
- );
564
- function calculateEventTypeHash(eventSignature) {
565
- return keccak256(toUtf8Bytes(eventSignature));
566
- }
567
-
568
736
  export {
569
737
  SIGN_RPC_METHODS,
570
738
  eip721Domain,
@@ -573,14 +741,23 @@ export {
573
741
  DEBUG_NAMESPACE_SILENTDATA_INTERCEPTOR,
574
742
  HEADER_SIGNATURE,
575
743
  HEADER_TIMESTAMP,
744
+ HEADER_SIGNATURE_TYPE,
576
745
  HEADER_EIP712_SIGNATURE,
577
746
  HEADER_DELEGATE,
578
747
  HEADER_DELEGATE_SIGNATURE,
579
748
  HEADER_EIP712_DELEGATE_SIGNATURE,
580
749
  HEADER_SIGNER_SWC,
750
+ HEADER_FROM_BLOCK,
751
+ ENTRYPOINT_ADDRESS,
752
+ DEFAULT_USER_OPERATION_RECEIPT_LOOKUP_RANGE,
753
+ USER_OPERATION_EVENT_SIGNATURE,
754
+ USER_OPERATION_EVENT_HASH,
581
755
  DEFAULT_DELEGATE_EXPIRES,
582
756
  DELEGATE_EXPIRATION_THRESHOLD_BUFFER,
583
757
  WHITELISTED_METHODS,
758
+ PRIVATE_EVENT_SIGNATURE,
759
+ PRIVATE_EVENT_SIGNATURE_HASH,
760
+ calculateEventTypeHash,
584
761
  ChainId,
585
762
  NetworkName,
586
763
  SignatureType,
@@ -590,8 +767,5 @@ export {
590
767
  defaultGetDelegate,
591
768
  prepareTypedDataPayload,
592
769
  SilentDataRollupBase,
593
- SilentDataRollupContract,
594
- PRIVATE_EVENT_SIGNATURE,
595
- PRIVATE_EVENT_SIGNATURE_HASH,
596
- calculateEventTypeHash
770
+ SilentDataRollupContract
597
771
  };
package/dist/index.d.mts CHANGED
@@ -15,11 +15,39 @@ declare const DEBUG_NAMESPACE = "silentdata:core";
15
15
  declare const DEBUG_NAMESPACE_SILENTDATA_INTERCEPTOR = "silentdata:interceptor";
16
16
  declare const HEADER_SIGNATURE = "x-signature";
17
17
  declare const HEADER_TIMESTAMP = "x-timestamp";
18
+ declare const HEADER_SIGNATURE_TYPE = "x-signature-type";
18
19
  declare const HEADER_EIP712_SIGNATURE = "x-eip712-signature";
19
20
  declare const HEADER_DELEGATE = "x-delegate";
20
21
  declare const HEADER_DELEGATE_SIGNATURE = "x-delegate-signature";
21
22
  declare const HEADER_EIP712_DELEGATE_SIGNATURE = "x-eip712-delegate-signature";
22
23
  declare const HEADER_SIGNER_SWC = "x-signer-swc";
24
+ /**
25
+ * Header used when signing a user operation receipt call to the bundler (eth_getUserOperationReceipt)
26
+ */
27
+ declare const HEADER_FROM_BLOCK = "x-from-block";
28
+ /**
29
+ * Entrypoint address used when building eth_getLogs payload for signing eth_getUserOperationReceipt calls.
30
+ * This is hardcoded for now as we are just starting and will have support for only one version anyway.
31
+ * We are not worrying about different versions for now.
32
+ * In the future, if we do support other versions, this should be made a config while initializing the provider.
33
+ */
34
+ declare const ENTRYPOINT_ADDRESS = "0x34F5Bda45f2Ce00B646BD6B19D0F9817b5D8D398";
35
+ /**
36
+ * Default number of blocks to look back when fetching user operation receipts.
37
+ * This is used to calculate the fromBlock parameter in eth_getUserOperationReceipt requests.
38
+ */
39
+ declare const DEFAULT_USER_OPERATION_RECEIPT_LOOKUP_RANGE = 1024;
40
+ /**
41
+ * The signature for the UserOperationEvent from ERC-4337
42
+ * This is the event emitted by the EntryPoint contract and wrapped in PrivateEvent
43
+ */
44
+ declare const USER_OPERATION_EVENT_SIGNATURE = "UserOperationEvent(bytes32,address,address,uint256,bool,uint256,uint256)";
45
+ /**
46
+ * The keccak256 hash of the UserOperationEvent signature
47
+ * Used as the eventType filter when querying for user operation receipts via eth_getLogs
48
+ * Value: 0x49628fd1471006c1482da88028e9ce4dbb080b815c9b0344d39e5a8e6ec1419f
49
+ */
50
+ declare const USER_OPERATION_EVENT_HASH: string;
23
51
  declare const DEFAULT_DELEGATE_EXPIRES: number;
24
52
  /**
25
53
  * A buffer time (in seconds) added to the current time when verifying the validity of a delegate.
@@ -73,6 +101,17 @@ type BaseConfig = {
73
101
  expires?: number;
74
102
  };
75
103
  authSignatureType?: SignatureType;
104
+ /**
105
+ * Smart wallet contract address.
106
+ * When provided, signing will be done on the hashed message for EIP-1271 verification.
107
+ */
108
+ smartWalletAddress?: string;
109
+ /**
110
+ * The block range to look back when fetching user operation receipts.
111
+ * This value is used to calculate the fromBlock parameter when querying for receipts.
112
+ * If not provided, defaults to DEFAULT_USER_OPERATION_RECEIPT_LOOKUP_RANGE (1024 blocks).
113
+ */
114
+ userOperationReceiptLookupRange?: number;
76
115
  };
77
116
  type DelegateSignerMessage = {
78
117
  expires: string;
@@ -89,6 +128,8 @@ type AuthHeaders = {
89
128
  [HEADER_TIMESTAMP]: string;
90
129
  [HEADER_SIGNATURE]?: string;
91
130
  [HEADER_EIP712_SIGNATURE]?: string;
131
+ [HEADER_SIGNATURE_TYPE]?: string;
132
+ [HEADER_FROM_BLOCK]?: string;
92
133
  };
93
134
  type DelegateHeaders = {
94
135
  [HEADER_DELEGATE]: string;
@@ -129,24 +170,39 @@ declare class SilentDataRollupBase {
129
170
  /**
130
171
  * Signs a raw delegate header message.
131
172
  * This method can be overridden by extending classes to customize the signing process.
173
+ * The signer implementation decides whether to add EIP-191 prefix or not.
132
174
  * @param provider - The provider used for signing
133
175
  * @param message - The delegate signer message to be signed
176
+ * @param isSWC - Whether signing for smart wallet contract (EIP-1271)
134
177
  * @returns A promise that resolves to the signature string
135
178
  */
136
- protected signRawDelegateHeader(provider: any, message: string): Promise<string>;
179
+ protected signDelegateHeader(provider: any, message: string, isSWC?: boolean): Promise<string>;
137
180
  /**
138
181
  * Signs a typed delegate header message.
139
182
  * This method can be overridden by extending classes to customize the signing process.
140
183
  * @param provider - The provider used for signing
184
+ * @param chainId - The chain ID
141
185
  * @param message - The delegate signer message to be signed
186
+ * @param isSWC - Whether signing for smart wallet contract (EIP-1271)
142
187
  * @returns A promise that resolves to the signature string
143
188
  */
144
- protected signTypedDelegateHeader(provider: any, chainId: string, message: DelegateSignerMessage): Promise<string>;
189
+ protected signTypedDelegateHeader(provider: any, chainId: string, message: DelegateSignerMessage, isSWC?: boolean): Promise<string>;
190
+ /**
191
+ * IMPORTANT: Return the cached promise (currentPromise), not the resolved value.
192
+ * This ensures multiple concurrent callers share the same in-flight request,
193
+ * preventing redundant API calls.
194
+ */
145
195
  getDelegateHeaders(provider: any): Promise<DelegateHeaders>;
146
196
  generateDelegateHeaders(provider: any): Promise<DelegateHeaders>;
147
197
  getAuthHeaders(provider: any, payload: JsonRpcPayload | JsonRpcPayload[]): Promise<AuthHeaders>;
148
- signAuthHeaderRawMessage(provider: any, payload: JsonRpcPayload | JsonRpcPayload[], timestamp: string, chainId: string): Promise<string>;
149
- signAuthHeaderTypedData(provider: any, payload: JsonRpcPayload | JsonRpcPayload[], timestamp: string, chainId: string): Promise<string>;
198
+ /**
199
+ * Signs auth header.
200
+ */
201
+ signAuthHeader(provider: any, payload: JsonRpcPayload | JsonRpcPayload[], timestamp: string, chainId: string, isSWC?: boolean): Promise<string>;
202
+ /**
203
+ * Signs auth header using typed data signature.
204
+ */
205
+ signTypedAuthHeader(provider: any, payload: JsonRpcPayload | JsonRpcPayload[], timestamp: string, chainId: string, isSWC?: boolean): Promise<string>;
150
206
  /**
151
207
  * Signs a message using the provided signer.
152
208
  * This method can be overridden to customize the signing process.
@@ -166,14 +222,45 @@ declare class SilentDataRollupBase {
166
222
  */
167
223
  protected signTypedData(signer: any, domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, message: Record<string, any>): Promise<string>;
168
224
  setContract(contract: Contract, contractMethodsToSign: string[]): void;
225
+ /**
226
+ * Calculates the fromBlock value for eth_getUserOperationReceipt requests.
227
+ * Gets the current block number and subtracts the configured userOperationReceiptLookupRange.
228
+ *
229
+ * IMPORTANT: The bundler strictly validates this value and will reject the request if:
230
+ * - The header is missing
231
+ * - The value is < 0
232
+ * - The value is > current block number
233
+ * - The value is too far back (< currentBlock - userOperationReceiptLookupRange)
234
+ *
235
+ * This method ensures the returned value is always within the valid range:
236
+ * max(0, currentBlock - userOperationReceiptLookupRange) <= fromBlock <= currentBlock
237
+ *
238
+ * @param provider - The provider to use for fetching the current block number
239
+ * @returns A promise that resolves to the fromBlock value as a bigint
240
+ * @throws Error if unable to fetch the current block number
241
+ */
242
+ protected getFromBlockForUserOperationReceipt(provider: any): Promise<bigint>;
243
+ /**
244
+ * Builds a custom eth_getLogs payload for signing when the original request is eth_getUserOperationReceipt.
245
+ * This method can be overridden to customize the payload construction.
246
+ *
247
+ * IMPORTANT: The bundler must reconstruct this exact payload to verify the signature.
248
+ * The bundler should use the same `id` from the original eth_getUserOperationReceipt request
249
+ * when constructing the eth_getLogs request to send to the RPC node.
250
+ *
251
+ * @param payload - The original eth_getUserOperationReceipt payload
252
+ * @param fromBlock - The fromBlock value to use in the eth_getLogs filter
253
+ * @returns A JsonRpcPayload with method 'eth_getLogs' to be used for signing
254
+ */
255
+ protected buildGetUserOperationReceiptSigningPayload(payload: JsonRpcPayload, fromBlock: bigint): JsonRpcPayload;
169
256
  /**
170
257
  * Prepares the message to be signed for the x-signature header.
171
258
  */
172
- prepareSignatureMessage(chainId: string, payload: JsonRpcPayload | JsonRpcPayload[], timestamp: string): string;
259
+ prepareMessage(chainId: string, payload: JsonRpcPayload | JsonRpcPayload[], timestamp: string): string;
173
260
  /**
174
261
  * Prepares the message to be signed for the x-eip712-signature header.
175
262
  */
176
- prepareSignatureTypedData(payload: JsonRpcPayload | JsonRpcPayload[], timestamp: string): AuthSignatureMessage;
263
+ prepareTypedData(payload: JsonRpcPayload | JsonRpcPayload[], timestamp: string): AuthSignatureMessage;
177
264
  }
178
265
 
179
266
  declare class SilentDataRollupContract extends Contract {
@@ -241,4 +328,4 @@ interface PrivateEvent {
241
328
  */
242
329
  declare function calculateEventTypeHash(eventSignature: string): string;
243
330
 
244
- export { type AuthHeaders, type AuthSignatureMessage, type AuthSignatureMessageRequest, type BaseConfig, ChainId, type ContractInfo, DEBUG_NAMESPACE, DEBUG_NAMESPACE_SILENTDATA_INTERCEPTOR, DEFAULT_DELEGATE_EXPIRES, DELEGATE_EXPIRATION_THRESHOLD_BUFFER, type DelegateConfig, type DelegateHeaders, type DelegateSignerMessage, HEADER_DELEGATE, HEADER_DELEGATE_SIGNATURE, HEADER_EIP712_DELEGATE_SIGNATURE, HEADER_EIP712_SIGNATURE, HEADER_SIGNATURE, HEADER_SIGNER_SWC, HEADER_TIMESTAMP, NetworkName, PRIVATE_EVENT_SIGNATURE, PRIVATE_EVENT_SIGNATURE_HASH, type PrivateEvent, SIGN_RPC_METHODS, SignatureType, type SilentDataProviderOptions, SilentDataRollupBase, SilentDataRollupContract, type SilentDataRollupContractConfig, type SilentdataNetworkConfig, WHITELISTED_METHODS, calculateEventTypeHash, defaultGetDelegate, delegateEIP721Types, eip721Domain, getAuthEIP721Types, getAuthHeaders, isSignableContractCall, prepareTypedDataPayload };
331
+ export { type AuthHeaders, type AuthSignatureMessage, type AuthSignatureMessageRequest, type BaseConfig, ChainId, type ContractInfo, DEBUG_NAMESPACE, DEBUG_NAMESPACE_SILENTDATA_INTERCEPTOR, DEFAULT_DELEGATE_EXPIRES, DEFAULT_USER_OPERATION_RECEIPT_LOOKUP_RANGE, DELEGATE_EXPIRATION_THRESHOLD_BUFFER, type DelegateConfig, type DelegateHeaders, type DelegateSignerMessage, ENTRYPOINT_ADDRESS, HEADER_DELEGATE, HEADER_DELEGATE_SIGNATURE, HEADER_EIP712_DELEGATE_SIGNATURE, HEADER_EIP712_SIGNATURE, HEADER_FROM_BLOCK, HEADER_SIGNATURE, HEADER_SIGNATURE_TYPE, HEADER_SIGNER_SWC, HEADER_TIMESTAMP, NetworkName, PRIVATE_EVENT_SIGNATURE, PRIVATE_EVENT_SIGNATURE_HASH, type PrivateEvent, SIGN_RPC_METHODS, SignatureType, type SilentDataProviderOptions, SilentDataRollupBase, SilentDataRollupContract, type SilentDataRollupContractConfig, type SilentdataNetworkConfig, USER_OPERATION_EVENT_HASH, USER_OPERATION_EVENT_SIGNATURE, WHITELISTED_METHODS, calculateEventTypeHash, defaultGetDelegate, delegateEIP721Types, eip721Domain, getAuthEIP721Types, getAuthHeaders, isSignableContractCall, prepareTypedDataPayload };