Skip to main content

Quick Start

import * as JsonRpc from 'voltaire-effect/jsonrpc'
import { Effect } from 'effect'

// Create type-safe requests
const request = JsonRpc.Request.from({
  method: 'eth_blockNumber',
  params: [],
  id: 1
})

// Parse responses with proper error handling
const program = Effect.gen(function* () {
  const response = yield* JsonRpc.Response.parse(rawResponse)
  const result = yield* JsonRpc.Response.unwrap(response)
  return result
})

Request Types

JsonRpcRequest

Create and manipulate JSON-RPC 2.0 requests.
import * as JsonRpc from 'voltaire-effect/jsonrpc'

// Create a request
const req = JsonRpc.Request.from({
  method: 'eth_getBalance',
  params: ['0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0', 'latest'],
  id: 1
})

// Check if notification (no id)
JsonRpc.Request.isNotification(req) // false

// Add params to existing request
const withParams = JsonRpc.Request.withParams(baseReq)(['0x...', 'latest'])

Typed Method Requests

Use namespace-specific request constructors for type safety:
import * as JsonRpc from 'voltaire-effect/jsonrpc'

// Eth namespace
const balanceReq = JsonRpc.Eth.GetBalanceRequest('0x...', 'latest')
const blockReq = JsonRpc.Eth.GetBlockByNumberRequest('0x1', false)
const callReq = JsonRpc.Eth.CallRequest({ to: '0x...', data: '0x...' }, 'latest')

// Wallet namespace (EIP-3326, EIP-747)
const switchChain = JsonRpc.Wallet.SwitchEthereumChainRequest('0x1')
const addChain = JsonRpc.Wallet.AddEthereumChainRequest({ chainId: '0x1', chainName: 'Mainnet', ... })

// Net namespace
const version = JsonRpc.Net.VersionRequest()
const listening = JsonRpc.Net.ListeningRequest()

// Web3 namespace
const clientVersion = JsonRpc.Web3.ClientVersionRequest()
const sha3 = JsonRpc.Web3.Sha3Request('0x68656c6c6f')

// Txpool namespace
const status = JsonRpc.Txpool.StatusRequest()
const content = JsonRpc.Txpool.ContentRequest()

// Anvil namespace (Foundry testing)
const mine = JsonRpc.Anvil.MineRequest(10)
const setBalance = JsonRpc.Anvil.SetBalanceRequest('0x...', '0x1000')

// Hardhat namespace
const reset = JsonRpc.Hardhat.ResetRequest({ forking: { jsonRpcUrl: '...' } })

Response Types

JsonRpcResponse

Parse and handle JSON-RPC responses with Effect error handling.
import * as JsonRpc from 'voltaire-effect/jsonrpc'
import { Effect } from 'effect'

const program = Effect.gen(function* () {
  // Parse raw response
  const response = yield* JsonRpc.Response.parse(rawJson)
  
  // Check response type
  if (JsonRpc.Response.isSuccess(response)) {
    const result = response.result
  }
  
  if (JsonRpc.Response.isError(response)) {
    const error = response.error
  }
  
  // Unwrap with automatic error handling
  const result = yield* JsonRpc.Response.unwrap(response)
  return result
})

JsonRpcError

Handle JSON-RPC errors with standard codes.
import * as JsonRpc from 'voltaire-effect/jsonrpc'

// Standard error codes
JsonRpc.Error.PARSE_ERROR         // -32700
JsonRpc.Error.INVALID_REQUEST     // -32600
JsonRpc.Error.METHOD_NOT_FOUND    // -32601
JsonRpc.Error.INVALID_PARAMS      // -32602
JsonRpc.Error.INTERNAL_ERROR      // -32603

// Ethereum-specific codes (EIP-1474)
JsonRpc.Error.INVALID_INPUT       // -32000
JsonRpc.Error.RESOURCE_NOT_FOUND  // -32001
JsonRpc.Error.TRANSACTION_REJECTED // -32003

// Create error
const error = JsonRpc.Error.from({
  code: -32603,
  message: 'Internal error',
  data: { details: 'Connection refused' }
})

// Format for display
const str = JsonRpc.Error.toString(error)

Batch Requests

Send multiple requests in a single call.
import * as JsonRpc from 'voltaire-effect/jsonrpc'

// Create batch
const batch = JsonRpc.BatchRequest.from([
  JsonRpc.Eth.BlockNumberRequest(),
  JsonRpc.Eth.ChainIdRequest(),
  JsonRpc.Eth.GetBalanceRequest('0x...', 'latest')
])

// Add to batch
const updated = JsonRpc.BatchRequest.add(batch)(newRequest)

// Check size
const size = JsonRpc.BatchRequest.size(batch) // 3

Batch Responses

Process batch response arrays.
import * as JsonRpc from 'voltaire-effect/jsonrpc'
import { Effect } from 'effect'

const program = Effect.gen(function* () {
  const batch = yield* JsonRpc.BatchResponse.parse(rawResponses)
  
  // Find by ID
  const response = JsonRpc.BatchResponse.findById(batch)(1)
  
  // Get all errors
  const errors = JsonRpc.BatchResponse.errors(batch)
  
  // Get all successful results
  const results = JsonRpc.BatchResponse.results(batch)
  
  return results
})

Effect Integration

All parsing operations return Effects with proper error channels.
import * as JsonRpc from 'voltaire-effect/jsonrpc'
import { Effect } from 'effect'

const program = Effect.gen(function* () {
  const response = yield* JsonRpc.Response.parse(rawJson)
  const result = yield* JsonRpc.Response.unwrap(response)
  return result
}).pipe(
  Effect.catchTag('JsonRpcParseError', (e) => 
    Effect.succeed({ error: 'Invalid JSON-RPC response' })
  ),
  Effect.catchTag('JsonRpcError', (e) =>
    Effect.succeed({ error: `RPC error ${e.code}: ${e.message}` })
  )
)

Error Types

ErrorDescription
JsonRpcParseErrorFailed to parse JSON-RPC message
JsonRpcErrorRPC returned an error response

Type Exports

import type {
  JsonRpcRequestType,
  JsonRpcResponseType,
  JsonRpcSuccessResponseType,
  JsonRpcErrorResponseType,
  JsonRpcErrorType,
  JsonRpcIdType,
  BatchRequestType,
  BatchResponseType,
} from 'voltaire-effect/jsonrpc'

Pure Functions

All functions are referentially transparent and work without Effect context:
import * as JsonRpc from 'voltaire-effect/jsonrpc'

// Request utilities (pure)
JsonRpc.Request.from({ method: 'eth_blockNumber', id: 1 })
JsonRpc.Request.isNotification(request)

// Response utilities (pure)  
JsonRpc.Response.isSuccess(response)
JsonRpc.Response.isError(response)

// Error utilities (pure)
JsonRpc.Error.toString(error)

// Batch utilities (pure)
JsonRpc.BatchRequest.size(batch)
JsonRpc.BatchResponse.findById(batch)(id)