@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 +21 -0
- package/dist/{chunk-5JD3GAHS.mjs → chunk-53A5RGL2.mjs} +238 -64
- package/dist/index.d.mts +94 -7
- package/dist/index.d.ts +94 -7
- package/dist/index.js +238 -63
- package/dist/index.mjs +13 -1
- package/dist/tests.js +15 -9
- package/dist/tests.mjs +1 -1
- package/package.json +7 -8
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 ?? "
|
|
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
|
|
332
|
-
log2("
|
|
333
|
-
|
|
334
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
443
|
+
log2("Generating delegate signature");
|
|
399
444
|
const delegateMessageToSign = chainId + JSON.stringify(delegateSignerMessage);
|
|
400
|
-
headers[HEADER_DELEGATE_SIGNATURE] = await this.
|
|
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
|
|
436
|
-
headers[HEADER_SIGNATURE] = await this.
|
|
513
|
+
log2("Generating auth header signature");
|
|
514
|
+
headers[HEADER_SIGNATURE] = await this.signAuthHeader(
|
|
437
515
|
provider,
|
|
438
|
-
|
|
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
|
|
445
|
-
headers[HEADER_EIP712_SIGNATURE] = await this.
|
|
523
|
+
log2("Generating auth header typed signature");
|
|
524
|
+
headers[HEADER_EIP712_SIGNATURE] = await this.signTypedAuthHeader(
|
|
446
525
|
provider,
|
|
447
|
-
|
|
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
|
-
|
|
459
|
-
|
|
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
|
|
463
|
-
|
|
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
|
-
|
|
467
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
149
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 };
|