@appliedblockchain/silentdatarollup-ethers-provider 1.0.1 → 1.0.3

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/README.md CHANGED
@@ -11,6 +11,10 @@
11
11
  - [Usage with a Contract](#usage-with-a-contract)
12
12
  - [Installing Usage with a Contract Dependencies](#installing-usage-with-a-contract-dependencies)
13
13
  - [Usage with a Contract Example](#usage-with-a-contract-example)
14
+ - [Private Events](#private-events)
15
+ - [Overview](#overview)
16
+ - [Using the SDInterface](#using-the-sdinterface)
17
+ - [Private Events Example](#private-events-example)
14
18
  - [Troubleshooting](#troubleshooting)
15
19
  - [License](#license)
16
20
  - [Additional Resources](#additional-resources)
@@ -106,6 +110,112 @@ const publicMethodResult = await contract.method3('param1', 'param2')
106
110
  console.log('Public method result:', publicMethodResult)
107
111
  ```
108
112
 
113
+ ### Private Events
114
+
115
+ #### Overview
116
+
117
+ Silent Data Rollup supports Private Events, which are blockchain events that are only visible to authorized users. The `SilentDataRollupProvider` includes specialized methods for working with these private events:
118
+
119
+ - `getAllLogs()` - Returns both regular and private events
120
+ - `getPrivateLogs()` - Returns only private events
121
+
122
+ Private events are wrapped in a `PrivateEvent` event with the following structure:
123
+
124
+ ```solidity
125
+ event PrivateEvent(
126
+ address[] allowedViewers,
127
+ bytes32 indexed eventType,
128
+ bytes payload
129
+ );
130
+ ```
131
+
132
+ #### Using the SDInterface
133
+
134
+ To easily decode private events, use the `SDInterface` class which extends ethers' `Interface`:
135
+
136
+ 1. Create an `SDInterface` instance with your contract ABI plus **the private additional event signatures**
137
+ 2. Use it to parse logs from `getAllLogs()` or `getPrivateLogs()`
138
+ 3. Access the decoded inner event via the `innerLog` property of the parsed log
139
+
140
+ #### Private Events Example
141
+
142
+ ```typescript
143
+ import {
144
+ SDInterface,
145
+ SilentDataRollupProvider,
146
+ } from '@appliedblockchain/silentdatarollup-ethers-provider'
147
+
148
+ // Initialize provider
149
+ const provider = new SilentDataRollupProvider({
150
+ rpcUrl: 'SILENT_DATA_ROLLUP_RPC_URL',
151
+ network: NetworkName.TESTNET,
152
+ privateKey: 'YOUR_PRIVATE_KEY',
153
+ })
154
+
155
+ // Contract ABI with PrivateEvent definition
156
+ const contractAbi = [
157
+ {
158
+ anonymous: false,
159
+ inputs: [
160
+ {
161
+ indexed: false,
162
+ internalType: 'address[]',
163
+ name: 'allowedViewers',
164
+ type: 'address[]',
165
+ },
166
+ {
167
+ indexed: true,
168
+ internalType: 'bytes32',
169
+ name: 'eventType',
170
+ type: 'bytes32',
171
+ },
172
+ {
173
+ indexed: false,
174
+ internalType: 'bytes',
175
+ name: 'payload',
176
+ type: 'bytes',
177
+ },
178
+ ],
179
+ name: 'PrivateEvent',
180
+ type: 'event',
181
+ },
182
+ // Your other contract events and functions...
183
+ ]
184
+
185
+ // Create SDInterface with contract ABI and any additional event signatures
186
+ const sdInterface = new SDInterface([
187
+ ...contractAbi,
188
+ // Note: The private event doesn't have indexed params.
189
+ // Add any other event signatures that might be emitted privately
190
+ 'event TransferPrivate(address from, address to, uint256 value)',
191
+ ])
192
+
193
+ // Get private events
194
+ const privateEvents = await provider.getPrivateLogs({
195
+ address: 'YOUR_CONTRACT_ADDRESS',
196
+ // Optional: Filter by specific event type
197
+ eventSignature: 'TransferPrivate(address,address,uint256)',
198
+ })
199
+
200
+ // Parse and access private events
201
+ for (const log of privateEvents) {
202
+ const parsedLog = sdInterface.parseLog(log)
203
+
204
+ console.log('Private Event:', parsedLog.name) // Will be 'PrivateEvent'
205
+ console.log('Private Args:', parsedLog.args) // Raw PrivateEvent args
206
+
207
+ // Access the decoded inner event
208
+ if (parsedLog.innerLog) {
209
+ console.log('Decoded Private Event:', parsedLog.innerLog.name) // e.g., 'TransferPrivate'
210
+ console.log('Decoded Private Args:', parsedLog.innerLog.args) // Decoded inner event args
211
+
212
+ // Access args by name
213
+ const { from, to, value } = parsedLog.innerLog.args
214
+ console.log(`Transfer from ${from} to ${to}: ${value}`)
215
+ }
216
+ }
217
+ ```
218
+
109
219
  ## License
110
220
 
111
221
  This project is licensed under the [MIT License](LICENSE).
package/dist/index.d.mts CHANGED
@@ -1,7 +1,8 @@
1
- import { Signer, JsonRpcApiProviderOptions, JsonRpcProvider, JsonRpcPayload, JsonRpcResult, FetchRequest } from 'ethers';
1
+ import { Signer, JsonRpcApiProviderOptions, Filter, JsonRpcProvider, JsonRpcPayload, JsonRpcResult, FetchRequest, Log, FilterByBlockHash, LogDescription, Interface } from 'ethers';
2
2
  import { BaseConfig, NetworkName } from '@appliedblockchain/silentdatarollup-core';
3
3
 
4
4
  declare const SIGN_RPC_METHODS: string[];
5
+ declare const DEBUG_NAMESPACE = "silentdata:ethers-provider";
5
6
 
6
7
  interface SilentDataRollupProviderConfig extends BaseConfig {
7
8
  rpcUrl: string;
@@ -11,6 +12,24 @@ interface SilentDataRollupProviderConfig extends BaseConfig {
11
12
  signer?: Signer;
12
13
  options?: JsonRpcApiProviderOptions;
13
14
  }
15
+ /**
16
+ * Extended filter type that includes a special flag for private events
17
+ * This flag is used to identify when a call to eth_getLogs originated from
18
+ * the getAllLogs method, so we can add authentication headers
19
+ */
20
+ interface PrivateEventsFilter extends Filter {
21
+ /**
22
+ * Internal flag to identify requests that should include auth headers
23
+ * Set automatically by getAllLogs method
24
+ */
25
+ _isPrivateEvent?: boolean;
26
+ /**
27
+ * Optional event signature for filtering private events by type
28
+ * Example: "Transfer(address,address,uint256)"
29
+ * Will be converted to a hash and used for topic filtering
30
+ */
31
+ eventSignature?: string;
32
+ }
14
33
 
15
34
  declare class SilentDataRollupProvider extends JsonRpcProvider {
16
35
  private config;
@@ -22,6 +41,66 @@ declare class SilentDataRollupProvider extends JsonRpcProvider {
22
41
  rpcUrl: string;
23
42
  }): FetchRequest;
24
43
  clone(): SilentDataRollupProvider;
44
+ /**
45
+ * Helper method to configure a filter for private events
46
+ * @param filter - The original filter
47
+ * @param forcePrivateOnly - Whether to force filtering for only PrivateEvents
48
+ * @returns The configured filter with proper topics
49
+ */
50
+ private configurePrivateEventsFilter;
51
+ /**
52
+ * Gets logs for private events, including authentication headers
53
+ * @param filter - The filter parameters for logs
54
+ * @returns Array of logs matching the filter
55
+ */
56
+ getAllLogs(filter?: PrivateEventsFilter): Promise<Array<Log>>;
57
+ /**
58
+ * Gets only private events (PrivateEvent logs), including authentication headers
59
+ * @param filter - The filter parameters for logs
60
+ * @returns Array of logs matching the filter, containing only PrivateEvent logs
61
+ */
62
+ getPrivateLogs(filter?: PrivateEventsFilter): Promise<Log[]>;
63
+ /**
64
+ * Override of ethers' getLogs method that preserves our custom _isPrivateEvent property
65
+ *
66
+ * IMPORTANT: This method mimics the behavior of ethers' original getLogs implementation
67
+ * but adds a crucial step to preserve the _isPrivateEvent flag. We need this because:
68
+ *
69
+ * 1. Ethers' _getFilter method sanitizes filter objects, removing any non-standard properties
70
+ * 2. Our _isPrivateEvent flag would be stripped by this sanitization
71
+ * 3. We need the flag to reach the _send method to trigger the addition of auth headers
72
+ *
73
+ * The approach here is to run the normal filter processing, then re-attach our flag as a
74
+ * non-enumerable property to avoid JSON serialization issues. This allows the flag to
75
+ * survive until _send where we check for it to determine if auth headers are needed.
76
+ *
77
+ * @param _filter - The filter with our potential _isPrivateEvent property
78
+ * @returns Array of logs matching the filter
79
+ */
80
+ getLogs(_filter: PrivateEventsFilter | FilterByBlockHash): Promise<Log[]>;
81
+ }
82
+
83
+ /**
84
+ * Extended LogDescription type that includes private event details
85
+ */
86
+ interface SDLogDescription extends LogDescription {
87
+ /**
88
+ * Only present for PrivateEvents - contains the decoded inner log
89
+ * If decoding failed, this will be null
90
+ */
91
+ innerLog?: LogDescription | null;
92
+ }
93
+ /**
94
+ * Extends ethers Interface to provide support for decoding PrivateEvent logs
95
+ * Make sure to include the PrivateEvent signature in your ABI when using this class
96
+ */
97
+ declare class SDInterface extends Interface {
98
+ /**
99
+ * Extends the parseLog method to handle PrivateEvent logs
100
+ * @param log - The log to parse
101
+ * @returns The parsed log description with additional private event details if applicable
102
+ */
103
+ parseLog(log: Parameters<Interface['parseLog']>[0]): SDLogDescription | null;
25
104
  }
26
105
 
27
- export { SIGN_RPC_METHODS, SilentDataRollupProvider, type SilentDataRollupProviderConfig };
106
+ export { DEBUG_NAMESPACE, type PrivateEventsFilter, SDInterface, type SDLogDescription, SIGN_RPC_METHODS, SilentDataRollupProvider, type SilentDataRollupProviderConfig };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
- import { Signer, JsonRpcApiProviderOptions, JsonRpcProvider, JsonRpcPayload, JsonRpcResult, FetchRequest } from 'ethers';
1
+ import { Signer, JsonRpcApiProviderOptions, Filter, JsonRpcProvider, JsonRpcPayload, JsonRpcResult, FetchRequest, Log, FilterByBlockHash, LogDescription, Interface } from 'ethers';
2
2
  import { BaseConfig, NetworkName } from '@appliedblockchain/silentdatarollup-core';
3
3
 
4
4
  declare const SIGN_RPC_METHODS: string[];
5
+ declare const DEBUG_NAMESPACE = "silentdata:ethers-provider";
5
6
 
6
7
  interface SilentDataRollupProviderConfig extends BaseConfig {
7
8
  rpcUrl: string;
@@ -11,6 +12,24 @@ interface SilentDataRollupProviderConfig extends BaseConfig {
11
12
  signer?: Signer;
12
13
  options?: JsonRpcApiProviderOptions;
13
14
  }
15
+ /**
16
+ * Extended filter type that includes a special flag for private events
17
+ * This flag is used to identify when a call to eth_getLogs originated from
18
+ * the getAllLogs method, so we can add authentication headers
19
+ */
20
+ interface PrivateEventsFilter extends Filter {
21
+ /**
22
+ * Internal flag to identify requests that should include auth headers
23
+ * Set automatically by getAllLogs method
24
+ */
25
+ _isPrivateEvent?: boolean;
26
+ /**
27
+ * Optional event signature for filtering private events by type
28
+ * Example: "Transfer(address,address,uint256)"
29
+ * Will be converted to a hash and used for topic filtering
30
+ */
31
+ eventSignature?: string;
32
+ }
14
33
 
15
34
  declare class SilentDataRollupProvider extends JsonRpcProvider {
16
35
  private config;
@@ -22,6 +41,66 @@ declare class SilentDataRollupProvider extends JsonRpcProvider {
22
41
  rpcUrl: string;
23
42
  }): FetchRequest;
24
43
  clone(): SilentDataRollupProvider;
44
+ /**
45
+ * Helper method to configure a filter for private events
46
+ * @param filter - The original filter
47
+ * @param forcePrivateOnly - Whether to force filtering for only PrivateEvents
48
+ * @returns The configured filter with proper topics
49
+ */
50
+ private configurePrivateEventsFilter;
51
+ /**
52
+ * Gets logs for private events, including authentication headers
53
+ * @param filter - The filter parameters for logs
54
+ * @returns Array of logs matching the filter
55
+ */
56
+ getAllLogs(filter?: PrivateEventsFilter): Promise<Array<Log>>;
57
+ /**
58
+ * Gets only private events (PrivateEvent logs), including authentication headers
59
+ * @param filter - The filter parameters for logs
60
+ * @returns Array of logs matching the filter, containing only PrivateEvent logs
61
+ */
62
+ getPrivateLogs(filter?: PrivateEventsFilter): Promise<Log[]>;
63
+ /**
64
+ * Override of ethers' getLogs method that preserves our custom _isPrivateEvent property
65
+ *
66
+ * IMPORTANT: This method mimics the behavior of ethers' original getLogs implementation
67
+ * but adds a crucial step to preserve the _isPrivateEvent flag. We need this because:
68
+ *
69
+ * 1. Ethers' _getFilter method sanitizes filter objects, removing any non-standard properties
70
+ * 2. Our _isPrivateEvent flag would be stripped by this sanitization
71
+ * 3. We need the flag to reach the _send method to trigger the addition of auth headers
72
+ *
73
+ * The approach here is to run the normal filter processing, then re-attach our flag as a
74
+ * non-enumerable property to avoid JSON serialization issues. This allows the flag to
75
+ * survive until _send where we check for it to determine if auth headers are needed.
76
+ *
77
+ * @param _filter - The filter with our potential _isPrivateEvent property
78
+ * @returns Array of logs matching the filter
79
+ */
80
+ getLogs(_filter: PrivateEventsFilter | FilterByBlockHash): Promise<Log[]>;
81
+ }
82
+
83
+ /**
84
+ * Extended LogDescription type that includes private event details
85
+ */
86
+ interface SDLogDescription extends LogDescription {
87
+ /**
88
+ * Only present for PrivateEvents - contains the decoded inner log
89
+ * If decoding failed, this will be null
90
+ */
91
+ innerLog?: LogDescription | null;
92
+ }
93
+ /**
94
+ * Extends ethers Interface to provide support for decoding PrivateEvent logs
95
+ * Make sure to include the PrivateEvent signature in your ABI when using this class
96
+ */
97
+ declare class SDInterface extends Interface {
98
+ /**
99
+ * Extends the parseLog method to handle PrivateEvent logs
100
+ * @param log - The log to parse
101
+ * @returns The parsed log description with additional private event details if applicable
102
+ */
103
+ parseLog(log: Parameters<Interface['parseLog']>[0]): SDLogDescription | null;
25
104
  }
26
105
 
27
- export { SIGN_RPC_METHODS, SilentDataRollupProvider, type SilentDataRollupProviderConfig };
106
+ export { DEBUG_NAMESPACE, type PrivateEventsFilter, SDInterface, type SDLogDescription, SIGN_RPC_METHODS, SilentDataRollupProvider, type SilentDataRollupProviderConfig };
package/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,11 +17,21 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
21
31
  var index_exports = {};
22
32
  __export(index_exports, {
33
+ DEBUG_NAMESPACE: () => DEBUG_NAMESPACE,
34
+ SDInterface: () => SDInterface,
23
35
  SIGN_RPC_METHODS: () => SIGN_RPC_METHODS,
24
36
  SilentDataRollupProvider: () => SilentDataRollupProvider
25
37
  });
@@ -33,10 +45,14 @@ var SIGN_RPC_METHODS = [
33
45
  "eth_getProof",
34
46
  "eth_getTransactionReceipt"
35
47
  ];
48
+ var DEBUG_NAMESPACE = "silentdata:ethers-provider";
36
49
 
37
50
  // src/provider.ts
38
51
  var import_silentdatarollup_core = require("@appliedblockchain/silentdatarollup-core");
39
52
  var import_ethers = require("ethers");
53
+ function isPromise(value) {
54
+ return value && typeof value.then === "function";
55
+ }
40
56
  function getNetwork(name, chainId) {
41
57
  switch (name) {
42
58
  case import_silentdatarollup_core.NetworkName.MAINNET:
@@ -89,16 +105,28 @@ var SilentDataRollupProvider = class _SilentDataRollupProvider extends import_et
89
105
  if (Array.isArray(payload)) {
90
106
  throw new Error("Batch requests are not currently supported");
91
107
  }
92
- if (payload.method === "eth_call" && Array.isArray(payload.params)) {
108
+ const isEthCallOrEstimateGas = payload.method === "eth_call" || payload.method === "eth_estimateGas";
109
+ if (isEthCallOrEstimateGas && Array.isArray(payload.params)) {
93
110
  const txParams = payload.params[0];
94
111
  if (typeof txParams === "object" && txParams !== null) {
95
112
  txParams.from = await this.signer.getAddress();
96
113
  }
97
114
  }
115
+ let isPrivateLogsRequest = false;
116
+ if (payload.method === "eth_getLogs" && Array.isArray(payload.params) && payload.params.length > 0) {
117
+ const filter = payload.params[0];
118
+ if (filter && typeof filter === "object" && "_isPrivateEvent" in filter) {
119
+ isPrivateLogsRequest = !!filter._isPrivateEvent;
120
+ if (isPrivateLogsRequest) {
121
+ const { _isPrivateEvent, ...filterCopy } = filter;
122
+ payload.params[0] = filterCopy;
123
+ }
124
+ }
125
+ }
98
126
  const request = this._getConnection();
99
127
  request.body = JSON.stringify(payload);
100
128
  request.setHeader("content-type", "application/json");
101
- const requiresAuthHeaders = import_silentdatarollup_core.SIGN_RPC_METHODS.includes(payload.method) || (0, import_silentdatarollup_core.isSignableContractCall)(
129
+ const requiresAuthHeaders = isPrivateLogsRequest || import_silentdatarollup_core.SIGN_RPC_METHODS.includes(payload.method) || (0, import_silentdatarollup_core.isSignableContractCall)(
102
130
  payload,
103
131
  this.baseProvider.contractMethodsToSign,
104
132
  this.baseProvider.contract
@@ -151,9 +179,172 @@ var SilentDataRollupProvider = class _SilentDataRollupProvider extends import_et
151
179
  const clonedProvider = new _SilentDataRollupProvider(this.config);
152
180
  return clonedProvider;
153
181
  }
182
+ /**
183
+ * Helper method to configure a filter for private events
184
+ * @param filter - The original filter
185
+ * @param forcePrivateOnly - Whether to force filtering for only PrivateEvents
186
+ * @returns The configured filter with proper topics
187
+ */
188
+ configurePrivateEventsFilter(filter, forcePrivateOnly = false) {
189
+ const privateFilter = {
190
+ ...filter,
191
+ _isPrivateEvent: true
192
+ };
193
+ privateFilter.topics = privateFilter.topics || [];
194
+ if (forcePrivateOnly || privateFilter.eventSignature) {
195
+ if (forcePrivateOnly) {
196
+ privateFilter.topics = [
197
+ import_silentdatarollup_core.PRIVATE_EVENT_SIGNATURE_HASH,
198
+ // Only match PrivateEvent
199
+ ...privateFilter.topics.slice(1)
200
+ // Preserve any other topic filters
201
+ ];
202
+ }
203
+ if (privateFilter.eventSignature) {
204
+ const eventTypeHash = (0, import_silentdatarollup_core.calculateEventTypeHash)(
205
+ privateFilter.eventSignature
206
+ );
207
+ if (forcePrivateOnly) {
208
+ privateFilter.topics[1] = eventTypeHash;
209
+ } else {
210
+ privateFilter.topics = [
211
+ import_silentdatarollup_core.PRIVATE_EVENT_SIGNATURE_HASH,
212
+ // Only match PrivateEvent
213
+ eventTypeHash,
214
+ // Only match the specific event type
215
+ ...(privateFilter.topics || []).slice(2)
216
+ // Preserve any other topics
217
+ ];
218
+ }
219
+ delete privateFilter.eventSignature;
220
+ }
221
+ }
222
+ return privateFilter;
223
+ }
224
+ /**
225
+ * Gets logs for private events, including authentication headers
226
+ * @param filter - The filter parameters for logs
227
+ * @returns Array of logs matching the filter
228
+ */
229
+ async getAllLogs(filter = {}) {
230
+ const privateFilter = this.configurePrivateEventsFilter(filter, false);
231
+ return await this.getLogs(privateFilter);
232
+ }
233
+ /**
234
+ * Gets only private events (PrivateEvent logs), including authentication headers
235
+ * @param filter - The filter parameters for logs
236
+ * @returns Array of logs matching the filter, containing only PrivateEvent logs
237
+ */
238
+ async getPrivateLogs(filter = {}) {
239
+ const privateFilter = this.configurePrivateEventsFilter(filter, true);
240
+ return await this.getLogs(privateFilter);
241
+ }
242
+ /**
243
+ * Override of ethers' getLogs method that preserves our custom _isPrivateEvent property
244
+ *
245
+ * IMPORTANT: This method mimics the behavior of ethers' original getLogs implementation
246
+ * but adds a crucial step to preserve the _isPrivateEvent flag. We need this because:
247
+ *
248
+ * 1. Ethers' _getFilter method sanitizes filter objects, removing any non-standard properties
249
+ * 2. Our _isPrivateEvent flag would be stripped by this sanitization
250
+ * 3. We need the flag to reach the _send method to trigger the addition of auth headers
251
+ *
252
+ * The approach here is to run the normal filter processing, then re-attach our flag as a
253
+ * non-enumerable property to avoid JSON serialization issues. This allows the flag to
254
+ * survive until _send where we check for it to determine if auth headers are needed.
255
+ *
256
+ * @param _filter - The filter with our potential _isPrivateEvent property
257
+ * @returns Array of logs matching the filter
258
+ */
259
+ async getLogs(_filter) {
260
+ let filter = this._getFilter(_filter);
261
+ if (isPromise(filter)) {
262
+ filter = await filter;
263
+ }
264
+ if (typeof _filter === "object" && "_isPrivateEvent" in _filter && _filter._isPrivateEvent) {
265
+ Object.defineProperty(filter, "_isPrivateEvent", {
266
+ value: true,
267
+ enumerable: false
268
+ });
269
+ }
270
+ const { network, params } = await (0, import_ethers.resolveProperties)({
271
+ network: this.getNetwork(),
272
+ params: this._perform({ method: "getLogs", filter })
273
+ });
274
+ return params.map((p) => this._wrapLog(p, network));
275
+ }
276
+ };
277
+
278
+ // src/sdInterface.ts
279
+ var import_debug = __toESM(require("debug"));
280
+ var import_ethers2 = require("ethers");
281
+ var debugLog = (0, import_debug.default)(DEBUG_NAMESPACE);
282
+ var SDInterface = class extends import_ethers2.Interface {
283
+ /**
284
+ * Extends the parseLog method to handle PrivateEvent logs
285
+ * @param log - The log to parse
286
+ * @returns The parsed log description with additional private event details if applicable
287
+ */
288
+ parseLog(log) {
289
+ const parsedLog = super.parseLog(log);
290
+ if (!parsedLog) {
291
+ debugLog(
292
+ "Failed to parse log - no matching event found or event is anonymous"
293
+ );
294
+ return null;
295
+ }
296
+ if (parsedLog.name === "PrivateEvent") {
297
+ debugLog("Processing PrivateEvent log");
298
+ const eventType = parsedLog.args.eventType;
299
+ const payload = parsedLog.args.payload;
300
+ debugLog(
301
+ `PrivateEvent - eventType: ${eventType}, payload length: ${payload?.length || 0}`
302
+ );
303
+ parsedLog.innerLog = null;
304
+ try {
305
+ if (!payload || payload === "0x") {
306
+ debugLog("Empty payload for PrivateEvent, cannot decode inner log");
307
+ return parsedLog;
308
+ }
309
+ const syntheticLog = {
310
+ topics: [eventType],
311
+ data: payload
312
+ };
313
+ debugLog(`Created synthetic log with topic: ${eventType}`);
314
+ const eventFragment = this.getEvent(eventType);
315
+ if (!eventFragment) {
316
+ debugLog(`No matching event found for topic ${eventType}`);
317
+ return parsedLog;
318
+ }
319
+ debugLog(`Found matching event fragment: ${eventFragment.name}`);
320
+ try {
321
+ const innerLogDescription = super.parseLog(syntheticLog);
322
+ if (innerLogDescription) {
323
+ debugLog(
324
+ `Successfully decoded inner log: ${innerLogDescription.name}`
325
+ );
326
+ parsedLog.innerLog = innerLogDescription;
327
+ } else {
328
+ debugLog(
329
+ "Failed to parse inner log - no matching inner event found"
330
+ );
331
+ }
332
+ } catch (innerError) {
333
+ debugLog(`Failed to parse synthetic log for inner log:`, innerError);
334
+ }
335
+ } catch (error) {
336
+ debugLog(`Failed to decode private event payload:`, error);
337
+ }
338
+ } else {
339
+ debugLog(`Processing regular event: ${parsedLog.name}`);
340
+ }
341
+ return parsedLog;
342
+ }
154
343
  };
155
344
  // Annotate the CommonJS export names for ESM import in node:
156
345
  0 && (module.exports = {
346
+ DEBUG_NAMESPACE,
347
+ SDInterface,
157
348
  SIGN_RPC_METHODS,
158
349
  SilentDataRollupProvider
159
350
  });
package/dist/index.mjs CHANGED
@@ -6,9 +6,11 @@ var SIGN_RPC_METHODS = [
6
6
  "eth_getProof",
7
7
  "eth_getTransactionReceipt"
8
8
  ];
9
+ var DEBUG_NAMESPACE = "silentdata:ethers-provider";
9
10
 
10
11
  // src/provider.ts
11
12
  import {
13
+ calculateEventTypeHash,
12
14
  ChainId,
13
15
  HEADER_DELEGATE,
14
16
  HEADER_DELEGATE_SIGNATURE,
@@ -18,6 +20,7 @@ import {
18
20
  HEADER_TIMESTAMP,
19
21
  isSignableContractCall,
20
22
  NetworkName,
23
+ PRIVATE_EVENT_SIGNATURE_HASH,
21
24
  SIGN_RPC_METHODS as SIGN_RPC_METHODS2,
22
25
  SignatureType,
23
26
  SilentDataRollupBase
@@ -27,8 +30,12 @@ import {
27
30
  FetchRequest,
28
31
  JsonRpcProvider,
29
32
  Network,
33
+ resolveProperties,
30
34
  Wallet
31
35
  } from "ethers";
36
+ function isPromise(value) {
37
+ return value && typeof value.then === "function";
38
+ }
32
39
  function getNetwork(name, chainId) {
33
40
  switch (name) {
34
41
  case NetworkName.MAINNET:
@@ -81,16 +88,28 @@ var SilentDataRollupProvider = class _SilentDataRollupProvider extends JsonRpcPr
81
88
  if (Array.isArray(payload)) {
82
89
  throw new Error("Batch requests are not currently supported");
83
90
  }
84
- if (payload.method === "eth_call" && Array.isArray(payload.params)) {
91
+ const isEthCallOrEstimateGas = payload.method === "eth_call" || payload.method === "eth_estimateGas";
92
+ if (isEthCallOrEstimateGas && Array.isArray(payload.params)) {
85
93
  const txParams = payload.params[0];
86
94
  if (typeof txParams === "object" && txParams !== null) {
87
95
  txParams.from = await this.signer.getAddress();
88
96
  }
89
97
  }
98
+ let isPrivateLogsRequest = false;
99
+ if (payload.method === "eth_getLogs" && Array.isArray(payload.params) && payload.params.length > 0) {
100
+ const filter = payload.params[0];
101
+ if (filter && typeof filter === "object" && "_isPrivateEvent" in filter) {
102
+ isPrivateLogsRequest = !!filter._isPrivateEvent;
103
+ if (isPrivateLogsRequest) {
104
+ const { _isPrivateEvent, ...filterCopy } = filter;
105
+ payload.params[0] = filterCopy;
106
+ }
107
+ }
108
+ }
90
109
  const request = this._getConnection();
91
110
  request.body = JSON.stringify(payload);
92
111
  request.setHeader("content-type", "application/json");
93
- const requiresAuthHeaders = SIGN_RPC_METHODS2.includes(payload.method) || isSignableContractCall(
112
+ const requiresAuthHeaders = isPrivateLogsRequest || SIGN_RPC_METHODS2.includes(payload.method) || isSignableContractCall(
94
113
  payload,
95
114
  this.baseProvider.contractMethodsToSign,
96
115
  this.baseProvider.contract
@@ -143,8 +162,171 @@ var SilentDataRollupProvider = class _SilentDataRollupProvider extends JsonRpcPr
143
162
  const clonedProvider = new _SilentDataRollupProvider(this.config);
144
163
  return clonedProvider;
145
164
  }
165
+ /**
166
+ * Helper method to configure a filter for private events
167
+ * @param filter - The original filter
168
+ * @param forcePrivateOnly - Whether to force filtering for only PrivateEvents
169
+ * @returns The configured filter with proper topics
170
+ */
171
+ configurePrivateEventsFilter(filter, forcePrivateOnly = false) {
172
+ const privateFilter = {
173
+ ...filter,
174
+ _isPrivateEvent: true
175
+ };
176
+ privateFilter.topics = privateFilter.topics || [];
177
+ if (forcePrivateOnly || privateFilter.eventSignature) {
178
+ if (forcePrivateOnly) {
179
+ privateFilter.topics = [
180
+ PRIVATE_EVENT_SIGNATURE_HASH,
181
+ // Only match PrivateEvent
182
+ ...privateFilter.topics.slice(1)
183
+ // Preserve any other topic filters
184
+ ];
185
+ }
186
+ if (privateFilter.eventSignature) {
187
+ const eventTypeHash = calculateEventTypeHash(
188
+ privateFilter.eventSignature
189
+ );
190
+ if (forcePrivateOnly) {
191
+ privateFilter.topics[1] = eventTypeHash;
192
+ } else {
193
+ privateFilter.topics = [
194
+ PRIVATE_EVENT_SIGNATURE_HASH,
195
+ // Only match PrivateEvent
196
+ eventTypeHash,
197
+ // Only match the specific event type
198
+ ...(privateFilter.topics || []).slice(2)
199
+ // Preserve any other topics
200
+ ];
201
+ }
202
+ delete privateFilter.eventSignature;
203
+ }
204
+ }
205
+ return privateFilter;
206
+ }
207
+ /**
208
+ * Gets logs for private events, including authentication headers
209
+ * @param filter - The filter parameters for logs
210
+ * @returns Array of logs matching the filter
211
+ */
212
+ async getAllLogs(filter = {}) {
213
+ const privateFilter = this.configurePrivateEventsFilter(filter, false);
214
+ return await this.getLogs(privateFilter);
215
+ }
216
+ /**
217
+ * Gets only private events (PrivateEvent logs), including authentication headers
218
+ * @param filter - The filter parameters for logs
219
+ * @returns Array of logs matching the filter, containing only PrivateEvent logs
220
+ */
221
+ async getPrivateLogs(filter = {}) {
222
+ const privateFilter = this.configurePrivateEventsFilter(filter, true);
223
+ return await this.getLogs(privateFilter);
224
+ }
225
+ /**
226
+ * Override of ethers' getLogs method that preserves our custom _isPrivateEvent property
227
+ *
228
+ * IMPORTANT: This method mimics the behavior of ethers' original getLogs implementation
229
+ * but adds a crucial step to preserve the _isPrivateEvent flag. We need this because:
230
+ *
231
+ * 1. Ethers' _getFilter method sanitizes filter objects, removing any non-standard properties
232
+ * 2. Our _isPrivateEvent flag would be stripped by this sanitization
233
+ * 3. We need the flag to reach the _send method to trigger the addition of auth headers
234
+ *
235
+ * The approach here is to run the normal filter processing, then re-attach our flag as a
236
+ * non-enumerable property to avoid JSON serialization issues. This allows the flag to
237
+ * survive until _send where we check for it to determine if auth headers are needed.
238
+ *
239
+ * @param _filter - The filter with our potential _isPrivateEvent property
240
+ * @returns Array of logs matching the filter
241
+ */
242
+ async getLogs(_filter) {
243
+ let filter = this._getFilter(_filter);
244
+ if (isPromise(filter)) {
245
+ filter = await filter;
246
+ }
247
+ if (typeof _filter === "object" && "_isPrivateEvent" in _filter && _filter._isPrivateEvent) {
248
+ Object.defineProperty(filter, "_isPrivateEvent", {
249
+ value: true,
250
+ enumerable: false
251
+ });
252
+ }
253
+ const { network, params } = await resolveProperties({
254
+ network: this.getNetwork(),
255
+ params: this._perform({ method: "getLogs", filter })
256
+ });
257
+ return params.map((p) => this._wrapLog(p, network));
258
+ }
259
+ };
260
+
261
+ // src/sdInterface.ts
262
+ import debug from "debug";
263
+ import { Interface } from "ethers";
264
+ var debugLog = debug(DEBUG_NAMESPACE);
265
+ var SDInterface = class extends Interface {
266
+ /**
267
+ * Extends the parseLog method to handle PrivateEvent logs
268
+ * @param log - The log to parse
269
+ * @returns The parsed log description with additional private event details if applicable
270
+ */
271
+ parseLog(log) {
272
+ const parsedLog = super.parseLog(log);
273
+ if (!parsedLog) {
274
+ debugLog(
275
+ "Failed to parse log - no matching event found or event is anonymous"
276
+ );
277
+ return null;
278
+ }
279
+ if (parsedLog.name === "PrivateEvent") {
280
+ debugLog("Processing PrivateEvent log");
281
+ const eventType = parsedLog.args.eventType;
282
+ const payload = parsedLog.args.payload;
283
+ debugLog(
284
+ `PrivateEvent - eventType: ${eventType}, payload length: ${payload?.length || 0}`
285
+ );
286
+ parsedLog.innerLog = null;
287
+ try {
288
+ if (!payload || payload === "0x") {
289
+ debugLog("Empty payload for PrivateEvent, cannot decode inner log");
290
+ return parsedLog;
291
+ }
292
+ const syntheticLog = {
293
+ topics: [eventType],
294
+ data: payload
295
+ };
296
+ debugLog(`Created synthetic log with topic: ${eventType}`);
297
+ const eventFragment = this.getEvent(eventType);
298
+ if (!eventFragment) {
299
+ debugLog(`No matching event found for topic ${eventType}`);
300
+ return parsedLog;
301
+ }
302
+ debugLog(`Found matching event fragment: ${eventFragment.name}`);
303
+ try {
304
+ const innerLogDescription = super.parseLog(syntheticLog);
305
+ if (innerLogDescription) {
306
+ debugLog(
307
+ `Successfully decoded inner log: ${innerLogDescription.name}`
308
+ );
309
+ parsedLog.innerLog = innerLogDescription;
310
+ } else {
311
+ debugLog(
312
+ "Failed to parse inner log - no matching inner event found"
313
+ );
314
+ }
315
+ } catch (innerError) {
316
+ debugLog(`Failed to parse synthetic log for inner log:`, innerError);
317
+ }
318
+ } catch (error) {
319
+ debugLog(`Failed to decode private event payload:`, error);
320
+ }
321
+ } else {
322
+ debugLog(`Processing regular event: ${parsedLog.name}`);
323
+ }
324
+ return parsedLog;
325
+ }
146
326
  };
147
327
  export {
328
+ DEBUG_NAMESPACE,
329
+ SDInterface,
148
330
  SIGN_RPC_METHODS,
149
331
  SilentDataRollupProvider
150
332
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appliedblockchain/silentdatarollup-ethers-provider",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Ethers.js provider for Silent Data [Rollup]",
5
5
  "author": "Applied Blockchain",
6
6
  "homepage": "https://github.com/appliedblockchain/silent-data-rollup-providers#readme",
@@ -32,7 +32,8 @@
32
32
  "test": "jest"
33
33
  },
34
34
  "dependencies": {
35
- "@appliedblockchain/silentdatarollup-core": "1.0.1",
35
+ "@appliedblockchain/silentdatarollup-core": "1.0.3",
36
+ "debug": "4.3.7",
36
37
  "ethers": "6.13.2"
37
38
  },
38
39
  "devDependencies": {