diff --git a/CHANGELOG.md b/CHANGELOG.md index 919a082..41a1e6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## Unreleased + +### Enhancements + +- Move and export abi decode functionality to `Ethers.TxData` module +- Export `Ethers.TxData.to_map/2` in docs + ## v0.5.4 (2024-10-22) ### Bug fixes diff --git a/lib/ethers.ex b/lib/ethers.ex index 8c49ee6..d2f72f1 100644 --- a/lib/ethers.ex +++ b/lib/ethers.ex @@ -661,14 +661,10 @@ defmodule Ethers do defp pre_process(data, [], _action, _opts), do: {:ok, data} - defp post_process({:ok, resp}, %{selector: selector}, :call) when valid_result(resp) do - selector - |> ABI.decode(Ethers.Utils.hex_decode!(resp), :output) - |> Enum.zip(selector.returns) - |> Enum.map(fn {return, type} -> Utils.human_arg(return, type) end) - |> case do - [element] -> {:ok, element} - elements -> {:ok, elements} + defp post_process({:ok, resp}, %{selector: _selector} = tx_data, :call) + when valid_result(resp) do + with {:ok, data} <- Utils.hex_decode(resp) do + TxData.abi_decode(data, tx_data, :output) end end diff --git a/lib/ethers/tx_data.ex b/lib/ethers/tx_data.ex index 9cc57b3..3703f38 100644 --- a/lib/ethers/tx_data.ex +++ b/lib/ethers/tx_data.ex @@ -4,6 +4,8 @@ defmodule Ethers.TxData do and the target `to` address. """ + alias Ethers.Utils + @typedoc """ Holds transaction data, the function selector and the default `to` address. @@ -31,8 +33,12 @@ defmodule Ethers.TxData do } end - @doc false + @doc """ + Converts a TxData struct and optional overrides to a map ready for RPC data. + """ @spec to_map(t() | map(), Keyword.t()) :: map() + def to_map(tx_data, overrides \\ []) + def to_map(%__MODULE__{} = tx_data, overrides) do tx_data |> get_tx_map() @@ -48,6 +54,33 @@ defmodule Ethers.TxData do end) end + @doc """ + ABI decodes a function input/output given a TxData or FunctionSelector + """ + @spec abi_decode(binary(), ABI.FunctionSelector.t() | t(), type :: :input | :output) :: + {:ok, any() | [any()]} + def abi_decode(data, tx_data_or_selector, type \\ :output) + + def abi_decode(data, %{selector: %ABI.FunctionSelector{} = selector}, type), + do: abi_decode(data, selector, type) + + def abi_decode(data, %ABI.FunctionSelector{} = selector, type) do + types = + case type do + :input -> selector.types + :output -> selector.returns + end + + selector + |> ABI.decode(data, type) + |> Enum.zip(types) + |> Enum.map(fn {return, type} -> Utils.human_arg(return, type) end) + |> case do + [element] -> {:ok, element} + elements -> {:ok, elements} + end + end + defp get_tx_map(%{selector: %{type: :function}} = tx_data) do %{data: tx_data.data} |> maybe_add_to_address(tx_data.default_address)