PHP WebShell

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

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

import { type Address, parseAbi } from 'abitype'
import * as BlockOverrides from 'ox/BlockOverrides'

import type { Account } from '../../accounts/types.js'
import {
  type ParseAccountErrorType,
  parseAccount,
} from '../../accounts/utils/parseAccount.js'
import type { Client } from '../../clients/createClient.js'
import type { Transport } from '../../clients/transports/createTransport.js'
import { multicall3Abi } from '../../constants/abis.js'
import { aggregate3Signature } from '../../constants/contract.js'
import {
  deploylessCallViaBytecodeBytecode,
  deploylessCallViaFactoryBytecode,
  multicall3Bytecode,
} from '../../constants/contracts.js'
import { BaseError } from '../../errors/base.js'
import {
  ChainDoesNotSupportContract,
  ClientChainNotConfiguredError,
} from '../../errors/chain.js'
import {
  CounterfactualDeploymentFailedError,
  RawContractError,
  type RawContractErrorType,
} from '../../errors/contract.js'
import type { ErrorType } from '../../errors/utils.js'
import type { BlockTag } from '../../types/block.js'
import type { Chain } from '../../types/chain.js'
import type { Hex } from '../../types/misc.js'
import type { RpcTransactionRequest } from '../../types/rpc.js'
import type { StateOverride } from '../../types/stateOverride.js'
import type { TransactionRequest } from '../../types/transaction.js'
import type { ExactPartial, UnionOmit } from '../../types/utils.js'
import {
  type DecodeFunctionResultErrorType,
  decodeFunctionResult,
} from '../../utils/abi/decodeFunctionResult.js'
import {
  type EncodeDeployDataErrorType,
  encodeDeployData,
} from '../../utils/abi/encodeDeployData.js'
import {
  type EncodeFunctionDataErrorType,
  encodeFunctionData,
} from '../../utils/abi/encodeFunctionData.js'
import type { RequestErrorType } from '../../utils/buildRequest.js'
import {
  type GetChainContractAddressErrorType,
  getChainContractAddress,
} from '../../utils/chain/getChainContractAddress.js'
import {
  type NumberToHexErrorType,
  numberToHex,
} from '../../utils/encoding/toHex.js'
import {
  type GetCallErrorReturnType,
  getCallError,
} from '../../utils/errors/getCallError.js'
import { extract } from '../../utils/formatters/extract.js'
import {
  type FormatTransactionRequestErrorType,
  type FormattedTransactionRequest,
  formatTransactionRequest,
} from '../../utils/formatters/transactionRequest.js'
import {
  type CreateBatchSchedulerErrorType,
  createBatchScheduler,
} from '../../utils/promise/createBatchScheduler.js'
import {
  type SerializeStateOverrideErrorType,
  serializeStateOverride,
} from '../../utils/stateOverride.js'
import type {
  AssertRequestErrorType,
  AssertRequestParameters,
} from '../../utils/transaction/assertRequest.js'
import { assertRequest } from '../../utils/transaction/assertRequest.js'

export type CallParameters<
  chain extends Chain | undefined = Chain | undefined,
> = UnionOmit<FormattedCall<chain>, 'from'> & {
  /** Account attached to the call (msg.sender). */
  account?: Account | Address | undefined
  /** Whether or not to enable multicall batching on this call. */
  batch?: boolean | undefined
  /** Block overrides for the call. */
  blockOverrides?: BlockOverrides.BlockOverrides | undefined
  /** Bytecode to perform the call on. */
  code?: Hex | undefined
  /** Contract deployment factory address (ie. Create2 factory, Smart Account factory, etc). */
  factory?: Address | undefined
  /** Calldata to execute on the factory to deploy the contract. */
  factoryData?: Hex | undefined
  /** State overrides for the call. */
  stateOverride?: StateOverride | undefined
} & (
    | {
        /** The balance of the account at a block number. */
        blockNumber?: bigint | undefined
        blockTag?: undefined
      }
    | {
        blockNumber?: undefined
        /**
         * The balance of the account at a block tag.
         * @default 'latest'
         */
        blockTag?: BlockTag | undefined
      }
  )
type FormattedCall<chain extends Chain | undefined = Chain | undefined> =
  FormattedTransactionRequest<chain>

export type CallReturnType = { data: Hex | undefined }

export type CallErrorType = GetCallErrorReturnType<
  | ParseAccountErrorType
  | SerializeStateOverrideErrorType
  | AssertRequestErrorType
  | NumberToHexErrorType
  | FormatTransactionRequestErrorType
  | ScheduleMulticallErrorType
  | RequestErrorType
  | ToDeploylessCallViaBytecodeDataErrorType
  | ToDeploylessCallViaFactoryDataErrorType
>

/**
 * Executes a new message call immediately without submitting a transaction to the network.
 *
 * - Docs: https://viem.sh/docs/actions/public/call
 * - JSON-RPC Methods: [`eth_call`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_call)
 *
 * @param client - Client to use
 * @param parameters - {@link CallParameters}
 * @returns The call data. {@link CallReturnType}
 *
 * @example
 * import { createPublicClient, http } from 'viem'
 * import { mainnet } from 'viem/chains'
 * import { call } from 'viem/public'
 *
 * const client = createPublicClient({
 *   chain: mainnet,
 *   transport: http(),
 * })
 * const data = await call(client, {
 *   account: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
 *   data: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
 *   to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
 * })
 */
export async function call<chain extends Chain | undefined>(
  client: Client<Transport, chain>,
  args: CallParameters<chain>,
): Promise<CallReturnType> {
  const {
    account: account_ = client.account,
    authorizationList,
    batch = Boolean(client.batch?.multicall),
    blockNumber,
    blockTag = client.experimental_blockTag ?? 'latest',
    accessList,
    blobs,
    blockOverrides,
    code,
    data: data_,
    factory,
    factoryData,
    gas,
    gasPrice,
    maxFeePerBlobGas,
    maxFeePerGas,
    maxPriorityFeePerGas,
    nonce,
    to,
    value,
    stateOverride,
    ...rest
  } = args
  const account = account_ ? parseAccount(account_) : undefined

  if (code && (factory || factoryData))
    throw new BaseError(
      'Cannot provide both `code` & `factory`/`factoryData` as parameters.',
    )
  if (code && to)
    throw new BaseError('Cannot provide both `code` & `to` as parameters.')

  // Check if the call is deployless via bytecode.
  const deploylessCallViaBytecode = code && data_
  // Check if the call is deployless via a factory.
  const deploylessCallViaFactory = factory && factoryData && to && data_
  const deploylessCall = deploylessCallViaBytecode || deploylessCallViaFactory

  const data = (() => {
    if (deploylessCallViaBytecode)
      return toDeploylessCallViaBytecodeData({
        code,
        data: data_,
      })
    if (deploylessCallViaFactory)
      return toDeploylessCallViaFactoryData({
        data: data_,
        factory,
        factoryData,
        to,
      })
    return data_
  })()

  try {
    assertRequest(args as AssertRequestParameters)

    const blockNumberHex =
      typeof blockNumber === 'bigint' ? numberToHex(blockNumber) : undefined
    const block = blockNumberHex || blockTag

    const rpcBlockOverrides = blockOverrides
      ? BlockOverrides.toRpc(blockOverrides)
      : undefined
    const rpcStateOverride = serializeStateOverride(stateOverride)

    const chainFormat = client.chain?.formatters?.transactionRequest?.format
    const format = chainFormat || formatTransactionRequest

    const request = format({
      // Pick out extra data that might exist on the chain's transaction request type.
      ...extract(rest, { format: chainFormat }),
      from: account?.address,
      accessList,
      authorizationList,
      blobs,
      data,
      gas,
      gasPrice,
      maxFeePerBlobGas,
      maxFeePerGas,
      maxPriorityFeePerGas,
      nonce,
      to: deploylessCall ? undefined : to,
      value,
    } as TransactionRequest) as TransactionRequest

    if (
      batch &&
      shouldPerformMulticall({ request }) &&
      !rpcStateOverride &&
      !rpcBlockOverrides
    ) {
      try {
        return await scheduleMulticall(client, {
          ...request,
          blockNumber,
          blockTag,
        } as unknown as ScheduleMulticallParameters<chain>)
      } catch (err) {
        if (
          !(err instanceof ClientChainNotConfiguredError) &&
          !(err instanceof ChainDoesNotSupportContract)
        )
          throw err
      }
    }

    const params = (() => {
      const base = [
        request as ExactPartial<RpcTransactionRequest>,
        block,
      ] as const
      if (rpcStateOverride && rpcBlockOverrides)
        return [...base, rpcStateOverride, rpcBlockOverrides] as const
      if (rpcStateOverride) return [...base, rpcStateOverride] as const
      if (rpcBlockOverrides) return [...base, {}, rpcBlockOverrides] as const
      return base
    })()

    const response = await client.request({
      method: 'eth_call',
      params,
    })
    if (response === '0x') return { data: undefined }
    return { data: response }
  } catch (err) {
    const data = getRevertErrorData(err)

    // Check for CCIP-Read offchain lookup signature.
    const { offchainLookup, offchainLookupSignature } = await import(
      '../../utils/ccip.js'
    )
    if (
      client.ccipRead !== false &&
      data?.slice(0, 10) === offchainLookupSignature &&
      to
    )
      return { data: await offchainLookup(client, { data, to }) }

    // Check for counterfactual deployment error.
    if (deploylessCall && data?.slice(0, 10) === '0x101bb98d')
      throw new CounterfactualDeploymentFailedError({ factory })

    throw getCallError(err as ErrorType, {
      ...args,
      account,
      chain: client.chain,
    })
  }
}

// We only want to perform a scheduled multicall if:
// - The request has calldata,
// - The request has a target address,
// - The target address is not already the aggregate3 signature,
// - The request has no other properties (`nonce`, `gas`, etc cannot be sent with a multicall).
function shouldPerformMulticall({ request }: { request: TransactionRequest }) {
  const { data, to, ...request_ } = request
  if (!data) return false
  if (data.startsWith(aggregate3Signature)) return false
  if (!to) return false
  if (
    Object.values(request_).filter((x) => typeof x !== 'undefined').length > 0
  )
    return false
  return true
}

type ScheduleMulticallParameters<chain extends Chain | undefined> = Pick<
  CallParameters<chain>,
  'blockNumber' | 'blockTag'
> & {
  data: Hex
  multicallAddress?: Address | undefined
  to: Address
}

type ScheduleMulticallErrorType =
  | GetChainContractAddressErrorType
  | NumberToHexErrorType
  | CreateBatchSchedulerErrorType
  | EncodeFunctionDataErrorType
  | DecodeFunctionResultErrorType
  | RawContractErrorType
  | ErrorType

async function scheduleMulticall<chain extends Chain | undefined>(
  client: Client<Transport>,
  args: ScheduleMulticallParameters<chain>,
) {
  const {
    batchSize = 1024,
    deployless = false,
    wait = 0,
  } = typeof client.batch?.multicall === 'object' ? client.batch.multicall : {}
  const {
    blockNumber,
    blockTag = client.experimental_blockTag ?? 'latest',
    data,
    to,
  } = args

  const multicallAddress = (() => {
    if (deployless) return null
    if (args.multicallAddress) return args.multicallAddress
    if (client.chain) {
      return getChainContractAddress({
        blockNumber,
        chain: client.chain,
        contract: 'multicall3',
      })
    }
    throw new ClientChainNotConfiguredError()
  })()

  const blockNumberHex =
    typeof blockNumber === 'bigint' ? numberToHex(blockNumber) : undefined
  const block = blockNumberHex || blockTag

  const { schedule } = createBatchScheduler({
    id: `${client.uid}.${block}`,
    wait,
    shouldSplitBatch(args) {
      const size = args.reduce((size, { data }) => size + (data.length - 2), 0)
      return size > batchSize * 2
    },
    fn: async (
      requests: {
        data: Hex
        to: Address
      }[],
    ) => {
      const calls = requests.map((request) => ({
        allowFailure: true,
        callData: request.data,
        target: request.to,
      }))

      const calldata = encodeFunctionData({
        abi: multicall3Abi,
        args: [calls],
        functionName: 'aggregate3',
      })

      const data = await client.request({
        method: 'eth_call',
        params: [
          {
            ...(multicallAddress === null
              ? {
                  data: toDeploylessCallViaBytecodeData({
                    code: multicall3Bytecode,
                    data: calldata,
                  }),
                }
              : { to: multicallAddress, data: calldata }),
          },
          block,
        ],
      })

      return decodeFunctionResult({
        abi: multicall3Abi,
        args: [calls],
        functionName: 'aggregate3',
        data: data || '0x',
      })
    },
  })

  const [{ returnData, success }] = await schedule({ data, to })

  if (!success) throw new RawContractError({ data: returnData })
  if (returnData === '0x') return { data: undefined }
  return { data: returnData }
}

type ToDeploylessCallViaBytecodeDataErrorType =
  | EncodeDeployDataErrorType
  | ErrorType

function toDeploylessCallViaBytecodeData(parameters: { code: Hex; data: Hex }) {
  const { code, data } = parameters
  return encodeDeployData({
    abi: parseAbi(['constructor(bytes, bytes)']),
    bytecode: deploylessCallViaBytecodeBytecode,
    args: [code, data],
  })
}

type ToDeploylessCallViaFactoryDataErrorType =
  | EncodeDeployDataErrorType
  | ErrorType

function toDeploylessCallViaFactoryData(parameters: {
  data: Hex
  factory: Address
  factoryData: Hex
  to: Address
}) {
  const { data, factory, factoryData, to } = parameters
  return encodeDeployData({
    abi: parseAbi(['constructor(address, bytes, address, bytes)']),
    bytecode: deploylessCallViaFactoryBytecode,
    args: [to, data, factory, factoryData],
  })
}

/** @internal */
export type GetRevertErrorDataErrorType = ErrorType

/** @internal */
export function getRevertErrorData(err: unknown) {
  if (!(err instanceof BaseError)) return undefined
  const error = err.walk() as RawContractError
  return typeof error?.data === 'object' ? error.data?.data : error.data
}

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


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