4
0
Fork 0

ExW3.Contract.uninstall_filter has been added as a cast

new-events
hswick 6 years ago
parent 477e566649
commit 77d98d4506
  1. 55
      README.md
  2. 18
      lib/exw3.ex
  3. 8
      test/exw3_test.exs

@ -98,68 +98,38 @@ ExW3 now provides async versions of `call` and `send`. They both return a `Task`
{:ok, data} = Task.await(t) {:ok, data} = Task.await(t)
``` ```
## Listening for Events ## Events
Elixir doesn't have event listeners like say JS. However, we can simulate that behavior with message passing. ExW3 allows the retrieval of event logs using filters. In this example, assume we have already deployed and registered a contract called :EventTester.
The way ExW3 handles event filters is with a background process that calls eth_getFilterChanges every cycle.
Whenever a change is detected it will send a message to whichever process is listening.
```elixir ```elixir
# Start the background listener # We can optionally specify extra parameters like `:fromBlock`, and `:toBlock`
ExW3.EventListener.start_link {:ok, filter_id} = ExW3.Contract.filter(:EventTester, "Simple", %{fromBlock: 42, toBlock: "latest"})
# Assuming we have already registered our contract called :EventTester # After some point that we think there are some new changes
# We can then add a filter for the event listener to look out for by passing in the event name, and the process we want to receive the messages when an event is triggered. {:ok, changes} = ExW3.Contract.get_filter_changes(filter_id)
# For now we are going to use the main process, however, we could pass in a pid of a different process.
# We can also optionally specify extra parameters like `:fromBlock`, and `:toBlock`
filter_id = ExW3.Contract.filter(:EventTester, "Simple", self(), %{fromBlock: 42, toBlock: "latest"})
# We can then wait for the event. Using the typical receive keyword we wait for the first instance of the event, and then continue with the rest of the code. This is useful for testing.
receive do
{:event, {filter_id, data}} -> IO.inspect data
end
# We can then uninstall the filter after we are done using it # We can then uninstall the filter after we are done using it
ExW3.uninstall_filter(filter_id) ExW3.Contract.uninstall_filter(filter_id)
# ExW3 also provides a helper method to continuously listen for events, with the `listen` method.
# One use is to combine all of our filters with pattern matching
ExW3.EventListener.listen(fn result ->
case result do
{filter_id, data} -> IO.inspect data
{filter_id2, data} -> IO.inspect data
end
end
# The listen method is a simple receive loop waiting for `{:event, _}` messages.
# It looks like this:
def listen(callback) do
receive do
{:event, result} -> apply callback, [result]
end
listen(callback)
end
# You could do something similar with your own process, whether it is a simple Task or a more involved GenServer.
``` ```
## Listening for Indexed Events ## Indexed Events
Ethereum allows for filtering events specific to its parameters using indexing. For all of the options see [here](https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newfilter) Ethereum allows a user to add topics to filters. This means the filter will only return events with the specific index parameters. For all of the extra options see [here](https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newfilter)
If you have written your event in Solidity like this: If you have written your event in Solidity like this:
``` ```
event SimpleIndex(uint256 indexed num, bytes32 indexed data, uint256 otherNum); event SimpleIndex(uint256 indexed num, bytes32 indexed data, uint256 otherNum);
``` ```
You can add filter on which logs will be returned back to the RPC client, based on the indexed fields. ExW3 allows for 2 ways of specifying these parameters or `topics` in two ways. The first, and probably more preferred way, is with a map: You can add filter on which logs will be returned back to the RPC client, based on the indexed fields.
ExW3 allows for 2 ways of specifying these parameters or `topics` in two ways. The first, and probably more preferred way, is with a map:
```elixir ```elixir
indexed_filter_id = ExW3.Contract.filter( indexed_filter_id = ExW3.Contract.filter(
:EventTester, :EventTester,
"SimpleIndex", "SimpleIndex",
self(),
%{ %{
topics: %{num: 46, data: "Hello, World!"}, topics: %{num: 46, data: "Hello, World!"},
} }
@ -171,7 +141,6 @@ The other option is with a list, but this is order dependent, and any values you
```elixir ```elixir
indexed_filter_id = ExW3.Contract.filter( indexed_filter_id = ExW3.Contract.filter(
:EventTester, :EventTester,
"SimpleIndex",
self(), self(),
%{ %{
topics: [nil, "Hello, World!"] topics: [nil, "Hello, World!"]

@ -192,7 +192,7 @@ defmodule ExW3 do
end end
@spec new_filter(%{}) :: binary() @spec new_filter(%{}) :: binary()
@doc "Creates a new filter, returns filter id" @doc "Creates a new filter, returns filter id. For more sophisticated use, prefer ExW3.Contract.filter."
def new_filter(map) do def new_filter(map) do
case Ethereumex.HttpClient.eth_new_filter(map) do case Ethereumex.HttpClient.eth_new_filter(map) do
{:ok, filter_id} -> filter_id {:ok, filter_id} -> filter_id
@ -200,6 +200,8 @@ defmodule ExW3 do
end end
end end
@spec get_filter_changes(binary()) :: any()
@doc "Gets event changes (logs) by filter. Unlike ExW3.Contract.get_filter_changes it does not return the data in a formatted way"
def get_filter_changes(filter_id) do def get_filter_changes(filter_id) do
case Ethereumex.HttpClient.eth_get_filter_changes(filter_id) do case Ethereumex.HttpClient.eth_get_filter_changes(filter_id) do
{:ok, changes} -> changes {:ok, changes} -> changes
@ -208,6 +210,7 @@ defmodule ExW3 do
end end
@spec uninstall_filter(binary()) :: boolean() @spec uninstall_filter(binary()) :: boolean()
@doc "Uninstalls filter from the ethereum node"
def uninstall_filter(filter_id) do def uninstall_filter(filter_id) do
case Ethereumex.HttpClient.eth_uninstall_filter(filter_id) do case Ethereumex.HttpClient.eth_uninstall_filter(filter_id) do
{:ok, result} -> result {:ok, result} -> result
@ -407,6 +410,12 @@ defmodule ExW3 do
GenServer.cast(ContractManager, {:register, {name, contract_info}}) GenServer.cast(ContractManager, {:register, {name, contract_info}})
end end
@spec uninstall_filter(binary()) :: :ok
@doc "Uninstalls the filter, and deletes the data associated with the filter id"
def uninstall_filter(filter_id) do
GenServer.cast(ContractManager, {:uninstall_filter, filter_id})
end
@spec at(keyword(), binary()) :: :ok @spec at(keyword(), binary()) :: :ok
@doc "Sets the address for the contract specified by the name argument" @doc "Sets the address for the contract specified by the name argument"
def at(name, address) do def at(name, address) do
@ -454,6 +463,7 @@ defmodule ExW3 do
end end
@spec filter(keyword(), binary(), %{}) :: {:ok, binary()} @spec filter(keyword(), binary(), %{}) :: {:ok, binary()}
@doc "Installs a filter on the Ethereum node. This also formats the parameters, and saves relevant information to format event logs."
def filter(contract_name, event_name, event_data \\ %{}) do def filter(contract_name, event_name, event_data \\ %{}) do
GenServer.call( GenServer.call(
ContractManager, ContractManager,
@ -462,6 +472,7 @@ defmodule ExW3 do
end end
@spec get_filter_changes(binary(), integer()) :: {:ok, []} @spec get_filter_changes(binary(), integer()) :: {:ok, []}
@doc "Using saved information related to the filter id, event logs are formatted properly"
def get_filter_changes(filter_id, seconds \\ 0) do def get_filter_changes(filter_id, seconds \\ 0) do
GenServer.call( GenServer.call(
ContractManager, ContractManager,
@ -653,6 +664,11 @@ defmodule ExW3 do
{:noreply, Map.put(state, name, register_helper(contract_info))} {:noreply, Map.put(state, name, register_helper(contract_info))}
end end
def handle_cast({:uninstall_filter, filter_id}, state) do
ExW3.uninstall_filter(filter_id)
{:noreply, Map.put(state, :filters, Map.delete(state[:filters], filter_id))}
end
# Calls # Calls
defp filter_topics_helper(event_signature, event_data, topic_types, topic_names) do defp filter_topics_helper(event_signature, event_data, topic_types, topic_names) do

@ -223,7 +223,7 @@ defmodule EXW3Test do
assert Map.get(log_data, "num") == 42 assert Map.get(log_data, "num") == 42
assert ExW3.bytes_to_string(Map.get(log_data, "data")) == "Hello, World!" assert ExW3.bytes_to_string(Map.get(log_data, "data")) == "Hello, World!"
ExW3.uninstall_filter(filter_id) ExW3.Contract.uninstall_filter(filter_id)
# Test indexed events # Test indexed events
@ -247,7 +247,7 @@ defmodule EXW3Test do
assert Map.get(log_data, "num") == 46 assert Map.get(log_data, "num") == 46
assert ExW3.bytes_to_string(Map.get(log_data, "data")) == "Hello, World!" assert ExW3.bytes_to_string(Map.get(log_data, "data")) == "Hello, World!"
assert Map.get(log_data, "otherNum") == 42 assert Map.get(log_data, "otherNum") == 42
ExW3.uninstall_filter(indexed_filter_id) ExW3.Contract.uninstall_filter(indexed_filter_id)
# Test Indexing Indexed Events # Test Indexing Indexed Events
@ -280,7 +280,7 @@ defmodule EXW3Test do
assert ExW3.bytes_to_string(Map.get(log_data, "data")) == "Hello, World!" assert ExW3.bytes_to_string(Map.get(log_data, "data")) == "Hello, World!"
assert Map.get(log_data, "otherNum") == 42 assert Map.get(log_data, "otherNum") == 42
ExW3.uninstall_filter(indexed_filter_id) ExW3.Contract.uninstall_filter(indexed_filter_id)
# Tests filter with map params # Tests filter with map params
@ -312,7 +312,7 @@ defmodule EXW3Test do
assert ExW3.bytes_to_string(Map.get(log_data, "data")) == "Hello, World!" assert ExW3.bytes_to_string(Map.get(log_data, "data")) == "Hello, World!"
assert Map.get(log_data, "otherNum") == 42 assert Map.get(log_data, "otherNum") == 42
ExW3.uninstall_filter(indexed_filter_id) ExW3.Contract.uninstall_filter(indexed_filter_id)
end end

Loading…
Cancel
Save