@metamask/abi-utils
Version:
Lightweight utilities for encoding and decoding Solidity ABI
191 lines (190 loc) • 9.26 kB
TypeScript
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;