PHP WebShell

Текущая директория: /usr/lib/node_modules/bitgo/node_modules/viem/utils/abi

Просмотр файла: parseEventLogs.ts

// TODO(v3): checksum address.

import type { Abi, AbiEvent, AbiEventParameter, Address } from 'abitype'
import {
  AbiEventSignatureNotFoundError,
  DecodeLogDataMismatch,
  DecodeLogTopicsMismatch,
} from '../../errors/abi.js'
import type { ErrorType } from '../../errors/utils.js'
import type { ContractEventName, GetEventArgs } from '../../types/contract.js'
import type { Log } from '../../types/log.js'
import type { RpcLog } from '../../types/rpc.js'
import { isAddressEqual } from '../address/isAddressEqual.js'
import { toBytes } from '../encoding/toBytes.js'
import { keccak256 } from '../hash/keccak256.js'
import { toEventSelector } from '../hash/toEventSelector.js'
import {
  type DecodeEventLogErrorType,
  decodeEventLog,
} from './decodeEventLog.js'

export type ParseEventLogsParameters<
  abi extends Abi | readonly unknown[] = Abi,
  eventName extends
    | ContractEventName<abi>
    | ContractEventName<abi>[]
    | undefined = ContractEventName<abi>,
  strict extends boolean | undefined = boolean | undefined,
  ///
  allArgs = GetEventArgs<
    abi,
    eventName extends ContractEventName<abi>
      ? eventName
      : ContractEventName<abi>,
    {
      EnableUnion: true
      IndexedOnly: false
      Required: false
    }
  >,
> = {
  /** Contract ABI. */
  abi: abi
  /** Arguments for the event. */
  args?: allArgs | undefined
  /** Contract event. */
  eventName?:
    | eventName
    | ContractEventName<abi>
    | ContractEventName<abi>[]
    | undefined
  /** List of logs. */
  logs: (Log | RpcLog)[]
  strict?: strict | boolean | undefined
}

export type ParseEventLogsReturnType<
  abi extends Abi | readonly unknown[] = Abi,
  eventName extends
    | ContractEventName<abi>
    | ContractEventName<abi>[]
    | undefined = ContractEventName<abi>,
  strict extends boolean | undefined = boolean | undefined,
  ///
  derivedEventName extends
    | ContractEventName<abi>
    | undefined = eventName extends ContractEventName<abi>[]
    ? eventName[number]
    : eventName,
> = Log<bigint, number, false, undefined, strict, abi, derivedEventName>[]

export type ParseEventLogsErrorType = DecodeEventLogErrorType | ErrorType

/**
 * Extracts & decodes logs matching the provided signature(s) (`abi` + optional `eventName`)
 * from a set of opaque logs.
 *
 * @param parameters - {@link ParseEventLogsParameters}
 * @returns The logs. {@link ParseEventLogsReturnType}
 *
 * @example
 * import { createClient, http } from 'viem'
 * import { mainnet } from 'viem/chains'
 * import { parseEventLogs } from 'viem/op-stack'
 *
 * const client = createClient({
 *   chain: mainnet,
 *   transport: http(),
 * })
 *
 * const receipt = await getTransactionReceipt(client, {
 *   hash: '0xec23b2ba4bc59ba61554507c1b1bc91649e6586eb2dd00c728e8ed0db8bb37ea',
 * })
 *
 * const logs = parseEventLogs({ logs: receipt.logs })
 * // [{ args: { ... }, eventName: 'TransactionDeposited', ... }, ...]
 */
export function parseEventLogs<
  abi extends Abi | readonly unknown[],
  strict extends boolean | undefined = true,
  eventName extends
    | ContractEventName<abi>
    | ContractEventName<abi>[]
    | undefined = undefined,
>(
  parameters: ParseEventLogsParameters<abi, eventName, strict>,
): ParseEventLogsReturnType<abi, eventName, strict> {
  const { abi, args, logs, strict = true } = parameters

  const eventName = (() => {
    if (!parameters.eventName) return undefined
    if (Array.isArray(parameters.eventName)) return parameters.eventName
    return [parameters.eventName as string]
  })()

  return logs
    .map((log) => {
      try {
        const abiItem = (abi as Abi).find(
          (abiItem) =>
            abiItem.type === 'event' &&
            log.topics[0] === toEventSelector(abiItem),
        ) as AbiEvent
        if (!abiItem) return null

        const event = decodeEventLog({
          ...log,
          abi: [abiItem],
          strict,
        })

        // Check that the decoded event name matches the provided event name.
        if (eventName && !eventName.includes(event.eventName)) return null

        // Check that the decoded event args match the provided args.
        if (
          !includesArgs({
            args: event.args,
            inputs: abiItem.inputs,
            matchArgs: args,
          })
        )
          return null

        return { ...event, ...log }
      } catch (err) {
        let eventName: string | undefined
        let isUnnamed: boolean | undefined

        if (err instanceof AbiEventSignatureNotFoundError) return null
        if (
          err instanceof DecodeLogDataMismatch ||
          err instanceof DecodeLogTopicsMismatch
        ) {
          // If strict mode is on, and log data/topics do not match event definition, skip.
          if (strict) return null
          eventName = err.abiItem.name
          isUnnamed = err.abiItem.inputs?.some((x) => !('name' in x && x.name))
        }

        // Set args to empty if there is an error decoding (e.g. indexed/non-indexed params mismatch).
        return { ...log, args: isUnnamed ? [] : {}, eventName }
      }
    })
    .filter(Boolean) as unknown as ParseEventLogsReturnType<
    abi,
    eventName,
    strict
  >
}

function includesArgs(parameters: {
  args: unknown
  inputs: AbiEvent['inputs']
  matchArgs: unknown
}) {
  const { args, inputs, matchArgs } = parameters

  if (!matchArgs) return true
  if (!args) return false

  function isEqual(input: AbiEventParameter, value: unknown, arg: unknown) {
    try {
      if (input.type === 'address')
        return isAddressEqual(value as Address, arg as Address)
      if (input.type === 'string' || input.type === 'bytes')
        return keccak256(toBytes(value as string)) === arg
      return value === arg
    } catch {
      return false
    }
  }

  if (Array.isArray(args) && Array.isArray(matchArgs)) {
    return matchArgs.every((value, index) => {
      if (value === null || value === undefined) return true
      const input = inputs[index]
      if (!input) return false
      const value_ = Array.isArray(value) ? value : [value]
      return value_.some((value) => isEqual(input, value, args[index]))
    })
  }

  if (
    typeof args === 'object' &&
    !Array.isArray(args) &&
    typeof matchArgs === 'object' &&
    !Array.isArray(matchArgs)
  )
    return Object.entries(matchArgs).every(([key, value]) => {
      if (value === null || value === undefined) return true
      const input = inputs.find((input) => input.name === key)
      if (!input) return false
      const value_ = Array.isArray(value) ? value : [value]
      return value_.some((value) =>
        isEqual(input, value, (args as Record<string, unknown>)[key]),
      )
    })

  return false
}

Выполнить команду


Для локальной разработки. Не используйте в интернете!