UNPKG

ox

Version:

Ethereum Standard Library

478 lines (456 loc) 12.3 kB
import * as abitype from 'abitype' import type * as Abi from './Abi.js' import * as AbiItem from './AbiItem.js' import * as AbiParameters from './AbiParameters.js' import type * as Errors from './Errors.js' import * as Hex from './Hex.js' import type * as internal from './internal/abiConstructor.js' import type { IsNarrowable } from './internal/types.js' /** Root type for an {@link ox#AbiItem.AbiItem} with a `constructor` type. */ export type AbiConstructor = abitype.AbiConstructor /** * ABI-decodes the provided constructor input (`inputs`). * * @example * ```ts twoslash * import { AbiConstructor } from 'ox' * * const constructor = AbiConstructor.from('constructor(address, uint256)') * * const bytecode = '0x...' * * const data = AbiConstructor.encode(constructor, { * bytecode, * args: ['0xd8da6bf26964af9d7eed9e03e53415d37aa96045', 123n], * }) * * const decoded = AbiConstructor.decode(constructor, { // [!code focus] * bytecode, // [!code focus] * data, // [!code focus] * }) // [!code focus] * ``` * * @example * ### ABI-shorthand * * You can also specify an entire ABI object as a parameter to `AbiConstructor.decode`. * * ```ts twoslash * // @noErrors * import { Abi, AbiConstructor } from 'ox' * * const abi = Abi.from([...]) * * const data = AbiConstructor.encode(abi, { * bytecode: '0x...', * args: ['0xd8da6bf26964af9d7eed9e03e53415d37aa96045', 123n], * }) * * const decoded = AbiConstructor.decode(abi, { // [!code focus] * bytecode: '0x...', // [!code focus] * data, // [!code focus] * }) // [!code focus] * ``` * * @param abiConstructor - The ABI Constructor to decode. * @param options - Decoding options. * @returns The decoded constructor inputs. */ export function decode< const abi extends Abi.Abi | readonly unknown[], abiConstructor extends AbiConstructor = fromAbi.ReturnType<abi> extends AbiConstructor ? fromAbi.ReturnType<abi> : never, >( abi: abi | Abi.Abi | readonly unknown[], options: decode.Options, ): decode.ReturnType<abiConstructor> export function decode<const abiConstructor extends AbiConstructor>( abiConstructor: abiConstructor | AbiConstructor, options: decode.Options, ): decode.ReturnType<abiConstructor> // eslint-disable-next-line jsdoc/require-jsdoc export function decode( ...parameters: | [abi: Abi.Abi | readonly unknown[], options: decode.Options] | [abiConstructor: AbiConstructor, options: decode.Options] ): decode.ReturnType { const [abiConstructor, options] = (() => { if (Array.isArray(parameters[0])) { const [abi, options] = parameters as [ Abi.Abi | readonly unknown[], decode.Options, ] return [fromAbi(abi), options] as [AbiConstructor, decode.Options] } return parameters as [AbiConstructor, decode.Options] })() const { bytecode } = options if (abiConstructor.inputs?.length === 0) return undefined const data = options.data.replace(bytecode, '0x') as Hex.Hex return AbiParameters.decode(abiConstructor.inputs, data) } export declare namespace decode { interface Options { /** The bytecode of the contract. */ bytecode: Hex.Hex /** The encoded constructor. */ data: Hex.Hex } type ReturnType<abiConstructor extends AbiConstructor = AbiConstructor> = | (abiConstructor['inputs']['length'] extends 0 ? undefined : abitype.AbiParametersToPrimitiveTypes<abiConstructor['inputs']>) | (IsNarrowable<abiConstructor, AbiConstructor> extends true ? never : undefined) type ErrorType = Errors.GlobalErrorType } /** * ABI-encodes the provided constructor input (`inputs`). * * @example * ```ts twoslash * import { AbiConstructor } from 'ox' * * const constructor = AbiConstructor.from('constructor(address, uint256)') * * const data = AbiConstructor.encode(constructor, { * bytecode: '0x...', * args: ['0xd8da6bf26964af9d7eed9e03e53415d37aa96045', 123n], * }) * ``` * * @example * ### ABI-shorthand * * You can also specify an entire ABI object as a parameter to `AbiConstructor.encode`. * * ```ts twoslash * // @noErrors * import { Abi, AbiConstructor } from 'ox' * * const abi = Abi.from([...]) * * const data = AbiConstructor.encode(abi, { // [!code focus] * bytecode: '0x...', // [!code focus] * args: ['0xd8da6bf26964af9d7eed9e03e53415d37aa96045', 123n], // [!code focus] * }) // [!code focus] * ``` * * @example * ### End-to-end * * Below is an end-to-end example of using `AbiConstructor.encode` to encode the constructor of a contract and deploy it. * * ```ts twoslash * import 'ox/window' * import { AbiConstructor, Hex } from 'ox' * * // 1. Instantiate the ABI Constructor. * const constructor = AbiConstructor.from( * 'constructor(address owner, uint256 amount)', * ) * * // 2. Encode the ABI Constructor. * const data = AbiConstructor.encode(constructor, { * bytecode: '0x...', * args: ['0xd8da6bf26964af9d7eed9e03e53415d37aa96045', 123n], * }) * * // 3. Deploy the contract. * const hash = await window.ethereum!.request({ * method: 'eth_sendTransaction', * params: [{ data }], * }) * ``` * * :::note * * For simplicity, the above example uses `window.ethereum.request`, but you can use any * type of JSON-RPC interface. * * ::: * * @param abiConstructor - The ABI Constructor to encode. * @param options - Encoding options. * @returns The encoded constructor. */ export function encode< const abi extends Abi.Abi | readonly unknown[], abiConstructor extends AbiConstructor = fromAbi.ReturnType<abi> extends AbiConstructor ? fromAbi.ReturnType<abi> : never, >( abi: abi | Abi.Abi | readonly unknown[], options: encode.Options<abiConstructor>, ): encode.ReturnType export function encode<const abiConstructor extends AbiConstructor>( abiConstructor: abiConstructor | AbiConstructor, options: encode.Options<abiConstructor>, ): encode.ReturnType // eslint-disable-next-line jsdoc/require-jsdoc export function encode( ...parameters: | [abi: Abi.Abi | readonly unknown[], options: encode.Options] | [abiConstructor: AbiConstructor, options: encode.Options] ): encode.ReturnType { const [abiConstructor, options] = (() => { if (Array.isArray(parameters[0])) { const [abi, options] = parameters as [ Abi.Abi | readonly unknown[], encode.Options, ] return [fromAbi(abi), options] as [AbiConstructor, encode.Options] } return parameters as [AbiConstructor, encode.Options] })() const { bytecode, args } = options return Hex.concat( bytecode, abiConstructor.inputs?.length && args?.length ? AbiParameters.encode(abiConstructor.inputs, args as readonly unknown[]) : '0x', ) } export declare namespace encode { type Options< abiConstructor extends AbiConstructor = AbiConstructor, /// args extends abitype.AbiParametersToPrimitiveTypes< abiConstructor['inputs'] > = abitype.AbiParametersToPrimitiveTypes<abiConstructor['inputs']>, > = { /** The bytecode of the contract. */ bytecode: Hex.Hex /** The constructor arguments to encode. */ args?: args | undefined } & (readonly [] extends args ? {} : { /** The constructor arguments to encode. */ args: args }) type ReturnType = Hex.Hex type ErrorType = | Hex.concat.ErrorType | AbiParameters.encode.ErrorType | Errors.GlobalErrorType } /** @internal */ export function format<const abiConstructor extends AbiConstructor>( abiConstructor: abiConstructor, ): format.ReturnType<abiConstructor> /** * Formats an {@link ox#AbiConstructor.AbiConstructor} into a **Human Readable ABI Function**. * * @example * ```ts twoslash * import { AbiConstructor } from 'ox' * * const formatted = AbiConstructor.format({ * inputs: [ * { name: 'owner', type: 'address' }, * ], * payable: false, * stateMutability: 'nonpayable', * type: 'constructor', * }) * * formatted * // ^? * * * ``` * * @param abiConstructor - The ABI Constructor to format. * @returns The formatted ABI Constructor. */ export function format(abiConstructor: AbiConstructor): string /** @internal */ export function format(abiConstructor: AbiConstructor): format.ReturnType { return abitype.formatAbiItem(abiConstructor) } export declare namespace format { type ReturnType<abiConstructor extends AbiConstructor = AbiConstructor> = abitype.FormatAbiItem<abiConstructor> 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}. * * @example * ### JSON ABIs * * ```ts twoslash * import { AbiConstructor } from 'ox' * * const constructor = AbiConstructor.from({ * inputs: [ * { name: 'owner', type: 'address' }, * ], * payable: false, * stateMutability: 'nonpayable', * type: 'constructor', * }) * * constructor * //^? * * * * * * * * * * * * * ``` * * @example * ### Human Readable ABIs * * A Human Readable ABI can be parsed into a typed ABI object: * * ```ts twoslash * import { AbiConstructor } from 'ox' * * const constructor = AbiConstructor.from( * 'constructor(address owner)' // [!code hl] * ) * * constructor * //^? * * * * * * * * * * * * * * ``` * * @example * It is possible to specify `struct`s along with your definitions: * * ```ts twoslash * import { AbiConstructor } from 'ox' * * const constructor = AbiConstructor.from([ * 'struct Foo { address owner; uint256 amount; }', // [!code hl] * 'constructor(Foo foo)', * ]) * * constructor * //^? * * * * * * * * * * * * * ``` * * * * @param abiConstructor - The ABI Constructor to parse. * @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 declare namespace from { type ReturnType< abiConstructor extends | AbiConstructor | string | readonly string[] = AbiConstructor, > = AbiItem.from.ReturnType<abiConstructor> type ErrorType = AbiItem.from.ErrorType | Errors.GlobalErrorType } /** @internal */ export function fromAbi<const abi extends Abi.Abi | readonly unknown[]>( abi: abi | Abi.Abi | readonly unknown[], ): fromAbi.ReturnType<abi> /** * Extracts an {@link ox#AbiConstructor.AbiConstructor} from an {@link ox#Abi.Abi} given a name and optional arguments. * * @example * ### Extracting by Name * * ABI Events can be extracted by their name using the `name` option: * * ```ts twoslash * import { Abi, AbiConstructor } from 'ox' * * const abi = Abi.from([ * 'constructor(address owner)', * 'function foo()', * 'event Transfer(address owner, address to, uint256 tokenId)', * 'function bar(string a) returns (uint256 x)', * ]) * * const item = AbiConstructor.fromAbi(abi) // [!code focus] * // ^? * * * * * * * ``` * * @returns The ABI constructor. */ export function fromAbi(abi: Abi.Abi | readonly unknown[]): AbiConstructor /** @internal */ export function fromAbi(abi: Abi.Abi | readonly unknown[]): fromAbi.ReturnType { const item = (abi as Abi.Abi).find((item) => item.type === 'constructor') if (!item) throw new AbiItem.NotFoundError({ name: 'constructor' }) return item } export declare namespace fromAbi { type ReturnType<abi extends Abi.Abi | readonly unknown[] = Abi.Abi> = Extract< abi[number], { type: 'constructor' } > type ErrorType = AbiItem.NotFoundError | Errors.GlobalErrorType }