1.2.0 - 2026-05-01

Public Surface Pass

  • Public Surface Pass bundle — decode_event/4 error contract narrowed. The decode_event/4 @spec previously declared {:ok, ...} | {:error, term()}, but the runtime path raised on malformed payloads instead of returning {:error, _} — so the typespec was a lie wherever a topic-matched log carried truncated/garbage data. Wrapped the decode_raw-driven payload-decode path in a try/rescue and converted raised exceptions into {:error, {:malformed_data, message}}. verify_event_signature/2 already returned {:error, ...} for signature mismatches but used a string format; tightened to the atom-tagged shape {:error, {:event_signature_mismatch, %{expected: ..., got: ...}}} so callers can pattern-match the failure mode without parsing strings. Added @type ABI.Event.decode_error/0 enumerating the closed error set ({:event_signature_mismatch, _}, {:topics_length_mismatch, _}, {:malformed_data, _}); narrowed ABI.decode_event/4's @spec from {:error, term()} to {:error, Event.decode_error()}. The api() declaration now carries an errors: block mirroring decode_call/3's pattern, so the manifest exposes the closed error set to agent consumers. Bugfix-honoring-typespec — not a breaking change in any sensible sense (the prior contract was unreachable for malformed payloads), but downstream callers that previously caught raises around decode_event/4 should switch to matching {:error, {:malformed_data, _}}.

  • Public Surface Pass bundle — encode_bytes/1 flipped from def to defp. Hygiene-only flip. Already @doc false (lib/abi/typeencoder.ex), zero callers outside type_encoder.ex itself across the local monorepo (cartouche, onchain, `onchain{aave,evm,js,tempo},mpp), and the agent-economy hint-rot test had already excluded it as a deliberate internal helper. Thedefwas a leftover from beforeencode_raw/2became the canonical raw-encoding entrypoint. Removed the now-redundant@doc falseand the explicit exclusion entry fromtest/abi/agent_economy_test.exs(the function is no longer inmodule_info(:exports)`, so the hint-rot cross-check skips it naturally). Manifest user-declared count unchanged (already excluded). ROADMAP's "(breaking, if changed)" wording was overcautious — the codebase already treated this as internal.

  • New API — ABI.decode_error/2. Decodes Solidity 0.8.4+ custom-error revert data: matches the first-4-byte selector in revert_data against a list of known error definitions (signature strings or pre-parsed FunctionSelector structs, mixed accepted) and decodes the payload of whichever matches. Returns {:ok, %{error: name, args: [...]}} on a hit, {:error, :no_match} when no definition's selector matches (or the list is empty), and {:error, :calldata_too_short} on <4 bytes. Mirrors decode_call/3's contract: malformed payload after a selector match still raises (same behavior as decode/3). The first definition with a matching selector wins — definition order is the disambiguation lever. Reuses ABI.method_id/1 (selector computation), ABI.decode/3 (payload decode), ABI.Parser.parse!/2 (signature normalization). api() declaration carries the closed error set under namespace /abi. Solidity 0.8.4+ custom-error revert data is selector-prefixed exactly like calldata, so the implementation is structurally identical to decode_call/3 modulo the multi-definition list.

  • New API — ABI.encode_packed/2. Solidity's non-standard packed encoding — used for Merkle airdrop leaves and keccak256(abi.encodePacked(...)) signature schemes. Per the spec: types <32 bytes concatenate tight (no padding); dynamic types (bytes, string) inline as raw payload (no length prefix); array elements are padded to 32 bytes (or 32-byte multiples for string/bytes) so element boundaries are recoverable; tuples/structs and nested arrays are explicitly unsupported and raise ArgumentError with a spec link. The wrapper accepts the same polymorphic first arg as encode/2 (binary() | FunctionSelector.t()); paren-only signatures like "(uint256,address)" parse as a single-tuple parameter (same shape as "foo((uint256,address))") and therefore raise — pass a struct-arg-free signature like "foo(uint256,address)" for a comma-separated arg list. Cross-checked against the canonical spec example (int16(-1), bytes1(0x42), uint16(0x03), string("Hello, world!")0xffff42000348656c6c6f2c20776f726c6421) and locked as a golden vector; Merkle-leaf golden vector (address ++ uint256 → 52 bytes pre-hash) included for airdrop consumers. Implementation lives next to encode_type/2 in lib/abi/type_encoder.ex with private packed_top/2, packed_array/2, pack_uint/2, pack_int/2 helpers — does NOT thread through Math.pad/4 (which always rounds to 32-byte multiples) since packed mode is the inverse of standard ABI. Inside an array, however, scalar elements DO route through encode_type/2 to reuse the 32-byte rounding correctly per spec.

  • Property Suite Expansion bundle (5 members — original 4 plus a production fix the suite surfaced). Single PR expanding test/abi/roundtrip_property_test.exs plus the resulting bug fix in lib/abi/type_decoder.ex.

    • tuple[] (dynamic array of tuples) round-trip coverage — the existing dispatcher already composes {:array, inner} with {:tuple, ...}, so no new generator clause was needed; what was missing was explicit pin-down. Three new properties: a static-only-element tuple[] (exercises the array-of-static-tuples layout), a mixed-element tuple[] where each element is itself dynamic (the most stress-testing shape — head/tail offsets are computed both per-array AND per-element), and an empty tuple[] unit test. The mixed-element property is what surfaced the string-NUL bug below on its first run.
    • Empty dynamic fields inside structs — explicit fixtures (not properties) for the four pinned shapes that mining surfaced from real protocol calldata: (bytes, string) with empty bytes and non-empty string, (bytes, bytes) both empty, empty tuple[] as the only dynamic field in a struct, and empty tuple[] followed by non-empty bytes. Inline test "..." do blocks rather than the @fixtures pattern from defi_calldata_test.exs — round-trip equality is enough; no need to lock against synthetic byte strings when the property tests already exercise the layout invariants.
    • Multiple top-level struct args — added a roundtrip_args/2 helper alongside the single-arg roundtrip/2. New property mirrors the Balancer V2 swap(SingleSwap, FundManagement, uint256, uint256) shape: two sibling structs of differing dynamic-rate (one mixed static+dynamic, one static-only) plus two scalar args at the ends. Exercises sibling-tuple offset arithmetic when adjacent top-level tuples are dynamic at different rates — roundtrip/2 always wrapped a single arg as [%{type: type}], so this layout was never exercised by the property suite.
    • Deep struct nesting (depth ≥ 4) round-trip — the recursive composite property's type_and_value_gen(3) cap was bumped to depth 5 (Pendle swapExactTokenForPt exercises depth 5 in real calldata). @tag timeout: 120_000 → 300_000 to absorb the larger generation surface; max_runs: 50 (down from the default 100) keeps CI bounded — depth-5 trees can balloon (lists of length 4 of strings up to 64 chars at multiple nesting levels), and 50 deep samples have higher information-density than 100 shallow ones.
    • Bug fix surfaced by the bundle: ABI.TypeDecoder previously called nul_terminate_string/1 on every decoded :string, splitting the binary at the first <<0>> byte and returning only the prefix. This treated Solidity strings as C strings — wrong. Per the Solidity ABI spec, strings are length-prefixed UTF-8 and may legally contain NUL codepoints (U+0000); the length is exact, so right-padded zeros after the data are the only NULs that need stripping (and decode_bytes/3 → Math.unpad/3 already handled that). Removed the nul_terminate_string/1 helper entirely; the :string decode clause now delegates straight to decode_bytes(rest, length, :right). Pre-existing in upstream exthereum/abi since 2018 (commit bdceb719 by Levi Aul) — undetected because random StreamData.string(:utf8, ...) rarely starts with NUL, and most real Solidity strings (function names, error messages, event signatures) don't either. The mixed-element tuple[] property happened to generate a NUL-prefixed string and surfaced it. Three regression unit tests added: leading-NUL string, embedded-NUL string, and all-NULs string. Production paths affected: ABI.decode/3, ABI.decode_call/3, ABI.decode_event/4, ABI.TypeDecoder.decode/3 — anywhere a :string is decoded. Should be filed upstream alongside #53/#54/#55 (or batched with the lexer x sub-bug) — affects every exthereum/abi consumer that decodes user-supplied strings.
  • DeFi Real-World Fixtures bundle. Tests-only addition; no production code touched. Closes both members of the bundle in a single commit.

    • test/abi/defi_calldata_test.exs — 10 round-trip golden vectors captured from defi-skills build --action <name> --json (defi-skills v0.3.0). Fixtures live inline as @fixtures (the originally-proposed test/fixtures/defi_calldata.exs shape was discarded — no .exs data-loading idiom exists in the repo and inline matches the existing test convention). Each fixture asserts both directions: ABI.encode(sig, args) reproduces the locked calldata byte-for-byte and ABI.decode_call(sig, calldata) recovers the original args. Coverage: Aave V3 supply/borrow/setCollateral, Compound V3 supply/claim, Lido stake/unstake (the only fixture exercising uint256[] head/tail layout), EigenLayer deposit, ERC-20 transfer, WETH unwrap. The aave_supply fixture reproduces the calldata locked in the ROADMAP planting note byte-for-byte.
    • test/abi/function_selector_real_world_test.exs — 12 explicit ABI.method_id/1 golden-vector assertions: ERC-20 (transfer, transferFrom), Aave V3 (supply, borrow), WETH/Curve gauge (withdraw(uint256) — duplicate selector by design), WETH/Rocket Pool (deposit() — duplicate by design), Lido (requestWithdrawals(uint256[],address)), Compound V3 (claim), Curve 3pool (add_liquidity(uint256[3],uint256) — fixed-size-array path), Uniswap V3 (exactInputSingle(tuple) — single tuple arg), Balancer V2 (swap — multiple top-level tuples), and EigenLayer (queueWithdrawals(tuple[]) — dynamic tuple[]). A second describe block round-trips the four tuple/tuple[]/fixed-array signatures through FunctionSelector.decode/1 ∘ encode/1 and re-asserts the selector — the 4 corner-case canonical-signature shapes were the entire reason the selector vectors are scoped this widely.
    • Why ship this. Before this bundle, hieroglyph had a single mainnet-style fixture in the entire test suite (the ERC-20 Transfer event doctest in lib/abi.ex). Every encoder/decoder change was verified only against synthetic property-suite shapes. Real-world contract evidence now lives next to the synthetic suite, giving cartouche / onchain CI a stable contract-stability surface across hieroglyph version bumps. The 4 tuple-bearing selectors specifically prove the canonical-signature serialization in function_selector.ex matches what real chains expect.

Agent Economy

  • Agent Economy — Phase 1: Descripex on ABI top-level. Annotated the seven public functions (encode/2, method_id/1, decode/3, decode_call/3, decode_event/4, event_signature/1, parse_specification/1) with api() declarations under namespace /abi. Six of the seven take a polymorphic first arg (binary() | FunctionSelector.t()); the union is described in prose per the established mpp/mcp payment_required_error/1 precedent (one api() block per function name; the formal union lives on @spec). ABI now uses Descripex.Discoverable with a single-module list — Phase 2 expands the list to all six annotated modules. Existing @doc blocks (with their doctests) preserved by ordering: api() first emits its generated @doc, then the manual @doc """...""" overrides slot 4 prose while @doc hints: survives via descripex's __before_compile__ ETS injection. @spec and runtime behaviour unchanged. Manifest emission via mix descripex.manifest --app hieroglyph already works (returns 7 ABI entries plus the four Discoverable bookkeeping exports); the dedicated mix hieroglyph.manifest task lands in Phase 3. Doctor docs/specs coverage held at 100/100, dialyzer 0 warnings, credo --strict 0 issues. New runtime dep: {:descripex, "~> 0.6"} (transitively pulls :json_spec). Version bumped from 1.1.x1.2.0 because adding a runtime dep changes downstream consumers' (cartouche, onchain) dependency closure.

  • Agent Economy — Phase 2: Descripex on the remaining five modules. Annotated all 18 documented public functions across ABI.Event (/selector — 3 fns), ABI.FunctionSelector (/selector — 5 fns; the three @doc false internals dynamic?/1, get_function_type/1, get_state_mutability/1 deliberately excluded), ABI.TypeEncoder (/codec — 2 fns; encode_bytes/1 @doc false excluded), ABI.TypeDecoder (/codec — 4 fns), and ABI.Math (/math — 4 fns). None of these 18 functions are polymorphic, so each api() block follows the standard shape — name, one-sentence description, params: keyword list, returns: map. composes_with: links wired across the natural pairings: Event.decode_event ↔ event_signature, FunctionSelector.decode ↔ encode, TypeEncoder.encode ↔ encode_raw, TypeDecoder.decode ↔ decode_raw. Extended use Descripex.Discoverable, modules: [...] in lib/abi.ex from [ABI] to all six annotated modules. Manifest now emits the full 25 user-declared api() entries (7 + 3 + 5 + 2 + 4 + 4) plus the 4 framework Discoverable exports — verified via mix descripex.manifest --app hieroglyph and per-module entry counts. @doc/doctest preservation pattern from Phase 1 carries through: existing @doc """...""" blocks override slot 4 prose; @doc hints: survives. No behaviour change, no @spec change.
  • Agent Economy — Phase 3: dedicated manifest task + hint-rot validation test. New mix task mix hieroglyph.manifest [path] (defaults to api_manifest.json) emits the JSON manifest using ABI.__descripex_modules__/0 as the single source of truth — direct port of the established mix mpp.manifest shape. Manifest is suitable for downstream cartouche/onchain CI to diff across hieroglyph version bumps as a contract-stability artifact. New test file test/abi/agent_economy_test.exs enforces the agent-discovery surface in three describe blocks plus a load-bearing cross-check: (1) every entry in each module's __api__/0 carries :hints.description; (2) ABI.describe/0..2 returns the expected modules / function lists / per-function detail; (3) namespaces (/abi, /selector, /codec, /math) match per-module assignments via Code.fetch_docs/1. The cross-check walks module_info(:exports), strips Elixir/Descripex framework exports, plus an explicit allow-list of the four @doc false internal helpers (FunctionSelector.dynamic?/1, get_function_type/1, get_state_mutability/1, TypeEncoder.encode_bytes/1), then asserts every remaining export is declared with api() — without this gate, hints rot silently when new defs land without api(), and silent rot here propagates as silent contract drift through cartouche-generated bindings into every onchain_<protocol> package.
  • Bug fix (sub-bug of upstream #54, upstream filing deferred — will be batched into a future combined-bugs issue with PR offer): ABI.FunctionSelector.decode_type("fixed128x18") and decode_type("ufixed256x80") (and the same-shape forms inside arrays/tuples/function signatures) raised a leaky FunctionClauseError instead of the friendly ArgumentError that bare fixed/ufixed already produced. Root cause was lexer rule ordering in src/ethereum_abi_lexer.xrl: the LETTERS rule ([a-zA-Z_]+) was listed before the standalone 'x' terminal, so leex picked LETTERS on equal-length matches and the single x between the two integers tokenized as letters. The grammar rule type -> typename digits 'x' digits never fired, the parser fell through to juxt_type(fixed, 128), and juxt_type/2 had no fixed clause. Fix: introduced dedicated fixed_typename / ufixed_typename terminals (so the 'x' separator is only valid in fixed/ufixed contexts), moved the 'x' rule above {LETTERS}, and extended the parser's identifier_part to also accept 'x', fixed_typename, and ufixed_typename so single-char x and the keyword forms still work as function/argument names. The explicit-M/N forms now route through ABI.Parser.reject_unsupported!/1 and raise the same friendly ArgumentError (with the upstream-#54 link) that the bare forms already produced. Yecc reports 3 shift/reduce conflicts (was 1) — the 2 new ones come from fixed_typename/ufixed_typename being able to start either a type or an identifier_part; yecc's default shift resolution is the desired behavior (fixed128 is a type prefix, not an identifier), and is documented inline in the .yrl Expect 3. comment. Regression tests added: explicit-M/N rejection (fixed128x18, ufixed256x80) plus x-keyword identifier handling (function named x, argument named x, function named fixed/ufixed, and a function name containing x mid-string).

1.1.0 - 2026-05-01

  • Bug fix (upstream #55): ABI.TypeEncoder.encode_int/2 rejected ALL int<N> values (including 0) for small bit widths, because the overflow guard mixed up bytes and bits — it compared byte_size(significant_bytes) against desired_size_bytes - 1, which evaluates to 0 for int8, raising on every input. Replaced with a numeric range check against 2^(N-1) performed up-front, so the encoder accepts the full signed range -2^(N-1)..2^(N-1)-1 for every int<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?/1 raised FunctionClauseError on {:array, T, 0} (zero-length fixed array). The grammar accepts T[0] (yrl rule allows N >= 0), so the type is parseable, but the existing clauses required len > 0 and no clause matched the zero case. Added def 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 extending roundtrip_property_test.exs's fixed-array length domain from 1..3 to 0..3. Pre-existing in upstream exthereum/abi; not yet filed.
  • ABI.FunctionSelector.@type type now carries a @typedoc clarifying that address payable collapses 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 of state_mutability rather than a separate type variant — so consumers shouldn't expect a distinct atom.
  • Added test/abi/roundtrip_property_test.exs — property-based decode(encode(x)) == x coverage using stream_data for every type in ABI.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 the encode_int bug 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() (selector 0xd0e30db0), 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>> and ABI.decode("deposit()", <<>>) == [], plus the function: nil/types: [] empty-bytes shape.
  • Test coverage for FunctionSelector selector-rendering and parser edge cases: encode/1 canonical-signature rendering of {:int, N}, {:struct, _, _, _}, dead-via-parse types (:function, {:fixed, M, N}, {:ufixed, M, N}) and nil-typed slots (defensive against partially-built typeinfo maps); plus parse_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/1 returns the 4-byte function selector (keccak256(canonical_signature)[0..3]) for a signature string or FunctionSelector struct. Returns <<>> for selectors with function: nil. Previously the same logic was private to ABI.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/3 is the symmetric counterpart to ABI.encode/2 for selector-prefixed calldata. Splits the 4-byte prefix, verifies it matches the expected selector, and decodes the payload via the existing decode/3 machinery. Returns {:ok, decoded} on match or {:error, reason} for :calldata_too_short (< 4 bytes), :selector_mismatch (prefix wrong), or :no_function_name (selector has function: nil, so there's nothing to verify against — caller should use decode/3 with the payload). ABI.decode/3 semantics are unchanged: it remains payload-only, matching eth-abi / ethers / viem / alloy conventions.

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 internal abi app name. Top-level module ABI preserved (the Solidity term is the correct module name). Repo renamed to ZenHive/hieroglyph; upstream exthereum/abi tracked in the package's "Upstream (fork-of)" link.
  • Bug fix (upstream #53): ABI.Event.decode_event/4 now 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 as keccak256(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 indexed string, indexed bytes, 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, and function types now raise ArgumentError at parse time (in ABI.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" inside TypeEncoder / TypeDecoder. Also filled the {:bytes, pos_integer()} gap in ABI.FunctionSelector.@type type — previously omitted even though fully supported by the encoder and decoder. Note: the explicit fixed<M>x<N> / ufixed<M>x<N> forms still raise a FunctionClauseError upstream of this walker due to a separate pre-existing lexer-rule-ordering bug (single x tokenizes as letters instead of the 'x' terminal) — tracked as a follow-up task. Also aligned the grammar's bare-fixed / bare-ufixed canonical expansion to the Solidity spec (fixed128x18 / ufixed128x18; previously x19) so the rejection error message reports the correct form.
  • Simplified ABI.Parser.parse!/2's unsupported-type walker to drop a dead is_list(returns) branch — the yecc grammar only emits nil or a single bare type for returns, never a list. No behavior change.
  • Extracted the 32-byte padding logic into ABI.Math.pad/4 and ABI.Math.unpad/3. ABI.TypeEncoder.encode_bytes/1, encode_int/2, and encode_uint/2 now delegate to ABI.Math.pad/4; ABI.TypeDecoder.decode_bytes/3 is a thin wrapper around ABI.Math.unpad/3. No behavior change; resolves the long-standing TODO: add to ABI.Math comments in both modules.
  • Renamed ABI.FunctionSelector.is_dynamic?/1 to ABI.FunctionSelector.dynamic?/1 to satisfy Credo.Check.Readability.PredicateFunctionNames. The function remains @doc false (internal). No deprecation shim — the old name had @doc false since 2017 and zero in-repo references outside three private call-sites, which were updated.
  • Drove mix credo --strict to zero violations (was 51). Covers Design.AliasUsage (top-of-module aliases added across ABI, ABI.Event, ABI.FunctionSelector, ABI.Parser, ABI.TypeDecoder, ABI.TypeEncoder, and ABI.Hex), Readability.MaxLineLength (spec / docstring wraps + the big Enum.reduce tuple-encoder broken into an encode_tuple_element/2 helper), Consistency.ParameterPatternMatching (flipped three record = %{…} heads to %{…} = record), and Refactor.Nesting (extracted ABI.Event.verify_event_signature/2 and ABI.TypeEncoder.fetch_named_field/2 + fetch_by_name/2 helpers 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 commit 46accc8, and this suite also exercises integer encoding (a43e9d5) through the map branch.
  • Added @spec typespecs and @doc strings for every previously-undeclared public function across ABI, ABI.Event, ABI.TypeDecoder, ABI.TypeEncoder, and ABI.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, and ABI.FunctionSelector.decode/1 / decode_raw/1 / parse_specification_item/1 / decode_type/1 / encode/3; also added docs for TypeDecoder.tuple_value/3 and TypeDecoder.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: bool with 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, and decode_event/4 returns 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/2 arity and flipped bytes<M> to supported in the Support checklist, migrated dead solidity.readthedocs.io links to docs.soliditylang.org, and added runnable examples for ABI.parse_specification/1, ABI.Event.decode_event/4, and map/struct input to encode/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_event to 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 string decoding to truncate on encountering NUL
  • Fix some edge-cases in tuple encoding/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.json files)
  • 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