Overview

@ethernauta/abi

The ABI codec. Encode function calls, decode return values, decode logs, generate TypeScript bindings from contract ABIs.

pnpm add @ethernauta/abi

What ships

  • Codec primitives (address, bool, string_, uint256, bytes32, …) — one per Solidity type.
  • Composersarray(codec), tuple(codecs) for higher-order types.
  • make_codec(fragment) — turn an ABI function fragment into a single encode/decode pair.
  • parse_abi(abi) — parse a raw JSON ABI into the codec-ready shape.
  • decode_logs(events, logs) — match raw logs against event signatures, return typed entries.
  • to_selector(signature) — 4-byte function selector from a Solidity signature string.
  • revert — decode Solidity revert payloads (Error(string), Panic(uint256), custom errors).
  • Code generation via @ethernauta/abi/generator — produce method binding files from an ABI.

Codec primitives

Each Solidity static type has a matching codec:

CodecSolidity
addressaddress
boolbool
string_string (underscore to avoid the JS keyword)
bytesbytes (dynamic)
bytes4, bytes8, bytes32, bytes48, bytes65, bytes256fixed-length bytesN
hash32semantic alias of bytes32 for hashes
uint8, uint16, uint24, uint32, uint40, uint48, uint56, uint64, uint96, uint128, uint160, uint192, uint224, uint256, uintunsigned ints

Each is an AbiCodec<T>:

import { uint256, address } from "@ethernauta/abi";

const encoded = uint256.encode(42n);
const decoded = uint256.decode(encoded);  // → 42n

InferCodec<C> extracts the TS type a codec produces; InferArrayElement<C> gets the element type of an array codec.

Composers

import { array, tuple, uint256, address, bool } from "@ethernauta/abi";

// uint256[]
const uint_array = array(uint256);

// (address, uint256, bool)
const trio = tuple([address, uint256, bool]);

// (address, uint256[])
const mixed = tuple([address, array(uint256)]);

The codec composes the way the type composes. The codec for a function fragment with (address spender, uint256 amount) is tuple([address, uint256]).

make_codec

The high-level builder. Pass an ABI function fragment, get a codec back:

import { make_codec, parse_abi } from "@ethernauta/abi";

const abi = parse_abi([
  "function transfer(address to, uint256 amount) returns (bool)",
]);

const transfer = make_codec(abi[0]);

const calldata = transfer.encode_inputs(["0xabc…", 1000n]);
const result = transfer.decode_outputs(return_bytes);

Used internally by packages/erc/src/*/methods/* to bind every ERC method.

Log decoding

import { decode_logs, parse_abi } from "@ethernauta/abi";

const events = parse_abi([
  "event Transfer(address indexed from, address indexed to, uint256 value)",
]);

const decoded = decode_logs(events, raw_logs);
// → DecodedLogEntry[]
//   each carrying { name, signature, args, log }

EventEntry<T> is the typed shape of one decoded event; DecodedLogEntry is the union across all event signatures the caller passed.

Selectors

import { to_selector } from "@ethernauta/abi";

to_selector("transfer(address,uint256)");   // → "0xa9059cbb"
to_selector("balanceOf(address)");          // → "0x70a08231"

Used by the registry generator (@ethernauta/erc/registry) and by the wallet’s wallet_sendCalls UI to display human-readable method names from selectors.

Revert decoding

import { revert } from "@ethernauta/abi";

// raw revert bytes from eth_call
const reason = revert.decode(raw_bytes);
// →
//   | { kind: "error_string"; message: string }
//   | { kind: "panic"; code: bigint }
//   | { kind: "custom_error"; selector: Bytes4; data: Bytes }
//   | { kind: "raw"; data: Bytes }

Picks Error(string), Panic(uint256), or custom-error / raw fallback. The wallet uses this to surface readable revert reasons in its UI; dapps use it for the same purpose.

Code generation

pnpm dlx @ethernauta/cli abi --in ./erc20.abi.json --out ./generated/

Produces one TypeScript file per ABI method. Each file binds the method into a Callable<T> you can use directly:

// generated/balance-of.ts (auto-generated)
import { make_codec, parse_abi } from "@ethernauta/abi";
import type { Callable } from "@ethernauta/transport";

const codec = make_codec(/* fragment */);

export function balance_of(args: { owner: Address }): Callable<Uint256> {
  return /* curried codec invocation */;
}

@ethernauta/abi/generator exposes the generator primitives (generate, emit_name_for, emit_file_basename_for) if you want to embed codegen in your own tooling.

See also