FRE-600: Fix code review blockers

- Consolidated duplicate UndoManagers to single instance
- Fixed connection promise to only resolve on 'connected' status
- Fixed WebSocketProvider import (WebsocketProvider)
- Added proper doc.destroy() cleanup
- Renamed isPresenceInitialized property to avoid conflict

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
2026-04-25 00:08:01 -04:00
parent 65b552bb08
commit 7c684a42cc
48450 changed files with 5679671 additions and 383 deletions

390
node_modules/viem/utils/abi/decodeAbiParameters.ts generated vendored Normal file
View File

@@ -0,0 +1,390 @@
import type {
AbiParameter,
AbiParameterKind,
AbiParametersToPrimitiveTypes,
} from 'abitype'
import {
AbiDecodingDataSizeTooSmallError,
AbiDecodingZeroDataError,
InvalidAbiDecodingTypeError,
type InvalidAbiDecodingTypeErrorType,
} from '../../errors/abi.js'
import type { ErrorType } from '../../errors/utils.js'
import type { ByteArray, Hex } from '../../types/misc.js'
import {
type ChecksumAddressErrorType,
checksumAddress,
} from '../address/getAddress.js'
import {
type CreateCursorErrorType,
type Cursor,
createCursor,
} from '../cursor.js'
import { type SizeErrorType, size } from '../data/size.js'
import { type SliceBytesErrorType, sliceBytes } from '../data/slice.js'
import { type TrimErrorType, trim } from '../data/trim.js'
import {
type BytesToBigIntErrorType,
type BytesToBoolErrorType,
type BytesToNumberErrorType,
type BytesToStringErrorType,
bytesToBigInt,
bytesToBool,
bytesToNumber,
bytesToString,
} from '../encoding/fromBytes.js'
import { type HexToBytesErrorType, hexToBytes } from '../encoding/toBytes.js'
import { type BytesToHexErrorType, bytesToHex } from '../encoding/toHex.js'
import { getArrayComponents } from './encodeAbiParameters.js'
export type DecodeAbiParametersReturnType<
params extends readonly AbiParameter[] = readonly AbiParameter[],
> = AbiParametersToPrimitiveTypes<
params extends readonly AbiParameter[] ? params : AbiParameter[],
AbiParameterKind,
true
>
export type DecodeAbiParametersErrorType =
| HexToBytesErrorType
| BytesToHexErrorType
| DecodeParameterErrorType
| SizeErrorType
| CreateCursorErrorType
| ErrorType
export function decodeAbiParameters<
const params extends readonly AbiParameter[],
>(
params: params,
data: ByteArray | Hex,
): DecodeAbiParametersReturnType<params> {
const bytes = typeof data === 'string' ? hexToBytes(data) : data
const cursor = createCursor(bytes)
if (size(bytes) === 0 && params.length > 0)
throw new AbiDecodingZeroDataError()
if (size(data) && size(data) < 32)
throw new AbiDecodingDataSizeTooSmallError({
data: typeof data === 'string' ? data : bytesToHex(data),
params: params as readonly AbiParameter[],
size: size(data),
})
let consumed = 0
const values = []
for (let i = 0; i < params.length; ++i) {
const param = params[i]
cursor.setPosition(consumed)
const [data, consumed_] = decodeParameter(cursor, param, {
staticPosition: 0,
})
consumed += consumed_
values.push(data)
}
return values as never
}
type DecodeParameterErrorType =
| DecodeArrayErrorType
| DecodeTupleErrorType
| DecodeAddressErrorType
| DecodeBoolErrorType
| DecodeBytesErrorType
| DecodeNumberErrorType
| DecodeStringErrorType
| InvalidAbiDecodingTypeErrorType
function decodeParameter(
cursor: Cursor,
param: AbiParameter,
{ staticPosition }: { staticPosition: number },
) {
const arrayComponents = getArrayComponents(param.type)
if (arrayComponents) {
const [length, type] = arrayComponents
return decodeArray(cursor, { ...param, type }, { length, staticPosition })
}
if (param.type === 'tuple')
return decodeTuple(cursor, param as TupleAbiParameter, { staticPosition })
if (param.type === 'address') return decodeAddress(cursor)
if (param.type === 'bool') return decodeBool(cursor)
if (param.type.startsWith('bytes'))
return decodeBytes(cursor, param, { staticPosition })
if (param.type.startsWith('uint') || param.type.startsWith('int'))
return decodeNumber(cursor, param)
if (param.type === 'string') return decodeString(cursor, { staticPosition })
throw new InvalidAbiDecodingTypeError(param.type, {
docsPath: '/docs/contract/decodeAbiParameters',
})
}
////////////////////////////////////////////////////////////////////
// Type Decoders
const sizeOfLength = 32
const sizeOfOffset = 32
type DecodeAddressErrorType =
| ChecksumAddressErrorType
| BytesToHexErrorType
| SliceBytesErrorType
| ErrorType
function decodeAddress(cursor: Cursor) {
const value = cursor.readBytes(32)
return [checksumAddress(bytesToHex(sliceBytes(value, -20))), 32]
}
type DecodeArrayErrorType = BytesToNumberErrorType | ErrorType
function decodeArray(
cursor: Cursor,
param: AbiParameter,
{ length, staticPosition }: { length: number | null; staticPosition: number },
) {
// If the length of the array is not known in advance (dynamic array),
// this means we will need to wonder off to the pointer and decode.
if (!length) {
// Dealing with a dynamic type, so get the offset of the array data.
const offset = bytesToNumber(cursor.readBytes(sizeOfOffset))
// Start is the static position of current slot + offset.
const start = staticPosition + offset
const startOfData = start + sizeOfLength
// Get the length of the array from the offset.
cursor.setPosition(start)
const length = bytesToNumber(cursor.readBytes(sizeOfLength))
// Check if the array has any dynamic children.
const dynamicChild = hasDynamicChild(param)
let consumed = 0
const value: unknown[] = []
for (let i = 0; i < length; ++i) {
// If any of the children is dynamic, then all elements will be offset pointer, thus size of one slot (32 bytes).
// Otherwise, elements will be the size of their encoding (consumed bytes).
cursor.setPosition(startOfData + (dynamicChild ? i * 32 : consumed))
const [data, consumed_] = decodeParameter(cursor, param, {
staticPosition: startOfData,
})
consumed += consumed_
value.push(data)
}
// As we have gone wondering, restore to the original position + next slot.
cursor.setPosition(staticPosition + 32)
return [value, 32]
}
// If the length of the array is known in advance,
// and the length of an element deeply nested in the array is not known,
// we need to decode the offset of the array data.
if (hasDynamicChild(param)) {
// Dealing with dynamic types, so get the offset of the array data.
const offset = bytesToNumber(cursor.readBytes(sizeOfOffset))
// Start is the static position of current slot + offset.
const start = staticPosition + offset
const value: unknown[] = []
for (let i = 0; i < length; ++i) {
// Move cursor along to the next slot (next offset pointer).
cursor.setPosition(start + i * 32)
const [data] = decodeParameter(cursor, param, {
staticPosition: start,
})
value.push(data)
}
// As we have gone wondering, restore to the original position + next slot.
cursor.setPosition(staticPosition + 32)
return [value, 32]
}
// If the length of the array is known in advance and the array is deeply static,
// then we can just decode each element in sequence.
let consumed = 0
const value: unknown[] = []
for (let i = 0; i < length; ++i) {
const [data, consumed_] = decodeParameter(cursor, param, {
staticPosition: staticPosition + consumed,
})
consumed += consumed_
value.push(data)
}
return [value, consumed]
}
type DecodeBoolErrorType = BytesToBoolErrorType | ErrorType
function decodeBool(cursor: Cursor) {
return [bytesToBool(cursor.readBytes(32), { size: 32 }), 32]
}
type DecodeBytesErrorType =
| BytesToNumberErrorType
| BytesToHexErrorType
| ErrorType
function decodeBytes(
cursor: Cursor,
param: AbiParameter,
{ staticPosition }: { staticPosition: number },
) {
const [_, size] = param.type.split('bytes')
if (!size) {
// Dealing with dynamic types, so get the offset of the bytes data.
const offset = bytesToNumber(cursor.readBytes(32))
// Set position of the cursor to start of bytes data.
cursor.setPosition(staticPosition + offset)
const length = bytesToNumber(cursor.readBytes(32))
// If there is no length, we have zero data.
if (length === 0) {
// As we have gone wondering, restore to the original position + next slot.
cursor.setPosition(staticPosition + 32)
return ['0x', 32]
}
const data = cursor.readBytes(length)
// As we have gone wondering, restore to the original position + next slot.
cursor.setPosition(staticPosition + 32)
return [bytesToHex(data), 32]
}
const value = bytesToHex(cursor.readBytes(Number.parseInt(size, 10), 32))
return [value, 32]
}
type DecodeNumberErrorType =
| BytesToNumberErrorType
| BytesToBigIntErrorType
| ErrorType
function decodeNumber(cursor: Cursor, param: AbiParameter) {
const signed = param.type.startsWith('int')
const size = Number.parseInt(param.type.split('int')[1] || '256', 10)
const value = cursor.readBytes(32)
return [
size > 48
? bytesToBigInt(value, { signed })
: bytesToNumber(value, { signed }),
32,
]
}
type TupleAbiParameter = AbiParameter & { components: readonly AbiParameter[] }
type DecodeTupleErrorType = BytesToNumberErrorType | ErrorType
function decodeTuple(
cursor: Cursor,
param: TupleAbiParameter,
{ staticPosition }: { staticPosition: number },
) {
// Tuples can have unnamed components (i.e. they are arrays), so we must
// determine whether the tuple is named or unnamed. In the case of a named
// tuple, the value will be an object where each property is the name of the
// component. In the case of an unnamed tuple, the value will be an array.
const hasUnnamedChild =
param.components.length === 0 || param.components.some(({ name }) => !name)
// Initialize the value to an object or an array, depending on whether the
// tuple is named or unnamed.
const value: any = hasUnnamedChild ? [] : {}
let consumed = 0
// If the tuple has a dynamic child, we must first decode the offset to the
// tuple data.
if (hasDynamicChild(param)) {
// Dealing with dynamic types, so get the offset of the tuple data.
const offset = bytesToNumber(cursor.readBytes(sizeOfOffset))
// Start is the static position of referencing slot + offset.
const start = staticPosition + offset
for (let i = 0; i < param.components.length; ++i) {
const component = param.components[i]
cursor.setPosition(start + consumed)
const [data, consumed_] = decodeParameter(cursor, component, {
staticPosition: start,
})
consumed += consumed_
value[hasUnnamedChild ? i : component?.name!] = data
}
// As we have gone wondering, restore to the original position + next slot.
cursor.setPosition(staticPosition + 32)
return [value, 32]
}
// If the tuple has static children, we can just decode each component
// in sequence.
for (let i = 0; i < param.components.length; ++i) {
const component = param.components[i]
const [data, consumed_] = decodeParameter(cursor, component, {
staticPosition,
})
value[hasUnnamedChild ? i : component?.name!] = data
consumed += consumed_
}
return [value, consumed]
}
type DecodeStringErrorType =
| BytesToNumberErrorType
| BytesToStringErrorType
| TrimErrorType
| ErrorType
function decodeString(
cursor: Cursor,
{ staticPosition }: { staticPosition: number },
) {
// Get offset to start of string data.
const offset = bytesToNumber(cursor.readBytes(32))
// Start is the static position of current slot + offset.
const start = staticPosition + offset
cursor.setPosition(start)
const length = bytesToNumber(cursor.readBytes(32))
// If there is no length, we have zero data (empty string).
if (length === 0) {
cursor.setPosition(staticPosition + 32)
return ['', 32]
}
const data = cursor.readBytes(length, 32)
const value = bytesToString(trim(data))
// As we have gone wondering, restore to the original position + next slot.
cursor.setPosition(staticPosition + 32)
return [value, 32]
}
function hasDynamicChild(param: AbiParameter) {
const { type } = param
if (type === 'string') return true
if (type === 'bytes') return true
if (type.endsWith('[]')) return true
if (type === 'tuple') return (param as any).components?.some(hasDynamicChild)
const arrayComponents = getArrayComponents(param.type)
if (
arrayComponents &&
hasDynamicChild({ ...param, type: arrayComponents[1] } as AbiParameter)
)
return true
return false
}

60
node_modules/viem/utils/abi/decodeDeployData.ts generated vendored Normal file
View File

@@ -0,0 +1,60 @@
import type { Abi } from 'abitype'
import {
AbiConstructorNotFoundError,
type AbiConstructorNotFoundErrorType,
AbiConstructorParamsNotFoundError,
type AbiConstructorParamsNotFoundErrorType,
} from '../../errors/abi.js'
import type { ErrorType } from '../../errors/utils.js'
import type { ContractConstructorArgs } from '../../types/contract.js'
import type { Hex } from '../../types/misc.js'
import {
type DecodeAbiParametersErrorType,
decodeAbiParameters,
} from './decodeAbiParameters.js'
const docsPath = '/docs/contract/decodeDeployData'
export type DecodeDeployDataParameters<
abi extends Abi | readonly unknown[] = Abi,
> = {
abi: abi
bytecode: Hex
data: Hex
}
export type DecodeDeployDataReturnType<
abi extends Abi | readonly unknown[] = Abi,
///
allArgs = ContractConstructorArgs<abi>,
> = {
bytecode: Hex
args: allArgs
}
export type DecodeDeployDataErrorType =
| AbiConstructorNotFoundErrorType
| AbiConstructorParamsNotFoundErrorType
| DecodeAbiParametersErrorType
| ErrorType
export function decodeDeployData<const abi extends Abi | readonly unknown[]>(
parameters: DecodeDeployDataParameters<abi>,
): DecodeDeployDataReturnType<abi> {
const { abi, bytecode, data } = parameters as DecodeDeployDataParameters
if (data === bytecode) return { bytecode } as DecodeDeployDataReturnType<abi>
const description = abi.find((x) => 'type' in x && x.type === 'constructor')
if (!description) throw new AbiConstructorNotFoundError({ docsPath })
if (!('inputs' in description))
throw new AbiConstructorParamsNotFoundError({ docsPath })
if (!description.inputs || description.inputs.length === 0)
throw new AbiConstructorParamsNotFoundError({ docsPath })
const args = decodeAbiParameters(
description.inputs,
`0x${data.replace(bytecode, '')}`,
)
return { args, bytecode } as unknown as DecodeDeployDataReturnType<abi>
}

92
node_modules/viem/utils/abi/decodeErrorResult.ts generated vendored Normal file
View File

@@ -0,0 +1,92 @@
import type { Abi, ExtractAbiError } from 'abitype'
import { solidityError, solidityPanic } from '../../constants/solidity.js'
import {
AbiDecodingZeroDataError,
type AbiDecodingZeroDataErrorType,
AbiErrorSignatureNotFoundError,
type AbiErrorSignatureNotFoundErrorType,
} from '../../errors/abi.js'
import type { BaseError } from '../../errors/base.js'
import type { ErrorType } from '../../errors/utils.js'
import type {
AbiItem,
ContractErrorArgs,
ContractErrorName,
} from '../../types/contract.js'
import type { Hex } from '../../types/misc.js'
import type { IsNarrowable, UnionEvaluate } from '../../types/utils.js'
import { slice } from '../data/slice.js'
import {
type ToFunctionSelectorErrorType,
toFunctionSelector,
} from '../hash/toFunctionSelector.js'
import {
type DecodeAbiParametersErrorType,
decodeAbiParameters,
} from './decodeAbiParameters.js'
import { type FormatAbiItemErrorType, formatAbiItem } from './formatAbiItem.js'
export type DecodeErrorResultParameters<
abi extends Abi | readonly unknown[] = Abi,
> = { abi?: abi | undefined; data: Hex; cause?: BaseError | Error | undefined }
export type DecodeErrorResultReturnType<
abi extends Abi | readonly unknown[] = Abi,
///
allErrorNames extends ContractErrorName<abi> = ContractErrorName<abi>,
> = IsNarrowable<abi, Abi> extends true
? UnionEvaluate<
{
[errorName in allErrorNames]: {
abiItem: abi extends Abi
? Abi extends abi
? AbiItem
: ExtractAbiError<abi, errorName>
: AbiItem
args: ContractErrorArgs<abi, errorName>
errorName: errorName
}
}[allErrorNames]
>
: {
abiItem: AbiItem
args: readonly unknown[] | undefined
errorName: string
}
export type DecodeErrorResultErrorType =
| AbiDecodingZeroDataErrorType
| AbiErrorSignatureNotFoundErrorType
| DecodeAbiParametersErrorType
| FormatAbiItemErrorType
| ToFunctionSelectorErrorType
| ErrorType
export function decodeErrorResult<const abi extends Abi | readonly unknown[]>(
parameters: DecodeErrorResultParameters<abi>,
): DecodeErrorResultReturnType<abi> {
const { abi, data, cause } = parameters as DecodeErrorResultParameters
const signature = slice(data, 0, 4)
if (signature === '0x') throw new AbiDecodingZeroDataError({ cause })
const abi_ = [...(abi || []), solidityError, solidityPanic]
const abiItem = abi_.find(
(x) =>
x.type === 'error' && signature === toFunctionSelector(formatAbiItem(x)),
)
if (!abiItem)
throw new AbiErrorSignatureNotFoundError(signature, {
docsPath: '/docs/contract/decodeErrorResult',
cause,
})
return {
abiItem,
args:
'inputs' in abiItem && abiItem.inputs && abiItem.inputs.length > 0
? decodeAbiParameters(abiItem.inputs, slice(data, 4))
: undefined,
errorName: (abiItem as { name: string }).name,
} as DecodeErrorResultReturnType<abi>
}

232
node_modules/viem/utils/abi/decodeEventLog.ts generated vendored Normal file
View File

@@ -0,0 +1,232 @@
import type { Abi, AbiParameter } from 'abitype'
import {
AbiDecodingDataSizeTooSmallError,
type AbiDecodingDataSizeTooSmallErrorType,
AbiEventSignatureEmptyTopicsError,
type AbiEventSignatureEmptyTopicsErrorType,
AbiEventSignatureNotFoundError,
type AbiEventSignatureNotFoundErrorType,
DecodeLogDataMismatch,
type DecodeLogDataMismatchErrorType,
DecodeLogTopicsMismatch,
type DecodeLogTopicsMismatchErrorType,
} from '../../errors/abi.js'
import { PositionOutOfBoundsError } from '../../errors/cursor.js'
import type { ErrorType } from '../../errors/utils.js'
import type {
ContractEventArgsFromTopics,
ContractEventName,
EventDefinition,
} from '../../types/contract.js'
import type { Hex } from '../../types/misc.js'
import type {
IsNarrowable,
Prettify,
UnionEvaluate,
} from '../../types/utils.js'
import { size } from '../data/size.js'
import {
type ToEventSelectorErrorType,
toEventSelector,
} from '../hash/toEventSelector.js'
import {
type DecodeAbiParametersErrorType,
decodeAbiParameters,
} from './decodeAbiParameters.js'
import { type FormatAbiItemErrorType, formatAbiItem } from './formatAbiItem.js'
export type DecodeEventLogParameters<
abi extends Abi | readonly unknown[] = Abi,
eventName extends ContractEventName<abi> | undefined = ContractEventName<abi>,
topics extends Hex[] = Hex[],
data extends Hex | undefined = undefined,
strict extends boolean = true,
> = {
abi: abi
data?: data | undefined
eventName?: eventName | ContractEventName<abi> | undefined
strict?: strict | boolean | undefined
topics: [signature: Hex, ...args: topics] | []
}
export type DecodeEventLogReturnType<
abi extends Abi | readonly unknown[] = Abi,
eventName extends ContractEventName<abi> | undefined = ContractEventName<abi>,
topics extends Hex[] = Hex[],
data extends Hex | undefined = undefined,
strict extends boolean = true,
///
allEventNames extends
ContractEventName<abi> = eventName extends ContractEventName<abi>
? eventName
: ContractEventName<abi>,
> = IsNarrowable<abi, Abi> extends true
? {
[name in allEventNames]: Prettify<
{
eventName: name
} & UnionEvaluate<
ContractEventArgsFromTopics<abi, name, strict> extends infer allArgs
? topics extends readonly []
? data extends undefined
? { args?: undefined }
: { args?: allArgs | undefined }
: { args: allArgs }
: never
>
>
}[allEventNames]
: {
eventName: eventName
args: readonly unknown[] | undefined
}
export type DecodeEventLogErrorType =
| AbiDecodingDataSizeTooSmallErrorType
| AbiEventSignatureEmptyTopicsErrorType
| AbiEventSignatureNotFoundErrorType
| DecodeAbiParametersErrorType
| DecodeLogTopicsMismatchErrorType
| DecodeLogDataMismatchErrorType
| FormatAbiItemErrorType
| ToEventSelectorErrorType
| ErrorType
const docsPath = '/docs/contract/decodeEventLog'
export function decodeEventLog<
const abi extends Abi | readonly unknown[],
eventName extends ContractEventName<abi> | undefined = undefined,
topics extends Hex[] = Hex[],
data extends Hex | undefined = undefined,
strict extends boolean = true,
>(
parameters: DecodeEventLogParameters<abi, eventName, topics, data, strict>,
): DecodeEventLogReturnType<abi, eventName, topics, data, strict> {
const {
abi,
data,
strict: strict_,
topics,
} = parameters as DecodeEventLogParameters
const strict = strict_ ?? true
const [signature, ...argTopics] = topics
if (!signature) throw new AbiEventSignatureEmptyTopicsError({ docsPath })
const abiItem = abi.find(
(x) =>
x.type === 'event' &&
signature === toEventSelector(formatAbiItem(x) as EventDefinition),
)
if (!(abiItem && 'name' in abiItem) || abiItem.type !== 'event')
throw new AbiEventSignatureNotFoundError(signature, { docsPath })
const { name, inputs } = abiItem
const isUnnamed = inputs?.some((x) => !('name' in x && x.name))
const args: any = isUnnamed ? [] : {}
// Decode topics (indexed args).
const indexedInputs = inputs
.map((x, i) => [x, i] as const)
.filter(([x]) => 'indexed' in x && x.indexed)
const missingIndexedInputs: [AbiParameter, number][] = []
for (let i = 0; i < indexedInputs.length; i++) {
const [param, argIndex] = indexedInputs[i]
const topic = argTopics[i]
if (!topic) {
if (strict)
throw new DecodeLogTopicsMismatch({
abiItem,
param: param as AbiParameter & { indexed: boolean },
})
// Track missing indexed inputs to decode from data when strict is false
missingIndexedInputs.push([param, argIndex])
continue
}
args[isUnnamed ? argIndex : param.name || argIndex] = decodeTopic({
param,
value: topic,
})
}
// Decode data (non-indexed args + missing indexed args when strict is false).
const nonIndexedInputs = inputs.filter((x) => !('indexed' in x && x.indexed))
// When strict is false, missing indexed inputs should be decoded from data
const inputsToDecode = strict
? nonIndexedInputs
: [...missingIndexedInputs.map(([param]) => param), ...nonIndexedInputs]
if (inputsToDecode.length > 0) {
if (data && data !== '0x') {
try {
const decodedData = decodeAbiParameters(
inputsToDecode,
data,
) as unknown[]
if (decodedData) {
let dataIndex = 0
// First, assign missing indexed parameters (when strict is false)
if (!strict) {
for (const [param, argIndex] of missingIndexedInputs) {
args[isUnnamed ? argIndex : param.name || argIndex] =
decodedData[dataIndex++]
}
}
// Then, assign non-indexed parameters
if (isUnnamed) {
for (let i = 0; i < inputs.length; i++)
if (args[i] === undefined && dataIndex < decodedData.length)
args[i] = decodedData[dataIndex++]
} else
for (let i = 0; i < nonIndexedInputs.length; i++)
args[nonIndexedInputs[i].name!] = decodedData[dataIndex++]
}
} catch (err) {
if (strict) {
if (
err instanceof AbiDecodingDataSizeTooSmallError ||
err instanceof PositionOutOfBoundsError
)
throw new DecodeLogDataMismatch({
abiItem,
data: data,
params: inputsToDecode,
size: size(data),
})
throw err
}
}
} else if (strict) {
throw new DecodeLogDataMismatch({
abiItem,
data: '0x',
params: inputsToDecode,
size: 0,
})
}
}
return {
eventName: name,
args: Object.values(args).length > 0 ? args : undefined,
} as unknown as DecodeEventLogReturnType<abi, eventName, topics, data, strict>
}
function decodeTopic({ param, value }: { param: AbiParameter; value: Hex }) {
if (
param.type === 'string' ||
param.type === 'bytes' ||
param.type === 'tuple' ||
param.type.match(/^(.*)\[(\d+)?\]$/)
)
return value
const decodedArg = decodeAbiParameters([param], value) || []
return decodedArg[0]
}

78
node_modules/viem/utils/abi/decodeFunctionData.ts generated vendored Normal file
View File

@@ -0,0 +1,78 @@
import type { Abi, AbiStateMutability } from 'abitype'
import { AbiFunctionSignatureNotFoundError } from '../../errors/abi.js'
import type { ErrorType } from '../../errors/utils.js'
import type {
ContractFunctionArgs,
ContractFunctionName,
} from '../../types/contract.js'
import type { Hex } from '../../types/misc.js'
import type { IsNarrowable, UnionEvaluate } from '../../types/utils.js'
import { type SliceErrorType, slice } from '../data/slice.js'
import {
type ToFunctionSelectorErrorType,
toFunctionSelector,
} from '../hash/toFunctionSelector.js'
import {
type DecodeAbiParametersErrorType,
decodeAbiParameters,
} from './decodeAbiParameters.js'
import { type FormatAbiItemErrorType, formatAbiItem } from './formatAbiItem.js'
export type DecodeFunctionDataParameters<
abi extends Abi | readonly unknown[] = Abi,
> = {
abi: abi
data: Hex
}
export type DecodeFunctionDataReturnType<
abi extends Abi | readonly unknown[] = Abi,
///
allFunctionNames extends
ContractFunctionName<abi> = ContractFunctionName<abi>,
> = IsNarrowable<abi, Abi> extends true
? UnionEvaluate<
{
[functionName in allFunctionNames]: {
args: ContractFunctionArgs<abi, AbiStateMutability, functionName>
functionName: functionName
}
}[allFunctionNames]
>
: {
args: readonly unknown[] | undefined
functionName: string
}
export type DecodeFunctionDataErrorType =
| AbiFunctionSignatureNotFoundError
| DecodeAbiParametersErrorType
| FormatAbiItemErrorType
| ToFunctionSelectorErrorType
| SliceErrorType
| ErrorType
export function decodeFunctionData<const abi extends Abi | readonly unknown[]>(
parameters: DecodeFunctionDataParameters<abi>,
) {
const { abi, data } = parameters as DecodeFunctionDataParameters
const signature = slice(data, 0, 4)
const description = abi.find(
(x) =>
x.type === 'function' &&
signature === toFunctionSelector(formatAbiItem(x)),
)
if (!description)
throw new AbiFunctionSignatureNotFoundError(signature, {
docsPath: '/docs/contract/decodeFunctionData',
})
return {
functionName: (description as { name: string }).name,
args: ('inputs' in description &&
description.inputs &&
description.inputs.length > 0
? decodeAbiParameters(description.inputs, slice(data, 4))
: undefined) as readonly unknown[] | undefined,
} as DecodeFunctionDataReturnType<abi>
}

166
node_modules/viem/utils/abi/decodeFunctionResult.ts generated vendored Normal file
View File

@@ -0,0 +1,166 @@
import type { Abi, AbiStateMutability, ExtractAbiFunctions } from 'abitype'
import {
AbiFunctionNotFoundError,
type AbiFunctionNotFoundErrorType,
AbiFunctionOutputsNotFoundError,
type AbiFunctionOutputsNotFoundErrorType,
} from '../../errors/abi.js'
import type { ErrorType } from '../../errors/utils.js'
import type {
ContractFunctionArgs,
ContractFunctionName,
ContractFunctionReturnType,
Widen,
} from '../../types/contract.js'
import type { Hex } from '../../types/misc.js'
import type { IsNarrowable, UnionEvaluate } from '../../types/utils.js'
import {
type DecodeAbiParametersErrorType,
decodeAbiParameters,
} from './decodeAbiParameters.js'
import { type GetAbiItemErrorType, getAbiItem } from './getAbiItem.js'
const docsPath = '/docs/contract/decodeFunctionResult'
export type DecodeFunctionResultParameters<
abi extends Abi | readonly unknown[] = Abi,
functionName extends
| ContractFunctionName<abi>
| undefined = ContractFunctionName<abi>,
args extends ContractFunctionArgs<
abi,
AbiStateMutability,
functionName extends ContractFunctionName<abi>
? functionName
: ContractFunctionName<abi>
> = ContractFunctionArgs<
abi,
AbiStateMutability,
functionName extends ContractFunctionName<abi>
? functionName
: ContractFunctionName<abi>
>,
///
hasFunctions = abi extends Abi
? Abi extends abi
? true
: [ExtractAbiFunctions<abi>] extends [never]
? false
: true
: true,
allArgs = ContractFunctionArgs<
abi,
AbiStateMutability,
functionName extends ContractFunctionName<abi>
? functionName
: ContractFunctionName<abi>
>,
allFunctionNames = ContractFunctionName<abi>,
> = {
abi: abi
data: Hex
} & UnionEvaluate<
IsNarrowable<abi, Abi> extends true
? abi['length'] extends 1
? { functionName?: functionName | allFunctionNames | undefined }
: { functionName: functionName | allFunctionNames }
: { functionName?: functionName | allFunctionNames | undefined }
> &
UnionEvaluate<
readonly [] extends allArgs
? {
args?:
| allArgs // show all options
// infer value, widen inferred value of `args` conditionally to match `allArgs`
| (abi extends Abi
? args extends allArgs
? Widen<args>
: never
: never)
| undefined
}
: {
args?:
| allArgs // show all options
| (Widen<args> & (args extends allArgs ? unknown : never)) // infer value, widen inferred value of `args` match `allArgs` (e.g. avoid union `args: readonly [123n] | readonly [bigint]`)
| undefined
}
> &
(hasFunctions extends true ? unknown : never)
export type DecodeFunctionResultReturnType<
abi extends Abi | readonly unknown[] = Abi,
functionName extends
| ContractFunctionName<abi>
| undefined = ContractFunctionName<abi>,
args extends ContractFunctionArgs<
abi,
AbiStateMutability,
functionName extends ContractFunctionName<abi>
? functionName
: ContractFunctionName<abi>
> = ContractFunctionArgs<
abi,
AbiStateMutability,
functionName extends ContractFunctionName<abi>
? functionName
: ContractFunctionName<abi>
>,
> = ContractFunctionReturnType<
abi,
AbiStateMutability,
functionName extends ContractFunctionName<abi>
? functionName
: ContractFunctionName<abi>,
args
>
export type DecodeFunctionResultErrorType =
| AbiFunctionNotFoundErrorType
| AbiFunctionOutputsNotFoundErrorType
| DecodeAbiParametersErrorType
| GetAbiItemErrorType
| ErrorType
export function decodeFunctionResult<
const abi extends Abi | readonly unknown[],
functionName extends ContractFunctionName<abi> | undefined = undefined,
const args extends ContractFunctionArgs<
abi,
AbiStateMutability,
functionName extends ContractFunctionName<abi>
? functionName
: ContractFunctionName<abi>
> = ContractFunctionArgs<
abi,
AbiStateMutability,
functionName extends ContractFunctionName<abi>
? functionName
: ContractFunctionName<abi>
>,
>(
parameters: DecodeFunctionResultParameters<abi, functionName, args>,
): DecodeFunctionResultReturnType<abi, functionName, args> {
const { abi, args, functionName, data } =
parameters as DecodeFunctionResultParameters
let abiItem = abi[0]
if (functionName) {
const item = getAbiItem({ abi, args, name: functionName })
if (!item) throw new AbiFunctionNotFoundError(functionName, { docsPath })
abiItem = item
}
if (abiItem.type !== 'function')
throw new AbiFunctionNotFoundError(undefined, { docsPath })
if (!abiItem.outputs)
throw new AbiFunctionOutputsNotFoundError(abiItem.name, { docsPath })
const values = decodeAbiParameters(abiItem.outputs, data)
if (values && values.length > 1)
return values as DecodeFunctionResultReturnType<abi, functionName, args>
if (values && values.length === 1)
return values[0] as DecodeFunctionResultReturnType<abi, functionName, args>
return undefined as DecodeFunctionResultReturnType<abi, functionName, args>
}

430
node_modules/viem/utils/abi/encodeAbiParameters.ts generated vendored Normal file
View File

@@ -0,0 +1,430 @@
import type {
AbiParameter,
AbiParameterKind,
AbiParametersToPrimitiveTypes,
AbiParameterToPrimitiveType,
} from 'abitype'
import {
AbiEncodingArrayLengthMismatchError,
type AbiEncodingArrayLengthMismatchErrorType,
AbiEncodingBytesSizeMismatchError,
type AbiEncodingBytesSizeMismatchErrorType,
AbiEncodingLengthMismatchError,
type AbiEncodingLengthMismatchErrorType,
InvalidAbiEncodingTypeError,
type InvalidAbiEncodingTypeErrorType,
InvalidArrayError,
type InvalidArrayErrorType,
} from '../../errors/abi.js'
import {
InvalidAddressError,
type InvalidAddressErrorType,
} from '../../errors/address.js'
import { BaseError } from '../../errors/base.js'
import { IntegerOutOfRangeError } from '../../errors/encoding.js'
import type { ErrorType } from '../../errors/utils.js'
import type { Hex } from '../../types/misc.js'
import { type IsAddressErrorType, isAddress } from '../address/isAddress.js'
import { type ConcatErrorType, concat } from '../data/concat.js'
import { type PadHexErrorType, padHex } from '../data/pad.js'
import { type SizeErrorType, size } from '../data/size.js'
import { type SliceErrorType, slice } from '../data/slice.js'
import {
type BoolToHexErrorType,
boolToHex,
type NumberToHexErrorType,
numberToHex,
type StringToHexErrorType,
stringToHex,
} from '../encoding/toHex.js'
import { integerRegex } from '../regex.js'
export type EncodeAbiParametersReturnType = Hex
export type EncodeAbiParametersErrorType =
| AbiEncodingLengthMismatchErrorType
| PrepareParamsErrorType
| EncodeParamsErrorType
| ErrorType
/**
* @description Encodes a list of primitive values into an ABI-encoded hex value.
*
* - Docs: https://viem.sh/docs/abi/encodeAbiParameters#encodeabiparameters
*
* Generates ABI encoded data using the [ABI specification](https://docs.soliditylang.org/en/latest/abi-spec), given a set of ABI parameters (inputs/outputs) and their corresponding values.
*
* @param params - a set of ABI Parameters (params), that can be in the shape of the inputs or outputs attribute of an ABI Item.
* @param values - a set of values (values) that correspond to the given params.
* @example
* ```typescript
* import { encodeAbiParameters } from 'viem'
*
* const encodedData = encodeAbiParameters(
* [
* { name: 'x', type: 'string' },
* { name: 'y', type: 'uint' },
* { name: 'z', type: 'bool' }
* ],
* ['wagmi', 420n, true]
* )
* ```
*
* You can also pass in Human Readable parameters with the parseAbiParameters utility.
*
* @example
* ```typescript
* import { encodeAbiParameters, parseAbiParameters } from 'viem'
*
* const encodedData = encodeAbiParameters(
* parseAbiParameters('string x, uint y, bool z'),
* ['wagmi', 420n, true]
* )
* ```
*/
export function encodeAbiParameters<
const params extends readonly AbiParameter[] | readonly unknown[],
>(
params: params,
values: params extends readonly AbiParameter[]
? AbiParametersToPrimitiveTypes<params, AbiParameterKind, true>
: never,
): EncodeAbiParametersReturnType {
if (params.length !== values.length)
throw new AbiEncodingLengthMismatchError({
expectedLength: params.length as number,
givenLength: values.length as any,
})
// Prepare the parameters to determine dynamic types to encode.
const preparedParams = prepareParams({
params: params as readonly AbiParameter[],
values: values as any,
})
const data = encodeParams(preparedParams)
if (data.length === 0) return '0x'
return data
}
/////////////////////////////////////////////////////////////////
type PreparedParam = { dynamic: boolean; encoded: Hex }
type TupleAbiParameter = AbiParameter & { components: readonly AbiParameter[] }
type Tuple = AbiParameterToPrimitiveType<TupleAbiParameter>
type PrepareParamsErrorType = PrepareParamErrorType | ErrorType
function prepareParams<const params extends readonly AbiParameter[]>({
params,
values,
}: {
params: params
values: AbiParametersToPrimitiveTypes<params>
}) {
const preparedParams: PreparedParam[] = []
for (let i = 0; i < params.length; i++) {
preparedParams.push(prepareParam({ param: params[i], value: values[i] }))
}
return preparedParams
}
type PrepareParamErrorType =
| EncodeAddressErrorType
| EncodeArrayErrorType
| EncodeBytesErrorType
| EncodeBoolErrorType
| EncodeNumberErrorType
| EncodeStringErrorType
| EncodeTupleErrorType
| GetArrayComponentsErrorType
| InvalidAbiEncodingTypeErrorType
| ErrorType
function prepareParam<const param extends AbiParameter>({
param,
value,
}: {
param: param
value: AbiParameterToPrimitiveType<param>
}): PreparedParam {
const arrayComponents = getArrayComponents(param.type)
if (arrayComponents) {
const [length, type] = arrayComponents
return encodeArray(value, { length, param: { ...param, type } })
}
if (param.type === 'tuple') {
return encodeTuple(value as unknown as Tuple, {
param: param as TupleAbiParameter,
})
}
if (param.type === 'address') {
return encodeAddress(value as unknown as Hex)
}
if (param.type === 'bool') {
return encodeBool(value as unknown as boolean)
}
if (param.type.startsWith('uint') || param.type.startsWith('int')) {
const signed = param.type.startsWith('int')
const [, , size = '256'] = integerRegex.exec(param.type) ?? []
return encodeNumber(value as unknown as number, {
signed,
size: Number(size),
})
}
if (param.type.startsWith('bytes')) {
return encodeBytes(value as unknown as Hex, { param })
}
if (param.type === 'string') {
return encodeString(value as unknown as string)
}
throw new InvalidAbiEncodingTypeError(param.type, {
docsPath: '/docs/contract/encodeAbiParameters',
})
}
/////////////////////////////////////////////////////////////////
type EncodeParamsErrorType = NumberToHexErrorType | SizeErrorType | ErrorType
function encodeParams(preparedParams: PreparedParam[]): Hex {
// 1. Compute the size of the static part of the parameters.
let staticSize = 0
for (let i = 0; i < preparedParams.length; i++) {
const { dynamic, encoded } = preparedParams[i]
if (dynamic) staticSize += 32
else staticSize += size(encoded)
}
// 2. Split the parameters into static and dynamic parts.
const staticParams: Hex[] = []
const dynamicParams: Hex[] = []
let dynamicSize = 0
for (let i = 0; i < preparedParams.length; i++) {
const { dynamic, encoded } = preparedParams[i]
if (dynamic) {
staticParams.push(numberToHex(staticSize + dynamicSize, { size: 32 }))
dynamicParams.push(encoded)
dynamicSize += size(encoded)
} else {
staticParams.push(encoded)
}
}
// 3. Concatenate static and dynamic parts.
return concat([...staticParams, ...dynamicParams])
}
/////////////////////////////////////////////////////////////////
type EncodeAddressErrorType =
| InvalidAddressErrorType
| IsAddressErrorType
| ErrorType
function encodeAddress(value: Hex): PreparedParam {
if (!isAddress(value)) throw new InvalidAddressError({ address: value })
return { dynamic: false, encoded: padHex(value.toLowerCase() as Hex) }
}
type EncodeArrayErrorType =
| AbiEncodingArrayLengthMismatchErrorType
| ConcatErrorType
| EncodeParamsErrorType
| InvalidArrayErrorType
| NumberToHexErrorType
// TODO: Add back once circular type reference is resolved
// | PrepareParamErrorType
| ErrorType
function encodeArray<const param extends AbiParameter>(
value: AbiParameterToPrimitiveType<param>,
{
length,
param,
}: {
length: number | null
param: param
},
): PreparedParam {
const dynamic = length === null
if (!Array.isArray(value)) throw new InvalidArrayError(value)
if (!dynamic && value.length !== length)
throw new AbiEncodingArrayLengthMismatchError({
expectedLength: length!,
givenLength: value.length,
type: `${param.type}[${length}]`,
})
let dynamicChild = false
const preparedParams: PreparedParam[] = []
for (let i = 0; i < value.length; i++) {
const preparedParam = prepareParam({ param, value: value[i] })
if (preparedParam.dynamic) dynamicChild = true
preparedParams.push(preparedParam)
}
if (dynamic || dynamicChild) {
const data = encodeParams(preparedParams)
if (dynamic) {
const length = numberToHex(preparedParams.length, { size: 32 })
return {
dynamic: true,
encoded: preparedParams.length > 0 ? concat([length, data]) : length,
}
}
if (dynamicChild) return { dynamic: true, encoded: data }
}
return {
dynamic: false,
encoded: concat(preparedParams.map(({ encoded }) => encoded)),
}
}
type EncodeBytesErrorType =
| AbiEncodingBytesSizeMismatchErrorType
| ConcatErrorType
| PadHexErrorType
| NumberToHexErrorType
| SizeErrorType
| ErrorType
function encodeBytes<const param extends AbiParameter>(
value: Hex,
{ param }: { param: param },
): PreparedParam {
const [, paramSize] = param.type.split('bytes')
const bytesSize = size(value)
if (!paramSize) {
let value_ = value
// If the size is not divisible by 32 bytes, pad the end
// with empty bytes to the ceiling 32 bytes.
if (bytesSize % 32 !== 0)
value_ = padHex(value_, {
dir: 'right',
size: Math.ceil((value.length - 2) / 2 / 32) * 32,
})
return {
dynamic: true,
encoded: concat([padHex(numberToHex(bytesSize, { size: 32 })), value_]),
}
}
if (bytesSize !== Number.parseInt(paramSize, 10))
throw new AbiEncodingBytesSizeMismatchError({
expectedSize: Number.parseInt(paramSize, 10),
value,
})
return { dynamic: false, encoded: padHex(value, { dir: 'right' }) }
}
type EncodeBoolErrorType = PadHexErrorType | BoolToHexErrorType | ErrorType
function encodeBool(value: boolean): PreparedParam {
if (typeof value !== 'boolean')
throw new BaseError(
`Invalid boolean value: "${value}" (type: ${typeof value}). Expected: \`true\` or \`false\`.`,
)
return { dynamic: false, encoded: padHex(boolToHex(value)) }
}
type EncodeNumberErrorType = NumberToHexErrorType | ErrorType
function encodeNumber(
value: number,
{ signed, size = 256 }: { signed: boolean; size?: number | undefined },
): PreparedParam {
if (typeof size === 'number') {
const max = 2n ** (BigInt(size) - (signed ? 1n : 0n)) - 1n
const min = signed ? -max - 1n : 0n
if (value > max || value < min)
throw new IntegerOutOfRangeError({
max: max.toString(),
min: min.toString(),
signed,
size: size / 8,
value: value.toString(),
})
}
return {
dynamic: false,
encoded: numberToHex(value, {
size: 32,
signed,
}),
}
}
type EncodeStringErrorType =
| ConcatErrorType
| NumberToHexErrorType
| PadHexErrorType
| SizeErrorType
| SliceErrorType
| StringToHexErrorType
| ErrorType
function encodeString(value: string): PreparedParam {
const hexValue = stringToHex(value)
const partsLength = Math.ceil(size(hexValue) / 32)
const parts: Hex[] = []
for (let i = 0; i < partsLength; i++) {
parts.push(
padHex(slice(hexValue, i * 32, (i + 1) * 32), {
dir: 'right',
}),
)
}
return {
dynamic: true,
encoded: concat([
padHex(numberToHex(size(hexValue), { size: 32 })),
...parts,
]),
}
}
type EncodeTupleErrorType =
| ConcatErrorType
| EncodeParamsErrorType
// TODO: Add back once circular type reference is resolved
// | PrepareParamErrorType
| ErrorType
function encodeTuple<
const param extends AbiParameter & { components: readonly AbiParameter[] },
>(
value: AbiParameterToPrimitiveType<param>,
{ param }: { param: param },
): PreparedParam {
let dynamic = false
const preparedParams: PreparedParam[] = []
for (let i = 0; i < param.components.length; i++) {
const param_ = param.components[i]
const index = Array.isArray(value) ? i : param_.name
const preparedParam = prepareParam({
param: param_,
value: (value as any)[index!] as readonly unknown[],
})
preparedParams.push(preparedParam)
if (preparedParam.dynamic) dynamic = true
}
return {
dynamic,
encoded: dynamic
? encodeParams(preparedParams)
: concat(preparedParams.map(({ encoded }) => encoded)),
}
}
type GetArrayComponentsErrorType = ErrorType
export function getArrayComponents(
type: string,
): [length: number | null, innerType: string] | undefined {
const matches = type.match(/^(.*)\[(\d+)?\]$/)
return matches
? // Return `null` if the array is dynamic.
[matches[2] ? Number(matches[2]) : null, matches[1]]
: undefined
}

65
node_modules/viem/utils/abi/encodeDeployData.ts generated vendored Normal file
View File

@@ -0,0 +1,65 @@
import type { Abi } from 'abitype'
import {
AbiConstructorNotFoundError,
type AbiConstructorNotFoundErrorType,
AbiConstructorParamsNotFoundError,
} from '../../errors/abi.js'
import type { ErrorType } from '../../errors/utils.js'
import type { ContractConstructorArgs } from '../../types/contract.js'
import type { Hex } from '../../types/misc.js'
import type { UnionEvaluate } from '../../types/utils.js'
import { type ConcatHexErrorType, concatHex } from '../data/concat.js'
import {
type EncodeAbiParametersErrorType,
encodeAbiParameters,
} from './encodeAbiParameters.js'
const docsPath = '/docs/contract/encodeDeployData'
export type EncodeDeployDataParameters<
abi extends Abi | readonly unknown[] = Abi,
///
hasConstructor = abi extends Abi
? Abi extends abi
? true
: [Extract<abi[number], { type: 'constructor' }>] extends [never]
? false
: true
: true,
allArgs = ContractConstructorArgs<abi>,
> = {
abi: abi
bytecode: Hex
} & UnionEvaluate<
hasConstructor extends false
? { args?: undefined }
: readonly [] extends allArgs
? { args?: allArgs | undefined }
: { args: allArgs }
>
export type EncodeDeployDataReturnType = Hex
export type EncodeDeployDataErrorType =
| AbiConstructorNotFoundErrorType
| ConcatHexErrorType
| EncodeAbiParametersErrorType
| ErrorType
export function encodeDeployData<const abi extends Abi | readonly unknown[]>(
parameters: EncodeDeployDataParameters<abi>,
): EncodeDeployDataReturnType {
const { abi, args, bytecode } = parameters as EncodeDeployDataParameters
if (!args || args.length === 0) return bytecode
const description = abi.find((x) => 'type' in x && x.type === 'constructor')
if (!description) throw new AbiConstructorNotFoundError({ docsPath })
if (!('inputs' in description))
throw new AbiConstructorParamsNotFoundError({ docsPath })
if (!description.inputs || description.inputs.length === 0)
throw new AbiConstructorParamsNotFoundError({ docsPath })
const data = encodeAbiParameters(description.inputs, args)
return concatHex([bytecode, data!])
}

96
node_modules/viem/utils/abi/encodeErrorResult.ts generated vendored Normal file
View File

@@ -0,0 +1,96 @@
import type { Abi, ExtractAbiErrors } from 'abitype'
import {
AbiErrorInputsNotFoundError,
AbiErrorNotFoundError,
} from '../../errors/abi.js'
import type { ErrorType } from '../../errors/utils.js'
import type {
ContractErrorArgs,
ContractErrorName,
} from '../../types/contract.js'
import type { Hex } from '../../types/misc.js'
import type { IsNarrowable, UnionEvaluate } from '../../types/utils.js'
import { type ConcatHexErrorType, concatHex } from '../data/concat.js'
import {
type ToFunctionSelectorErrorType,
toFunctionSelector,
} from '../hash/toFunctionSelector.js'
import {
type EncodeAbiParametersErrorType,
encodeAbiParameters,
} from './encodeAbiParameters.js'
import { type FormatAbiItemErrorType, formatAbiItem } from './formatAbiItem.js'
import { type GetAbiItemErrorType, getAbiItem } from './getAbiItem.js'
const docsPath = '/docs/contract/encodeErrorResult'
export type EncodeErrorResultParameters<
abi extends Abi | readonly unknown[] = Abi,
errorName extends ContractErrorName<abi> | undefined = ContractErrorName<abi>,
///
hasErrors = abi extends Abi
? Abi extends abi
? true
: [ExtractAbiErrors<abi>] extends [never]
? false
: true
: true,
allArgs = ContractErrorArgs<
abi,
errorName extends ContractErrorName<abi>
? errorName
: ContractErrorName<abi>
>,
allErrorNames = ContractErrorName<abi>,
> = {
abi: abi
args?: allArgs | undefined
} & UnionEvaluate<
IsNarrowable<abi, Abi> extends true
? abi['length'] extends 1
? { errorName?: errorName | allErrorNames | undefined }
: { errorName: errorName | allErrorNames }
: { errorName?: errorName | allErrorNames | undefined }
> &
(hasErrors extends true ? unknown : never)
export type EncodeErrorResultReturnType = Hex
export type EncodeErrorResultErrorType =
| GetAbiItemErrorType
| FormatAbiItemErrorType
| ToFunctionSelectorErrorType
| EncodeAbiParametersErrorType
| ConcatHexErrorType
| ErrorType
export function encodeErrorResult<
const abi extends Abi | readonly unknown[],
errorName extends ContractErrorName<abi> | undefined = undefined,
>(
parameters: EncodeErrorResultParameters<abi, errorName>,
): EncodeErrorResultReturnType {
const { abi, errorName, args } = parameters as EncodeErrorResultParameters
let abiItem = abi[0]
if (errorName) {
const item = getAbiItem({ abi, args, name: errorName })
if (!item) throw new AbiErrorNotFoundError(errorName, { docsPath })
abiItem = item
}
if (abiItem.type !== 'error')
throw new AbiErrorNotFoundError(undefined, { docsPath })
const definition = formatAbiItem(abiItem)
const signature = toFunctionSelector(definition)
let data: Hex = '0x'
if (args && args.length > 0) {
if (!abiItem.inputs)
throw new AbiErrorInputsNotFoundError(abiItem.name, { docsPath })
data = encodeAbiParameters(abiItem.inputs, args)
}
return concatHex([signature, data])
}

146
node_modules/viem/utils/abi/encodeEventTopics.ts generated vendored Normal file
View File

@@ -0,0 +1,146 @@
import type {
Abi,
AbiParameter,
AbiParameterToPrimitiveType,
ExtractAbiEvents,
} from 'abitype'
import {
AbiEventNotFoundError,
type AbiEventNotFoundErrorType,
} from '../../errors/abi.js'
import {
FilterTypeNotSupportedError,
type FilterTypeNotSupportedErrorType,
} from '../../errors/log.js'
import type { ErrorType } from '../../errors/utils.js'
import type {
ContractEventArgs,
ContractEventName,
EventDefinition,
} from '../../types/contract.js'
import type { Hex } from '../../types/misc.js'
import type { IsNarrowable, UnionEvaluate } from '../../types/utils.js'
import { type ToBytesErrorType, toBytes } from '../encoding/toBytes.js'
import { type Keccak256ErrorType, keccak256 } from '../hash/keccak256.js'
import {
type ToEventSelectorErrorType,
toEventSelector,
} from '../hash/toEventSelector.js'
import {
type EncodeAbiParametersErrorType,
encodeAbiParameters,
} from './encodeAbiParameters.js'
import { type FormatAbiItemErrorType, formatAbiItem } from './formatAbiItem.js'
import { type GetAbiItemErrorType, getAbiItem } from './getAbiItem.js'
const docsPath = '/docs/contract/encodeEventTopics'
export type EncodeEventTopicsParameters<
abi extends Abi | readonly unknown[] = Abi,
eventName extends ContractEventName<abi> | undefined = ContractEventName<abi>,
///
hasEvents = abi extends Abi
? Abi extends abi
? true
: [ExtractAbiEvents<abi>] extends [never]
? false
: true
: true,
allArgs = ContractEventArgs<
abi,
eventName extends ContractEventName<abi>
? eventName
: ContractEventName<abi>
>,
allErrorNames = ContractEventName<abi>,
> = {
abi: abi
args?: allArgs | undefined
} & UnionEvaluate<
IsNarrowable<abi, Abi> extends true
? abi['length'] extends 1
? { eventName?: eventName | allErrorNames | undefined }
: { eventName: eventName | allErrorNames }
: { eventName?: eventName | allErrorNames | undefined }
> &
(hasEvents extends true ? unknown : never)
export type EncodeEventTopicsReturnType = [Hex, ...(Hex | Hex[] | null)[]]
export type EncodeEventTopicsErrorType =
| AbiEventNotFoundErrorType
| EncodeArgErrorType
| FormatAbiItemErrorType
| GetAbiItemErrorType
| ToEventSelectorErrorType
| ErrorType
export function encodeEventTopics<
const abi extends Abi | readonly unknown[],
eventName extends ContractEventName<abi> | undefined = undefined,
>(
parameters: EncodeEventTopicsParameters<abi, eventName>,
): EncodeEventTopicsReturnType {
const { abi, eventName, args } = parameters as EncodeEventTopicsParameters
let abiItem = abi[0]
if (eventName) {
const item = getAbiItem({ abi, name: eventName })
if (!item) throw new AbiEventNotFoundError(eventName, { docsPath })
abiItem = item
}
if (abiItem.type !== 'event')
throw new AbiEventNotFoundError(undefined, { docsPath })
const definition = formatAbiItem(abiItem)
const signature = toEventSelector(definition as EventDefinition)
let topics: (Hex | Hex[] | null)[] = []
if (args && 'inputs' in abiItem) {
const indexedInputs = abiItem.inputs?.filter(
(param) => 'indexed' in param && param.indexed,
)
const args_ = Array.isArray(args)
? args
: Object.values(args).length > 0
? (indexedInputs?.map((x: any) => (args as any)[x.name]) ?? [])
: []
if (args_.length > 0) {
topics =
indexedInputs?.map((param, i) => {
if (Array.isArray(args_[i]))
return args_[i].map((_: any, j: number) =>
encodeArg({ param, value: args_[i][j] }),
)
return typeof args_[i] !== 'undefined' && args_[i] !== null
? encodeArg({ param, value: args_[i] })
: null
}) ?? []
}
}
return [signature, ...topics]
}
export type EncodeArgErrorType =
| Keccak256ErrorType
| ToBytesErrorType
| EncodeAbiParametersErrorType
| FilterTypeNotSupportedErrorType
| ErrorType
function encodeArg({
param,
value,
}: {
param: AbiParameter
value: AbiParameterToPrimitiveType<AbiParameter>
}) {
if (param.type === 'string' || param.type === 'bytes')
return keccak256(toBytes(value as string))
if (param.type === 'tuple' || param.type.match(/^(.*)\[(\d+)?\]$/))
throw new FilterTypeNotSupportedError(param.type)
return encodeAbiParameters([param], [value])
}

95
node_modules/viem/utils/abi/encodeFunctionData.ts generated vendored Normal file
View File

@@ -0,0 +1,95 @@
import type { Abi, AbiStateMutability, ExtractAbiFunctions } from 'abitype'
import type { AbiFunctionNotFoundErrorType } from '../../errors/abi.js'
import type { ErrorType } from '../../errors/utils.js'
import type {
ContractFunctionArgs,
ContractFunctionName,
} from '../../types/contract.js'
import type { Hex } from '../../types/misc.js'
import type { IsNarrowable, UnionEvaluate } from '../../types/utils.js'
import { type ConcatHexErrorType, concatHex } from '../data/concat.js'
import type { ToFunctionSelectorErrorType } from '../hash/toFunctionSelector.js'
import {
type EncodeAbiParametersErrorType,
encodeAbiParameters,
} from './encodeAbiParameters.js'
import type { FormatAbiItemErrorType } from './formatAbiItem.js'
import type { GetAbiItemErrorType } from './getAbiItem.js'
import { prepareEncodeFunctionData } from './prepareEncodeFunctionData.js'
export type EncodeFunctionDataParameters<
abi extends Abi | readonly unknown[] = Abi,
functionName extends
| ContractFunctionName<abi>
| Hex
| undefined = ContractFunctionName<abi>,
///
hasFunctions = abi extends Abi
? Abi extends abi
? true
: [ExtractAbiFunctions<abi>] extends [never]
? false
: true
: true,
allArgs = ContractFunctionArgs<
abi,
AbiStateMutability,
functionName extends ContractFunctionName<abi>
? functionName
: ContractFunctionName<abi>
>,
allFunctionNames = ContractFunctionName<abi>,
> = {
abi: abi
} & UnionEvaluate<
IsNarrowable<abi, Abi> extends true
? abi['length'] extends 1
? { functionName?: functionName | allFunctionNames | Hex | undefined }
: { functionName: functionName | allFunctionNames | Hex }
: { functionName?: functionName | allFunctionNames | Hex | undefined }
> &
UnionEvaluate<
readonly [] extends allArgs
? { args?: allArgs | undefined }
: { args: allArgs }
> &
(hasFunctions extends true ? unknown : never)
export type EncodeFunctionDataReturnType = Hex
export type EncodeFunctionDataErrorType =
| AbiFunctionNotFoundErrorType
| ConcatHexErrorType
| EncodeAbiParametersErrorType
| FormatAbiItemErrorType
| GetAbiItemErrorType
| ToFunctionSelectorErrorType
| ErrorType
export function encodeFunctionData<
const abi extends Abi | readonly unknown[],
functionName extends ContractFunctionName<abi> | undefined = undefined,
>(
parameters: EncodeFunctionDataParameters<abi, functionName>,
): EncodeFunctionDataReturnType {
const { args } = parameters as EncodeFunctionDataParameters
const { abi, functionName } = (() => {
if (
parameters.abi.length === 1 &&
parameters.functionName?.startsWith('0x')
)
return parameters as { abi: Abi; functionName: Hex }
return prepareEncodeFunctionData(parameters)
})()
const abiItem = abi[0]
const signature = functionName
const data =
'inputs' in abiItem && abiItem.inputs
? encodeAbiParameters(abiItem.inputs, args ?? [])
: undefined
return concatHex([signature, data ?? '0x'])
}

97
node_modules/viem/utils/abi/encodeFunctionResult.ts generated vendored Normal file
View File

@@ -0,0 +1,97 @@
import type { Abi, AbiStateMutability, ExtractAbiFunctions } from 'abitype'
import {
AbiFunctionNotFoundError,
AbiFunctionOutputsNotFoundError,
InvalidArrayError,
} from '../../errors/abi.js'
import type { ErrorType } from '../../errors/utils.js'
import type {
ContractFunctionName,
ContractFunctionReturnType,
} from '../../types/contract.js'
import type { Hex } from '../../types/misc.js'
import type { IsNarrowable, UnionEvaluate } from '../../types/utils.js'
import {
type EncodeAbiParametersErrorType,
encodeAbiParameters,
} from './encodeAbiParameters.js'
import { type GetAbiItemErrorType, getAbiItem } from './getAbiItem.js'
const docsPath = '/docs/contract/encodeFunctionResult'
export type EncodeFunctionResultParameters<
abi extends Abi | readonly unknown[] = Abi,
functionName extends
| ContractFunctionName<abi>
| undefined = ContractFunctionName<abi>,
///
hasFunctions = abi extends Abi
? Abi extends abi
? true
: [ExtractAbiFunctions<abi>] extends [never]
? false
: true
: true,
allFunctionNames = ContractFunctionName<abi>,
> = {
abi: abi
result?:
| ContractFunctionReturnType<
abi,
AbiStateMutability,
functionName extends ContractFunctionName<abi>
? functionName
: ContractFunctionName<abi>,
never // allow all args. required for overloads to work.
>
| undefined
} & UnionEvaluate<
IsNarrowable<abi, Abi> extends true
? abi['length'] extends 1
? { functionName?: functionName | allFunctionNames | undefined }
: { functionName: functionName | allFunctionNames }
: { functionName?: functionName | allFunctionNames | undefined }
> &
(hasFunctions extends true ? unknown : never)
export type EncodeFunctionResultReturnType = Hex
export type EncodeFunctionResultErrorType =
| AbiFunctionOutputsNotFoundError
| AbiFunctionNotFoundError
| EncodeAbiParametersErrorType
| GetAbiItemErrorType
| ErrorType
export function encodeFunctionResult<
const abi extends Abi | readonly unknown[],
functionName extends ContractFunctionName<abi> | undefined = undefined,
>(
parameters: EncodeFunctionResultParameters<abi, functionName>,
): EncodeFunctionResultReturnType {
const { abi, functionName, result } =
parameters as EncodeFunctionResultParameters
let abiItem = abi[0]
if (functionName) {
const item = getAbiItem({ abi, name: functionName })
if (!item) throw new AbiFunctionNotFoundError(functionName, { docsPath })
abiItem = item
}
if (abiItem.type !== 'function')
throw new AbiFunctionNotFoundError(undefined, { docsPath })
if (!abiItem.outputs)
throw new AbiFunctionOutputsNotFoundError(abiItem.name, { docsPath })
const values = (() => {
if (abiItem.outputs.length === 0) return []
if (abiItem.outputs.length === 1) return [result]
if (Array.isArray(result)) return result
throw new InvalidArrayError(result)
})()
return encodeAbiParameters(abiItem.outputs, values)
}

140
node_modules/viem/utils/abi/encodePacked.ts generated vendored Normal file
View File

@@ -0,0 +1,140 @@
import type {
AbiParameterToPrimitiveType,
AbiType,
Address,
SolidityAddress,
SolidityArrayWithoutTuple,
SolidityBool,
SolidityBytes,
SolidityInt,
SolidityString,
} from 'abitype'
import {
AbiEncodingLengthMismatchError,
type AbiEncodingLengthMismatchErrorType,
BytesSizeMismatchError,
type BytesSizeMismatchErrorType,
UnsupportedPackedAbiType,
} from '../../errors/abi.js'
import {
InvalidAddressError,
type InvalidAddressErrorType,
} from '../../errors/address.js'
import type { ErrorType } from '../../errors/utils.js'
import type { Hex } from '../../types/misc.js'
import { type IsAddressErrorType, isAddress } from '../address/isAddress.js'
import { type ConcatHexErrorType, concatHex } from '../data/concat.js'
import { type PadErrorType, pad } from '../data/pad.js'
import {
type BoolToHexErrorType,
boolToHex,
type NumberToHexErrorType,
numberToHex,
type StringToHexErrorType,
stringToHex,
} from '../encoding/toHex.js'
import { arrayRegex, bytesRegex, integerRegex } from '../regex.js'
type PackedAbiType =
| SolidityAddress
| SolidityBool
| SolidityBytes
| SolidityInt
| SolidityString
| SolidityArrayWithoutTuple
type EncodePackedValues<
packedAbiTypes extends readonly PackedAbiType[] | readonly unknown[],
> = {
[K in keyof packedAbiTypes]: packedAbiTypes[K] extends AbiType
? AbiParameterToPrimitiveType<{ type: packedAbiTypes[K] }>
: unknown
}
export type EncodePackedErrorType =
| AbiEncodingLengthMismatchErrorType
| ConcatHexErrorType
| EncodeErrorType
| ErrorType
export function encodePacked<
const packedAbiTypes extends readonly PackedAbiType[] | readonly unknown[],
>(types: packedAbiTypes, values: EncodePackedValues<packedAbiTypes>): Hex {
if (types.length !== values.length)
throw new AbiEncodingLengthMismatchError({
expectedLength: types.length as number,
givenLength: values.length as number,
})
const data: Hex[] = []
for (let i = 0; i < (types as unknown[]).length; i++) {
const type = types[i]
const value = values[i]
data.push(encode(type, value))
}
return concatHex(data)
}
type EncodeErrorType =
| BoolToHexErrorType
| BytesSizeMismatchErrorType
| InvalidAddressErrorType
| IsAddressErrorType
| NumberToHexErrorType
| PadErrorType
| StringToHexErrorType
| UnsupportedPackedAbiType
| ErrorType
function encode<const packedAbiType extends PackedAbiType | unknown>(
type: packedAbiType,
value: EncodePackedValues<[packedAbiType]>[0],
isArray = false,
): Hex {
if (type === 'address') {
const address = value as Address
if (!isAddress(address)) throw new InvalidAddressError({ address })
return pad(address.toLowerCase() as Hex, {
size: isArray ? 32 : null,
}) as Address
}
if (type === 'string') return stringToHex(value as string)
if (type === 'bytes') return value as Hex
if (type === 'bool')
return pad(boolToHex(value as boolean), { size: isArray ? 32 : 1 })
const intMatch = (type as string).match(integerRegex)
if (intMatch) {
const [_type, baseType, bits = '256'] = intMatch
const size = Number.parseInt(bits, 10) / 8
return numberToHex(value as number, {
size: isArray ? 32 : size,
signed: baseType === 'int',
})
}
const bytesMatch = (type as string).match(bytesRegex)
if (bytesMatch) {
const [_type, size] = bytesMatch
if (Number.parseInt(size, 10) !== ((value as Hex).length - 2) / 2)
throw new BytesSizeMismatchError({
expectedSize: Number.parseInt(size, 10),
givenSize: ((value as Hex).length - 2) / 2,
})
return pad(value as Hex, { dir: 'right', size: isArray ? 32 : null }) as Hex
}
const arrayMatch = (type as string).match(arrayRegex)
if (arrayMatch && Array.isArray(value)) {
const [_type, childType] = arrayMatch
const data: Hex[] = []
for (let i = 0; i < value.length; i++) {
data.push(encode(childType, value[i], true))
}
if (data.length === 0) return '0x'
return concatHex(data)
}
throw new UnsupportedPackedAbiType(type)
}

54
node_modules/viem/utils/abi/formatAbiItem.ts generated vendored Normal file
View File

@@ -0,0 +1,54 @@
import type { AbiParameter } from 'abitype'
import {
InvalidDefinitionTypeError,
type InvalidDefinitionTypeErrorType,
} from '../../errors/abi.js'
import type { ErrorType } from '../../errors/utils.js'
import type { AbiItem } from '../../types/contract.js'
export type FormatAbiItemErrorType =
| FormatAbiParamsErrorType
| InvalidDefinitionTypeErrorType
| ErrorType
export function formatAbiItem(
abiItem: AbiItem,
{ includeName = false }: { includeName?: boolean | undefined } = {},
) {
if (
abiItem.type !== 'function' &&
abiItem.type !== 'event' &&
abiItem.type !== 'error'
)
throw new InvalidDefinitionTypeError(abiItem.type)
return `${abiItem.name}(${formatAbiParams(abiItem.inputs, { includeName })})`
}
export type FormatAbiParamsErrorType = ErrorType
export function formatAbiParams(
params: readonly AbiParameter[] | undefined,
{ includeName = false }: { includeName?: boolean | undefined } = {},
): string {
if (!params) return ''
return params
.map((param) => formatAbiParam(param, { includeName }))
.join(includeName ? ', ' : ',')
}
export type FormatAbiParamErrorType = ErrorType
function formatAbiParam(
param: AbiParameter,
{ includeName }: { includeName: boolean },
): string {
if (param.type.startsWith('tuple')) {
return `(${formatAbiParams(
(param as unknown as { components: AbiParameter[] }).components,
{ includeName },
)})${param.type.slice('tuple'.length)}`
}
return param.type + (includeName && param.name ? ` ${param.name}` : '')
}

31
node_modules/viem/utils/abi/formatAbiItemWithArgs.ts generated vendored Normal file
View File

@@ -0,0 +1,31 @@
import type { AbiParameter } from 'abitype'
import type { ErrorType } from '../../errors/utils.js'
import type { AbiItem } from '../../types/contract.js'
import { stringify } from '../stringify.js'
export type FormatAbiItemWithArgsErrorType = ErrorType
export function formatAbiItemWithArgs({
abiItem,
args,
includeFunctionName = true,
includeName = false,
}: {
abiItem: AbiItem
args: readonly unknown[]
includeFunctionName?: boolean | undefined
includeName?: boolean | undefined
}) {
if (!('name' in abiItem)) return
if (!('inputs' in abiItem)) return
if (!abiItem.inputs) return
return `${includeFunctionName ? abiItem.name : ''}(${abiItem.inputs
.map(
(input: AbiParameter, i: number) =>
`${includeName && input.name ? `${input.name}: ` : ''}${
typeof args[i] === 'object' ? stringify(args[i]) : args[i]
}`,
)
.join(', ')})`
}

257
node_modules/viem/utils/abi/getAbiItem.ts generated vendored Normal file
View File

@@ -0,0 +1,257 @@
import type { Abi, AbiParameter, Address } from 'abitype'
import {
AbiItemAmbiguityError,
type AbiItemAmbiguityErrorType,
} from '../../errors/abi.js'
import type { ErrorType } from '../../errors/utils.js'
import type {
AbiItem,
AbiItemArgs,
AbiItemName,
ExtractAbiItemForArgs,
Widen,
} from '../../types/contract.js'
import type { Hex } from '../../types/misc.js'
import type { UnionEvaluate } from '../../types/utils.js'
import { type IsHexErrorType, isHex } from '../../utils/data/isHex.js'
import { type IsAddressErrorType, isAddress } from '../address/isAddress.js'
import { toEventSelector } from '../hash/toEventSelector.js'
import {
type ToFunctionSelectorErrorType,
toFunctionSelector,
} from '../hash/toFunctionSelector.js'
export type GetAbiItemParameters<
abi extends Abi | readonly unknown[] = Abi,
name extends AbiItemName<abi> = AbiItemName<abi>,
args extends AbiItemArgs<abi, name> | undefined = AbiItemArgs<abi, name>,
///
allArgs = AbiItemArgs<abi, name>,
allNames = AbiItemName<abi>,
> = {
abi: abi
name:
| allNames // show all options
| (name extends allNames ? name : never) // infer value
| Hex // function selector
} & UnionEvaluate<
readonly [] extends allArgs
? {
args?:
| allArgs // show all options
// infer value, widen inferred value of `args` conditionally to match `allArgs`
| (abi extends Abi
? args extends allArgs
? Widen<args>
: never
: never)
| undefined
}
: {
args?:
| allArgs // show all options
| (Widen<args> & (args extends allArgs ? unknown : never)) // infer value, widen inferred value of `args` match `allArgs` (e.g. avoid union `args: readonly [123n] | readonly [bigint]`)
| undefined
}
>
export type GetAbiItemErrorType =
| IsArgOfTypeErrorType
| IsHexErrorType
| ToFunctionSelectorErrorType
| AbiItemAmbiguityErrorType
| ErrorType
export type GetAbiItemReturnType<
abi extends Abi | readonly unknown[] = Abi,
name extends AbiItemName<abi> = AbiItemName<abi>,
args extends AbiItemArgs<abi, name> | undefined = AbiItemArgs<abi, name>,
> = abi extends Abi
? Abi extends abi
? AbiItem | undefined
: ExtractAbiItemForArgs<
abi,
name,
args extends AbiItemArgs<abi, name> ? args : AbiItemArgs<abi, name>
>
: AbiItem | undefined
export function getAbiItem<
const abi extends Abi | readonly unknown[],
name extends AbiItemName<abi>,
const args extends AbiItemArgs<abi, name> | undefined = undefined,
>(
parameters: GetAbiItemParameters<abi, name, args>,
): GetAbiItemReturnType<abi, name, args> {
const { abi, args = [], name } = parameters as unknown as GetAbiItemParameters
const isSelector = isHex(name, { strict: false })
const abiItems = (abi as Abi).filter((abiItem) => {
if (isSelector) {
if (abiItem.type === 'function')
return toFunctionSelector(abiItem) === name
if (abiItem.type === 'event') return toEventSelector(abiItem) === name
return false
}
return 'name' in abiItem && abiItem.name === name
})
if (abiItems.length === 0)
return undefined as GetAbiItemReturnType<abi, name, args>
if (abiItems.length === 1)
return abiItems[0] as GetAbiItemReturnType<abi, name, args>
let matchedAbiItem: AbiItem | undefined
for (const abiItem of abiItems) {
if (!('inputs' in abiItem)) continue
if (!args || args.length === 0) {
if (!abiItem.inputs || abiItem.inputs.length === 0)
return abiItem as GetAbiItemReturnType<abi, name, args>
continue
}
if (!abiItem.inputs) continue
if (abiItem.inputs.length === 0) continue
if (abiItem.inputs.length !== args.length) continue
const matched = args.every((arg, index) => {
const abiParameter = 'inputs' in abiItem && abiItem.inputs![index]
if (!abiParameter) return false
return isArgOfType(arg, abiParameter)
})
if (matched) {
// Check for ambiguity against already matched parameters (e.g. `address` vs `bytes20`).
if (
matchedAbiItem &&
'inputs' in matchedAbiItem &&
matchedAbiItem.inputs
) {
const ambiguousTypes = getAmbiguousTypes(
abiItem.inputs,
matchedAbiItem.inputs,
args as readonly unknown[],
)
if (ambiguousTypes)
throw new AbiItemAmbiguityError(
{
abiItem,
type: ambiguousTypes[0],
},
{
abiItem: matchedAbiItem,
type: ambiguousTypes[1],
},
)
}
matchedAbiItem = abiItem
}
}
if (matchedAbiItem)
return matchedAbiItem as GetAbiItemReturnType<abi, name, args>
return abiItems[0] as GetAbiItemReturnType<abi, name, args>
}
type IsArgOfTypeErrorType = IsAddressErrorType | ErrorType
/** @internal */
export function isArgOfType(arg: unknown, abiParameter: AbiParameter): boolean {
const argType = typeof arg
const abiParameterType = abiParameter.type
switch (abiParameterType) {
case 'address':
return isAddress(arg as Address, { strict: false })
case 'bool':
return argType === 'boolean'
case 'function':
return argType === 'string'
case 'string':
return argType === 'string'
default: {
if (abiParameterType === 'tuple' && 'components' in abiParameter)
return Object.values(abiParameter.components).every(
(component, index) => {
return (
argType === 'object' &&
isArgOfType(
Object.values(arg as unknown[] | Record<string, unknown>)[
index
],
component as AbiParameter,
)
)
},
)
// `(u)int<M>`: (un)signed integer type of `M` bits, `0 < M <= 256`, `M % 8 == 0`
// https://regexr.com/6v8hp
if (
/^u?int(8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)?$/.test(
abiParameterType,
)
)
return argType === 'number' || argType === 'bigint'
// `bytes<M>`: binary type of `M` bytes, `0 < M <= 32`
// https://regexr.com/6va55
if (/^bytes([1-9]|1[0-9]|2[0-9]|3[0-2])?$/.test(abiParameterType))
return argType === 'string' || arg instanceof Uint8Array
// fixed-length (`<type>[M]`) and dynamic (`<type>[]`) arrays
// https://regexr.com/6va6i
if (/[a-z]+[1-9]{0,3}(\[[0-9]{0,}\])+$/.test(abiParameterType)) {
return (
Array.isArray(arg) &&
arg.every((x: unknown) =>
isArgOfType(x, {
...abiParameter,
// Pop off `[]` or `[M]` from end of type
type: abiParameterType.replace(/(\[[0-9]{0,}\])$/, ''),
} as AbiParameter),
)
)
}
return false
}
}
}
/** @internal */
export function getAmbiguousTypes(
sourceParameters: readonly AbiParameter[],
targetParameters: readonly AbiParameter[],
args: AbiItemArgs,
): AbiParameter['type'][] | undefined {
for (const parameterIndex in sourceParameters) {
const sourceParameter = sourceParameters[parameterIndex]
const targetParameter = targetParameters[parameterIndex]
if (
sourceParameter.type === 'tuple' &&
targetParameter.type === 'tuple' &&
'components' in sourceParameter &&
'components' in targetParameter
)
return getAmbiguousTypes(
sourceParameter.components,
targetParameter.components,
(args as any)[parameterIndex],
)
const types = [sourceParameter.type, targetParameter.type]
const ambiguous = (() => {
if (types.includes('address') && types.includes('bytes20')) return true
if (types.includes('address') && types.includes('string'))
return isAddress(args[parameterIndex] as Address, { strict: false })
if (types.includes('address') && types.includes('bytes'))
return isAddress(args[parameterIndex] as Address, { strict: false })
return false
})()
if (ambiguous) return types
}
return
}

251
node_modules/viem/utils/abi/parseEventLogs.ts generated vendored Normal file
View File

@@ -0,0 +1,251 @@
// TODO(v3): checksum address.
import type { Abi, AbiEvent, AbiEventParameter, Address } from 'abitype'
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 { formatLog } from '../formatters/log.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]
})()
const abiTopics = (abi as Abi)
.filter((abiItem) => abiItem.type === 'event')
.map((abiItem) => ({
abi: abiItem,
selector: toEventSelector(abiItem),
}))
return logs
.map((log) => {
// Normalize RpcLog (hex-encoded quantities) to Log (bigint/number).
// When logs come directly from an RPC response (e.g. eth_getLogs),
// fields like blockNumber are hex strings instead of bigints.
const formattedLog =
typeof log.blockNumber === 'string' ? formatLog(log as RpcLog) : log
// Find all matching ABI items with the same selector.
// Multiple events can share the same selector but differ in indexed parameters
// (e.g., ERC20 vs ERC721 Transfer events).
const abiItems = abiTopics.filter(
(abiTopic) => formattedLog.topics[0] === abiTopic.selector,
)
if (abiItems.length === 0) return null
// Try each matching ABI item until one successfully decodes.
let event: { eventName: string; args: unknown } | undefined
let abiItem: { abi: AbiEvent; selector: Address } | undefined
for (const item of abiItems) {
try {
event = decodeEventLog({
...formattedLog,
abi: [item.abi],
strict: true,
})
abiItem = item
break
} catch {
// Try next ABI item
}
}
// If strict decoding failed for all, and we're in non-strict mode,
// fall back to the first matching ABI item.
if (!event && !strict) {
abiItem = abiItems[0]
try {
event = decodeEventLog({
data: formattedLog.data,
topics: formattedLog.topics,
abi: [abiItem.abi],
strict: false,
})
} catch {
// If decoding still fails, return partial log in non-strict mode.
const isUnnamed = abiItem.abi.inputs?.some(
(x) => !('name' in x && x.name),
)
return {
...formattedLog,
args: isUnnamed ? [] : {},
eventName: abiItem.abi.name,
}
}
}
// If no event was found, return null.
if (!event || !abiItem) return null
// 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.abi.inputs,
matchArgs: args,
})
)
return null
return { ...event, ...formattedLog }
})
.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
}

View File

@@ -0,0 +1,111 @@
import type {
Abi,
AbiStateMutability,
ExtractAbiFunction,
ExtractAbiFunctions,
} from 'abitype'
import {
AbiFunctionNotFoundError,
type AbiFunctionNotFoundErrorType,
} from '../../errors/abi.js'
import type { ErrorType } from '../../errors/utils.js'
import type {
ContractFunctionArgs,
ContractFunctionName,
} from '../../types/contract.js'
import type { Hex } from '../../types/misc.js'
import type { IsNarrowable, UnionEvaluate } from '../../types/utils.js'
import type { ConcatHexErrorType } from '../data/concat.js'
import {
type ToFunctionSelectorErrorType,
toFunctionSelector,
} from '../hash/toFunctionSelector.js'
import { type FormatAbiItemErrorType, formatAbiItem } from './formatAbiItem.js'
import { type GetAbiItemErrorType, getAbiItem } from './getAbiItem.js'
const docsPath = '/docs/contract/encodeFunctionData'
export type PrepareEncodeFunctionDataParameters<
abi extends Abi | readonly unknown[] = Abi,
functionName extends
| ContractFunctionName<abi>
| undefined = ContractFunctionName<abi>,
///
hasFunctions = abi extends Abi
? Abi extends abi
? true
: [ExtractAbiFunctions<abi>] extends [never]
? false
: true
: true,
allArgs = ContractFunctionArgs<
abi,
AbiStateMutability,
functionName extends ContractFunctionName<abi>
? functionName
: ContractFunctionName<abi>
>,
allFunctionNames = ContractFunctionName<abi>,
> = {
abi: abi
} & UnionEvaluate<
IsNarrowable<abi, Abi> extends true
? abi['length'] extends 1
? { functionName?: functionName | allFunctionNames | Hex | undefined }
: { functionName: functionName | allFunctionNames | Hex }
: { functionName?: functionName | allFunctionNames | Hex | undefined }
> &
UnionEvaluate<{ args?: allArgs | undefined }> &
(hasFunctions extends true ? unknown : never)
export type PrepareEncodeFunctionDataReturnType<
abi extends Abi | readonly unknown[] = Abi,
functionName extends
| ContractFunctionName<abi>
| undefined = ContractFunctionName<abi>,
> = {
abi: abi extends Abi
? functionName extends ContractFunctionName<abi>
? [ExtractAbiFunction<abi, functionName>]
: abi
: Abi
functionName: Hex
}
export type PrepareEncodeFunctionDataErrorType =
| AbiFunctionNotFoundErrorType
| ConcatHexErrorType
| FormatAbiItemErrorType
| GetAbiItemErrorType
| ToFunctionSelectorErrorType
| ErrorType
export function prepareEncodeFunctionData<
const abi extends Abi | readonly unknown[],
functionName extends ContractFunctionName<abi> | undefined = undefined,
>(
parameters: PrepareEncodeFunctionDataParameters<abi, functionName>,
): PrepareEncodeFunctionDataReturnType<abi, functionName> {
const { abi, args, functionName } =
parameters as PrepareEncodeFunctionDataParameters
let abiItem = abi[0]
if (functionName) {
const item = getAbiItem({
abi,
args,
name: functionName,
})
if (!item) throw new AbiFunctionNotFoundError(functionName, { docsPath })
abiItem = item
}
if (abiItem.type !== 'function')
throw new AbiFunctionNotFoundError(undefined, { docsPath })
return {
abi: [abiItem],
functionName: toFunctionSelector(formatAbiItem(abiItem)),
} as unknown as PrepareEncodeFunctionDataReturnType<abi, functionName>
}