@appliedblockchain/silentdatarollup-core 1.0.9 → 1.0.10
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/dist/chunk-RWLHE5DT.mjs +3312 -0
- package/dist/index.d.mts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2544 -2
- package/dist/index.mjs +5 -3
- package/dist/tests.js +181 -0
- package/dist/tests.mjs +1 -1
- package/package.json +3 -1
- package/dist/chunk-53A5RGL2.mjs +0 -771
package/dist/chunk-53A5RGL2.mjs
DELETED
|
@@ -1,771 +0,0 @@
|
|
|
1
|
-
// src/Base.ts
|
|
2
|
-
import debug2 from "debug";
|
|
3
|
-
import {
|
|
4
|
-
getBytes,
|
|
5
|
-
keccak256 as keccak2563,
|
|
6
|
-
toUtf8Bytes as toUtf8Bytes3,
|
|
7
|
-
TypedDataEncoder
|
|
8
|
-
} from "ethers";
|
|
9
|
-
|
|
10
|
-
// src/constants.ts
|
|
11
|
-
import { keccak256, toUtf8Bytes } from "ethers";
|
|
12
|
-
var SIGN_RPC_METHODS = [
|
|
13
|
-
"eth_estimateGas",
|
|
14
|
-
"eth_getProof",
|
|
15
|
-
"eth_getTransactionByHash",
|
|
16
|
-
"eth_getTransactionReceipt",
|
|
17
|
-
"eth_getUserOperationReceipt",
|
|
18
|
-
"eth_getUserOperationByHash"
|
|
19
|
-
];
|
|
20
|
-
var eip721Domain = {
|
|
21
|
-
name: "Silent Data [Rollup]",
|
|
22
|
-
version: "1"
|
|
23
|
-
};
|
|
24
|
-
var delegateEIP721Types = {
|
|
25
|
-
Ticket: [
|
|
26
|
-
{ name: "expires", type: "string" },
|
|
27
|
-
{ name: "ephemeralAddress", type: "string" }
|
|
28
|
-
]
|
|
29
|
-
};
|
|
30
|
-
var DEBUG_NAMESPACE = "silentdata:core";
|
|
31
|
-
var DEBUG_NAMESPACE_SILENTDATA_INTERCEPTOR = "silentdata:interceptor";
|
|
32
|
-
var HEADER_SIGNATURE = "x-signature";
|
|
33
|
-
var HEADER_TIMESTAMP = "x-timestamp";
|
|
34
|
-
var HEADER_SIGNATURE_TYPE = "x-signature-type";
|
|
35
|
-
var HEADER_EIP712_SIGNATURE = "x-eip712-signature";
|
|
36
|
-
var HEADER_DELEGATE = "x-delegate";
|
|
37
|
-
var HEADER_DELEGATE_SIGNATURE = "x-delegate-signature";
|
|
38
|
-
var HEADER_EIP712_DELEGATE_SIGNATURE = "x-eip712-delegate-signature";
|
|
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
|
-
);
|
|
47
|
-
var DEFAULT_DELEGATE_EXPIRES = 10 * 60 * 60;
|
|
48
|
-
var DELEGATE_EXPIRATION_THRESHOLD_BUFFER = 5;
|
|
49
|
-
var WHITELISTED_METHODS = [
|
|
50
|
-
"eth_blockNumber",
|
|
51
|
-
"eth_call",
|
|
52
|
-
"eth_chainId",
|
|
53
|
-
"eth_estimateGas",
|
|
54
|
-
"eth_feeHistory",
|
|
55
|
-
"eth_gasPrice",
|
|
56
|
-
"eth_getBalance",
|
|
57
|
-
"eth_getBlockByHash",
|
|
58
|
-
"eth_getBlockByNumber",
|
|
59
|
-
"eth_getCode",
|
|
60
|
-
"eth_getFilterChanges",
|
|
61
|
-
"eth_getFilterLogs",
|
|
62
|
-
"eth_getHeaderByHash",
|
|
63
|
-
"eth_getLogs",
|
|
64
|
-
"eth_getProof",
|
|
65
|
-
"eth_getTransactionByHash",
|
|
66
|
-
"eth_getTransactionCount",
|
|
67
|
-
"eth_getTransactionReceipt",
|
|
68
|
-
"eth_maxPriorityFeePerGas",
|
|
69
|
-
"eth_newBlockFilter",
|
|
70
|
-
"eth_sendRawTransaction",
|
|
71
|
-
"eth_syncing",
|
|
72
|
-
"eth_newFilter",
|
|
73
|
-
"eth_newPendingTransactionFilter",
|
|
74
|
-
"net_listening",
|
|
75
|
-
"net_version",
|
|
76
|
-
"net_peerCount",
|
|
77
|
-
"web3_clientVersion",
|
|
78
|
-
"web3_sha3"
|
|
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
|
-
|
|
91
|
-
// src/types.ts
|
|
92
|
-
var ChainId = /* @__PURE__ */ ((ChainId2) => {
|
|
93
|
-
ChainId2[ChainId2["MAINNET"] = 380929] = "MAINNET";
|
|
94
|
-
ChainId2[ChainId2["TESTNET"] = 381185] = "TESTNET";
|
|
95
|
-
return ChainId2;
|
|
96
|
-
})(ChainId || {});
|
|
97
|
-
var NetworkName = /* @__PURE__ */ ((NetworkName2) => {
|
|
98
|
-
NetworkName2["MAINNET"] = "sdr";
|
|
99
|
-
NetworkName2["TESTNET"] = "sdr-testnet";
|
|
100
|
-
return NetworkName2;
|
|
101
|
-
})(NetworkName || {});
|
|
102
|
-
var SignatureType = /* @__PURE__ */ ((SignatureType2) => {
|
|
103
|
-
SignatureType2["Raw"] = "RAW";
|
|
104
|
-
SignatureType2["EIP191"] = "EIP191";
|
|
105
|
-
SignatureType2["EIP712"] = "EIP712";
|
|
106
|
-
return SignatureType2;
|
|
107
|
-
})(SignatureType || {});
|
|
108
|
-
|
|
109
|
-
// src/utils.ts
|
|
110
|
-
import debug from "debug";
|
|
111
|
-
import { Wallet } from "ethers";
|
|
112
|
-
var log = debug(DEBUG_NAMESPACE);
|
|
113
|
-
function getAuthEIP721Types(payload) {
|
|
114
|
-
return {
|
|
115
|
-
Call: [
|
|
116
|
-
{
|
|
117
|
-
name: "request",
|
|
118
|
-
type: Array.isArray(payload) ? "JsonRPCRequest[]" : "JsonRPCRequest"
|
|
119
|
-
},
|
|
120
|
-
{ name: "timestamp", type: "string" }
|
|
121
|
-
],
|
|
122
|
-
JsonRPCRequest: [
|
|
123
|
-
{ name: "jsonrpc", type: "string" },
|
|
124
|
-
{ name: "method", type: "string" },
|
|
125
|
-
{ name: "params", type: "string" },
|
|
126
|
-
{ name: "id", type: "uint256" }
|
|
127
|
-
]
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
async function signAuthHeaderTypedData(signer, payload, timestamp, chainId) {
|
|
131
|
-
log("Preparing payload for signTypedData");
|
|
132
|
-
const preparePayload = (p) => ({
|
|
133
|
-
...p,
|
|
134
|
-
params: JSON.stringify(p.params)
|
|
135
|
-
});
|
|
136
|
-
const preparedPayload = Array.isArray(payload) ? payload.map(preparePayload) : preparePayload(payload);
|
|
137
|
-
const message = {
|
|
138
|
-
request: preparedPayload,
|
|
139
|
-
timestamp
|
|
140
|
-
};
|
|
141
|
-
const types = getAuthEIP721Types(payload);
|
|
142
|
-
const domain = { ...eip721Domain, chainId };
|
|
143
|
-
log("Signing typed data", JSON.stringify({ domain, types, message }, null, 2));
|
|
144
|
-
const signature = await signer.signTypedData(domain, types, message);
|
|
145
|
-
log("Signature generated:", signature);
|
|
146
|
-
return signature;
|
|
147
|
-
}
|
|
148
|
-
async function signAuthHeaderRawMessage(signer, payload, timestamp, chainId) {
|
|
149
|
-
log("Preparing raw message for signing");
|
|
150
|
-
const serialRequest = JSON.stringify(payload);
|
|
151
|
-
const xMessage = chainId + serialRequest + timestamp;
|
|
152
|
-
log("Raw message:", xMessage);
|
|
153
|
-
const signature = await signer.signMessage(xMessage);
|
|
154
|
-
log("Raw signature generated:", signature);
|
|
155
|
-
return signature;
|
|
156
|
-
}
|
|
157
|
-
async function getAuthHeaders(signer, payload, chainId, signatureType) {
|
|
158
|
-
const xTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
159
|
-
const headers = {
|
|
160
|
-
[HEADER_TIMESTAMP]: xTimestamp
|
|
161
|
-
};
|
|
162
|
-
switch (signatureType) {
|
|
163
|
-
case "RAW" /* Raw */:
|
|
164
|
-
log("Generating raw signature");
|
|
165
|
-
headers[HEADER_SIGNATURE] = await signAuthHeaderRawMessage(
|
|
166
|
-
signer,
|
|
167
|
-
payload,
|
|
168
|
-
xTimestamp,
|
|
169
|
-
chainId
|
|
170
|
-
);
|
|
171
|
-
break;
|
|
172
|
-
case "EIP712" /* EIP712 */:
|
|
173
|
-
log("Generating EIP712 signature");
|
|
174
|
-
headers[HEADER_EIP712_SIGNATURE] = await signAuthHeaderTypedData(
|
|
175
|
-
signer,
|
|
176
|
-
payload,
|
|
177
|
-
xTimestamp,
|
|
178
|
-
chainId
|
|
179
|
-
);
|
|
180
|
-
break;
|
|
181
|
-
default:
|
|
182
|
-
throw new Error(`Unsupported signature type: ${signatureType}`);
|
|
183
|
-
}
|
|
184
|
-
return headers;
|
|
185
|
-
}
|
|
186
|
-
function isSignableContractCall(payload, contracts) {
|
|
187
|
-
if (!contracts || contracts.length === 0) {
|
|
188
|
-
return false;
|
|
189
|
-
}
|
|
190
|
-
log("Checking if contract call is signable");
|
|
191
|
-
if (payload.method !== "eth_call") {
|
|
192
|
-
log("Payload method is not eth_call, returning false");
|
|
193
|
-
return false;
|
|
194
|
-
}
|
|
195
|
-
const params = payload.params;
|
|
196
|
-
if (!params || params.length === 0 || typeof params[0] !== "object") {
|
|
197
|
-
log("Invalid params, returning false");
|
|
198
|
-
return false;
|
|
199
|
-
}
|
|
200
|
-
const callTarget = params[0].to;
|
|
201
|
-
if (!callTarget) {
|
|
202
|
-
log("Missing call target, returning false");
|
|
203
|
-
return false;
|
|
204
|
-
}
|
|
205
|
-
log(`Call target: ${callTarget}`);
|
|
206
|
-
const contractIndex = contracts.findIndex(
|
|
207
|
-
(c) => c.contract.target.toString().toLowerCase() === callTarget.toLowerCase()
|
|
208
|
-
);
|
|
209
|
-
if (contractIndex < 0) {
|
|
210
|
-
log("Contract not found, returning false");
|
|
211
|
-
return false;
|
|
212
|
-
}
|
|
213
|
-
const callData = params[0].data;
|
|
214
|
-
if (!callData) {
|
|
215
|
-
log("Missing call data, returning false");
|
|
216
|
-
return false;
|
|
217
|
-
}
|
|
218
|
-
const methodSignature = callData.slice(2, 10);
|
|
219
|
-
if (!methodSignature) {
|
|
220
|
-
log("Missing method signature, returning false");
|
|
221
|
-
return false;
|
|
222
|
-
}
|
|
223
|
-
log(`Method signature: ${methodSignature}`);
|
|
224
|
-
const { contract, contractMethodsToSign } = contracts[contractIndex];
|
|
225
|
-
const isSignable = contractMethodsToSign.some((methodName) => {
|
|
226
|
-
const fragment = contract.interface.getFunction(methodName);
|
|
227
|
-
return !!fragment && methodSignature.startsWith(fragment.selector.slice(2));
|
|
228
|
-
});
|
|
229
|
-
log("Is signable contract call:", isSignable);
|
|
230
|
-
return isSignable;
|
|
231
|
-
}
|
|
232
|
-
var defaultGetDelegate = async (provider) => {
|
|
233
|
-
return Wallet.createRandom();
|
|
234
|
-
};
|
|
235
|
-
var prepareTypedDataPayload = (p) => ({
|
|
236
|
-
...p,
|
|
237
|
-
params: JSON.stringify(p.params)
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
// src/Base.ts
|
|
241
|
-
var log2 = debug2(DEBUG_NAMESPACE);
|
|
242
|
-
var SilentDataRollupBase = class {
|
|
243
|
-
constructor(config) {
|
|
244
|
-
this.currentDelegateSigner = null;
|
|
245
|
-
this.delegateSignerExpires = 0;
|
|
246
|
-
this.cachedDelegateHeaders = null;
|
|
247
|
-
this.cachedHeadersExpiry = 0;
|
|
248
|
-
this.delegateHeadersPromise = null;
|
|
249
|
-
this.contracts = [];
|
|
250
|
-
this._cachedNetwork = null;
|
|
251
|
-
this.config = {
|
|
252
|
-
...config,
|
|
253
|
-
authSignatureType: config.authSignatureType ?? "EIP191" /* EIP191 */
|
|
254
|
-
};
|
|
255
|
-
this.delegateConfig = this.resolveDelegateConfig(config);
|
|
256
|
-
log2(
|
|
257
|
-
"SilentDataRollupBase initialized with config:",
|
|
258
|
-
JSON.stringify(config, null, 2)
|
|
259
|
-
);
|
|
260
|
-
}
|
|
261
|
-
resolveDelegateConfig(config) {
|
|
262
|
-
if (config.delegate === true) {
|
|
263
|
-
return {
|
|
264
|
-
getDelegate: defaultGetDelegate,
|
|
265
|
-
expires: DEFAULT_DELEGATE_EXPIRES
|
|
266
|
-
};
|
|
267
|
-
} else if (typeof config.delegate === "object") {
|
|
268
|
-
return {
|
|
269
|
-
getDelegate: config.delegate.getDelegate ?? defaultGetDelegate,
|
|
270
|
-
expires: config.delegate.expires ?? DEFAULT_DELEGATE_EXPIRES
|
|
271
|
-
};
|
|
272
|
-
}
|
|
273
|
-
return null;
|
|
274
|
-
}
|
|
275
|
-
/**
|
|
276
|
-
* Get cached network with simple caching
|
|
277
|
-
* @param provider - The provider to get the network from
|
|
278
|
-
* @returns Promise<Network> - The cached or freshly fetched network
|
|
279
|
-
*/
|
|
280
|
-
async getCachedNetwork(provider) {
|
|
281
|
-
if (!this._cachedNetwork) {
|
|
282
|
-
this._cachedNetwork = await provider.getNetwork();
|
|
283
|
-
log2("Network cached:", this._cachedNetwork);
|
|
284
|
-
}
|
|
285
|
-
return this._cachedNetwork;
|
|
286
|
-
}
|
|
287
|
-
async getDelegateSigner(provider) {
|
|
288
|
-
if (!this.delegateConfig) {
|
|
289
|
-
log2("getDelegateSigner: No delegate config, returning null");
|
|
290
|
-
return null;
|
|
291
|
-
}
|
|
292
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
293
|
-
log2("getDelegateSigner: Current time:", now);
|
|
294
|
-
const isDelegateSignerValid = this.currentDelegateSigner && this.delegateSignerExpires - DELEGATE_EXPIRATION_THRESHOLD_BUFFER > now;
|
|
295
|
-
if (isDelegateSignerValid) {
|
|
296
|
-
log2(
|
|
297
|
-
"getDelegateSigner: Returning existing delegate signer, expires in:",
|
|
298
|
-
this.delegateSignerExpires - now,
|
|
299
|
-
"seconds"
|
|
300
|
-
);
|
|
301
|
-
return this.currentDelegateSigner;
|
|
302
|
-
} else {
|
|
303
|
-
log2("getDelegateSigner: Getting new delegate signer");
|
|
304
|
-
try {
|
|
305
|
-
const newSigner = await this.delegateConfig.getDelegate(provider);
|
|
306
|
-
this.currentDelegateSigner = newSigner;
|
|
307
|
-
this.delegateSignerExpires = now + this.delegateConfig.expires;
|
|
308
|
-
log2(
|
|
309
|
-
"getDelegateSigner: New delegate signer set, expires in:",
|
|
310
|
-
this.delegateConfig.expires,
|
|
311
|
-
"seconds"
|
|
312
|
-
);
|
|
313
|
-
return newSigner;
|
|
314
|
-
} catch (error) {
|
|
315
|
-
log2("getDelegateSigner: Error getting delegate signer:", error);
|
|
316
|
-
throw new Error("Failed to get delegate signer");
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
async getDelegateSignerMessage(provider) {
|
|
321
|
-
if (!this.delegateConfig) {
|
|
322
|
-
log2("No delegate config, returning null");
|
|
323
|
-
return null;
|
|
324
|
-
}
|
|
325
|
-
const delegateSigner = await this.getDelegateSigner(provider);
|
|
326
|
-
if (!delegateSigner) {
|
|
327
|
-
log2("Failed to get delegate signer, returning null");
|
|
328
|
-
return null;
|
|
329
|
-
}
|
|
330
|
-
return {
|
|
331
|
-
expires: new Date(this.delegateSignerExpires * 1e3).toISOString(),
|
|
332
|
-
ephemeralAddress: await delegateSigner.getAddress()
|
|
333
|
-
};
|
|
334
|
-
}
|
|
335
|
-
/**
|
|
336
|
-
* Signs a raw delegate header message.
|
|
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.
|
|
339
|
-
* @param provider - The provider used for signing
|
|
340
|
-
* @param message - The delegate signer message to be signed
|
|
341
|
-
* @param isSWC - Whether signing for smart wallet contract (EIP-1271)
|
|
342
|
-
* @returns A promise that resolves to the signature string
|
|
343
|
-
*/
|
|
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);
|
|
357
|
-
return signature;
|
|
358
|
-
}
|
|
359
|
-
/**
|
|
360
|
-
* Signs a typed delegate header message.
|
|
361
|
-
* This method can be overridden by extending classes to customize the signing process.
|
|
362
|
-
* @param provider - The provider used for signing
|
|
363
|
-
* @param chainId - The chain ID
|
|
364
|
-
* @param message - The delegate signer message to be signed
|
|
365
|
-
* @param isSWC - Whether signing for smart wallet contract (EIP-1271)
|
|
366
|
-
* @returns A promise that resolves to the signature string
|
|
367
|
-
*/
|
|
368
|
-
async signTypedDelegateHeader(provider, chainId, message, isSWC) {
|
|
369
|
-
log2("signTypedDelegateHeader: Signing typed delegate header");
|
|
370
|
-
log2(
|
|
371
|
-
"signTypedDelegateHeader: Typed message:",
|
|
372
|
-
JSON.stringify(message, null, 2)
|
|
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
|
-
}
|
|
386
|
-
const signature = await provider.signer.signTypedData(
|
|
387
|
-
domain,
|
|
388
|
-
delegateEIP721Types,
|
|
389
|
-
message
|
|
390
|
-
);
|
|
391
|
-
log2("signTypedDelegateHeader: Signature generated:", signature);
|
|
392
|
-
return signature;
|
|
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
|
-
*/
|
|
399
|
-
async getDelegateHeaders(provider) {
|
|
400
|
-
log2("Getting delegate headers");
|
|
401
|
-
if (!this.delegateHeadersPromise) {
|
|
402
|
-
this.delegateHeadersPromise = this.generateDelegateHeaders(provider);
|
|
403
|
-
}
|
|
404
|
-
const currentPromise = this.delegateHeadersPromise;
|
|
405
|
-
try {
|
|
406
|
-
const delegateHeaders = await currentPromise;
|
|
407
|
-
log2("Delegate headers:", JSON.stringify(delegateHeaders, null, 2));
|
|
408
|
-
return currentPromise;
|
|
409
|
-
} catch (error) {
|
|
410
|
-
log2("Error getting delegate headers:", error);
|
|
411
|
-
throw new Error("Failed to get delegate headers");
|
|
412
|
-
} finally {
|
|
413
|
-
if (this.delegateHeadersPromise === currentPromise) {
|
|
414
|
-
this.delegateHeadersPromise = null;
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
async generateDelegateHeaders(provider) {
|
|
419
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
420
|
-
const signatureType = this.config.authSignatureType;
|
|
421
|
-
const isCachedHeadersValid = this.cachedDelegateHeaders && this.cachedHeadersExpiry - DELEGATE_EXPIRATION_THRESHOLD_BUFFER > now;
|
|
422
|
-
if (isCachedHeadersValid) {
|
|
423
|
-
log2("Returning cached delegate headers");
|
|
424
|
-
return this.cachedDelegateHeaders;
|
|
425
|
-
}
|
|
426
|
-
try {
|
|
427
|
-
const delegateSignerMessage = await this.getDelegateSignerMessage(provider);
|
|
428
|
-
if (!delegateSignerMessage) {
|
|
429
|
-
throw new Error("Failed to get delegate signer message");
|
|
430
|
-
}
|
|
431
|
-
const delegateSigner = await this.getDelegateSigner(provider);
|
|
432
|
-
if (!delegateSigner) {
|
|
433
|
-
throw new Error("Failed to get delegate signer");
|
|
434
|
-
}
|
|
435
|
-
const headers = {
|
|
436
|
-
[HEADER_DELEGATE]: JSON.stringify(delegateSignerMessage)
|
|
437
|
-
};
|
|
438
|
-
const chainId = (await this.getCachedNetwork(provider)).chainId.toString();
|
|
439
|
-
const isSWC = !!this.config.smartWalletAddress;
|
|
440
|
-
switch (signatureType) {
|
|
441
|
-
case "EIP191" /* EIP191 */:
|
|
442
|
-
case "RAW" /* Raw */: {
|
|
443
|
-
log2("Generating delegate signature");
|
|
444
|
-
const delegateMessageToSign = chainId + JSON.stringify(delegateSignerMessage);
|
|
445
|
-
headers[HEADER_DELEGATE_SIGNATURE] = await this.signDelegateHeader(
|
|
446
|
-
provider,
|
|
447
|
-
delegateMessageToSign,
|
|
448
|
-
isSWC
|
|
449
|
-
);
|
|
450
|
-
break;
|
|
451
|
-
}
|
|
452
|
-
case "EIP712" /* EIP712 */:
|
|
453
|
-
log2("Generating delegate EIP712 signature");
|
|
454
|
-
headers[HEADER_EIP712_DELEGATE_SIGNATURE] = await this.signTypedDelegateHeader(
|
|
455
|
-
provider,
|
|
456
|
-
chainId,
|
|
457
|
-
delegateSignerMessage,
|
|
458
|
-
isSWC
|
|
459
|
-
);
|
|
460
|
-
break;
|
|
461
|
-
default:
|
|
462
|
-
throw new Error(`Unsupported signature type: ${signatureType}`);
|
|
463
|
-
}
|
|
464
|
-
this.cachedDelegateHeaders = headers;
|
|
465
|
-
this.cachedHeadersExpiry = new Date(delegateSignerMessage.expires).getTime() / 1e3;
|
|
466
|
-
return this.cachedDelegateHeaders;
|
|
467
|
-
} catch (error) {
|
|
468
|
-
log2("Error getting delegate headers:", error);
|
|
469
|
-
throw new Error("Failed to get delegate headers");
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
async getAuthHeaders(provider, payload) {
|
|
473
|
-
log2("Getting auth headers", JSON.stringify(payload, null, 2));
|
|
474
|
-
const xTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
475
|
-
const headers = {
|
|
476
|
-
[HEADER_TIMESTAMP]: xTimestamp
|
|
477
|
-
};
|
|
478
|
-
const chainId = (await this.getCachedNetwork(provider)).chainId.toString();
|
|
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
|
-
}
|
|
510
|
-
switch (signatureType) {
|
|
511
|
-
case "EIP191" /* EIP191 */:
|
|
512
|
-
case "RAW" /* Raw */:
|
|
513
|
-
log2("Generating auth header signature");
|
|
514
|
-
headers[HEADER_SIGNATURE] = await this.signAuthHeader(
|
|
515
|
-
provider,
|
|
516
|
-
payloadToSign,
|
|
517
|
-
xTimestamp,
|
|
518
|
-
chainId,
|
|
519
|
-
isSWC
|
|
520
|
-
);
|
|
521
|
-
break;
|
|
522
|
-
case "EIP712" /* EIP712 */:
|
|
523
|
-
log2("Generating auth header typed signature");
|
|
524
|
-
headers[HEADER_EIP712_SIGNATURE] = await this.signTypedAuthHeader(
|
|
525
|
-
provider,
|
|
526
|
-
payloadToSign,
|
|
527
|
-
xTimestamp,
|
|
528
|
-
chainId,
|
|
529
|
-
isSWC
|
|
530
|
-
);
|
|
531
|
-
break;
|
|
532
|
-
default:
|
|
533
|
-
throw new Error(`Unsupported signature type: ${signatureType}`);
|
|
534
|
-
}
|
|
535
|
-
log2("Auth headers:", JSON.stringify(headers, null, 2));
|
|
536
|
-
return headers;
|
|
537
|
-
}
|
|
538
|
-
/**
|
|
539
|
-
* Signs auth header.
|
|
540
|
-
*/
|
|
541
|
-
async signAuthHeader(provider, payload, timestamp, chainId, isSWC) {
|
|
542
|
-
const xMessage = this.prepareMessage(chainId, payload, timestamp);
|
|
543
|
-
const delegateSigner = await this.getDelegateSigner(this);
|
|
544
|
-
const signer = delegateSigner ?? provider.signer;
|
|
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);
|
|
557
|
-
return signature;
|
|
558
|
-
}
|
|
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);
|
|
564
|
-
const types = getAuthEIP721Types(payload);
|
|
565
|
-
const delegateSigner = await this.getDelegateSigner(this);
|
|
566
|
-
const signer = delegateSigner ?? provider.signer;
|
|
567
|
-
const usingDelegate = !!delegateSigner;
|
|
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
|
-
}
|
|
577
|
-
log2(
|
|
578
|
-
"Signing typed data",
|
|
579
|
-
JSON.stringify({ message, types, domain }, null, 2)
|
|
580
|
-
);
|
|
581
|
-
const messageHash = TypedDataEncoder.hash(domain, types, message);
|
|
582
|
-
log2("EIP-712 hash (EOA):", messageHash);
|
|
583
|
-
const signature = await this.signTypedData(signer, domain, types, message);
|
|
584
|
-
log2("Message signed with EIP-712. Signature:", signature);
|
|
585
|
-
return signature;
|
|
586
|
-
}
|
|
587
|
-
/**
|
|
588
|
-
* Signs a message using the provided signer.
|
|
589
|
-
* This method can be overridden to customize the signing process.
|
|
590
|
-
* @param signer - The signer to use
|
|
591
|
-
* @param message - The message to sign
|
|
592
|
-
* @returns A promise that resolves to the signature string
|
|
593
|
-
*/
|
|
594
|
-
async signMessage(signer, message) {
|
|
595
|
-
return signer.signMessage(message);
|
|
596
|
-
}
|
|
597
|
-
/**
|
|
598
|
-
* Signs typed data using the provided signer.
|
|
599
|
-
* This method can be overridden to customize the signing process.
|
|
600
|
-
* @param signer - The signer to use
|
|
601
|
-
* @param domain - The EIP-712 domain
|
|
602
|
-
* @param types - The EIP-712 types
|
|
603
|
-
* @param message - The message to sign
|
|
604
|
-
* @returns A promise that resolves to the signature string
|
|
605
|
-
*/
|
|
606
|
-
async signTypedData(signer, domain, types, message) {
|
|
607
|
-
return signer.signTypedData(domain, types, message);
|
|
608
|
-
}
|
|
609
|
-
setContract(contract, contractMethodsToSign) {
|
|
610
|
-
log2("Setting contract and methods to sign: ", contractMethodsToSign);
|
|
611
|
-
this.contracts.push({
|
|
612
|
-
contract,
|
|
613
|
-
contractMethodsToSign
|
|
614
|
-
});
|
|
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
|
-
}
|
|
687
|
-
/**
|
|
688
|
-
* Prepares the message to be signed for the x-signature header.
|
|
689
|
-
*/
|
|
690
|
-
prepareMessage(chainId, payload, timestamp) {
|
|
691
|
-
log2("Preparing raw message for signing", {
|
|
692
|
-
payload: JSON.stringify(payload, null, 2),
|
|
693
|
-
timestamp
|
|
694
|
-
});
|
|
695
|
-
const serialRequest = JSON.stringify(payload);
|
|
696
|
-
const xMessage = chainId + serialRequest + timestamp;
|
|
697
|
-
log2("Raw message to be signed:", xMessage);
|
|
698
|
-
return xMessage;
|
|
699
|
-
}
|
|
700
|
-
/**
|
|
701
|
-
* Prepares the message to be signed for the x-eip712-signature header.
|
|
702
|
-
*/
|
|
703
|
-
prepareTypedData(payload, timestamp) {
|
|
704
|
-
log2("Preparing payload for signTypedData");
|
|
705
|
-
const preparedPayload = Array.isArray(payload) ? payload.map(prepareTypedDataPayload) : prepareTypedDataPayload(payload);
|
|
706
|
-
const message = {
|
|
707
|
-
request: preparedPayload,
|
|
708
|
-
timestamp
|
|
709
|
-
};
|
|
710
|
-
log2("Prepared payload for signTypedData", JSON.stringify(message, null, 2));
|
|
711
|
-
return message;
|
|
712
|
-
}
|
|
713
|
-
};
|
|
714
|
-
|
|
715
|
-
// src/contract.ts
|
|
716
|
-
import { Contract as Contract2, Interface } from "ethers";
|
|
717
|
-
var SilentDataRollupContract = class extends Contract2 {
|
|
718
|
-
constructor(config) {
|
|
719
|
-
const { address, abi, runner, contractMethodsToSign } = config;
|
|
720
|
-
const contractInterface = new Interface(abi);
|
|
721
|
-
contractMethodsToSign.forEach((method) => {
|
|
722
|
-
if (!contractInterface.hasFunction(method)) {
|
|
723
|
-
throw new Error(
|
|
724
|
-
`Method to sign '${method}' not found in the contract ABI`
|
|
725
|
-
);
|
|
726
|
-
}
|
|
727
|
-
});
|
|
728
|
-
super(address, abi, runner);
|
|
729
|
-
const baseProvider = runner.baseProvider || runner.provider?.baseProvider;
|
|
730
|
-
if (typeof baseProvider?.setContract === "function") {
|
|
731
|
-
baseProvider.setContract(this, contractMethodsToSign);
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
};
|
|
735
|
-
|
|
736
|
-
export {
|
|
737
|
-
SIGN_RPC_METHODS,
|
|
738
|
-
eip721Domain,
|
|
739
|
-
delegateEIP721Types,
|
|
740
|
-
DEBUG_NAMESPACE,
|
|
741
|
-
DEBUG_NAMESPACE_SILENTDATA_INTERCEPTOR,
|
|
742
|
-
HEADER_SIGNATURE,
|
|
743
|
-
HEADER_TIMESTAMP,
|
|
744
|
-
HEADER_SIGNATURE_TYPE,
|
|
745
|
-
HEADER_EIP712_SIGNATURE,
|
|
746
|
-
HEADER_DELEGATE,
|
|
747
|
-
HEADER_DELEGATE_SIGNATURE,
|
|
748
|
-
HEADER_EIP712_DELEGATE_SIGNATURE,
|
|
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,
|
|
755
|
-
DEFAULT_DELEGATE_EXPIRES,
|
|
756
|
-
DELEGATE_EXPIRATION_THRESHOLD_BUFFER,
|
|
757
|
-
WHITELISTED_METHODS,
|
|
758
|
-
PRIVATE_EVENT_SIGNATURE,
|
|
759
|
-
PRIVATE_EVENT_SIGNATURE_HASH,
|
|
760
|
-
calculateEventTypeHash,
|
|
761
|
-
ChainId,
|
|
762
|
-
NetworkName,
|
|
763
|
-
SignatureType,
|
|
764
|
-
getAuthEIP721Types,
|
|
765
|
-
getAuthHeaders,
|
|
766
|
-
isSignableContractCall,
|
|
767
|
-
defaultGetDelegate,
|
|
768
|
-
prepareTypedDataPayload,
|
|
769
|
-
SilentDataRollupBase,
|
|
770
|
-
SilentDataRollupContract
|
|
771
|
-
};
|