Skip to main content

Quick Start

import { Effect } from 'effect'
import { CcipService, DefaultCcip } from 'voltaire-effect'

const program = Effect.gen(function* () {
  const ccip = yield* CcipService
  return yield* ccip.request({
    sender: '0x1234567890123456789012345678901234567890',
    urls: ['https://ccip.ens.domains/{sender}/{data}'],
    callData: '0xabcdef',
    callbackSelector: '0x12345678',
    extraData: '0x'
  })
}).pipe(
  Effect.provide(DefaultCcip)
)

What is CCIP?

EIP-3668 defines a standard for contracts to request offchain data during execution. When a contract reverts with OffchainLookup, the client fetches data from gateway URLs and retries with the response. Common use cases:
  • ENS resolution - Fetching offchain name records
  • L2 state proofs - Verifying L2 data on L1
  • Offchain attestations - Retrieving signed data from trusted gateways

CcipRequest

interface CcipRequest {
  /** Contract address that initiated the lookup */
  readonly sender: `0x${string}`
  /** Gateway URLs to try in order */
  readonly urls: readonly string[]
  /** Calldata to send to the gateway */
  readonly callData: `0x${string}`
  /** Callback function selector on the contract */
  readonly callbackSelector: `0x${string}`
  /** Extra data passed through to the callback */
  readonly extraData: `0x${string}`
}
URL templating per EIP-3668:
  • {sender} → replaced with contract address (lowercase)
  • {data} → replaced with calldata
  • If URL contains {data}, uses GET; otherwise POST with { sender, data }

Error Handling

import { CcipError } from 'voltaire-effect'

program.pipe(
  Effect.catchTag('CcipError', (e) => {
    console.error('CCIP failed:', e.message)
    console.error('Tried URLs:', e.urls)
    return Effect.succeed('0x')
  })
)
CcipError includes:
  • urls - The URLs that were attempted
  • message - Human-readable error description
  • cause - Underlying error (if any)

NoopCcip

Disable offchain lookups for testing or sandboxed environments:
import { NoopCcip } from 'voltaire-effect'

const testProgram = program.pipe(
  Effect.provide(NoopCcip)  // Always fails with "CCIP disabled"
)

Layer Composition

DefaultCcip is a self-contained Layer with no dependencies:
import * as Layer from 'effect/Layer'
import { DefaultCcip, Provider, HttpTransport } from 'voltaire-effect'

// Compose with other services
const AppLayer = Layer.mergeAll(
  DefaultCcip,
  Provider.pipe(Layer.provide(HttpTransport('https://eth.llamarpc.com')))
)

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

Service Interface

type CcipShape = {
  readonly request: (params: CcipRequest) => Effect.Effect<`0x${string}`, CcipError>
}

class CcipService extends Context.Tag("CcipService")<CcipService, CcipShape>() {}

See Also