Skip to main content
Skill — Copyable reference implementation. Use as-is or customize. See Skills Philosophy.

Ethers-style Interface

A copyable reference implementation that follows ethers v6 Interface patterns, built on Voltaire primitives.

Overview

This Skill provides:
  • Interface: Main ABI abstraction with ethers v6 API
  • Fragment classes: FunctionFragment, EventFragment, ErrorFragment
  • Encoding/Decoding: Function data, event logs, error results
  • Parsing: Parse transactions, logs, and errors from raw data

Quick Start

import { Interface } from './examples/ethers-interface/index.js';

const abi = [
  {
    type: 'function',
    name: 'transfer',
    inputs: [
      { type: 'address', name: 'to' },
      { type: 'uint256', name: 'amount' },
    ],
    outputs: [{ type: 'bool', name: '' }],
    stateMutability: 'nonpayable',
  },
  {
    type: 'event',
    name: 'Transfer',
    inputs: [
      { type: 'address', name: 'from', indexed: true },
      { type: 'address', name: 'to', indexed: true },
      { type: 'uint256', name: 'value', indexed: false },
    ],
  },
  {
    type: 'error',
    name: 'InsufficientBalance',
    inputs: [
      { type: 'uint256', name: 'available' },
      { type: 'uint256', name: 'required' },
    ],
  },
];

const iface = new Interface(abi);

Encoding Function Calls

Encode function call data for transaction data field:
// Encode transfer(address,uint256)
const data = iface.encodeFunctionData('transfer', [
  '0x742d35Cc6634C0532925a3b844Bc9e7595f251e3',
  1000000n,
]);
// 0xa9059cbb000000000000000000000000742d35cc6634c0532925a3b844bc9e7595f251e3...

// Get function by selector
const fn = iface.getFunction('0xa9059cbb');
console.log(fn?.name); // "transfer"

Decoding Function Data

Decode function call data back to arguments:
// Decode transaction data
const decoded = iface.decodeFunctionData('transfer', data);
console.log(decoded[0]); // "0x742d35cc6634c0532925a3b844bc9e7595f251e3"
console.log(decoded[1]); // 1000000n

// Decode function result (return values)
const result = iface.decodeFunctionResult('transfer', returnData);
console.log(result[0]); // true

Event Encoding/Decoding

Encode event logs for testing or mocking:
// Encode event log
const { data, topics } = iface.encodeEventLog('Transfer', [
  '0x742d35Cc6634C0532925a3b844Bc9e7595f251e3',
  '0x1234567890123456789012345678901234567890',
  1000n,
]);

// Decode event log from raw data
const decoded = iface.decodeEventLog('Transfer', data, topics);
console.log(decoded[0]); // from address
console.log(decoded[1]); // to address
console.log(decoded[2]); // 1000n

Error Encoding/Decoding

Handle custom error reverts:
// Encode error for testing
const errorData = iface.encodeErrorResult('InsufficientBalance', [100n, 200n]);

// Decode error from revert data
const decoded = iface.decodeErrorResult('InsufficientBalance', errorData);
console.log(decoded[0]); // 100n (available)
console.log(decoded[1]); // 200n (required)

// Built-in Error and Panic
const standardError = iface.encodeErrorResult('Error', ['Something went wrong']);
const panicError = iface.encodeErrorResult('Panic', [0x11n]); // Arithmetic overflow

Parsing Raw Data

Parse unknown data to discover its type:
// Parse transaction
const txDescription = iface.parseTransaction({ data, value: 0n });
if (txDescription) {
  console.log(txDescription.name);      // "transfer"
  console.log(txDescription.args);      // [address, amount]
  console.log(txDescription.selector);  // "0xa9059cbb"
}

// Parse log
const logDescription = iface.parseLog({ topics, data });
if (logDescription) {
  console.log(logDescription.name);     // "Transfer"
  console.log(logDescription.args);     // [from, to, value]
  console.log(logDescription.topic);    // topic hash
}

// Parse error
const errorDescription = iface.parseError(revertData);
if (errorDescription) {
  console.log(errorDescription.name);   // "InsufficientBalance"
  console.log(errorDescription.args);   // [available, required]
}

Fragment Inspection

Inspect ABI fragments:
// Get all functions
iface.forEachFunction((fn, index) => {
  console.log(fn.name, fn.selector, fn.stateMutability);
});

// Get all events
iface.forEachEvent((ev, index) => {
  console.log(ev.name, ev.topicHash, ev.anonymous);
});

// Check existence
console.log(iface.hasFunction('transfer')); // true
console.log(iface.hasEvent('Transfer'));    // true

// Get by name or selector
const fn = iface.getFunction('transfer');
const ev = iface.getEvent('Transfer');
const err = iface.getError('InsufficientBalance');

Human-Readable Format

Convert ABI to human-readable format:
// Full format with names
const full = iface.format(false);
// ["function transfer(address to, uint256 amount) returns (bool)", ...]

// Minimal format without names
const minimal = iface.format(true);
// ["function transfer(address,uint256) returns (bool)", ...]

// JSON format
const json = iface.formatJson();
// '[{"type":"function","name":"transfer",...}]'

Constructor Encoding

Encode constructor arguments for deployment:
const constructorAbi = [
  {
    type: 'constructor',
    inputs: [
      { type: 'string', name: 'name' },
      { type: 'string', name: 'symbol' },
    ],
  },
];

const iface = new Interface(constructorAbi);
const deployData = iface.encodeDeploy(['My Token', 'MTK']);
// Append to bytecode for deployment

Filter Topics

Create filter topics for eth_getLogs:
// Filter for specific sender
const topics = iface.encodeFilterTopics('Transfer', [
  '0x742d35Cc6634C0532925a3b844Bc9e7595f251e3',
  null, // any recipient
]);

// Filter for multiple possible values
const multiTopics = iface.encodeFilterTopics('Transfer', [
  ['0xaddr1', '0xaddr2'], // from either address
  null,
]);

Fragment Classes

Work with fragments directly:
import {
  FunctionFragment,
  EventFragment,
  ErrorFragment,
  ParamType
} from './examples/ethers-interface/index.js';

// Create fragments from ABI items
const fn = FunctionFragment.from({
  type: 'function',
  name: 'test',
  inputs: [{ type: 'uint256', name: 'x' }],
  outputs: [{ type: 'bool' }],
  stateMutability: 'view',
});

console.log(fn.selector);       // "0x29e99f07"
console.log(fn.format());       // "test(uint256)"
console.log(fn.constant);       // true (view function)

// Compute selectors directly
const selector = FunctionFragment.getSelector('transfer', ['address', 'uint256']);
const topicHash = EventFragment.getTopicHash('Transfer', ['address', 'address', 'uint256']);

ParamType

Inspect parameter types:
const param = ParamType.from({
  type: 'tuple',
  name: 'data',
  components: [
    { type: 'uint256', name: 'id' },
    { type: 'string', name: 'name' },
  ],
});

console.log(param.type);        // "tuple"
console.log(param.isTuple());   // true
console.log(param.isArray());   // false
console.log(param.components);  // [ParamType, ParamType]

// Format in different modes
console.log(param.format('sighash')); // "(uint256,string)"
console.log(param.format('full'));    // "(uint256 id, string name) data"

Source Code

Full implementation available at:
  • examples/ethers-interface/Interface.js - Main Interface class
  • examples/ethers-interface/Fragment.js - Fragment classes
  • examples/ethers-interface/errors.ts - Error classes
  • examples/ethers-interface/EthersInterfaceTypes.ts - TypeScript types
  • examples/ethers-interface/EthersInterface.test.ts - Comprehensive tests

API Reference

Interface

MethodDescription
encodeFunctionData(name, values?)Encode function call data
decodeFunctionData(name, data)Decode function call params
decodeFunctionResult(name, data)Decode function return data
encodeFunctionResult(name, values?)Encode function return data
encodeEventLog(name, values)Encode event log
decodeEventLog(name, data, topics?)Decode event log
encodeFilterTopics(name, values)Encode filter topics
encodeErrorResult(name, values?)Encode error data
decodeErrorResult(name, data)Decode error data
encodeDeploy(values?)Encode constructor args
parseTransaction(tx)Parse transaction data
parseLog(log)Parse event log
parseError(data)Parse error data
getFunction(key, values?)Get function fragment
getEvent(key, values?)Get event fragment
getError(key, values?)Get error fragment
forEachFunction(cb)Iterate functions
forEachEvent(cb)Iterate events
forEachError(cb)Iterate errors
format(minimal?)Human-readable ABI
formatJson()JSON ABI string

FunctionFragment

PropertyDescription
nameFunction name
selector4-byte selector (hex)
inputsInput parameters
outputsOutput parameters
stateMutabilitypure/view/nonpayable/payable
constantIs view/pure
payableCan receive ether

EventFragment

PropertyDescription
nameEvent name
topicHash32-byte topic hash
inputsEvent parameters
anonymousIs anonymous event

ErrorFragment

PropertyDescription
nameError name
selector4-byte selector
inputsError parameters