Simple Provider Setup
UseMainnetProvider for the simplest setup:
Copy
Ask AI
import { Effect } from 'effect'
import { getBlockNumber, getChainId, MainnetProvider } from 'voltaire-effect'
const program = Effect.gen(function* () {
const blockNumber = yield* getBlockNumber()
const chainId = yield* getChainId()
return { blockNumber, chainId }
}).pipe(Effect.provide(MainnetProvider('https://eth.llamarpc.com')))
await Effect.runPromise(program)
Multi-Chain Applications
Switch between chains by swapping presets:Copy
Ask AI
import { Effect } from 'effect'
import {
getBlockNumber as getBlockNum,
MainnetProvider,
OptimismProvider,
ArbitrumProvider,
BaseProvider
} from 'voltaire-effect'
const getBlockNumber = Effect.gen(function* () {
return yield* getBlockNum()
})
// Run on different chains
const [mainnet, optimism, arbitrum, base] = await Promise.all([
Effect.runPromise(getBlockNumber.pipe(
Effect.provide(MainnetProvider('https://eth.llamarpc.com'))
)),
Effect.runPromise(getBlockNumber.pipe(
Effect.provide(OptimismProvider('https://mainnet.optimism.io'))
)),
Effect.runPromise(getBlockNumber.pipe(
Effect.provide(ArbitrumProvider('https://arb1.arbitrum.io/rpc'))
)),
Effect.runPromise(getBlockNumber.pipe(
Effect.provide(BaseProvider('https://mainnet.base.org'))
))
])
console.log({ mainnet, optimism, arbitrum, base })
Full Service Stack
UseMainnetFullProvider for applications needing all services:
Copy
Ask AI
import { Effect } from 'effect'
import {
getBlockNumber,
ChainService,
FeeEstimatorService,
NonceManagerService,
CacheService,
MainnetFullProvider
} from 'voltaire-effect'
const program = Effect.gen(function* () {
const chain = yield* ChainService
const feeEstimator = yield* FeeEstimatorService
const nonceManager = yield* NonceManagerService
const cache = yield* CacheService
// All services available
const blockNumber = yield* getBlockNumber()
const fees = yield* feeEstimator.estimate()
const nonce = yield* nonceManager.getNextNonce('0x742d35Cc6634C0532925a3b844Bc9e7595f251e3')
return {
chain: chain.name,
chainId: chain.id,
blockNumber,
fees,
nonce
}
}).pipe(Effect.provide(MainnetFullProvider('https://eth.llamarpc.com')))
L2 Applications
Chain-specific presets include proper chain configuration:Copy
Ask AI
import { Effect } from 'effect'
import {
getBlockNumber,
ChainService,
ContractsService,
OptimismProvider
} from 'voltaire-effect'
const program = Effect.gen(function* () {
const chain = yield* ChainService
const contracts = yield* ContractsService
// Chain config is pre-populated
console.log(`Chain: ${chain.name}`) // "Optimism"
console.log(`Chain ID: ${chain.id}`) // 10
console.log(`Block time: ${chain.blockTime}`) // 2000ms
console.log(`Multicall3: ${contracts.multicall3?.address}`)
const block = yield* getBlockNumber()
return block
}).pipe(Effect.provide(OptimismProvider('https://mainnet.optimism.io')))
Testing with Presets
Replace transport for testing:Copy
Ask AI
import { Effect, Layer } from 'effect'
import {
getBlockNumber,
Provider,
TransportService,
mainnet,
DefaultFormatter,
DefaultTransactionSerializer,
DefaultFeeEstimator,
DefaultNonceManager,
MemoryCache
} from 'voltaire-effect'
// Mock transport
const MockTransport = Layer.succeed(TransportService, {
request: (method: string, _params?: unknown[]) => {
const responses: Record<string, unknown> = {
eth_blockNumber: '0x1234',
eth_chainId: '0x1',
eth_getBalance: '0xde0b6b3a7640000'
}
return Effect.succeed(responses[method])
}
})
// Test provider with same structure as MainnetFullProvider
const TestProvider = Layer.mergeAll(
Provider.pipe(Layer.provide(MockTransport)),
DefaultFormatter,
DefaultTransactionSerializer.Live,
DefaultFeeEstimator.pipe(Layer.provide(Provider.pipe(Layer.provide(MockTransport)))),
DefaultNonceManager.pipe(Layer.provide(Provider.pipe(Layer.provide(MockTransport)))),
MemoryCache(),
mainnet
)
// Test
const testProgram = Effect.gen(function* () {
return yield* getBlockNumber()
}).pipe(Effect.provide(TestProvider))
await Effect.runPromise(testProgram) // 4660n
createProvider for Custom Networks
For networks without a preset:Copy
Ask AI
import { Effect } from 'effect'
import { getChainId, createProvider } from 'voltaire-effect'
// Any EVM-compatible network
const fantomProvider = createProvider('https://rpc.ftm.tools')
const avalancheProvider = createProvider('https://api.avax.network/ext/bc/C/rpc')
const bscProvider = createProvider('https://bsc-dataseed.binance.org')
const program = Effect.gen(function* () {
return yield* getChainId()
})
// Fantom
await Effect.runPromise(program.pipe(Effect.provide(fantomProvider))) // 250n
// Avalanche
await Effect.runPromise(program.pipe(Effect.provide(avalancheProvider))) // 43114n
// BSC
await Effect.runPromise(program.pipe(Effect.provide(bscProvider))) // 56n
When to Use Manual Composition
Presets are convenient, but manual composition gives more control:Copy
Ask AI
import { Effect, Layer } from 'effect'
import {
Provider,
HttpTransport,
mainnet,
DefaultFormatter
} from 'voltaire-effect'
// Custom layer with only what you need
const MinimalProvider = Layer.mergeAll(
Provider.pipe(Layer.provide(HttpTransport('https://eth.llamarpc.com'))),
mainnet
)
// Or with custom implementations
import { MyCustomFeeEstimator } from './custom-fee-estimator'
const CustomStack = Layer.mergeAll(
Provider.pipe(Layer.provide(HttpTransport('https://eth.llamarpc.com'))),
DefaultFormatter,
mainnet,
MyCustomFeeEstimator // Your implementation
)

