@ethernauta/transport
v0.0.48
Published
Resolver shapes (Readable / Writable / Signable / Callable / Bridgeable) + HTTP transport + EIP-1193 provider adapter for Ethernauta.
Maintainers
Readme
Philosophy
This module owns the protocol layer that the rest of the library composes on top of:
- JSON-RPC 2.0 primitives (
Call,Request,Response,http(),websocket()). - CAIP chain, account, asset and token identifiers.
- The chain-config-driven resolver factories —
create_reader,create_writer,create_contract— and their matching method shapes. - The dapp-side adapter (
create_provider) that lifts any EIP-1193 source into Ethernauta's{ reader, signer }resolver shape — the only way to obtain aSignable<T>resolver.
It is the only package every other published package depends on.
The four method shapes
Every method exported across the library is one of these four shapes:
| Shape | Factory | Resolver input | Use for |
| --- | --- | --- | --- |
| Readable<T> | create_reader(chains) | { chain_id } | Chain reads via JSON-RPC (eth_getBalance, eth_getBlockByHash, …) |
| Writable<T> | create_writer(chains) | { chain_id } | Chain writes via JSON-RPC (eth_sendRawTransaction) |
| Signable<T> | create_provider(provider).signer | { chain_id, to? } | Wallet interactions (eth_requestAccounts, eth_signTransaction, contract Signable methods). provider is an EIP-1193 source — typically from an EIP-6963 announce |
| Callable<T> | create_contract(chains) | { chain_id, to } | eth_call reads against a specific contract |
All four factories take the same chains array shape — each entry is { chainId, transports? }. Promise.any is used internally so that any one transport succeeding resolves the call.
Modules
- abi [NPM]
- chain [NPM]
- cli [NPM]
- core [NPM]
- crypto [NPM]
- eip [NPM]
- ens [NPM]
- erc [NPM]
- eth [NPM]
- react [NPM]
- transaction [NPM]
- transport [NPM]
- utils [NPM]
- wallet
API
create_reader
import { eip155_11155111 } from "@ethernauta/chain"
import { create_reader, encode_chain_id, http } from "@ethernauta/transport"
export const SEPOLIA_CHAIN_ID = encode_chain_id({
namespace: "eip155",
reference: eip155_11155111.chainId,
})
export const reader = create_reader([
{
chainId: SEPOLIA_CHAIN_ID,
transports: [http("https://ethereum-sepolia-rpc.publicnode.com")],
},
])create_writer
import { create_writer, http } from "@ethernauta/transport"
export const writer = create_writer([
{
chainId: SEPOLIA_CHAIN_ID,
transports: [http("https://ethereum-sepolia-rpc.publicnode.com")],
},
])create_contract
import { create_contract, http } from "@ethernauta/transport"
// Contract resolver targets a specific `to` address and is
// used by Callable methods that issue `eth_call`.
export const contract = create_contract([
{
chainId: SEPOLIA_CHAIN_ID,
transports: [http("https://ethereum-sepolia-rpc.publicnode.com")],
},
])create_subscriber
import { create_subscriber, websocket } from "@ethernauta/transport"
export const subscriber = create_subscriber([
{
chainId: SEPOLIA_CHAIN_ID,
websockets: [websocket("wss://ethereum-sepolia-rpc.publicnode.com")],
},
])
// Used with eth_subscribe* methods from @ethernauta/eth.create_multicall
import { create_multicall, http } from "@ethernauta/transport"
const multicall = create_multicall([
{ chainId: SEPOLIA_CHAIN_ID, transports: [http(RPC_URL)] },
])
// Aggregate N independent `Callable<T>` reads (e.g. balanceOf,
// supportsInterface) into one round-trip via the canonical
// Multicall3 deployment.
const [balance, supports_erc721] = await multicall([
balanceOf({ owner: account })(contract({ chain_id, to: TOKEN })),
supportsInterface({ interfaceId: "0x80ac58cd" })(contract({ chain_id, to: NFT })),
])
// Per-call failure tolerance:
const results = await multicall(calls, { allow_failure: true })
// results[i]: { success: true, value: T } | { success: false }create_provider — dapp-side EIP-1193 adapter
import { create_provider } from "@ethernauta/transport"
const resolver = create_provider(window.ethereum) // any 1193-shaped source
const chain_id = await eth_chainId()(resolver.reader({ chain_id: SEPOLIA_CHAIN_ID }))
const [account] = await eth_requestAccounts()(resolver.signer({ chain_id }))create_injected_transport is also exported for callers that want only the reader-side adapter (e.g. building an Http against window.ethereum without the signer pair).
Transports — http
import { http, type HttpOptions, type HttpRetryOptions, type HttpBatchOptions } from "@ethernauta/transport"
const transport = http("https://…", {
timeout_ms: 10_000,
retry: { attempts: 3, base_delay_ms: 250, max_delay_ms: 30_000 },
batch: { window_ms: 10, max_size: 100 }, // or `batch: true` for defaults
headers: { "X-Custom": "…" },
})Transports — websocket
import { websocket, type WebsocketTransport, type Unsubscribe } from "@ethernauta/transport"
const ws = websocket("wss://…")
const unsubscribe = await ws.subscribe(["eth_subscribe", ["newHeads"]], (event) => {
// event: { subscription, result }
})
await unsubscribe()
await ws.close()CAIP encoders
import {
encode_chain_id, decode_chain_id,
AccountIdSchema, type AccountId,
AssetIdSchema, type AssetId,
AssetTypeSchema, type AssetType,
AssetNameSchema, type AssetName,
TokenIdSchema, type TokenId,
ChainIdSchema, type ChainId,
} from "@ethernauta/transport"
const chain_id = encode_chain_id({ namespace: "eip155", reference: "11155111" })
// "eip155:11155111"
const { namespace, reference } = decode_chain_id(chain_id)CAIP-2 (chain), CAIP-10 (account) and CAIP-19 (asset / token) primitives are all exported as Valibot schemas plus their inferred types.
JSON-RPC primitives
import {
type Call, CallSchema,
type Request, RequestSchema,
type Response, ResponseSchema,
type FailedResponse, type SuccesfulResponse,
type Parameters, ParametersSchema,
type Id, IdSchema,
MethodSchema,
} from "@ethernauta/transport"FunctionSidecar
Sidecar metadata that travels alongside a eth_signTransaction request so the wallet can display human-readable function and parameter names. The wallet verifies keccak(signature)[0:4] === input[0:4] before showing anything — names is display-only.
import type { FunctionSignature } from "@ethernauta/transport"
import { FunctionSignatureSchema } from "@ethernauta/transport"
const _function: FunctionSignature = {
signature: "transfer(address,uint256)",
names: ["to", "value"],
}The matching EthernautaContext and EthernautaContextSchema are exported for callers that want to assemble the full sidecar by hand.
