UNPKG

@metamask/abi-utils

Version:

Lightweight utilities for encoding and decoding Solidity ABI

191 lines (190 loc) 9.26 kB
import { BytesLike } from '@metamask/utils'; import { TypeMap } from './types'; /** * Encode the data with the provided types. The types must be valid Solidity * ABI types. * * This will attempt to parse the values into the correct types. For example, * if you pass in a hex string for a `uint256`, it will be parsed into a * `bigint`. Regular strings are interpreted as UTF-8 strings. If you want to * pass in a hex string, you must pass it in as a `Uint8Array`, or use the * "0x"-prefix. * * It will also attempt to infer the types of the values. For example, if you * pass in a string for a `uint256`, it will result in a TypeScript compile-time * error. This does not work for all types, however. For example, if you use * nested arrays or tuples, the type will be inferred as `unknown`. * * The following types are supported: * * - `address`: A 20-byte Ethereum address. * - As a 40-character-long hexadecimal string, starting with "0x". * - As a 20-byte-long byte array, i.e., `Uint8Array`. * - `bool`: A boolean value. * - As a boolean literal, i.e., `true` or `false`. * - As the strings "true" or "false". * - `bytes(n)`: A dynamic byte array. * - As a hexadecimal string, starting with "0x". * - As a byte array, i.e., `Uint8Array`. * - As a regular string, which will be interpreted as UTF-8. * - `function`: A Solidity function. * - As a 48-character-long hexadecimal string, starting with "0x". * - As a 24-byte-long byte array, i.e., `Uint8Array`. * - As a {@link SolidityFunction} object. * - `int(n)`: A signed integer. * - As a number. * - As a `bigint`. * - As a hexadecimal string, starting with "0x". * - `string`: A dynamic UTF-8 string. * - As a regular string. * - As a hexadecimal string, starting with "0x". * - As a byte array, i.e., `Uint8Array`. * - `tuple`: A tuple of values. * - As an array of values. * - `uint(n)`: An unsigned integer. * - As a number. * - As a `bigint`. * - As a hexadecimal string, starting with "0x". * * @example * ```typescript * import { encode, decode } from '@metamask/abi-utils'; * * const types = ['uint256', 'string']; * const encoded = encode(types, [42, 'Hello, world!']); * const decoded = decode(types, encoded); * * console.log(decoded); // [42n, 'Hello, world!'] * ``` * @see https://docs.soliditylang.org/en/v0.8.17/abi-spec.html * @param types - The types to encode. * @param values - The values to encode. This array must have the same length as * the types array. * @param packed - Whether to use the non-standard packed mode. Defaults to * `false`. * @param tight - Whether to pack the values tightly. When enabled, the values * will be packed without any padding. This matches the behaviour of * `ethereumjs-abi`. Defaults to `false`. * @returns The ABI encoded bytes. */ export declare const encode: <Type extends readonly string[]>(types: Type, values: TypeMap<Type, "input">, packed?: boolean, tight?: boolean) => Uint8Array; /** * Encode the data with the provided type. The type must be a valid Solidity * ABI type. * * See {@link encode} for more information on how values are parsed. * * @example * ```typescript * import { encodeSingle, decodeSingle } from '@metamask/abi-utils'; * * const encoded = encodeSingle('uint256', 42); * const decoded = decodeSingle('uint256', encoded); * * console.log(decoded); // 42n * ``` * @see https://docs.soliditylang.org/en/v0.8.17/abi-spec.html#types * @param type - The type to encode. * @param value - The value to encode. * @returns The ABI encoded bytes. */ export declare const encodeSingle: <Type extends string>(type: Type, value: Type extends "string" | "function" | "address" | ("bytes" | `bytes${number}`) | ("int" | `int${number}` | "uint" | `uint${number}`) | `(${string})` | "bool" | "bytes[]" | "int[]" | "uint[]" | "address[]" | "bool[]" | "function[]" | "string[]" | `bytes${number}[]` | `int${number}[]` | `uint${number}[]` | `(${string})[]` | `bytes[${number}]` | `int[${number}]` | `uint[${number}]` | `address[${number}]` | `bool[${number}]` | `function[${number}]` | `string[${number}]` | `bytes${number}[${number}]` | `int${number}[${number}]` | `uint${number}[${number}]` | `(${string})[${number}]` ? import("./types").Type[Type]["input"] : unknown) => Uint8Array; /** * Encode the data with the provided types. The types must be valid Solidity * ABI types. This is similar to {@link encode}, but the values are encoded in * the non-standard packed mode. This differs from the standard encoding in the * following ways: * * - Most values are packed tightly, without alignment padding. * - The exception is array values, which are padded to 32 bytes. * - Values are still padded to their full size, i.e., `uint16` values are still * padded to 2 bytes, regardless of the length of the value. * - The encoding of dynamic types (`bytes`, `string`) is different. The length * of the dynamic type is not included in the encoding, and the dynamic type is * not padded to a multiple of 32 bytes. * - All values are encoded in-place, without any offsets. * * The encoding of this is ambiguous as soon as there is more than one dynamic * type. That means that these values cannot be decoded with {@link decode} or * Solidity's `abi.decode` function. * * See {@link encode} for more information on how values are parsed. * * @example * ```typescript * import { encodePacked } from '@metamask/abi-utils'; * * const encoded = encodePacked(['uint8'], [42]); * * console.log(encoded); // `Uint8Array [ 42 ]` * ``` * @see https://docs.soliditylang.org/en/v0.8.17/abi-spec.html#types * @see https://docs.soliditylang.org/en/v0.8.17/abi-spec.html#non-standard-packed-mode * @param types - The types to encode. * @param values - The values to encode. * @param tight - Whether to pack the values tightly. When enabled, `bytesN` * values in arrays will be packed without any padding. This matches the * behaviour of `ethereumjs-abi`. Defaults to `false`. * @returns The ABI encoded bytes. */ export declare const encodePacked: <Type extends readonly string[]>(types: Type, values: TypeMap<Type, "input">, tight?: boolean) => Uint8Array; /** * Decode an ABI encoded buffer with the specified types. The types must be * valid Solidity ABI types. * * This will attempt to infer the output types from the input types. For * example, if you use `uint256` as an input type, the output type will be * `bigint`. This does not work for all types, however. For example, if you use * nested array types or tuple types, the output type will be `unknown`. * * The resulting types of the values will be as follows: * * | Contract ABI Type | Resulting JavaScript Type | * | ----------------- | ------------------------- | * | `address` | `string` | * | `bool` | `boolean` | * | `bytes(n)` | `Uint8Array` | * | `function` | {@link SolidityFunction} | * | `int(n)` | `bigint` | * | `string` | `string` | * | `tuple` | `Array` | * | `array` | `Array` | * | `uint(n)` | `bigint` | * * @example * ```typescript * import { encode, decode } from '@metamask/abi-utils'; * * const types = ['uint256', 'string']; * const encoded = encode(types, [42, 'Hello, world!']); * const decoded = decode(types, encoded); * * console.log(decoded); // [42n, 'Hello, world!'] * ``` * @see https://docs.soliditylang.org/en/v0.8.17/abi-spec.html#types * @param types - The types to decode the bytes with. * @param value - The bytes-like value to decode. * @returns The decoded values as array. */ export declare const decode: <Type extends readonly string[], Output = TypeMap<Type, "output">>(types: Type, value: BytesLike) => Output; /** * Decode the data with the provided type. The type must be a valid Solidity * ABI type. * * See {@link decode} for more information on how values are parsed. * * @example * ```typescript * import { encodeSingle, decodeSingle } from '@metamask/abi-utils'; * * const encoded = encodeSingle('uint256', 42); * const decoded = decodeSingle('uint256', encoded); * * console.log(decoded); // 42n * ``` * @see https://docs.soliditylang.org/en/v0.8.17/abi-spec.html#types * @param type - The type to decode. * @param value - The bytes-like value to decode. * @returns The decoded value. */ export declare const decodeSingle: <Type extends string>(type: Type, value: BytesLike) => Type extends "string" | "function" | "address" | ("bytes" | `bytes${number}`) | ("int" | `int${number}` | "uint" | `uint${number}`) | `(${string})` | "bool" | "bytes[]" | "int[]" | "uint[]" | "address[]" | "bool[]" | "function[]" | "string[]" | `bytes${number}[]` | `int${number}[]` | `uint${number}[]` | `(${string})[]` | `bytes[${number}]` | `int[${number}]` | `uint[${number}]` | `address[${number}]` | `bool[${number}]` | `function[${number}]` | `string[${number}]` | `bytes${number}[${number}]` | `int${number}[${number}]` | `uint${number}[${number}]` | `(${string})[${number}]` ? import("./types").Type[Type]["output"] : unknown;