import { allowNull, arrayOf, formatBlock } from 'ethers/providers';
import { accessListify, Contract, Interface, Result, Signature } from 'ethers';
import { getAddress } from 'ethers/address';
import { getBigInt } from 'ethers/utils';
import { ALL_ADDRESS } from '../core/index.js';
export class SimpleEthersError extends Error {
    e;
    constructor(message, e, stack) {
        super(message);
        this.stack = stack;
    }
    toString() {
        return this.message + '\n' + this.stack?.toString();
    }
}
export function transformEtherError(e, ctx, stack) {
    if (e instanceof SimpleEthersError) {
        return e;
    }
    const checkPage = 'Check here for possible cause and fix: https://docs.sentio.xyz/docs/handling-errors#ethers-error';
    let msg = '';
    const err = e;
    if (err.code === 'CALL_EXCEPTION' || err.code === 'BAD_DATA') {
        if (err.data === '0x') {
            if (ctx) {
                msg = `jsonrpc eth_call return '0x' (likely contract not existed) at chain ${ctx.chainId}, ${checkPage}:\n${e.message}`;
            }
            else {
                msg = `jsonrpc eth_call return '0x' (likely contract not existed), ${checkPage}:\n${e.message}`;
            }
            return new SimpleEthersError(msg, err, stack);
        }
        else {
            return new SimpleEthersError(`eth call error ${err.message}, ${checkPage}:\n${JSON.stringify(err)}`, err, stack);
        }
    }
    msg = `other error during call error ${e.message}\n` + JSON.stringify(e) + '\n' + stack?.toString() + '\n' + checkPage;
    return new Error(msg);
}
export function fixEmptyKey(result) {
    const keys = [];
    for (const [i, arg] of result.fragment.inputs.entries()) {
        if (arg.name === '') {
            keys.push('arg' + i);
        }
        else {
            keys.push(arg.name);
        }
    }
    return Result.fromItems(Array.from(result.args.values()), keys);
}
function formatTransactionReceipt(value) {
    return new FormattedTransactionReceipt(value);
}
class FormattedTransactionReceipt {
    data;
    constructor(data) {
        this.data = data;
    }
    _to;
    get to() {
        return this._to !== undefined ? this._to : (this._to = allowNull(getAddress)(this._to));
    }
    _from;
    get from() {
        return this._from ?? (this._from = getAddress(this.data.from));
    }
    _contractAddress;
    get contractAddress() {
        return this._contractAddress !== undefined
            ? this._contractAddress
            : (this._contractAddress = allowNull(getAddress)(this.data.contractAddress));
    }
    get hash() {
        return this.data.transactionHash;
    }
    get index() {
        return this.data.transactionIndex;
    }
    get blockHash() {
        return this.data.blockHash;
    }
    get blockNumber() {
        return this.data.blockNumber;
    }
    get logsBloom() {
        return this.data.logsBloom;
    }
    _logs;
    get logs() {
        return this._logs ?? (this._logs = arrayOf(formatLog, true)(this.data.logs));
    }
    _gasUsed;
    get gasUsed() {
        return this._gasUsed ?? (this._gasUsed = getBigInt(this.data.gasUsed));
    }
    _blobGasUsed;
    get blobGasUsed() {
        return this._blobGasUsed !== undefined
            ? this._blobGasUsed
            : (this._blobGasUsed = allowNull(getBigInt)(this.data.blobGasUsed));
    }
    _cumulativeGasUsed;
    get cumulativeGasUsed() {
        return this._cumulativeGasUsed ?? (this._cumulativeGasUsed = getBigInt(this.data.cumulativeGasUsed));
    }
    _gasPrice;
    get gasPrice() {
        return this._gasPrice !== undefined ? this._gasPrice : (this._gasPrice = allowNull(getBigInt)(this.data.gasPrice));
    }
    _blobGasPrice;
    get blobGasPrice() {
        return this._blobGasPrice !== undefined
            ? this._blobGasPrice
            : (this._blobGasPrice = allowNull(getBigInt)(this.data.blobGasPrice));
    }
    _effectiveGasPrice;
    get effectiveGasPrice() {
        return this._effectiveGasPrice !== undefined
            ? this._effectiveGasPrice
            : (this._effectiveGasPrice = allowNull(getBigInt)(this.data.effectiveGasPrice));
    }
    _l1Fee;
    get l1Fee() {
        return this._l1Fee !== undefined ? this._l1Fee : (this._l1Fee = allowNull(getBigInt)(this.data.l1Fee));
    }
    get type() {
        return this.data.type;
    }
    get status() {
        return this.data.status;
    }
    get root() {
        return this.data.root;
    }
}
export class FormattedLog {
    raw;
    constructor(raw) {
        this.raw = raw;
    }
    get transactionHash() {
        return this.raw.transactionHash;
    }
    get blockHash() {
        return this.raw.blockHash;
    }
    get blockNumber() {
        return this.raw.blockNumber;
    }
    get removed() {
        return this.raw.removed;
    }
    _address;
    get address() {
        return this._address ?? (this._address = getAddress(this.raw.address));
    }
    get topics() {
        return this.raw.topics;
    }
    get transactionIndex() {
        return this.raw.transactionIndex;
    }
    get index() {
        return this.raw.logIndex;
    }
    get data() {
        return this.raw.data;
    }
}
export class TypedEvent extends FormattedLog {
    log;
    name;
    args;
    constructor(log, name, args) {
        super(log.raw);
        this.log = log;
        this.name = name;
        this.args = args;
    }
}
function formatLog(log) {
    return new FormattedLog(log);
}
export function formatEthData(data) {
    // Return cached result if it exists
    if (data.__formattedEthData) {
        return data.__formattedEthData;
    }
    try {
        const log = data.log ? formatLog(data.log) : undefined;
        if (data.block && !data.block.transactions) {
            data.block.transactions = [];
        }
        const block = data.block ? formatRichBlock(data.block) : undefined;
        const trace = data.trace ? data.trace : undefined;
        let transaction = undefined;
        if (data.transaction) {
            if (!data.transaction.v) {
                data.transaction.v = '0x1c';
                data.transaction.r = '0x88ff6cf0fefd94db46111149ae4bfc179e9b94721fffd821d38d16464b3f71d0';
                data.transaction.s = '0x45e0aff800961cfce805daef7016b9b675c137a6a41a548f7b60a3484c06a33a';
            }
            transaction = formatTransactionResponse(data.transaction);
        }
        const transactionReceipt = data.transactionReceipt ? formatTransactionReceipt(data.transactionReceipt) : undefined;
        const result = {
            log,
            block,
            trace,
            transaction,
            transactionReceipt
        };
        // Cache the result on the input data object
        data.__formattedEthData = result;
        return result;
    }
    catch (e) {
        console.error('Error formatting eth data', e);
        return data;
    }
}
class FormattedTransactionResponse {
    raw;
    constructor(raw) {
        this.raw = raw;
    }
    get blockNumber() {
        return this.raw.blockNumber;
    }
    _blockHash;
    get blockHash() {
        if (this._blockHash !== undefined) {
            return this._blockHash;
        }
        if (this.raw.blockHash && getBigInt(this.raw.blockHash) === 0n) {
            this._blockHash = null;
        }
        else {
            this._blockHash = this.raw.blockHash;
        }
        return this._blockHash;
    }
    get hash() {
        return this.raw.hash;
    }
    get index() {
        return this.raw.transactionIndex;
    }
    get type() {
        if (this.raw.type === '0x' || this.raw.type == null) {
            return 0;
        }
        return this.raw.type;
    }
    _to;
    get to() {
        if (this._to != null) {
            return this._to;
        }
        if (this.raw.to && getBigInt(this.raw.to) === 0n) {
            return (this._to = '0x0000000000000000000000000000000000000000');
        }
        return (this._to = allowNull(getAddress)(this.raw.to));
    }
    _from;
    get from() {
        return this._from ?? (this._from = getAddress(this.raw.from));
    }
    get nonce() {
        return this.raw.nonce;
    }
    _gasLimit;
    get gasLimit() {
        return this._gasLimit ?? (this._gasLimit = getBigInt(this.raw.gasLimit ?? this.raw.gas));
    }
    get gas() {
        return this.gasLimit;
    }
    _gasPrice;
    get gasPrice() {
        return this._gasPrice ?? (this._gasPrice = getBigInt(this.raw.gasPrice));
    }
    _maxPriorityFeePerGas;
    get maxPriorityFeePerGas() {
        return this._maxPriorityFeePerGas !== undefined
            ? this._maxPriorityFeePerGas
            : (this._maxPriorityFeePerGas = allowNull(getBigInt)(this.raw.maxPriorityFeePerGas));
    }
    _maxFeePerGas;
    get maxFeePerGas() {
        return this._maxFeePerGas !== undefined
            ? this._maxFeePerGas
            : (this._maxFeePerGas = allowNull(getBigInt)(this.raw.maxFeePerGas));
    }
    _maxFeePerBlobGas;
    get maxFeePerBlobGas() {
        return this._maxFeePerBlobGas !== undefined
            ? this._maxFeePerBlobGas
            : (this._maxFeePerBlobGas = allowNull(getBigInt)(this.raw.maxFeePerBlobGas));
    }
    get data() {
        return this.raw.data ?? this.raw.input;
    }
    _value;
    get value() {
        return this._value ?? (this._value = getBigInt(this.raw.value));
    }
    _chainId;
    get chainId() {
        if (this._chainId != null) {
            return this._chainId;
        }
        this._chainId = allowNull(getBigInt)(this.raw.chainId);
        if (this._chainId == null && this.signature) {
            const legacyChainId = this.signature.legacyChainId;
            if (legacyChainId != null) {
                this._chainId = legacyChainId;
            }
        }
        return this._chainId ?? 0n;
    }
    _signature;
    get signature() {
        if (this._signature) {
            return this._signature;
        }
        if (this.raw.signature) {
            this._signature = Signature.from(this.raw.signature);
        }
        else {
            this._signature = Signature.from(this.raw);
        }
        return this._signature;
    }
    get accessList() {
        return this.raw.accessList ? accessListify(this.raw.accessList) : null;
    }
    get blobVersionedHashes() {
        return this.raw.blobVersionedHashes ?? null;
    }
}
export function formatTransactionResponse(value) {
    return new FormattedTransactionResponse(value);
}
export function formatRichBlock(block) {
    block = { ...block, ...formatBlock(block) };
    if (block.transactionReceipts) {
        block.transactionReceipts = block.transactionReceipts.map((t) => formatTransactionReceipt(t));
    }
    return block;
}
// const TRACE: Trace = {
//   action: {
//     from: '0xd771111cbfa2bbdafbf9f0e58b49b3f827da31f5',
//     callType: 'call',
//     gas: 0x12154,
//     input:
//       '0xb1a417f4000000000000000000000000d771111cbfa2bbdafbf9f0e58b49b3f827da31f5000000000000000000000000d771111cbfa2bbdafbf9f0e58b49b3f827da31f500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000131888b5aaf000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000',
//     to: '0x0baba1ad5be3a5c0a66e7ac838a129bf948f1ea4',
//     value: 0x131888b5aaf0000,
//   },
//   blockHash: '0x5451711bc530a7c04128fedbe149eb359c10eccd44a83909d448c5244c7eea26',
//   blockNumber: 15533908,
//   result: { gasUsed: 0x114c1, output: '0x' },
//   subtraces: 1,
//   traceAddress: [],
//   transactionHash: '0x66dce11d6217042ed709a38e507e7762c93b1bde4a0447ae7a243493bbdffc0e',
//   transactionPosition: 73,
//   type: 'call',
// }
// const trac2: Trace =
//     {
//       "action": {
//         "from": "0x95ba4cf87d6723ad9c0db21737d862be80e93911",
//         "gas": 0x630d0b,
//         "init": " ",
//         "value": 0x0
//       },
//       "blockHash": "0xb2f6986457f5a24ff088a0beb5567c8c1fe2da02687c78e743507ee7c982b977",
//       "blockNumber": 6082465,
//       "result": {
//         "address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
//         "code": " ",
//         "gasUsed": 0x74f42
//       },
//       "subtraces": 0,
//       "traceAddress": [],
//       "transactionHash": "0xe7e0fe390354509cd08c9a0168536938600ddc552b3f7cb96030ebef62e75895",
//       "transactionPosition": 22,
//       "type": "create"
//     }
export function isNullAddress(address) {
    try {
        // Normalize the input address
        const normalizedAddress = getAddress(address);
        // Check if the normalized address is equal to the null address (all zeros)
        return normalizedAddress === '0x0000000000000000000000000000000000000000';
    }
    catch (error) {
        return false;
    }
}
export function validateAndNormalizeAddress(address) {
    if (address === ALL_ADDRESS) {
        return address;
    }
    const normalizedAddress = getAddress(address);
    return normalizedAddress.toLowerCase();
}
export function encodeCallData(context, name, funcABI, values) {
    try {
        const iface = new Interface([funcABI]);
        const calldata = iface.encodeFunctionData(name, values);
        return {
            context,
            calldata
        };
    }
    catch (e) {
        const stack = new Error().stack;
        throw transformEtherError(e, undefined, stack);
    }
}
export function newContract(target, abi, runner) {
    return new Contract(target, abi, runner);
}
export function newInterface(fragments) {
    return new Interface(fragments);
}
//# sourceMappingURL=eth.js.map