stocks29

stocks29

W3WS

Ethereum websocket library for Elixir

Features

  • W3WS.Listener for listening to new events
  • W3WS.Replayer for replaying events from past blocks
  • W3WS.Rpc for sending messages and receiving responses over the websocket with both sync and async interfaces.

Links

Notes

The library is still in the early stages. Feedback and suggestions are welcome.

Showing Posts 1 to 6

darkblueorange

darkblueorange

Thanks for this library!

I gave it a try and had some trouble parsing the contract.
It seems like I didn’t have the same ABI encoding
W3WS.ABI.from_files/1 kept returning empty list

I could make it work by adding some little tweak:

def from_files(paths) do
    Enum.flat_map(paths, fn path ->
      json_abi = path
      |> File.read!()
      |> Jason.decode!()

      json_abi
      |> Map.has_key?("abi")
      |> case do
        true ->
          resolve_bad_list(json_abi["abi"])
        _ ->
          ABI.parse_specification(json_abi, include_events?: true)
      end
      |> filter_abi_events()
    end)
  end

  defp resolve_bad_list(abi) do
    abi
    |> Enum.reduce([], fn elem, acc ->
      [parsed] = ABI.parse_specification([elem], include_events?: true)
      [parsed | acc]
    end)
  end

I used hardhat with pragma solidity ^0.8.0 version

darkblueorange

darkblueorange

Now I’m stuck on a weird network authentication-like error:

[debug] Connecting to wss://eth-sepolia.g.alchemy.com/v2/C
[debug] Connected
[debug] queuing request
[error] GenServer #PID<0.721.0> terminating
** (stop) {:error, {:ssl, {:sslsocket, {:gen_tcp, #Port<0.27>, :tls_connection, :undefined}, [#PID<0.725.0>, #PID<0.724.0>]}, "HTTP/1.1 401 Unauthorized\r\nDate: Fri, 26 Jan 2024 16:12:13 GMT\r\nContent-Type: text/html\r\nTransfer-Encoding: chunked\r\nConnection: keep-alive\r\nCF-Cache-Status: DYNAMIC\r\nSet-Cookie: _cfuvid=l16ZjaRtcX.p8qVvGJ1utKYUIEJc3qcoRuxrPxx1nf4-1706285533469-0-604800000; path=/; domain=.g.alchemy.com; HttpOnly; Secure; SameSite=None\r\nServer: cloudflare\r\nCF-RAY: 84ba09462ff66eff-CDG\r\n\r\n11\r\nApp key not found\r\n0\r\n\r\n"}}
Last message: {:ssl, {:sslsocket, {:gen_tcp, #Port<0.27>, :tls_connection, :undefined}, [#PID<0.725.0>, #PID<0.724.0>]}, "HTTP/1.1 401 Unauthorized\r\nDate: Fri, 26 Jan 2024 16:12:13 GMT\r\nContent-Type: text/html\r\nTransfer-Encoding: chunked\r\nConnection: keep-alive\r\nCF-Cache-Status: DYNAMIC\r\nSet-Cookie: _cfuvid=l16ZjaRtcX.p8qVvGJ1utKYUIEJc3qcoRuxrPxx1nf4-1706285533469-0-604800000; path=/; domain=.g.alchemy.com; HttpOnly; Secure; SameSite=None\r\nServer: cloudflare\r\nCF-RAY: 84ba09462ff66eff-CDG\r\n\r\n11\r\nApp key not found\r\n0\r\n\r\n"}

I cannot see in your docs any distant URL to test, so maybe I’m not using the library as intended.
The same configuration works in JS with ethers.js though, and I don’t need any API key to listen to pubsub events.

I hope you have an idea of what’s going on, I would really be happy to have a steady websocket listening to the blockchain.

EDIT

Well, I was too hasty saying I had no APP key.

My Alchemy URL actually contains it:
wss://eth-sepolia.g.alchemy.com/v2/<APP_KEY>

And apparently URI.new!/1 truncates it in your __using__ macro inside @behaviour W3WS.RpcBase (rpc_base.ex)

The guilty is there:

def start_link(module, args) do
    uri = URI.new!(args[:uri]) # <= truncation to the first character /v2/C
    …
end

when I manually add a correct :path key containing my APP key inside this function it does seem to work (no error logs).

EDIT bis

The mistake was on my side !! forget this post please :slight_smile:
I keep digging into the docs for now :ok_hand:

darkblueorange

darkblueorange

So I continued to use the library.

TLDR: it crashes in almost all places if networks is lost (because of Wind lack of error handling).

It seems to work globally fine, except when network is lost.
I spelunked a bit and arrived to this understanding:
Underlying Mint library raises, but Wind wrapper does not handle it properly.

In wind client.ex (Module Wind.Client) handle_info/2 receives a damaged state (disconnected or closed) and fails in handling the error on this line:
{:ok, conn, websocket, data} = Wind.decode(conn, ref, websocket, message)
(it was quite hard for me to debug, I couldn’t find some simple/working way to decompile Elixir .beam files to find the proper line failing at first, with all the macros and using_ all over the place)

If we go to the decode/4 function of Wind Module we can see another failing error handling case:
with {:ok, conn, [{:data, ^ref, data}]} <- Mint.WebSocket.stream(conn, message), ... do ...
(no else, and this is actually the first place failing)

Besides when this function crashes (Wind.decode/2 first, but if we fix it, Wind.Client.handle_info/2 will also crash), the associated GenServer reboots and attempts to launch a new connection through handle_continue/2 in Wind.Client Module.
Of course network is still down, so here is another fail (the infamous :nxdomain), and we end up in sending {:stop, {:error, conn, reason}, state} to the GenServer.
It could be good to fail (I would personally have preferred a ping spaced more and more to attempt after n*i seconds with i growing over time), but the thing is that when the GenServer stops it crashes the whole application.
Maybe here you can do something to avoid this?
I’m not familiar with all the GenServer communication and possible events handling (maybe using Process.send_after(self(), ... is a way), so I could not propose some PR in a reasonable time.

Bug is easy to reproduce, just switch off your wifi, or close your laptop for a minute :slight_smile:

stocks29

stocks29 OP

Thanks for trying out W3WS and providing feedback!

I’m using "hardhat-abi-exporter": "^2.10.1", to export the ABI for my projects. This seems to be a standard format which JS libraries understand. I’d be interested to understand what ABI format you’re using. Would you mind sharing an example?

Regarding the crashing, I think this is something that should be handled at the ListenerManager (supervisor) level, since if the network is gone there is nothing the Listener can do. We could add an optional opts keyword list which would be passed to DynamicSupervisor.start_link/2 allowing you to pass :max_restarts and :max_seconds. This is the block of code I’m referring to:

This would enable you to allow more time for the network to come back. Of course if the network never comes back the app is still going to crash. Would you want to give this a try and see if it suites your needs?

darkblueorange

darkblueorange

Thanks a lot for the very quick reply

Well I’m a real beginner in Blockchain dev.
I didn’t use any external library, I naively retrieved the JSON produced by npx hardhat compile, under artifact/contracts/MyContract.sol/MyContract.json

I see that the output is really similar:

{
  "_format": "hh-sol-artifact-1",
  "contractName": "DEXtito4",
  "sourceName": "contracts/MySmartv6.sol",
  "abi": [ 

  // here is the exact same output than npx hardhat export-abi

  ],
  "bytecode": 
...,
  "linkReferences": {},
  "deployedLinkReferences": {}
}

only a key dive away (“abi”) to get the same thing.

Regarding the network crashing, I’m not sure handling blockchain network availability by hoping that a crash or a change in it (node adress, alchemy account, anything) would just be temporary is the way I would like to go.
My whole application is not only about blockchain interactions, so seing it totally crash because of a blockchain network issue makes it too dependant.
I read in the meantime about GenServers, and the philosophy I got was that restarts are not a design pattern, but an exception.
I raised an issue in Wind github, as I believed it should be handled there (I may be mistaken :person_shrugging:).

stocks29

stocks29 OP

Thanks for opening the wind ticket. I agree that crashing the entire app is not ideal in this case.

It would be great if Wind could re-connect when the network issue is resolved. I do worry about it failing silently forever though.

Another option would be some sort of supervisor that tries to restart the worker forever without failing. I’m not sure if something like this exists. Potentially the same problem with failing silently forever though.

All that said, I’m yet to have any issues with alchemy. Their reliability has been great so far. Although something could definitely break between your instance and their API.

On a security note, you may want to remove your alchemy API key from this comment: Crash when network is lost · Issue #3 · bfolkens/wind · GitHub

— All posts loaded —

Where Next? Top

Trending in Announcing Top

woylie
Flop is an Elixir library that applies filtering, ordering and pagination parameters to your Ecto queries. offset-based pagination with...
New
MRdotB
I needed to reuse React components from my Chrome extension in my Phoenix/LiveView backend. I noticed that for Svelte/Vue, there are live...
New
woylie
I released Doggo, a collection of unstyled Phoenix components. https://github.com/woylie/doggo Features Unstyled Phoenix components....
New
GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
ahamez
Hi everyone, I’ve been working on this protobuf library for 3 years. We use it in the company I work for, EasyMile, to communicate with ...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New
bartblast
Hey folks, I just published a post about Hologram’s funding and where the project goes next - the short version: Curiosum as Main Spons...
New
CodeSync
:microphone: ElixirConf 2026 - Call for Talks is open! We’re heading to Chicago :united_states: :round_pushpin: In person + virtual :d...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews