You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
842 lines
21 KiB
842 lines
21 KiB
import * as abitype from 'abitype'
|
|
import type * as Abi from './Abi.js'
|
|
import * as Errors from './Errors.js'
|
|
import * as Hash from './Hash.js'
|
|
import * as Hex from './Hex.js'
|
|
import * as internal from './internal/abiItem.js'
|
|
import type { UnionCompute } from './internal/types.js'
|
|
|
|
/** Root type for an item on an {@link ox#Abi.Abi}. */
|
|
export type AbiItem = Abi.Abi[number]
|
|
|
|
/**
|
|
* Extracts an {@link ox#AbiItem.AbiItem} item from an {@link ox#Abi.Abi}, given a name.
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* import { Abi, AbiItem } from 'ox'
|
|
*
|
|
* const abi = Abi.from([
|
|
* 'error Foo(string)',
|
|
* 'function foo(string)',
|
|
* 'event Bar(uint256)',
|
|
* ])
|
|
*
|
|
* type Foo = AbiItem.FromAbi<typeof abi, 'Foo'>
|
|
* // ^?
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
* ```
|
|
*/
|
|
export type FromAbi<
|
|
abi extends Abi.Abi,
|
|
name extends ExtractNames<abi>,
|
|
> = Extract<abi[number], { name: name }>
|
|
|
|
/**
|
|
* Extracts the names of all {@link ox#AbiItem.AbiItem} items in an {@link ox#Abi.Abi}.
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* import { Abi, AbiItem } from 'ox'
|
|
*
|
|
* const abi = Abi.from([
|
|
* 'error Foo(string)',
|
|
* 'function foo(string)',
|
|
* 'event Bar(uint256)',
|
|
* ])
|
|
*
|
|
* type names = AbiItem.Name<typeof abi>
|
|
* // ^?
|
|
*
|
|
* ```
|
|
*/
|
|
export type Name<abi extends Abi.Abi | readonly unknown[] = Abi.Abi> =
|
|
abi extends Abi.Abi ? ExtractNames<abi> : string
|
|
|
|
export type ExtractNames<abi extends Abi.Abi> = Extract<
|
|
abi[number],
|
|
{ name: string }
|
|
>['name']
|
|
|
|
/**
|
|
* Formats an {@link ox#AbiItem.AbiItem} into a **Human Readable ABI Item**.
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* import { AbiItem } from 'ox'
|
|
*
|
|
* const formatted = AbiItem.format({
|
|
* type: 'function',
|
|
* name: 'approve',
|
|
* stateMutability: 'nonpayable',
|
|
* inputs: [
|
|
* {
|
|
* name: 'spender',
|
|
* type: 'address',
|
|
* },
|
|
* {
|
|
* name: 'amount',
|
|
* type: 'uint256',
|
|
* },
|
|
* ],
|
|
* outputs: [{ type: 'bool' }],
|
|
* })
|
|
*
|
|
* formatted
|
|
* // ^?
|
|
*
|
|
*
|
|
* ```
|
|
*
|
|
* @param abiItem - The ABI Item to format.
|
|
* @returns The formatted ABI Item .
|
|
*/
|
|
export function format<const abiItem extends AbiItem>(
|
|
abiItem: abiItem | AbiItem,
|
|
): abitype.FormatAbiItem<abiItem> {
|
|
return abitype.formatAbiItem(abiItem) as never
|
|
}
|
|
|
|
export declare namespace format {
|
|
type ErrorType = Errors.GlobalErrorType
|
|
}
|
|
|
|
/**
|
|
* Parses an arbitrary **JSON ABI Item** or **Human Readable ABI Item** into a typed {@link ox#AbiItem.AbiItem}.
|
|
*
|
|
* @example
|
|
* ### JSON ABIs
|
|
*
|
|
* ```ts twoslash
|
|
* import { AbiItem } from 'ox'
|
|
*
|
|
* const abiItem = AbiItem.from({
|
|
* type: 'function',
|
|
* name: 'approve',
|
|
* stateMutability: 'nonpayable',
|
|
* inputs: [
|
|
* {
|
|
* name: 'spender',
|
|
* type: 'address',
|
|
* },
|
|
* {
|
|
* name: 'amount',
|
|
* type: 'uint256',
|
|
* },
|
|
* ],
|
|
* outputs: [{ type: 'bool' }],
|
|
* })
|
|
*
|
|
* abiItem
|
|
* //^?
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
* ```
|
|
*
|
|
* @example
|
|
* ### Human Readable ABIs
|
|
*
|
|
* A Human Readable ABI can be parsed into a typed ABI object:
|
|
*
|
|
* ```ts twoslash
|
|
* import { AbiItem } from 'ox'
|
|
*
|
|
* const abiItem = AbiItem.from(
|
|
* 'function approve(address spender, uint256 amount) returns (bool)' // [!code hl]
|
|
* )
|
|
*
|
|
* abiItem
|
|
* //^?
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
* ```
|
|
*
|
|
* @example
|
|
* It is possible to specify `struct`s along with your definitions:
|
|
*
|
|
* ```ts twoslash
|
|
* import { AbiItem } from 'ox'
|
|
*
|
|
* const abiItem = AbiItem.from([
|
|
* 'struct Foo { address spender; uint256 amount; }', // [!code hl]
|
|
* 'function approve(Foo foo) returns (bool)',
|
|
* ])
|
|
*
|
|
* abiItem
|
|
* //^?
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
* ```
|
|
*
|
|
*
|
|
*
|
|
* @param abiItem - The ABI Item to parse.
|
|
* @returns The typed ABI Item.
|
|
*/
|
|
export function from<
|
|
const abiItem extends AbiItem | string | readonly string[],
|
|
>(
|
|
abiItem: (abiItem | AbiItem | string | readonly string[]) &
|
|
(
|
|
| (abiItem extends string ? internal.Signature<abiItem> : never)
|
|
| (abiItem extends readonly string[]
|
|
? internal.Signatures<abiItem>
|
|
: never)
|
|
| AbiItem
|
|
),
|
|
options: from.Options = {},
|
|
): from.ReturnType<abiItem> {
|
|
const { prepare = true } = options
|
|
const item = (() => {
|
|
if (Array.isArray(abiItem)) return abitype.parseAbiItem(abiItem)
|
|
if (typeof abiItem === 'string')
|
|
return abitype.parseAbiItem(abiItem as never)
|
|
return abiItem
|
|
})() as AbiItem
|
|
return {
|
|
...item,
|
|
...(prepare ? { hash: getSignatureHash(item) } : {}),
|
|
} as never
|
|
}
|
|
|
|
export declare namespace from {
|
|
type Options = {
|
|
/**
|
|
* Whether or not to prepare the extracted item (optimization for encoding performance).
|
|
* When `true`, the `hash` property is computed and included in the returned value.
|
|
*
|
|
* @default true
|
|
*/
|
|
prepare?: boolean | undefined
|
|
}
|
|
|
|
type ReturnType<abiItem extends AbiItem | string | readonly string[]> =
|
|
abiItem extends string
|
|
? abitype.ParseAbiItem<abiItem>
|
|
: abiItem extends readonly string[]
|
|
? abitype.ParseAbiItem<abiItem>
|
|
: abiItem
|
|
|
|
type ErrorType = Errors.GlobalErrorType
|
|
}
|
|
|
|
/**
|
|
* Extracts an {@link ox#AbiItem.AbiItem} from an {@link ox#Abi.Abi} given a name and optional arguments.
|
|
*
|
|
* @example
|
|
* ABI Items can be extracted by their name using the `name` option:
|
|
*
|
|
* ```ts twoslash
|
|
* import { Abi, AbiItem } from 'ox'
|
|
*
|
|
* const abi = Abi.from([
|
|
* 'function foo()',
|
|
* 'event Transfer(address owner, address to, uint256 tokenId)',
|
|
* 'function bar(string a) returns (uint256 x)',
|
|
* ])
|
|
*
|
|
* const item = AbiItem.fromAbi(abi, 'Transfer') // [!code focus]
|
|
* // ^?
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
* ```
|
|
*
|
|
* @example
|
|
* ### Extracting by Selector
|
|
*
|
|
* ABI Items can be extract by their selector when {@link ox#Hex.Hex} is provided to `name`.
|
|
*
|
|
* ```ts twoslash
|
|
* import { Abi, AbiItem } from 'ox'
|
|
*
|
|
* const abi = Abi.from([
|
|
* 'function foo()',
|
|
* 'event Transfer(address owner, address to, uint256 tokenId)',
|
|
* 'function bar(string a) returns (uint256 x)',
|
|
* ])
|
|
* const item = AbiItem.fromAbi(abi, '0x095ea7b3') // [!code focus]
|
|
* // ^?
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
* ```
|
|
*
|
|
* :::note
|
|
*
|
|
* Extracting via a hex selector is useful when extracting an ABI Item from an `eth_call` RPC response,
|
|
* a Transaction `input`, or from Event Log `topics`.
|
|
*
|
|
* :::
|
|
*
|
|
* @param abi - The ABI to extract from.
|
|
* @param name - The name (or selector) of the ABI item to extract.
|
|
* @param options - Extraction options.
|
|
* @returns The ABI item.
|
|
*/
|
|
export function fromAbi<
|
|
const abi extends Abi.Abi | readonly unknown[],
|
|
name extends Name<abi>,
|
|
const args extends internal.ExtractArgs<abi, name> | undefined = undefined,
|
|
//
|
|
allNames = Name<abi>,
|
|
>(
|
|
abi: abi | Abi.Abi | readonly unknown[],
|
|
name: Hex.Hex | (name extends allNames ? name : never),
|
|
options?: fromAbi.Options<abi, name, args>,
|
|
): fromAbi.ReturnType<abi, name, args> {
|
|
const { args = [], prepare = true } = (options ??
|
|
{}) as unknown as fromAbi.Options
|
|
|
|
const isSelector = Hex.validate(name, { strict: false })
|
|
const abiItems = (abi as Abi.Abi).filter((abiItem) => {
|
|
if (isSelector) {
|
|
if (abiItem.type === 'function' || abiItem.type === 'error')
|
|
return getSelector(abiItem) === Hex.slice(name, 0, 4)
|
|
if (abiItem.type === 'event') return getSignatureHash(abiItem) === name
|
|
return false
|
|
}
|
|
return 'name' in abiItem && abiItem.name === name
|
|
})
|
|
|
|
if (abiItems.length === 0) throw new NotFoundError({ name: name as string })
|
|
if (abiItems.length === 1)
|
|
return {
|
|
...abiItems[0],
|
|
...(prepare ? { hash: getSignatureHash(abiItems[0]!) } : {}),
|
|
} as never
|
|
|
|
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,
|
|
...(prepare ? { hash: getSignatureHash(abiItem) } : {}),
|
|
} as never
|
|
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 internal.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 = internal.getAmbiguousTypes(
|
|
abiItem.inputs,
|
|
matchedAbiItem.inputs,
|
|
args as readonly unknown[],
|
|
)
|
|
if (ambiguousTypes)
|
|
throw new AmbiguityError(
|
|
{
|
|
abiItem,
|
|
type: ambiguousTypes[0]!,
|
|
},
|
|
{
|
|
abiItem: matchedAbiItem,
|
|
type: ambiguousTypes[1]!,
|
|
},
|
|
)
|
|
}
|
|
|
|
matchedAbiItem = abiItem
|
|
}
|
|
}
|
|
|
|
const abiItem = (() => {
|
|
if (matchedAbiItem) return matchedAbiItem
|
|
const [abiItem, ...overloads] = abiItems
|
|
return { ...abiItem!, overloads }
|
|
})()
|
|
|
|
if (!abiItem) throw new NotFoundError({ name: name as string })
|
|
return {
|
|
...abiItem,
|
|
...(prepare ? { hash: getSignatureHash(abiItem) } : {}),
|
|
} as never
|
|
}
|
|
|
|
export declare namespace fromAbi {
|
|
type Options<
|
|
abi extends Abi.Abi | readonly unknown[] = Abi.Abi,
|
|
name extends Name<abi> = Name<abi>,
|
|
args extends
|
|
| internal.ExtractArgs<abi, name>
|
|
| undefined = internal.ExtractArgs<abi, name>,
|
|
///
|
|
allArgs = internal.ExtractArgs<abi, name>,
|
|
> = {
|
|
/**
|
|
* Whether or not to prepare the extracted item (optimization for encoding performance).
|
|
* When `true`, the `hash` property is computed and included in the returned value.
|
|
*
|
|
* @default true
|
|
*/
|
|
prepare?: boolean | undefined
|
|
} & UnionCompute<
|
|
readonly [] extends allArgs
|
|
? {
|
|
args?:
|
|
| allArgs // show all options
|
|
// infer value, widen inferred value of `args` conditionally to match `allArgs`
|
|
| (abi extends Abi.Abi
|
|
? args extends allArgs
|
|
? internal.Widen<args>
|
|
: never
|
|
: never)
|
|
| undefined
|
|
}
|
|
: {
|
|
args?:
|
|
| allArgs // show all options
|
|
| (internal.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
|
|
}
|
|
>
|
|
|
|
type ReturnType<
|
|
abi extends Abi.Abi | readonly unknown[] = Abi.Abi,
|
|
name extends Name<abi> = Name<abi>,
|
|
args extends
|
|
| internal.ExtractArgs<abi, name>
|
|
| undefined = internal.ExtractArgs<abi, name>,
|
|
fallback = AbiItem,
|
|
> = abi extends Abi.Abi
|
|
? Abi.Abi extends abi
|
|
? fallback
|
|
: internal.ExtractForArgs<
|
|
abi,
|
|
name,
|
|
args extends internal.ExtractArgs<abi, name>
|
|
? args
|
|
: internal.ExtractArgs<abi, name>
|
|
>
|
|
: fallback
|
|
|
|
type ErrorType = Errors.GlobalErrorType
|
|
}
|
|
|
|
/**
|
|
* Computes the [4-byte selector](https://solidity-by-example.org/function-selector/) for an {@link ox#AbiItem.AbiItem}.
|
|
*
|
|
* Useful for computing function selectors for calldata.
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* import { AbiItem } from 'ox'
|
|
*
|
|
* const selector = AbiItem.getSelector('function ownerOf(uint256 tokenId)')
|
|
* // @log: '0x6352211e'
|
|
* ```
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* // @noErrors
|
|
* import { Abi, AbiItem } from 'ox'
|
|
*
|
|
* const erc20Abi = Abi.from([...])
|
|
*
|
|
* const selector = AbiItem.getSelector(erc20Abi, 'ownerOf')
|
|
* // @log: '0x6352211e'
|
|
* ```
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* import { AbiItem } from 'ox'
|
|
*
|
|
* const selector = AbiItem.getSelector({
|
|
* inputs: [{ type: 'uint256' }],
|
|
* name: 'ownerOf',
|
|
* outputs: [],
|
|
* stateMutability: 'view',
|
|
* type: 'function'
|
|
* })
|
|
* // @log: '0x6352211e'
|
|
* ```
|
|
*
|
|
* @param abiItem - The ABI item to compute the selector for. Can be a signature or an ABI item for an error, event, function, etc.
|
|
* @returns The first 4 bytes of the {@link ox#Hash.(keccak256:function)} hash of the function signature.
|
|
*/
|
|
export function getSelector<
|
|
abi extends Abi.Abi | readonly unknown[],
|
|
name extends Name<abi>,
|
|
>(abi: abi | Abi.Abi | readonly unknown[], name: name): Hex.Hex
|
|
export function getSelector(abiItem: string | AbiItem): Hex.Hex
|
|
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
export function getSelector(
|
|
...parameters:
|
|
| [abi: Abi.Abi | readonly unknown[], name: string]
|
|
| [string | AbiItem]
|
|
): Hex.Hex {
|
|
const abiItem = (() => {
|
|
if (Array.isArray(parameters[0])) {
|
|
const [abi, name] = parameters as [Abi.Abi | readonly unknown[], string]
|
|
return fromAbi(abi, name)
|
|
}
|
|
return parameters[0] as string | AbiItem
|
|
})()
|
|
return Hex.slice(getSignatureHash(abiItem), 0, 4)
|
|
}
|
|
|
|
export declare namespace getSelector {
|
|
type ErrorType =
|
|
| getSignatureHash.ErrorType
|
|
| Hex.slice.ErrorType
|
|
| Errors.GlobalErrorType
|
|
}
|
|
|
|
/**
|
|
* Computes the stringified signature for a given {@link ox#AbiItem.AbiItem}.
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* import { AbiItem } from 'ox'
|
|
*
|
|
* const signature = AbiItem.getSignature('function ownerOf(uint256 tokenId)')
|
|
* // @log: 'ownerOf(uint256)'
|
|
* ```
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* // @noErrors
|
|
* import { Abi, AbiItem } from 'ox'
|
|
*
|
|
* const erc20Abi = Abi.from([...])
|
|
*
|
|
* const signature = AbiItem.getSignature(erc20Abi, 'ownerOf')
|
|
* // @log: 'ownerOf(uint256)'
|
|
* ```
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* import { AbiItem } from 'ox'
|
|
*
|
|
* const signature = AbiItem.getSignature({
|
|
* name: 'ownerOf',
|
|
* type: 'function',
|
|
* inputs: [{ name: 'tokenId', type: 'uint256' }],
|
|
* outputs: [],
|
|
* stateMutability: 'view',
|
|
* })
|
|
* // @log: 'ownerOf(uint256)'
|
|
* ```
|
|
*
|
|
* @param abiItem - The ABI Item to compute the signature for.
|
|
* @returns The stringified signature of the ABI Item.
|
|
*/
|
|
export function getSignature<
|
|
abi extends Abi.Abi | readonly unknown[],
|
|
name extends Name<abi>,
|
|
>(abi: abi | Abi.Abi | readonly unknown[], name: name): string
|
|
export function getSignature(abiItem: string | AbiItem): string
|
|
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
export function getSignature(
|
|
...parameters:
|
|
| [abi: Abi.Abi | readonly unknown[], name: string]
|
|
| [string | AbiItem]
|
|
): string {
|
|
const abiItem = (() => {
|
|
if (Array.isArray(parameters[0])) {
|
|
const [abi, name] = parameters as [Abi.Abi | readonly unknown[], string]
|
|
return fromAbi(abi, name)
|
|
}
|
|
return parameters[0] as string | AbiItem
|
|
})()
|
|
const signature = (() => {
|
|
if (typeof abiItem === 'string') return abiItem
|
|
return abitype.formatAbiItem(abiItem)
|
|
})()
|
|
return internal.normalizeSignature(signature)
|
|
}
|
|
|
|
export declare namespace getSignature {
|
|
type ErrorType =
|
|
| internal.normalizeSignature.ErrorType
|
|
| Errors.GlobalErrorType
|
|
}
|
|
|
|
/**
|
|
* Computes the signature hash for an {@link ox#AbiItem.AbiItem}.
|
|
*
|
|
* Useful for computing Event Topic values.
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* import { AbiItem } from 'ox'
|
|
*
|
|
* const hash = AbiItem.getSignatureHash('event Transfer(address indexed from, address indexed to, uint256 amount)')
|
|
* // @log: '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
|
|
* ```
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* // @noErrors
|
|
* import { Abi, AbiItem } from 'ox'
|
|
*
|
|
* const erc20Abi = Abi.from([...])
|
|
*
|
|
* const hash = AbiItem.getSignatureHash(erc20Abi, 'Transfer')
|
|
* // @log: '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
|
|
* ```
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* import { AbiItem } from 'ox'
|
|
*
|
|
* const hash = AbiItem.getSignatureHash({
|
|
* name: 'Transfer',
|
|
* type: 'event',
|
|
* inputs: [
|
|
* { name: 'from', type: 'address', indexed: true },
|
|
* { name: 'to', type: 'address', indexed: true },
|
|
* { name: 'amount', type: 'uint256', indexed: false },
|
|
* ],
|
|
* })
|
|
* // @log: '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
|
|
* ```
|
|
*
|
|
* @param abiItem - The ABI Item to compute the signature hash for.
|
|
* @returns The {@link ox#Hash.(keccak256:function)} hash of the ABI item's signature.
|
|
*/
|
|
export function getSignatureHash<
|
|
abi extends Abi.Abi | readonly unknown[],
|
|
name extends Name<abi>,
|
|
>(abi: abi | Abi.Abi | readonly unknown[], name: name): Hex.Hex
|
|
export function getSignatureHash(abiItem: string | AbiItem): Hex.Hex
|
|
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
export function getSignatureHash(
|
|
...parameters:
|
|
| [abi: Abi.Abi | readonly unknown[], name: string]
|
|
| [string | AbiItem]
|
|
): Hex.Hex {
|
|
const abiItem = (() => {
|
|
if (Array.isArray(parameters[0])) {
|
|
const [abi, name] = parameters as [Abi.Abi | readonly unknown[], string]
|
|
return fromAbi(abi, name)
|
|
}
|
|
return parameters[0] as string | AbiItem
|
|
})()
|
|
if (typeof abiItem !== 'string' && 'hash' in abiItem && abiItem.hash)
|
|
return abiItem.hash as Hex.Hex
|
|
return Hash.keccak256(Hex.fromString(getSignature(abiItem)))
|
|
}
|
|
|
|
export declare namespace getSignatureHash {
|
|
type ErrorType =
|
|
| getSignature.ErrorType
|
|
| Hash.keccak256.ErrorType
|
|
| Hex.fromString.ErrorType
|
|
| Errors.GlobalErrorType
|
|
}
|
|
|
|
/**
|
|
* Throws when ambiguous types are found on overloaded ABI items.
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* import { Abi, AbiFunction } from 'ox'
|
|
*
|
|
* const foo = Abi.from(['function foo(address)', 'function foo(bytes20)'])
|
|
* AbiFunction.fromAbi(foo, 'foo', {
|
|
* args: ['0xA0Cf798816D4b9b9866b5330EEa46a18382f251e'],
|
|
* })
|
|
* // @error: AbiItem.AmbiguityError: Found ambiguous types in overloaded ABI Items.
|
|
* // @error: `bytes20` in `foo(bytes20)`, and
|
|
* // @error: `address` in `foo(address)`
|
|
* // @error: These types encode differently and cannot be distinguished at runtime.
|
|
* // @error: Remove one of the ambiguous items in the ABI.
|
|
* ```
|
|
*
|
|
* ### Solution
|
|
*
|
|
* Remove one of the ambiguous types from the ABI.
|
|
*
|
|
* ```ts twoslash
|
|
* import { Abi, AbiFunction } from 'ox'
|
|
*
|
|
* const foo = Abi.from([
|
|
* 'function foo(address)',
|
|
* 'function foo(bytes20)' // [!code --]
|
|
* ])
|
|
* AbiFunction.fromAbi(foo, 'foo', {
|
|
* args: ['0xA0Cf798816D4b9b9866b5330EEa46a18382f251e'],
|
|
* })
|
|
* // @error: AbiItem.AmbiguityError: Found ambiguous types in overloaded ABI Items.
|
|
* // @error: `bytes20` in `foo(bytes20)`, and
|
|
* // @error: `address` in `foo(address)`
|
|
* // @error: These types encode differently and cannot be distinguished at runtime.
|
|
* // @error: Remove one of the ambiguous items in the ABI.
|
|
* ```
|
|
*/
|
|
export class AmbiguityError extends Errors.BaseError {
|
|
override readonly name = 'AbiItem.AmbiguityError'
|
|
constructor(
|
|
x: { abiItem: Abi.Abi[number]; type: string },
|
|
y: { abiItem: Abi.Abi[number]; type: string },
|
|
) {
|
|
super('Found ambiguous types in overloaded ABI Items.', {
|
|
metaMessages: [
|
|
// TODO: abitype to add support for signature-formatted ABI items.
|
|
`\`${x.type}\` in \`${internal.normalizeSignature(abitype.formatAbiItem(x.abiItem))}\`, and`,
|
|
`\`${y.type}\` in \`${internal.normalizeSignature(abitype.formatAbiItem(y.abiItem))}\``,
|
|
'',
|
|
'These types encode differently and cannot be distinguished at runtime.',
|
|
'Remove one of the ambiguous items in the ABI.',
|
|
],
|
|
})
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Throws when an ABI item is not found in the ABI.
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* // @noErrors
|
|
* import { Abi, AbiFunction } from 'ox'
|
|
*
|
|
* const foo = Abi.from([
|
|
* 'function foo(address)',
|
|
* 'function bar(uint)'
|
|
* ])
|
|
* AbiFunction.fromAbi(foo, 'baz')
|
|
* // @error: AbiItem.NotFoundError: ABI function with name "baz" not found.
|
|
* ```
|
|
*
|
|
* ### Solution
|
|
*
|
|
* Ensure the ABI item exists on the ABI.
|
|
*
|
|
* ```ts twoslash
|
|
* // @noErrors
|
|
* import { Abi, AbiFunction } from 'ox'
|
|
*
|
|
* const foo = Abi.from([
|
|
* 'function foo(address)',
|
|
* 'function bar(uint)',
|
|
* 'function baz(bool)' // [!code ++]
|
|
* ])
|
|
* AbiFunction.fromAbi(foo, 'baz')
|
|
* ```
|
|
*/
|
|
export class NotFoundError extends Errors.BaseError {
|
|
override readonly name = 'AbiItem.NotFoundError'
|
|
constructor({
|
|
name,
|
|
data,
|
|
type = 'item',
|
|
}: {
|
|
name?: string | undefined
|
|
data?: Hex.Hex | undefined
|
|
type?: string | undefined
|
|
}) {
|
|
const selector = (() => {
|
|
if (name) return ` with name "${name}"`
|
|
if (data) return ` with data "${data}"`
|
|
return ''
|
|
})()
|
|
super(`ABI ${type}${selector} not found.`)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Throws when the selector size is invalid.
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* import { Abi, AbiFunction } from 'ox'
|
|
*
|
|
* const foo = Abi.from([
|
|
* 'function foo(address)',
|
|
* 'function bar(uint)'
|
|
* ])
|
|
* AbiFunction.fromAbi(foo, '0xaaa')
|
|
* // @error: AbiItem.InvalidSelectorSizeError: Selector size is invalid. Expected 4 bytes. Received 2 bytes ("0xaaa").
|
|
* ```
|
|
*
|
|
* ### Solution
|
|
*
|
|
* Ensure the selector size is 4 bytes.
|
|
*
|
|
* ```ts twoslash
|
|
* // @noErrors
|
|
* import { Abi, AbiFunction } from 'ox'
|
|
*
|
|
* const foo = Abi.from([
|
|
* 'function foo(address)',
|
|
* 'function bar(uint)'
|
|
* ])
|
|
* AbiFunction.fromAbi(foo, '0x7af82b1a')
|
|
* ```
|
|
*/
|
|
export class InvalidSelectorSizeError extends Errors.BaseError {
|
|
override readonly name = 'AbiItem.InvalidSelectorSizeError'
|
|
constructor({ data }: { data: Hex.Hex }) {
|
|
super(
|
|
`Selector size is invalid. Expected 4 bytes. Received ${Hex.size(data)} bytes ("${data}").`,
|
|
)
|
|
}
|
|
}
|