Skip to main content

Quick Start

import { Effect, Layer } from 'effect'
import {
  FeeEstimatorService,
  DefaultFeeEstimator,
  Provider,
  HttpTransport
} from 'voltaire-effect'

// Compose layers first
const ProviderLayer = Provider.pipe(Layer.provide(HttpTransport('https://eth.llamarpc.com')))
const FeeEstimatorLayer = DefaultFeeEstimator.pipe(Layer.provide(ProviderLayer))

const program = Effect.gen(function* () {
  const feeEstimator = yield* FeeEstimatorService
  const fees = yield* feeEstimator.estimateFeesPerGas('eip1559')
  return fees
}).pipe(Effect.provide(FeeEstimatorLayer))

EIP-1559 Fee Estimation

const feeEstimator = yield* FeeEstimatorService

const fees = yield* feeEstimator.estimateFeesPerGas('eip1559')
// { maxFeePerGas: bigint, maxPriorityFeePerGas: bigint }

console.log('Max fee:', fees.maxFeePerGas)
console.log('Priority fee:', fees.maxPriorityFeePerGas)

Legacy Gas Price

const fees = yield* feeEstimator.estimateFeesPerGas('legacy')
// { gasPrice: bigint }

console.log('Gas price:', fees.gasPrice)

Priority Fee Only

const priorityFee = yield* feeEstimator.getMaxPriorityFeePerGas()
// bigint - the current max priority fee (tip)

Custom Base Fee Multiplier

The default multiplier is 1.2 (20% buffer). Use makeFeeEstimator for volatile networks:
import { makeFeeEstimator } from 'voltaire-effect'

// 1.5x multiplier for more volatile networks
const CustomFeeEstimator = makeFeeEstimator(1.5)

// Compose layers first
const ProviderLayer = Provider.pipe(Layer.provide(HttpTransport('https://...')))
const CustomFeeEstimatorLayer = CustomFeeEstimator.pipe(Layer.provide(ProviderLayer))

const program = Effect.gen(function* () {
  const feeEstimator = yield* FeeEstimatorService
  console.log('Multiplier:', feeEstimator.baseFeeMultiplier) // 1.5
  return yield* feeEstimator.estimateFeesPerGas('eip1559')
}).pipe(Effect.provide(CustomFeeEstimatorLayer))

Error Handling

import { FeeEstimationError } from 'voltaire-effect'

program.pipe(
  Effect.catchTag('FeeEstimationError', (e) => {
    console.error('Fee estimation failed:', e.message)
    // Fallback to zero fees
    return Effect.succeed({ maxFeePerGas: 0n, maxPriorityFeePerGas: 0n })
  })
)
Common error causes:
  • Network/RPC failures
  • Pre-EIP-1559 chains (no baseFeePerGas in block)

FiberRef Helpers

Use withTimeout and withRetrySchedule for per-request overrides:
import { Effect, Schedule, Duration } from 'effect'
import { FeeEstimatorService, withTimeout, withRetrySchedule } from 'voltaire-effect'

const program = Effect.gen(function* () {
  const feeEstimator = yield* FeeEstimatorService
  
  // Fast timeout for fee estimation
  const fees = yield* feeEstimator.estimateFeesPerGas('eip1559').pipe(
    withTimeout("2 seconds"),
    withRetrySchedule(Schedule.recurs(2))
  )
  return fees
})

Layer Composition

DefaultFeeEstimator requires ProviderService:
import * as Layer from 'effect/Layer'

// FeeEstimator depends on Provider
const DefaultFeeEstimator: Layer.Layer<FeeEstimatorService, never, ProviderService>

// Compose full stack
const ProviderLayer = Provider.pipe(Layer.provide(HttpTransport('https://eth.llamarpc.com')))
const FullStack = DefaultFeeEstimator.pipe(Layer.provide(ProviderLayer))

const program = myEffect.pipe(Effect.provide(FullStack))

Fee Value Types

type FeeValuesLegacy = {
  readonly gasPrice: bigint
}

type FeeValuesEIP1559 = {
  readonly maxFeePerGas: bigint
  readonly maxPriorityFeePerGas: bigint
}

type FeeValues = FeeValuesLegacy | FeeValuesEIP1559

Service Interface

type FeeEstimatorShape = {
  readonly estimateFeesPerGas: (
    type: 'legacy' | 'eip1559'
  ) => Effect.Effect<FeeValues, FeeEstimationError, ProviderService>

  readonly getMaxPriorityFeePerGas: () => Effect.Effect<
    bigint,
    FeeEstimationError,
    ProviderService
  >

  readonly baseFeeMultiplier: number
}