1.1.0 - 2026-05-01
- Bug fix (upstream #55):
ABI.TypeEncoder.encode_int/2rejected ALLint<N>values (including0) for small bit widths, because the overflow guard mixed up bytes and bits — it comparedbyte_size(significant_bytes)againstdesired_size_bytes - 1, which evaluates to0forint8, raising on every input. Replaced with a numeric range check against2^(N-1)performed up-front, so the encoder accepts the full signed range-2^(N-1)..2^(N-1)-1for everyint<N>. The pre-existing"int overflow raises data overflow"test passed only because the encoder was broken for any value; tightened the test to assert specific in-range values encode AND specific boundary cases (128,-129) raise. - Bug fix:
ABI.FunctionSelector.dynamic?/1raisedFunctionClauseErroron{:array, T, 0}(zero-length fixed array). The grammar acceptsT[0](yrl rule allowsN >= 0), so the type is parseable, but the existing clauses requiredlen > 0and no clause matched the zero case. Addeddef dynamic?({:array, _type, 0}), do: false— a zero-length fixed array has no head/tail layout and no payload, so it is static by any sensible definition. Encoder and decoder paths already handle zero-length arrays (encode_type({:array, _, 0})produces an empty repeated-type tuple;decode_type({:array, _, 0}, data, _opts) -> {[], data}); verified by extendingroundtrip_property_test.exs's fixed-array length domain from1..3to0..3. Pre-existing in upstreamexthereum/abi; not yet filed. ABI.FunctionSelector.@type typenow carries a@typedocclarifying thataddress payablecollapses to:address. Solidity's ABI JSON only emits"address"for both forms, the on-the-wire encoding is identical (20-byte left-padded), and payability is a property ofstate_mutabilityrather than a separate type variant — so consumers shouldn't expect a distinct atom.- Added
test/abi/roundtrip_property_test.exs— property-baseddecode(encode(x)) == xcoverage usingstream_datafor every type inABI.FunctionSelector.@type type/0:uint,int,address,bool,string,bytes,bytesN, fixed and dynamic arrays, and recursively nested tuples (depth ≤ 3). Per-type properties localize failures to a single clause; the recursive composite property exercises nested{:tuple, [{:array, ...}]}shapes where head/tail offsets matter. Surfaced theencode_intbug above on its first run. Test-only dep{:stream_data, "~> 1.1"}added. - Test coverage for the empty-args calldata path (
f()shape — 4-byte selector with zero ABI-encoded args).weth.deposit()(selector0xd0e30db0),rocket_pool.deposit(), and similar zero-arg calls were untested by the round-trip property suite (every generator produced at least one value). Pinned both directions:ABI.encode("deposit()", []) == <<0xD0, 0xE3, 0x0D, 0xB0>>andABI.decode("deposit()", <<>>) == [], plus thefunction: nil/types: []empty-bytes shape. - Test coverage for
FunctionSelectorselector-rendering and parser edge cases:encode/1canonical-signature rendering of{:int, N},{:struct, _, _, _}, dead-via-parse types (:function,{:fixed, M, N},{:ufixed, M, N}) andnil-typed slots (defensive against partially-built typeinfo maps); plusparse_specification/1's%{"indexed" => _}-without-"name"branch (older Solidity versions and hand-written ABIs may omit names on indexed event params). - New API:
ABI.method_id/1returns the 4-byte function selector (keccak256(canonical_signature)[0..3]) for a signature string orFunctionSelectorstruct. Returns<<>>for selectors withfunction: nil. Previously the same logic was private toABI.TypeEncoder; exposing it is a useful primitive for callers that need to compute selectors without encoding args (selector-table routing, log-topic matching, calldata pre-validation). - New API:
ABI.decode_call/3is the symmetric counterpart toABI.encode/2for selector-prefixed calldata. Splits the 4-byte prefix, verifies it matches the expected selector, and decodes the payload via the existingdecode/3machinery. Returns{:ok, decoded}on match or{:error, reason}for:calldata_too_short(< 4 bytes),:selector_mismatch(prefix wrong), or:no_function_name(selector hasfunction: nil, so there's nothing to verify against — caller should usedecode/3with the payload).ABI.decode/3semantics are unchanged: it remains payload-only, matchingeth-abi/ethers/viem/alloyconventions.
1.0.0 - 2026-04-24
First hex.pm release as hieroglyph. This is a maintained fork of exthereum/abi; the module namespace is unchanged (ABI.encode/2, ABI.decode/2, etc. — consumers just swap the hex dep name). Version resets to 1.0.0 under the new package name; the 1.0.0-alpha* / 1.0.0-bravo1 lines below this entry are the upstream's pre-release history, carried forward for context but never published to hex under hieroglyph.
- Published as
hieroglyph— hex package renamed from the internalabiapp name. Top-level moduleABIpreserved (the Solidity term is the correct module name). Repo renamed toZenHive/hieroglyph; upstreamexthereum/abitracked in the package's "Upstream (fork-of)" link. - Bug fix (upstream #53):
ABI.Event.decode_event/4now returns{:indexed_hash, <<32 bytes>>}for indexed parameters of reference type (string,bytes, all arrays — fixed-size or dynamic — and tuples/structs) instead of silently misdecoding the keccak topic as if it were a raw ABI-encoded value. Per the Solidity ABI spec, indexed reference-type values are stored in topics askeccak256(value)and the original is unrecoverable — the tagged tuple preserves the hash (useful for log filtering and equality checks) and makes the "unrecoverable" signal pattern-matchable. This is broader than the ABI head/tail "dynamic" rule:uint256[2]and tuples of all-static members are static for regular ABI encoding but are still hashed in event topics, and this fix handles both. Breaking for callers that consumed the previous garbage bytes directly; static value-type indexed params (uint/int/address/bool/bytesN/function/fixed/ufixed) are unchanged. Regression tests added for indexedstring, indexedbytes, indexed dynamic array, indexed fixed-size static array (uint256[2]), indexed tuple of static members, and mixed static+dynamic+non-indexed cases. - Bug fix (upstream #54):
fixed,ufixed, andfunctiontypes now raiseArgumentErrorat parse time (inABI.Parser.parse!/2, walking nested arrays and tuples) with a link to the tracking issue, instead of parsing silently into unsupported internal terms and later raising the cryptic"Unsupported encoding type"insideTypeEncoder/TypeDecoder. Also filled the{:bytes, pos_integer()}gap inABI.FunctionSelector.@type type— previously omitted even though fully supported by the encoder and decoder. Note: the explicitfixed<M>x<N>/ufixed<M>x<N>forms still raise aFunctionClauseErrorupstream of this walker due to a separate pre-existing lexer-rule-ordering bug (singlextokenizes aslettersinstead of the'x'terminal) — tracked as a follow-up task. Also aligned the grammar's bare-fixed/ bare-ufixedcanonical expansion to the Solidity spec (fixed128x18/ufixed128x18; previouslyx19) so the rejection error message reports the correct form. - Simplified
ABI.Parser.parse!/2's unsupported-type walker to drop a deadis_list(returns)branch — the yecc grammar only emitsnilor a single bare type forreturns, never a list. No behavior change. - Extracted the 32-byte padding logic into
ABI.Math.pad/4andABI.Math.unpad/3.ABI.TypeEncoder.encode_bytes/1,encode_int/2, andencode_uint/2now delegate toABI.Math.pad/4;ABI.TypeDecoder.decode_bytes/3is a thin wrapper aroundABI.Math.unpad/3. No behavior change; resolves the long-standingTODO: add to ABI.Mathcomments in both modules. - Renamed
ABI.FunctionSelector.is_dynamic?/1toABI.FunctionSelector.dynamic?/1to satisfyCredo.Check.Readability.PredicateFunctionNames. The function remains@doc false(internal). No deprecation shim — the old name had@doc falsesince 2017 and zero in-repo references outside three private call-sites, which were updated. - Drove
mix credo --strictto zero violations (was 51). CoversDesign.AliasUsage(top-of-module aliases added acrossABI,ABI.Event,ABI.FunctionSelector,ABI.Parser,ABI.TypeDecoder,ABI.TypeEncoder, andABI.Hex),Readability.MaxLineLength(spec / docstring wraps + the bigEnum.reducetuple-encoder broken into anencode_tuple_element/2helper),Consistency.ParameterPatternMatching(flipped threerecord = %{…}heads to%{…} = record), andRefactor.Nesting(extractedABI.Event.verify_event_signature/2andABI.TypeEncoder.fetch_named_field/2+fetch_by_name/2helpers to drop nesting below 3). - Added regression tests for the map-input encoder path (
ABI.TypeEncoder.data_to_list/2): atom-keyed maps, string-keyed maps, camelCase→snake_case name resolution, string-over-atom key priority, integer values inside nested named-struct maps, and the missing-field / unnamed-type error raises. The map branch previously had zero test coverage; the string-key path was added in commit46accc8, and this suite also exercises integer encoding (a43e9d5) through the map branch. - Added
@spectypespecs and@docstrings for every previously-undeclared public function acrossABI,ABI.Event,ABI.TypeDecoder,ABI.TypeEncoder, andABI.FunctionSelector:ABI.event_signature/1,ABI.parse_specification/1,ABI.TypeDecoder.decode/3,ABI.TypeDecoder.tuple_value/3,ABI.TypeEncoder.encode_raw/2,ABI.Event.decode_event/4/event_signature/1/canonical/2, andABI.FunctionSelector.decode/1/decode_raw/1/parse_specification_item/1/decode_type/1/encode/3; also added docs forTypeDecoder.tuple_value/3andTypeDecoder.decode_bytes/3. Matches the style widened in PR #52. Doctor spec coverage 42% → 88%, doc coverage 88% → 96%. - Added regression tests for eleven previously-uncovered error paths:
boolwith non-boolean values,bytes<N>size mismatches and wrong-datatype values, unsupported type atoms across encoder / decoder / function-selector, int/uint overflow, trailing decode data, anddecode_event/4returns for mismatched event signatures and invalid topic counts. - README refreshed: dropped the stale "tuples with multiple elements don't parse" caveat (false since JSON-ABI support), corrected
ABI.encode/2arity and flippedbytes<M>to supported in the Support checklist, migrated deadsolidity.readthedocs.iolinks todocs.soliditylang.org, and added runnable examples forABI.parse_specification/1,ABI.Event.decode_event/4, and map/struct input toencode/2.
1.0.0-bravo1
- Fix ABI tuple encoding for nested inlined tuples
1.0.0-alpha9
- Add Names to Event Signatures
1.0.0-alpha8
- Add Event Signature check to ABI.Event.decode_event
- Change
decode_eventto return an {:ok, event_name, event_params} tuple. - Add ability to add
"indexed"keyword to ABI canonicals
1.0.0-alpha7
- Bugfix for event decoding with dynamic parameters
1.0.0-alpha6
- Bugfix for is_dynamic
0.1.15
- Properly treat all function encodes as tuple encodings
0.1.14
- Fix 0-length
type[]encoding
0.1.13
- Drop dependency on exth crypto and move in functionality
0.1.12
- Fix
stringdecoding to truncate on encountering NUL - Fix some edge-cases in
tupleencoding/decoding
0.1.11
- Add support for method ID calculation of all standard types
0.1.10
- Fix parsing of function names containing uppercase letters/digits/underscores
- Add support for
bytes<M>
0.1.9
- Add support for parsing ABI specification documents (
.abi.jsonfiles) - Reimplement function signature parsing using a BNF grammar
- Fix potential stack overflow during encoding/decoding
0.1.8
- Fix ordering of elements in tuples
0.1.7
- Fix support for arrays of uint types
0.1.6
- Add public interface to raw function versions.
0.1.5
- Bugfix so that addresses are still left padded.
0.1.4
- Bugfix for tuples to properly handle tail pointer poisition.
0.1.3
- Bugfix for tuples to properly handle head/tail encoding
0.1.2
- Add support for tuples, fixed-length and variable length arrays