UNPKG

@appliedblockchain/silentdatarollup-ethers-provider

Version:
336 lines (331 loc) 13.3 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { DEBUG_NAMESPACE: () => DEBUG_NAMESPACE, SDInterface: () => SDInterface, SilentDataRollupProvider: () => SilentDataRollupProvider }); module.exports = __toCommonJS(index_exports); // src/constants.ts var DEBUG_NAMESPACE = "silentdata:ethers-provider"; // src/provider.ts var import_silentdatarollup_core = require("@appliedblockchain/silentdatarollup-core"); var import_ethers = require("ethers"); function isPromise(value) { return value && typeof value.then === "function"; } function getNetwork(networkName, chainId) { if (chainId) { return new import_ethers.Network(networkName ?? "custom", chainId); } if (networkName === import_silentdatarollup_core.NetworkName.MAINNET) { return new import_ethers.Network(networkName, import_silentdatarollup_core.ChainId.MAINNET); } else if (networkName === import_silentdatarollup_core.NetworkName.TESTNET) { return new import_ethers.Network(networkName, import_silentdatarollup_core.ChainId.TESTNET); } return void 0; } var providerDefaultOptions = { batchMaxCount: 1 }; var SilentDataRollupProvider = class _SilentDataRollupProvider extends import_ethers.JsonRpcProvider { constructor(config) { (0, import_ethers.assertArgument)(config.rpcUrl, "rpcUrl is mandatory", "config", config); const network = getNetwork(config.network, config.chainId); const request = _SilentDataRollupProvider.getRequest({ rpcUrl: config.rpcUrl }); const combinedOptions = { ...providerDefaultOptions, ...config.options }; super(request, network, combinedOptions); (0, import_ethers.assertArgument)( config.signer || config.privateKey, "signer or privateKey is mandatory", "config", config ); this.baseProvider = new import_silentdatarollup_core.SilentDataRollupBase(config); this.config = config; this.config.authSignatureType = config.authSignatureType || import_silentdatarollup_core.SignatureType.Raw; if (config.signer) { try { this.signer = config.signer.connect(this); } catch { this.signer = config.signer; } } else { const wallet = new import_ethers.Wallet(config.privateKey); this.signer = wallet.connect(this); } } async _send(payload) { if (Array.isArray(payload)) { throw new Error("Batch requests are not currently supported"); } const isEthCallOrEstimateGas = payload.method === "eth_call" || payload.method === "eth_estimateGas"; if (isEthCallOrEstimateGas && Array.isArray(payload.params)) { const txParams = payload.params[0]; if (typeof txParams === "object" && txParams !== null) { txParams.from = await this.signer.getAddress(); } } let isPrivateLogsRequest = false; if (payload.method === "eth_getLogs" && Array.isArray(payload.params) && payload.params.length > 0) { const filter = payload.params[0]; if (filter && typeof filter === "object" && "_isPrivateEvent" in filter) { isPrivateLogsRequest = !!filter._isPrivateEvent; if (isPrivateLogsRequest) { const { _isPrivateEvent, ...filterWithoutPrivateEvent } = filter; payload.params[0] = filterWithoutPrivateEvent; } } } const request = this._getConnection(); request.body = JSON.stringify(payload); request.setHeader("content-type", "application/json"); const requiresAuthHeaders = isPrivateLogsRequest || import_silentdatarollup_core.SIGN_RPC_METHODS.includes(payload.method) || (0, import_silentdatarollup_core.isSignableContractCall)(payload, this.baseProvider.contracts); if (requiresAuthHeaders) { if (this.config.delegate) { const { [import_silentdatarollup_core.HEADER_DELEGATE]: xDelegate, [import_silentdatarollup_core.HEADER_DELEGATE_SIGNATURE]: xDelegateSignature, [import_silentdatarollup_core.HEADER_EIP712_DELEGATE_SIGNATURE]: xEip712DelegateSignature } = await this.baseProvider.getDelegateHeaders(this); request.setHeader(import_silentdatarollup_core.HEADER_DELEGATE, xDelegate); if (xDelegateSignature) { request.setHeader(import_silentdatarollup_core.HEADER_DELEGATE_SIGNATURE, xDelegateSignature); } if (xEip712DelegateSignature) { request.setHeader( import_silentdatarollup_core.HEADER_EIP712_DELEGATE_SIGNATURE, xEip712DelegateSignature ); } } const { [import_silentdatarollup_core.HEADER_TIMESTAMP]: xTimestamp, [import_silentdatarollup_core.HEADER_SIGNATURE]: xSignature, [import_silentdatarollup_core.HEADER_EIP712_SIGNATURE]: xEip712Signature } = await this.baseProvider.getAuthHeaders(this, payload); request.setHeader(import_silentdatarollup_core.HEADER_TIMESTAMP, xTimestamp); if (xSignature) { request.setHeader(import_silentdatarollup_core.HEADER_SIGNATURE, xSignature); } if (xEip712Signature) { request.setHeader(import_silentdatarollup_core.HEADER_EIP712_SIGNATURE, xEip712Signature); } } const response = await request.send(); response.assertOk(); let resp = response.bodyJson; if (!Array.isArray(resp)) { resp = [resp]; } return resp; } static getRequest({ rpcUrl }) { const request = new import_ethers.FetchRequest(rpcUrl); request.allowGzip = true; return request; } clone() { const clonedProvider = new _SilentDataRollupProvider(this.config); return clonedProvider; } /** * Helper method to configure a filter for private events * @param filter - The original filter * @param forcePrivateOnly - Whether to force filtering for only PrivateEvents * @returns The configured filter with proper topics */ configurePrivateEventsFilter(filter, forcePrivateOnly = false) { const privateFilter = { ...filter, _isPrivateEvent: true }; privateFilter.topics = privateFilter.topics || []; if (forcePrivateOnly || privateFilter.eventSignature) { if (forcePrivateOnly) { privateFilter.topics = [ import_silentdatarollup_core.PRIVATE_EVENT_SIGNATURE_HASH, // Only match PrivateEvent ...privateFilter.topics.slice(1) // Preserve any other topic filters ]; } if (privateFilter.eventSignature) { const eventTypeHash = (0, import_silentdatarollup_core.calculateEventTypeHash)( privateFilter.eventSignature ); if (forcePrivateOnly) { privateFilter.topics[1] = eventTypeHash; } else { privateFilter.topics = [ import_silentdatarollup_core.PRIVATE_EVENT_SIGNATURE_HASH, // Only match PrivateEvent eventTypeHash, // Only match the specific event type ...(privateFilter.topics || []).slice(2) // Preserve any other topics ]; } delete privateFilter.eventSignature; } } return privateFilter; } /** * Gets logs for private events, including authentication headers * @param filter - The filter parameters for logs * @returns Array of logs matching the filter */ async getAllLogs(filter = {}) { const privateFilter = this.configurePrivateEventsFilter(filter, false); return await this.getLogs(privateFilter); } /** * Gets only private events (PrivateEvent logs), including authentication headers * @param filter - The filter parameters for logs * @returns Array of logs matching the filter, containing only PrivateEvent logs */ async getPrivateLogs(filter = {}) { const privateFilter = this.configurePrivateEventsFilter(filter, true); return await this.getLogs(privateFilter); } /** * Override of ethers' getLogs method that preserves our custom _isPrivateEvent property * * IMPORTANT: This method mimics the behavior of ethers' original getLogs implementation * but adds a crucial step to preserve the _isPrivateEvent flag. We need this because: * * 1. Ethers' _getFilter method sanitizes filter objects, removing any non-standard properties * 2. Our _isPrivateEvent flag would be stripped by this sanitization * 3. We need the flag to reach the _send method to trigger the addition of auth headers * * The approach here is to run the normal filter processing, then re-attach our flag as a * non-enumerable property to avoid JSON serialization issues. This allows the flag to * survive until _send where we check for it to determine if auth headers are needed. * * @param _filter - The filter with our potential _isPrivateEvent property * @returns Array of logs matching the filter */ async getLogs(_filter) { let filter = this._getFilter(_filter); if (isPromise(filter)) { filter = await filter; } if (typeof _filter === "object" && "_isPrivateEvent" in _filter && _filter._isPrivateEvent) { Object.defineProperty(filter, "_isPrivateEvent", { value: true, enumerable: false }); } const { network, params } = await (0, import_ethers.resolveProperties)({ network: this.getNetwork(), params: this._perform({ method: "getLogs", filter }) }); return params.map((p) => this._wrapLog(p, network)); } }; // src/sdInterface.ts var import_debug = __toESM(require("debug")); var import_ethers2 = require("ethers"); var debugLog = (0, import_debug.default)(DEBUG_NAMESPACE); var SDInterface = class extends import_ethers2.Interface { /** * Extends the parseLog method to handle PrivateEvent logs * @param log - The log to parse * @returns The parsed log description with additional private event details if applicable */ parseLog(log) { const parsedLog = super.parseLog(log); if (!parsedLog) { debugLog( "Failed to parse log - no matching event found or event is anonymous" ); return null; } if (parsedLog.name === "PrivateEvent") { debugLog("Processing PrivateEvent log"); const eventType = parsedLog.args.eventType; const payload = parsedLog.args.payload; debugLog( `PrivateEvent - eventType: ${eventType}, payload length: ${payload?.length || 0}` ); parsedLog.innerLog = null; try { if (!payload || payload === "0x") { debugLog("Empty payload for PrivateEvent, cannot decode inner log"); return parsedLog; } const syntheticLog = { topics: [eventType], data: payload }; debugLog(`Created synthetic log with topic: ${eventType}`); const eventFragment = this.getEvent(eventType); if (!eventFragment) { debugLog(`No matching event found for topic ${eventType}`); return parsedLog; } debugLog(`Found matching event fragment: ${eventFragment.name}`); try { const innerLogDescription = super.parseLog(syntheticLog); if (innerLogDescription) { debugLog( `Successfully decoded inner log: ${innerLogDescription.name}` ); parsedLog.innerLog = innerLogDescription; } else { debugLog( "Failed to parse inner log - no matching inner event found" ); } } catch (innerError) { debugLog(`Failed to parse synthetic log for inner log:`, innerError); } } catch (error) { debugLog(`Failed to decode private event payload:`, error); } } else { debugLog(`Processing regular event: ${parsedLog.name}`); } return parsedLog; } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { DEBUG_NAMESPACE, SDInterface, SilentDataRollupProvider });