Skip to content

polish: Elixir 1.19 compat, typespec accuracy, and small fixes#52

Open
e-fu wants to merge 2 commits intoexthereum:mainfrom
ZenHive:polish-and-elixir-1.19-compat
Open

polish: Elixir 1.19 compat, typespec accuracy, and small fixes#52
e-fu wants to merge 2 commits intoexthereum:mainfrom
ZenHive:polish-and-elixir-1.19-compat

Conversation

@e-fu
Copy link
Copy Markdown

@e-fu e-fu commented Apr 24, 2026

Small polish pass on the library surface — five topics bundled as one PR. Happy to split if you'd prefer.

1. Fix opts silently dropped in ABI.decode/3 with string signatures

The string-signature clause re-dispatched through decode(selector, data), losing opts. Options like decode_structs: true never reached TypeDecoder.decode_raw unless the caller had already parsed the selector themselves:

# Before: opts silently dropped
ABI.decode("foo((uint256,address))", data, decode_structs: true)

# After: opts flow through both arms

Standard header + default pattern; non-breaking for existing decode/2 callers.

2. Elixir 1.19+ compat: pin variables in binary-pattern sizes

Elixir 1.19 deprecates unpinned variables in bitstring pattern sizes. TypeDecoder now uses ^var:

<<value::integer-size(^total_bit_size), rest::binary>> = data

Compiles clean on both 1.18 and 1.19.

3. Typespec accuracy

  • ABI.FunctionSelector.t.function is String.t() | nil — raw-tuple selectors have nil.
  • ABI.FunctionSelector.t.returns is type() | [argument_type()] | nil — the constructor sets it to a list, and nil is valid.
  • ABI.TypeDecoder drops the unused decode_options() map typespec and switches specs to keyword(), matching the actual call convention (decode_raw(data, types, opts \\ [])).
  • @spec added on public entry points: ABI.encode/2, ABI.decode/3, ABI.decode_event/4, ABI.TypeDecoder.decode_raw/3, ABI.TypeEncoder.encode/2.

4. Grammar cleanup in src/ethereum_abi_parser.yrl

  • Drop unused 'expecting identifier' terminal.
  • Declare Expect 1. with a comment — the structural shift/reduce conflict between dispatch rules (a bare tuple vs. a selector starting with a typespec that is itself a tuple) is not a bug, and this silences the compile-time warning.

5. @moduledoc for ABI.Event

One paragraph summarising what the module does.


All 98 existing tests pass.

Copilot AI review requested due to automatic review settings April 24, 2026 06:36
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Polish pass across the Elixir ABI library to improve Elixir 1.19 compatibility, tighten typespecs, and clean up small correctness/documentation issues in decoding/encoding and parsing.

Changes:

  • Fix ABI.decode/3 so opts are forwarded when called with string signatures (restoring options like decode_structs: true).
  • Update ABI.TypeDecoder bitstring patterns to pin size variables for Elixir 1.19+, and adjust several public typespecs/specs.
  • Parser grammar cleanup (.yrl) + add @moduledoc for ABI.Event.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/ethereum_abi_parser.yrl Removes an unused terminal and documents/declares the expected shift/reduce conflict to silence warnings.
lib/abi/type_encoder.ex Adds/updates specs and applies formatting-only refactors; no behavioral changes observed in the diff.
lib/abi/type_decoder.ex Switches opts types from map typespec to keyword(), and pins bitstring size vars for Elixir 1.19 compatibility.
lib/abi/function_selector.ex Removes unused require Integer and corrects selector struct typespec fields (function, returns, types).
lib/abi/event.ex Adds module documentation and refactors signature-check logic for clarity (same behavior).
lib/abi.ex Adds specs to public entry points and fixes decode/3 to pass opts through for string signatures.
Comments suppressed due to low confidence (2)

lib/abi.ex:114

  • The new @spec for decode/3 says it always returns a list, but this function can return a map when decode_structs: true and the selector includes named tuple elements (because ABI.TypeDecoder.tuple_value/3 returns a map and this code returns it unchanged when it’s not a tuple). Please widen the return typespec (or adjust the function to always return a list) so the spec matches actual behavior.
  @spec decode(binary() | ABI.FunctionSelector.t(), binary(), keyword()) :: [any()]
  def decode(function_signature, data, opts \\ [])

  def decode(function_signature, data, opts) when is_binary(function_signature) do
    decode(ABI.FunctionSelector.decode(function_signature), data, opts)
  end

  def decode(%ABI.FunctionSelector{} = function_selector, data, opts) do
    [res] = ABI.TypeDecoder.decode_raw(data, [%{type: {:tuple, function_selector.types}}], opts)

    if is_tuple(res) do
      Tuple.to_list(res)
    else
      res
    end

lib/abi.ex:186

  • The new @spec for decode_event/4 claims the returned event name is always String.t(), but function_selector.function is typed as String.t() | nil and this function can return {:ok, nil, map()} when check_event_signature: false (no signature verification forces a crash). Either validate that function_selector.function is present (and return an error if not) or widen the spec to include nil.
  @spec decode_event(binary() | ABI.FunctionSelector.t(), binary(), [binary()], keyword()) ::
          {:ok, String.t(), map()} | {:error, term()}
  def decode_event(function_signature, data, topics, opts \\ [])

  def decode_event(function_signature, data, topics, opts) when is_binary(function_signature) do
    decode_event(ABI.FunctionSelector.decode(function_signature), data, topics, opts)
  end

  def decode_event(%ABI.FunctionSelector{} = function_selector, data, topics, opts) do
    ABI.Event.decode_event(data, topics, function_selector, opts)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/abi.ex Outdated
Comment on lines 100 to 105
@spec decode(binary() | ABI.FunctionSelector.t(), binary(), keyword()) :: [any()]
def decode(function_signature, data, opts \\ [])

def decode(function_signature, data, opts) when is_binary(function_signature) do
decode(ABI.FunctionSelector.decode(function_signature), data, opts)
end
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR fixes ABI.decode/3 option passthrough for string signatures, but there’s no doctest covering the regression (the test suite appears to be doctest-based for ABI). Consider adding a doctest example that calls ABI.decode("(uint256 a,bool b)", data, decode_structs: true) (or similar) to ensure opts reach TypeDecoder.decode_raw/3 and that named tuples can be returned as maps when requested.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants