Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .changeset/abi-from-name-selector-split.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
"ox": major
---

Split polymorphic `from` and `fromAbi` on `Abi*` modules into shape-specific helpers and defaulted `Abi.from` to precompute signature `hash`es.

```diff
- AbiFunction.from('function approve(address,uint256)')
+ AbiFunction.fromHumanReadable('function approve(address,uint256)')

- AbiFunction.from({ type: 'function', name: 'approve', /* ... */ })
+ AbiFunction.fromJson({ type: 'function', name: 'approve', /* ... */ })

- AbiFunction.fromAbi(abi, 'approve')
+ AbiFunction.fromAbiName(abi, 'approve')

- AbiFunction.fromAbi(abi, '0x095ea7b3')
+ AbiFunction.fromAbiSelector(abi, '0x095ea7b3')

- Abi.from(abi) // no `hash` precomputed
+ Abi.from(abi) // `hash` precomputed by default
+ Abi.from(abi, { prepare: false }) // opt out of `hash` precomputation
```
61 changes: 33 additions & 28 deletions src/core/Abi.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as abitype from 'abitype'
import * as AbiItem from './AbiItem.js'
import type * as Errors from './Errors.js'
import * as internal from './internal/abi.js'
import type * as AbiItem_internal from './internal/abiItem.js'
Expand Down Expand Up @@ -61,10 +62,14 @@ export function from<const abi extends Abi | readonly string[]>(
(abi extends readonly string[]
? AbiItem_internal.Signatures<abi>
: unknown),
options?: from.Options | undefined,
): from.ReturnType<abi>
/**
* Parses an arbitrary **JSON ABI** or **Human Readable ABI** into a typed {@link ox#Abi.Abi}.
*
* By default, each item is prepared (signature hash precomputed and cached on the item)
* for faster subsequent encode/decode. Opt out with `{ prepare: false }`.
*
* @example
* ### JSON ABIs
*
Expand Down Expand Up @@ -93,18 +98,6 @@ export function from<const abi extends Abi | readonly string[]>(
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
* ```
*
* @example
Expand All @@ -122,31 +115,43 @@ export function from<const abi extends Abi | readonly string[]>(
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
* ```
*
* @param abi - The ABI to parse.
* @param options - Parsing options.
* @returns The typed ABI.
*/
export function from(abi: Abi | readonly string[]): Abi
export function from(
abi: Abi | readonly string[],
options?: from.Options | undefined,
): Abi
// eslint-disable-next-line jsdoc/require-jsdoc
export function from(abi: Abi | readonly string[]): from.ReturnType {
if (internal.isSignatures(abi)) return abitype.parseAbi(abi)
return abi
export function from(
abi: Abi | readonly string[],
options?: from.Options | undefined,
): from.ReturnType {
const { prepare = true } = options ?? {}
const parsed = (
internal.isSignatures(abi) ? abitype.parseAbi(abi) : abi
) as Abi
if (!prepare) return parsed as never
return parsed.map((item) => ({
...item,
hash: AbiItem.getSignatureHash(item as AbiItem.AbiItem),
})) as never
}

export declare namespace from {
type Options = {
/**
* Whether or not to prepare each ABI item (optimization for encoding performance).
* When `true`, the `hash` property is precomputed and attached to every item.
*
* @default true
*/
prepare?: boolean | undefined
}

type ReturnType<
abi extends Abi | readonly string[] | readonly unknown[] = Abi,
> = abi extends readonly string[] ? abitype.ParseAbi<abi> : abi
Expand Down
177 changes: 96 additions & 81 deletions src/core/AbiConstructor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export type AbiConstructor = abitype.AbiConstructor
* ```ts twoslash
* import { AbiConstructor } from 'ox'
*
* const constructor = AbiConstructor.from('constructor(address, uint256)')
* const constructor = AbiConstructor.fromHumanReadable('constructor(address, uint256)')
*
* const bytecode = '0x...'
*
Expand Down Expand Up @@ -126,7 +126,7 @@ export declare namespace decode {
* ```ts twoslash
* import { AbiConstructor } from 'ox'
*
* const constructor = AbiConstructor.from('constructor(address, uint256)')
* const constructor = AbiConstructor.fromHumanReadable('constructor(address, uint256)')
*
* const data = AbiConstructor.encode(constructor, {
* bytecode: '0x...',
Expand Down Expand Up @@ -161,7 +161,7 @@ export declare namespace decode {
* import { AbiConstructor, Hex } from 'ox'
*
* // 1. Instantiate the ABI Constructor.
* const constructor = AbiConstructor.from(
* const constructor = AbiConstructor.fromHumanReadable(
* 'constructor(address owner, uint256 amount)',
* )
*
Expand Down Expand Up @@ -299,134 +299,149 @@ export declare namespace format {
type ErrorType = Errors.GlobalErrorType
}

/** @internal */
export function from<
const abiConstructor extends AbiConstructor | string | readonly string[],
>(
abiConstructor: (abiConstructor | string | readonly string[]) &
(
| (abiConstructor extends string
? internal.Signature<abiConstructor>
: never)
| (abiConstructor extends readonly string[]
? internal.Signatures<abiConstructor>
: never)
| AbiConstructor
),
): from.ReturnType<abiConstructor>
/**
* Parses an arbitrary **JSON ABI Constructor** or **Human Readable ABI Constructor** into a typed {@link ox#AbiConstructor.AbiConstructor}.
* Parses a **Human Readable ABI Constructor** signature (or array of signatures with optional structs) into a typed {@link ox#AbiConstructor.AbiConstructor}.
*
* @example
* ### JSON ABIs
*
* ```ts twoslash
* import { AbiConstructor } from 'ox'
*
* const constructor = AbiConstructor.from({
* inputs: [
* { name: 'owner', type: 'address' },
* ],
* payable: false,
* stateMutability: 'nonpayable',
* type: 'constructor',
* })
* const constructor = AbiConstructor.fromHumanReadable(
* 'constructor(address owner)'
* )
*
* constructor
* //^?
*
*
*
*
*
*
*
*
*
*
*
*
* ```
*
* @example
* ### Human Readable ABIs
*
* A Human Readable ABI can be parsed into a typed ABI object:
* It is possible to specify `struct`s along with your definitions by passing an array:
*
* ```ts twoslash
* import { AbiConstructor } from 'ox'
*
* const constructor = AbiConstructor.from(
* 'constructor(address owner)' // [!code hl]
* )
* const constructor = AbiConstructor.fromHumanReadable([
* 'struct Foo { address owner; uint256 amount; }',
* 'constructor(Foo foo)',
* ])
*
* constructor
* //^?
*
*
*
*
*
*
*
*
*
*
*
*
*
* ```
*
* @example
* It is possible to specify `struct`s along with your definitions:
* @param signature - The human-readable signature (or array of signatures with optional structs) to parse.
* @param options - Parsing options.
* @returns Typed ABI Constructor.
*/
export function fromHumanReadable<
const signature extends string | readonly string[],
>(
signature: signature &
(
| (signature extends string ? internal.Signature<signature> : never)
| (signature extends readonly string[]
? internal.Signatures<signature>
: never)
),
options?: AbiItem.fromHumanReadable.Options,
): fromHumanReadable.ReturnType<signature> {
return AbiItem.fromHumanReadable(signature as never, options) as never
}

export declare namespace fromHumanReadable {
type ReturnType<signature extends string | readonly string[]> =
AbiItem.fromHumanReadable.ReturnType<signature>

type ErrorType = AbiItem.fromHumanReadable.ErrorType | Errors.GlobalErrorType
}

/**
* Parses a **JSON ABI Constructor** into a typed {@link ox#AbiConstructor.AbiConstructor}.
*
* @example
* ```ts twoslash
* import { AbiConstructor } from 'ox'
*
* const constructor = AbiConstructor.from([
* 'struct Foo { address owner; uint256 amount; }', // [!code hl]
* 'constructor(Foo foo)',
* ])
* const constructor = AbiConstructor.fromJson({
* inputs: [
* { name: 'owner', type: 'address' },
* ],
* payable: false,
* stateMutability: 'nonpayable',
* type: 'constructor',
* })
*
* constructor
* //^?
*
*
*
*
*
*
*
*
*
*
*
*
* ```
*
*
*
* @param abiConstructor - The ABI Constructor to parse.
* @param abiConstructor - The JSON ABI Constructor to parse.
* @param options - Parsing options.
* @returns Typed ABI Constructor.
*/
export function from(
abiConstructor: AbiConstructor | string | readonly string[],
): AbiConstructor
/** @internal */
export function from(
abiConstructor: AbiConstructor | string | readonly string[],
): from.ReturnType {
return AbiItem.from(abiConstructor as AbiConstructor)
export function fromJson<const abiConstructor extends AbiConstructor>(
abiConstructor: abiConstructor | AbiConstructor,
options?: AbiItem.fromJson.Options,
): fromJson.ReturnType<abiConstructor> {
return AbiItem.fromJson(abiConstructor as never, options) as never
}

export declare namespace fromJson {
type ReturnType<abiConstructor extends AbiConstructor> =
AbiItem.fromJson.ReturnType<abiConstructor>

type ErrorType = AbiItem.fromJson.ErrorType | Errors.GlobalErrorType
}

/**
* Internal dispatcher used by `decode` / `encode` shorthand overloads.
* Picks {@link AbiConstructor.fromHumanReadable} or {@link AbiConstructor.fromJson}
* based on the input shape.
*
* @internal
*/
export function from<
const abiConstructor extends AbiConstructor | string | readonly string[],
>(
abiConstructor: (
| abiConstructor
| AbiConstructor
| string
| readonly string[]
) &
(
| (abiConstructor extends string
? internal.Signature<abiConstructor>
: never)
| (abiConstructor extends readonly string[]
? internal.Signatures<abiConstructor>
: never)
| AbiConstructor
),
options?: AbiItem.from.Options,
): from.ReturnType<abiConstructor> {
return AbiItem.from(abiConstructor as never, options) as never
}

export declare namespace from {
/** @internal */
type ReturnType<
abiConstructor extends
| AbiConstructor
| string
| readonly string[] = AbiConstructor,
> = AbiItem.from.ReturnType<abiConstructor>

/** @internal */
type ErrorType = AbiItem.from.ErrorType | Errors.GlobalErrorType
}

Expand Down Expand Up @@ -489,7 +504,7 @@ export declare namespace fromAbi {
* import { AbiConstructor } from 'ox'
*
* AbiConstructor.decode(
* AbiConstructor.from('constructor(address)'),
* AbiConstructor.fromHumanReadable('constructor(address)'),
* { bytecode: '0x6080...', data: '0xdeadbeef' },
* )
* // @error: AbiConstructor.BytecodeMismatchError: Provided `data` does not start with the provided `bytecode`.
Expand Down
Loading
Loading