"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.EtherJSProvider = exports.createEtherJSProvider = void 0;
const ethers_1 = require("ethers");
const base_provider_1 = require("./base.provider");
const interfaces_1 = require("./interfaces");
const rate_limiter_1 = require("../transports/rate-limiter");
const trie_verifier_1 = require("./trie-verifier");
const value_normalization_1 = require("../value-normalization");
const createEtherJSProvider = (options) => new EtherJSProvider(options);
exports.createEtherJSProvider = createEtherJSProvider;
class EtherJSProvider extends base_provider_1.BaseNodeProvider {
    type = interfaces_1.NodeProviderTypes.ETHERJS;
    httpUrl;
    wsUrl;
    network;
    rateLimiter;
    requestId = 1;
    responseTimeout;
    constructor(options) {
        super(options);
        this.httpUrl = new URL(options.httpUrl).toString();
        this.wsUrl = options.wsUrl;
        this.network = options.network;
        this.responseTimeout = options.responseTimeout ?? 5000;
        this._hasWebSocketUrl = !!this.wsUrl;
        const ethersNetwork = new ethers_1.ethers.Network(this.network.nativeCurrencySymbol, this.network.chainId);
        const fetchRequest = new ethers_1.ethers.FetchRequest(this.httpUrl);
        fetchRequest.timeout = this.responseTimeout;
        this._httpClient = new ethers_1.ethers.JsonRpcProvider(fetchRequest, ethersNetwork);
        if (this.wsUrl)
            this.initializeWebSocket();
        this.rateLimiter = new rate_limiter_1.RateLimiter(options.rateLimits);
    }
    get connectionOptions() {
        return {
            type: this.type,
            uniqName: this.uniqName,
            httpUrl: this.httpUrl,
            wsUrl: this.wsUrl,
            network: this.network,
            rateLimits: this.rateLimits,
        };
    }
    initializeWebSocket() {
        if (!this.wsUrl)
            return;
        try {
            const ethersNetwork = new ethers_1.ethers.Network(this.network.nativeCurrencySymbol, this.network.chainId);
            this._wsClient = new ethers_1.ethers.WebSocketProvider(this.wsUrl, ethersNetwork);
            this._wsClient.websocket.on('open', () => {
                this._isWebSocketConnected = true;
            });
            this._wsClient.websocket.on('close', () => {
                this._isWebSocketConnected = false;
            });
            this._wsClient.websocket.on('error', () => {
                this._isWebSocketConnected = false;
            });
        }
        catch {
            this._isWebSocketConnected = false;
        }
    }
    healthcheckWebSocket() {
        if (!this._hasWebSocketUrl || !this._wsClient)
            return false;
        const ws = this._wsClient.websocket;
        return ws && ws.readyState === ws.READY_STATE_OPEN && this._isWebSocketConnected;
    }
    async handleConnectionError(error, _methodName) {
        throw error;
    }
    async healthcheck() {
        try {
            const results = await this.rateLimiter.execute([{ method: 'eth_blockNumber', params: [] }], (calls) => this._batchRpcCall(calls));
            return !!results[0];
        }
        catch {
            return false;
        }
    }
    async connect() {
        if (!(await this.healthcheck()))
            throw new Error('Cannot connect to the node');
        try {
            const raw = await this._httpClient.send('eth_chainId', []);
            const parsed = parseInt(raw, 16);
            if (parsed !== this.network.chainId) {
                throw new Error(`Chain ID mismatch: expected ${this.network.chainId}, got ${parsed}`);
            }
        }
        catch (e) {
            throw new Error(`Chain validation failed: ${e}`);
        }
        if (this._hasWebSocketUrl && !this._wsClient)
            this.initializeWebSocket();
    }
    async disconnect() {
        await this.rateLimiter.stop();
        if (this._wsClient) {
            this._wsClient.destroy();
            this._wsClient = undefined;
            this._isWebSocketConnected = false;
        }
        this._httpClient.destroy();
    }
    async reconnectWebSocket() {
        if (!this._hasWebSocketUrl || !this.wsUrl)
            throw new Error('No WebSocket URL');
        if (this._wsClient) {
            this._wsClient.destroy();
            this._wsClient = undefined;
            this._isWebSocketConnected = false;
        }
        this.initializeWebSocket();
        await new Promise((r) => setTimeout(r, 1000));
        if (!this.healthcheckWebSocket())
            throw new Error('WebSocket reconnect failed');
    }
    async _batchRpcCall(calls) {
        const payload = calls.map((c) => ({ jsonrpc: '2.0', method: c.method, params: c.params, id: this.requestId++ }));
        const response = await fetch(this.httpUrl, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(payload),
            signal: AbortSignal.timeout(this.responseTimeout),
        });
        if (!response.ok)
            throw new Error(`HTTP ${response.status}: ${response.statusText}`);
        const results = await response.json();
        return (Array.isArray(results) ? results : [results]).map((r) => {
            if (r === null || r === undefined)
                return null;
            if (r.error)
                throw new Error(`JSON-RPC Error ${r.error.code}: ${r.error.message}`);
            return r.result;
        });
    }
    async _batchWsCall(calls) {
        if (!this._wsClient || !this.healthcheckWebSocket())
            throw new Error('WebSocket not available');
        const results = await Promise.all(calls.map((c) => this._wsClient.send(c.method, c.params)));
        return results.map((r) => {
            if (r === null || r === undefined)
                return null;
            if (r?.error)
                throw new Error(`JSON-RPC Error ${r.error.code}: ${r.error.message}`);
            return r;
        });
    }
    get batchCall() {
        return this._wsClient && this.healthcheckWebSocket()
            ? (calls) => this._batchWsCall(calls)
            : (calls) => this._batchRpcCall(calls);
    }
    async executeWithErrorHandling(op, method) {
        try {
            return await op();
        }
        catch (e) {
            await this.handleConnectionError(e, method);
            throw e;
        }
    }
    subscribeToNewBlocks(callback) {
        if (!this._wsClient || !this.healthcheckWebSocket())
            throw new Error('WebSocket not available');
        this._wsClient.on('block', callback);
        return {
            unsubscribe: () => {
                if (this._wsClient)
                    this._wsClient.off('block', callback);
            },
        };
    }
    // ===== BLOCK METHODS =====
    async getBlockHeight() {
        return this.executeWithErrorHandling(async () => {
            const results = await this.rateLimiter.execute([{ method: 'eth_blockNumber', params: [] }], this.batchCall);
            return parseInt(results[0], 16);
        }, 'getBlockHeight');
    }
    async getManyBlocksByHeights(heights, fullTransactions = false, verifyTrie = false) {
        if (!heights.length)
            return [];
        return this.executeWithErrorHandling(async () => {
            const requests = heights.map((h) => ({
                method: 'eth_getBlockByNumber',
                params: [`0x${h.toString(16)}`, fullTransactions],
            }));
            const rawBlocks = await this.rateLimiter.execute(requests, this.batchCall);
            return Promise.all(rawBlocks.map(async (block, i) => {
                if (!block || block.error)
                    return null;
                if (verifyTrie && fullTransactions && block.transactions?.length && block.transactionsRoot) {
                    const valid = await trie_verifier_1.EvmTrieVerifier.verifyTransactionsRoot(block.transactions, block.transactionsRoot);
                    if (!valid)
                        throw new Error(`Transactions root mismatch for block ${heights[i]}`);
                }
                const nb = this.normalizeRawBlock(block);
                if (nb.blockNumber == null)
                    nb.blockNumber = heights[i];
                return nb;
            }));
        }, 'getManyBlocksByHeights');
    }
    async getManyBlocksByHashes(hashes, fullTransactions = false) {
        if (!hashes.length)
            return [];
        return this.executeWithErrorHandling(async () => {
            const requests = hashes.map((h) => ({ method: 'eth_getBlockByHash', params: [h, fullTransactions] }));
            const rawBlocks = await this.rateLimiter.execute(requests, this.batchCall);
            return rawBlocks.map((b) => (!b || b.error ? null : this.normalizeRawBlock(b)));
        }, 'getManyBlocksByHashes');
    }
    async getManyBlocksStatsByHeights(heights) {
        if (!heights.length)
            return [];
        return this.executeWithErrorHandling(async () => {
            const requests = heights.map((h) => ({ method: 'eth_getBlockByNumber', params: [`0x${h.toString(16)}`, false] }));
            const rawBlocks = await this.rateLimiter.execute(requests, this.batchCall);
            return rawBlocks.map((b, i) => {
                if (!b || b.error)
                    return null;
                const stats = this.normalizeBlockStats(b);
                if (!stats.number)
                    stats.number = heights[i];
                return stats;
            });
        }, 'getManyBlocksStatsByHeights');
    }
    async getManyBlocksWithReceipts(heights, fullTransactions = false, verifyTrie = false) {
        if (!heights.length)
            return [];
        return this.executeWithErrorHandling(async () => {
            const numeric = heights.map(Number);
            const rawBlocks = await this.getManyBlocksByHeights(numeric, fullTransactions, verifyTrie && fullTransactions);
            const receiptsRoots = verifyTrie ? rawBlocks.map((b) => b?.receiptsRoot) : undefined;
            const allReceipts = await this.getManyBlocksReceipts(numeric, rawBlocks, verifyTrie, receiptsRoots);
            return rawBlocks.map((rawBlock, i) => {
                if (!rawBlock)
                    return null;
                return { ...rawBlock, receipts: allReceipts[i] || [] };
            });
        }, 'getManyBlocksWithReceipts');
    }
    async getManyBlocksReceipts(heights, blocks, verifyTrie = false, receiptsRoots) {
        if (!heights.length)
            return [];
        const strategy = this.network.receiptsStrategy ?? 'auto';
        if (strategy === 'block-receipts' || strategy === 'auto') {
            try {
                return await this.getManyBlocksReceiptsViaBlockRpc(heights, verifyTrie, receiptsRoots);
            }
            catch (error) {
                if (strategy === 'block-receipts')
                    throw error;
            }
        }
        return this.getManyBlocksReceiptsViaTransactions(heights, blocks, verifyTrie, receiptsRoots);
    }
    async getManyBlocksReceiptsViaBlockRpc(heights, verifyTrie = false, receiptsRoots) {
        const requests = heights.map((h) => ({ method: 'eth_getBlockReceipts', params: [`0x${h.toString(16)}`] }));
        const rawAll = await this.rateLimiter.execute(requests, this.batchCall);
        return Promise.all(rawAll.map(async (rawReceipts, i) => {
            if (!rawReceipts || !Array.isArray(rawReceipts))
                return [];
            if (verifyTrie && receiptsRoots?.[i]) {
                const valid = await trie_verifier_1.EvmTrieVerifier.verifyReceiptsRoot(rawReceipts, receiptsRoots[i]);
                if (!valid)
                    throw new Error(`Receipts root mismatch for block ${heights[i]}`);
            }
            return rawReceipts.map((r) => this.normalizeRawReceiptForHeight(r, heights[i]));
        }));
    }
    async getManyBlocksReceiptsViaTransactions(heights, blocks, verifyTrie = false, receiptsRoots) {
        return Promise.all(blocks.map(async (block, i) => {
            if (!block)
                return [];
            const hashes = (block.transactions ?? [])
                .map((tx) => (typeof tx === 'string' ? tx : tx.hash))
                .filter(Boolean);
            if (hashes.length === 0)
                return [];
            const requests = hashes.map((hash) => ({ method: 'eth_getTransactionReceipt', params: [hash] }));
            const rawReceipts = await this.rateLimiter.execute(requests, this.batchCall);
            const receipts = rawReceipts
                .filter((receipt) => receipt !== null)
                .map((receipt) => this.normalizeRawReceiptForHeight(receipt, heights[i]));
            if (verifyTrie && receiptsRoots?.[i]) {
                const valid = await trie_verifier_1.EvmTrieVerifier.verifyReceiptsRoot(rawReceipts.filter(Boolean), receiptsRoots[i]);
                if (!valid)
                    throw new Error(`Receipts root mismatch for block ${heights[i]}`);
            }
            return receipts;
        }));
    }
    normalizeRawReceiptForHeight(raw, fallbackHeight) {
        const receipt = this.normalizeRawReceipt(raw);
        if (receipt.blockNumber == null)
            receipt.blockNumber = fallbackHeight;
        return receipt;
    }
    async assertTraceSupport() {
        const height = await this.getBlockHeight();
        const hexHeight = `0x${height.toString(16)}`;
        const strategy = this.network.traceStrategy ?? 'auto';
        const errors = [];
        if (strategy === 'debug-trace' || strategy === 'auto') {
            try {
                await this._batchRpcCall([
                    { method: 'debug_traceBlockByNumber', params: [hexHeight, { tracer: 'callTracer' }] },
                ]);
                return;
            }
            catch (e) {
                errors.push(`debug_traceBlockByNumber: ${e?.message ?? String(e)}`);
                if (strategy === 'debug-trace')
                    throw new Error(`Trace API debug-trace is not supported by provider ${this.uniqName}: ${errors.join(' | ')}`);
            }
        }
        if (strategy === 'parity-trace' || strategy === 'auto') {
            try {
                await this._batchRpcCall([{ method: 'trace_block', params: [hexHeight] }]);
                return;
            }
            catch (e) {
                errors.push(`trace_block: ${e?.message ?? String(e)}`);
                if (strategy === 'parity-trace')
                    throw new Error(`Trace API parity-trace is not supported by provider ${this.uniqName}: ${errors.join(' | ')}`);
            }
        }
        throw new Error(`Trace API is not supported by provider ${this.uniqName}: ${errors.join(' | ')}`);
    }
    // ===== TRACE METHODS =====
    /**
     * Tries debug_traceBlockByNumber (Geth) → trace_block (Erigon/OE).
     * Returns [] if neither is supported.
     */
    async getTracesByBlockNumber(blockNumber) {
        const hexHeight = `0x${blockNumber.toString(16)}`;
        const strategy = this.network.traceStrategy ?? 'auto';
        const errors = [];
        if (strategy === 'debug-trace' || strategy === 'auto') {
            try {
                const results = await this._batchRpcCall([
                    {
                        method: 'debug_traceBlockByNumber',
                        params: [hexHeight, { tracer: 'callTracer' }],
                    },
                ]);
                if (results[0] && Array.isArray(results[0])) {
                    return results[0].map((t) => this.normalizeGethTrace(t, blockNumber));
                }
            }
            catch (e) {
                errors.push(`debug_traceBlockByNumber: ${e?.message ?? String(e)}`);
                if (strategy === 'debug-trace')
                    throw new Error(`Trace API debug-trace is not supported by provider ${this.uniqName} for block ${blockNumber}: ${errors.join(' | ')}`);
            }
        }
        if (strategy === 'parity-trace' || strategy === 'auto') {
            try {
                const results = await this._batchRpcCall([{ method: 'trace_block', params: [hexHeight] }]);
                if (results[0] && Array.isArray(results[0])) {
                    return results[0].map((t) => this.normalizeParityTrace(t));
                }
            }
            catch (e) {
                errors.push(`trace_block: ${e?.message ?? String(e)}`);
                if (strategy === 'parity-trace')
                    throw new Error(`Trace API parity-trace is not supported by provider ${this.uniqName} for block ${blockNumber}: ${errors.join(' | ')}`);
            }
        }
        throw new Error(`Trace API is not supported by provider ${this.uniqName} for block ${blockNumber}: ${errors.join(' | ')}`);
    }
    async getTracesByTxHash(hash) {
        const strategy = this.network.traceStrategy ?? 'auto';
        const errors = [];
        if (strategy === 'debug-trace' || strategy === 'auto') {
            try {
                const results = await this._batchRpcCall([
                    {
                        method: 'debug_traceTransaction',
                        params: [hash, { tracer: 'callTracer' }],
                    },
                ]);
                if (results[0])
                    return [this.normalizeGethTrace(results[0], undefined, hash)];
            }
            catch (e) {
                errors.push(`debug_traceTransaction: ${e?.message ?? String(e)}`);
                if (strategy === 'debug-trace')
                    throw new Error(`Trace API debug-trace is not supported by provider ${this.uniqName} for transaction ${hash}: ${errors.join(' | ')}`);
            }
        }
        if (strategy === 'parity-trace' || strategy === 'auto') {
            try {
                const results = await this._batchRpcCall([{ method: 'trace_transaction', params: [hash] }]);
                if (results[0] && Array.isArray(results[0])) {
                    return results[0].map((t) => this.normalizeParityTrace(t));
                }
            }
            catch (e) {
                errors.push(`trace_transaction: ${e?.message ?? String(e)}`);
                if (strategy === 'parity-trace')
                    throw new Error(`Trace API parity-trace is not supported by provider ${this.uniqName} for transaction ${hash}: ${errors.join(' | ')}`);
            }
        }
        throw new Error(`Trace API is not supported by provider ${this.uniqName} for transaction ${hash}: ${errors.join(' | ')}`);
    }
    // ===== MEMPOOL METHODS =====
    subscribeToPendingTransactions(callback) {
        if (!this._wsClient || !this.healthcheckWebSocket()) {
            throw new Error('WebSocket not available for pending transactions subscription');
        }
        const handler = (txHash) => callback(txHash);
        this._wsClient.on('pending', handler);
        return {
            unsubscribe: () => {
                if (this._wsClient)
                    this._wsClient.off('pending', handler);
            },
        };
    }
    async getRawMempool() {
        try {
            const results = await this._batchRpcCall([{ method: 'txpool_content', params: [] }]);
            return results[0]?.pending || {};
        }
        catch {
            return {};
        }
    }
    async getTransactionByHash(hash) {
        try {
            const results = await this.rateLimiter.execute([{ method: 'eth_getTransactionByHash', params: [hash] }], this.batchCall);
            if (!results[0])
                return null;
            return this.normalizeRawTransaction(results[0]);
        }
        catch {
            return null;
        }
    }
    // ===== NORMALIZATION =====
    normalizeGethTrace(raw, blockNumber, txHash) {
        return {
            transactionHash: txHash || raw.txHash || raw.transactionHash || '',
            transactionPosition: raw.index ?? 0,
            type: (raw.type || 'call').toLowerCase(),
            action: raw.from !== undefined ? { from: raw.from, to: raw.to, value: raw.value, gas: raw.gas, input: raw.input } : raw,
            result: raw.output !== undefined ? { output: raw.output, gasUsed: raw.gasUsed } : undefined,
            error: raw.error,
            subtraces: raw.calls?.length || 0,
            traceAddress: [],
        };
    }
    normalizeParityTrace(raw) {
        return {
            transactionHash: raw.transactionHash || '',
            transactionPosition: raw.transactionPosition || 0,
            type: raw.type || 'call',
            action: raw.action || {},
            result: raw.result,
            error: raw.error,
            subtraces: raw.subtraces || 0,
            traceAddress: raw.traceAddress || [],
        };
    }
    normalizeBlockStats(raw) {
        const gasLimit = (0, value_normalization_1.quantityToNumber)(raw.gasLimit);
        const gasUsed = (0, value_normalization_1.quantityToNumber)(raw.gasUsed);
        return {
            hash: (0, value_normalization_1.normalizeHex)(raw.hash),
            number: (0, value_normalization_1.quantityToNumber)(raw.number ?? raw.blockNumber),
            ...(raw.size !== undefined && { size: (0, value_normalization_1.quantityToNumber)(raw.size) }),
            gasLimit,
            gasUsed,
            gasUsedPercentage: gasLimit > 0 ? Math.round((gasUsed / gasLimit) * 100 * 100) / 100 : 0,
            timestamp: (0, value_normalization_1.quantityToNumber)(raw.timestamp),
            transactionCount: raw.transactions?.length || 0,
            baseFeePerGas: raw.baseFeePerGas ? (0, value_normalization_1.quantityToDecimalString)(raw.baseFeePerGas) : undefined,
            blobGasUsed: raw.blobGasUsed ? (0, value_normalization_1.quantityToDecimalString)(raw.blobGasUsed) : undefined,
            excessBlobGas: raw.excessBlobGas ? (0, value_normalization_1.quantityToDecimalString)(raw.excessBlobGas) : undefined,
            miner: (0, value_normalization_1.normalizeAddress)(raw.miner ?? raw.author ?? raw.feeRecipient),
            ...(raw.difficulty !== undefined && { difficulty: (0, value_normalization_1.quantityToDecimalString)(raw.difficulty) }),
            parentHash: (0, value_normalization_1.normalizeHex)(raw.parentHash),
            unclesCount: raw.uncles?.length || 0,
        };
    }
    normalizeRawBlock(raw) {
        const block = {
            hash: (0, value_normalization_1.normalizeHex)(raw.hash),
            parentHash: (0, value_normalization_1.normalizeHex)(raw.parentHash),
            ...(raw.nonce !== undefined && { nonce: (0, value_normalization_1.normalizeHex)(raw.nonce) }),
            ...(raw.sha3Uncles !== undefined && { sha3Uncles: (0, value_normalization_1.normalizeHex)(raw.sha3Uncles) }),
            ...(raw.logsBloom !== undefined && { logsBloom: (0, value_normalization_1.normalizeHex)(raw.logsBloom) }),
            transactionsRoot: (0, value_normalization_1.normalizeHex)(raw.transactionsRoot),
            stateRoot: (0, value_normalization_1.normalizeHex)(raw.stateRoot),
            ...(raw.receiptsRoot !== undefined && { receiptsRoot: (0, value_normalization_1.normalizeHex)(raw.receiptsRoot) }),
            miner: (0, value_normalization_1.normalizeAddress)(raw.miner ?? raw.author ?? raw.feeRecipient),
            ...(raw.difficulty !== undefined && { difficulty: (0, value_normalization_1.quantityToDecimalString)(raw.difficulty) }),
            ...(raw.totalDifficulty !== undefined && { totalDifficulty: (0, value_normalization_1.quantityToDecimalString)(raw.totalDifficulty) }),
            extraData: (0, value_normalization_1.normalizeHex)(raw.extraData),
            ...(raw.size !== undefined && { size: (0, value_normalization_1.quantityToNumber)(raw.size) }),
            gasLimit: (0, value_normalization_1.quantityToNumber)(raw.gasLimit),
            gasUsed: (0, value_normalization_1.quantityToNumber)(raw.gasUsed),
            timestamp: (0, value_normalization_1.quantityToNumber)(raw.timestamp),
            uncles: raw.uncles || [],
            baseFeePerGas: raw.baseFeePerGas ? (0, value_normalization_1.quantityToDecimalString)(raw.baseFeePerGas) : undefined,
            withdrawals: raw.withdrawals,
            withdrawalsRoot: raw.withdrawalsRoot,
            blobGasUsed: raw.blobGasUsed ? (0, value_normalization_1.quantityToDecimalString)(raw.blobGasUsed) : undefined,
            excessBlobGas: raw.excessBlobGas ? (0, value_normalization_1.quantityToDecimalString)(raw.excessBlobGas) : undefined,
            parentBeaconBlockRoot: raw.parentBeaconBlockRoot,
            transactions: raw.transactions?.map((tx) => typeof tx === 'string' ? (0, value_normalization_1.normalizeHex)(tx) : this.normalizeRawTransaction(tx)),
        };
        if (raw.blockNumber != null)
            block.blockNumber = (0, value_normalization_1.quantityToNumber)(raw.blockNumber);
        else if (raw.number != null)
            block.blockNumber = (0, value_normalization_1.quantityToNumber)(raw.number);
        return block;
    }
    normalizeRawTransaction(raw) {
        return {
            hash: (0, value_normalization_1.normalizeHex)(raw.hash),
            nonce: (0, value_normalization_1.quantityToNumber)(raw.nonce),
            from: (0, value_normalization_1.normalizeAddress)(raw.from),
            to: raw.to ? (0, value_normalization_1.normalizeAddress)(raw.to) : null,
            value: (0, value_normalization_1.quantityToDecimalString)(raw.value),
            gas: (0, value_normalization_1.quantityToNumber)(raw.gas),
            input: (0, value_normalization_1.normalizeHex)(raw.input || raw.data || '0x'),
            blockHash: raw.blockHash ? (0, value_normalization_1.normalizeHex)(raw.blockHash) : null,
            blockNumber: (0, value_normalization_1.optionalQuantityToNumber)(raw.blockNumber),
            transactionIndex: (0, value_normalization_1.optionalQuantityToNumber)(raw.transactionIndex),
            gasPrice: raw.gasPrice ? (0, value_normalization_1.quantityToDecimalString)(raw.gasPrice) : undefined,
            chainId: raw.chainId ? (0, value_normalization_1.quantityToNumber)(raw.chainId) : undefined,
            v: raw.v ? (0, value_normalization_1.normalizeHex)(raw.v) : undefined,
            r: raw.r ? (0, value_normalization_1.normalizeHex)(raw.r) : undefined,
            s: raw.s ? (0, value_normalization_1.normalizeHex)(raw.s) : undefined,
            type: raw.type ? (0, value_normalization_1.normalizeHex)(raw.type) : '0x0',
            maxFeePerGas: raw.maxFeePerGas ? (0, value_normalization_1.quantityToDecimalString)(raw.maxFeePerGas) : undefined,
            maxPriorityFeePerGas: raw.maxPriorityFeePerGas ? (0, value_normalization_1.quantityToDecimalString)(raw.maxPriorityFeePerGas) : undefined,
            accessList: raw.accessList,
            maxFeePerBlobGas: raw.maxFeePerBlobGas ? (0, value_normalization_1.quantityToDecimalString)(raw.maxFeePerBlobGas) : undefined,
            blobVersionedHashes: raw.blobVersionedHashes,
        };
    }
    normalizeRawReceipt(raw) {
        return {
            transactionHash: (0, value_normalization_1.normalizeHex)(raw.transactionHash),
            transactionIndex: (0, value_normalization_1.quantityToNumber)(raw.transactionIndex),
            blockHash: (0, value_normalization_1.normalizeHex)(raw.blockHash),
            blockNumber: (0, value_normalization_1.quantityToNumber)(raw.blockNumber),
            from: (0, value_normalization_1.normalizeAddress)(raw.from),
            to: raw.to ? (0, value_normalization_1.normalizeAddress)(raw.to) : null,
            cumulativeGasUsed: (0, value_normalization_1.quantityToNumber)(raw.cumulativeGasUsed),
            gasUsed: (0, value_normalization_1.quantityToNumber)(raw.gasUsed),
            contractAddress: raw.contractAddress ? (0, value_normalization_1.normalizeAddress)(raw.contractAddress) : null,
            logs: (raw.logs || []).map((log) => ({
                address: (0, value_normalization_1.normalizeAddress)(log.address),
                topics: log.topics || [],
                data: (0, value_normalization_1.normalizeHex)(log.data || '0x'),
                blockNumber: (0, value_normalization_1.optionalQuantityToNumber)(log.blockNumber),
                transactionHash: log.transactionHash ? (0, value_normalization_1.normalizeHex)(log.transactionHash) : null,
                transactionIndex: (0, value_normalization_1.optionalQuantityToNumber)(log.transactionIndex),
                blockHash: log.blockHash ? (0, value_normalization_1.normalizeHex)(log.blockHash) : null,
                logIndex: (0, value_normalization_1.optionalQuantityToNumber)(log.logIndex),
                removed: log.removed || false,
            })),
            ...(raw.logsBloom !== undefined && { logsBloom: (0, value_normalization_1.normalizeHex)(raw.logsBloom) }),
            ...(raw.status !== undefined && {
                status: raw.status === '0x1' || raw.status === 1 || raw.status === true ? '0x1' : '0x0',
            }),
            ...(raw.root !== undefined && { root: (0, value_normalization_1.normalizeHex)(raw.root) }),
            type: raw.type ? (0, value_normalization_1.normalizeHex)(raw.type) : '0x0',
            ...(raw.effectiveGasPrice !== undefined && { effectiveGasPrice: (0, value_normalization_1.quantityToDecimalString)(raw.effectiveGasPrice) }),
            blobGasUsed: raw.blobGasUsed ? (0, value_normalization_1.quantityToDecimalString)(raw.blobGasUsed) : undefined,
            blobGasPrice: raw.blobGasPrice ? (0, value_normalization_1.quantityToDecimalString)(raw.blobGasPrice) : undefined,
        };
    }
}
exports.EtherJSProvider = EtherJSProvider;
