Stateless JSON-RPC 2.0 client for Polygon (and any other EVM chain
that speaks standard eth_* JSON-RPC).
The building blocks a transaction-submission layer needs:
call/3— raw JSON-RPC call returning the parsedresult.chain_id/1,block_number/1,gas_price/1— uint helpers.nonce/2/nonce/3—eth_getTransactionCount, default block tag"pending".estimate_gas/2—eth_estimateGaswith Elixir-friendly param shape (atom keys, integer amounts).send_raw_transaction/2— accepts a hex string, raw binary, or a signedPolymarket.Txstruct.
This module is intentionally stateless. The Polymarket.RPC struct
carries :url, :headers, :timeout, :retry, :max_retries,
and :plug (test-only). No GenServer, no connection pool of our
own — Req's Finch pool handles transport. Caller threads the struct
through calls.
Response contract
{:ok, result}— successful RPC response.resultis the decodedresultfield (a hex string for mosteth_*calls, a map or list for others). Typed helpers parseresultinto a typed value before returning.{:error, {:rpc_error, code, message, data}}— JSON-RPC protocol error returned by the node.dataisnilwhen the node didn't include it.{:error, {:http_error, status, body}}— non-2xx HTTP response from the node.{:error, {:transport_error, reason}}— Req-level transport failure.{:error, {:invalid_response, body_or_message}}— well-formed HTTP 200 but the body is missing the expectedresult/errorshape, or a typed helper got back something it couldn't parse (e.g.chain_id/1got non-hex).
Scope
This module ships only the JSON-RPC client and nonce/gas
primitives. Higher-level submit wrappers
(Polymarket.CTF.redeem_positions/2 etc.) compose calldata
(Polymarket.ABI) + tx serialization (Polymarket.Tx) + this RPC
layer.
Options
:url(required) — RPC endpoint URL (e.g."https://polygon-rpc.com").:headers— extra request headers (default[]).:timeout—receive_timeoutin milliseconds (default15_000).:retry— Req retry policy. Defaultfalse. JSON-RPC POSTs are not idempotent at the application layer (sending a tx twice double-spends nonce); leave retry off unless the caller knows the specific RPC method is safe to retry.:max_retries— Req max retries (default0).:plug— test-only; injects a Req.Test plug into the request.
Summary
Functions
Calls eth_blockNumber. Returns the latest block number as an integer.
Performs a raw JSON-RPC call. Returns the decoded result field on
success; see the moduledoc for the full error shape.
Calls eth_chainId. Returns the chain id as a non-negative integer.
Calls eth_estimateGas. params is a map with optional keys
Calls eth_gasPrice. Returns wei as an integer.
Builds a Polymarket.RPC config struct.
Calls eth_getTransactionCount(address, block_tag). Default block
tag is "pending" so the returned nonce is suitable for the next
transaction the caller will sign.
Calls eth_sendRawTransaction(signed_bytes). Returns the
0x-prefixed transaction hash from the node on success.
Types
@type t() :: %Polymarket.RPC{ headers: list(), max_retries: non_neg_integer(), plug: term(), retry: term(), timeout: pos_integer(), url: String.t() }
Functions
@spec block_number(t()) :: {:ok, non_neg_integer()} | {:error, term()}
Calls eth_blockNumber. Returns the latest block number as an integer.
Performs a raw JSON-RPC call. Returns the decoded result field on
success; see the moduledoc for the full error shape.
@spec chain_id(t()) :: {:ok, non_neg_integer()} | {:error, term()}
Calls eth_chainId. Returns the chain id as a non-negative integer.
@spec estimate_gas(t(), map()) :: {:ok, non_neg_integer()} | {:error, term()}
Calls eth_estimateGas. params is a map with optional keys:
:from— sender address (0x-prefixed hex).:to— destination address (0x-prefixed hex).:value— non-negative integer (wei).:gas— non-negative integer (gas units).:gas_price— non-negative integer (wei).:data— binary or0x-prefixed hex string.
Integer amounts are encoded as 0x-prefixed hex on the wire.
@spec gas_price(t()) :: {:ok, non_neg_integer()} | {:error, term()}
Calls eth_gasPrice. Returns wei as an integer.
Builds a Polymarket.RPC config struct.
Required: :url. Optional: :headers, :timeout, :retry,
:max_retries, :plug.
@spec nonce(t(), String.t(), String.t()) :: {:ok, non_neg_integer()} | {:error, term()}
Calls eth_getTransactionCount(address, block_tag). Default block
tag is "pending" so the returned nonce is suitable for the next
transaction the caller will sign.
@spec send_raw_transaction(t(), Polymarket.Tx.t() | binary()) :: {:ok, String.t()} | {:error, term()}
Calls eth_sendRawTransaction(signed_bytes). Returns the
0x-prefixed transaction hash from the node on success.
Accepts:
- a
Polymarket.Txstruct that has been signed (:vpopulated); - a
0x-prefixed hex string; - a raw binary (will be hex-encoded with
0xprefix).