Skip to content

Commit b3a69f7

Browse files
authored
Use @external_resource module attribute (#189)
* Use external_resource module attribute * Fix helper tests for read_abi * Fix read_abi spec
1 parent e41ad54 commit b3a69f7

File tree

4 files changed

+36
-15
lines changed

4 files changed

+36
-15
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
- Update `Ethers` module to RPCfy eth_call request params
1212
- Removed unnecessary hex decode/encodes in requests
13+
- Use `@external_resource` in generated contracts to track ABI changes and recompile if needed. (Thanks @sitch)
1314

1415
## v0.6.3 (2025-01-21)
1516

lib/ethers/contract.ex

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ defmodule Ethers.Contract do
8080
|> Module.get_attribute(:_ethers_using_opts)
8181
|> Code.eval_quoted([], env)
8282

83-
{:ok, abi} = read_abi(opts)
83+
{abi, abi_file} = read_abi(opts)
8484
contract_binary = maybe_read_contract_binary(opts)
8585
default_address = Keyword.get(opts, :default_address)
8686
skip_docs = Keyword.get(opts, :skip_docs, false)
@@ -124,11 +124,20 @@ defmodule Ethers.Contract do
124124
events_impl = Enum.map(events, &impl(&1, module, impl_opts))
125125
event_selectors = Enum.flat_map(events, & &1.selectors)
126126

127+
external_resource_ast =
128+
if abi_file do
129+
quote do
130+
@external_resource unquote(abi_file)
131+
end
132+
end
133+
127134
events_module_ast =
128135
quote context: module do
129136
defmodule unquote(events_mod_name) do
130137
@moduledoc "Events for `#{Macro.to_string(unquote(module))}`"
131138

139+
unquote(external_resource_ast)
140+
132141
defdelegate __default_address__, to: unquote(module)
133142
unquote(events_impl)
134143

@@ -150,6 +159,8 @@ defmodule Ethers.Contract do
150159
defmodule unquote(errors_mod_name) do
151160
@moduledoc false
152161

162+
unquote(external_resource_ast)
163+
153164
unquote(error_modules_ast)
154165
unquote(errors_module_impl)
155166
end
@@ -164,6 +175,8 @@ defmodule Ethers.Contract do
164175

165176
extra_ast =
166177
quote context: module do
178+
unquote(external_resource_ast)
179+
167180
def __contract_binary__, do: unquote(contract_binary)
168181

169182
@doc """

lib/ethers/contract_helpers.ex

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ defmodule Ethers.ContractHelpers do
33

44
require Logger
55

6-
@spec read_abi(Keyword.t()) :: {:ok, [...]} | {:error, atom()}
6+
@spec read_abi(Keyword.t()) :: {abi :: [...], file_path :: String.t() | nil}
77
def read_abi(opts) do
88
case Keyword.take(opts, [:abi, :abi_file]) do
99
[{type, data}] ->
10-
read_abi(type, data)
10+
do_read_abi(type, data, nil)
1111

1212
_ ->
13-
{:error, :bad_argument}
13+
raise ArgumentError, "Invalid arguments. Specify either `:abi` or `:abi_file` option"
1414
end
1515
end
1616

@@ -389,21 +389,22 @@ defmodule Ethers.ContractHelpers do
389389
|> Enum.map(&elem(&1, 0))
390390
end
391391

392-
defp read_abi(:abi, abi) when is_list(abi), do: {:ok, abi}
393-
defp read_abi(:abi, %{"abi" => abi}), do: read_abi(:abi, abi)
392+
defp do_read_abi(:abi, abi, file_path) when is_list(abi), do: {abi, file_path}
393+
defp do_read_abi(:abi, %{"abi" => abi}, file_path), do: do_read_abi(:abi, abi, file_path)
394394

395-
defp read_abi(:abi, abi) when is_atom(abi) do
396-
read_abi(:abi_file, Path.join(:code.priv_dir(:ethers), "abi/#{abi}.json"))
395+
defp do_read_abi(:abi, abi, _file_path) when is_atom(abi) do
396+
file_path = Path.join(:code.priv_dir(:ethers), "abi/#{abi}.json")
397+
do_read_abi(:abi_file, file_path, nil)
397398
end
398399

399-
defp read_abi(:abi, abi) when is_binary(abi) do
400+
defp do_read_abi(:abi, abi, file_path) when is_binary(abi) do
400401
abi = Ethers.json_module().decode!(abi)
401-
read_abi(:abi, abi)
402+
do_read_abi(:abi, abi, file_path)
402403
end
403404

404-
defp read_abi(:abi_file, file) do
405+
defp do_read_abi(:abi_file, file, _file_path) do
405406
abi = File.read!(file)
406-
read_abi(:abi, abi)
407+
do_read_abi(:abi, abi, file)
407408
end
408409

409410
defp get_argument_name_ast({ast, name}) do

test/ethers/contract_helpers_test.exs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,19 @@ defmodule Ethers.ContractHelpersTest do
44

55
describe "read_abi" do
66
test "works with default abis" do
7-
assert {:ok, abi_results} = ContractHelpers.read_abi(abi: :erc20)
7+
assert {abi_results, abi_file} = ContractHelpers.read_abi(abi: :erc20)
88
assert is_list(abi_results)
9+
assert String.ends_with?(abi_file, "priv/abi/erc20.json")
910
end
1011

1112
test "returns error with invalid parameters" do
12-
assert {:error, :bad_argument} = ContractHelpers.read_abi(abi: :erc20, abi_file: "file")
13-
assert {:error, :bad_argument} = ContractHelpers.read_abi(bad_arg: true)
13+
assert_raise ArgumentError, fn ->
14+
ContractHelpers.read_abi(abi: :erc20, abi_file: "file")
15+
end
16+
17+
assert_raise ArgumentError, fn ->
18+
assert {:error, :bad_argument} = ContractHelpers.read_abi(bad_arg: true)
19+
end
1420
end
1521
end
1622

0 commit comments

Comments
 (0)