Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

ABI Utilities

Function Selectors

Compute the 4-byte function selector for any function signature.

import sh.brane.core.abi.Abi;
import sh.brane.core.types.HexData;
 
// "transfer(address,uint256)" -> 0xa9059cbb
HexData selector = Abi.functionSelector("transfer(address,uint256)");

Event Topics

Compute the 32-byte topic hash for an event signature.

import sh.brane.core.abi.Abi;
import sh.brane.core.types.Hash;
 
// "Transfer(address,address,uint256)" -> 0xddf252...
Hash topic = Abi.eventTopic("Transfer(address,address,uint256)");

Decoding Events

Decode raw logs into Java objects using Abi.decodeEvents.

import sh.brane.rpc.Brane;
import sh.brane.rpc.LogFilter;
import sh.brane.core.abi.Abi;
 
Brane client = Brane.connect("https://eth-mainnet.example.com");
LogFilter filter = ...;
Abi abi = ...;
 
// 1. Define the Event Class
public static class TransferEvent {
    public Address from;
    public Address to;
    public BigInteger value;
}
 
// 2. Fetch Logs
var logs = client.getLogs(filter);
 
// 3. Decode
var transfers = abi.decodeEvents("Transfer", logs, TransferEvent.class);
 
for (var event : transfers) {
    System.out.println("Transfer: " + event.value);
}

FastAbiEncoder

For low-level encoding of arguments (useful for manual eth_call construction).

import sh.brane.core.abi.FastAbiEncoder;
import sh.brane.core.abi.UInt;
 
byte[] encoded = FastAbiEncoder.encode(List.of(
    new UInt(256, BigInteger.TEN)
));

Zero-Allocation Encoding

For hot paths, use encodeTo() with a pre-allocated buffer:

import java.nio.ByteBuffer;
 
// Pre-allocate once
ByteBuffer buffer = ByteBuffer.allocate(68);  // 4 selector + 32*2 args
 
// Encode directly to buffer (0 allocations)
byte[] selector = Abi.functionSelector("transfer(address,uint256)").toBytes();
FastAbiEncoder.encodeTo(selector, args, buffer);
 
// For primitive uint256 values (avoids BigInteger boxing)
FastAbiEncoder.encodeUint256(42L, buffer);

See Performance > Allocation-Conscious APIs for more details.

Array Type

The Array<T> record represents Solidity arrays (both static T[N] and dynamic T[]).

Creating Arrays

Arrays require an elementTypeName parameter for correct ABI encoding:

import sh.brane.core.abi.Array;
import sh.brane.core.abi.UInt;
 
// Dynamic array: uint256[]
Array<UInt> dynamicArray = new Array<>(
    List.of(new UInt(256, BigInteger.ONE), new UInt(256, BigInteger.TWO)),
    UInt.class,
    true,       // isDynamicLength
    "uint256"   // elementTypeName
);
 
// Static array: address[3]
Array<sh.brane.core.abi.Address> staticArray = new Array<>(
    List.of(addr1, addr2, addr3),
    sh.brane.core.abi.Address.class,
    false,      // isDynamicLength (fixed size)
    "address"   // elementTypeName
);

Why elementTypeName is Required

Java's type erasure prevents inferring the Solidity type at runtime. The elementTypeName ensures correct type signatures for:

  • Function selectors: transfer(address,uint256[]) vs transfer(address,uint128[])
  • Event topics: Correct hashing of event signatures
  • Empty arrays: No elements to inspect for type inference
ParameterDescription
valuesList of elements (may be empty)
typeJava class of elements (e.g., UInt.class)
isDynamicLengthtrue for T[], false for T[N]
elementTypeNameSolidity type name (e.g., "uint256", "address")

Array Methods

Array<UInt> arr = new Array<>(values, UInt.class, true, "uint256");
 
arr.typeName();      // "uint256[]" or "uint256[3]"
arr.isDynamic();     // true if dynamic length or element is dynamic
arr.byteSize();      // ABI-encoded byte size