Repository files navigation

Exth

Exth is an Elixir client for interacting with EVM-compatible blockchain nodes via JSON-RPC. It provides a robust, type-safe interface for making Ethereum RPC calls.

Features

  • πŸ”’ Type Safety: Comprehensive type specs and validation
  • πŸ”„ Transport Agnostic: Pluggable transport system (HTTP, WebSocket, IPC)
  • 🎯 Smart Defaults: Sensible defaults with full configurability
  • πŸ›‘οΈ Error Handling: Detailed error reporting and recovery
  • πŸ“¦ Batch Support: Efficient batch request processing
  • πŸ”Œ Protocol Compliance: Full JSON-RPC 2.0 specification support
  • βš™οΈ Dynamic Configuration: Flexible configuration through both inline options and application config

Installation

Add exth to your list of dependencies in mix.exs:

defdepsdo[{:exth,"~> 0.1.0"},# Optional dependencies:# Mint for Tesla adapter{:mint,"~> 1.7"}]end

Usage

Exth offers two ways to interact with EVM nodes:

  1. Provider (High-Level): Define a provider module with convenient function names and no need to pass client references.
  2. RPC Client (Low-Level): Direct client usage with more control, requiring explicit client handling.

Provider (Recommended)

# Basic usage with inline configurationdefmoduleMyProviderdouseExth.Provider,otp_app: :your_otp_app,transport_type: :http,rpc_url: "https://YOUR-RPC-URL"end# Dynamic configuration through application config# In your config/config.exs or similar:config:your_otp_app,MyProvider,rpc_url: "https://YOUR-RPC-URL",timeout: 30_000,max_retries: 3# Then in your provider module:defmoduleMyProviderdouseExth.Provider,otp_app: :your_otp_app,transport_type: :httpend# Configuration is merged with inline options taking precedencedefmoduleMyProviderdouseExth.Provider,otp_app: :your_otp_app,transport_type: :http,rpc_url: "https://OVERRIDE-RPC-URL"# This will override the config valueend# Use the provider{:ok,block_number}=MyProvider.block_number(){:ok,balance}=MyProvider.get_balance("0x742d35Cc6634C0532925a3b844Bc454e4438f44e","latest"){:ok,block}=MyProvider.get_block_by_number("0x1",true){:ok,tx_hash}=MyProvider.send_raw_transaction("0x...")

The Provider approach is recommended for most use cases as it provides:

  • ✨ Clean, intuitive function names
  • πŸ”’ Type-safe parameters
  • πŸ“ Better documentation and IDE support
  • 🎯 No need to manage client references
  • βš™οΈ Flexible configuration through both inline options and application config

Configuration Options

Providers can be configured through both inline options and application config. Inline options take precedence over application config. Here are the available options:

# Required optionstransport_type: :http|:websocket|:ipc|:custom# Transport type to userpc_url: "https://..."# RPC endpoint URL (for HTTP/WebSocket)path: "/tmp/ethereum.ipc"# Socket path (for IPC)# Required inline optionotp_app: :your_otp_app# Application name for config lookup# Custom transport optionsmodule: MyCustomTransport# Required when transport_type is :custom# Optional HTTP optionstimeout: 30_000# Request timeout in millisecondsheaders: [{"header","value"}]# Custom headers for HTTP transportadapter: Tesla.Adapter.Mint# HTTP adapter (defaults to Mint)# Optional WebSocket optionsdispatch_callback: fnresponse->handle_response(response)end# Required for WebSocket# Optional IPC optionspool_size: 10# Connection pool sizesocket_opts: [:binary,active: false,reuseaddr: true]# Socket options

RPC Client

aliasExth.Rpc# 1. Define a client{:ok,client}=Rpc.new_client(transport_type: :http,rpc_url: "https://YOUR-RPC-URL")# 2.1. Make RPC calls with explicit clientrequest1=Rpc.request(client,"eth_blockNumber",[]){:ok,block_number}=Rpc.send(client,request1)# 2.2. Or make RPC calls without a clientrequest2=Rpc.request("eth_getBalance",["0x742d35Cc6634C0532925a3b844Bc454e4438f44e","latest"]){:ok,balance}=Rpc.send(client,request2)# 3. You can also send multiple requests in one callrequests=[request1,request2]{:ok,responses}=Rpc.send(client,requests)# 4. You can invert the order of the arguments and pipeRpc.request("eth_blockNumber",[])|>Rpc.send(client)# OR[request1,request2]|>Rpc.send(client)

Use the RPC Client approach when you need:

  • πŸ”§ Direct control over RPC calls
  • πŸ”„ Dynamic method names
  • πŸ› οΈ Custom parameter handling
  • πŸŽ›οΈ Flexible client management (multiple clients, runtime configuration)

Transport Options

Exth uses a pluggable transport system that supports different communication protocols. Each transport type can be configured with specific options:

HTTP Transport

The HTTP transport provides robust HTTP/HTTPS communication with configurable middleware:

# Provider configurationdefmoduleMyProviderdouseExth.Provider,transport_type: :http,rpc_url: "https://eth-mainnet.example.com",# Optional HTTP-specific configurationadapter: Tesla.Adapter.Mint,# Default HTTP adapterheaders: [{"authorization","Bearer token"}],timeout: 30_000# Request timeout in msend# Direct client configuration{:ok,client}=Exth.Rpc.new_client(transport_type: :http,rpc_url: "https://eth-mainnet.example.com",adapter: Tesla.Adapter.Mint,headers: [{"authorization","Bearer token"}],timeout: 30_000)

HTTP Features:

  • Built on Tesla HTTP client with middleware support
  • Configurable adapters (Mint, Hackney, etc.)
  • Configurable headers and timeouts
  • Automatic URL validation and formatting

WebSocket Transport

The WebSocket transport provides full-duplex communication for real-time updates and subscriptions:

# Provider configurationdefmoduleMyProviderdouseExth.Provider,transport_type: :websocket,rpc_url: "wss://eth-mainnet.example.com",dispatch_callback: fnresponse->handle_response(response)endend# Direct client configuration{:ok,client}=Exth.Rpc.new_client(transport_type: :websocket,rpc_url: "wss://eth-mainnet.example.com",dispatch_callback: fnresponse->handle_response(response)end)# Example subscriptionrequest=Rpc.request("eth_subscribe",["newHeads"]){:ok,response}=Rpc.send(client,request)

WebSocket Features:

  • Full-duplex communication
  • Support for subscriptions and real-time updates
  • Automatic connection management and lifecycle
  • Asynchronous message handling via dispatch callbacks
  • Connection state management and supervision

IPC Transport

The IPC transport provides communication with local Ethereum nodes via Unix domain sockets:

# Provider configurationdefmoduleMyProviderdouseExth.Provider,transport_type: :ipc,path: "/tmp/ethereum.ipc",# Optional IPC-specific configurationtimeout: 30_000,# Request timeout in mspool_size: 10,# Number of connections in the poolsocket_opts: [:binary,active: false,reuseaddr: true]end# Direct client configuration{:ok,client}=Exth.Rpc.new_client(transport_type: :ipc,path: "/tmp/ethereum.ipc",timeout: 30_000,pool_size: 5)# Make requestsrequest=Rpc.request("eth_blockNumber",[]){:ok,response}=Rpc.send(client,request)

IPC Features:

  • Unix domain socket communication
  • Connection pooling with NimblePool for efficient resource management
  • Low latency for local nodes
  • Automatic connection lifecycle management
  • Note: Only available on Unix-like systems

IPC Configuration Options:

  • :path - (required) The Unix domain socket path (e.g., "/tmp/ethereum.ipc")
  • :timeout - Request timeout in milliseconds (default: 30,000ms)
  • :socket_opts - TCP socket options (default: [:binary, active: false, reuseaddr: true])
  • :pool_size - Number of connections in the pool (default: 10)
  • :pool_lazy_workers - Whether to create workers lazily (default: true)
  • :pool_worker_idle_timeout - Worker idle timeout (default: nil)
  • :pool_max_idle_pings - Maximum idle pings before worker termination (default: -1)

Custom Transport

Implement your own transport by creating a module and implementing the Exth.Transport behaviour:

defmoduleMyCustomTransportdouseExth.Transport@implExth.Transportdefinit(opts)do# Initialize your transport{:ok,transport_state}end@implExth.Transportdefhandle_request(transport_state,request)do# Handle the JSON-RPC request# Return {:ok, response} or {:error, reason}endend# Use your custom transportdefmoduleMyProviderdouseExth.Provider,transport_type: :custom,module: MyCustomTransport,rpc_url: "custom://endpoint",# Additional custom optionscustom_option: "value"end# Direct client configuration{:ok,client}=Exth.Rpc.new_client(transport_type: :custom,module: MyCustomTransport,custom_option: "value")

Custom Transport Features:

  • Full control over transport implementation
  • Custom state management
  • Behaviour-based implementation for consistency

Examples

Check out our examples directory for practical usage examples.

Requirements

  • Elixir ~> 1.18
  • Erlang/OTP 26 or later

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b feature/my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin feature/my-new-feature)
  5. Create new Pull Request

License

This project is licensed under the MIT License. See LICENSE for details.

About

Elixir JSON-RPC client fo EVM-compatible blockchains πŸ“‘

Topics

Resources

Stars

8 stars

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

Exth

Exth is an Elixir client for interacting with EVM-compatible blockchain nodes via JSON-RPC. It provides a robust, type-safe interface for making Ethereum RPC calls.

Features

  • πŸ”’ Type Safety: Comprehensive type specs and validation
  • πŸ”„ Transport Agnostic: Pluggable transport system (HTTP, WebSocket, IPC)
  • 🎯 Smart Defaults: Sensible defaults with full configurability
  • πŸ›‘οΈ Error Handling: Detailed error reporting and recovery
  • πŸ“¦ Batch Support: Efficient batch request processing
  • πŸ”Œ Protocol Compliance: Full JSON-RPC 2.0 specification support
  • βš™οΈ Dynamic Configuration: Flexible configuration through both inline options and application config

Installation

Add exth to your list of dependencies in mix.exs:

defdepsdo[{:exth,"~> 0.1.0"},# Optional dependencies:# Mint for Tesla adapter{:mint,"~> 1.7"}]end

Usage

Exth offers two ways to interact with EVM nodes:

  1. Provider (High-Level): Define a provider module with convenient function names and no need to pass client references.
  2. RPC Client (Low-Level): Direct client usage with more control, requiring explicit client handling.

Provider (Recommended)

# Basic usage with inline configurationdefmoduleMyProviderdouseExth.Provider,otp_app: :your_otp_app,transport_type: :http,rpc_url: "https://YOUR-RPC-URL"end# Dynamic configuration through application config# In your config/config.exs or similar:config:your_otp_app,MyProvider,rpc_url: "https://YOUR-RPC-URL",timeout: 30_000,max_retries: 3# Then in your provider module:defmoduleMyProviderdouseExth.Provider,otp_app: :your_otp_app,transport_type: :httpend# Configuration is merged with inline options taking precedencedefmoduleMyProviderdouseExth.Provider,otp_app: :your_otp_app,transport_type: :http,rpc_url: "https://OVERRIDE-RPC-URL"# This will override the config valueend# Use the provider{:ok,block_number}=MyProvider.block_number(){:ok,balance}=MyProvider.get_balance("0x742d35Cc6634C0532925a3b844Bc454e4438f44e","latest"){:ok,block}=MyProvider.get_block_by_number("0x1",true){:ok,tx_hash}=MyProvider.send_raw_transaction("0x...")

The Provider approach is recommended for most use cases as it provides:

  • ✨ Clean, intuitive function names
  • πŸ”’ Type-safe parameters
  • πŸ“ Better documentation and IDE support
  • 🎯 No need to manage client references
  • βš™οΈ Flexible configuration through both inline options and application config

Configuration Options

Providers can be configured through both inline options and application config. Inline options take precedence over application config. Here are the available options:

# Required optionstransport_type: :http|:websocket|:ipc|:custom# Transport type to userpc_url: "https://..."# RPC endpoint URL (for HTTP/WebSocket)path: "/tmp/ethereum.ipc"# Socket path (for IPC)# Required inline optionotp_app: :your_otp_app# Application name for config lookup# Custom transport optionsmodule: MyCustomTransport# Required when transport_type is :custom# Optional HTTP optionstimeout: 30_000# Request timeout in millisecondsheaders: [{"header","value"}]# Custom headers for HTTP transportadapter: Tesla.Adapter.Mint# HTTP adapter (defaults to Mint)# Optional WebSocket optionsdispatch_callback: fnresponse->handle_response(response)end# Required for WebSocket# Optional IPC optionspool_size: 10# Connection pool sizesocket_opts: [:binary,active: false,reuseaddr: true]# Socket options

RPC Client

aliasExth.Rpc# 1. Define a client{:ok,client}=Rpc.new_client(transport_type: :http,rpc_url: "https://YOUR-RPC-URL")# 2.1. Make RPC calls with explicit clientrequest1=Rpc.request(client,"eth_blockNumber",[]){:ok,block_number}=Rpc.send(client,request1)# 2.2. Or make RPC calls without a clientrequest2=Rpc.request("eth_getBalance",["0x742d35Cc6634C0532925a3b844Bc454e4438f44e","latest"]){:ok,balance}=Rpc.send(client,request2)# 3. You can also send multiple requests in one callrequests=[request1,request2]{:ok,responses}=Rpc.send(client,requests)# 4. You can invert the order of the arguments and pipeRpc.request("eth_blockNumber",[])|>Rpc.send(client)# OR[request1,request2]|>Rpc.send(client)

Use the RPC Client approach when you need:

  • πŸ”§ Direct control over RPC calls
  • πŸ”„ Dynamic method names
  • πŸ› οΈ Custom parameter handling
  • πŸŽ›οΈ Flexible client management (multiple clients, runtime configuration)

Transport Options

Exth uses a pluggable transport system that supports different communication protocols. Each transport type can be configured with specific options:

HTTP Transport

The HTTP transport provides robust HTTP/HTTPS communication with configurable middleware:

# Provider configurationdefmoduleMyProviderdouseExth.Provider,transport_type: :http,rpc_url: "https://eth-mainnet.example.com",# Optional HTTP-specific configurationadapter: Tesla.Adapter.Mint,# Default HTTP adapterheaders: [{"authorization","Bearer token"}],timeout: 30_000# Request timeout in msend# Direct client configuration{:ok,client}=Exth.Rpc.new_client(transport_type: :http,rpc_url: "https://eth-mainnet.example.com",adapter: Tesla.Adapter.Mint,headers: [{"authorization","Bearer token"}],timeout: 30_000)

HTTP Features:

  • Built on Tesla HTTP client with middleware support
  • Configurable adapters (Mint, Hackney, etc.)
  • Configurable headers and timeouts
  • Automatic URL validation and formatting

WebSocket Transport

The WebSocket transport provides full-duplex communication for real-time updates and subscriptions:

# Provider configurationdefmoduleMyProviderdouseExth.Provider,transport_type: :websocket,rpc_url: "wss://eth-mainnet.example.com",dispatch_callback: fnresponse->handle_response(response)endend# Direct client configuration{:ok,client}=Exth.Rpc.new_client(transport_type: :websocket,rpc_url: "wss://eth-mainnet.example.com",dispatch_callback: fnresponse->handle_response(response)end)# Example subscriptionrequest=Rpc.request("eth_subscribe",["newHeads"]){:ok,response}=Rpc.send(client,request)

WebSocket Features:

  • Full-duplex communication
  • Support for subscriptions and real-time updates
  • Automatic connection management and lifecycle
  • Asynchronous message handling via dispatch callbacks
  • Connection state management and supervision

IPC Transport

The IPC transport provides communication with local Ethereum nodes via Unix domain sockets:

# Provider configurationdefmoduleMyProviderdouseExth.Provider,transport_type: :ipc,path: "/tmp/ethereum.ipc",# Optional IPC-specific configurationtimeout: 30_000,# Request timeout in mspool_size: 10,# Number of connections in the poolsocket_opts: [:binary,active: false,reuseaddr: true]end# Direct client configuration{:ok,client}=Exth.Rpc.new_client(transport_type: :ipc,path: "/tmp/ethereum.ipc",timeout: 30_000,pool_size: 5)# Make requestsrequest=Rpc.request("eth_blockNumber",[]){:ok,response}=Rpc.send(client,request)

IPC Features:

  • Unix domain socket communication
  • Connection pooling with NimblePool for efficient resource management
  • Low latency for local nodes
  • Automatic connection lifecycle management
  • Note: Only available on Unix-like systems

IPC Configuration Options:

  • :path - (required) The Unix domain socket path (e.g., "/tmp/ethereum.ipc")
  • :timeout - Request timeout in milliseconds (default: 30,000ms)
  • :socket_opts - TCP socket options (default: [:binary, active: false, reuseaddr: true])
  • :pool_size - Number of connections in the pool (default: 10)
  • :pool_lazy_workers - Whether to create workers lazily (default: true)
  • :pool_worker_idle_timeout - Worker idle timeout (default: nil)
  • :pool_max_idle_pings - Maximum idle pings before worker termination (default: -1)

Custom Transport

Implement your own transport by creating a module and implementing the Exth.Transport behaviour:

defmoduleMyCustomTransportdouseExth.Transport@implExth.Transportdefinit(opts)do# Initialize your transport{:ok,transport_state}end@implExth.Transportdefhandle_request(transport_state,request)do# Handle the JSON-RPC request# Return {:ok, response} or {:error, reason}endend# Use your custom transportdefmoduleMyProviderdouseExth.Provider,transport_type: :custom,module: MyCustomTransport,rpc_url: "custom://endpoint",# Additional custom optionscustom_option: "value"end# Direct client configuration{:ok,client}=Exth.Rpc.new_client(transport_type: :custom,module: MyCustomTransport,custom_option: "value")

Custom Transport Features:

  • Full control over transport implementation
  • Custom state management
  • Behaviour-based implementation for consistency

Examples

Check out our examples directory for practical usage examples.

Requirements

  • Elixir ~> 1.18
  • Erlang/OTP 26 or later

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b feature/my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin feature/my-new-feature)
  5. Create new Pull Request

License

This project is licensed under the MIT License. See LICENSE for details.

About

Elixir JSON-RPC client fo EVM-compatible blockchains πŸ“‘

Topics

Resources

Stars

8 stars

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Exth

Exth is an Elixir client for interacting with EVM-compatible blockchain nodes via JSON-RPC. It provides a robust, type-safe interface for making Ethereum RPC calls.

Features

  • πŸ”’ Type Safety: Comprehensive type specs and validation
  • πŸ”„ Transport Agnostic: Pluggable transport system (HTTP, WebSocket, IPC)
  • 🎯 Smart Defaults: Sensible defaults with full configurability
  • πŸ›‘οΈ Error Handling: Detailed error reporting and recovery
  • πŸ“¦ Batch Support: Efficient batch request processing
  • πŸ”Œ Protocol Compliance: Full JSON-RPC 2.0 specification support
  • βš™οΈ Dynamic Configuration: Flexible configuration through both inline options and application config

Installation

Add exth to your list of dependencies in mix.exs:

defdepsdo[{:exth,"~> 0.1.0"},# Optional dependencies:# Mint for Tesla adapter{:mint,"~> 1.7"}]end

Usage

Exth offers two ways to interact with EVM nodes:

  1. Provider (High-Level): Define a provider module with convenient function names and no need to pass client references.
  2. RPC Client (Low-Level): Direct client usage with more control, requiring explicit client handling.

Provider (Recommended)

# Basic usage with inline configurationdefmoduleMyProviderdouseExth.Provider,otp_app: :your_otp_app,transport_type: :http,rpc_url: "https://YOUR-RPC-URL"end# Dynamic configuration through application config# In your config/config.exs or similar:config:your_otp_app,MyProvider,rpc_url: "https://YOUR-RPC-URL",timeout: 30_000,max_retries: 3# Then in your provider module:defmoduleMyProviderdouseExth.Provider,otp_app: :your_otp_app,transport_type: :httpend# Configuration is merged with inline options taking precedencedefmoduleMyProviderdouseExth.Provider,otp_app: :your_otp_app,transport_type: :http,rpc_url: "https://OVERRIDE-RPC-URL"# This will override the config valueend# Use the provider{:ok,block_number}=MyProvider.block_number(){:ok,balance}=MyProvider.get_balance("0x742d35Cc6634C0532925a3b844Bc454e4438f44e","latest"){:ok,block}=MyProvider.get_block_by_number("0x1",true){:ok,tx_hash}=MyProvider.send_raw_transaction("0x...")

The Provider approach is recommended for most use cases as it provides:

  • ✨ Clean, intuitive function names
  • πŸ”’ Type-safe parameters
  • πŸ“ Better documentation and IDE support
  • 🎯 No need to manage client references
  • βš™οΈ Flexible configuration through both inline options and application config

Configuration Options

Providers can be configured through both inline options and application config. Inline options take precedence over application config. Here are the available options:

# Required optionstransport_type: :http|:websocket|:ipc|:custom# Transport type to userpc_url: "https://..."# RPC endpoint URL (for HTTP/WebSocket)path: "/tmp/ethereum.ipc"# Socket path (for IPC)# Required inline optionotp_app: :your_otp_app# Application name for config lookup# Custom transport optionsmodule: MyCustomTransport# Required when transport_type is :custom# Optional HTTP optionstimeout: 30_000# Request timeout in millisecondsheaders: [{"header","value"}]# Custom headers for HTTP transportadapter: Tesla.Adapter.Mint# HTTP adapter (defaults to Mint)# Optional WebSocket optionsdispatch_callback: fnresponse->handle_response(response)end# Required for WebSocket# Optional IPC optionspool_size: 10# Connection pool sizesocket_opts: [:binary,active: false,reuseaddr: true]# Socket options

RPC Client

aliasExth.Rpc# 1. Define a client{:ok,client}=Rpc.new_client(transport_type: :http,rpc_url: "https://YOUR-RPC-URL")# 2.1. Make RPC calls with explicit clientrequest1=Rpc.request(client,"eth_blockNumber",[]){:ok,block_number}=Rpc.send(client,request1)# 2.2. Or make RPC calls without a clientrequest2=Rpc.request("eth_getBalance",["0x742d35Cc6634C0532925a3b844Bc454e4438f44e","latest"]){:ok,balance}=Rpc.send(client,request2)# 3. You can also send multiple requests in one callrequests=[request1,request2]{:ok,responses}=Rpc.send(client,requests)# 4. You can invert the order of the arguments and pipeRpc.request("eth_blockNumber",[])|>Rpc.send(client)# OR[request1,request2]|>Rpc.send(client)

Use the RPC Client approach when you need:

  • πŸ”§ Direct control over RPC calls
  • πŸ”„ Dynamic method names
  • πŸ› οΈ Custom parameter handling
  • πŸŽ›οΈ Flexible client management (multiple clients, runtime configuration)

Transport Options

Exth uses a pluggable transport system that supports different communication protocols. Each transport type can be configured with specific options:

HTTP Transport

The HTTP transport provides robust HTTP/HTTPS communication with configurable middleware:

# Provider configurationdefmoduleMyProviderdouseExth.Provider,transport_type: :http,rpc_url: "https://eth-mainnet.example.com",# Optional HTTP-specific configurationadapter: Tesla.Adapter.Mint,# Default HTTP adapterheaders: [{"authorization","Bearer token"}],timeout: 30_000# Request timeout in msend# Direct client configuration{:ok,client}=Exth.Rpc.new_client(transport_type: :http,rpc_url: "https://eth-mainnet.example.com",adapter: Tesla.Adapter.Mint,headers: [{"authorization","Bearer token"}],timeout: 30_000)

HTTP Features:

  • Built on Tesla HTTP client with middleware support
  • Configurable adapters (Mint, Hackney, etc.)
  • Configurable headers and timeouts
  • Automatic URL validation and formatting

WebSocket Transport

The WebSocket transport provides full-duplex communication for real-time updates and subscriptions:

# Provider configurationdefmoduleMyProviderdouseExth.Provider,transport_type: :websocket,rpc_url: "wss://eth-mainnet.example.com",dispatch_callback: fnresponse->handle_response(response)endend# Direct client configuration{:ok,client}=Exth.Rpc.new_client(transport_type: :websocket,rpc_url: "wss://eth-mainnet.example.com",dispatch_callback: fnresponse->handle_response(response)end)# Example subscriptionrequest=Rpc.request("eth_subscribe",["newHeads"]){:ok,response}=Rpc.send(client,request)

WebSocket Features:

  • Full-duplex communication
  • Support for subscriptions and real-time updates
  • Automatic connection management and lifecycle
  • Asynchronous message handling via dispatch callbacks
  • Connection state management and supervision

IPC Transport

The IPC transport provides communication with local Ethereum nodes via Unix domain sockets:

# Provider configurationdefmoduleMyProviderdouseExth.Provider,transport_type: :ipc,path: "/tmp/ethereum.ipc",# Optional IPC-specific configurationtimeout: 30_000,# Request timeout in mspool_size: 10,# Number of connections in the poolsocket_opts: [:binary,active: false,reuseaddr: true]end# Direct client configuration{:ok,client}=Exth.Rpc.new_client(transport_type: :ipc,path: "/tmp/ethereum.ipc",timeout: 30_000,pool_size: 5)# Make requestsrequest=Rpc.request("eth_blockNumber",[]){:ok,response}=Rpc.send(client,request)

IPC Features:

  • Unix domain socket communication
  • Connection pooling with NimblePool for efficient resource management
  • Low latency for local nodes
  • Automatic connection lifecycle management
  • Note: Only available on Unix-like systems

IPC Configuration Options:

  • :path - (required) The Unix domain socket path (e.g., "/tmp/ethereum.ipc")
  • :timeout - Request timeout in milliseconds (default: 30,000ms)
  • :socket_opts - TCP socket options (default: [:binary, active: false, reuseaddr: true])
  • :pool_size - Number of connections in the pool (default: 10)
  • :pool_lazy_workers - Whether to create workers lazily (default: true)
  • :pool_worker_idle_timeout - Worker idle timeout (default: nil)
  • :pool_max_idle_pings - Maximum idle pings before worker termination (default: -1)

Custom Transport

Implement your own transport by creating a module and implementing the Exth.Transport behaviour:

defmoduleMyCustomTransportdouseExth.Transport@implExth.Transportdefinit(opts)do# Initialize your transport{:ok,transport_state}end@implExth.Transportdefhandle_request(transport_state,request)do# Handle the JSON-RPC request# Return {:ok, response} or {:error, reason}endend# Use your custom transportdefmoduleMyProviderdouseExth.Provider,transport_type: :custom,module: MyCustomTransport,rpc_url: "custom://endpoint",# Additional custom optionscustom_option: "value"end# Direct client configuration{:ok,client}=Exth.Rpc.new_client(transport_type: :custom,module: MyCustomTransport,custom_option: "value")

Custom Transport Features:

  • Full control over transport implementation
  • Custom state management
  • Behaviour-based implementation for consistency

Examples

Check out our examples directory for practical usage examples.

Requirements

  • Elixir ~> 1.18
  • Erlang/OTP 26 or later

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b feature/my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin feature/my-new-feature)
  5. Create new Pull Request

License

This project is licensed under the MIT License. See LICENSE for details.

About

Elixir JSON-RPC client fo EVM-compatible blockchains πŸ“‘

Topics

Resources

Stars

8 stars

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Exth

Exth is an Elixir client for interacting with EVM-compatible blockchain nodes via JSON-RPC. It provides a robust, type-safe interface for making Ethereum RPC calls.

Features

  • πŸ”’ Type Safety: Comprehensive type specs and validation
  • πŸ”„ Transport Agnostic: Pluggable transport system (HTTP, WebSocket, IPC)
  • 🎯 Smart Defaults: Sensible defaults with full configurability
  • πŸ›‘οΈ Error Handling: Detailed error reporting and recovery
  • πŸ“¦ Batch Support: Efficient batch request processing
  • πŸ”Œ Protocol Compliance: Full JSON-RPC 2.0 specification support
  • βš™οΈ Dynamic Configuration: Flexible configuration through both inline options and application config

Installation

Add exth to your list of dependencies in mix.exs:

defdepsdo[{:exth,"~> 0.1.0"},# Optional dependencies:# Mint for Tesla adapter{:mint,"~> 1.7"}]end

Usage

Exth offers two ways to interact with EVM nodes:

  1. Provider (High-Level): Define a provider module with convenient function names and no need to pass client references.
  2. RPC Client (Low-Level): Direct client usage with more control, requiring explicit client handling.

Provider (Recommended)

# Basic usage with inline configurationdefmoduleMyProviderdouseExth.Provider,otp_app: :your_otp_app,transport_type: :http,rpc_url: "https://YOUR-RPC-URL"end# Dynamic configuration through application config# In your config/config.exs or similar:config:your_otp_app,MyProvider,rpc_url: "https://YOUR-RPC-URL",timeout: 30_000,max_retries: 3# Then in your provider module:defmoduleMyProviderdouseExth.Provider,otp_app: :your_otp_app,transport_type: :httpend# Configuration is merged with inline options taking precedencedefmoduleMyProviderdouseExth.Provider,otp_app: :your_otp_app,transport_type: :http,rpc_url: "https://OVERRIDE-RPC-URL"# This will override the config valueend# Use the provider{:ok,block_number}=MyProvider.block_number(){:ok,balance}=MyProvider.get_balance("0x742d35Cc6634C0532925a3b844Bc454e4438f44e","latest"){:ok,block}=MyProvider.get_block_by_number("0x1",true){:ok,tx_hash}=MyProvider.send_raw_transaction("0x...")

The Provider approach is recommended for most use cases as it provides:

  • ✨ Clean, intuitive function names
  • πŸ”’ Type-safe parameters
  • πŸ“ Better documentation and IDE support
  • 🎯 No need to manage client references
  • βš™οΈ Flexible configuration through both inline options and application config

Configuration Options

Providers can be configured through both inline options and application config. Inline options take precedence over application config. Here are the available options:

# Required optionstransport_type: :http|:websocket|:ipc|:custom# Transport type to userpc_url: "https://..."# RPC endpoint URL (for HTTP/WebSocket)path: "/tmp/ethereum.ipc"# Socket path (for IPC)# Required inline optionotp_app: :your_otp_app# Application name for config lookup# Custom transport optionsmodule: MyCustomTransport# Required when transport_type is :custom# Optional HTTP optionstimeout: 30_000# Request timeout in millisecondsheaders: [{"header","value"}]# Custom headers for HTTP transportadapter: Tesla.Adapter.Mint# HTTP adapter (defaults to Mint)# Optional WebSocket optionsdispatch_callback: fnresponse->handle_response(response)end# Required for WebSocket# Optional IPC optionspool_size: 10# Connection pool sizesocket_opts: [:binary,active: false,reuseaddr: true]# Socket options

RPC Client

aliasExth.Rpc# 1. Define a client{:ok,client}=Rpc.new_client(transport_type: :http,rpc_url: "https://YOUR-RPC-URL")# 2.1. Make RPC calls with explicit clientrequest1=Rpc.request(client,"eth_blockNumber",[]){:ok,block_number}=Rpc.send(client,request1)# 2.2. Or make RPC calls without a clientrequest2=Rpc.request("eth_getBalance",["0x742d35Cc6634C0532925a3b844Bc454e4438f44e","latest"]){:ok,balance}=Rpc.send(client,request2)# 3. You can also send multiple requests in one callrequests=[request1,request2]{:ok,responses}=Rpc.send(client,requests)# 4. You can invert the order of the arguments and pipeRpc.request("eth_blockNumber",[])|>Rpc.send(client)# OR[request1,request2]|>Rpc.send(client)

Use the RPC Client approach when you need:

  • πŸ”§ Direct control over RPC calls
  • πŸ”„ Dynamic method names
  • πŸ› οΈ Custom parameter handling
  • πŸŽ›οΈ Flexible client management (multiple clients, runtime configuration)

Transport Options

Exth uses a pluggable transport system that supports different communication protocols. Each transport type can be configured with specific options:

HTTP Transport

The HTTP transport provides robust HTTP/HTTPS communication with configurable middleware:

# Provider configurationdefmoduleMyProviderdouseExth.Provider,transport_type: :http,rpc_url: "https://eth-mainnet.example.com",# Optional HTTP-specific configurationadapter: Tesla.Adapter.Mint,# Default HTTP adapterheaders: [{"authorization","Bearer token"}],timeout: 30_000# Request timeout in msend# Direct client configuration{:ok,client}=Exth.Rpc.new_client(transport_type: :http,rpc_url: "https://eth-mainnet.example.com",adapter: Tesla.Adapter.Mint,headers: [{"authorization","Bearer token"}],timeout: 30_000)

HTTP Features:

  • Built on Tesla HTTP client with middleware support
  • Configurable adapters (Mint, Hackney, etc.)
  • Configurable headers and timeouts
  • Automatic URL validation and formatting

WebSocket Transport

The WebSocket transport provides full-duplex communication for real-time updates and subscriptions:

# Provider configurationdefmoduleMyProviderdouseExth.Provider,transport_type: :websocket,rpc_url: "wss://eth-mainnet.example.com",dispatch_callback: fnresponse->handle_response(response)endend# Direct client configuration{:ok,client}=Exth.Rpc.new_client(transport_type: :websocket,rpc_url: "wss://eth-mainnet.example.com",dispatch_callback: fnresponse->handle_response(response)end)# Example subscriptionrequest=Rpc.request("eth_subscribe",["newHeads"]){:ok,response}=Rpc.send(client,request)

WebSocket Features:

  • Full-duplex communication
  • Support for subscriptions and real-time updates
  • Automatic connection management and lifecycle
  • Asynchronous message handling via dispatch callbacks
  • Connection state management and supervision

IPC Transport

The IPC transport provides communication with local Ethereum nodes via Unix domain sockets:

# Provider configurationdefmoduleMyProviderdouseExth.Provider,transport_type: :ipc,path: "/tmp/ethereum.ipc",# Optional IPC-specific configurationtimeout: 30_000,# Request timeout in mspool_size: 10,# Number of connections in the poolsocket_opts: [:binary,active: false,reuseaddr: true]end# Direct client configuration{:ok,client}=Exth.Rpc.new_client(transport_type: :ipc,path: "/tmp/ethereum.ipc",timeout: 30_000,pool_size: 5)# Make requestsrequest=Rpc.request("eth_blockNumber",[]){:ok,response}=Rpc.send(client,request)

IPC Features:

  • Unix domain socket communication
  • Connection pooling with NimblePool for efficient resource management
  • Low latency for local nodes
  • Automatic connection lifecycle management
  • Note: Only available on Unix-like systems

IPC Configuration Options:

  • :path - (required) The Unix domain socket path (e.g., "/tmp/ethereum.ipc")
  • :timeout - Request timeout in milliseconds (default: 30,000ms)
  • :socket_opts - TCP socket options (default: [:binary, active: false, reuseaddr: true])
  • :pool_size - Number of connections in the pool (default: 10)
  • :pool_lazy_workers - Whether to create workers lazily (default: true)
  • :pool_worker_idle_timeout - Worker idle timeout (default: nil)
  • :pool_max_idle_pings - Maximum idle pings before worker termination (default: -1)

Custom Transport

Implement your own transport by creating a module and implementing the Exth.Transport behaviour:

defmoduleMyCustomTransportdouseExth.Transport@implExth.Transportdefinit(opts)do# Initialize your transport{:ok,transport_state}end@implExth.Transportdefhandle_request(transport_state,request)do# Handle the JSON-RPC request# Return {:ok, response} or {:error, reason}endend# Use your custom transportdefmoduleMyProviderdouseExth.Provider,transport_type: :custom,module: MyCustomTransport,rpc_url: "custom://endpoint",# Additional custom optionscustom_option: "value"end# Direct client configuration{:ok,client}=Exth.Rpc.new_client(transport_type: :custom,module: MyCustomTransport,custom_option: "value")

Custom Transport Features:

  • Full control over transport implementation
  • Custom state management
  • Behaviour-based implementation for consistency

Examples

Check out our examples directory for practical usage examples.

Requirements

  • Elixir ~> 1.18
  • Erlang/OTP 26 or later

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b feature/my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin feature/my-new-feature)
  5. Create new Pull Request

License

This project is licensed under the MIT License. See LICENSE for details.

About

Elixir JSON-RPC client fo EVM-compatible blockchains πŸ“‘

Topics

Resources

Stars

8 stars

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

Exth

Exth is an Elixir client for interacting with EVM-compatible blockchain nodes via JSON-RPC. It provides a robust, type-safe interface for making Ethereum RPC calls.

Features

  • πŸ”’ Type Safety: Comprehensive type specs and validation
  • πŸ”„ Transport Agnostic: Pluggable transport system (HTTP, WebSocket, IPC)
  • 🎯 Smart Defaults: Sensible defaults with full configurability
  • πŸ›‘οΈ Error Handling: Detailed error reporting and recovery
  • πŸ“¦ Batch Support: Efficient batch request processing
  • πŸ”Œ Protocol Compliance: Full JSON-RPC 2.0 specification support
  • βš™οΈ Dynamic Configuration: Flexible configuration through both inline options and application config

Installation

Add exth to your list of dependencies in mix.exs:

defdepsdo[{:exth,"~> 0.1.0"},# Optional dependencies:# Mint for Tesla adapter{:mint,"~> 1.7"}]end

Usage

Exth offers two ways to interact with EVM nodes:

  1. Provider (High-Level): Define a provider module with convenient function names and no need to pass client references.
  2. RPC Client (Low-Level): Direct client usage with more control, requiring explicit client handling.

Provider (Recommended)

# Basic usage with inline configurationdefmoduleMyProviderdouseExth.Provider,otp_app: :your_otp_app,transport_type: :http,rpc_url: "https://YOUR-RPC-URL"end# Dynamic configuration through application config# In your config/config.exs or similar:config:your_otp_app,MyProvider,rpc_url: "https://YOUR-RPC-URL",timeout: 30_000,max_retries: 3# Then in your provider module:defmoduleMyProviderdouseExth.Provider,otp_app: :your_otp_app,transport_type: :httpend# Configuration is merged with inline options taking precedencedefmoduleMyProviderdouseExth.Provider,otp_app: :your_otp_app,transport_type: :http,rpc_url: "https://OVERRIDE-RPC-URL"# This will override the config valueend# Use the provider{:ok,block_number}=MyProvider.block_number(){:ok,balance}=MyProvider.get_balance("0x742d35Cc6634C0532925a3b844Bc454e4438f44e","latest"){:ok,block}=MyProvider.get_block_by_number("0x1",true){:ok,tx_hash}=MyProvider.send_raw_transaction("0x...")

The Provider approach is recommended for most use cases as it provides:

  • ✨ Clean, intuitive function names
  • πŸ”’ Type-safe parameters
  • πŸ“ Better documentation and IDE support
  • 🎯 No need to manage client references
  • βš™οΈ Flexible configuration through both inline options and application config

Configuration Options

Providers can be configured through both inline options and application config. Inline options take precedence over application config. Here are the available options:

# Required optionstransport_type: :http|:websocket|:ipc|:custom# Transport type to userpc_url: "https://..."# RPC endpoint URL (for HTTP/WebSocket)path: "/tmp/ethereum.ipc"# Socket path (for IPC)# Required inline optionotp_app: :your_otp_app# Application name for config lookup# Custom transport optionsmodule: MyCustomTransport# Required when transport_type is :custom# Optional HTTP optionstimeout: 30_000# Request timeout in millisecondsheaders: [{"header","value"}]# Custom headers for HTTP transportadapter: Tesla.Adapter.Mint# HTTP adapter (defaults to Mint)# Optional WebSocket optionsdispatch_callback: fnresponse->handle_response(response)end# Required for WebSocket# Optional IPC optionspool_size: 10# Connection pool sizesocket_opts: [:binary,active: false,reuseaddr: true]# Socket options

RPC Client

aliasExth.Rpc# 1. Define a client{:ok,client}=Rpc.new_client(transport_type: :http,rpc_url: "https://YOUR-RPC-URL")# 2.1. Make RPC calls with explicit clientrequest1=Rpc.request(client,"eth_blockNumber",[]){:ok,block_number}=Rpc.send(client,request1)# 2.2. Or make RPC calls without a clientrequest2=Rpc.request("eth_getBalance",["0x742d35Cc6634C0532925a3b844Bc454e4438f44e","latest"]){:ok,balance}=Rpc.send(client,request2)# 3. You can also send multiple requests in one callrequests=[request1,request2]{:ok,responses}=Rpc.send(client,requests)# 4. You can invert the order of the arguments and pipeRpc.request("eth_blockNumber",[])|>Rpc.send(client)# OR[request1,request2]|>Rpc.send(client)

Use the RPC Client approach when you need:

  • πŸ”§ Direct control over RPC calls
  • πŸ”„ Dynamic method names
  • πŸ› οΈ Custom parameter handling
  • πŸŽ›οΈ Flexible client management (multiple clients, runtime configuration)

Transport Options

Exth uses a pluggable transport system that supports different communication protocols. Each transport type can be configured with specific options:

HTTP Transport

The HTTP transport provides robust HTTP/HTTPS communication with configurable middleware:

# Provider configurationdefmoduleMyProviderdouseExth.Provider,transport_type: :http,rpc_url: "https://eth-mainnet.example.com",# Optional HTTP-specific configurationadapter: Tesla.Adapter.Mint,# Default HTTP adapterheaders: [{"authorization","Bearer token"}],timeout: 30_000# Request timeout in msend# Direct client configuration{:ok,client}=Exth.Rpc.new_client(transport_type: :http,rpc_url: "https://eth-mainnet.example.com",adapter: Tesla.Adapter.Mint,headers: [{"authorization","Bearer token"}],timeout: 30_000)

HTTP Features:

  • Built on Tesla HTTP client with middleware support
  • Configurable adapters (Mint, Hackney, etc.)
  • Configurable headers and timeouts
  • Automatic URL validation and formatting

WebSocket Transport

The WebSocket transport provides full-duplex communication for real-time updates and subscriptions:

# Provider configurationdefmoduleMyProviderdouseExth.Provider,transport_type: :websocket,rpc_url: "wss://eth-mainnet.example.com",dispatch_callback: fnresponse->handle_response(response)endend# Direct client configuration{:ok,client}=Exth.Rpc.new_client(transport_type: :websocket,rpc_url: "wss://eth-mainnet.example.com",dispatch_callback: fnresponse->handle_response(response)end)# Example subscriptionrequest=Rpc.request("eth_subscribe",["newHeads"]){:ok,response}=Rpc.send(client,request)

WebSocket Features:

  • Full-duplex communication
  • Support for subscriptions and real-time updates
  • Automatic connection management and lifecycle
  • Asynchronous message handling via dispatch callbacks
  • Connection state management and supervision

IPC Transport

The IPC transport provides communication with local Ethereum nodes via Unix domain sockets:

# Provider configurationdefmoduleMyProviderdouseExth.Provider,transport_type: :ipc,path: "/tmp/ethereum.ipc",# Optional IPC-specific configurationtimeout: 30_000,# Request timeout in mspool_size: 10,# Number of connections in the poolsocket_opts: [:binary,active: false,reuseaddr: true]end# Direct client configuration{:ok,client}=Exth.Rpc.new_client(transport_type: :ipc,path: "/tmp/ethereum.ipc",timeout: 30_000,pool_size: 5)# Make requestsrequest=Rpc.request("eth_blockNumber",[]){:ok,response}=Rpc.send(client,request)

IPC Features:

  • Unix domain socket communication
  • Connection pooling with NimblePool for efficient resource management
  • Low latency for local nodes
  • Automatic connection lifecycle management
  • Note: Only available on Unix-like systems

IPC Configuration Options:

  • :path - (required) The Unix domain socket path (e.g., "/tmp/ethereum.ipc")
  • :timeout - Request timeout in milliseconds (default: 30,000ms)
  • :socket_opts - TCP socket options (default: [:binary, active: false, reuseaddr: true])
  • :pool_size - Number of connections in the pool (default: 10)
  • :pool_lazy_workers - Whether to create workers lazily (default: true)
  • :pool_worker_idle_timeout - Worker idle timeout (default: nil)
  • :pool_max_idle_pings - Maximum idle pings before worker termination (default: -1)

Custom Transport

Implement your own transport by creating a module and implementing the Exth.Transport behaviour:

defmoduleMyCustomTransportdouseExth.Transport@implExth.Transportdefinit(opts)do# Initialize your transport{:ok,transport_state}end@implExth.Transportdefhandle_request(transport_state,request)do# Handle the JSON-RPC request# Return {:ok, response} or {:error, reason}endend# Use your custom transportdefmoduleMyProviderdouseExth.Provider,transport_type: :custom,module: MyCustomTransport,rpc_url: "custom://endpoint",# Additional custom optionscustom_option: "value"end# Direct client configuration{:ok,client}=Exth.Rpc.new_client(transport_type: :custom,module: MyCustomTransport,custom_option: "value")

Custom Transport Features:

  • Full control over transport implementation
  • Custom state management
  • Behaviour-based implementation for consistency

Examples

Check out our examples directory for practical usage examples.

Requirements

  • Elixir ~> 1.18
  • Erlang/OTP 26 or later

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b feature/my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin feature/my-new-feature)
  5. Create new Pull Request

License

This project is licensed under the MIT License. See LICENSE for details.

About

Elixir JSON-RPC client fo EVM-compatible blockchains πŸ“‘

Topics

Resources

Stars

8 stars

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Exth

Exth is an Elixir client for interacting with EVM-compatible blockchain nodes via JSON-RPC. It provides a robust, type-safe interface for making Ethereum RPC calls.

Features

  • πŸ”’ Type Safety: Comprehensive type specs and validation
  • πŸ”„ Transport Agnostic: Pluggable transport system (HTTP, WebSocket, IPC)
  • 🎯 Smart Defaults: Sensible defaults with full configurability
  • πŸ›‘οΈ Error Handling: Detailed error reporting and recovery
  • πŸ“¦ Batch Support: Efficient batch request processing
  • πŸ”Œ Protocol Compliance: Full JSON-RPC 2.0 specification support
  • βš™οΈ Dynamic Configuration: Flexible configuration through both inline options and application config

Installation

Add exth to your list of dependencies in mix.exs:

defdepsdo[{:exth,"~> 0.1.0"},# Optional dependencies:# Mint for Tesla adapter{:mint,"~> 1.7"}]end

Usage

Exth offers two ways to interact with EVM nodes:

  1. Provider (High-Level): Define a provider module with convenient function names and no need to pass client references.
  2. RPC Client (Low-Level): Direct client usage with more control, requiring explicit client handling.

Provider (Recommended)

# Basic usage with inline configurationdefmoduleMyProviderdouseExth.Provider,otp_app: :your_otp_app,transport_type: :http,rpc_url: "https://YOUR-RPC-URL"end# Dynamic configuration through application config# In your config/config.exs or similar:config:your_otp_app,MyProvider,rpc_url: "https://YOUR-RPC-URL",timeout: 30_000,max_retries: 3# Then in your provider module:defmoduleMyProviderdouseExth.Provider,otp_app: :your_otp_app,transport_type: :httpend# Configuration is merged with inline options taking precedencedefmoduleMyProviderdouseExth.Provider,otp_app: :your_otp_app,transport_type: :http,rpc_url: "https://OVERRIDE-RPC-URL"# This will override the config valueend# Use the provider{:ok,block_number}=MyProvider.block_number(){:ok,balance}=MyProvider.get_balance("0x742d35Cc6634C0532925a3b844Bc454e4438f44e","latest"){:ok,block}=MyProvider.get_block_by_number("0x1",true){:ok,tx_hash}=MyProvider.send_raw_transaction("0x...")

The Provider approach is recommended for most use cases as it provides:

  • ✨ Clean, intuitive function names
  • πŸ”’ Type-safe parameters
  • πŸ“ Better documentation and IDE support
  • 🎯 No need to manage client references
  • βš™οΈ Flexible configuration through both inline options and application config

Configuration Options

Providers can be configured through both inline options and application config. Inline options take precedence over application config. Here are the available options:

# Required optionstransport_type: :http|:websocket|:ipc|:custom# Transport type to userpc_url: "https://..."# RPC endpoint URL (for HTTP/WebSocket)path: "/tmp/ethereum.ipc"# Socket path (for IPC)# Required inline optionotp_app: :your_otp_app# Application name for config lookup# Custom transport optionsmodule: MyCustomTransport# Required when transport_type is :custom# Optional HTTP optionstimeout: 30_000# Request timeout in millisecondsheaders: [{"header","value"}]# Custom headers for HTTP transportadapter: Tesla.Adapter.Mint# HTTP adapter (defaults to Mint)# Optional WebSocket optionsdispatch_callback: fnresponse->handle_response(response)end# Required for WebSocket# Optional IPC optionspool_size: 10# Connection pool sizesocket_opts: [:binary,active: false,reuseaddr: true]# Socket options

RPC Client

aliasExth.Rpc# 1. Define a client{:ok,client}=Rpc.new_client(transport_type: :http,rpc_url: "https://YOUR-RPC-URL")# 2.1. Make RPC calls with explicit clientrequest1=Rpc.request(client,"eth_blockNumber",[]){:ok,block_number}=Rpc.send(client,request1)# 2.2. Or make RPC calls without a clientrequest2=Rpc.request("eth_getBalance",["0x742d35Cc6634C0532925a3b844Bc454e4438f44e","latest"]){:ok,balance}=Rpc.send(client,request2)# 3. You can also send multiple requests in one callrequests=[request1,request2]{:ok,responses}=Rpc.send(client,requests)# 4. You can invert the order of the arguments and pipeRpc.request("eth_blockNumber",[])|>Rpc.send(client)# OR[request1,request2]|>Rpc.send(client)

Use the RPC Client approach when you need:

  • πŸ”§ Direct control over RPC calls
  • πŸ”„ Dynamic method names
  • πŸ› οΈ Custom parameter handling
  • πŸŽ›οΈ Flexible client management (multiple clients, runtime configuration)

Transport Options

Exth uses a pluggable transport system that supports different communication protocols. Each transport type can be configured with specific options:

HTTP Transport

The HTTP transport provides robust HTTP/HTTPS communication with configurable middleware:

# Provider configurationdefmoduleMyProviderdouseExth.Provider,transport_type: :http,rpc_url: "https://eth-mainnet.example.com",# Optional HTTP-specific configurationadapter: Tesla.Adapter.Mint,# Default HTTP adapterheaders: [{"authorization","Bearer token"}],timeout: 30_000# Request timeout in msend# Direct client configuration{:ok,client}=Exth.Rpc.new_client(transport_type: :http,rpc_url: "https://eth-mainnet.example.com",adapter: Tesla.Adapter.Mint,headers: [{"authorization","Bearer token"}],timeout: 30_000)

HTTP Features:

  • Built on Tesla HTTP client with middleware support
  • Configurable adapters (Mint, Hackney, etc.)
  • Configurable headers and timeouts
  • Automatic URL validation and formatting

WebSocket Transport

The WebSocket transport provides full-duplex communication for real-time updates and subscriptions:

# Provider configurationdefmoduleMyProviderdouseExth.Provider,transport_type: :websocket,rpc_url: "wss://eth-mainnet.example.com",dispatch_callback: fnresponse->handle_response(response)endend# Direct client configuration{:ok,client}=Exth.Rpc.new_client(transport_type: :websocket,rpc_url: "wss://eth-mainnet.example.com",dispatch_callback: fnresponse->handle_response(response)end)# Example subscriptionrequest=Rpc.request("eth_subscribe",["newHeads"]){:ok,response}=Rpc.send(client,request)

WebSocket Features:

  • Full-duplex communication
  • Support for subscriptions and real-time updates
  • Automatic connection management and lifecycle
  • Asynchronous message handling via dispatch callbacks
  • Connection state management and supervision

IPC Transport

The IPC transport provides communication with local Ethereum nodes via Unix domain sockets:

# Provider configurationdefmoduleMyProviderdouseExth.Provider,transport_type: :ipc,path: "/tmp/ethereum.ipc",# Optional IPC-specific configurationtimeout: 30_000,# Request timeout in mspool_size: 10,# Number of connections in the poolsocket_opts: [:binary,active: false,reuseaddr: true]end# Direct client configuration{:ok,client}=Exth.Rpc.new_client(transport_type: :ipc,path: "/tmp/ethereum.ipc",timeout: 30_000,pool_size: 5)# Make requestsrequest=Rpc.request("eth_blockNumber",[]){:ok,response}=Rpc.send(client,request)

IPC Features:

  • Unix domain socket communication
  • Connection pooling with NimblePool for efficient resource management
  • Low latency for local nodes
  • Automatic connection lifecycle management
  • Note: Only available on Unix-like systems

IPC Configuration Options:

  • :path - (required) The Unix domain socket path (e.g., "/tmp/ethereum.ipc")
  • :timeout - Request timeout in milliseconds (default: 30,000ms)
  • :socket_opts - TCP socket options (default: [:binary, active: false, reuseaddr: true])
  • :pool_size - Number of connections in the pool (default: 10)
  • :pool_lazy_workers - Whether to create workers lazily (default: true)
  • :pool_worker_idle_timeout - Worker idle timeout (default: nil)
  • :pool_max_idle_pings - Maximum idle pings before worker termination (default: -1)

Custom Transport

Implement your own transport by creating a module and implementing the Exth.Transport behaviour:

defmoduleMyCustomTransportdouseExth.Transport@implExth.Transportdefinit(opts)do# Initialize your transport{:ok,transport_state}end@implExth.Transportdefhandle_request(transport_state,request)do# Handle the JSON-RPC request# Return {:ok, response} or {:error, reason}endend# Use your custom transportdefmoduleMyProviderdouseExth.Provider,transport_type: :custom,module: MyCustomTransport,rpc_url: "custom://endpoint",# Additional custom optionscustom_option: "value"end# Direct client configuration{:ok,client}=Exth.Rpc.new_client(transport_type: :custom,module: MyCustomTransport,custom_option: "value")

Custom Transport Features:

  • Full control over transport implementation
  • Custom state management
  • Behaviour-based implementation for consistency

Examples

Check out our examples directory for practical usage examples.

Requirements

  • Elixir ~> 1.18
  • Erlang/OTP 26 or later

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b feature/my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin feature/my-new-feature)
  5. Create new Pull Request

License

This project is licensed under the MIT License. See LICENSE for details.

About

Elixir JSON-RPC client fo EVM-compatible blockchains πŸ“‘

Topics

Resources

Stars

8 stars

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Exth

Exth is an Elixir client for interacting with EVM-compatible blockchain nodes via JSON-RPC. It provides a robust, type-safe interface for making Ethereum RPC calls.

Features

  • πŸ”’ Type Safety: Comprehensive type specs and validation
  • πŸ”„ Transport Agnostic: Pluggable transport system (HTTP, WebSocket, IPC)
  • 🎯 Smart Defaults: Sensible defaults with full configurability
  • πŸ›‘οΈ Error Handling: Detailed error reporting and recovery
  • πŸ“¦ Batch Support: Efficient batch request processing
  • πŸ”Œ Protocol Compliance: Full JSON-RPC 2.0 specification support
  • βš™οΈ Dynamic Configuration: Flexible configuration through both inline options and application config

Installation

Add exth to your list of dependencies in mix.exs:

defdepsdo[{:exth,"~> 0.1.0"},# Optional dependencies:# Mint for Tesla adapter{:mint,"~> 1.7"}]end

Usage

Exth offers two ways to interact with EVM nodes:

  1. Provider (High-Level): Define a provider module with convenient function names and no need to pass client references.
  2. RPC Client (Low-Level): Direct client usage with more control, requiring explicit client handling.

Provider (Recommended)

# Basic usage with inline configurationdefmoduleMyProviderdouseExth.Provider,otp_app: :your_otp_app,transport_type: :http,rpc_url: "https://YOUR-RPC-URL"end# Dynamic configuration through application config# In your config/config.exs or similar:config:your_otp_app,MyProvider,rpc_url: "https://YOUR-RPC-URL",timeout: 30_000,max_retries: 3# Then in your provider module:defmoduleMyProviderdouseExth.Provider,otp_app: :your_otp_app,transport_type: :httpend# Configuration is merged with inline options taking precedencedefmoduleMyProviderdouseExth.Provider,otp_app: :your_otp_app,transport_type: :http,rpc_url: "https://OVERRIDE-RPC-URL"# This will override the config valueend# Use the provider{:ok,block_number}=MyProvider.block_number(){:ok,balance}=MyProvider.get_balance("0x742d35Cc6634C0532925a3b844Bc454e4438f44e","latest"){:ok,block}=MyProvider.get_block_by_number("0x1",true){:ok,tx_hash}=MyProvider.send_raw_transaction("0x...")

The Provider approach is recommended for most use cases as it provides:

  • ✨ Clean, intuitive function names
  • πŸ”’ Type-safe parameters
  • πŸ“ Better documentation and IDE support
  • 🎯 No need to manage client references
  • βš™οΈ Flexible configuration through both inline options and application config

Configuration Options

Providers can be configured through both inline options and application config. Inline options take precedence over application config. Here are the available options:

# Required optionstransport_type: :http|:websocket|:ipc|:custom# Transport type to userpc_url: "https://..."# RPC endpoint URL (for HTTP/WebSocket)path: "/tmp/ethereum.ipc"# Socket path (for IPC)# Required inline optionotp_app: :your_otp_app# Application name for config lookup# Custom transport optionsmodule: MyCustomTransport# Required when transport_type is :custom# Optional HTTP optionstimeout: 30_000# Request timeout in millisecondsheaders: [{"header","value"}]# Custom headers for HTTP transportadapter: Tesla.Adapter.Mint# HTTP adapter (defaults to Mint)# Optional WebSocket optionsdispatch_callback: fnresponse->handle_response(response)end# Required for WebSocket# Optional IPC optionspool_size: 10# Connection pool sizesocket_opts: [:binary,active: false,reuseaddr: true]# Socket options

RPC Client

aliasExth.Rpc# 1. Define a client{:ok,client}=Rpc.new_client(transport_type: :http,rpc_url: "https://YOUR-RPC-URL")# 2.1. Make RPC calls with explicit clientrequest1=Rpc.request(client,"eth_blockNumber",[]){:ok,block_number}=Rpc.send(client,request1)# 2.2. Or make RPC calls without a clientrequest2=Rpc.request("eth_getBalance",["0x742d35Cc6634C0532925a3b844Bc454e4438f44e","latest"]){:ok,balance}=Rpc.send(client,request2)# 3. You can also send multiple requests in one callrequests=[request1,request2]{:ok,responses}=Rpc.send(client,requests)# 4. You can invert the order of the arguments and pipeRpc.request("eth_blockNumber",[])|>Rpc.send(client)# OR[request1,request2]|>Rpc.send(client)

Use the RPC Client approach when you need:

  • πŸ”§ Direct control over RPC calls
  • πŸ”„ Dynamic method names
  • πŸ› οΈ Custom parameter handling
  • πŸŽ›οΈ Flexible client management (multiple clients, runtime configuration)

Transport Options

Exth uses a pluggable transport system that supports different communication protocols. Each transport type can be configured with specific options:

HTTP Transport

The HTTP transport provides robust HTTP/HTTPS communication with configurable middleware:

# Provider configurationdefmoduleMyProviderdouseExth.Provider,transport_type: :http,rpc_url: "https://eth-mainnet.example.com",# Optional HTTP-specific configurationadapter: Tesla.Adapter.Mint,# Default HTTP adapterheaders: [{"authorization","Bearer token"}],timeout: 30_000# Request timeout in msend# Direct client configuration{:ok,client}=Exth.Rpc.new_client(transport_type: :http,rpc_url: "https://eth-mainnet.example.com",adapter: Tesla.Adapter.Mint,headers: [{"authorization","Bearer token"}],timeout: 30_000)

HTTP Features:

  • Built on Tesla HTTP client with middleware support
  • Configurable adapters (Mint, Hackney, etc.)
  • Configurable headers and timeouts
  • Automatic URL validation and formatting

WebSocket Transport

The WebSocket transport provides full-duplex communication for real-time updates and subscriptions:

# Provider configurationdefmoduleMyProviderdouseExth.Provider,transport_type: :websocket,rpc_url: "wss://eth-mainnet.example.com",dispatch_callback: fnresponse->handle_response(response)endend# Direct client configuration{:ok,client}=Exth.Rpc.new_client(transport_type: :websocket,rpc_url: "wss://eth-mainnet.example.com",dispatch_callback: fnresponse->handle_response(response)end)# Example subscriptionrequest=Rpc.request("eth_subscribe",["newHeads"]){:ok,response}=Rpc.send(client,request)

WebSocket Features:

  • Full-duplex communication
  • Support for subscriptions and real-time updates
  • Automatic connection management and lifecycle
  • Asynchronous message handling via dispatch callbacks
  • Connection state management and supervision

IPC Transport

The IPC transport provides communication with local Ethereum nodes via Unix domain sockets:

# Provider configurationdefmoduleMyProviderdouseExth.Provider,transport_type: :ipc,path: "/tmp/ethereum.ipc",# Optional IPC-specific configurationtimeout: 30_000,# Request timeout in mspool_size: 10,# Number of connections in the poolsocket_opts: [:binary,active: false,reuseaddr: true]end# Direct client configuration{:ok,client}=Exth.Rpc.new_client(transport_type: :ipc,path: "/tmp/ethereum.ipc",timeout: 30_000,pool_size: 5)# Make requestsrequest=Rpc.request("eth_blockNumber",[]){:ok,response}=Rpc.send(client,request)

IPC Features:

  • Unix domain socket communication
  • Connection pooling with NimblePool for efficient resource management
  • Low latency for local nodes
  • Automatic connection lifecycle management
  • Note: Only available on Unix-like systems

IPC Configuration Options:

  • :path - (required) The Unix domain socket path (e.g., "/tmp/ethereum.ipc")
  • :timeout - Request timeout in milliseconds (default: 30,000ms)
  • :socket_opts - TCP socket options (default: [:binary, active: false, reuseaddr: true])
  • :pool_size - Number of connections in the pool (default: 10)
  • :pool_lazy_workers - Whether to create workers lazily (default: true)
  • :pool_worker_idle_timeout - Worker idle timeout (default: nil)
  • :pool_max_idle_pings - Maximum idle pings before worker termination (default: -1)

Custom Transport

Implement your own transport by creating a module and implementing the Exth.Transport behaviour:

defmoduleMyCustomTransportdouseExth.Transport@implExth.Transportdefinit(opts)do# Initialize your transport{:ok,transport_state}end@implExth.Transportdefhandle_request(transport_state,request)do# Handle the JSON-RPC request# Return {:ok, response} or {:error, reason}endend# Use your custom transportdefmoduleMyProviderdouseExth.Provider,transport_type: :custom,module: MyCustomTransport,rpc_url: "custom://endpoint",# Additional custom optionscustom_option: "value"end# Direct client configuration{:ok,client}=Exth.Rpc.new_client(transport_type: :custom,module: MyCustomTransport,custom_option: "value")

Custom Transport Features:

  • Full control over transport implementation
  • Custom state management
  • Behaviour-based implementation for consistency

Examples

Check out our examples directory for practical usage examples.

Requirements

  • Elixir ~> 1.18
  • Erlang/OTP 26 or later

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b feature/my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin feature/my-new-feature)
  5. Create new Pull Request

License

This project is licensed under the MIT License. See LICENSE for details.

About

Elixir JSON-RPC client fo EVM-compatible blockchains πŸ“‘

Topics

Resources

Stars

8 stars

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

Exth

Exth is an Elixir client for interacting with EVM-compatible blockchain nodes via JSON-RPC. It provides a robust, type-safe interface for making Ethereum RPC calls.

Features

  • πŸ”’ Type Safety: Comprehensive type specs and validation
  • πŸ”„ Transport Agnostic: Pluggable transport system (HTTP, WebSocket, IPC)
  • 🎯 Smart Defaults: Sensible defaults with full configurability
  • πŸ›‘οΈ Error Handling: Detailed error reporting and recovery
  • πŸ“¦ Batch Support: Efficient batch request processing
  • πŸ”Œ Protocol Compliance: Full JSON-RPC 2.0 specification support
  • βš™οΈ Dynamic Configuration: Flexible configuration through both inline options and application config

Installation

Add exth to your list of dependencies in mix.exs:

defdepsdo[{:exth,"~> 0.1.0"},# Optional dependencies:# Mint for Tesla adapter{:mint,"~> 1.7"}]end

Usage

Exth offers two ways to interact with EVM nodes:

  1. Provider (High-Level): Define a provider module with convenient function names and no need to pass client references.
  2. RPC Client (Low-Level): Direct client usage with more control, requiring explicit client handling.

Provider (Recommended)

# Basic usage with inline configurationdefmoduleMyProviderdouseExth.Provider,otp_app: :your_otp_app,transport_type: :http,rpc_url: "https://YOUR-RPC-URL"end# Dynamic configuration through application config# In your config/config.exs or similar:config:your_otp_app,MyProvider,rpc_url: "https://YOUR-RPC-URL",timeout: 30_000,max_retries: 3# Then in your provider module:defmoduleMyProviderdouseExth.Provider,otp_app: :your_otp_app,transport_type: :httpend# Configuration is merged with inline options taking precedencedefmoduleMyProviderdouseExth.Provider,otp_app: :your_otp_app,transport_type: :http,rpc_url: "https://OVERRIDE-RPC-URL"# This will override the config valueend# Use the provider{:ok,block_number}=MyProvider.block_number(){:ok,balance}=MyProvider.get_balance("0x742d35Cc6634C0532925a3b844Bc454e4438f44e","latest"){:ok,block}=MyProvider.get_block_by_number("0x1",true){:ok,tx_hash}=MyProvider.send_raw_transaction("0x...")

The Provider approach is recommended for most use cases as it provides:

  • ✨ Clean, intuitive function names
  • πŸ”’ Type-safe parameters
  • πŸ“ Better documentation and IDE support
  • 🎯 No need to manage client references
  • βš™οΈ Flexible configuration through both inline options and application config

Configuration Options

Providers can be configured through both inline options and application config. Inline options take precedence over application config. Here are the available options:

# Required optionstransport_type: :http|:websocket|:ipc|:custom# Transport type to userpc_url: "https://..."# RPC endpoint URL (for HTTP/WebSocket)path: "/tmp/ethereum.ipc"# Socket path (for IPC)# Required inline optionotp_app: :your_otp_app# Application name for config lookup# Custom transport optionsmodule: MyCustomTransport# Required when transport_type is :custom# Optional HTTP optionstimeout: 30_000# Request timeout in millisecondsheaders: [{"header","value"}]# Custom headers for HTTP transportadapter: Tesla.Adapter.Mint# HTTP adapter (defaults to Mint)# Optional WebSocket optionsdispatch_callback: fnresponse->handle_response(response)end# Required for WebSocket# Optional IPC optionspool_size: 10# Connection pool sizesocket_opts: [:binary,active: false,reuseaddr: true]# Socket options

RPC Client

aliasExth.Rpc# 1. Define a client{:ok,client}=Rpc.new_client(transport_type: :http,rpc_url: "https://YOUR-RPC-URL")# 2.1. Make RPC calls with explicit clientrequest1=Rpc.request(client,"eth_blockNumber",[]){:ok,block_number}=Rpc.send(client,request1)# 2.2. Or make RPC calls without a clientrequest2=Rpc.request("eth_getBalance",["0x742d35Cc6634C0532925a3b844Bc454e4438f44e","latest"]){:ok,balance}=Rpc.send(client,request2)# 3. You can also send multiple requests in one callrequests=[request1,request2]{:ok,responses}=Rpc.send(client,requests)# 4. You can invert the order of the arguments and pipeRpc.request("eth_blockNumber",[])|>Rpc.send(client)# OR[request1,request2]|>Rpc.send(client)

Use the RPC Client approach when you need:

  • πŸ”§ Direct control over RPC calls
  • πŸ”„ Dynamic method names
  • πŸ› οΈ Custom parameter handling
  • πŸŽ›οΈ Flexible client management (multiple clients, runtime configuration)

Transport Options

Exth uses a pluggable transport system that supports different communication protocols. Each transport type can be configured with specific options:

HTTP Transport

The HTTP transport provides robust HTTP/HTTPS communication with configurable middleware:

# Provider configurationdefmoduleMyProviderdouseExth.Provider,transport_type: :http,rpc_url: "https://eth-mainnet.example.com",# Optional HTTP-specific configurationadapter: Tesla.Adapter.Mint,# Default HTTP adapterheaders: [{"authorization","Bearer token"}],timeout: 30_000# Request timeout in msend# Direct client configuration{:ok,client}=Exth.Rpc.new_client(transport_type: :http,rpc_url: "https://eth-mainnet.example.com",adapter: Tesla.Adapter.Mint,headers: [{"authorization","Bearer token"}],timeout: 30_000)

HTTP Features:

  • Built on Tesla HTTP client with middleware support
  • Configurable adapters (Mint, Hackney, etc.)
  • Configurable headers and timeouts
  • Automatic URL validation and formatting

WebSocket Transport

The WebSocket transport provides full-duplex communication for real-time updates and subscriptions:

# Provider configurationdefmoduleMyProviderdouseExth.Provider,transport_type: :websocket,rpc_url: "wss://eth-mainnet.example.com",dispatch_callback: fnresponse->handle_response(response)endend# Direct client configuration{:ok,client}=Exth.Rpc.new_client(transport_type: :websocket,rpc_url: "wss://eth-mainnet.example.com",dispatch_callback: fnresponse->handle_response(response)end)# Example subscriptionrequest=Rpc.request("eth_subscribe",["newHeads"]){:ok,response}=Rpc.send(client,request)

WebSocket Features:

  • Full-duplex communication
  • Support for subscriptions and real-time updates
  • Automatic connection management and lifecycle
  • Asynchronous message handling via dispatch callbacks
  • Connection state management and supervision

IPC Transport

The IPC transport provides communication with local Ethereum nodes via Unix domain sockets:

# Provider configurationdefmoduleMyProviderdouseExth.Provider,transport_type: :ipc,path: "/tmp/ethereum.ipc",# Optional IPC-specific configurationtimeout: 30_000,# Request timeout in mspool_size: 10,# Number of connections in the poolsocket_opts: [:binary,active: false,reuseaddr: true]end# Direct client configuration{:ok,client}=Exth.Rpc.new_client(transport_type: :ipc,path: "/tmp/ethereum.ipc",timeout: 30_000,pool_size: 5)# Make requestsrequest=Rpc.request("eth_blockNumber",[]){:ok,response}=Rpc.send(client,request)

IPC Features:

  • Unix domain socket communication
  • Connection pooling with NimblePool for efficient resource management
  • Low latency for local nodes
  • Automatic connection lifecycle management
  • Note: Only available on Unix-like systems

IPC Configuration Options:

  • :path - (required) The Unix domain socket path (e.g., "/tmp/ethereum.ipc")
  • :timeout - Request timeout in milliseconds (default: 30,000ms)
  • :socket_opts - TCP socket options (default: [:binary, active: false, reuseaddr: true])
  • :pool_size - Number of connections in the pool (default: 10)
  • :pool_lazy_workers - Whether to create workers lazily (default: true)
  • :pool_worker_idle_timeout - Worker idle timeout (default: nil)
  • :pool_max_idle_pings - Maximum idle pings before worker termination (default: -1)

Custom Transport

Implement your own transport by creating a module and implementing the Exth.Transport behaviour:

defmoduleMyCustomTransportdouseExth.Transport@implExth.Transportdefinit(opts)do# Initialize your transport{:ok,transport_state}end@implExth.Transportdefhandle_request(transport_state,request)do# Handle the JSON-RPC request# Return {:ok, response} or {:error, reason}endend# Use your custom transportdefmoduleMyProviderdouseExth.Provider,transport_type: :custom,module: MyCustomTransport,rpc_url: "custom://endpoint",# Additional custom optionscustom_option: "value"end# Direct client configuration{:ok,client}=Exth.Rpc.new_client(transport_type: :custom,module: MyCustomTransport,custom_option: "value")

Custom Transport Features:

  • Full control over transport implementation
  • Custom state management
  • Behaviour-based implementation for consistency

Examples

Check out our examples directory for practical usage examples.

Requirements

  • Elixir ~> 1.18
  • Erlang/OTP 26 or later

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b feature/my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin feature/my-new-feature)
  5. Create new Pull Request

License

This project is licensed under the MIT License. See LICENSE for details.

About

Elixir JSON-RPC client fo EVM-compatible blockchains πŸ“‘

Topics

Resources

Stars

8 stars

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages